pax_global_header00006660000000000000000000000064152666063660014532gustar00rootroot0000000000000052 comment=aa54a5e1e0e50cd1a2561b854ae5490ff589d065 axios-axios-aa54a5e/000077500000000000000000000000001526660636600144655ustar00rootroot00000000000000axios-axios-aa54a5e/.devcontainer/000077500000000000000000000000001526660636600172245ustar00rootroot00000000000000axios-axios-aa54a5e/.devcontainer/devcontainer.json000066400000000000000000000007131526660636600226010ustar00rootroot00000000000000{ "name": "axios", "image": "mcr.microsoft.com/devcontainers/javascript-node:1-24-bookworm", "features": { "ghcr.io/devcontainers/features/github-cli:1": {}, }, "postCreateCommand": "npm ci --ignore-scripts", "customizations": { "vscode": { "extensions": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"], "settings": { "eslint.validate": ["javascript"], "editor.formatOnSave": false, }, }, }, } axios-axios-aa54a5e/.github/000077500000000000000000000000001526660636600160255ustar00rootroot00000000000000axios-axios-aa54a5e/.github/CODEOWNERS000066400000000000000000000021721526660636600174220ustar00rootroot00000000000000# CODEOWNERS # # Reviewers required for matching paths. Last-match-wins. # Keep path-scoped rules below the catch-all so sensitive paths override it. # # Single-maintainer caveat: with only one owner, path-scoped rules cannot # enforce a distinct second reviewer. They still (a) surface sensitive-path # changes explicitly in the review UI, and (b) pre-stage scoped ownership # for when additional maintainers are added. # Default owner * @jasonsaayman # Runtime source — shipped to every consumer /lib/ @jasonsaayman /index.js @jasonsaayman /index.d.ts @jasonsaayman /index.d.cts @jasonsaayman # Build / release infrastructure /rollup.config.js @jasonsaayman /package.json @jasonsaayman /package-lock.json @jasonsaayman /.npmrc @jasonsaayman # CI and repo automation /.github/workflows/ @jasonsaayman /.github/CODEOWNERS @jasonsaayman /.github/dependabot.yml @jasonsaayman # Security-critical docs /THREATMODEL.md @jasonsaayman /SECURITY.md @jasonsaayman axios-axios-aa54a5e/.github/FUNDING.yml000066400000000000000000000000451526660636600176410ustar00rootroot00000000000000open_collective: axios github: axios axios-axios-aa54a5e/.github/ISSUE_TEMPLATE.md000066400000000000000000000015711526660636600205360ustar00rootroot00000000000000 ## Describe the issue A clear description of the bug or feature request. ## Example code ```js ``` ## Expected behavior What you expected to happen. ## Actual behavior What actually happened (include error messages or stack traces if relevant). ## Environment - Axios version: - Adapter: - Runtime: - OS: - Additional context (framework, bundler, proxy, etc.): axios-axios-aa54a5e/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000051651526660636600216350ustar00rootroot00000000000000 ## Summary ## Linked issue ## Changes - ## Validation - ## Compatibility and documentation #### Checklist - [ ] Scope, target branch, and linked issue/rationale checked - [ ] `PRE_RELEASE_CHANGELOG.md` entry added or updated (or AGENTS.md's automated release-preparation exception explained) - [ ] Regression tests and relevant negative/platform cases covered (or N/A with reason) - [ ] Applicable local checks completed, with actual commands and results recorded above - [ ] Deferred release docs and ESM/CJS types/tests updated where needed (or N/A with reason) - [ ] Semver impact stated and intentional breaking changes explained - [ ] Final diff reviewed for security, unrelated changes, generated artifacts, and test/debug leftovers - [ ] Latest GitHub Actions status recorded separately from review/security bot results axios-axios-aa54a5e/.github/copilot-instructions.md000066400000000000000000000137471526660636600225760ustar00rootroot00000000000000# GitHub Copilot Instructions The canonical contributor guide for this repo is [`AGENTS.md`](../AGENTS.md). It covers setup, commands, package shape, architecture boundaries, naming, error handling, interceptor order, the request lifecycle, cancellation, common pitfalls, tests, and security-sensitive code. The rules below are a Copilot-facing subset of the load-bearing safety guarantees from `AGENTS.md`. If they ever drift, `AGENTS.md` is authoritative — update both. ## Setup safety - Install with `npm ci`; the repo's `.npmrc` sets `ignore-scripts=true`. Do not remove that flag. If husky hooks are needed after a fresh install, run `npm rebuild husky && npx husky` once. - Do not add new runtime dependencies without discussion. `package-lock.json` is verified by `lockfile-lint` for npm HTTPS hosts and integrity hashes. - Package, lockfile, and GitHub Actions update PRs are maintainer/bot-only; close these PRs from outside collaborators. Keep the 7-day Dependabot delay unless a critical vulnerability requires a maintainer-led manual update. ## Pull requests - Apply the existing `ai-assisted` repository label to pull requests created or updated with AI assistance, including AI-generated descriptions. Do not add a surfer emoji to PR titles or bodies. If you cannot apply labels, report that a maintainer needs to apply `ai-assisted`. ## Architecture in one screen - `lib/core/` — domain logic: `Axios`, `AxiosError`, `AxiosHeaders`, `InterceptorManager`, config merge, request dispatch. - `lib/adapters/` — I/O: `xhr.js`, `http.js`, `fetch.js`. Default preference `['xhr', 'http', 'fetch']`, picked by capability detection in `lib/adapters/adapters.js`. Never branch on environment name. - `lib/platform/` — Node by default; browser builds alias to `lib/platform/browser`. - `lib/helpers/` — generic, reusable utilities; no axios-specific lifecycle logic here. - Source is ESM (`type: module`) with explicit `.js` import extensions. `dist/` is generated by Rollup — never edit it by hand. Keep `index.d.ts` (ESM) and `index.d.cts` (CJS) in sync for any public API change. ## Error handling - Throw `AxiosError(message, code, config, request, response)` for axios-originated failures; never raw `Error`. - Wrap third-party errors with `AxiosError.from(error, code, config, request, response)`. - Use a code from `lib/core/AxiosError.js` (`ERR_NETWORK`, `ETIMEDOUT`, `ECONNABORTED`, `ERR_CANCELED`, `ERR_BAD_REQUEST`, `ERR_BAD_RESPONSE`, `ERR_FR_TOO_MANY_REDIRECTS`, `ERR_FORM_DATA_DEPTH_EXCEEDED`, `ERR_INVALID_URL`, `ERR_BAD_OPTION`, `ERR_BAD_OPTION_VALUE`, `ERR_NOT_SUPPORT`, `ERR_DEPRECATED`, `ECONNREFUSED`). ## Interceptor order - Request interceptors run **LIFO** (last-registered-first); response interceptors run **FIFO**. Both support `synchronous: true` and `runWhen(config)`. ## Naming and style - PascalCase classes (`Axios`, `AxiosHeaders`), camelCase functions (`buildURL`, `mergeConfig`), UPPER_SNAKE_CASE error codes. - Internal slots use `Symbol`-keyed properties (see `$internals` in `lib/core/AxiosHeaders.js`), not underscore-prefixed names. - Use `lib/helpers/bind.js`, not `Function.prototype.bind`, when binding library functions — the helper forwards `arguments` via `apply`. - `'use strict';` appears at the top of files that already use it; match the surrounding file rather than blanket-adding it. ## Security guarantees that must not regress - For config reads on potentially untrusted input, use own-property checks (`utils.hasOwnProp` / local `own()` helpers); never `in`, destructuring, or direct `config.foo` access. - Any merge or object materialization must continue to filter `__proto__`, `constructor`, and `prototype`. Regressions here are security bugs. - Changes touching URL construction, redirects, proxy/env handling, XSRF, socket paths, decompression limits, or adapters require consulting `THREATMODEL.md` and adding focused regression tests. - `withXSRFToken === true` is the only thing that forces cross-origin XSRF header attachment — keep that behavior explicit. - Do not weaken `beforeRedirect`, proxy, or `socketPath` safeguards without tests covering credential leakage and SSRF-style cases. ## Common pitfalls - Do not mutate config objects in place; return new ones from merges/transforms. - Do not assume browser- or Node-only globals exist; capability-check first. - Validate options through the existing `validator` helper rather than inventing ad-hoc validation paths. ## Pre-release tracking - Every PR MUST add or update `PRE_RELEASE_CHANGELOG.md`, including documentation, tests, internal refactors, tooling, and CI changes, except for the narrow [automated release-preparation exception in `AGENTS.md`](../AGENTS.md#pre-release-notes). An unchanged public API is not an exemption. Use the Maintenance section for test-only changes, internal refactors, tooling, dependency updates, and CI changes without a user-visible behavior change. Explain the final change, rationale, and compatibility impact; PR descriptions do not replace the entry. `CHANGELOG.md` remains release-owned. - Track deferred README, docs site, examples, migration guide, and translated docs updates in `PRE_RELEASE_DOCS.md`; do not update release docs for unreleased runtime/API changes unless explicitly doing release preparation. ## Before submitting a pull request - Complete the mandatory [PR submission requirements in `AGENTS.md`](../AGENTS.md#before-opening-or-updating-a-pull-request) and the PR template before requesting maintainer review: scope/base, prerelease entry, regression coverage, applicable local checks, compatibility, docs/types, security, and final diff review. - Report the commands actually run, results, and runtime versions. Explain genuinely inapplicable checks; keep required-but-blocked local validation in a draft PR. Do not claim that planned checks passed. - After opening or updating the PR, inspect GitHub Actions for the latest commit. Review/security bots do not replace CI; report pending or approval-required runs accurately and resolve relevant failures before reporting the PR ready to merge. axios-axios-aa54a5e/.github/dependabot.yml000066400000000000000000000026531526660636600206630ustar00rootroot00000000000000version: 2 # Package and GitHub Actions update PRs are maintainer/bot-only. # Keep the 7-day cooldown unless a critical vulnerability requires a # maintainer-led manual update. updates: - package-ecosystem: 'github-actions' directory: '/' groups: github-actions: patterns: - '*' labels: - 'commit::chore' - 'type::automated-pr' schedule: interval: 'weekly' cooldown: default-days: 7 - package-ecosystem: 'npm' directory: '/' schedule: interval: 'weekly' open-pull-requests-limit: 5 groups: production_dependencies: dependency-type: 'production' update-types: - 'minor' - 'patch' development_dependencies: dependency-type: 'development' update-types: - 'minor' - 'patch' ignore: - dependency-name: '*' update-types: ['version-update:semver-major'] labels: - 'commit::chore' - 'type::automated-pr' cooldown: default-days: 7 # Give security updates in nested packages the same custom labels. # Keep these packages limited to security updates. - package-ecosystem: 'npm' directories: - '/docs' - '/tests/module/*' - '/tests/smoke/*' schedule: interval: 'weekly' open-pull-requests-limit: 0 labels: - 'commit::chore' - 'type::automated-pr' cooldown: default-days: 7 axios-axios-aa54a5e/.github/workflows/000077500000000000000000000000001526660636600200625ustar00rootroot00000000000000axios-axios-aa54a5e/.github/workflows/bundle-size.yml000066400000000000000000000025431526660636600230320ustar00rootroot00000000000000name: Bundle Size on: pull_request: types: - opened - synchronize - reopened permissions: contents: read jobs: bundle-size: runs-on: ubuntu-latest steps: - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup node uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: 26.x registry-url: 'https://registry.npmjs.org' package-manager-cache: false - name: Install dependencies run: npm ci --ignore-scripts - name: Build project run: npm run build - name: Compare bundle size uses: axios/bundle-size@6940d767de28b002599d1ab36cc04748de2c49e7 # 0.2.0 with: path: '.' package-name: axios files: | dist/axios.js dist/axios.min.js dist/browser/axios.cjs dist/esm/axios.js dist/esm/axios.min.js dist/node/axios.cjs output-file: bundle-size-comparison.json release-stream: '1' - name: Add bundle size report to summary if: always() && hashFiles('bundle-size-comparison.md') != '' run: cat bundle-size-comparison.md >> "$GITHUB_STEP_SUMMARY" axios-axios-aa54a5e/.github/workflows/lockfile-lint.yml000066400000000000000000000026431526660636600233460ustar00rootroot00000000000000name: Lockfile lint on: pull_request: paths: - 'package.json' - 'package-lock.json' - '.github/workflows/lockfile-lint.yml' push: branches: [v1.x] paths: - 'package.json' - 'package-lock.json' permissions: contents: read jobs: lockfile-lint: name: Validate package-lock.json runs-on: ubuntu-latest steps: - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup node uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: 26.x registry-url: 'https://registry.npmjs.org' package-manager-cache: false - name: Run lockfile-lint # Validates that every resolved URL uses HTTPS on registry.npmjs.org # and that every entry carries an integrity hash. Catches swap to a # mirror, a git/file: URL, or integrity stripping on a dep-update PR. # Pinned by name only (no lockfile-lint in devDependencies) so that a # compromised dev tree cannot suppress this check. run: > npx --yes lockfile-lint@4.14.0 --type npm --path package-lock.json --validate-https --allowed-hosts npm --validate-integrity --validate-package-names --empty-hostname false axios-axios-aa54a5e/.github/workflows/moderator.yml000066400000000000000000000015741526660636600226100ustar00rootroot00000000000000name: AI Moderator on: issues: types: [opened] issue_comment: types: [created] pull_request_review_comment: types: [created] jobs: spam-detection: name: AI Moderator runs-on: ubuntu-latest permissions: issues: write pull-requests: write models: read contents: read steps: - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Run AI Moderator uses: github/ai-moderator@81159c370785e295c97461ade67d7c33576e9319 # v1.1.4 with: token: ${{ secrets.GITHUB_TOKEN }} spam-label: 'spam' ai-label: 'ai-generated' minimize-detected-comments: true enable-spam-detection: true enable-link-spam-detection: true enable-ai-detection: true axios-axios-aa54a5e/.github/workflows/publish.yml000066400000000000000000000014751526660636600222620ustar00rootroot00000000000000name: Publish package to NPM on: push: tags: - 'v1.*.*' permissions: contents: read id-token: write jobs: publish: runs-on: ubuntu-latest environment: npm-publish steps: - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup Node.js uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: 26.x registry-url: 'https://registry.npmjs.org' package-manager-cache: false - name: Install dependencies run: npm ci --ignore-scripts - name: Build project run: npm run build - name: Publish to NPM run: npm stage publish --provenance --access public axios-axios-aa54a5e/.github/workflows/release-branch.yml000066400000000000000000000260641526660636600234700ustar00rootroot00000000000000name: Create release branch on: workflow_dispatch: inputs: type: type: choice description: Choose release type options: - patch - minor - major default: patch beta: type: boolean description: Prerelease default: false permissions: contents: read jobs: build-and-run-vitest: name: Build and run vitest runs-on: ubuntu-latest steps: - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup node uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: 26.x registry-url: 'https://registry.npmjs.org' package-manager-cache: false - name: Install dependencies run: npm ci --ignore-scripts - name: Build project run: npm run build - name: Install Playwright with deps run: npx playwright install --with-deps - name: Run unit tests run: npm run test:vitest:unit - name: Run browser tests run: npm run test:vitest:browser:headless - name: Pack npm tarball run: npm pack - name: Upload npm pack artifact uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: axios-tarball path: axios-*.tgz if-no-files-found: error retention-days: 1 cjs-smoke-tests: name: CJS smoke tests (Node ${{ matrix.node-version }}) needs: build-and-run-vitest runs-on: ubuntu-latest strategy: fail-fast: false matrix: node-version: [12, 14, 16, 18] steps: - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup node uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: ${{ matrix.node-version }} registry-url: 'https://registry.npmjs.org' package-manager-cache: false - name: Download npm pack artifact uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: axios-tarball path: artifacts - name: Install CJS smoke test dependencies working-directory: tests/smoke/cjs run: npm install --ignore-scripts - name: Install packed axios working-directory: tests/smoke/cjs run: npm install --no-save ../../../artifacts/axios-*.tgz - name: Run CJS smoke tests working-directory: tests/smoke/cjs run: npm run test:smoke:cjs:mocha esm-smoke-tests: name: ESM smoke tests (Node ${{ matrix.node-version }}) needs: build-and-run-vitest runs-on: ubuntu-latest strategy: fail-fast: false matrix: node-version: [20, 22, 24, 26] steps: - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup node uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: ${{ matrix.node-version }} registry-url: 'https://registry.npmjs.org' package-manager-cache: false - name: Download npm pack artifact uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: axios-tarball path: artifacts - name: Install ESM smoke test dependencies working-directory: tests/smoke/esm run: npm install --ignore-scripts - name: Install packed axios working-directory: tests/smoke/esm run: npm install --no-save ../../../artifacts/axios-*.tgz - name: Run ESM smoke tests working-directory: tests/smoke/esm run: npm run test:smoke:esm:vitest cjs-module-tests: name: CJS module tests (Node ${{ matrix.node-version }}) needs: build-and-run-vitest runs-on: ubuntu-latest strategy: fail-fast: false matrix: node-version: [12, 14, 16, 18] steps: - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup node uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: ${{ matrix.node-version }} registry-url: 'https://registry.npmjs.org' package-manager-cache: false - name: Download npm pack artifact uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: axios-tarball path: artifacts - name: Install CJS module test dependencies working-directory: tests/module/cjs run: npm install --ignore-scripts - name: Install packed axios working-directory: tests/module/cjs run: npm install --no-save ../../../artifacts/axios-*.tgz - name: Run CJS module tests working-directory: tests/module/cjs run: npm run test:module:cjs esm-module-tests: name: ESM module tests (Node ${{ matrix.node-version }}) needs: build-and-run-vitest runs-on: ubuntu-latest strategy: fail-fast: false matrix: node-version: [20, 22, 24, 26] steps: - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup node uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: ${{ matrix.node-version }} registry-url: 'https://registry.npmjs.org' package-manager-cache: false - name: Download npm pack artifact uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: axios-tarball path: artifacts - name: Install ESM module test dependencies working-directory: tests/module/esm run: npm install --ignore-scripts - name: Install packed axios working-directory: tests/module/esm run: npm install --no-save ../../../artifacts/axios-*.tgz - name: Run ESM module tests working-directory: tests/module/esm run: npm run test:module:esm bun-smoke-tests: name: Bun smoke tests needs: build-and-run-vitest runs-on: ubuntu-latest steps: - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup bun uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 with: no-cache: true - name: Download npm pack artifact uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: axios-tarball path: artifacts - name: Install packed axios env: TMPDIR: ${{ runner.temp }} BUN_INSTALL_CACHE_DIR: ${{ runner.temp }}/bun-cache run: | mkdir -p "$BUN_INSTALL_CACHE_DIR" mv artifacts/axios-*.tgz artifacts/axios.tgz cd tests/smoke/bun bun add file:../../../artifacts/axios.tgz - name: Run Bun smoke tests working-directory: tests/smoke/bun run: bun test deno-smoke-tests: name: Deno smoke tests needs: build-and-run-vitest runs-on: ubuntu-latest steps: - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup deno uses: denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed # v2.0.5 with: cache: false - name: Download npm pack artifact uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: axios-tarball path: artifacts - name: Prepare packed axios dist run: mkdir -p dist && tar -xzf artifacts/axios-*.tgz -C artifacts && cp -R artifacts/package/dist/. ./dist - name: Install Deno smoke test dependencies working-directory: tests/smoke/deno run: deno install - name: Run Deno smoke tests working-directory: tests/smoke/deno run: deno task test bump-version-and-create-pr: name: Bump version and create PR needs: [build-and-run-vitest, cjs-smoke-tests, esm-smoke-tests, bun-smoke-tests, deno-smoke-tests] runs-on: ubuntu-latest permissions: contents: write pull-requests: write steps: - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup Node.js uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: 26.x registry-url: 'https://registry.npmjs.org' package-manager-cache: false - name: Install dependencies run: npm ci --ignore-scripts - name: Configure git identity run: | git config --global user.name "github-actions[bot]" git config --global user.email "github-actions[bot]@users.noreply.github.com" - name: Bump version with NPM version env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} INPUT_TYPE: ${{ github.event.inputs.type }} INPUT_BETA: ${{ github.event.inputs.beta }} id: bump-version run: | TYPE="${INPUT_TYPE}" BETA="${INPUT_BETA}" if [ "$TYPE" = "auto" ]; then npm version $(npm version | grep -Eo 'patch|minor|major' | head -1) else if [ "$BETA" = "true" ]; then npm version $TYPE --preid=beta else npm version $TYPE fi fi VERSION=$(node -p "require('./package.json').version") echo "newTag=$VERSION" >> "$GITHUB_OUTPUT" echo "newBranch=release-v1x/version-$VERSION" >> "$GITHUB_OUTPUT" - name: Run preversion env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: npm run preversion - name: Build project run: npm run build - name: Create Pull Request uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 with: branch: ${{ steps.bump-version.outputs.newBranch }} base: v1.x commit-message: 'chore(release): prepare release ${{ steps.bump-version.outputs.newTag }}' body: 'This PR prepares the release ${{ steps.bump-version.outputs.newTag }}.' title: 'chore(release): prepare release ${{ steps.bump-version.outputs.newTag }}' maintainer-can-modify: true draft: false labels: | type::automated-pr commit::chore axios-axios-aa54a5e/.github/workflows/run-ci.yml000066400000000000000000000210441526660636600220030ustar00rootroot00000000000000name: Continuous integration on: pull_request: types: [opened, synchronize, reopened] permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: build-and-run-vitest: name: Build and run vitest runs-on: ubuntu-latest steps: - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup node uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: 26.x registry-url: 'https://registry.npmjs.org' package-manager-cache: false - name: Install dependencies run: npm ci --ignore-scripts - name: Lint run: npm run lint - name: Build project run: npm run build - name: Install Playwright with deps run: npx playwright install --with-deps - name: Run unit tests run: npm run test:vitest:unit - name: Run browser tests run: npm run test:vitest:browser:headless - name: Pack npm tarball run: npm pack - name: Dependency Review uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 - name: Upload npm pack artifact uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: axios-tarball path: axios-*.tgz if-no-files-found: error retention-days: 1 cjs-smoke-tests: name: CJS smoke tests (Node ${{ matrix.node-version }}) needs: build-and-run-vitest runs-on: ubuntu-latest strategy: fail-fast: false matrix: node-version: [12, 14, 16, 18] steps: - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup node uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: ${{ matrix.node-version }} registry-url: 'https://registry.npmjs.org' package-manager-cache: false - name: Download npm pack artifact uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: axios-tarball path: artifacts - name: Install CJS smoke test dependencies working-directory: tests/smoke/cjs run: npm install --ignore-scripts - name: Install packed axios working-directory: tests/smoke/cjs run: npm install --no-save ../../../artifacts/axios-*.tgz - name: Run CJS smoke tests working-directory: tests/smoke/cjs run: npm run test:smoke:cjs:mocha esm-smoke-tests: name: ESM smoke tests (Node ${{ matrix.node-version }}) needs: build-and-run-vitest runs-on: ubuntu-latest strategy: fail-fast: false matrix: node-version: [20, 22, 24, 26] steps: - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup node uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: ${{ matrix.node-version }} registry-url: 'https://registry.npmjs.org' package-manager-cache: false - name: Download npm pack artifact uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: axios-tarball path: artifacts - name: Install ESM smoke test dependencies working-directory: tests/smoke/esm run: npm ci --ignore-scripts - name: Install packed axios working-directory: tests/smoke/esm run: npm install --no-save ../../../artifacts/axios-*.tgz - name: Run ESM smoke tests working-directory: tests/smoke/esm run: npm run test:smoke:esm:vitest cjs-module-tests: name: CJS module tests (Node ${{ matrix.node-version }}) needs: build-and-run-vitest runs-on: ubuntu-latest strategy: fail-fast: false matrix: node-version: [12, 14, 16, 18] steps: - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup node uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: ${{ matrix.node-version }} registry-url: 'https://registry.npmjs.org' package-manager-cache: false - name: Download npm pack artifact uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: axios-tarball path: artifacts - name: Install CJS module test dependencies working-directory: tests/module/cjs run: npm install --ignore-scripts - name: Install packed axios working-directory: tests/module/cjs run: npm install --no-save ../../../artifacts/axios-*.tgz - name: Run CJS module tests working-directory: tests/module/cjs run: npm run test:module:cjs esm-module-tests: name: ESM module tests (Node ${{ matrix.node-version }}) needs: build-and-run-vitest runs-on: ubuntu-latest strategy: fail-fast: false matrix: node-version: [20, 22, 24, 26] steps: - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup node uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: ${{ matrix.node-version }} registry-url: 'https://registry.npmjs.org' package-manager-cache: false - name: Download npm pack artifact uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: axios-tarball path: artifacts - name: Install ESM module test dependencies working-directory: tests/module/esm run: npm install --ignore-scripts - name: Install packed axios working-directory: tests/module/esm run: npm install --no-save ../../../artifacts/axios-*.tgz - name: Run ESM module tests working-directory: tests/module/esm run: npm run test:module:esm bun-smoke-tests: name: Bun smoke tests needs: build-and-run-vitest runs-on: ubuntu-latest steps: - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup bun uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 with: no-cache: true - name: Download npm pack artifact uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: axios-tarball path: artifacts - name: Install packed axios env: TMPDIR: ${{ runner.temp }} BUN_INSTALL_CACHE_DIR: ${{ runner.temp }}/bun-cache run: | mkdir -p "$BUN_INSTALL_CACHE_DIR" mv artifacts/axios-*.tgz artifacts/axios.tgz cd tests/smoke/bun bun add file:../../../artifacts/axios.tgz - name: Run Bun smoke tests working-directory: tests/smoke/bun run: bun test deno-smoke-tests: name: Deno smoke tests needs: build-and-run-vitest runs-on: ubuntu-latest steps: - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup deno uses: denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed # v2.0.5 with: cache: false - name: Download npm pack artifact uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 with: name: axios-tarball path: artifacts - name: Prepare packed axios dist run: mkdir -p dist && tar -xzf artifacts/axios-*.tgz -C artifacts && cp -R artifacts/package/dist/. ./dist - name: Install Deno smoke test dependencies working-directory: tests/smoke/deno run: deno install - name: Run Deno smoke tests working-directory: tests/smoke/deno run: deno task test axios-axios-aa54a5e/.github/workflows/verify-build-reproducibility.yml000066400000000000000000000050041526660636600264140ustar00rootroot00000000000000name: Verify build reproducibility on: pull_request: paths: - 'lib/**' - 'rollup.config.js' - 'package.json' - 'package-lock.json' - '.github/workflows/verify-build-reproducibility.yml' push: branches: [v1.x] permissions: contents: read jobs: verify-reproducible-build: name: Two-pass build and diff runs-on: ubuntu-latest # Non-blocking until divergence is eliminated. Surfaces regressions in # the build's determinism without gating merges. Remove this line to # promote to a hard gate once the build is byte-identical across runs. continue-on-error: true steps: - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Setup node uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: node-version: 26.x registry-url: 'https://registry.npmjs.org' package-manager-cache: false - name: Install (pass 1) run: npm ci --ignore-scripts - name: Build (pass 1) run: npm run build - name: Snapshot pass 1 run: | mv dist dist-pass1 find dist-pass1 -type f -exec sha256sum {} + | sort -k2 > pass1.sha256 echo "--- pass 1 hashes ---" cat pass1.sha256 - name: Clean and reinstall (pass 2) run: | rm -rf node_modules npm ci --ignore-scripts - name: Build (pass 2) run: npm run build - name: Snapshot pass 2 and diff run: | find dist -type f -exec sha256sum {} + | sort -k2 | sed 's| dist/| dist-pass1/|' > pass2.sha256 echo "--- pass 2 hashes (path-normalised) ---" cat pass2.sha256 if ! diff -u pass1.sha256 pass2.sha256; then echo "::warning::Build is not reproducible — dist/ differs between passes." echo "This does not fail the job (continue-on-error: true) but is visible in the run summary." echo "See THREATMODEL.md §T-S5 for context." exit 1 fi echo "Build is byte-identical across passes." - name: Upload diff artifact on divergence if: failure() uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: name: reproducibility-diff path: | pass1.sha256 pass2.sha256 dist-pass1 dist retention-days: 7 axios-axios-aa54a5e/.github/workflows/zizmor.yml000066400000000000000000000010041526660636600221320ustar00rootroot00000000000000name: GitHub Actions Security Analysis with zizmor on: push: branches: [v1.x] pull_request: branches: ['**'] permissions: {} jobs: zizmor: runs-on: ubuntu-latest permissions: security-events: write steps: - name: Checkout repo uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Run zizmor uses: zizmorcore/zizmor-action@cc914d7f3750a2d13d75c7f184a1060aa0e9d482 # v0.6.4 axios-axios-aa54a5e/.gitignore000066400000000000000000000003671526660636600164630ustar00rootroot00000000000000*.iml .idea .tscache .DS_Store node_modules/ coverage/ temp/ test/typescript/axios.js* sauce_connect.log test/module/**/package-lock.json backup/ .env dist/ .vscode/ docs/.vitepress/dist docs/.vitepress/cache .claude/ openspec/ .opencode/ .codex/ axios-axios-aa54a5e/.husky/000077500000000000000000000000001526660636600157065ustar00rootroot00000000000000axios-axios-aa54a5e/.husky/commit-msg000077500000000000000000000000331526660636600177040ustar00rootroot00000000000000npx commitlint --edit "$1" axios-axios-aa54a5e/.npmrc000066400000000000000000000000241526660636600156010ustar00rootroot00000000000000ignore-scripts=true axios-axios-aa54a5e/.prettierignore000066400000000000000000000003351526660636600175310ustar00rootroot00000000000000# Dependencies node_modules/ # Build output dist/ coverage/ # Lock files package-lock.json **/package-lock.json # Generated / vendored *.min.js test/typescript/axios.js* # Misc *.iml .idea .DS_Store sauce_connect.log axios-axios-aa54a5e/.prettierrc000066400000000000000000000001511526660636600166460ustar00rootroot00000000000000{ "semi": true, "singleQuote": true, "tabWidth": 2, "trailingComma": "es5", "printWidth": 100 }axios-axios-aa54a5e/AGENTS.md000066400000000000000000000352351526660636600160000ustar00rootroot00000000000000# AGENTS.md axios is a promise-based HTTP client for the browser and Node.js. The default instance is exported from `lib/axios.js` via `index.js`. Browser builds use the XHR or Fetch adapter; Node uses the HTTP/HTTPS adapter. Platform selection lives in `lib/platform/`. This file is the canonical contributor guide for both human and AI agents working in this repo. `.github/copilot-instructions.md` is a thin stub that points back here — keep it in sync with the load-bearing safety rules below if you change them. ## Setup And Safety - Use `npm ci`; repo `.npmrc` sets `ignore-scripts=true`, and CI also uses `npm ci --ignore-scripts`. - Do not remove `ignore-scripts=true`; if git hooks are needed after a fresh install, run `npm rebuild husky && npx husky` once. - Adding or updating dependencies is security-sensitive; `package-lock.json` is checked by `lockfile-lint` for npm HTTPS hosts and integrity hashes. - Package, lockfile, and GitHub Actions update PRs are maintainer/bot-only; close these PRs from outside collaborators. Keep the 7-day Dependabot delay unless a critical vulnerability requires a maintainer-led manual update. - Build/test/lint tools still execute dependency code despite `ignore-scripts`; run the validation required by the PR submission checklist below and avoid unrelated runtime checks for documentation-only changes. - Do not add new runtime dependencies without discussion; the dependency surface is intentionally tiny. ## Pull Requests - Apply the existing `ai-assisted` repository label to pull requests created or updated with AI assistance, including AI-generated descriptions. Use `gh pr create --label ai-assisted` when opening a PR or `gh pr edit --add-label ai-assisted` for an existing PR. - Do not add a surfer emoji to PR titles or bodies to indicate AI assistance. If you cannot apply labels, report that a maintainer needs to apply `ai-assisted`. ## Commands - Build published artifacts: `npm run build` (`gulp clear` deletes `dist/`, then Rollup writes browser ESM/UMD/CJS and Node CJS bundles). - Lint source only: `npm run lint`; focused lint: `npx eslint lib/path/to/file.js`. - Unit tests: `npm run test:vitest:unit`; focused unit test: `npm run test:vitest:unit -- tests/unit/path.test.js`. - Browser tests need Playwright installed first (`npx playwright install` locally; CI uses `npx playwright install --with-deps`); run `npm run test:vitest:browser:headless` for CI parity. - Smoke/module compatibility suites test the packed package, not the source tree: run `npm run build`, `npm pack`, install the tarball into the relevant `tests/smoke/*` or `tests/module/*` package, then run that suite's npm script. - CI order is install -> lint -> build -> Playwright install -> unit -> browser headless -> pack -> CJS/ESM module and smoke tests -> Bun/Deno smoke tests. ## Package Shape - Source is ESM (`type: module`); public ESM entry is `index.js`, which re-exports the default instance from `lib/axios.js`. - Do not edit `dist/` by hand; it is ignored and generated from `lib/` by Rollup. - Runtime package exports are split by environment: browser/react-native map Node HTTP/platform files to browser/null replacements, while Node CJS ships as `dist/node/axios.cjs`. - Keep public runtime exports, `index.d.ts` (ESM types), and `index.d.cts` (CJS `export = axios` types) in sync for API changes. - `lib/env/data.js` is version-generated by `gulp version`; do not edit it for normal feature work. ## Pre-Release Notes - Every PR MUST add or update an entry in `PRE_RELEASE_CHANGELOG.md`, except for the automated release-preparation PRs described below. This includes runtime fixes, features, internal refactors, tests, documentation, tooling, and CI changes. An unchanged public API or a documentation-only diff is not a reason to omit the entry. - Use **Features** for new capabilities, **Bug Fixes** for behavior corrections, **Documentation** for documentation changes, and **Maintenance** for test-only changes, internal refactors, tooling, dependency updates, and CI changes without a user-visible behavior change. Explain the final change, why it matters, and any compatibility implications. Update the existing entry when revising the same PR instead of adding duplicates. Link the issue and PR when their numbers are known; never invent a reference. - **Automated release-preparation exception:** PRs created by [the release-branch workflow](.github/workflows/release-branch.yml) that contain only generated version, package metadata, lockfile, and build changes may omit a new prerelease entry. They prepare changes already recorded by the contributing PRs. If additional fixes, features, or maintenance changes are added, those changes still require an entry. This exception only covers the prerelease-entry requirement; maintainers must complete the other applicable submission requirements and release documentation before merge. - `CHANGELOG.md` is release-owned and should only be updated as part of preparing an actual release. A PR body, commit message, or planned release note does not replace the committed prerelease entry. - Track deferred README, docs site, examples, migration guide, and translated docs updates in `PRE_RELEASE_DOCS.md`. Use enough context for release preparation; do not store brittle diffs or line-number-only notes. - Do not update `README.md` or the docs site for unreleased runtime/API changes unless the task is explicitly release preparation. During feature/fix work, record what docs need to say in `PRE_RELEASE_DOCS.md` so it can be applied during release work. ## Before Opening Or Updating A Pull Request These requirements apply to human contributors and AI agents. Complete the applicable work before submitting a PR for maintainer review; a checked box must describe verified work, not an intention. Use `.github/PULL_REQUEST_TEMPLATE.md` and keep its answers accurate when the implementation changes. 1. **Confirm scope and base.** Read this guide, `CONTRIBUTING.md`, and `SECURITY.md`; consult `THREATMODEL.md` for security-sensitive changes. Target the intended supported release branch, link the issue or explain the problem, and review the complete diff against that base. Keep unrelated changes out of the PR. 2. **Commit the prerelease entry.** Follow the mandatory Pre-Release Notes rules above. Confirm that the entry describes the final implementation, including any limitations. Only the automated release-preparation exception above may be marked not applicable; explain why it applies in the PR description. 3. **Prove the behavior.** Bug fixes need a regression test that fails before the fix and passes afterward. Cover relevant negative cases and preserve existing defensive tests. For adapter or platform changes, consider Node, browser, custom-environment, and affected-runtime behavior; a mock should reflect the capability being fixed. Use a safe local runtime reproduction when the ordinary suites cannot establish compatibility. 4. **Run the required local checks.** Install with `npm ci --ignore-scripts`. For runtime changes, run `npm run lint`, `npm run build`, and `npm run test:vitest:unit`; adapter or browser changes also need `npm run test:vitest:browser:headless`. Public type changes require the matching CJS/ESM module tests. Packaging, exports, or build changes require building and testing the packed package with the affected module/smoke suites. Follow the supported runtimes in `.github/workflows/run-ci.yml`. For changes limited to documentation or contributor instructions, check formatting, links, and consistency; explain why runtime checks are not applicable instead of running unrelated suites. 5. **Check compatibility and documentation.** State the semver impact and call out intentional breaking behavior. Keep runtime exports, `index.d.ts`, `index.d.cts`, and their tests aligned when the public API changes. Record deferred release documentation in `PRE_RELEASE_DOCS.md`, or explain why no docs/types update is needed. A bug fix without an API change still requires its prerelease changelog entry. 6. **Review security and hygiene.** Preserve the guards and architecture boundaries below. Inspect the final diff for unrelated dependency or workflow changes, generated bundles, version churn, secrets, debug code, focused tests (`.only`, `fit`, `fdescribe`), and unexplained lint suppressions. Respect the maintainer-only dependency/workflow policy and never edit generated artifacts by hand. 7. **Write an accurate submission.** Use Conventional Commits for the title and commits. Explain the concrete problem, resulting behavior, issue/rationale, and validation. List the commands actually run, their results, and relevant runtime versions. Give a specific reason for each genuinely inapplicable check; distinguish failed or blocked checks from checks that do not apply. Fill in the PR description yourself rather than relying on a bot to reconstruct it. CI runs after a PR is opened. Inspect the actual GitHub Actions checks for the latest commit before reporting the PR ready to merge; review/security bot approvals are not build, test, packaging, or runtime-matrix evidence. Resolve relevant failures and report pending or approval-required runs accurately. If required local checks are blocked, keep the PR as a draft and describe the blocker and remaining validation. After further code changes, rerun the affected checks and update the PR's evidence. ## Architecture Boundaries - `lib/core/` is axios domain logic: request dispatch, config merge, interceptors, headers, errors. Key classes: `Axios` (request dispatch + interceptor chains), `AxiosError` (standardized error codes), `AxiosHeaders` (case-insensitive header normalization), `InterceptorManager` (sync/async interceptor registration). - `lib/adapters/` performs I/O; default adapter preference is `['xhr', 'http', 'fetch']`, with capability selection in `lib/adapters/adapters.js`. Detect by capability, not environment name. - `lib/platform/` selects Node by default; browser builds rely on package/rollup aliasing to `lib/platform/browser`. - `lib/helpers/` should stay generic and reusable outside axios; do not put axios-specific request lifecycle logic there. - New `lib/**/*.js` files should match existing source style: ESM imports with explicit `.js` extensions, `'use strict';` where current library files use it, and `AxiosError` for axios-originated failures. ## Naming Conventions - Classes: PascalCase (`Axios`, `AxiosError`, `InterceptorManager`). - Functions: camelCase (`buildURL`, `mergeConfig`, `dispatchRequest`). - Error codes: UPPER_SNAKE_CASE constants on `AxiosError` (`ERR_NETWORK`, `ETIMEDOUT`). - Internal class slots: `Symbol`-keyed (e.g. `const $internals = Symbol('internals')` in `lib/core/AxiosHeaders.js`) rather than underscore-prefixed properties. ## Error Handling - Throw `AxiosError` for axios-originated failures; never raw `Error`. Pass `(message, code, config, request, response)` so consumers can introspect. - Wrap third-party errors with `AxiosError.from(error, code, config, request, response)`. - Canonical code list lives in `lib/core/AxiosError.js`; current codes include `ERR_BAD_OPTION_VALUE`, `ERR_BAD_OPTION`, `ECONNABORTED`, `ETIMEDOUT`, `ECONNREFUSED`, `ERR_NETWORK`, `ERR_FR_TOO_MANY_REDIRECTS`, `ERR_DEPRECATED`, `ERR_BAD_RESPONSE`, `ERR_BAD_REQUEST`, `ERR_CANCELED`, `ERR_NOT_SUPPORT`, `ERR_INVALID_URL`, `ERR_FORM_DATA_DEPTH_EXCEEDED`. - Validate config options through the `validator` helper; do not invent ad-hoc validation paths. ## Interceptor Execution Order - Request interceptors run **last-registered-first** (LIFO) by default. Setting `transitional.legacyInterceptorReqResOrdering: false` switches them to **first-registered-first** (FIFO). - Response interceptors run **first-registered-first** (FIFO). - Only request interceptors support `synchronous: true` (avoids Promise wrapping when no async request handler is in the chain) and `runWhen: (config) => boolean` for conditional execution. Response interceptors run through the Promise chain and do not use these options. - Order matters for both behavior and tests; document it when adding new built-in interceptors. ## Request Lifecycle 1. User calls `axios()` or a method alias. 2. Merge instance defaults with request config via `mergeConfig`. 3. Run request interceptors (LIFO by default; FIFO with `transitional.legacyInterceptorReqResOrdering: false`). 4. Apply `transformRequest` functions. 5. Select adapter via `lib/adapters/adapters.js` capability check. 6. Adapter performs the HTTP request. 7. Apply `transformResponse` functions. 8. Run response interceptors (FIFO). 9. Resolve promise with `AxiosResponse` or reject with `AxiosError`. ## Cancellation - Both `CancelToken` (legacy) and `AbortSignal` (modern) are supported simultaneously; do not break either path. - Cancellation must work at any lifecycle stage, including mid-flight body reads. - Always remove signal listeners on settlement or cancellation to prevent memory leaks. ## Common Pitfalls - Do not mutate config objects in-place; return new objects from merges/transforms. - Do not assume browser- or Node-specific globals exist; capability-check first. - Do not use `Function.prototype.bind` directly — use `lib/helpers/bind.js`, which forwards `arguments` via `apply` and is what the rest of the library relies on. - Do not throw raw `Error` from library code; use `AxiosError` with an appropriate code (see Error Handling). ## Tests - Test layout is runtime-first: `tests/unit/**/*.test.js`, `tests/browser/**/*.browser.test.js`, `tests/smoke/esm/**/*.smoke.test.js`, `tests/smoke/cjs/**/*.smoke.test.cjs`. - Use `tests/setup/server.js` for local HTTP servers and cleanup with `try/finally`; leaking servers causes Vitest hangs. - Keep CJS and ESM smoke coverage aligned when behavior is packaging/import related. - Type compatibility is exercised through `tests/module/cjs` with TypeScript 4.9 and `tests/module/esm` with TypeScript 5.x; run the matching module suite for declaration changes. - Browser tests replace globals such as XHR; restore globals and reset spies in cleanup hooks. ## Security-Sensitive Code - For config reads that affect behavior, do not use prototype-walking reads (`in`, destructuring, or direct `config.foo` on untrusted config); guard with own-property checks as in `utils.hasOwnProp` / local `own()` helpers. - New merge or object materialization code must continue filtering `__proto__`, `constructor`, and `prototype`; regressions here are security bugs. - Changes touching URL construction, redirects, proxy/env handling, XSRF, socket paths, decompression limits, or adapters should consult `THREATMODEL.md` and add focused regression tests. - Keep `withXSRFToken` cross-origin behavior explicit: only `true` forces cross-origin XSRF header attachment. - Do not weaken `beforeRedirect`, proxy, or `socketPath` safeguards without tests covering credential leakage or SSRF-style cases. axios-axios-aa54a5e/CHANGELOG.md000066400000000000000000004234611526660636600163100ustar00rootroot00000000000000# Changelog ## v1.19.0 — July 22, 2026 This release raises the form-data security floor, adds configuration and type-system capabilities, and fixes NO_PROXY matching, interceptor errors, progress reporting, and serialization edge cases. ## 🔒 Security Fixes - Multipart Form Data: Raised the form-data dependency floor to ^4.0.6, preventing fresh installations from resolving versions affected by the CRLF injection vulnerability GHSA-hmw2-7cc7-3qxx (https://github.com/advisories/GHSA-hmw2-7cc7-3qxx). (#11028) ## 🚀 New Features - Configuration Extensibility: Preserved own-enumerable symbol-keyed fields through mergeConfig and added a generic params type across public TypeScript declarations, responses, errors, adapters, and serializers. (#11043, #11081) - Header Parameter Parsing: Added the opt-in AxiosHeaders.parseParameters() parser for quote-aware, RFC-style HTTP parameter parsing while preserving legacy parsing behavior. (#11051) - HTTP Status Codes: Added the missing Cloudflare 520 WebServerReturnsAnUnknownError status and matching ESM/CJS declarations. (#11067) ## � Bug Fixes - Form Data Conversion: Limited formDataToJSON path splitting to dot and bracket notation, preserving literal punctuation in keys, and removed browser-facing Buffer.from usage from toFormData to avoid unnecessary polyfills. (#11006, #11018) - Proxy Bypass: Canonicalized IPv4 shorthand, octal, and hexadecimal forms during NO_PROXY matching and honored * entries within comma- or space-separated bypass lists. (#11029, #11053) - Cancellation: Propagated already-aborted input signals immediately when composing abort signals. (#11035) - Header Handling: Preserved empty first values for duplicate singleton headers and made AxiosHeaders#getSetCookie() consistently return arrays for present values. (#11036, #11037) - URL Handling: Included normalized, safely redacted offending URLs in malformed-protocol errors and removed repeated trailing slashes when combining base URLs. (#11008, #11038) - Progress Events: Clamped malformed negative progress values to zero and ensured final Node.js download progress events are delivered before streamed responses close. (#11039, #11040) - Error and JSON Serialization: Serialized Set values as arrays in JSON-compatible snapshots and synthesized useful AxiosError messages from otherwise-empty AggregateError instances. (#11044, #11059) - Content-Length Enforcement: Corrected base64 data: URL size estimation so maxContentLength is enforced consistently by the HTTP and Fetch adapters. (#11061) - Synchronous Interceptors: Prevented requests from being dispatched after synchronous request interceptors fail unless their paired rejection handler resolves successfully. (#11071) ## 🔧 Maintenance & Chores - Dependencies: Updated development and test tooling, the docs fixture's Axios version, and GitHub Actions integrations including Checkout, Setup Node, Setup Deno, and Zizmor. (#11031, #11055, #11056, #11058, #11079, #11080, #11088, #11089, #11090) - Build Outputs: Limited sourcemap generation to published minified bundles, removing broken map references from non-minified builds. (#11054) - Form Data Internals: Centralized FormData header handling and made the Node.js adapter tolerate getHeaders() returning undefined under the content-only policy. (#11062) - Developer Experience: Ignored common local AI-tooling directories and fixed a constant-reassignment crash when the development sandbox serves its root path. (#11032, #11073) - Documentation: Updated sponsor information, clarified that baseURL is not a path-security boundary, scoped provenance claims to attested releases, and corrected the configuration-defaults documentation. (#11041, #11068, #11076, #11078) - Publishing: Simplified v1 publishing to use the npm version bundled with Node.js 26 and updated package metadata for the 1.19.0 release. (#11083, #11095) ## 🌟 New Contributors We are thrilled to welcome our new contributors. Thank you for helping improve Axios: - @afonsojramos (#11028) - @MahinAnowar (#11006) - @yassertawfik4 (#11024) - @AnandSundar (#11029) - @lin-hongkuan (#11035) - @Wali007-lab (#11054) - @magicdawn (#11043) - @andrewkernel (#11053) - @Sagargupta16 (#11059) - @Rpaudel379 (#11078) - @kobihikri (#11076) - @spokodev (#11061) - @shaedrich (#11081) - @QodeXcli (#11062) - @akahoshi1421 (#11067) - @TheHonoredOne914 (#11071) - @Ahsan1Murtaza (#11073) [Full Changelog](https://github.com/axios/axios/compare/v1.18.1...v1.19.0) ## v1.18.0 — June 13, 2026 This release hardens redirect and URL handling, improves the validateStatus configuration semantics, and includes updates to documentation, dependencies, and release metadata. ## 🔒 Security Fixes * **Redirect Header Safety:** Added Node HTTP adapter support for stripping caller-specified sensitive headers on cross-origin redirects, helping prevent custom auth headers such as API keys from leaking to another origin. (__#10892__) * **URL And Request Hardening:** Rejects malformed `http:` and `https:` URLs that omit `//` with `ERR_INVALID_URL`, while tightening prototype-pollution-safe config reads, stream size limits, FormData depth handling, data URL sizing, and local `NO_PROXY` matching. (__#11000__) ## � Bug Fixes * **Status Validation:** Added `transitional.validateStatusUndefinedResolves` so applications can opt in to treating `validateStatus: undefined` like the option was omitted, while `validateStatus: null` remains the explicit way to accept every status. (__#10899__) ## 🔧 Maintenance & Chores * **Documentation:** Published the v1.17.0 release notes, fixed a changelog typo, clarified the package update PR policy, and marked the `proxy` request config as Node.js-only in the advanced docs. (__#10984__, __#10988__, __#10992__, __#10995__) * **Dependencies:** Bumped `@babel/core`, `@babel/preset-env`, `@commitlint/cli`, `@commitlint/config-conventional`, `@rollup/plugin-babel`, `@rollup/plugin-commonjs`, `@vitest/browser`, `@vitest/browser-playwright`, `eslint`, `lint-staged`, `rollup`, `vitest`, and `actions/checkout`. (__#10989__, __#10996__, __#10997__) * **Release Metadata:** Prepared the 1.18.0 release by updating package metadata and the runtime `VERSION` value. (__#11003__) ## 🌟 New Contributors We are thrilled to welcome our new contributors. Thank you for helping improve axios: * __@drori12__ (__#10984__) * __@eyupcanakman__ (__#10899__) * __@Adi-Beker__ (__#10995__) [Full Changelog](https://github.com/axios/axios/compare/v1.17.0...v1.18.0) ## v1.17.0 — June 1, 2026 This release adds Node HTTP zstd decompression, hardens config and release workflows, and fixes authentication, header, proxy, and type-handling regressions. ## 🔒 Security Fixes * **Config Hardening:** Guarded `socketPath`, `params`, and `paramsSerializer` reads with own-property checks to prevent inherited prototype values from affecting request behavior, including SSRF-sensitive paths. (__#10901__, __#10922__) * **Release Publishing:** Switched the publish workflow to npm staged publishing for safer, auditable package releases with provenance. (__#10926__) ## 🚀 New Features * **HTTP Compression:** Added Node HTTP adapter support for zstd response decompression, with `transitional.advertiseZstdAcceptEncoding` controlling whether `zstd` is advertised in `Accept-Encoding`. (__#6792__, __#10920__) ## � Bug Fixes * **Authentication Handling:** Restored Basic auth on same-origin Node redirects while continuing to strip credentials cross-origin, and aligned the fetch adapter with HTTP adapter behavior for URL-embedded Basic auth. (__#10929__, __#10896__) * **Proxy TLS:** Preserved user `httpsAgent` TLS options when tunneling HTTPS requests through HTTP CONNECT proxies. (__#10957__) * **React Native FormData:** Cleared default `Content-Type` for React Native `FormData` so multipart boundaries can be generated correctly. (__#10898__) * **Headers:** Silently skipped empty or whitespace-only header names instead of throwing, matching parsed-header behavior and avoiding React Native response crashes. (__#10875__) * **Request Data Merging:** Preserved enumerable symbol keys when cloning plain request data through axios merge logic. (__#10812__) * **Bundler Compatibility:** Converted `resolveConfig` from an arrow default export to a named function export to avoid webpack and Babel transform interop failures. (__#10891__) * **Types:** Corrected `AxiosHeaders.toJSON()` return types and updated CommonJS `isCancel` typings to narrow to `CanceledError`. (__#10956__, __#10952__) * **Build Tooling:** Avoided emitting a null `Authorization` header from the GitHub build helper when `GITHUB_TOKEN` is unset. (__#10931__) ## 🔧 Maintenance & Chores * **HTTP/2 Internals:** Extracted `Http2Sessions` into its own helper module and added direct unit coverage for session pooling, timeout, and cleanup behavior. (__#10861__) * **Package Publishing:** Reduced published package size by switching to a `files` allowlist and dropping unneeded unminified bundle source maps. (__#10939__) * **CI and Release Automation:** Added bundle-size reporting, moved reports to the job summary, fixed bundle-size comparison coverage, added Node 26 to the matrix, pinned npm for staged publishing, and prepared the 1.17.0 release. (__#10907__, __#10911__, __#10916__, __#10927__, __#10935__, __#10983__) * **Developer Workflow:** Added a dev container and iterated on OpenSpec workflow files before removing them from the release branch. (__#10925__, __#10914__, __#10958__) * **Documentation and Policy:** Updated disclosure, contributor, collaboration, threat-model, advanced docs, README badges, release notes, moderator configuration, and project metadata. (__#10890__, __#10889__, __#10921__, __#10945__, __#10905__, __#10933__, __#10915__, __#10887__, __#10955__) * **Dependencies:** Bumped Babel tooling, Commitlint, ESLint, Rollup, Globals, Vitest, Playwright, `fs-extra`, `qs`, docs dependencies, and GitHub Actions dependencies including `actions/dependency-review-action` and `zizmorcore/zizmor-action`. (__#10871__, __#10879__, __#10918__, __#10919__, __#10934__, __#10947__, __#10954__, __#10960__) ## 🌟 New Contributors We are thrilled to welcome our new contributors. Thank you for helping improve axios: * __@BasixKOR__ (__#6792__) * __@carladams1299-lab__ (__#10861__) * __@LaplaceYoung__ (__#10812__) * __@JamieMagee__ (__#10939__) * __@RonGamzu__ (__#10905__) * __@sapirbaruch__ (__#10891__) * __@nezukoagent__ (__#10901__) * __@devareddy05__ (__#10929__) * __@Mohammad-Faiz-Cloud-Engineer__ (__#10922__) * __@azandabot__ (__#10931__) * __@niksy__ (__#10896__) [Full Changelog](https://github.com/axios/axios/compare/v1.16.1...v1.17.0) ## v1.16.1 — May 13, 2026 This release ships a defence-in-depth fix for prototype pollution in `formDataToJSON`, hardens proxy and CI workflows, restores Webpack 4 compatibility for the fetch adapter, and includes several small bug fixes and maintenance improvements. ## 🔒 Security Fixes * **Prototype Pollution Defence-in-Depth:** Hardened `formDataToJSON` against already-polluted `Object.prototype` by walking own properties only, so attacker-controlled keys inherited from a poisoned prototype cannot propagate through deserialization. (__#7413__) * **Proxy Cleartext Leak:** Fixed an issue where HTTPS request data could be transmitted in cleartext to an HTTP proxy under certain configurations. (__#10858__) * **CI Cache Removal:** Removed all GitHub Actions caches as a defence-in-depth measure against cache poisoning vectors in the build pipeline. (__#10882__) ## � Bug Fixes * **Data URI Parsing:** Updated the `fromDataURI` regex to match RFC 2397 more strictly, fixing edge cases in `data:` URL handling. (__#10829__) * **Unicode Headers:** Preserved Unicode header values when running through request interceptors, so non-ASCII header content is no longer corrupted before dispatch. (__#10850__) * **XHR Upload Progress:** Guarded against malformed `ProgressEvent` payloads emitted by some environments during XHR upload, preventing crashes when `loaded` / `total` are missing or invalid. (__#10868__) * **Webpack 4 Fetch Adapter:** Fixed an "unexpected token" error caused by syntax in the fetch adapter that Webpack 4 could not parse, restoring compatibility for legacy bundler users. (__#10864__) * **Type Definitions:** Made `parseReviver` `context.source` optional in the type definitions to align with the ES2023 specification. (__#10837__) * **URL Object Support Reverted:** Reverted the change that allowed passing a `URL` object as `config.url` (originally __#10866__) due to regressions; this support will be reintroduced in a later release once the underlying issues are addressed. (__#10874__) ## 🔧 Maintenance & Chores * **Cycle Detection Refactor:** Replaced the array-based cycle tracker in `toJSONObject` with a `WeakSet`, improving performance and memory behaviour on large nested structures. (__#10832__) * **composeSignals Cleanup:** Refactored `composeSignals` to use a clearer early-return structure, simplifying the cancellation/abort composition path. (__#10844__) * **AI Readiness & Repo Docs:** Added `AGENTS.md` and related contributor-guide updates for both human and AI agents, plus post-release documentation improvements. (__#10835__, __#10841__) * **Docs Improvements:** Clarified the GET request example, fixed the interceptor `eject` example to reference the correct instance, and corrected the Buzzoid sponsor description in the README. (__#10836__, __#10853__, __#10856__) * **Sponsorship Tooling:** Fixed empty sponsor arrays in the sponsor processing script, added the ability to inject additional sponsors, updated the sponsorship link, and added a Twicsy advertisement entry. (__#10843__, __#10859__, __#10869__) * **Dependencies:** Bumped `@commitlint/cli` from 20.5.0 to 20.5.2. (__#10846__) ## 🌟 New Contributors We are thrilled to welcome our new contributors. Thank you for helping improve axios: * __@hpinmetaverse__ (__#10836__) * __@tommyhgunz14__ (__#7413__) * __@abhu85__ (__#10829__) * __@divyanshuraj1095__ (__#10853__) * __@sagodi97__ (__#10856__) * __@rkdfx__ (__#10868__) * __@Liuwei1125__ (__#10866__) [Full Changelog](https://github.com/axios/axios/compare/v1.16.0...v1.16.1) ## v1.16.0 — May 2, 2026 This release adds support for the QUERY HTTP method and a new `ECONNREFUSED` error constant, lands a substantial wave of HTTP, fetch, and XHR adapter bug fixes around redirects, aborts, headers, and timeouts, and welcomes 23 new contributors. ## ⚠� Notable Changes A handful of fixes in this release are either security-adjacent or change observable behaviour. Please review before upgrading: - **Fetch adapter now enforces `maxBodyLength` and `maxContentLength`.** These limits were silently ignored on the fetch adapter prior to 1.16.0 — anyone relying on them as a safety net (DoS protection, accidental large uploads) had no protection. (**#10795**) - **Proxy requests now preserve user-supplied `Host` headers.** Previously, the proxy path could overwrite a custom `Host`. Virtual-host-style routing through a proxy will now behave correctly. (**#10822**) - **Basic auth credentials embedded in URLs are now URL-decoded.** If you have percent-encoded credentials in a URL (e.g. `https://user:p%40ss@host`), the decoded value is what now goes on the wire. (**#10825**) - **`parseProtocol` now strictly requires a colon in the protocol separator.** Strings that loosely parsed as protocols before may no longer match. (**#10729**) - **Deprecated `unescape()` replaced with modern UTF-8 encoding.** Non-ASCII URL handling is now spec-correct; consumers depending on legacy `unescape()` quirks may see different output bytes. (**#7378**) - **`transformRequest` input typing change was reverted.** The typing change introduced in #10745 was reverted in #10810 after follow-up review — net behavior is unchanged from 1.15.2. (**#10745**, **#10810**) ## 🚀 New Features - **QUERY HTTP Method:** Added support for the QUERY HTTP method across adapters and type definitions. (**#10802**) - **ECONNREFUSED Error Constant:** Exposed `ECONNREFUSED` as a constant on `AxiosError` so callers can match connection-refused failures without comparing string literals (closes #6485). (**#10680**) - **Encode Helper Export:** Exported the internal `encode` helper from `buildURL` so userland param serializers can reuse the same encoding logic that axios uses internally. (**#6897**) ## � Bug Fixes - **HTTP Adapter — Redirects & Headers:** Cleared stale headers when a redirect targets a no-proxy host, fixed the redirect listener chain so listeners no longer stack across hops, restored the missing `requestDetails` argument on `beforeRedirect`, preserved user-supplied `Host` headers when forwarding through a proxy, and properly URL-decoded basic auth credentials. (**#10794**, **#10800**, **#6241**, **#10822**, **#10825**) - **HTTP Adapter — Streams & Timeouts:** Preserved the partial response object on `AxiosError` when a stream is aborted after headers arrive, honoured the `timeout` option during the connect phase when redirects are disabled, and resolved an unsettled-promise hang when an aborted request was combined with compression and `maxRedirects: 0`. (**#10708**, **#10819**, **#7149**) - **Fetch Adapter:** Enforced `maxBodyLength` / `maxContentLength` in the fetch adapter, set the `User-Agent` header to match the HTTP adapter, preserved the original abort reason instead of replacing it with a generic error, and deferred global access so importing the module no longer throws a `TypeError` in restricted environments. (**#10795**, **#10772**, **#10806**, **#7260**) - **XHR Adapter:** Unsubscribed the `cancelToken` and `AbortSignal` listeners on the error, timeout, and abort code paths to prevent leaked subscriptions. (**#10787**) - **Error Handling:** Attached the parsed response to `AxiosError` when `JSON.parse` fails inside `dispatchRequest`, prevented `settle` from emitting `undefined` error codes, and tightened the `parseProtocol` regex to require a colon in the protocol separator. (**#10724**, **#7276**, **#10729**) - **Types & Exports:** Aligned the CommonJS `CancelToken` typings with the ESM build, fixed a compiler error caused by `RawAxiosHeaders`, and re-exported `create` from the package index. (**#7414**, **#6389**, **#6460**) - **UTF-8 Encoding:** Replaced the deprecated `unescape()` call with a modern UTF-8 encoding implementation. (**#7378**) - **Misc Cleanup:** Resolved a batch of small inconsistencies and gadget-level issues across the codebase. (**#10833**) ## 🔧 Maintenance & Chores - **Refactor — ES6 Modernisation:** Modernised the `utils` module and XHR adapter to use ES6 features, and tidied the multipart boundary error message. (**#10588**, **#7419**) - **Tests:** Hardened the HTTP test server lifecycle to fix flaky `FormData` EPIPE failures, fixed Win32 platform support for the pipe tests, and corrected an incorrect test assumption. (**#10820**, **#10791**, **#10796**) - **Docs:** Documented `paramsSerializer.encode` for strict RFC 3986 query encoding, updated the `parseReviver` TypeScript definitions and configuration docs for ES2023, added timeout guidance to the README's first async example, and expanded notes around the recent type changes. (**#10821**, **#10782**, **#10759**, **#10804**) - **Reverted:** Reverted the `transformRequest` input typing change from #10745 after follow-up review. (**#10745**, **#10810**) - **Dependencies:** Bumped `actions/setup-node`, the `github-actions` group, and `postcss` (in `/docs`) to their latest versions. (**#10785**, **#10813**, **#10814**) - **Release:** Updated changelog and packages, and prepared the 1.16.0 release. (**#10790**, **#10834**) ## 🌟 New Contributors We are thrilled to welcome our new contributors. Thank you for helping improve axios: - **@singhankit001** (**#10588**) - **@cuiweixie** (**#7419**) - **@iruizsalinas** (**#10787**) - **@MarcosNocetti** (**#10680**) - **@deepview-autofix** (**#10729**) - **@atharvasingh7007** (**#10745**) - **@OfekDanny** (**#10772**) - **@mnahkies** (**#7414**) - **@tboyila** (**#10759**) - **@Kingo64** (**#6897**) - **@ramram1048** (**#6389**) - **@FLNacif** (**#6460**) - **@zozo123** (**#10806**) - **@pierluigilenoci** (**#10802**) - **@afurm** (**#10708**) - **@karan-lrn** (**#7378**) - **@ebeigarts** (**#7149**) - **@Raymondo97** (**#10782**) - **@mixelburg** (**#10821**) - **@ashishkr96** (**#10822**) - **@cyphercodes** (**#10819**) - **@Jye10032** (**#7260**) - **@VeerShah41** (**#7276**) [Full Changelog](https://github.com/axios/axios/compare/v1.15.2...v1.16.0) ## v1.15.2 - April 21, 2026 This release delivers prototype-pollution hardening for the Node HTTP adapter, adds an opt-in `allowedSocketPaths` allowlist to mitigate SSRF via Unix domain sockets, fixes a keep-alive socket memory leak, and ships supply-chain hardening across CI and security docs. ## 🔒 Security Fixes - **Prototype Pollution Hardening (HTTP Adapter):** Hardened the Node HTTP adapter and `resolveConfig`/`mergeConfig`/validator paths to read only own properties and use null-prototype config objects, preventing polluted `auth`, `baseURL`, `socketPath`, `beforeRedirect`, and `insecureHTTPParser` from influencing requests. (**#10779**) - **SSRF via `socketPath`:** Rejects non-string `socketPath` values and adds an opt-in `allowedSocketPaths` config option to restrict permitted Unix domain socket paths, returning `AxiosError` `ERR_BAD_OPTION_VALUE` on mismatch. (**#10777**) - **Supply-chain Hardening:** Added `.npmrc` with `ignore-scripts=true`, lockfile lint CI, non-blocking reproducible build diff, scoped CODEOWNERS, expanded `SECURITY.md`/`THREATMODEL.md` with provenance verification (`npm audit signatures`), 60-day resolution policy, and maintainer incident-response runbook. (**#10776**) ## 🚀 New Features - **`allowedSocketPaths` Config Option:** New request config option (and TypeScript types) to allowlist Unix domain socket paths used by the Node http adapter; backwards compatible when unset. (**#10777**) ## � Bug Fixes - **Keep-alive Socket Memory Leak:** Installs a single per-socket `error` listener tracking the active request via `kAxiosSocketListener`/`kAxiosCurrentReq`, eliminating per-request listener accumulation, `MaxListenersExceededWarning`, and linear heap growth under concurrent or long-running keep-alive workloads (fixes #10780). (**#10788**) ## 🔧 Maintenance & Chores - **Changelog:** Updated `CHANGELOG.md` with v1.15.1 release notes. (**#10781**) [Full Changelog](https://github.com/axios/axios/compare/v1.15.1...v1.15.2) --- ## v1.15.1 - April 19, 2026 This release ships a coordinated set of security hardening fixes across headers, body/redirect limits, multipart handling, and XSRF/prototype-pollution vectors, alongside a broad sweep of bug fixes, test migrations, and threat-model documentation updates. ## 🔒 Security Fixes - **Header Injection Hardening:** Tightened validation and sanitisation across request header construction to close the header-injection attack surface. (**#10749**) - **CRLF Stripping in Multipart Headers:** Correctly strips CR/LF from multipart header values to prevent injection via field names and filenames. (**#10758**) - **Prototype Pollution / Auth Bypass:** Replaced unsafe `in` checks with `hasOwnProperty` to prevent authentication bypass via prototype pollution on config objects, with additional regression tests. (**#10761**, **#10760**) - **`withXSRFToken` Truthy Bypass:** Short-circuits on any truthy non-boolean value, so an ambiguous config no longer silently leaks the XSRF token cross-origin. (**#10762**) - **`maxBodyLength` With Zero Redirects:** Enforces `maxBodyLength` even when `maxRedirects` is set to `0`, closing a bypass path for oversized request bodies. (**#10753**) - **Streamed Response `maxContentLength` Bypass:** Applies `maxContentLength` to streamed responses that previously bypassed the cap. (**#10754**) - **Follow-up CVE Completion:** Completes an earlier incomplete CVE fix to fully close the regression window. (**#10755**) ## 🚀 New Features - **AI-Based Docs Translations:** Initial scaffold for AI-assisted translations of the documentation site. (**#10705**) - **`Location` Request Header Type:** Adds `Location` to `CommonRequestHeadersList` for accurate typing of redirect-aware requests. (**#7528**) ## � Bug Fixes - **FormData Handling:** Removes `Content-Type` when no boundary is present on `FormData` fetch requests, supports multi-select fields, cancels `request.body` instead of the source stream on fetch abort, and fixes a recursion bug in form-data serialisation. (**#7314**, **#10676**, **#10702**, **#10726**) - **HTTP Adapter:** Handles socket-only request errors without leaking keep-alive listeners. (**#10576**) - **Progress Events:** Clamps `loaded` to `total` for computable upload/download progress events. (**#7458**) - **Types:** Aligns `runWhen` type with the runtime behaviour in `InterceptorManager` and makes response header keys case-insensitive. (**#7529**, **#10677**) - **`buildFullPath`:** Uses strict equality in the base/relative URL check. (**#7252**) - **`AxiosURLSearchParams` Regex:** Improves the regex used for param serialisation to avoid edge-case mismatches. (**#10736**) - **Resilient Value Parsing:** Parses out header/config values instead of throwing on malformed input. (**#10687**) - **Docs Artefact Cleanup:** Removes the docs content that was incorrectly committed. (**#10727**) ## 🔧 Maintenance & Chores - **Threat Model & Security Docs:** Ongoing refinement of `THREATMODEL.md`, including Hopper security update, TLS and tag-replay wording, mitigation descriptions, decompression-bomb guidance, and further cleanup. (**#10672**, **#10715**, **#10718**, **#10722**, **#10763**, **#10765**) - **Test Coverage & Migration:** Expanded `shouldBypassProxy` coverage for wildcard/IPv6/edge cases, documented and tested `AxiosError.status`, and migrated `progressEventReducer` tests to Vitest. (**#10723**, **#10725**, **#10741**) - **Type Refactor:** Uses TypeScript utility types to deduplicate literal unions. (**#7520**) - **Repo & CI:** Adds `CODEOWNERS`, switches v1.x releases to an ephemeral release branch, and removes orphaned Bower support. (**#10739**, **#10738**, **#10746**) ## 🌟 New Contributors We are thrilled to welcome our new contributors. Thank you for helping improve axios: - **@curiouscoder-cmd** (**#7252**) - **@tryonelove** (**#7520**) - **@darwin808** (**#7314**) - **@zoontek** (**#10702**) - **@AKIB473** (**#10725**) [Full Changelog](https://github.com/axios/axios/compare/v1.15.0...v1.15.1) --- ## v1.15.0 - April 7, 2026 This release delivers two critical security patches targeting header injection and SSRF via proxy bypass, adds official runtime support for Deno and Bun, and includes significant CI security hardening. ## 🔒 Security Fixes - **Header Injection (CRLF):** Rejects any header value containing `\r` or `\n` characters to block CRLF injection chains that could be used to exfiltrate cloud metadata (IMDS). Behavior change: headers with CR/LF now throw `"Invalid character in header content"`. (**#10660**) - **SSRF via `no_proxy` Bypass:** Introduces a `shouldBypassProxy` helper that normalises hostnames (strips trailing dots, handles bracketed IPv6) before evaluating `no_proxy`/`NO_PROXY` rules, closing a gap that could cause loopback or internal hosts to be inadvertently proxied. (**#10661**) ## 🚀 New Features - **Deno & Bun Runtime Support:** Added full smoke test suites for Deno and Bun, with CI workflows that run both runtimes before any release is cut. (**#10652**) ## � Bug Fixes - **Node.js v22 Compatibility:** Replaced deprecated `url.parse()` calls with the WHATWG `URL`/`URLSearchParams` API across examples, sandbox, and tests, eliminating `DEP0169` deprecation warnings on Node.js v22+. (**#10625**) ## 🔧 Maintenance & Chores - **CI Security Hardening:** Added [zizmor](https://github.com/zizmorcore/zizmor) GitHub Actions security scanner; switched npm publish to OIDC Trusted Publishing (removing the long-lived `NODE_AUTH_TOKEN`); pinned all action references to full commit SHAs; narrowed workflow permissions to least privilege; gated the publish step behind a dedicated `npm-publish` environment; and blocked the sponsor-block workflow from running on forks. (**#10618**, **#10619**, **#10627**, **#10637**, **#10641**, **#10666**) - **Docs:** Clarified HTTP/2 support and the unsupported `httpVersion` option; added documentation for header case preservation; improved the `beforeRedirect` example to prevent accidental credential leakage. (**#10644**, **#10654**, **#10624**) - **Dependencies:** Bumped `picomatch`, `handlebars`, `serialize-javascript`, `vite` (×3), `denoland/setup-deno`, and 4 additional dev dependencies to latest versions. (**#10564**, **#10565**, **#10567**, **#10568**, **#10572**, **#10574**, **#10663**, **#10664**, **#10665**, **#10669**, **#10670**) ## 🌟 New Contributors We are thrilled to welcome our new contributors. Thank you for helping improve axios: - **@Kilros0817** (**#10625**) - **@shaanmajid** (**#10616**, **#10617**, **#10618**, **#10619**, **#10637**, **#10641**, **#10666**) - **@ashstrc** (**#10624**, **#10644**) - **@Abhi3975** (**#10589**) - **@raashish1601** (**#10573**) [Full Changelog](https://github.com/axios/axios/compare/v1.14.0...v1.15.0) --- ## v1.14.0 - March 27, 2026 This release fixes a security vulnerability in the `formidable` dependency, resolves a CommonJS compatibility regression, hardens proxy and HTTP/2 handling, and modernises the build and test toolchain. ## 🔒 Security Fixes - **Formidable Vulnerability:** Upgraded `formidable` from v2 to v3 to address a reported arbitrary-file vulnerability. Updated test server and assertions to align with the v3 API. (**#7533**) ## � Bug Fixes - **CommonJS Compatibility:** Restored `require('axios')` in Node.js by correcting the `main` field in `package.json` to point to the built CJS bundle. (**#7532**) - **Fetch Adapter:** Cancel the `ReadableStream` body after the request stream capability probe to prevent resource leaks. (**#7515**) - **Proxy:** Upgraded `proxy-from-env` to v2 and switched to the named `getProxyForUrl` export, fixing proxy detection from environment variables and resolving CJS bundling errors. (**#7499**) - **HTTP/2:** Close detached HTTP/2 sessions on timeout to free resources when no new requests arrive. (**#7457**) - **Headers:** Trim trailing CRLF characters from normalised header values. (**#7456**) ## 🔧 Maintenance & Chores - **Toolchain Modernisation:** Migrated test suite to Vitest, updated ESLint to v10, upgraded Rollup and `@rollup/plugin-babel`, migrated to Husky 9, upgraded TypeScript to latest, and modernised the Express test harness. (**#7484**, **#7489**, **#7498**, **#7505**, **#7506**, **#7507**, **#7508**, **#7509**, **#7510**, **#7516**, **#7522**) - **Dependencies:** Bumped `multer` to v2, `minimatch`, `tar`, `pacote`, `@babel/preset-env`, and additional dev dependencies. (**#7453**, **#7480**, **#7491**, **#7504**, **#7517**, **#7531**) ## 🌟 New Contributors We are thrilled to welcome our new contributors. Thank you for helping improve axios: - **@penkzhou** (**#7515**) - **@aviu16** (**#7456**) - **@fedotov** (**#7457**) [Full Changelog](https://github.com/axios/axios/compare/v1.13.6...v1.14.0) --- ## v1.13.6 - February 27, 2026 This release adds React Native Blob support, fixes several enumeration and export regressions, and patches FormData detection for WeChat Mini Program environments. ## 🚀 New Features - **React Native Blob Support:** Axios now correctly handles native Blob objects in React Native environments. (**#5764**) ## � Bug Fixes - **AxiosError:** Fixed `AxiosError.from` not copying the `status` field from the source error. (**#7403**) - **AxiosError:** Made the `message` property enumerable so it appears in `JSON.stringify` output and `Object.keys`. (**#7392**) - **FormData Detection:** Corrected safe FormData detection for WeChat Mini Program environments. (**#7324**) - **React Native / Browserify Export:** Fixed broken module export that caused import failures in React Native and Browserify. (**#7386**) ## 🔧 Maintenance & Chores - **Dependencies:** Migrated `@rollup/plugin-babel` from v5 to v6 and bumped the development dependencies group. (**#7424**, **#7432**) ## 🌟 New Contributors We are thrilled to welcome our new contributors. Thank you for helping improve axios: - **@moh3n9595** (**#5764**) - **@skrtheboss** (**#7403**) - **@ybbus** (**#7392**) - **@Shiwaangee** (**#7324**) - **@Gudahtt** (**#7386**) [Full Changelog](https://github.com/axios/axios/compare/v1.13.5...v1.13.6) --- ## v1.13.5 - February 8, 2026 This release patches a prototype pollution denial-of-service vulnerability, fixes a missing `status` field regression in `AxiosError`, adds interceptor ordering control, and introduces URL validation for `isAbsoluteURL`. ## 🔒 Security Fixes - **Prototype Pollution (DoS):** Hardened `mergeConfig` to ignore `__proto__`, `constructor`, and `prototype` keys, preventing denial-of-service via prototype pollution when merging user-supplied config. (**#7369**) ## 🚀 New Features - **`isAbsoluteURL` Validation:** Added input validation to `isAbsoluteURL` to handle malformed or unexpected input gracefully. (**#7326**) ## � Bug Fixes - **AxiosError `status`:** Restored the `status` field on `AxiosError` instances, which was missing in v1.13.3 and later. (**#7368**) - **Interceptor Ordering:** Added a `useLegacyInterceptorOrder` option to restore pre-v1.13 interceptor execution order for applications relying on the previous behaviour. ([569f028](https://github.com/axios/axios/commit/569f028a5878faaec8d7d138ba686aac407bda4c)) ## 🔧 Maintenance & Chores - **CI:** Fixed run conditions and updated workflow YAMLs. (**#7372**, **#7373**) - **Dependencies:** Bumped `karma-sourcemap-loader` and minor package versions. (**#7356**, **#7360**) ## 🌟 New Contributors We are thrilled to welcome our new contributors. Thank you for helping improve axios: - **@asmitha-16** (**#7326**) [Full Changelog](https://github.com/axios/axios/compare/v1.13.4...v1.13.5) --- ## v1.13.4 - January 27, 2026 Patch release fixing regressions introduced in v1.13.3, including TypeScript export compatibility and CI/build stability. ## � Bug Fixes - **v1.13.3 Regressions:** Fixed multiple issues introduced by the v1.13.3 release, including broken merge configs. (**#7352**) - **TypeScript Exports:** Corrected TypeScript export declarations to restore proper type resolution. (**#4884**) ## 🔧 Maintenance & Chores - **CI & Build:** Refactored CI pipeline and build configuration for stability. (**#7340**) [Full Changelog](https://github.com/axios/axios/compare/v1.13.3...v1.13.4) --- ## [1.13.3](https://github.com/axios/axios/compare/v1.13.2...v1.13.3) (2026-01-20) ### Bug Fixes - **http2:** Use port 443 for HTTPS connections by default. ([#7256](https://github.com/axios/axios/issues/7256)) ([d7e6065](https://github.com/axios/axios/commit/d7e60653460480ffacecf85383012ca1baa6263e)) - **interceptor:** handle the error in the same interceptor ([#6269](https://github.com/axios/axios/issues/6269)) ([5945e40](https://github.com/axios/axios/commit/5945e40bb171d4ac4fc195df276cf952244f0f89)) - main field in package.json should correspond to cjs artifacts ([#5756](https://github.com/axios/axios/issues/5756)) ([7373fbf](https://github.com/axios/axios/commit/7373fbff24cd92ce650d99ff6f7fe08c2e2a0a04)) - **package.json:** add 'bun' package.json 'exports' condition. Load the Node.js build in Bun instead of the browser build ([#5754](https://github.com/axios/axios/issues/5754)) ([b89217e](https://github.com/axios/axios/commit/b89217e3e91de17a3d55e2b8f39ceb0e9d8aeda8)) - silentJSONParsing=false should throw on invalid JSON ([#7253](https://github.com/axios/axios/issues/7253)) ([#7257](https://github.com/axios/axios/issues/7257)) ([7d19335](https://github.com/axios/axios/commit/7d19335e43d6754a1a9a66e424f7f7da259895bf)) - turn AxiosError into a native error ([#5394](https://github.com/axios/axios/issues/5394)) ([#5558](https://github.com/axios/axios/issues/5558)) ([1c6a86d](https://github.com/axios/axios/commit/1c6a86dd2c0623ee1af043a8491dbc96d40e883b)) - **types:** add handlers to AxiosInterceptorManager interface ([#5551](https://github.com/axios/axios/issues/5551)) ([8d1271b](https://github.com/axios/axios/commit/8d1271b49fc226ed7defd07cd577bd69a55bb13a)) - **types:** restore AxiosError.cause type from unknown to Error ([#7327](https://github.com/axios/axios/issues/7327)) ([d8233d9](https://github.com/axios/axios/commit/d8233d9e8e9a64bfba9bbe01d475ba417510b82b)) - unclear error message is thrown when specifying an empty proxy authorization ([#6314](https://github.com/axios/axios/issues/6314)) ([6ef867e](https://github.com/axios/axios/commit/6ef867e684adf7fb2343e3b29a79078a3c76dc29)) ### Features - add `undefined` as a value in AxiosRequestConfig ([#5560](https://github.com/axios/axios/issues/5560)) ([095033c](https://github.com/axios/axios/commit/095033c626895ecdcda2288050b63dcf948db3bd)) - add automatic minor and patch upgrades to dependabot ([#6053](https://github.com/axios/axios/issues/6053)) ([65a7584](https://github.com/axios/axios/commit/65a7584eda6164980ddb8cf5372f0afa2a04c1ed)) - add Node.js coverage script using c8 (closes [#7289](https://github.com/axios/axios/issues/7289)) ([#7294](https://github.com/axios/axios/issues/7294)) ([ec9d94e](https://github.com/axios/axios/commit/ec9d94e9f88da13e9219acadf65061fb38ce080a)) - added copilot instructions ([3f83143](https://github.com/axios/axios/commit/3f83143bfe617eec17f9d7dcf8bafafeeae74c26)) - compatibility with frozen prototypes ([#6265](https://github.com/axios/axios/issues/6265)) ([860e033](https://github.com/axios/axios/commit/860e03396a536e9b926dacb6570732489c9d7012)) - enhance pipeFileToResponse with error handling ([#7169](https://github.com/axios/axios/issues/7169)) ([88d7884](https://github.com/axios/axios/commit/88d78842541610692a04282233933d078a8a2552)) - **types:** Intellisense for string literals in a widened union ([#6134](https://github.com/axios/axios/issues/6134)) ([f73474d](https://github.com/axios/axios/commit/f73474d02c5aa957b2daeecee65508557fd3c6e5)), closes [/github.com/microsoft/TypeScript/issues/33471#issuecomment-1376364329](https://github.com//github.com/microsoft/TypeScript/issues/33471/issues/issuecomment-1376364329) ### Reverts - Revert "fix: silentJSONParsing=false should throw on invalid JSON (#7253) (#7…" (#7298) ([a4230f5](https://github.com/axios/axios/commit/a4230f5581b3f58b6ff531b6dbac377a4fd7942a)), closes [#7253](https://github.com/axios/axios/issues/7253) [#7](https://github.com/axios/axios/issues/7) [#7298](https://github.com/axios/axios/issues/7298) - **deps:** bump peter-evans/create-pull-request from 7 to 8 in the github-actions group ([#7334](https://github.com/axios/axios/issues/7334)) ([2d6ad5e](https://github.com/axios/axios/commit/2d6ad5e48bd29b0b2b5e7e95fb473df98301543a)) ### Contributors to this release - avatar [Ashvin Tiwari](https://github.com/ashvin2005 '+1752/-4 (#7218 #7218 )') - avatar [Nikunj Mochi](https://github.com/mochinikunj '+940/-12 (#7294 #7294 )') - avatar [Anchal Singh](https://github.com/imanchalsingh '+544/-102 (#7169 #7185 )') - avatar [jasonsaayman](https://github.com/jasonsaayman '+317/-73 (#7334 #7298 )') - avatar [Julian Dax](https://github.com/brodo '+99/-120 (#5558 )') - avatar [Akash Dhar Dubey](https://github.com/AKASHDHARDUBEY '+167/-0 (#7287 #7288 )') - avatar [Madhumita](https://github.com/madhumitaaa '+20/-68 (#7198 )') - avatar [Tackoil](https://github.com/Tackoil '+80/-2 (#6269 )') - avatar [Justin Dhillon](https://github.com/justindhillon '+41/-41 (#6324 #6315 )') - avatar [Rudransh](https://github.com/Rudrxxx '+71/-2 (#7257 )') - avatar [WuMingDao](https://github.com/WuMingDao '+36/-36 (#7215 )') - avatar [codenomnom](https://github.com/codenomnom '+70/-0 (#7201 #7201 )') - avatar [Nandan Acharya](https://github.com/Nandann018-ux '+60/-10 (#7272 )') - avatar [Eric Dubé](https://github.com/KernelDeimos '+22/-40 (#7042 )') - avatar [Tibor Pilz](https://github.com/tiborpilz '+40/-4 (#5551 )') - avatar [Gabriel Quaresma](https://github.com/joaoGabriel55 '+31/-4 (#6314 )') - avatar [Turadg Aleahmad](https://github.com/turadg '+23/-6 (#6265 )') - avatar [JohnTitor](https://github.com/kiritosan '+14/-14 (#6155 )') - avatar [rohit miryala](https://github.com/rohitmiryala '+22/-0 (#7250 )') - avatar [Wilson Mun](https://github.com/wmundev '+20/-0 (#6053 )') - avatar [techcodie](https://github.com/techcodie '+7/-7 (#7236 )') - avatar [Ved Vadnere](https://github.com/Archis009 '+5/-6 (#7283 )') - avatar [svihpinc](https://github.com/svihpinc '+5/-3 (#6134 )') - avatar [SANDESH LENDVE](https://github.com/mrsandy1965 '+3/-3 (#7246 )') - avatar [Lubos](https://github.com/mrlubos '+5/-1 (#7312 )') - avatar [Jarred Sumner](https://github.com/Jarred-Sumner '+5/-1 (#5754 )') - avatar [Adam Hines](https://github.com/thebanjomatic '+2/-1 (#5756 )') - avatar [Subhan Kumar Rai](https://github.com/Subhan030 '+2/-1 (#7256 )') - avatar [Joseph Frazier](https://github.com/josephfrazier '+1/-1 (#7311 )') - avatar [KT0803](https://github.com/KT0803 '+0/-2 (#7229 )') - avatar [Albie](https://github.com/AlbertoSadoc '+1/-1 (#5560 )') - avatar [Jake Hayes](https://github.com/thejayhaykid '+1/-0 (#5999 )') ## [1.13.2](https://github.com/axios/axios/compare/v1.13.1...v1.13.2) (2025-11-04) ### Bug Fixes - **http:** fix 'socket hang up' bug for keep-alive requests when using timeouts; ([#7206](https://github.com/axios/axios/issues/7206)) ([8d37233](https://github.com/axios/axios/commit/8d372335f5c50ecd01e8615f2468a9eb19703117)) - **http:** use default export for http2 module to support stubs; ([#7196](https://github.com/axios/axios/issues/7196)) ([0588880](https://github.com/axios/axios/commit/0588880ac7ddba7594ef179930493884b7e90bf5)) ### Performance Improvements - **http:** fix early loop exit; ([#7202](https://github.com/axios/axios/issues/7202)) ([12c314b](https://github.com/axios/axios/commit/12c314b603e7852a157e93e47edb626a471ba6c5)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+28/-9 (#7206 #7202 )') - avatar [Kasper Isager Dalsgarð](https://github.com/kasperisager '+9/-9 (#7196 )') ## [1.13.1](https://github.com/axios/axios/compare/v1.13.0...v1.13.1) (2025-10-28) ### Bug Fixes - **http:** fixed a regression that caused the data stream to be interrupted for responses with non-OK HTTP statuses; ([#7193](https://github.com/axios/axios/issues/7193)) ([bcd5581](https://github.com/axios/axios/commit/bcd5581d208cd372055afdcb2fd10b68ca40613c)) ### Contributors to this release - avatar [Anchal Singh](https://github.com/imanchalsingh '+220/-111 (#7173 )') - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+18/-1 (#7193 )') # [1.13.0](https://github.com/axios/axios/compare/v1.12.2...v1.13.0) (2025-10-27) ### Bug Fixes - **fetch:** prevent TypeError when config.env is undefined ([#7155](https://github.com/axios/axios/issues/7155)) ([015faec](https://github.com/axios/axios/commit/015faeca9f26db76f9562760f04bb9f8229f4db1)) - resolve issue [#7131](https://github.com/axios/axios/issues/7131) (added spacing in mergeConfig.js) ([#7133](https://github.com/axios/axios/issues/7133)) ([9b9ec98](https://github.com/axios/axios/commit/9b9ec98548d93e9f2204deea10a5f1528bf3ce62)) ### Features - **http:** add HTTP2 support; ([#7150](https://github.com/axios/axios/issues/7150)) ([d676df7](https://github.com/axios/axios/commit/d676df772244726533ca320f42e967f5af056bac)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+794/-180 (#7186 #7150 #7039 )') - avatar [Noritaka Kobayashi](https://github.com/noritaka1166 '+24/-509 (#7032 )') - avatar [Aviraj2929](https://github.com/Aviraj2929 '+211/-93 (#7136 #7135 #7134 #7112 )') - avatar [prasoon patel](https://github.com/Prasoon52 '+167/-6 (#7099 )') - avatar [Samyak Dandge](https://github.com/Samy-in '+134/-0 (#7171 )') - avatar [Anchal Singh](https://github.com/imanchalsingh '+53/-56 (#7170 )') - avatar [Rahul Kumar](https://github.com/jaiyankargupta '+28/-28 (#7073 )') - avatar [Amit Verma](https://github.com/Amitverma0509 '+24/-13 (#7129 )') - avatar [Abhishek3880](https://github.com/abhishekmaniy '+23/-4 (#7119 #7117 #7116 #7115 )') - avatar [Dhvani Maktuporia](https://github.com/Dhvani365 '+14/-5 (#7175 )') - avatar [Usama Ayoub](https://github.com/sam3690 '+4/-4 (#7133 )') - avatar [ikuy1203](https://github.com/ikuy1203 '+3/-3 (#7166 )') - avatar [Nikhil Simon Toppo](https://github.com/Kirito-Excalibur '+1/-1 (#7172 )') - avatar [Jane Wangari](https://github.com/Wangarijane '+1/-1 (#7155 )') - avatar [Supakorn Ieamgomol](https://github.com/Supakornn '+1/-1 (#7065 )') - avatar [Kian-Meng Ang](https://github.com/kianmeng '+1/-1 (#7046 )') - avatar [UTSUMI Keiji](https://github.com/k-utsumi '+1/-1 (#7037 )') ## [1.12.2](https://github.com/axios/axios/compare/v1.12.1...v1.12.2) (2025-09-14) ### Bug Fixes - **fetch:** use current global fetch instead of cached one when env fetch is not specified to keep MSW support; ([#7030](https://github.com/axios/axios/issues/7030)) ([cf78825](https://github.com/axios/axios/commit/cf78825e1229b60d1629ad0bbc8a752ff43c3f53)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+247/-16 (#7030 #7022 #7024 )') - avatar [Noritaka Kobayashi](https://github.com/noritaka1166 '+2/-6 (#7028 #7029 )') ## [1.12.1](https://github.com/axios/axios/compare/v1.12.0...v1.12.1) (2025-09-12) ### Bug Fixes - **types:** fixed env config types; ([#7020](https://github.com/axios/axios/issues/7020)) ([b5f26b7](https://github.com/axios/axios/commit/b5f26b75bdd9afa95016fb67d0cab15fc74cbf05)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+10/-4 (#7020 )') # [1.12.0](https://github.com/axios/axios/compare/v1.11.0...v1.12.0) (2025-09-11) ### Bug Fixes - adding build artifacts ([9ec86de](https://github.com/axios/axios/commit/9ec86de257bfa33856571036279169f385ed92bd)) - dont add dist on release ([a2edc36](https://github.com/axios/axios/commit/a2edc3606a4f775d868a67bb3461ff18ce7ecd11)) - **fetch-adapter:** set correct Content-Type for Node FormData ([#6998](https://github.com/axios/axios/issues/6998)) ([a9f47af](https://github.com/axios/axios/commit/a9f47afbf3224d2ca987dbd8188789c7ea853c5d)) - **node:** enforce maxContentLength for data: URLs ([#7011](https://github.com/axios/axios/issues/7011)) ([945435f](https://github.com/axios/axios/commit/945435fc51467303768202250debb8d4ae892593)) - package exports ([#5627](https://github.com/axios/axios/issues/5627)) ([aa78ac2](https://github.com/axios/axios/commit/aa78ac23fc9036163308c0f6bd2bb885e7af3f36)) - **params:** removing '[' and ']' from URL encode exclude characters ([#3316](https://github.com/axios/axios/issues/3316)) ([#5715](https://github.com/axios/axios/issues/5715)) ([6d84189](https://github.com/axios/axios/commit/6d84189349c43b1dcdd977b522610660cc4c7042)) - release pr run ([fd7f404](https://github.com/axios/axios/commit/fd7f404488b2c4f238c2fbe635b58026a634bfd2)) - **types:** change the type guard on isCancel ([#5595](https://github.com/axios/axios/issues/5595)) ([0dbb7fd](https://github.com/axios/axios/commit/0dbb7fd4f61dc568498cd13a681fa7f907d6ec7e)) ### Features - **adapter:** surface low‑level network error details; attach original error via cause ([#6982](https://github.com/axios/axios/issues/6982)) ([78b290c](https://github.com/axios/axios/commit/78b290c57c978ed2ab420b90d97350231c9e5d74)) - **fetch:** add fetch, Request, Response env config variables for the adapter; ([#7003](https://github.com/axios/axios/issues/7003)) ([c959ff2](https://github.com/axios/axios/commit/c959ff29013a3bc90cde3ac7ea2d9a3f9c08974b)) - support reviver on JSON.parse ([#5926](https://github.com/axios/axios/issues/5926)) ([2a97634](https://github.com/axios/axios/commit/2a9763426e43d996fd60d01afe63fa6e1f5b4fca)), closes [#5924](https://github.com/axios/axios/issues/5924) - **types:** extend AxiosResponse interface to include custom headers type ([#6782](https://github.com/axios/axios/issues/6782)) ([7960d34](https://github.com/axios/axios/commit/7960d34eded2de66ffd30b4687f8da0e46c4903e)) ### Contributors to this release - avatar [Willian Agostini](https://github.com/WillianAgostini '+132/-16760 (#7002 #5926 #6782 )') - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+4263/-293 (#7006 #7003 )') - avatar [khani](https://github.com/mkhani01 '+111/-15 (#6982 )') - avatar [Ameer Assadi](https://github.com/AmeerAssadi '+123/-0 (#7011 )') - avatar [Emiedonmokumo Dick-Boro](https://github.com/emiedonmokumo '+55/-35 (#6998 )') - avatar [Zeroday BYTE](https://github.com/opsysdebug '+8/-8 (#6980 )') - avatar [Jason Saayman](https://github.com/jasonsaayman '+7/-7 (#6985 #6985 )') - avatar [최예찬](https://github.com/HealGaren '+5/-7 (#5715 )') - avatar [Gligor Kotushevski](https://github.com/gligorkot '+3/-1 (#5627 )') - avatar [Aleksandar Dimitrov](https://github.com/adimit '+2/-1 (#5595 )') # [1.11.0](https://github.com/axios/axios/compare/v1.10.0...v1.11.0) (2025-07-22) ### Bug Fixes - form-data npm package ([#6970](https://github.com/axios/axios/issues/6970)) ([e72c193](https://github.com/axios/axios/commit/e72c193722530db538b19e5ddaaa4544d226b253)) - prevent RangeError when using large Buffers ([#6961](https://github.com/axios/axios/issues/6961)) ([a2214ca](https://github.com/axios/axios/commit/a2214ca1bc60540baf2c80573cea3a0ff91ba9d1)) - **types:** resolve type discrepancies between ESM and CJS TypeScript declaration files ([#6956](https://github.com/axios/axios/issues/6956)) ([8517aa1](https://github.com/axios/axios/commit/8517aa16f8d082fc1d5309c642220fa736159110)) ### Contributors to this release - avatar [izzy goldman](https://github.com/izzygld '+186/-93 (#6970 )') - avatar [Manish Sahani](https://github.com/manishsahanidev '+70/-0 (#6961 )') - avatar [Noritaka Kobayashi](https://github.com/noritaka1166 '+12/-10 (#6938 #6939 )') - avatar [James Nail](https://github.com/jrnail23 '+13/-2 (#6956 )') - avatar [Tejaswi1305](https://github.com/Tejaswi1305 '+1/-1 (#6894 )') # [1.10.0](https://github.com/axios/axios/compare/v1.9.0...v1.10.0) (2025-06-14) ### Bug Fixes - **adapter:** pass fetchOptions to fetch function ([#6883](https://github.com/axios/axios/issues/6883)) ([0f50af8](https://github.com/axios/axios/commit/0f50af8e076b7fb403844789bd5e812dedcaf4ed)) - **form-data:** convert boolean values to strings in FormData serialization ([#6917](https://github.com/axios/axios/issues/6917)) ([5064b10](https://github.com/axios/axios/commit/5064b108de336ff34862650709761b8a96d26be0)) - **package:** add module entry point for React Native; ([#6933](https://github.com/axios/axios/issues/6933)) ([3d343b8](https://github.com/axios/axios/commit/3d343b86dc4fd0eea0987059c5af04327c7ae304)) ### Features - **types:** improved fetchOptions interface ([#6867](https://github.com/axios/axios/issues/6867)) ([63f1fce](https://github.com/axios/axios/commit/63f1fce233009f5db1abf2586c145825ac98c3d7)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+30/-19 (#6933 #6920 #6893 #6892 )') - avatar [Noritaka Kobayashi](https://github.com/noritaka1166 '+2/-6 (#6922 #6923 )') - avatar [Dimitrios Lazanas](https://github.com/dimitry-lzs '+4/-0 (#6917 )') - avatar [Adrian Knapp](https://github.com/AdrianKnapp '+2/-2 (#6867 )') - avatar [Howie Zhao](https://github.com/howiezhao '+3/-1 (#6872 )') - avatar [Uhyeon Park](https://github.com/warpdev '+1/-1 (#6883 )') - avatar [Sampo Silvennoinen](https://github.com/stscoundrel '+1/-1 (#6913 )') # [1.9.0](https://github.com/axios/axios/compare/v1.8.4...v1.9.0) (2025-04-24) ### Bug Fixes - **core:** fix the Axios constructor implementation to treat the config argument as optional; ([#6881](https://github.com/axios/axios/issues/6881)) ([6c5d4cd](https://github.com/axios/axios/commit/6c5d4cd69286868059c5e52d45085cb9a894a983)) - **fetch:** fixed ERR_NETWORK mapping for Safari browsers; ([#6767](https://github.com/axios/axios/issues/6767)) ([dfe8411](https://github.com/axios/axios/commit/dfe8411c9a082c3d068bdd1f8d6e73054f387f45)) - **headers:** allow iterable objects to be a data source for the set method; ([#6873](https://github.com/axios/axios/issues/6873)) ([1b1f9cc](https://github.com/axios/axios/commit/1b1f9ccdc15f1ea745160ec9a5223de9db4673bc)) - **headers:** fix `getSetCookie` by using 'get' method for caseless access; ([#6874](https://github.com/axios/axios/issues/6874)) ([d4f7df4](https://github.com/axios/axios/commit/d4f7df4b304af8b373488fdf8e830793ff843eb9)) - **headers:** fixed support for setting multiple header values from an iterated source; ([#6885](https://github.com/axios/axios/issues/6885)) ([f7a3b5e](https://github.com/axios/axios/commit/f7a3b5e0f7e5e127b97defa92a132fbf1b55cf15)) - **http:** send minimal end multipart boundary ([#6661](https://github.com/axios/axios/issues/6661)) ([987d2e2](https://github.com/axios/axios/commit/987d2e2dd3b362757550f36eab875e60640b6ddc)) - **types:** fix autocomplete for adapter config ([#6855](https://github.com/axios/axios/issues/6855)) ([e61a893](https://github.com/axios/axios/commit/e61a8934d8f94dd429a2f309b48c67307c700df0)) ### Features - **AxiosHeaders:** add getSetCookie method to retrieve set-cookie headers values ([#5707](https://github.com/axios/axios/issues/5707)) ([80ea756](https://github.com/axios/axios/commit/80ea756e72bcf53110fa792f5d7ab76e8b11c996)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+200/-34 (#6890 #6889 #6888 #6885 #6881 #6767 #6874 #6873 )') - avatar [Jay](https://github.com/jasonsaayman '+26/-1 ()') - avatar [Willian Agostini](https://github.com/WillianAgostini '+21/-0 (#5707 )') - avatar [George Cheng](https://github.com/Gerhut '+3/-3 (#5096 )') - avatar [FatahChan](https://github.com/FatahChan '+2/-2 (#6855 )') - avatar [Ionuț G. Stan](https://github.com/igstan '+1/-1 (#6661 )') ## [1.8.4](https://github.com/axios/axios/compare/v1.8.3...v1.8.4) (2025-03-19) ### Bug Fixes - **buildFullPath:** handle `allowAbsoluteUrls: false` without `baseURL` ([#6833](https://github.com/axios/axios/issues/6833)) ([f10c2e0](https://github.com/axios/axios/commit/f10c2e0de7fde0051f848609a29c2906d0caa1d9)) ### Contributors to this release - avatar [Marc Hassan](https://github.com/mhassan1 '+5/-1 (#6833 )') ## [1.8.3](https://github.com/axios/axios/compare/v1.8.2...v1.8.3) (2025-03-10) ### Bug Fixes - add missing type for allowAbsoluteUrls ([#6818](https://github.com/axios/axios/issues/6818)) ([10fa70e](https://github.com/axios/axios/commit/10fa70ef14fe39558b15a179f0e82f5f5e5d11b2)) - **xhr/fetch:** pass `allowAbsoluteUrls` to `buildFullPath` in `xhr` and `fetch` adapters ([#6814](https://github.com/axios/axios/issues/6814)) ([ec159e5](https://github.com/axios/axios/commit/ec159e507bdf08c04ba1a10fe7710094e9e50ec9)) ### Contributors to this release - avatar [Ashcon Partovi](https://github.com/Electroid '+6/-0 (#6811 )') - avatar [StefanBRas](https://github.com/StefanBRas '+4/-0 (#6818 )') - avatar [Marc Hassan](https://github.com/mhassan1 '+2/-2 (#6814 )') ## [1.8.2](https://github.com/axios/axios/compare/v1.8.1...v1.8.2) (2025-03-07) ### Bug Fixes - **http-adapter:** add allowAbsoluteUrls to path building ([#6810](https://github.com/axios/axios/issues/6810)) ([fb8eec2](https://github.com/axios/axios/commit/fb8eec214ce7744b5ca787f2c3b8339b2f54b00f)) ### Contributors to this release - avatar [Fasoro-Joseph Alexander](https://github.com/lexcorp16 '+1/-1 (#6810 )') ## [1.8.1](https://github.com/axios/axios/compare/v1.8.0...v1.8.1) (2025-02-26) ### Bug Fixes - **utils:** move `generateString` to platform utils to avoid importing crypto module into client builds; ([#6789](https://github.com/axios/axios/issues/6789)) ([36a5a62](https://github.com/axios/axios/commit/36a5a620bec0b181451927f13ac85b9888b86cec)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+51/-47 (#6789 )') # [1.8.0](https://github.com/axios/axios/compare/v1.7.9...v1.8.0) (2025-02-25) ### Bug Fixes - **examples:** application crashed when navigating examples in browser ([#5938](https://github.com/axios/axios/issues/5938)) ([1260ded](https://github.com/axios/axios/commit/1260ded634ec101dd5ed05d3b70f8e8f899dba6c)) - missing word in SUPPORT_QUESTION.yml ([#6757](https://github.com/axios/axios/issues/6757)) ([1f890b1](https://github.com/axios/axios/commit/1f890b13f2c25a016f3c84ae78efb769f244133e)) - **utils:** replace getRandomValues with crypto module ([#6788](https://github.com/axios/axios/issues/6788)) ([23a25af](https://github.com/axios/axios/commit/23a25af0688d1db2c396deb09229d2271cc24f6c)) ### Features - Add config for ignoring absolute URLs ([#5902](https://github.com/axios/axios/issues/5902)) ([#6192](https://github.com/axios/axios/issues/6192)) ([32c7bcc](https://github.com/axios/axios/commit/32c7bcc0f233285ba27dec73a4b1e81fb7a219b3)) ### Reverts - Revert "chore: expose fromDataToStream to be consumable (#6731)" (#6732) ([1317261](https://github.com/axios/axios/commit/1317261125e9c419fe9f126867f64d28f9c1efda)), closes [#6731](https://github.com/axios/axios/issues/6731) [#6732](https://github.com/axios/axios/issues/6732) ### BREAKING CHANGES - code relying on the above will now combine the URLs instead of prefer request URL - feat: add config option for allowing absolute URLs - fix: add default value for allowAbsoluteUrls in buildFullPath - fix: typo in flow control when setting allowAbsoluteUrls ### Contributors to this release - avatar [Michael Toscano](https://github.com/GethosTheWalrus '+42/-8 (#6192 )') - avatar [Willian Agostini](https://github.com/WillianAgostini '+26/-3 (#6788 #6777 )') - avatar [Naron](https://github.com/naronchen '+27/-0 (#5901 )') - avatar [shravan || श�रvan](https://github.com/shravan20 '+7/-3 (#6116 )') - avatar [Justin Dhillon](https://github.com/justindhillon '+0/-7 (#6312 )') - avatar [yionr](https://github.com/yionr '+5/-1 (#6129 )') - avatar [Shin'ya Ueoka](https://github.com/ueokande '+3/-3 (#5935 )') - avatar [Dan Dascalescu](https://github.com/dandv '+3/-3 (#5908 #6757 )') - avatar [Nitin Ramnani](https://github.com/NitinRamnani '+2/-2 (#5938 )') - avatar [Shay Molcho](https://github.com/shaymolcho '+2/-2 (#6770 )') - avatar [Jay](https://github.com/jasonsaayman '+0/-3 (#6732 )') - fancy45daddy - avatar [Habip Akyol](https://github.com/habipakyol '+1/-1 (#6030 )') - avatar [Bailey Lissington](https://github.com/llamington '+1/-1 (#6771 )') - avatar [Bernardo da Eira Duarte](https://github.com/bernardoduarte '+1/-1 (#6480 )') - avatar [Shivam Batham](https://github.com/Shivam-Batham '+1/-1 (#5949 )') - avatar [Lipin Kariappa](https://github.com/lipinnnnn '+1/-1 (#5936 )') ## [1.7.9](https://github.com/axios/axios/compare/v1.7.8...v1.7.9) (2024-12-04) ### Reverts - Revert "fix(types): export CJS types from ESM (#6218)" (#6729) ([c44d2f2](https://github.com/axios/axios/commit/c44d2f2316ad289b38997657248ba10de11deb6c)), closes [#6218](https://github.com/axios/axios/issues/6218) [#6729](https://github.com/axios/axios/issues/6729) ### Contributors to this release - avatar [Jay](https://github.com/jasonsaayman '+596/-108 (#6729 )') ## [1.7.8](https://github.com/axios/axios/compare/v1.7.7...v1.7.8) (2024-11-25) ### Bug Fixes - allow passing a callback as paramsSerializer to buildURL ([#6680](https://github.com/axios/axios/issues/6680)) ([eac4619](https://github.com/axios/axios/commit/eac4619fe2e0926e876cd260ee21e3690381dbb5)) - **core:** fixed config merging bug ([#6668](https://github.com/axios/axios/issues/6668)) ([5d99fe4](https://github.com/axios/axios/commit/5d99fe4491202a6268c71e5dcc09192359d73cea)) - fixed width form to not shrink after 'Send Request' button is clicked ([#6644](https://github.com/axios/axios/issues/6644)) ([7ccd5fd](https://github.com/axios/axios/commit/7ccd5fd42402102d38712c32707bf055be72ab54)) - **http:** add support for File objects as payload in http adapter ([#6588](https://github.com/axios/axios/issues/6588)) ([#6605](https://github.com/axios/axios/issues/6605)) ([6841d8d](https://github.com/axios/axios/commit/6841d8d18ddc71cc1bd202ffcfddb3f95622eef3)) - **http:** fixed proxy-from-env module import ([#5222](https://github.com/axios/axios/issues/5222)) ([12b3295](https://github.com/axios/axios/commit/12b32957f1258aee94ef859809ed39f8f88f9dfa)) - **http:** use `globalThis.TextEncoder` when available ([#6634](https://github.com/axios/axios/issues/6634)) ([df956d1](https://github.com/axios/axios/commit/df956d18febc9100a563298dfdf0f102c3d15410)) - ios11 breaks when build ([#6608](https://github.com/axios/axios/issues/6608)) ([7638952](https://github.com/axios/axios/commit/763895270f7b50c7c780c3c9807ae8635de952cd)) - **types:** add missing types for mergeConfig function ([#6590](https://github.com/axios/axios/issues/6590)) ([00de614](https://github.com/axios/axios/commit/00de614cd07b7149af335e202aef0e076c254f49)) - **types:** export CJS types from ESM ([#6218](https://github.com/axios/axios/issues/6218)) ([c71811b](https://github.com/axios/axios/commit/c71811b00f2fcff558e4382ba913bdac4ad7200e)) - updated stream aborted error message to be more clear ([#6615](https://github.com/axios/axios/issues/6615)) ([cc3217a](https://github.com/axios/axios/commit/cc3217a612024d83a663722a56d7a98d8759c6d5)) - use URL API instead of DOM to fix a potential vulnerability warning; ([#6714](https://github.com/axios/axios/issues/6714)) ([0a8d6e1](https://github.com/axios/axios/commit/0a8d6e19da5b9899a2abafaaa06a75ee548597db)) ### Contributors to this release - avatar [Remco Haszing](https://github.com/remcohaszing '+108/-596 (#6218 )') - avatar [Jay](https://github.com/jasonsaayman '+281/-19 (#6640 #6619 )') - avatar [Aayush Yadav](https://github.com/aayushyadav020 '+124/-111 (#6617 )') - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+12/-65 (#6714 )') - avatar [Ell Bradshaw](https://github.com/cincodenada '+29/-0 (#6489 )') - avatar [Amit Saini](https://github.com/amitsainii '+13/-3 (#5237 )') - avatar [Tommaso Paulon](https://github.com/guuido '+14/-1 (#6680 )') - avatar [Akki](https://github.com/Aakash-Rana '+5/-5 (#6668 )') - avatar [Sampo Silvennoinen](https://github.com/stscoundrel '+3/-3 (#6633 )') - avatar [Kasper Isager Dalsgarð](https://github.com/kasperisager '+2/-2 (#6634 )') - avatar [Christian Clauss](https://github.com/cclauss '+4/-0 (#6683 )') - avatar [Pavan Welihinda](https://github.com/pavan168 '+2/-2 (#5222 )') - avatar [Taylor Flatt](https://github.com/taylorflatt '+2/-2 (#6615 )') - avatar [Kenzo Wada](https://github.com/Kenzo-Wada '+2/-2 (#6608 )') - avatar [Ngole Lawson](https://github.com/echelonnought '+3/-0 (#6644 )') - avatar [Haven](https://github.com/Baoyx007 '+3/-0 (#6590 )') - avatar [Shrivali Dutt](https://github.com/shrivalidutt '+1/-1 (#6637 )') - avatar [Henco Appel](https://github.com/hencoappel '+1/-1 (#6605 )') ## [1.7.7](https://github.com/axios/axios/compare/v1.7.6...v1.7.7) (2024-08-31) ### Bug Fixes - **fetch:** fix stream handling in Safari by fallback to using a stream reader instead of an async iterator; ([#6584](https://github.com/axios/axios/issues/6584)) ([d198085](https://github.com/axios/axios/commit/d1980854fee1765cd02fa0787adf5d6e34dd9dcf)) - **http:** fixed support for IPv6 literal strings in url ([#5731](https://github.com/axios/axios/issues/5731)) ([364993f](https://github.com/axios/axios/commit/364993f0d8bc6e0e06f76b8a35d2d0a35cab054c)) ### Contributors to this release - avatar [Rishi556](https://github.com/Rishi556 '+39/-1 (#5731 )') - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+27/-7 (#6584 )') ## [1.7.6](https://github.com/axios/axios/compare/v1.7.5...v1.7.6) (2024-08-30) ### Bug Fixes - **fetch:** fix content length calculation for FormData payload; ([#6524](https://github.com/axios/axios/issues/6524)) ([085f568](https://github.com/axios/axios/commit/085f56861a83e9ac02c140ad9d68dac540dfeeaa)) - **fetch:** optimize signals composing logic; ([#6582](https://github.com/axios/axios/issues/6582)) ([df9889b](https://github.com/axios/axios/commit/df9889b83c2cc37e9e6189675a73ab70c60f031f)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+98/-46 (#6582 )') - avatar [Jacques Germishuys](https://github.com/jacquesg '+5/-1 (#6524 )') - avatar [kuroino721](https://github.com/kuroino721 '+3/-1 (#6575 )') ## [1.7.5](https://github.com/axios/axios/compare/v1.7.4...v1.7.5) (2024-08-23) ### Bug Fixes - **adapter:** fix undefined reference to hasBrowserEnv ([#6572](https://github.com/axios/axios/issues/6572)) ([7004707](https://github.com/axios/axios/commit/7004707c4180b416341863bd86913fe4fc2f1df1)) - **core:** add the missed implementation of AxiosError#status property; ([#6573](https://github.com/axios/axios/issues/6573)) ([6700a8a](https://github.com/axios/axios/commit/6700a8adac06942205f6a7a21421ecb36c4e0852)) - **core:** fix `ReferenceError: navigator is not defined` for custom environments; ([#6567](https://github.com/axios/axios/issues/6567)) ([fed1a4b](https://github.com/axios/axios/commit/fed1a4b2d78ed4a588c84e09d32749ed01dc2794)) - **fetch:** fix credentials handling in Cloudflare workers ([#6533](https://github.com/axios/axios/issues/6533)) ([550d885](https://github.com/axios/axios/commit/550d885eb90fd156add7b93bbdc54d30d2f9a98d)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+187/-83 (#6573 #6567 #6566 #6564 #6563 #6557 #6556 #6555 #6554 #6552 )') - avatar [Antonin Bas](https://github.com/antoninbas '+6/-6 (#6572 )') - avatar [Hans Otto Wirtz](https://github.com/hansottowirtz '+4/-1 (#6533 )') ## [1.7.4](https://github.com/axios/axios/compare/v1.7.3...v1.7.4) (2024-08-13) ### Bug Fixes - **sec:** CVE-2024-39338 ([#6539](https://github.com/axios/axios/issues/6539)) ([#6543](https://github.com/axios/axios/issues/6543)) ([6b6b605](https://github.com/axios/axios/commit/6b6b605eaf73852fb2dae033f1e786155959de3a)) - **sec:** disregard protocol-relative URL to remediate SSRF ([#6539](https://github.com/axios/axios/issues/6539)) ([07a661a](https://github.com/axios/axios/commit/07a661a2a6b9092c4aa640dcc7f724ec5e65bdda)) ### Contributors to this release - avatar [Lev Pachmanov](https://github.com/levpachmanov '+47/-11 (#6543 )') - avatar [�ỗ Tr�ng Hải](https://github.com/hainenber '+49/-4 (#6539 )') ## [1.7.3](https://github.com/axios/axios/compare/v1.7.2...v1.7.3) (2024-08-01) ### Bug Fixes - **adapter:** fix progress event emitting; ([#6518](https://github.com/axios/axios/issues/6518)) ([e3c76fc](https://github.com/axios/axios/commit/e3c76fc9bdd03aa4d98afaf211df943e2031453f)) - **fetch:** fix withCredentials request config ([#6505](https://github.com/axios/axios/issues/6505)) ([85d4d0e](https://github.com/axios/axios/commit/85d4d0ea0aae91082f04e303dec46510d1b4e787)) - **xhr:** return original config on errors from XHR adapter ([#6515](https://github.com/axios/axios/issues/6515)) ([8966ee7](https://github.com/axios/axios/commit/8966ee7ea62ecbd6cfb39a905939bcdab5cf6388)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+211/-159 (#6518 #6519 )') - avatar [Valerii Sidorenko](https://github.com/ValeraS '+3/-3 (#6515 )') - avatar [prianYu](https://github.com/prianyu '+2/-2 (#6505 )') ## [1.7.2](https://github.com/axios/axios/compare/v1.7.1...v1.7.2) (2024-05-21) ### Bug Fixes - **fetch:** enhance fetch API detection; ([#6413](https://github.com/axios/axios/issues/6413)) ([4f79aef](https://github.com/axios/axios/commit/4f79aef81b7c4644328365bfc33acf0a9ef595bc)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+3/-3 (#6413 )') ## [1.7.1](https://github.com/axios/axios/compare/v1.7.0...v1.7.1) (2024-05-20) ### Bug Fixes - **fetch:** fixed ReferenceError issue when TextEncoder is not available in the environment; ([#6410](https://github.com/axios/axios/issues/6410)) ([733f15f](https://github.com/axios/axios/commit/733f15fe5bd2d67e1fadaee82e7913b70d45dc5e)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+14/-9 (#6410 )') # [1.7.0](https://github.com/axios/axios/compare/v1.7.0-beta.2...v1.7.0) (2024-05-19) ### Features - **adapter:** add fetch adapter; ([#6371](https://github.com/axios/axios/issues/6371)) ([a3ff99b](https://github.com/axios/axios/commit/a3ff99b59d8ec2ab5dd049e68c043617a4072e42)) ### Bug Fixes - **core/axios:** handle un-writable error stack ([#6362](https://github.com/axios/axios/issues/6362)) ([81e0455](https://github.com/axios/axios/commit/81e0455b7b57fbaf2be16a73ebe0e6591cc6d8f9)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+1015/-127 (#6371 )') - avatar [Jay](https://github.com/jasonsaayman '+30/-14 ()') - avatar [Alexandre ABRIOUX](https://github.com/alexandre-abrioux '+56/-6 (#6362 )') # [1.7.0-beta.2](https://github.com/axios/axios/compare/v1.7.0-beta.1...v1.7.0-beta.2) (2024-05-19) ### Bug Fixes - **fetch:** capitalize HTTP method names; ([#6395](https://github.com/axios/axios/issues/6395)) ([ad3174a](https://github.com/axios/axios/commit/ad3174a3515c3c2573f4bcb94818d582826f3914)) - **fetch:** fix & optimize progress capturing for cases when the request data has a nullish value or zero data length ([#6400](https://github.com/axios/axios/issues/6400)) ([95a3e8e](https://github.com/axios/axios/commit/95a3e8e346cfd6a5548e171f2341df3235d0e26b)) - **fetch:** fix headers getting from a stream response; ([#6401](https://github.com/axios/axios/issues/6401)) ([870e0a7](https://github.com/axios/axios/commit/870e0a76f60d0094774a6a63fa606eec52a381af)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+99/-46 (#6405 #6404 #6401 #6400 #6395 )') # [1.7.0-beta.1](https://github.com/axios/axios/compare/v1.7.0-beta.0...v1.7.0-beta.1) (2024-05-07) ### Bug Fixes - **core/axios:** handle un-writable error stack ([#6362](https://github.com/axios/axios/issues/6362)) ([81e0455](https://github.com/axios/axios/commit/81e0455b7b57fbaf2be16a73ebe0e6591cc6d8f9)) - **fetch:** fix cases when ReadableStream or Response.body are not available; ([#6377](https://github.com/axios/axios/issues/6377)) ([d1d359d](https://github.com/axios/axios/commit/d1d359da347704e8b28d768e61515a3e96c5b072)) - **fetch:** treat fetch-related TypeError as an AxiosError.ERR_NETWORK error; ([#6380](https://github.com/axios/axios/issues/6380)) ([bb5f9a5](https://github.com/axios/axios/commit/bb5f9a5ab768452de9e166dc28d0ffc234245ef1)) ### Contributors to this release - avatar [Alexandre ABRIOUX](https://github.com/alexandre-abrioux '+56/-6 (#6362 )') - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+42/-17 (#6380 #6377 )') # [1.7.0-beta.0](https://github.com/axios/axios/compare/v1.6.8...v1.7.0-beta.0) (2024-04-28) ### Features - **adapter:** add fetch adapter; ([#6371](https://github.com/axios/axios/issues/6371)) ([a3ff99b](https://github.com/axios/axios/commit/a3ff99b59d8ec2ab5dd049e68c043617a4072e42)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+1015/-127 (#6371 )') - avatar [Jay](https://github.com/jasonsaayman '+30/-14 ()') ## [1.6.8](https://github.com/axios/axios/compare/v1.6.7...v1.6.8) (2024-03-15) ### Bug Fixes - **AxiosHeaders:** fix AxiosHeaders conversion to an object during config merging ([#6243](https://github.com/axios/axios/issues/6243)) ([2656612](https://github.com/axios/axios/commit/2656612bc10fe2757e9832b708ed773ab340b5cb)) - **import:** use named export for EventEmitter; ([7320430](https://github.com/axios/axios/commit/7320430aef2e1ba2b89488a0eaf42681165498b1)) - **vulnerability:** update follow-redirects to 1.15.6 ([#6300](https://github.com/axios/axios/issues/6300)) ([8786e0f](https://github.com/axios/axios/commit/8786e0ff55a8c68d4ca989801ad26df924042e27)) ### Contributors to this release - avatar [Jay](https://github.com/jasonsaayman '+4572/-3446 (#6238 )') - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+30/-0 (#6231 )') - avatar [Mitchell](https://github.com/Creaous '+9/-9 (#6300 )') - avatar [Emmanuel](https://github.com/mannoeu '+2/-2 (#6196 )') - avatar [Lucas Keller](https://github.com/ljkeller '+3/-0 (#6194 )') - avatar [Aditya Mogili](https://github.com/ADITYA-176 '+1/-1 ()') - avatar [Miroslav Petrov](https://github.com/petrovmiroslav '+1/-1 (#6243 )') ## [1.6.7](https://github.com/axios/axios/compare/v1.6.6...v1.6.7) (2024-01-25) ### Bug Fixes - capture async stack only for rejections with native error objects; ([#6203](https://github.com/axios/axios/issues/6203)) ([1a08f90](https://github.com/axios/axios/commit/1a08f90f402336e4d00e9ee82f211c6adb1640b0)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+30/-26 (#6203 )') - avatar [zhoulixiang](https://github.com/zh-lx '+0/-3 (#6186 )') ## [1.6.6](https://github.com/axios/axios/compare/v1.6.5...v1.6.6) (2024-01-24) ### Bug Fixes - fixed missed dispatchBeforeRedirect argument ([#5778](https://github.com/axios/axios/issues/5778)) ([a1938ff](https://github.com/axios/axios/commit/a1938ff073fcb0f89011f001dfbc1fa1dc995e39)) - wrap errors to improve async stack trace ([#5987](https://github.com/axios/axios/issues/5987)) ([123f354](https://github.com/axios/axios/commit/123f354b920f154a209ea99f76b7b2ef3d9ebbab)) ### Contributors to this release - avatar [Ilya Priven](https://github.com/ikonst '+91/-8 (#5987 )') - avatar [Zao Soula](https://github.com/zaosoula '+6/-6 (#5778 )') ## [1.6.5](https://github.com/axios/axios/compare/v1.6.4...v1.6.5) (2024-01-05) ### Bug Fixes - **ci:** refactor notify action as a job of publish action; ([#6176](https://github.com/axios/axios/issues/6176)) ([0736f95](https://github.com/axios/axios/commit/0736f95ce8776366dc9ca569f49ba505feb6373c)) - **dns:** fixed lookup error handling; ([#6175](https://github.com/axios/axios/issues/6175)) ([f4f2b03](https://github.com/axios/axios/commit/f4f2b039dd38eb4829e8583caede4ed6d2dd59be)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+41/-6 (#6176 #6175 )') - avatar [Jay](https://github.com/jasonsaayman '+6/-1 ()') ## [1.6.4](https://github.com/axios/axios/compare/v1.6.3...v1.6.4) (2024-01-03) ### Bug Fixes - **security:** fixed formToJSON prototype pollution vulnerability; ([#6167](https://github.com/axios/axios/issues/6167)) ([3c0c11c](https://github.com/axios/axios/commit/3c0c11cade045c4412c242b5727308cff9897a0e)) - **security:** fixed security vulnerability in follow-redirects ([#6163](https://github.com/axios/axios/issues/6163)) ([75af1cd](https://github.com/axios/axios/commit/75af1cdff5b3a6ca3766d3d3afbc3115bb0811b8)) ### Contributors to this release - avatar [Jay](https://github.com/jasonsaayman '+34/-6 ()') - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+34/-3 (#6172 #6167 )') - avatar [Guy Nesher](https://github.com/gnesher '+10/-10 (#6163 )') ## [1.6.3](https://github.com/axios/axios/compare/v1.6.2...v1.6.3) (2023-12-26) ### Bug Fixes - Regular Expression Denial of Service (ReDoS) ([#6132](https://github.com/axios/axios/issues/6132)) ([5e7ad38](https://github.com/axios/axios/commit/5e7ad38fb0f819fceb19fb2ee5d5d38f56aa837d)) ### Contributors to this release - avatar [Jay](https://github.com/jasonsaayman '+15/-6 (#6145 )') - avatar [Willian Agostini](https://github.com/WillianAgostini '+17/-2 (#6132 )') - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+3/-0 (#6084 )') ## [1.6.2](https://github.com/axios/axios/compare/v1.6.1...v1.6.2) (2023-11-14) ### Features - **withXSRFToken:** added withXSRFToken option as a workaround to achieve the old `withCredentials` behavior; ([#6046](https://github.com/axios/axios/issues/6046)) ([cff9967](https://github.com/axios/axios/commit/cff996779b272a5e94c2b52f5503ccf668bc42dc)) ### PRs - feat(withXSRFToken): added withXSRFToken option as a workaround to achieve the old `withCredentials` behavior; ( [#6046](https://api.github.com/repos/axios/axios/pulls/6046) ) ``` 📢 This PR added 'withXSRFToken' option as a replacement for old withCredentials behaviour. You should now use withXSRFToken along with withCredential to get the old behavior. This functionality is considered as a fix. ``` ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+271/-146 (#6081 #6080 #6079 #6078 #6046 #6064 #6063 )') - avatar [Ng Choon Khon (CK)](https://github.com/ckng0221 '+4/-4 (#6073 )') - avatar [Muhammad Noman](https://github.com/mnomanmemon '+2/-2 (#6048 )') ## [1.6.1](https://github.com/axios/axios/compare/v1.6.0...v1.6.1) (2023-11-08) ### Bug Fixes - **formdata:** fixed content-type header normalization for non-standard browser environments; ([#6056](https://github.com/axios/axios/issues/6056)) ([dd465ab](https://github.com/axios/axios/commit/dd465ab22bbfa262c6567be6574bf46a057d5288)) - **platform:** fixed emulated browser detection in node.js environment; ([#6055](https://github.com/axios/axios/issues/6055)) ([3dc8369](https://github.com/axios/axios/commit/3dc8369e505e32a4e12c22f154c55fd63ac67fbb)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+432/-65 (#6059 #6056 #6055 )') - avatar [Fabian Meyer](https://github.com/meyfa '+5/-2 (#5835 )') ### PRs - feat(withXSRFToken): added withXSRFToken option as a workaround to achieve the old `withCredentials` behavior; ( [#6046](https://api.github.com/repos/axios/axios/pulls/6046) ) ``` 📢 This PR added 'withXSRFToken' option as a replacement for old withCredentials behaviour. You should now use withXSRFToken along with withCredential to get the old behavior. This functionality is considered as a fix. ``` # [1.6.0](https://github.com/axios/axios/compare/v1.5.1...v1.6.0) (2023-10-26) ### Bug Fixes - **CSRF:** fixed CSRF vulnerability CVE-2023-45857 ([#6028](https://github.com/axios/axios/issues/6028)) ([96ee232](https://github.com/axios/axios/commit/96ee232bd3ee4de2e657333d4d2191cd389e14d0)) - **dns:** fixed lookup function decorator to work properly in node v20; ([#6011](https://github.com/axios/axios/issues/6011)) ([5aaff53](https://github.com/axios/axios/commit/5aaff532a6b820bb9ab6a8cd0f77131b47e2adb8)) - **types:** fix AxiosHeaders types; ([#5931](https://github.com/axios/axios/issues/5931)) ([a1c8ad0](https://github.com/axios/axios/commit/a1c8ad008b3c13d53e135bbd0862587fb9d3fc09)) ### PRs - CVE 2023 45857 ( [#6028](https://api.github.com/repos/axios/axios/pulls/6028) ) ``` ⚠� Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459 ``` ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+449/-114 (#6032 #6021 #6011 #5932 #5931 )') - avatar [Valentin Panov](https://github.com/valentin-panov '+4/-4 (#6028 )') - avatar [Rinku Chaudhari](https://github.com/therealrinku '+1/-1 (#5889 )') ## [1.5.1](https://github.com/axios/axios/compare/v1.5.0...v1.5.1) (2023-09-26) ### Bug Fixes - **adapters:** improved adapters loading logic to have clear error messages; ([#5919](https://github.com/axios/axios/issues/5919)) ([e410779](https://github.com/axios/axios/commit/e4107797a7a1376f6209fbecfbbce73d3faa7859)) - **formdata:** fixed automatic addition of the `Content-Type` header for FormData in non-browser environments; ([#5917](https://github.com/axios/axios/issues/5917)) ([bc9af51](https://github.com/axios/axios/commit/bc9af51b1886d1b3529617702f2a21a6c0ed5d92)) - **headers:** allow `content-encoding` header to handle case-insensitive values ([#5890](https://github.com/axios/axios/issues/5890)) ([#5892](https://github.com/axios/axios/issues/5892)) ([4c89f25](https://github.com/axios/axios/commit/4c89f25196525e90a6e75eda9cb31ae0a2e18acd)) - **types:** removed duplicated code ([9e62056](https://github.com/axios/axios/commit/9e6205630e1c9cf863adf141c0edb9e6d8d4b149)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+89/-18 (#5919 #5917 )') - avatar [David Dallas](https://github.com/DavidJDallas '+11/-5 ()') - avatar [Sean Sattler](https://github.com/fb-sean '+2/-8 ()') - avatar [Mustafa Ateş Uzun](https://github.com/0o001 '+4/-4 ()') - avatar [Przemyslaw Motacki](https://github.com/sfc-gh-pmotacki '+2/-1 (#5892 )') - avatar [Michael Di Prisco](https://github.com/Cadienvan '+1/-1 ()') ### PRs - CVE 2023 45857 ( [#6028](https://api.github.com/repos/axios/axios/pulls/6028) ) ``` ⚠� Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459 ``` # [1.5.0](https://github.com/axios/axios/compare/v1.4.0...v1.5.0) (2023-08-26) ### Bug Fixes - **adapter:** make adapter loading error more clear by using platform-specific adapters explicitly ([#5837](https://github.com/axios/axios/issues/5837)) ([9a414bb](https://github.com/axios/axios/commit/9a414bb6c81796a95c6c7fe668637825458e8b6d)) - **dns:** fixed `cacheable-lookup` integration; ([#5836](https://github.com/axios/axios/issues/5836)) ([b3e327d](https://github.com/axios/axios/commit/b3e327dcc9277bdce34c7ef57beedf644b00d628)) - **headers:** added support for setting header names that overlap with class methods; ([#5831](https://github.com/axios/axios/issues/5831)) ([d8b4ca0](https://github.com/axios/axios/commit/d8b4ca0ea5f2f05efa4edfe1e7684593f9f68273)) - **headers:** fixed common Content-Type header merging; ([#5832](https://github.com/axios/axios/issues/5832)) ([8fda276](https://github.com/axios/axios/commit/8fda2766b1e6bcb72c3fabc146223083ef13ce17)) ### Features - export getAdapter function ([#5324](https://github.com/axios/axios/issues/5324)) ([ca73eb8](https://github.com/axios/axios/commit/ca73eb878df0ae2dace81fe3a7f1fb5986231bf1)) - **export:** export adapters without `unsafe` prefix ([#5839](https://github.com/axios/axios/issues/5839)) ([1601f4a](https://github.com/axios/axios/commit/1601f4a27a81ab47fea228f1e244b2c4e3ce28bf)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+66/-29 (#5839 #5837 #5836 #5832 #5831 )') - avatar [夜葬](https://github.com/geekact '+42/-0 (#5324 )') - avatar [Jonathan Budiman](https://github.com/JBudiman00 '+30/-0 (#5788 )') - avatar [Michael Di Prisco](https://github.com/Cadienvan '+3/-5 (#5791 )') ### PRs - CVE 2023 45857 ( [#6028](https://api.github.com/repos/axios/axios/pulls/6028) ) ``` ⚠� Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459 ``` # [1.4.0](https://github.com/axios/axios/compare/v1.3.6...v1.4.0) (2023-04-27) ### Bug Fixes - **formdata:** add `multipart/form-data` content type for FormData payload on custom client environments; ([#5678](https://github.com/axios/axios/issues/5678)) ([bbb61e7](https://github.com/axios/axios/commit/bbb61e70cb1185adfb1cbbb86eaf6652c48d89d1)) - **package:** export package internals with unsafe path prefix; ([#5677](https://github.com/axios/axios/issues/5677)) ([df38c94](https://github.com/axios/axios/commit/df38c949f26414d88ba29ec1e353c4d4f97eaf09)) ### Features - **dns:** added support for a custom lookup function; ([#5339](https://github.com/axios/axios/issues/5339)) ([2701911](https://github.com/axios/axios/commit/2701911260a1faa5cc5e1afe437121b330a3b7bb)) - **types:** export `AxiosHeaderValue` type. ([#5525](https://github.com/axios/axios/issues/5525)) ([726f1c8](https://github.com/axios/axios/commit/726f1c8e00cffa0461a8813a9bdcb8f8b9d762cf)) ### Performance Improvements - **merge-config:** optimize mergeConfig performance by avoiding duplicate key visits; ([#5679](https://github.com/axios/axios/issues/5679)) ([e6f7053](https://github.com/axios/axios/commit/e6f7053bf1a3e87cf1f9da8677e12e3fe829d68e)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+151/-16 (#5684 #5339 #5679 #5678 #5677 )') - avatar [Arthur Fiorette](https://github.com/arthurfiorette '+19/-19 (#5525 )') - avatar [PIYUSH NEGI](https://github.com/npiyush97 '+2/-18 (#5670 )') ### PRs - CVE 2023 45857 ( [#6028](https://api.github.com/repos/axios/axios/pulls/6028) ) ``` ⚠� Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459 ``` ## [1.3.6](https://github.com/axios/axios/compare/v1.3.5...v1.3.6) (2023-04-19) ### Bug Fixes - **types:** added transport to RawAxiosRequestConfig ([#5445](https://github.com/axios/axios/issues/5445)) ([6f360a2](https://github.com/axios/axios/commit/6f360a2531d8d70363fd9becef6a45a323f170e2)) - **utils:** make isFormData detection logic stricter to avoid unnecessary calling of the `toString` method on the target; ([#5661](https://github.com/axios/axios/issues/5661)) ([aa372f7](https://github.com/axios/axios/commit/aa372f7306295dfd1100c1c2c77ce95c95808e76)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+48/-10 (#5665 #5661 #5663 )') - avatar [Michael Di Prisco](https://github.com/Cadienvan '+2/-0 (#5445 )') ### PRs - CVE 2023 45857 ( [#6028](https://api.github.com/repos/axios/axios/pulls/6028) ) ``` ⚠� Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459 ``` ## [1.3.5](https://github.com/axios/axios/compare/v1.3.4...v1.3.5) (2023-04-05) ### Bug Fixes - **headers:** fixed isValidHeaderName to support full list of allowed characters; ([#5584](https://github.com/axios/axios/issues/5584)) ([e7decef](https://github.com/axios/axios/commit/e7decef6a99f4627e27ed9ea5b00ce8e201c3841)) - **params:** re-added the ability to set the function as `paramsSerializer` config; ([#5633](https://github.com/axios/axios/issues/5633)) ([a56c866](https://github.com/axios/axios/commit/a56c8661209d5ce5a645a05f294a0e08a6c1f6b3)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+28/-10 (#5633 #5584 )') ### PRs - CVE 2023 45857 ( [#6028](https://api.github.com/repos/axios/axios/pulls/6028) ) ``` ⚠� Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459 ``` ## [1.3.4](https://github.com/axios/axios/compare/v1.3.3...v1.3.4) (2023-02-22) ### Bug Fixes - **blob:** added a check to make sure the Blob class is available in the browser's global scope; ([#5548](https://github.com/axios/axios/issues/5548)) ([3772c8f](https://github.com/axios/axios/commit/3772c8fe74112a56e3e9551f894d899bc3a9443a)) - **http:** fixed regression bug when handling synchronous errors inside the adapter; ([#5564](https://github.com/axios/axios/issues/5564)) ([a3b246c](https://github.com/axios/axios/commit/a3b246c9de5c3bc4b5a742e15add55b375479451)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+38/-26 (#5564 )') - avatar [lcysgsg](https://github.com/lcysgsg '+4/-0 (#5548 )') - avatar [Michael Di Prisco](https://github.com/Cadienvan '+3/-0 (#5444 )') ### PRs - CVE 2023 45857 ( [#6028](https://api.github.com/repos/axios/axios/pulls/6028) ) ``` ⚠� Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459 ``` ## [1.3.3](https://github.com/axios/axios/compare/v1.3.2...v1.3.3) (2023-02-13) ### Bug Fixes - **formdata:** added a check to make sure the FormData class is available in the browser's global scope; ([#5545](https://github.com/axios/axios/issues/5545)) ([a6dfa72](https://github.com/axios/axios/commit/a6dfa72010db5ad52db8bd13c0f98e537e8fd05d)) - **formdata:** fixed setting NaN as Content-Length for form payload in some cases; ([#5535](https://github.com/axios/axios/issues/5535)) ([c19f7bf](https://github.com/axios/axios/commit/c19f7bf770f90ae8307f4ea3104f227056912da1)) - **headers:** fixed the filtering logic of the clear method; ([#5542](https://github.com/axios/axios/issues/5542)) ([ea87ebf](https://github.com/axios/axios/commit/ea87ebfe6d1699af072b9e7cd40faf8f14b0ab93)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+11/-7 (#5545 #5535 #5542 )') - avatar [陈若枫](https://github.com/ruofee '+2/-2 (#5467 )') ### PRs - CVE 2023 45857 ( [#6028](https://api.github.com/repos/axios/axios/pulls/6028) ) ``` ⚠� Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459 ``` ## [1.3.2](https://github.com/axios/axios/compare/v1.3.1...v1.3.2) (2023-02-03) ### Bug Fixes - **http:** treat http://localhost as base URL for relative paths to avoid `ERR_INVALID_URL` error; ([#5528](https://github.com/axios/axios/issues/5528)) ([128d56f](https://github.com/axios/axios/commit/128d56f4a0fb8f5f2ed6e0dd80bc9225fee9538c)) - **http:** use explicit import instead of TextEncoder global; ([#5530](https://github.com/axios/axios/issues/5530)) ([6b3c305](https://github.com/axios/axios/commit/6b3c305fc40c56428e0afabedc6f4d29c2830f6f)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+2/-1 (#5530 #5528 )') ### PRs - CVE 2023 45857 ( [#6028](https://api.github.com/repos/axios/axios/pulls/6028) ) ``` ⚠� Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459 ``` ## [1.3.1](https://github.com/axios/axios/compare/v1.3.0...v1.3.1) (2023-02-01) ### Bug Fixes - **formdata:** add hotfix to use the asynchronous API to compute the content-length header value; ([#5521](https://github.com/axios/axios/issues/5521)) ([96d336f](https://github.com/axios/axios/commit/96d336f527619f21da012fe1f117eeb53e5a2120)) - **serializer:** fixed serialization of array-like objects; ([#5518](https://github.com/axios/axios/issues/5518)) ([08104c0](https://github.com/axios/axios/commit/08104c028c0f9353897b1b6691d74c440fd0c32d)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+27/-8 (#5521 #5518 )') ### PRs - CVE 2023 45857 ( [#6028](https://api.github.com/repos/axios/axios/pulls/6028) ) ``` ⚠� Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459 ``` # [1.3.0](https://github.com/axios/axios/compare/v1.2.6...v1.3.0) (2023-01-31) ### Bug Fixes - **headers:** fixed & optimized clear method; ([#5507](https://github.com/axios/axios/issues/5507)) ([9915635](https://github.com/axios/axios/commit/9915635c69d0ab70daca5738488421f67ca60959)) - **http:** add zlib headers if missing ([#5497](https://github.com/axios/axios/issues/5497)) ([65e8d1e](https://github.com/axios/axios/commit/65e8d1e28ce829f47a837e45129730e541950d3c)) ### Features - **fomdata:** added support for spec-compliant FormData & Blob types; ([#5316](https://github.com/axios/axios/issues/5316)) ([6ac574e](https://github.com/axios/axios/commit/6ac574e00a06731288347acea1e8246091196953)) ### Contributors to this release - avatar [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+352/-67 (#5514 #5512 #5510 #5509 #5508 #5316 #5507 )') - avatar [ItsNotGoodName](https://github.com/ItsNotGoodName '+43/-2 (#5497 )') ### PRs - CVE 2023 45857 ( [#6028](https://api.github.com/repos/axios/axios/pulls/6028) ) ``` ⚠� Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459 ``` ## [1.2.6](https://github.com/axios/axios/compare/v1.2.5...v1.2.6) (2023-01-28) ### Bug Fixes - **headers:** added missed Authorization accessor; ([#5502](https://github.com/axios/axios/issues/5502)) ([342c0ba](https://github.com/axios/axios/commit/342c0ba9a16ea50f5ed7d2366c5c1a2c877e3f26)) - **types:** fixed `CommonRequestHeadersList` & `CommonResponseHeadersList` types to be private in commonJS; ([#5503](https://github.com/axios/axios/issues/5503)) ([5a3d0a3](https://github.com/axios/axios/commit/5a3d0a3234d77361a1bc7cedee2da1e11df08e2c)) ### Contributors to this release - ![avatar](https://avatars.githubusercontent.com/u/12586868?v=4&s=16) [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+24/-9 (#5503 #5502 )') ### PRs - CVE 2023 45857 ( [#6028](https://api.github.com/repos/axios/axios/pulls/6028) ) ``` ⚠� Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459 ``` ## [1.2.5](https://github.com/axios/axios/compare/v1.2.4...v1.2.5) (2023-01-26) ### Bug Fixes - **types:** fixed AxiosHeaders to handle spread syntax by making all methods non-enumerable; ([#5499](https://github.com/axios/axios/issues/5499)) ([580f1e8](https://github.com/axios/axios/commit/580f1e8033a61baa38149d59fd16019de3932c22)) ### Contributors to this release - ![avatar](https://avatars.githubusercontent.com/u/12586868?v=4&s=16) [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+82/-54 (#5499 )') - ![avatar](https://avatars.githubusercontent.com/u/20516159?v=4&s=16) [Elliot Ford](https://github.com/EFord36 '+1/-1 (#5462 )') ### PRs - CVE 2023 45857 ( [#6028](https://api.github.com/repos/axios/axios/pulls/6028) ) ``` ⚠� Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459 ``` ## [1.2.4](https://github.com/axios/axios/compare/v1.2.3...v1.2.4) (2023-01-22) ### Bug Fixes - **types:** renamed `RawAxiosRequestConfig` back to `AxiosRequestConfig`; ([#5486](https://github.com/axios/axios/issues/5486)) ([2a71f49](https://github.com/axios/axios/commit/2a71f49bc6c68495fa419003a3107ed8bd703ad0)) - **types:** fix `AxiosRequestConfig` generic; ([#5478](https://github.com/axios/axios/issues/5478)) ([9bce81b](https://github.com/axios/axios/commit/186ea062da8b7d578ae78b1a5c220986b9bce81b)) ### Contributors to this release - ![avatar](https://avatars.githubusercontent.com/u/12586868?v=4&s=16) [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+242/-108 (#5486 #5482 )') - ![avatar](https://avatars.githubusercontent.com/u/9430821?v=4&s=16) [Daniel Hillmann](https://github.com/hilleer '+1/-1 (#5478 )') ### PRs - CVE 2023 45857 ( [#6028](https://api.github.com/repos/axios/axios/pulls/6028) ) ``` ⚠� Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459 ``` ## [1.2.3](https://github.com/axios/axios/compare/1.2.2...1.2.3) (2023-01-10) ### Bug Fixes - **types:** fixed AxiosRequestConfig header interface by refactoring it to RawAxiosRequestConfig; ([#5420](https://github.com/axios/axios/issues/5420)) ([0811963](https://github.com/axios/axios/commit/08119634a22f1d5b19f5c9ea0adccb6d3eebc3bc)) ### Contributors to this release - ![avatar](https://avatars.githubusercontent.com/u/12586868?v=4&s=16) [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS '+938/-442 (#5456 #5455 #5453 #5451 #5449 #5447 #5446 #5443 #5442 #5439 #5420 )') ### PRs - CVE 2023 45857 ( [#6028](https://api.github.com/repos/axios/axios/pulls/6028) ) ``` ⚠� Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459 ``` ## [1.2.2] - 2022-12-29 ### Fixed - fix(ci): fix release script inputs [#5392](https://github.com/axios/axios/pull/5392) - fix(ci): prerelease scipts [#5377](https://github.com/axios/axios/pull/5377) - fix(ci): release scripts [#5376](https://github.com/axios/axios/pull/5376) - fix(ci): typescript tests [#5375](https://github.com/axios/axios/pull/5375) - fix: Brotli decompression [#5353](https://github.com/axios/axios/pull/5353) - fix: add missing HttpStatusCode [#5345](https://github.com/axios/axios/pull/5345) ### Chores - chore(ci): set conventional-changelog header config [#5406](https://github.com/axios/axios/pull/5406) - chore(ci): fix automatic contributors resolving [#5403](https://github.com/axios/axios/pull/5403) - chore(ci): improved logging for the contributors list generator [#5398](https://github.com/axios/axios/pull/5398) - chore(ci): fix release action [#5397](https://github.com/axios/axios/pull/5397) - chore(ci): fix version bump script by adding bump argument for target version [#5393](https://github.com/axios/axios/pull/5393) - chore(deps): bump decode-uri-component from 0.2.0 to 0.2.2 [#5342](https://github.com/axios/axios/pull/5342) - chore(ci): GitHub Actions Release script [#5384](https://github.com/axios/axios/pull/5384) - chore(ci): release scripts [#5364](https://github.com/axios/axios/pull/5364) ### Contributors to this release - ![avatar](https://avatars.githubusercontent.com/u/12586868?v=4&s=16) [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS) - ![avatar](https://avatars.githubusercontent.com/u/1652293?v=4&s=16) [Winnie](https://github.com/winniehell) ## [1.2.1] - 2022-12-05 ### Changed - feat(exports): export mergeConfig [#5151](https://github.com/axios/axios/pull/5151) ### Fixed - fix(CancelledError): include config [#4922](https://github.com/axios/axios/pull/4922) - fix(general): removing multiple/trailing/leading whitespace [#5022](https://github.com/axios/axios/pull/5022) - fix(headers): decompression for responses without Content-Length header [#5306](https://github.com/axios/axios/pull/5306) - fix(webWorker): exception to sending form data in web worker [#5139](https://github.com/axios/axios/pull/5139) ### Refactors - refactor(types): AxiosProgressEvent.event type to any [#5308](https://github.com/axios/axios/pull/5308) - refactor(types): add missing types for static AxiosError.from method [#4956](https://github.com/axios/axios/pull/4956) ### Chores - chore(docs): remove README link to non-existent upgrade guide [#5307](https://github.com/axios/axios/pull/5307) - chore(docs): typo in issue template name [#5159](https://github.com/axios/axios/pull/5159) ### Contributors to this release - [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS) - [Zachary Lysobey](https://github.com/zachlysobey) - [Kevin Ennis](https://github.com/kevincennis) - [Philipp Loose](https://github.com/phloose) - [secondl1ght](https://github.com/secondl1ght) - [wenzheng](https://github.com/0x30) - [Ivan Barsukov](https://github.com/ovarn) - [Arthur Fiorette](https://github.com/arthurfiorette) ### PRs - CVE 2023 45857 ( [#6028](https://api.github.com/repos/axios/axios/pulls/6028) ) ``` ⚠� Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459 ``` ## [1.2.0] - 2022-11-10 ### Changed - changed: refactored module exports [#5162](https://github.com/axios/axios/pull/5162) - change: re-added support for loading Axios with require('axios').default [#5225](https://github.com/axios/axios/pull/5225) ### Fixed - fix: improve AxiosHeaders class [#5224](https://github.com/axios/axios/pull/5224) - fix: TypeScript type definitions for commonjs [#5196](https://github.com/axios/axios/pull/5196) - fix: type definition of use method on AxiosInterceptorManager to match the README [#5071](https://github.com/axios/axios/pull/5071) - fix: \_\_dirname is not defined in the sandbox [#5269](https://github.com/axios/axios/pull/5269) - fix: AxiosError.toJSON method to avoid circular references [#5247](https://github.com/axios/axios/pull/5247) - fix: Z_BUF_ERROR when content-encoding is set but the response body is empty [#5250](https://github.com/axios/axios/pull/5250) ### Refactors - refactor: allowing adapters to be loaded by name [#5277](https://github.com/axios/axios/pull/5277) ### Chores - chore: force CI restart [#5243](https://github.com/axios/axios/pull/5243) - chore: update ECOSYSTEM.md [#5077](https://github.com/axios/axios/pull/5077) - chore: update get/index.html [#5116](https://github.com/axios/axios/pull/5116) - chore: update Sandbox UI/UX [#5205](https://github.com/axios/axios/pull/5205) - chore:(actions): remove git credentials after checkout [#5235](https://github.com/axios/axios/pull/5235) - chore(actions): bump actions/dependency-review-action from 2 to 3 [#5266](https://github.com/axios/axios/pull/5266) - chore(packages): bump loader-utils from 1.4.1 to 1.4.2 [#5295](https://github.com/axios/axios/pull/5295) - chore(packages): bump engine.io from 6.2.0 to 6.2.1 [#5294](https://github.com/axios/axios/pull/5294) - chore(packages): bump socket.io-parser from 4.0.4 to 4.0.5 [#5241](https://github.com/axios/axios/pull/5241) - chore(packages): bump loader-utils from 1.4.0 to 1.4.1 [#5245](https://github.com/axios/axios/pull/5245) - chore(docs): update Resources links in README [#5119](https://github.com/axios/axios/pull/5119) - chore(docs): update the link for JSON url [#5265](https://github.com/axios/axios/pull/5265) - chore(docs): fix broken links [#5218](https://github.com/axios/axios/pull/5218) - chore(docs): update and rename UPGRADE_GUIDE.md to MIGRATION_GUIDE.md [#5170](https://github.com/axios/axios/pull/5170) - chore(docs): typo fix line #856 and #920 [#5194](https://github.com/axios/axios/pull/5194) - chore(docs): typo fix #800 [#5193](https://github.com/axios/axios/pull/5193) - chore(docs): fix typos [#5184](https://github.com/axios/axios/pull/5184) - chore(docs): fix punctuation in README.md [#5197](https://github.com/axios/axios/pull/5197) - chore(docs): update readme in the Handling Errors section - issue reference #5260 [#5261](https://github.com/axios/axios/pull/5261) - chore: remove \b from filename [#5207](https://github.com/axios/axios/pull/5207) - chore(docs): update CHANGELOG.md [#5137](https://github.com/axios/axios/pull/5137) - chore: add sideEffects false to package.json [#5025](https://github.com/axios/axios/pull/5025) ### Contributors to this release - [Maddy Miller](https://github.com/me4502) - [Amit Saini](https://github.com/amitsainii) - [ecyrbe](https://github.com/ecyrbe) - [Ikko Ashimine](https://github.com/eltociear) - [Geeth Gunnampalli](https://github.com/thetechie7) - [Shreem Asati](https://github.com/shreem-123) - [Frieder Bluemle](https://github.com/friederbluemle) - [윤세�](https://github.com/yunseyeong) - [Claudio Busatto](https://github.com/cjcbusatto) - [Remco Haszing](https://github.com/remcohaszing) - [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS) - [Csaba Maulis](https://github.com/om4csaba) - [MoPaMo](https://github.com/MoPaMo) - [Daniel Fjeldstad](https://github.com/w3bdesign) - [Adrien Brunet](https://github.com/adrien-may) - [Frazer Smith](https://github.com/Fdawgs) - [HaiTao](https://github.com/836334258) - [AZM](https://github.com/aziyatali) - [relbns](https://github.com/relbns) ### PRs - CVE 2023 45857 ( [#6028](https://api.github.com/repos/axios/axios/pulls/6028) ) ``` ⚠� Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459 ``` ## [1.1.3] - 2022-10-15 ### Added - Added custom params serializer support [#5113](https://github.com/axios/axios/pull/5113) ### Fixed - Fixed top-level export to keep them in-line with static properties [#5109](https://github.com/axios/axios/pull/5109) - Stopped including null values to query string. [#5108](https://github.com/axios/axios/pull/5108) - Restored proxy config backwards compatibility with 0.x [#5097](https://github.com/axios/axios/pull/5097) - Added back AxiosHeaders in AxiosHeaderValue [#5103](https://github.com/axios/axios/pull/5103) - Pin CDN install instructions to a specific version [#5060](https://github.com/axios/axios/pull/5060) - Handling of array values fixed for AxiosHeaders [#5085](https://github.com/axios/axios/pull/5085) ### Chores - docs: match badge style, add link to them [#5046](https://github.com/axios/axios/pull/5046) - chore: fixing comments typo [#5054](https://github.com/axios/axios/pull/5054) - chore: update issue template [#5061](https://github.com/axios/axios/pull/5061) - chore: added progress capturing section to the docs; [#5084](https://github.com/axios/axios/pull/5084) ### Contributors to this release - [Jason Saayman](https://github.com/jasonsaayman) - [scarf](https://github.com/scarf005) - [Lenz Weber-Tronic](https://github.com/phryneas) - [Arvindh](https://github.com/itsarvindh) - [Félix Legrelle](https://github.com/FelixLgr) - [Patrick Petrovic](https://github.com/ppati000) - [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS) - [littledian](https://github.com/littledian) - [ChronosMasterOfAllTime](https://github.com/ChronosMasterOfAllTime) ### PRs - CVE 2023 45857 ( [#6028](https://api.github.com/repos/axios/axios/pulls/6028) ) ``` ⚠� Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459 ``` ## [1.1.2] - 2022-10-07 ### Fixed - Fixed broken exports for UMD builds. ### Contributors to this release - [Jason Saayman](https://github.com/jasonsaayman) ### PRs - CVE 2023 45857 ( [#6028](https://api.github.com/repos/axios/axios/pulls/6028) ) ``` ⚠� Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459 ``` ## [1.1.1] - 2022-10-07 ### Fixed - Fixed broken exports for common js. This fix breaks a prior fix, I will fix both issues ASAP but the commonJS use is more impactful. ### Contributors to this release - [Jason Saayman](https://github.com/jasonsaayman) ### PRs - CVE 2023 45857 ( [#6028](https://api.github.com/repos/axios/axios/pulls/6028) ) ``` ⚠� Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459 ``` ## [1.1.0] - 2022-10-06 ### Fixed - Fixed missing exports in type definition index.d.ts [#5003](https://github.com/axios/axios/pull/5003) - Fixed query params composing [#5018](https://github.com/axios/axios/pull/5018) - Fixed GenericAbortSignal interface by making it more generic [#5021](https://github.com/axios/axios/pull/5021) - Fixed adding "clear" to AxiosInterceptorManager [#5010](https://github.com/axios/axios/pull/5010) - Fixed commonjs & umd exports [#5030](https://github.com/axios/axios/pull/5030) - Fixed inability to access response headers when using axios 1.x with Jest [#5036](https://github.com/axios/axios/pull/5036) ### Contributors to this release - [Trim21](https://github.com/trim21) - [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS) - [shingo.sasaki](https://github.com/s-sasaki-0529) - [Ivan Pepelko](https://github.com/ivanpepelko) - [Richard Kořínek](https://github.com/risa) ### PRs - CVE 2023 45857 ( [#6028](https://api.github.com/repos/axios/axios/pulls/6028) ) ``` ⚠� Critical vulnerability fix. See https://security.snyk.io/vuln/SNYK-JS-AXIOS-6032459 ``` ## [1.0.0] - 2022-10-04 ### Added - Added stack trace to AxiosError [#4624](https://github.com/axios/axios/pull/4624) - Add AxiosError to AxiosStatic [#4654](https://github.com/axios/axios/pull/4654) - Replaced Rollup as our build runner [#4596](https://github.com/axios/axios/pull/4596) - Added generic TS types for the exposed toFormData helper [#4668](https://github.com/axios/axios/pull/4668) - Added listen callback function [#4096](https://github.com/axios/axios/pull/4096) - Added instructions for installing using PNPM [#4207](https://github.com/axios/axios/pull/4207) - Added generic AxiosAbortSignal TS interface to avoid importing AbortController polyfill [#4229](https://github.com/axios/axios/pull/4229) - Added axios-url-template in ECOSYSTEM.md [#4238](https://github.com/axios/axios/pull/4238) - Added a clear() function to the request and response interceptors object so a user can ensure that all interceptors have been removed from an axios instance [#4248](https://github.com/axios/axios/pull/4248) - Added react hook plugin [#4319](https://github.com/axios/axios/pull/4319) - Adding HTTP status code for transformResponse [#4580](https://github.com/axios/axios/pull/4580) - Added blob to the list of protocols supported by the browser [#4678](https://github.com/axios/axios/pull/4678) - Resolving proxy from env on redirect [#4436](https://github.com/axios/axios/pull/4436) - Added enhanced toFormData implementation with additional options [4704](https://github.com/axios/axios/pull/4704) - Adding Canceler parameters config and request [#4711](https://github.com/axios/axios/pull/4711) - Added automatic payload serialization to application/x-www-form-urlencoded [#4714](https://github.com/axios/axios/pull/4714) - Added the ability for webpack users to overwrite built-ins [#4715](https://github.com/axios/axios/pull/4715) - Added string[] to AxiosRequestHeaders type [#4322](https://github.com/axios/axios/pull/4322) - Added the ability for the url-encoded-form serializer to respect the formSerializer config [#4721](https://github.com/axios/axios/pull/4721) - Added isCancel type assert [#4293](https://github.com/axios/axios/pull/4293) - Added data URL support for node.js [#4725](https://github.com/axios/axios/pull/4725) - Adding types for progress event callbacks [#4675](https://github.com/axios/axios/pull/4675) - URL params serializer [#4734](https://github.com/axios/axios/pull/4734) - Added axios.formToJSON method [#4735](https://github.com/axios/axios/pull/4735) - Bower platform add data protocol [#4804](https://github.com/axios/axios/pull/4804) - Use WHATWG URL API instead of url.parse() [#4852](https://github.com/axios/axios/pull/4852) - Add ENUM containing Http Status Codes to typings [#4903](https://github.com/axios/axios/pull/4903) - Improve typing of timeout in index.d.ts [#4934](https://github.com/axios/axios/pull/4934) ### Changed - Updated AxiosError.config to be optional in the type definition [#4665](https://github.com/axios/axios/pull/4665) - Updated README emphasizing the URLSearchParam built-in interface over other solutions [#4590](https://github.com/axios/axios/pull/4590) - Include request and config when creating a CanceledError instance [#4659](https://github.com/axios/axios/pull/4659) - Changed func-names eslint rule to as-needed [#4492](https://github.com/axios/axios/pull/4492) - Replacing deprecated substr() with slice() as substr() is deprecated [#4468](https://github.com/axios/axios/pull/4468) - Updating HTTP links in README.md to use HTTPS [#4387](https://github.com/axios/axios/pull/4387) - Updated to a better trim() polyfill [#4072](https://github.com/axios/axios/pull/4072) - Updated types to allow specifying partial default headers on instance create [#4185](https://github.com/axios/axios/pull/4185) - Expanded isAxiosError types [#4344](https://github.com/axios/axios/pull/4344) - Updated type definition for axios instance methods [#4224](https://github.com/axios/axios/pull/4224) - Updated eslint config [#4722](https://github.com/axios/axios/pull/4722) - Updated Docs [#4742](https://github.com/axios/axios/pull/4742) - Refactored Axios to use ES2017 [#4787](https://github.com/axios/axios/pull/4787) ### Deprecated - There are multiple deprecations, refactors and fixes provided in this release. Please read through the full release notes to see how this may impact your project and use case. ### Removed - Removed incorrect argument for NetworkError constructor [#4656](https://github.com/axios/axios/pull/4656) - Removed Webpack [#4596](https://github.com/axios/axios/pull/4596) - Removed function that transform arguments to array [#4544](https://github.com/axios/axios/pull/4544) ### Fixed - Fixed grammar in README [#4649](https://github.com/axios/axios/pull/4649) - Fixed code error in README [#4599](https://github.com/axios/axios/pull/4599) - Optimized the code that checks cancellation [#4587](https://github.com/axios/axios/pull/4587) - Fix url pointing to defaults.js in README [#4532](https://github.com/axios/axios/pull/4532) - Use type alias instead of interface for AxiosPromise [#4505](https://github.com/axios/axios/pull/4505) - Fix some word spelling and lint style in code comments [#4500](https://github.com/axios/axios/pull/4500) - Edited readme with 3 updated browser icons of Chrome, FireFox and Safari [#4414](https://github.com/axios/axios/pull/4414) - Bump follow-redirects from 1.14.9 to 1.15.0 [#4673](https://github.com/axios/axios/pull/4673) - Fixing http tests to avoid hanging when assertions fail [#4435](https://github.com/axios/axios/pull/4435) - Fix TS definition for AxiosRequestTransformer [#4201](https://github.com/axios/axios/pull/4201) - Fix grammatical issues in README [#4232](https://github.com/axios/axios/pull/4232) - Fixing instance.defaults.headers type [#4557](https://github.com/axios/axios/pull/4557) - Fixed race condition on immediate requests cancellation [#4261](https://github.com/axios/axios/pull/4261) - Fixing Z_BUF_ERROR when no content [#4701](https://github.com/axios/axios/pull/4701) - Fixing proxy beforeRedirect regression [#4708](https://github.com/axios/axios/pull/4708) - Fixed AxiosError status code type [#4717](https://github.com/axios/axios/pull/4717) - Fixed AxiosError stack capturing [#4718](https://github.com/axios/axios/pull/4718) - Fixing AxiosRequestHeaders typings [#4334](https://github.com/axios/axios/pull/4334) - Fixed max body length defaults [#4731](https://github.com/axios/axios/pull/4731) - Fixed toFormData Blob issue on node>v17 [#4728](https://github.com/axios/axios/pull/4728) - Bump grunt from 1.5.2 to 1.5.3 [#4743](https://github.com/axios/axios/pull/4743) - Fixing content-type header repeated [#4745](https://github.com/axios/axios/pull/4745) - Fixed timeout error message for http [4738](https://github.com/axios/axios/pull/4738) - Request ignores false, 0 and empty string as body values [#4785](https://github.com/axios/axios/pull/4785) - Added back missing minified builds [#4805](https://github.com/axios/axios/pull/4805) - Fixed a type error [#4815](https://github.com/axios/axios/pull/4815) - Fixed a regression bug with unsubscribing from cancel token; [#4819](https://github.com/axios/axios/pull/4819) - Remove repeated compression algorithm [#4820](https://github.com/axios/axios/pull/4820) - The error of calling extend to pass parameters [#4857](https://github.com/axios/axios/pull/4857) - SerializerOptions.indexes allows boolean | null | undefined [#4862](https://github.com/axios/axios/pull/4862) - Require interceptors to return values [#4874](https://github.com/axios/axios/pull/4874) - Removed unused imports [#4949](https://github.com/axios/axios/pull/4949) - Allow null indexes on formSerializer and paramsSerializer [#4960](https://github.com/axios/axios/pull/4960) ### Chores - Set permissions for GitHub actions [#4765](https://github.com/axios/axios/pull/4765) - Included githubactions in the dependabot config [#4770](https://github.com/axios/axios/pull/4770) - Included dependency review [#4771](https://github.com/axios/axios/pull/4771) - Update security.md [#4784](https://github.com/axios/axios/pull/4784) - Remove unnecessary spaces [#4854](https://github.com/axios/axios/pull/4854) - Simplify the import path of AxiosError [#4875](https://github.com/axios/axios/pull/4875) - Fix Gitpod dead link [#4941](https://github.com/axios/axios/pull/4941) - Enable syntax highlighting for a code block [#4970](https://github.com/axios/axios/pull/4970) - Using Logo Axios in Readme.md [#4993](https://github.com/axios/axios/pull/4993) - Fix markup for note in README [#4825](https://github.com/axios/axios/pull/4825) - Fix typo and formatting, add colons [#4853](https://github.com/axios/axios/pull/4853) - Fix typo in readme [#4942](https://github.com/axios/axios/pull/4942) ### Security - Update SECURITY.md [#4687](https://github.com/axios/axios/pull/4687) ### Contributors to this release - [Bertrand Marron](https://github.com/tusbar) - [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS) - [Dan Mooney](https://github.com/danmooney) - [Michael Li](https://github.com/xiaoyu-tamu) - [aong](https://github.com/yxwzaxns) - [Des Preston](https://github.com/despreston) - [Ted Robertson](https://github.com/tredondo) - [zhoulixiang](https://github.com/zh-lx) - [Arthur Fiorette](https://github.com/arthurfiorette) - [Kumar Shanu](https://github.com/Kr-Shanu) - [JALAL](https://github.com/JLL32) - [Jingyi Lin](https://github.com/MageeLin) - [Philipp Loose](https://github.com/phloose) - [Alexander Shchukin](https://github.com/sashsvamir) - [Dave Cardwell](https://github.com/davecardwell) - [Cat Scarlet](https://github.com/catscarlet) - [Luca Pizzini](https://github.com/lpizzinidev) - [Kai](https://github.com/Schweinepriester) - [Maxime Bargiel](https://github.com/mbargiel) - [Brian Helba](https://github.com/brianhelba) - [reslear](https://github.com/reslear) - [Jamie Slome](https://github.com/JamieSlome) - [Landro3](https://github.com/Landro3) - [rafw87](https://github.com/rafw87) - [Afzal Sayed](https://github.com/afzalsayed96) - [Koki Oyatsu](https://github.com/kaishuu0123) - [Dave](https://github.com/wangcch) - [暴走�七](https://github.com/baozouai) - [Spencer](https://github.com/spalger) - [Adrian Wieprzkowicz](https://github.com/Argeento) - [Jamie Telin](https://github.com/lejahmie) - [毛呆](https://github.com/aweikalee) - [Kirill Shakirov](https://github.com/turisap) - [Rraji Abdelbari](https://github.com/estarossa0) - [Jelle Schutter](https://github.com/jelleschutter) - [Tom Ceuppens](https://github.com/KyorCode) - [Johann Cooper](https://github.com/JohannCooper) - [Dimitris Halatsis](https://github.com/mitsos1os) - [chenjigeng](https://github.com/chenjigeng) - [João Gabriel Quaresma](https://github.com/joaoGabriel55) - [Victor Augusto](https://github.com/VictorAugDB) - [neilnaveen](https://github.com/neilnaveen) - [Pavlos](https://github.com/psmoros) - [Kiryl Valkovich](https://github.com/visortelle) - [Naveen](https://github.com/naveensrinivasan) - [wenzheng](https://github.com/0x30) - [hcwhan](https://github.com/hcwhan) - [Bassel Rachid](https://github.com/basselworkforce) - [Grégoire Pineau](https://github.com/lyrixx) - [felipedamin](https://github.com/felipedamin) - [Karl Horky](https://github.com/karlhorky) - [Yue JIN](https://github.com/kingyue737) - [Usman Ali Siddiqui](https://github.com/usman250994) - [WD](https://github.com/techbirds) - [Günther Foidl](https://github.com/gfoidl) - [Stephen Jennings](https://github.com/jennings) - [C.T.Lin](https://github.com/chentsulin) - [mia-z](https://github.com/mia-z) - [Parth Banathia](https://github.com/Parth0105) - [parth0105pluang](https://github.com/parth0105pluang) - [Marco Weber](https://github.com/mrcwbr) - [Luca Pizzini](https://github.com/lpizzinidev) - [Willian Agostini](https://github.com/WillianAgostini) - [Huyen Nguyen](https://github.com/huyenltnguyen) axios-axios-aa54a5e/CODE_OF_CONDUCT.md000066400000000000000000000031721526660636600172670ustar00rootroot00000000000000# Code of conduct ## Our pledge Contributors and community members commit to a welcoming, safe, and harassment-free environment for everyone. We welcome people regardless of age, body size, disability, ethnicity, gender identity or expression, experience level, education, nationality, race, religion, sexual orientation, or any other personal characteristic. ## Our standards Examples of positive behavior include: - Showing empathy and kindness toward others - Being respectful of different opinions and experiences - Accepting constructive feedback and learning from it - Taking responsibility for mistakes and offering sincere apologies Unacceptable behaviors include: - Sexualized language, imagery, or unwanted advances - Personal attacks, trolling, or insulting comments - Publishing private information (such as email or address) without permission - Any other conduct that would be considered inappropriate in a professional setting ## Enforcement Report unacceptable behavior to the maintainers at [jasonsaayman@gmail.com](mailto:jasonsaayman@gmail.com). Maintainers will review reports promptly and handle them confidentially. ## Enforcement guidelines 1. Correction: a private warning and an explanation of why the behavior was inappropriate. 2. Warning: a clear warning with consequences for continued behavior. 3. Temporary ban: a temporary ban for repeated or severe violations. 4. Permanent ban: permanent removal for repeated harassment or sustained inappropriate conduct. --- _This code of conduct is adapted from the [Contributor Covenant, version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html)._ axios-axios-aa54a5e/COLLABORATOR_GUIDE.md000066400000000000000000000113751526660636600176160ustar00rootroot00000000000000# Collaborator guide As a collaborator, you help administer axios. This guide covers the responsibilities that come with that access. For architecture, lifecycle, security-sensitive code, and conventions, use [AGENTS.md](./AGENTS.md), the canonical contributor guide. ## Code of conduct Read the [Code of Conduct](./CODE_OF_CONDUCT.md) and help enforce it. Keep the community friendly and welcoming. ## Triage issues - Apply appropriate labels and respond as needed. - For bug reports, ask for a minimal reproduction (axios version, environment, request/response details) before deeper triage. - If an issue is not directly about axios, convert it to a discussion. - Close issues only when they are resolved, a fix is merged, the report lacks enough detail or a reproduction, or the reporter requests closure. Do not close issues for inactivity. We want to keep the history and respond if more information comes in later. ## Answer questions Be helpful and patient. If a question comes from unclear docs, update the docs and consider adding an example instead of only answering in the thread. You are not expected to teach JavaScript or unrelated tooling. Redirect those questions politely. ## Submit PRs When opening a PR, make sure: - The change fits within axios. Decline features early and kindly when they belong in user code or a plugin. - Cover behavior across the XHR, Fetch, and Node HTTP adapters where relevant. Detect by capability, not environment name. - Update both `index.d.ts` (ESM) and `index.d.cts` (CJS) when the public API changes. - Make axios-originated failures throw `AxiosError` with an appropriate code, never raw `Error`. Wrap third-party errors with `AxiosError.from`. - Cover the change with unit tests. Update browser, smoke, or module suites when packaging or runtime surface is affected. - Lint and tests pass before review. Do not merge red PRs. - Do not add runtime dependencies without discussion. `package-lock.json` changes must keep `lockfile-lint` happy (npm HTTPS hosts, integrity hashes). - Package and GitHub Actions update PRs are maintainer/bot-only. Close PRs from outside collaborators that only update npm packages, lockfiles, or GitHub Actions versions. - Keep the 7-day Dependabot delay for these updates. Bypass it only when a critical vulnerability requires a maintainer-led manual update. - Security-sensitive changes get extra scrutiny and focused regression tests. This includes URL construction, redirects, proxy/env handling, XSRF, socket paths, decompression limits, prototype walking, and adapters. Consult [THREATMODEL.md](./THREATMODEL.md). - Warn before removing functionality. - New public API surface is predictable, consistent with existing options, and documented. - PR titles use [Conventional Commits](https://www.conventionalcommits.org/) (`fix:`, `feat:`, `chore:`, `docs:`, etc.). Release tooling depends on this. - Call out whether the change is patch, minor, or breaking, and target the right branch (`v1.x` for the current maintenance line; breaking work goes elsewhere). At least one maintainer must review and approve a PR before merge. If you are unsure about the impact of a change, ask for a second opinion. Call out breaking changes in the PR description and send them to the appropriate branch. Bug fixes need a test that reproduces the issue and verifies the fix. If changes are requested, address them promptly. If you cannot make the changes, say so clearly so someone else can pick them up. > [!IMPORTANT] > We wait up to 28 days for a response to requested changes before closing the PR as stale. After that, we will either address the issue in a maintainer-led PR or open an issue for other contributors. If the author wants to continue the work, they should recreate the PR from the latest version of the correct target branch, address all feedback, and request review from a maintainer. ## Security disclosures If someone reports a suspected vulnerability in a public issue, do not discuss specifics in the thread. Redirect them to the process described in [SECURITY.md](./SECURITY.md) (GitHub security advisories) and close or hide the issue as appropriate. ## What collaborators should not do - Add runtime dependencies without discussion. The dependency surface is intentionally tiny. - Merge package, lockfile, or GitHub Actions version update PRs from outside collaborators. - Disable `ignore-scripts` in `.npmrc` or otherwise weaken install-time safety. - Weaken `beforeRedirect`, proxy, `socketPath`, XSRF, or prototype-pollution safeguards without tests covering the regression cases. --- Thanks for helping keep axios healthy. If you are unsure about a call, ask another collaborator before acting. We would rather move a little slower than ship a regression. If you have questions about your role or responsibilities, contact the maintainers. axios-axios-aa54a5e/CONTRIBUTING.md000066400000000000000000000051711526660636600167220ustar00rootroot00000000000000# Contributing We accept community contributions. By contributing to axios, you agree to follow the [code of conduct](https://github.com/axios/axios/blob/master/CODE_OF_CONDUCT.md). Before submitting a PR for review, complete the [mandatory submission requirements in AGENTS.md](AGENTS.md#before-opening-or-updating-a-pull-request) and the [PR template](.github/PULL_REQUEST_TEMPLATE.md). These requirements apply to human and AI contributors. Every PR must add or update `PRE_RELEASE_CHANGELOG.md`, including documentation-only and test-only changes, except for the narrow [automated release-preparation exception](AGENTS.md#pre-release-notes). Use the Maintenance section for test-only changes, internal refactors, tooling, dependency updates, and CI changes without a user-visible behavior change. ## Code style Follow the [node style guide](https://github.com/felixge/node-style-guide). ## Commit messages Follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/). ## Testing Update tests for your changes. Pull requests must pass GitHub Actions. ## Documentation Keep the API and documentation in sync. For unreleased runtime/API changes, record the required documentation updates in `PRE_RELEASE_DOCS.md` so they can be applied during release preparation. Follow the [prerelease notes rules](AGENTS.md#pre-release-notes); `CHANGELOG.md` is reserved for release preparation. ## Dependency and GitHub Actions updates Please do not open pull requests that only update npm packages, lockfiles, or GitHub Actions versions. We close these PRs from outside collaborators. Only maintainers and approved automated bots may create package and GitHub Actions update PRs. We keep the 7-day Dependabot delay for these updates unless a critical vulnerability requires a maintainer-led manual update. ## Developing - `npm run lint` checks the library source - `npm test` runs the unit and browser test suites; install the browsers first with `npx playwright install` - `npm run test:vitest:unit` runs the Node unit tests - `npm run test:vitest:browser:headless` runs the Chromium, Firefox, and WebKit browser tests; install the browsers first with `npx playwright install` (`npx playwright install --with-deps` also installs system dependencies on supported Linux hosts) - `npm run build` runs Rollup and bundles the source - `npm run version` prepares the code for release ## Running examples Use the examples for manual testing. Run the examples: ```bash > npm run examples # Open 127.0.0.1:3000 ``` Run the browser sandbox: ```bash > npm start # Open 127.0.0.1:3000 ``` Run the terminal sandbox: ```bash > npm start > node ./sandbox/client ``` axios-axios-aa54a5e/CONTRIBUTORS.md000066400000000000000000000010651526660636600167460ustar00rootroot00000000000000# Contributors Thanks to everyone who has contributed to axios. ## Core team - [Jay](https://github.com/jasonsaayman) - Lead Maintainer - [Dmitriy Mozgovoy](https://github.com/DigitalBrainJS) - Core Contributor - [Matt Zabriskie](https://github.com/mzabriskie) - Creator ## Notable contributors ## How to contribute To contribute, read the [Contributing Guide](CONTRIBUTING.md). --- _This list is maintained by hand. If you've contributed and want to be listed, open a pull request._ axios-axios-aa54a5e/ECOSYSTEM.md000066400000000000000000000072031526660636600163640ustar00rootroot00000000000000# Ecosystem This is a list of axios-related libraries and resources. To add one, open a pull request. ## Libraries ### General - [axios-vcr](https://github.com/nettofarah/axios-vcr) - Records and replays axios requests - [@3846masa/axios-cookiejar-support](https://github.com/3846masa/axios-cookiejar-support) - Adds tough-cookie support to axios - [axios-method-override](https://github.com/jacobbuck/axios-method-override) - Overrides HTTP request methods in axios - [axios-cache-plugin](https://github.com/jin5354/axios-cache-plugin) - Caches GET requests made with axios - [axios-extensions](https://github.com/kuitos/axios-extensions) - Adds axios extensions, including throttling and GET request caching - [axios-fetch](https://github.com/lifeomic/axios-fetch) - Provides a Web API Fetch implementation backed by an axios client - [axios-actions](https://github.com/davestewart/axios-actions) - Bundles endpoints as callable, reusable services - [axios-api-versioning](https://weffe.github.io/axios-api-versioning) - Adds API version management to axios - [axios-data-unpacker](https://github.com/anubhavsrivastava/axios-data-unpacker) - Unpacks HTTP responses through an axios interceptor - [r2curl](https://github.com/uyu423/r2curl) - Extracts cURL command strings from axios objects. (AxiosResponse, AxiosRequestConfig) - [axios-endpoints](https://github.com/renancaraujo/axios-endpoints) - Defines concise endpoint mappings for axios - [axios-multi-api](https://github.com/MattCCC/axios-multi-api) - Handles multiple APIs with declarative axios request definitions - [axios-url-template](https://github.com/rafw87/axios-url-template) - Adds URL template support through an axios interceptor ### API clients - [@hey-api/openapi-ts](https://heyapi.dev/openapi-ts/clients/axios) - Generates TypeScript clients, SDKs, and validators from OpenAPI schemas - [swagger-taxos-codegen](https://github.com/michalzaq12/swagger-taxos-codegen) - Generates TypeScript Swagger clients that use axios - [zodios](https://www.zodios.org) - Type-safe API client based on axios ### Logging and debugging - [axios-response-logger](https://github.com/srph/axios-response-logger) - Logs responses through an axios interceptor - [axios-debug-log](https://github.com/Gerhut/axios-debug-log) - Logs axios requests and responses with debug - [axios-curlirize](https://www.npmjs.com/package/axios-curlirize) - Logs axios requests as cURL commands and attaches the command to the response object ### React and redux - [axios-hooks](https://github.com/simoneb/axios-hooks) - React hooks for axios, with built-in support for server-side rendering - [react-hooks-axios](https://github.com/use-hooks/react-hooks-axios) - Custom React hooks for axios - [redux-saga-requests](https://github.com/klis87/redux-saga-requests) - Redux-Saga addon for AJAX request handling - [redux-axios-middleware](https://github.com/svrcekmichal/redux-axios-middleware) - Redux middleware for fetching data with axios HTTP client - [@react-cmpt/react-request-hook](https://github.com/react-cmpt/react-request-hook) - Lightweight React hook plugin for axios with minimal code changes ### Unit testing - [axiosist](https://github.com/Gerhut/axiosist) - Converts Node.js request handlers into axios adapters for server unit tests - [axios-mock-adapter](https://github.com/ctimmerm/axios-mock-adapter) - Mocks axios requests through a custom adapter - [axios-test-instance](https://github.com/remcohaszing/axios-test-instance) - Tests Node.js backends with axios - [moxios](https://github.com/axios/moxios) - Mocks axios requests for testing - [mocha-axios](https://github.com/jdrydn/mocha-axios) - Streamlines integration testing with Mocha and axios axios-axios-aa54a5e/LICENSE000066400000000000000000000020741526660636600154750ustar00rootroot00000000000000# Copyright (c) 2014-present Matt Zabriskie & Collaborators Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. axios-axios-aa54a5e/MIGRATION_GUIDE.md000066400000000000000000000523561526660636600172700ustar00rootroot00000000000000# Axios Migration Guide > **Migrating from Axios 0.x to 1.x** > > This guide helps developers upgrade from Axios 0.x to 1.x by documenting breaking changes, providing migration strategies, and offering solutions to common upgrade challenges. ## Table of Contents - [Overview](#overview) - [Breaking Changes](#breaking-changes) - [Error Handling Migration](#error-handling-migration) - [API Changes](#api-changes) - [Configuration Changes](#configuration-changes) - [Migration Strategies](#migration-strategies) - [Common Patterns](#common-patterns) - [Troubleshooting](#troubleshooting) - [Resources](#resources) ## Overview Axios 1.x introduced several breaking changes to improve consistency, security, and developer experience. While these changes provide better error handling and more predictable behavior, they require code updates when migrating from 0.x versions. ### Key Changes Summary | Area | 0.x Behavior | 1.x Behavior | Impact | |------|--------------|--------------|--------| | Error Handling | Selective throwing | Consistent throwing | High | | JSON Parsing | Lenient | Strict | Medium | | Browser Support | IE11+ | Modern browsers | Low-Medium | | TypeScript | Partial | Full support | Low | ### Migration Complexity - **Simple applications**: 1-2 hours - **Medium applications**: 1-2 days - **Large applications with complex error handling**: 3-5 days ## Breaking Changes ### 1. Error Handling Changes **The most significant change in Axios 1.x is how errors are handled.** #### 0.x Behavior ```javascript // Axios 0.x - Some HTTP error codes didn't throw axios.get('/api/data') .then(response => { // Response interceptor could handle all errors console.log('Success:', response.data); }); // Response interceptor handled everything axios.interceptors.response.use( response => response, error => { handleError(error); // Error was "handled" and didn't propagate } ); ``` #### 1.x Behavior ```javascript // Axios 1.x - All HTTP errors throw consistently axios.get('/api/data') .then(response => { console.log('Success:', response.data); }) .catch(error => { // Must handle errors at call site or they propagate console.error('Request failed:', error); }); // Response interceptor must re-throw or return rejected promise axios.interceptors.response.use( response => response, error => { handleError(error); // Must explicitly handle propagation return Promise.reject(error); // or throw error; } ); ``` #### Impact - **Response interceptors** can no longer "swallow" errors silently - **Every API call** must handle errors explicitly or they become unhandled promise rejections - **Centralized error handling** requires new patterns ### 2. JSON Parsing Changes #### 0.x Behavior ```javascript // Axios 0.x - Lenient JSON parsing // Would attempt to parse even invalid JSON response.data; // Might contain partial data or fallbacks ``` #### 1.x Behavior ```javascript // Axios 1.x - Strict JSON parsing // Throws clear errors for invalid JSON try { const data = response.data; } catch (error) { // Handle JSON parsing errors explicitly } ``` ### 3. Request/Response Transform Changes #### 0.x Behavior ```javascript // Implicit transformations with some edge cases transformRequest: [function (data) { // Less predictable behavior return data; }] ``` #### 1.x Behavior ```javascript // More consistent transformation pipeline transformRequest: [function (data, headers) { // Headers parameter always available // More predictable behavior return data; }] ``` ### 4. Browser Support Changes - **0.x**: Supported IE11 and older browsers - **1.x**: Requires modern browsers with Promise support - **Polyfills**: May be needed for older browser support ## Error Handling Migration The error handling changes are the most complex part of migrating to Axios 1.x. Here are proven strategies: ### Strategy 1: Centralized Error Handling with Error Boundary ```javascript // Create a centralized error handler class ApiErrorHandler { constructor() { this.setupInterceptors(); } setupInterceptors() { axios.interceptors.response.use( response => response, error => { // Centralized error processing this.processError(error); // Return a resolved promise with error info for handled errors if (this.isHandledError(error)) { return Promise.resolve({ data: null, error: this.normalizeError(error), handled: true }); } // Re-throw unhandled errors return Promise.reject(error); } ); } processError(error) { // Log errors console.error('API Error:', error); // Show user notifications if (error.response?.status === 401) { this.handleAuthError(); } else if (error.response?.status >= 500) { this.showErrorNotification('Server error occurred'); } } isHandledError(error) { // Define which errors are "handled" centrally const handledStatuses = [401, 403, 404, 422, 500, 502, 503]; return handledStatuses.includes(error.response?.status); } normalizeError(error) { return { status: error.response?.status, message: error.response?.data?.message || error.message, code: error.response?.data?.code || error.code }; } handleAuthError() { // Redirect to login, clear tokens, etc. localStorage.removeItem('token'); window.location.href = '/login'; } showErrorNotification(message) { // Show user-friendly error message console.error(message); // Replace with your notification system } } // Initialize globally const errorHandler = new ApiErrorHandler(); // Usage in components/services async function fetchUserData(userId) { try { const response = await axios.get(`/api/users/${userId}`); // Check if error was handled centrally if (response.handled) { return { data: null, error: response.error }; } return { data: response.data, error: null }; } catch (error) { // Unhandled errors still need local handling return { data: null, error: { message: 'Unexpected error occurred' } }; } } ``` ### Strategy 2: Wrapper Function Pattern ```javascript // Create a wrapper that provides 0.x-like behavior function createApiWrapper() { const api = axios.create(); // Add response interceptor for centralized handling api.interceptors.response.use( response => response, error => { // Handle common errors centrally if (error.response?.status === 401) { // Handle auth errors handleAuthError(); } if (error.response?.status >= 500) { // Handle server errors showServerErrorNotification(); } // Always reject to maintain error propagation return Promise.reject(error); } ); // Wrapper function that mimics 0.x behavior function safeRequest(requestConfig, options = {}) { return api(requestConfig) .then(response => response) .catch(error => { if (options.suppressErrors) { // Return error info instead of throwing return { data: null, error: { status: error.response?.status, message: error.response?.data?.message || error.message } }; } throw error; }); } return { safeRequest, axios: api }; } // Usage const { safeRequest } = createApiWrapper(); // For calls where you want centralized error handling const result = await safeRequest( { method: 'get', url: '/api/data' }, { suppressErrors: true } ); if (result.error) { // Handle error case console.log('Request failed:', result.error.message); } else { // Handle success case console.log('Data:', result.data); } ``` ### Strategy 3: Global Error Handler with Custom Events ```javascript // Set up global error handling with events class GlobalErrorHandler extends EventTarget { constructor() { super(); this.setupInterceptors(); } setupInterceptors() { axios.interceptors.response.use( response => response, error => { // Emit custom event for global handling this.dispatchEvent(new CustomEvent('apiError', { detail: { error, timestamp: new Date() } })); // Always reject to maintain proper error flow return Promise.reject(error); } ); } } const globalErrorHandler = new GlobalErrorHandler(); // Set up global listeners globalErrorHandler.addEventListener('apiError', (event) => { const { error } = event.detail; // Centralized error logic if (error.response?.status === 401) { handleAuthError(); } if (error.response?.status >= 500) { showErrorNotification('Server error occurred'); } }); // Usage remains clean async function apiCall() { try { const response = await axios.get('/api/data'); return response.data; } catch (error) { // Error was already handled globally // Just handle component-specific logic return null; } } ``` ## API Changes ### Request Configuration #### 0.x to 1.x Changes ```javascript // 0.x - Some properties had different defaults const config = { timeout: 0, // No timeout by default maxContentLength: -1, // No limit }; // 1.x - More secure defaults const config = { timeout: 0, // Still no timeout, but easier to configure maxContentLength: 2000, // Default limit for security maxBodyLength: 2000, // New property }; ``` ### Response Object The response object structure remains largely the same, but error responses are more consistent: ```javascript // Both 0.x and 1.x response = { data: {}, // Response body status: 200, // HTTP status statusText: 'OK', // HTTP status message headers: {}, // Response headers config: {}, // Request config request: {} // Request object }; // Error responses are more consistent in 1.x error.response = { data: {}, // Error response body status: 404, // HTTP error status statusText: 'Not Found', headers: {}, config: {}, request: {} }; ``` ## Configuration Changes ### Default Configuration Updates ```javascript // 0.x defaults axios.defaults.timeout = 0; // No timeout axios.defaults.maxContentLength = -1; // No limit // 1.x defaults (more secure) axios.defaults.timeout = 0; // Still no timeout axios.defaults.maxContentLength = 2000; // 2MB limit axios.defaults.maxBodyLength = 2000; // 2MB limit ``` ### Instance Configuration ```javascript // 0.x - Instance creation const api = axios.create({ baseURL: 'https://api.example.com', timeout: 1000, }); // 1.x - Same API, but more options available const api = axios.create({ baseURL: 'https://api.example.com', timeout: 1000, maxBodyLength: Infinity, // Override default if needed maxContentLength: Infinity, }); ``` ## Migration Strategies ### Step-by-Step Migration Process #### Phase 1: Preparation 1. **Audit Current Error Handling** ```bash # Find all axios usage grep -r "axios\." src/ grep -r "\.catch" src/ grep -r "interceptors" src/ ``` 2. **Identify Patterns** - Response interceptors that handle errors - Components that rely on centralized error handling - Authentication and retry logic 3. **Create Test Cases** ```javascript // Test current error handling behavior describe('Error Handling Migration', () => { it('should handle 401 errors consistently', async () => { // Test authentication error flows }); it('should handle 500 errors with user feedback', async () => { // Test server error handling }); }); ``` #### Phase 2: Implementation 1. **Update Dependencies** ```bash npm update axios ``` 2. **Implement New Error Handling** - Choose one of the strategies above - Update response interceptors - Add error handling to API calls 3. **Update Authentication Logic** ```javascript // 0.x pattern axios.interceptors.response.use(null, error => { if (error.response?.status === 401) { logout(); // Error was "handled" } }); // 1.x pattern axios.interceptors.response.use( response => response, error => { if (error.response?.status === 401) { logout(); } return Promise.reject(error); // Always propagate } ); ``` #### Phase 3: Testing and Validation 1. **Test Error Scenarios** - Network failures - HTTP error codes (401, 403, 404, 500, etc.) - Timeout errors - JSON parsing errors 2. **Validate User Experience** - Error messages are shown appropriately - Authentication redirects work - Loading states are handled correctly ### Gradual Migration Approach For large applications, consider gradual migration: ```javascript // Create a compatibility layer const axiosCompat = { // Use new axios instance for new code v1: axios.create({ // 1.x configuration }), // Wrapper for legacy code legacy: createLegacyWrapper(axios.create({ // Configuration that mimics 0.x behavior })) }; function createLegacyWrapper(axiosInstance) { // Add interceptors that provide 0.x-like behavior axiosInstance.interceptors.response.use( response => response, error => { // Handle errors in 0.x style for legacy code handleLegacyError(error); // Don't propagate certain errors if (shouldSuppressError(error)) { return Promise.resolve({ data: null, error: true }); } return Promise.reject(error); } ); return axiosInstance; } ``` ## Common Patterns ### Authentication Interceptors #### Updated Authentication Pattern ```javascript // Token refresh interceptor for 1.x let isRefreshing = false; let refreshSubscribers = []; function subscribeTokenRefresh(cb) { refreshSubscribers.push(cb); } function onTokenRefreshed(token) { refreshSubscribers.forEach(cb => cb(token)); refreshSubscribers = []; } axios.interceptors.response.use( response => response, async error => { const originalRequest = error.config; if (error.response?.status === 401 && !originalRequest._retry) { if (isRefreshing) { // Wait for token refresh return new Promise(resolve => { subscribeTokenRefresh(token => { originalRequest.headers.Authorization = `Bearer ${token}`; resolve(axios(originalRequest)); }); }); } originalRequest._retry = true; isRefreshing = true; try { const newToken = await refreshToken(); onTokenRefreshed(newToken); isRefreshing = false; originalRequest.headers.Authorization = `Bearer ${newToken}`; return axios(originalRequest); } catch (refreshError) { isRefreshing = false; logout(); return Promise.reject(refreshError); } } return Promise.reject(error); } ); ``` ### Retry Logic ```javascript // Retry interceptor for 1.x function createRetryInterceptor(maxRetries = 3, retryDelay = 1000) { return axios.interceptors.response.use( response => response, async error => { const config = error.config; if (!config || !config.retry) { return Promise.reject(error); } config.__retryCount = config.__retryCount || 0; if (config.__retryCount >= maxRetries) { return Promise.reject(error); } config.__retryCount += 1; // Exponential backoff const delay = retryDelay * Math.pow(2, config.__retryCount - 1); await new Promise(resolve => setTimeout(resolve, delay)); return axios(config); } ); } // Usage const api = axios.create(); createRetryInterceptor(3, 1000); // Make request with retry api.get('/api/data', { retry: true }); ``` ### Loading State Management ```javascript // Loading interceptor for 1.x class LoadingManager { constructor() { this.requests = new Set(); this.setupInterceptors(); } setupInterceptors() { axios.interceptors.request.use(config => { this.requests.add(config); this.updateLoadingState(); return config; }); axios.interceptors.response.use( response => { this.requests.delete(response.config); this.updateLoadingState(); return response; }, error => { this.requests.delete(error.config); this.updateLoadingState(); return Promise.reject(error); } ); } updateLoadingState() { const isLoading = this.requests.size > 0; // Update your loading UI document.body.classList.toggle('loading', isLoading); } } const loadingManager = new LoadingManager(); ``` ## Troubleshooting ### Common Migration Issues #### Issue 1: Unhandled Promise Rejections **Problem:** ```javascript // This pattern worked in 0.x but causes unhandled rejections in 1.x axios.get('/api/data'); // No .catch() handler ``` **Solution:** ```javascript // Always handle promises axios.get('/api/data') .catch(error => { // Handle error appropriately console.error('Request failed:', error.message); }); // Or use async/await with try/catch async function fetchData() { try { const response = await axios.get('/api/data'); return response.data; } catch (error) { console.error('Request failed:', error.message); return null; } } ``` #### Issue 2: Response Interceptors Not "Handling" Errors **Problem:** ```javascript // 0.x style - interceptor "handled" errors axios.interceptors.response.use(null, error => { showErrorMessage(error.message); // Error was considered "handled" }); ``` **Solution:** ```javascript // 1.x style - explicitly control error propagation axios.interceptors.response.use( response => response, error => { showErrorMessage(error.message); // Choose whether to propagate the error if (shouldPropagateError(error)) { return Promise.reject(error); } // Return success-like response for "handled" errors return Promise.resolve({ data: null, handled: true, error: normalizeError(error) }); } ); ``` #### Issue 3: JSON Parsing Errors **Problem:** ```javascript // 1.x is stricter about JSON parsing // This might throw where 0.x was lenient const data = response.data; ``` **Solution:** ```javascript // Add response transformer for better error handling axios.defaults.transformResponse = [ function (data) { if (typeof data === 'string') { try { return JSON.parse(data); } catch (e) { // Handle JSON parsing errors gracefully console.warn('Invalid JSON response:', data); return { error: 'Invalid JSON', rawData: data }; } } return data; } ]; ``` #### Issue 4: TypeScript Errors After Upgrade **Problem:** ```typescript // TypeScript errors after upgrade const response = await axios.get('/api/data'); // Property 'someProperty' does not exist on type 'any' ``` **Solution:** ```typescript // Define proper interfaces interface ApiResponse { data: any; message: string; success: boolean; } const response = await axios.get('/api/data'); // Now properly typed console.log(response.data.data); ``` ### Debug Migration Issues #### Enable Debug Logging ```javascript // Add request/response logging axios.interceptors.request.use(config => { console.log('Request:', config); return config; }); axios.interceptors.response.use( response => { console.log('Response:', response); return response; }, error => { console.log('Error:', error); return Promise.reject(error); } ); ``` #### Compare Behavior ```javascript // Create side-by-side comparison during migration const axios0x = require('axios-0x'); // Keep old version for testing const axios1x = require('axios'); async function compareRequests(config) { try { const [result0x, result1x] = await Promise.allSettled([ axios0x(config), axios1x(config) ]); console.log('0.x result:', result0x); console.log('1.x result:', result1x); } catch (error) { console.log('Comparison error:', error); } } ``` ## Resources ### Official Documentation - [Axios 1.x Documentation](https://axios-http.com/) - [Axios GitHub Repository](https://github.com/axios/axios) - [Axios Changelog](https://github.com/axios/axios/blob/main/CHANGELOG.md) ### Migration Tools - [Axios Migration Codemod](https://github.com/axios/axios-migration-codemod) *(if available)* - [ESLint Rules for Axios 1.x](https://github.com/axios/eslint-plugin-axios) *(if available)* ### Community Resources - [Stack Overflow - Axios Migration Questions](https://stackoverflow.com/questions/tagged/axios+migration) - [GitHub Discussions](https://github.com/axios/axios/discussions) - [Axios Discord Community](https://discord.gg/axios) *(if available)* ### Related Issues - [Error Handling Changes Discussion](https://github.com/axios/axios/issues/7208) - [Migration Guide Request](https://github.com/axios/axios/issues/xxxx) *(link to related issues)* --- ## Need Help? If you encounter issues during migration that aren't covered in this guide: 1. **Search existing issues** in the [Axios GitHub repository](https://github.com/axios/axios/issues) 2. **Ask questions** in [GitHub Discussions](https://github.com/axios/axios/discussions) 3. **Contribute improvements** to this migration guide --- *This migration guide is maintained by the community. If you find errors or have suggestions, please [open an issue](https://github.com/axios/axios/issues) or submit a pull request.*axios-axios-aa54a5e/PRE_RELEASE_CHANGELOG.md000066400000000000000000000520561526660636600201540ustar00rootroot00000000000000# Pre-Release Changelog ## Unreleased ## Features - **Response duration:** Added `response.duration` and `error.response.duration` when a response is available, with matching ESM/CommonJS types. The value measures wall-clock milliseconds from adapter invocation to settlement, excluding interceptors and request/response transforms. Buffered responses include body reception; streamed responses stop timing when the stream becomes available. Backward clock adjustments clamp to zero, while forward adjustments can inflate the value. Errors without a response have no duration. This additive API replaces any custom adapter's existing `duration` value with the dispatch measurement. (**[#11214](https://github.com/axios/axios/pull/11214)**; related to **[#695](https://github.com/axios/axios/issues/695)** and **[#6984](https://github.com/axios/axios/issues/6984)**) - **Opt-in UTF-8 Basic auth usernames:** Added `transitional.encodeBasicAuthUsername: true` to UTF-8 encode `auth.username` in XHR/Fetch config resolution, matching the existing password encoding and allowing usernames beyond Latin-1. The option defaults to `false`, preserving XHR's legacy username bytes, existing pre-encoding workarounds, and Fetch's existing rejection of config usernames above U+00FF. Fetch's final UTF-8 header encoding, URL credentials, and Node HTTP authentication remain unchanged. Invalid UTF-16 still rejects with `ERR_BAD_OPTION_VALUE` in XHR/Fetch. This is an additive, non-breaking option with matching ESM/CommonJS declarations. (**[#11159](https://github.com/axios/axios/pull/11159)**) - **Proxy bypass CIDR ranges:** Added IPv4 and IPv6 CIDR matching to `NO_PROXY`/`no_proxy`, including bracketed IPv6 and IPv4-mapped IPv6 normalization, while malformed ranges fail closed. A `/0` entry bypasses the proxy for its entire address family. - **HTTP status code names:** Added the RFC 9110 names `HttpStatusCode.ContentTooLarge` for 413 and `HttpStatusCode.UnprocessableContent` for 422 across the runtime API and ESM/CommonJS declarations. The existing `PayloadTooLarge` and `UnprocessableEntity` names remain as deprecated aliases, and numeric reverse lookups retain their existing v1.x names for compatibility. (**#11082**, closes **#11066**) ## Bug Fixes - **XHR progress cleanup:** Detach download and upload progress listeners when a request completes, fails, times out, or is canceled. Final progress callbacks that throw are reported asynchronously without skipping listener and cancellation cleanup, and reentrant cancellation remains safe. Preserve the asynchronous XSRF timeout cleanup and existing public API and types. This ensures deterministic teardown; it does not establish the cause of the memory growth reported in #5641. (**[#11225](https://github.com/axios/axios/pull/11225)**, related to **[#5641](https://github.com/axios/axios/issues/5641)**) - **XHR adapter cookie access:** Read eligible XSRF cookies through the asynchronous Cookie Store API when supported, avoiding synchronous cookie access while preserving existing URI decoding, malformed-value handling, and fallback behavior. Cookie preparation shares the configured request timeout and cancellation lifecycle; late results cannot send canceled or expired requests. Fetch behavior, public options, and token values remain compatible. (**[#11166](https://github.com/axios/axios/pull/11166)**, closes **[#10826](https://github.com/axios/axios/issues/10826)**) - **Node HTTP adapter - proxy tunnel TLS options:** Apply `https.globalAgent` TLS options, including a custom `ca`, to HTTPS origins reached through CONNECT proxies when no per-request `httpsAgent` is supplied. Read origin TLS options for each request so cached tunneling agents pick up changes to the selected agent or a replacement global agent. Explicit agents retain precedence, and the HTTPS proxy connection retains its existing TLS configuration. No new API or type changes. (**#11178**, related to **#11176**; the reported explicit-agent failure remains unverified.) - **Percent-encoded Base64 data URLs:** Decode URL-escaped Base64 alphabet, padding, and whitespace before converting the Node HTTP adapter's data URL response. Estimate the Buffer backing allocation after the same decoding so encoded alphabet and padding no longer inflate byte-limit checks, while avoiding the per-character decoding scan for unescaped Base64. Ordinary Base64 inputs, forgiving Buffer decoding, and case-insensitive URL schemes and Base64 markers are preserved; pre-allocation limits still count ignored characters and content after padding. (**[#11294](https://github.com/axios/axios/pull/11294)**) - **URL scheme casing:** Accept uppercase and mixed-case supported schemes in the XHR adapter and Node data URLs. Keep data URL allocation estimates aligned with decoding so configured limits reject oversized inputs before Buffer allocation, including ignored Base64 tails. URL paths, query values, payload casing, and unsupported-protocol rejection remain unchanged; no new API or type changes. (**[#11163](https://github.com/axios/axios/pull/11163)**) - **Request handling consistency:** Preserve explicit multipart boundaries, configured Blob constructors, and encoded body limits across Fetch uploads. Keep direct connection pools tied to the configured Node agent's cleanup, connection hooks, and pool keys; reject unsupported pool or TLS-session lifecycle overrides with `ERR_BAD_OPTION_VALUE` instead of discarding them. Parameter merging now handles deep objects without recursive copying, respects serializer depth options, ignores replaced defaults, supports runtimes without symbol reflection, avoids evaluating reserved-name accessors during validation, and reports circular parameters as `ERR_BAD_OPTION_VALUE`. Form serialization preserves hidden array-like indexes and numeric field names that cannot be array indexes. Data URL errors and upload chunking retain their runtime-specific behavior. (**#11242**) - **Fetch response size errors:** Preserve `ERR_BAD_RESPONSE` and the current request/config when a fetch response exceeds `maxContentLength` and the runtime wraps the stream failure, including when it drops the original cause. Clarify when custom `env.Response` constructors must accept tracked streams at runtime while preserving existing response limits and constructor signatures. (**#11179**) - **TypeScript declarations:** Exposed the branded response-default marker type so consumers can emit declarations for helpers that forward Axios request response generics. (**#11116**) - **Header matcher callbacks:** Bind function matchers passed to `AxiosHeaders.has()`, `delete()`, and `clear()` to the headers instance, matching the existing TypeScript declarations and allowing callbacks to use header methods through `this` ([#11216](https://github.com/axios/axios/pull/11216)). - **Method-specific headers:** Added default header buckets for all supported methods and removed those structural buckets during request preparation, so `OPTIONS`, `PURGE`, `LINK`, `UNLINK`, and `QUERY` defaults apply only to matching requests instead of leaking as literal headers. (**#11096**) - **Literal method-named headers:** Preserve literal request headers named `common` or after an HTTP method, including `Link`, instead of dropping them or merging them as defaults. Dates, Buffers, typed arrays, and custom class values now use normal header serialization. Defaults buckets are limited to plain/null-prototype maps and compatible `AxiosHeaders`, including instances from different package builds that provide the complete header-container interface; a matching tag and `toJSON` alone do not turn application objects into defaults buckets. Only matching method defaults apply. **Compatibility change:** arbitrary class instances used as defaults buckets must be converted to a plain map or `AxiosHeaders` first, otherwise they are serialized as literal header values. Bucket and brand reads exclude shared-prototype values; throwing brand or `toJSON` getters fall back to literal header serialization instead of aborting request preparation. No new API or type changes. (**#11156**; refines **#11096**) - **Node HTTP adapter option errors:** Invalid custom DNS lookup addresses and invalid or unsupported `httpVersion` values, including non-coercible JavaScript types, now reject with `AxiosError` and `ERR_BAD_OPTION_VALUE` while preserving the request config for diagnostics. (**#11096**) - **AxiosHeaders - header named `__proto__`:** Preserve this header under the safe case variant `__Proto__`, including raw header strings and normalization of padded names from objects, Maps, or arrays of pairs. Previously, string values were discarded and array values could replace the instance prototype and remove its methods. Fetch and XHR response headers now retain the value exposed by the platform, including repeated raw header lines. Header lookup remains case-insensitive, and copying the stored header onto a plain object preserves its value without invoking the prototype setter. Request config merging continues to filter `__proto__`, `constructor`, and `prototype`. (**#11134**) - **Runtime configuration hardening:** Prevented values inherited only from shared prototypes, including another JavaScript realm's `Object.prototype` even when its `constructor` is altered, from becoming request behavior after config merging or interceptor replacement. Methods, headers, adapters, transports, FormData hooks, serializer options, and Fetch `Request` options are protected. Already-safe writable merged configs retain their identity through dispatch, while frozen, sealed, accessor-based, otherwise restricted, or unsafe-key-bearing replacements become writable filtered snapshots. Interceptor replacements with non-terminal application-defined prototypes remain supported as normalized null-prototype snapshots, terminal null-prototype ancestors are treated as shared boundaries to fail closed, and own `__proto__`, `constructor`, and `prototype` keys remain excluded from materialized configs. - **Fetch adapter consistency:** Custom fetch implementations now receive the fully resolved `Request` plus a safe second `fetchOptions` argument that preserves custom own fields without overriding Axios-managed request fields. The authoritative method, headers, body, signal, duplex, and credentials are available on the `Request` and omitted from the second argument. `maxRedirects: 0` selects manual redirect handling when supported; Node exposes the unfollowed 3xx response, while browsers may expose an opaque redirect with status 0 and inaccessible headers. The no-`Request` fallback receives the same safe resolved options. - **HTTP/2 adapter consistency:** Custom DNS lookup functions now apply to HTTP/2 connections and can reuse pooled sessions safely. Explicit Axios proxy objects fail with `ERR_NOT_SUPPORT`, while process-environment and HTTP/1-agent `proxyEnv` settings are ignored because `http2.connect()` cannot apply them; `proxy: false` continues to force a direct connection. Failed sessions are removed without leaking an unhandled session error. - **HTTP/2 session pool:** Remove sessions from the pool immediately on `GOAWAY`, including before requests started inside a session's `goaway` callback can reuse the draining connection. Existing streams may finish, and their completion no longer schedules an idle timer for an already-removed session that could delay process exit. No public API or default changes. ([#11210](https://github.com/axios/axios/pull/11210)) - **Data URI validation:** Rejected media types containing extra slash separators and avoided excessive backtracking for long malformed media types. - **Node HTTP adapter - keep-alive memory retention:** Reused a module-scoped socket error listener so pooled keep-alive sockets no longer retain the adapter context, response body, and buffers from the request that first used each socket. Socket errors continue to destroy only the currently active request. (**#11091**) - **Data URLs - `;base64` token casing:** The Node HTTP adapter now accepts uppercase and mixed-case base64 markers, such as `data:text/plain;BASE64,SGVsbG8=`, instead of rejecting them with `ERR_BAD_REQUEST` (the internal helper raised `ERR_INVALID_URL`). The decoder and its allocation estimator recognize the same case-insensitive trailing marker, allowing payloads at `maxContentLength` while rejecting oversized Buffer allocations before decoding, including ignored input and percent-containing bodies. URL scheme casing, percent decoding, public APIs, and types are unchanged. (**[#11138](https://github.com/axios/axios/pull/11138)**) - **XHR Adapter - final download progress delivery:** `onDownloadProgress` now always receives a final delivery when a completed XHR download reaches its successful `loadend` handler. The callback runs while that event is still dispatching, so listeners reading incremental data from `event.currentTarget` observe the complete response. Previously the final delivery happened only when a throttled progress event was still pending, and it replayed an event whose dispatch had already finished — `event.currentTarget` was `null` — so text-streaming consumers could lose every chunk after the first in browsers whose progress cadence kept deliveries inside the throttle window. A final callback that cancels the request stops settlement processing cleanly, while a throwing callback does not leave the request pending. Upload progress and flushes triggered by stream errors, abort reasons, or failed XHR downloads retain their prior pending-event behavior. (closes **#6796**) - **XHR Adapter - navigation-canceled requests:** A response that reaches `loadend` with status 0 is now rejected as an `ECONNABORTED` error instead of resolving as a success with an empty body. Firefox 152 stopped firing `error` and `abort` for requests canceled by a document navigation, leaving `loadend` as the only handler that runs, so those requests were settling as successful responses. Rejecting with `ECONNABORTED` matches what `onabort` raised on Firefox 151, so the outcome an application sees is unchanged by that update. Reads over `file:`, which some environments report as status 0 on success, still resolve, whether the scheme appears on the request URL, is inherited from a `file:` page origin by a relative request URL, or appears only on `responseURL`. Because no response was received, the rejection is not suppressed by `validateStatus`, matching how `onerror` and `onabort` already behave. (**#11094**, closes **#11093**) - **Request error stacks:** Preserved original request failures when custom `Error` stack instrumentation returns non-string data or throws during optional stack decoration. (**#11109**, closes **#11108**) - **Request error stacks:** Restored appending of one- and two-frame reconstructed request stacks when they are not already present, preserving caller context when the runtime captures it. Duplicate suppression and protection against failing custom stack hooks remain unchanged; the runtime's stack capture limit is not increased. (**#11132**, closes **#11131**) - **Fetch adapter - cache mode:** Omitted the implicit `cache: 'default'` when a runtime rejects it but accepts a request without a cache option, restoring requests on Cloudflare Workers, including older compatibility dates and `cache_option_disabled` configurations that reject every explicit cache mode. Supporting runtimes retain the explicit default to protect against `Object.prototype` pollution, and caller-supplied cache options remain unchanged. (**#11194**, closes **#11192**) - **Interceptor storage:** Removed trailing interceptor tombstones after ejection so repeated register-then-eject cycles no longer grow the handlers array, while preserving its public array shape, interceptor iteration behavior, and interceptor ID identity across registrations. (**#11070**) - **Wrapped error stacks:** `AxiosError.from()` now appends an available wrapped error stack to `error.stack` under a `Caused by:` section for reporters that only read stack strings. Reconstructed request/caller frames stay ahead of the cause, including one- and two-frame stacks, without being duplicated on repeated rejection or confusing marker-like text in messages with the appended section. `error.cause` remains unchanged and non-enumerable; missing, empty, non-string, throwing, or unwritable stacks do not make optional decoration fail error normalization. Each wrap adds one nested section, so repeated wrapping increases stack size. This does not reveal network details withheld by browsers. (**[#11142](https://github.com/axios/axios/pull/11142)**, relates to **[#6670](https://github.com/axios/axios/issues/6670)**) - **Interceptor ID compatibility:** Restored `eject(String(id))` removal after interceptor storage moved to a Map, including registrations made after trailing handlers have been compacted. Empty, noncanonical, unknown, and stale IDs remain no-ops. The documented numeric-ID API and TypeScript declarations are unchanged. Existing nullish-handler tolerance and automatic recovery on `use()` from #11118 are preserved. (**[#11139](https://github.com/axios/axios/pull/11139)**, follows up **[#11114](https://github.com/axios/axios/issues/11114)**) ## Documentation - **README sponsors:** Removed Stytch, polskie kasyno online, Buzzoid, and Twicsy from the Gold sponsor listings following cancellation of their Open Collective contributions, and regrouped the remaining sponsors into three-column rows. This updates sponsor recognition only, with no runtime, public API, or type compatibility changes. ([#11369](https://github.com/axios/axios/pull/11369)) - **Contributor request lifecycle:** Corrected the contributor guide to limit `synchronous` and `runWhen` options to request interceptors, describe the opt-in FIFO request order, and place request transforms before adapter selection. This aligns contributor guidance with the existing implementation; runtime behavior, public APIs, and types are unchanged. ([#11335](https://github.com/axios/axios/pull/11335)) - **README CDN examples:** Updated the jsDelivr and unpkg script tags from Axios 1.13.2 to the published 1.20.0 browser bundle so readers copying the examples use the current release. Both examples remain explicitly version-pinned; this documentation change does not alter package APIs or existing installations. (**[#11149](https://github.com/axios/axios/pull/11149)**) - **Contributor test setup:** Clarified that `npm test` runs the unit and browser test suites and requires Playwright browsers to be installed first, helping contributors avoid missing-browser errors. No runtime, public API, or type compatibility changes. (**#11284**) - **Contributor submission requirements:** Required prerelease changelog entries and a submission checklist covering regression tests, local validation, compatibility, docs/types, security, and accurate GitHub Actions status so maintainers can trace all contributions and assess verified readiness before review. Defined Maintenance entry placement and a narrow exception for automated release-preparation PRs containing only generated release changes. Kept `AGENTS.md`, Copilot instructions, the contributor guide, and the PR template aligned. This changes the contribution process, with no runtime, public API, or type compatibility impact. (**#11223**) - **Global search:** Added localized, private, in-browser full-text search for the active documentation language, with fuzzy and prefix matching. ## Maintenance - **Proxy hostname regression coverage:** Added a long non-matching request-hostname test for `NO_PROXY` matching, complementing the existing long-entry test so regressions confined to request-hostname normalization exceed the test's time budget. This protects coverage of the previously fixed trailing-dot normalization denial of service; runtime behavior, public APIs, and types are unchanged. (**[#11235](https://github.com/axios/axios/pull/11235)**) - **Request configuration merge performance:** Reduced the CPU cost of `mergeConfig`, most noticeably for requests with many headers, by avoiding redundant plain-object prototype walks, indexing lowercase keys during case-insensitive header merges, and building the stateless merge-strategy table once at module load instead of on every call. Added a reproducible no-I/O request benchmark in `scripts/benchmark-merge-config.js`. Merge results, prototype-pollution guards, and the public API and types are unchanged. The returned config's non-enumerable `hasOwnProperty` is now captured at module load, and stateful Proxy traps on config objects observe fewer prototype lookups. (**[#11208](https://github.com/axios/axios/pull/11208)**, closes **[#11207](https://github.com/axios/axios/issues/11207)**) - **Browser origin checks:** Removed obsolete Internet Explorer user-agent detection from `isURLSameOrigin`, retaining protocol, host, and port comparisons and document-base URL resolution. Native `URL.host` already includes non-default ports, so origin decisions and XSRF header behavior remain unchanged in supported environments. Added coverage for port and protocol boundaries, default-port normalization, legacy user-agent strings, relative URLs, and cross-port XSRF exclusion. No public API or type changes. (**[#11174](https://github.com/axios/axios/pull/11174)**) axios-axios-aa54a5e/PRE_RELEASE_DOCS.md000066400000000000000000001012731526660636600174110ustar00rootroot00000000000000# Pre-Release Documentation Notes ## Purpose Track documentation updates that should be applied during release preparation. Do not treat this file as final documentation. Each entry should give enough context for a maintainer or LLM to update README, docs pages, examples, migration guides, and translated docs when the release is prepared. Do not store raw diffs or line-number-only instructions here; prefer stable section names, target files, required concepts, examples, and release-specific notes. ## Entry Format - **Change:** Short feature/fix name. - **Source:** PR, issue, or changelog reference. - **Status:** Pending | Applied | Skipped. - **Docs targets:** Files or docs sections likely needing updates. - **Required content:** What the docs must explain. - **Examples:** Any code snippets or examples that should be included. - **Notes:** Constraints, release-only wording, translation follow-up, etc. ## Unreleased ### Response duration - **Change:** Expose elapsed adapter time as the optional numeric `AxiosResponse.duration` property in milliseconds, including on `error.response` when present. - **Source:** [PR #11214](https://github.com/axios/axios/pull/11214), related to [request/response timing #695](https://github.com/axios/axios/issues/695) and [feature suggestions #6984](https://github.com/axios/axios/issues/6984). - **Status:** Pending. - **Docs targets:** README response schema and error-handling examples; docs-site response schema and error handling; corresponding translations. - **Required content:** Timing starts immediately before adapter invocation, after request interceptors and request transforms. It ends in the adapter's settlement handler, before response transforms and response interceptors. Buffered responses include body reception; HTTP/Fetch streaming responses measure only the time until the stream becomes available, and consuming or failing that stream later does not update the value. This is not a DNS/TCP/TLS/TTFB breakdown or server-processing time. It includes adapter work such as connection queuing and redirects. Errors without a response, including ordinary network errors, timeouts, and cancellations, have no duration. Strict JSON parsing failures retain the duration on their attached response. - **Examples:** Show `const response = await axios.get('/users'); console.log(response.duration);`. For errors, guard with `axios.isAxiosError(error)` and `error.response` before reading `error.response.duration`; do not imply that every error has a response. - **Notes:** Values use `Date.now()`, with two clock reads for a response receiving timing metadata. Backward clock adjustments clamp to zero; forward adjustments can inflate the value, so it is approximate wall-clock timing rather than a monotonic measurement. The field is enumerable and overwrites a custom adapter's existing `duration` property. Both declaration formats keep it optional for custom adapters and manually constructed responses. Semver impact is minor; no new option, dependency, or migration for built-in-adapter users. Apply published documentation only during release preparation. ### Asynchronous XSRF cookie reads in the XHR adapter - **Change:** Document the XHR adapter's use of the Cookie Store API for XSRF token reads. - **Source:** `PRE_RELEASE_CHANGELOG.md` Bug Fixes, XHR adapter cookie access; PR #11166 and issue #10826. - **Status:** Pending. - **Docs targets:** README and request-config guidance for `xsrfCookieName`, `xsrfHeaderName`, and `withXSRFToken`; browser support notes; translated docs after the English documentation is finalized. - **Required content:** Explain that the XHR adapter automatically uses `window.cookieStore` when available to avoid synchronous `document.cookie` access while resolving an eligible XSRF token. Browsers without Cookie Store support retain the existing `document.cookie` behavior, and a failed Cookie Store read also falls back to that path. Both read paths preserve Axios's existing URI decoding (once), with malformed escape sequences left unchanged. Cookie lookup and the subsequent XHR share the configured timeout; both cancellation APIs also work during lookup, and late results cannot dispatch a canceled or expired request. The Fetch adapter and the public request-config surface are unchanged; no migration is required. - **Examples:** None required because capability detection is automatic and adds no configuration. - **Notes:** Keep the explanation scoped to XHR XSRF reads; do not imply that all cookie operations or the Fetch adapter use Cookie Store. Issue #10826 originally requested raw values; compatibility requires retaining Axios's existing decoding because Cookie Store does not percent-decode stored cookies. ### Opt-in UTF-8 Basic auth usernames - **Change:** Added `transitional.encodeBasicAuthUsername`, a boolean defaulting to `false`, to opt into UTF-8 encoding of `auth.username` during XHR/Fetch config resolution. - **Source:** [PR #11159](https://github.com/axios/axios/pull/11159); `PRE_RELEASE_CHANGELOG.md` Features. - **Status:** Pending. - **Docs targets:** README request-config `auth` and transitional options; request-config and authentication documentation in the docs site and translations. - **Required content:** Explain that applications talking to UTF-8 Basic-auth servers can enable this option per request or with `axios.create`. Existing defaults remain compatible: XHR uses legacy single-byte username encoding and UTF-8 passwords, while Fetch already sends UTF-8 but its config resolution rejects usernames above U+00FF unless opted in. Node HTTP and Fetch URL-embedded credentials are unchanged. Setting the option to `false` on a request overrides an instance opt-in; it does not select Latin-1 for Fetch or Node HTTP. - **Examples:** `axios.get('/account', { auth: { username: 'π', password: 'secret' }, transitional: { encodeBasicAuthUsername: true } })`. Also show an instance opt-in with a request-level `false` override for a legacy XHR endpoint. - **Compatibility:** No migration is required unless enabling the option. Remove manual UTF-8-to-byte-string username conversion when opting in to avoid double encoding. Do not enable it for XHR servers expecting legacy Latin-1 username bytes. RFC 7617 leaves the default encoding unspecified; this option does not negotiate server challenges or normalize Unicode. ASCII/empty credentials are unchanged and malformed UTF-16 continues to reject with `ERR_BAD_OPTION_VALUE` in XHR/Fetch. - **Types:** Both `index.d.ts` and `index.d.cts` expose the optional boolean in `TransitionalOptions`. ### Global TLS options for CONNECT-tunneled origins - **Change:** Apply the current `https.globalAgent.options` to an HTTPS origin reached through an Axios-managed CONNECT tunnel when no per-request `httpsAgent` is configured. - **Source:** `PRE_RELEASE_CHANGELOG.md` Bug Fixes, #11178; related to #11176, whose explicit-agent failure is not established as fixed by this change. - **Status:** Pending. - **Docs targets:** README request-config proxy/agent guidance and the Node proxy configuration docs; translated docs after the English documentation is finalized. - **Required content:** Explain that an explicit `httpsAgent` takes precedence over the global agent, without merging their trust settings. Origin TLS options are read for each request, including after the tunneling agent is cached; changes to either selected agent's options and replacement of `https.globalAgent` are observed by subsequent requests. Global TLS settings apply only to the tunneled origin, not to the HTTPS proxy's TLS connection. Existing explicit-agent handling for the proxy is unchanged. - **Examples:** Show a process-installed private CA with `https.globalAgent.options.ca = fs.readFileSync('/path/to/corp-ca.pem')` and `axios.get('https://internal-api.example.com/data', { proxy: { protocol: 'http', host: 'proxy.corp.net', port: 3128 } })`, without an explicit `httpsAgent`. Explain that a request-specific agent must carry its own required CA. - **Notes:** Patch-level behavior correction with no new API, type, or migration requirements. Configured TLS options include `checkServerIdentity` and `rejectUnauthorized`; do not recommend disabling certificate validation. This forwards options, not arbitrary custom agent connection hooks. Browser adapters and HTTP/2 behavior are unchanged. ### Literal method-named request headers - **Change:** Restore literal headers such as `Link`, including non-plain object values, and distinguish them from common and method-specific header maps. - **Source:** `PRE_RELEASE_CHANGELOG.md` Bug Fixes, Literal method-named headers; #11156 and #11096. - **Status:** Pending. - **Docs targets:** README and docs site request-header and configuration-defaults guidance; migration guide for custom class defaults. - **Required content:** Plain objects, null-prototype maps, and compatible `AxiosHeaders` (including subclasses and instances from other package builds) remain defaults buckets. Compatible foreign instances must provide the header-container interface; application objects exposing only the `AxiosHeaders` tag and `toJSON` remain literal values. Only the matching method bucket applies; these buckets are never transmitted as literal headers. Scalars, arrays, and other objects such as Dates, Buffers, typed arrays, and application class instances use normal header-value serialization. Existing null/undefined/false header omission remains intact. Explain the breaking compatibility change for applications using arbitrary class instances as defaults buckets: convert those instances to plain maps or `AxiosHeaders` before assigning them under `common` or a method name. Otherwise their string representation becomes a literal header, including on nonmatching requests; this can expose contents included by a custom `toString()`. - **Examples:** Show `headers: { Link: '; rel="type"' }` and a JavaScript example passing `Buffer.from('literal')` directly. For a custom defaults class, migrate `headers: { post: customDefaults }` to `headers: { post: { ...customDefaults } }` or `headers: { post: new AxiosHeaders({ ...customDefaults }) }`; both retain its own enumerable header entries as method defaults. - **Notes:** No new options, exports, or type declarations. Document the class-defaults migration prominently; do not describe this as universally nonbreaking. Object-to-string normalization is existing JavaScript runtime behavior and does not expand the declared TypeScript header-value types. ### Interceptor IDs and nullish handler recovery - **Change:** Restore numeric-string interceptor ID compatibility and clarify interceptor storage behavior. - **Source:** `PRE_RELEASE_CHANGELOG.md` Bug Fixes, #11139 and #11114; nullish handler recovery was implemented in #11118. - **Status:** Pending. - **Docs targets:** `README.md` Interceptors section; interceptor API reference; translated docs after the English documentation is finalized. - **Required content:** Explain that the numeric value returned by `use()` is an opaque interceptor ID that must be passed back to `eject()` unchanged, rather than used as an index into the handlers array. Recommend `clear()` and `eject()` for managing interceptors. If the handlers array is replaced with `null` or `undefined`, the chain is skipped and the next `use()` automatically restores an array. Unknown IDs and repeated ejections are no-ops; stale IDs cannot remove newly registered handlers after compaction or clearing. - **Examples:** Keep the existing `eject(myInterceptor)` example using the original numeric ID. - **Notes:** Canonical numeric-string IDs are accepted for historical runtime compatibility; this does not expand the documented numeric-ID API or TypeScript declarations. Interceptor execution order is unchanged. ### Fetch response size errors - **Change:** Preserve fetch response-limit errors across runtime wrappers and clarify the custom Response constructor's existing stream requirements. - **Source:** #11179; `PRE_RELEASE_CHANGELOG.md` Bug Fixes, Fetch response size errors. - **Status:** Pending. - **Docs targets:** Fetch adapter and `maxContentLength` guidance; TypeScript/custom fetch examples. - **Required content:** Explain that exceeding `maxContentLength` while consuming a fetch response rejects with `ERR_BAD_RESPONSE`, retaining the current request and config even if the runtime wraps or drops the original stream error. When response-body streaming is supported and the response has a body, custom `env.Response` implementations must accept a tracked `ReadableStream` if `maxContentLength` is enabled (a numeric value greater than `-1`), `onDownloadProgress` is set, or `responseType` is `'stream'` or `'response'` with cancellation/timeout tracking. An enabled size limit requires stream support even without a progress callback. The constructor signature retains its legacy body type for compatibility; assignability to `env.Response` alone does not establish support for these runtime stream inputs. Node-only TypeScript imports can keep DOM declarations excluded with declaration checking enabled. - **Examples:** Show handling `ERR_BAD_RESPONSE` separately from `ERR_NETWORK`, and a custom Response wrapper that forwards tracked streams unchanged when used with `maxContentLength` or `onDownloadProgress`. - **Notes:** Do not imply the limit, default adapter, genuine network-error behavior, or accepted constructor types change. No migration is required. ### Runtime configuration prototype hardening - **Change:** Document the shared-prototype filtering applied to request config and interceptor replacements. - **Source:** `PRE_RELEASE_CHANGELOG.md` Bug Fixes, Runtime configuration hardening. - **Status:** Pending. - **Docs targets:** Request interceptor and custom adapter guidance; request-config security and migration notes; translated docs after the English documentation is finalized. - **Required content:** Explain that own request-config fields remain supported, including fields on a root null-prototype config, except that unsafe materialization keys (`__proto__`, `constructor`, and `prototype`) are always excluded. Values inherited only from a realm's shared `Object.prototype` are ignored even if that prototype's `constructor` is changed, deleted, or replaced by an accessor. An interceptor that returns the writable, already-merged null-prototype config preserves object identity through the adapter and `response.config`. A frozen, sealed, accessor-based, otherwise restricted, or unsafe-key-bearing null-prototype replacement is materialized into a writable filtered snapshot because dispatch updates fields such as headers, data, and temporary response state and must retain the dangerous-key filtering invariant. An interceptor replacement with a non-terminal application-defined prototype is likewise converted to a null-prototype normalized snapshot: safe inherited fields are materialized as own fields, but the original identity, prototype, `instanceof` branding, accessor placement, and property descriptor attributes are not preserved. Because a foreign shared `Object.prototype` is structurally indistinguishable from an application-created terminal null-prototype template once mutable properties are altered, inherited fields on terminal null-prototype ancestors are intentionally excluded as a fail-closed security boundary. - **Examples:** Show an unchanged merged config retaining identity between a request interceptor and custom adapter. Show a request interceptor returning an object with a non-terminal application prototype whose custom adapter field is materialized into the normalized snapshot, and contrast it with a terminal `Object.create(null)` prototype whose inherited behavior fields are ignored. - **Notes:** Present replacement normalization and the terminal null-prototype restriction as intentional security compatibility changes. Do not imply that mutating `Object.prototype` is supported or safe. ### Proxy bypass CIDR ranges - **Change:** Document CIDR matching in `NO_PROXY` and `no_proxy`. - **Source:** `PRE_RELEASE_CHANGELOG.md` Features, Proxy bypass CIDR ranges. - **Status:** Pending. - **Docs targets:** Node proxy/environment-variable guidance and request-config proxy documentation; translated docs after the English documentation is finalized. - **Required content:** Explain that IPv4 and IPv6 CIDR entries are supported, bracketed IPv6 is accepted, IPv4-mapped IPv6 ranges are normalized to IPv4 when their prefix permits it, address families remain distinct, and malformed CIDR entries do not bypass the proxy. State explicitly that `0.0.0.0/0` bypasses the proxy for every IPv4 destination and `::/0` does the same for IPv6. - **Examples:** Show `NO_PROXY=10.0.0.0/8,2001:db8::/32` bypassing matching HTTP destinations and identify `/0` as the entire-family form. - **Notes:** Preserve the existing hostname, explicit-port, wildcard, loopback, and non-CIDR matching behavior. ### Fetch and HTTP/2 adapter option consistency - **Change:** Document adapter-specific redirect, custom fetch, DNS lookup, and proxy behavior. - **Source:** `PRE_RELEASE_CHANGELOG.md` Bug Fixes, Fetch adapter consistency and HTTP/2 adapter consistency. - **Status:** Pending. - **Docs targets:** Request-config entries for `fetchOptions`, `maxRedirects`, `lookup`, `httpVersion`, and `proxy`; custom adapter/fetch guidance; translated docs after the English documentation is finalized. - **Required content:** State that a custom fetch receives the fully resolved `Request` when `Request` is supported and continues to receive a second `fetchOptions` argument containing safe own custom fields; Axios-managed fields such as method, headers, body, signal, duplex, and credentials are represented by the `Request` and omitted from that second argument. Custom fetch implementations that previously inspected those fields on the second argument must migrate to the `Request`; identify this as an intentional compatibility change that prevents the second argument from overriding the authoritative request. Explain that `maxRedirects: 0` requests manual redirect handling in the Fetch adapter, but response visibility follows the Fetch runtime: Node may expose the 3xx status and `Location`, while browsers return an opaque redirect with status 0 and inaccessible headers. Custom DNS lookup applies to HTTP/2 connections and participates in session reuse. HTTP/2 ignores process-environment and HTTP/1-agent `proxyEnv` settings because `http2.connect()` cannot apply them, `proxy: false` remains direct, and an explicit Axios proxy object rejects with `ERR_NOT_SUPPORT`. - **Examples:** Include focused Fetch `maxRedirects: 0` and Node `httpVersion: 2` plus `lookup` examples. - **Notes:** Present the filtered custom-Fetch second argument, Fetch manual redirects, and explicit HTTP/2 proxy rejection as intentional compatibility changes. Do not imply that positive Fetch `maxRedirects` values enforce a redirect count; only zero maps to the platform's manual redirect mode. Do not present the Node-visible 3xx response as portable browser behavior. Keep the HTTP/2 environment-proxy direct-egress residual prominent for deployments that treat proxying as mandatory policy. ### RFC 9110 HTTP status code names - **Change:** Document the additive RFC 9110 names for HTTP statuses 413 and 422. - **Source:** `PRE_RELEASE_CHANGELOG.md` Features, #11082, closes #11066. - **Status:** Pending. - **Docs targets:** `README.md` and `docs/pages/advanced/api-reference.md` `HttpStatusCode` guidance; migration or upgrade notes; translated docs after the English documentation is finalized. - **Required content:** Introduce `HttpStatusCode.ContentTooLarge` for 413 and `HttpStatusCode.UnprocessableContent` for 422 as the preferred RFC 9110 names. Explain that `PayloadTooLarge` and `UnprocessableEntity` remain available as deprecated aliases throughout v1.x, and that numeric reverse lookups continue returning those legacy names for backward compatibility. - **Examples:** Show forward comparisons using `HttpStatusCode.ContentTooLarge` and `HttpStatusCode.UnprocessableContent`. - **Notes:** Removing the deprecated aliases or changing the numeric reverse-lookup strings is reserved for a future major release. Keep ESM and CommonJS examples aligned and update translated documentation after the English wording is finalized. ### Streaming reads from download progress events - **Change:** Document how to read incremental response data from throttled download progress events, and the guaranteed final delivery on successful XHR `loadend`. - **Source:** `PRE_RELEASE_CHANGELOG.md` Bug Fixes, closes #6796. - **Status:** Pending. - **Docs targets:** README request config reference for `onDownloadProgress`; any response streaming examples. - **Required content:** Progress callbacks are throttled, so intermediate deliveries can run after the originating browser event finished dispatching; in that case `event.currentTarget` is `null` per DOM semantics, while `event.target` still references the request. A final download delivery with the complete transfer state is guaranteed when a completed XHR download reaches its successful `loadend` handler and is dispatched live. Upload progress, stream-error or abort-reason flushes, and failed XHR downloads retain their prior pending-event behavior. - **Examples:** An incremental `responseText` reader that slices new data using `progressEvent.event.target` inside `onDownloadProgress`. ### Typed request params - **Change:** Document the additive request-params generic across axios's public TypeScript declarations. - **Source:** `PRE_RELEASE_CHANGELOG.md` Features, #11081, closes #4954. - **Status:** Applied. - **Docs targets:** TypeScript usage guidance; request config reference for `params` and `paramsSerializer`; API reference for request methods, `AxiosResponse`, `AxiosPromise`, `AxiosError`, `CanceledError`, `isCancel`, and adapters; cancellation guidance; translated docs after the English documentation is finalized. - **Required content:** Explain that `AxiosRequestConfig` uses `D` for request data and `P` for query params, and that custom params serializers receive the same `P`. Cover propagation through `RawAxiosRequestConfig`, `InternalAxiosRequestConfig`, defaults, default response shapes, `AxiosResponse`, `AxiosPromise`, `AxiosError`, `CanceledError`, the `isCancel` type guard, request aliases, `request()`, callable instances, adapters, and `mergeConfig()`. State that default request results and explicitly typed `AxiosPromise` values preserve `D` and `P` on `response.config.data` and `response.config.params`, including when request methods infer those types from request config. Note that request methods add `P` as the final generic so the existing `T`, custom response `R`, and `D` positions remain unchanged, and explicitly supplied custom response types continue to control the resolved value. - **Examples:** Show a `SearchParams` interface used with `AxiosRequestConfig`, including a serializer callback that receives `SearchParams`, an invalid params object rejected by TypeScript, and an inferred default response whose `response.config.params` remains `SearchParams`. Include an `AxiosPromise` adapter/promise example and cancellation narrowing from `unknown` with `isCancel()`, demonstrating that both preserve request data and params on the config. - **Notes:** README, English docs, and Spanish, French, and Chinese translations now cover the request-data and params generics, serializer typing, default response propagation, promises/adapters, error and cancellation narrowing, request method order, and config merging. The `any` default is documented for backward compatibility; the internal default-response marker remains undocumented. ### Synchronous request interceptor error handling - **Change:** Document how synchronous request interceptor errors are handled without changing the existing paired-handler contract. - **Source:** `PRE_RELEASE_CHANGELOG.md` Bug Fixes, #11071. - **Status:** Applied. - **Docs targets:** `README.md` Interceptors section; interceptor API reference; migration/upgrade notes; translated docs after the English documentation is finalized. - **Required content:** Explain that when a synchronous request interceptor throws, axios calls that interceptor's paired `onRejected` handler and stops running the remaining request interceptors. If the handler returns normally, including returning `undefined` or a fulfilled Promise, axios treats the error as handled and dispatches with the last valid config; a value returned by the handler does not replace that config. If there is no rejection handler, or the handler throws or returns a rejected Promise, axios does not dispatch the request. Terminal errors continue through response rejection interceptors. - **Examples:** Show a synchronous validation interceptor whose rejection handler returns `Promise.reject(error)` to block dispatch, and a logging-only rejection handler that returns normally to preserve the existing request-continuation behavior. - **Notes:** README, interceptor docs, upgrade guidance, and Spanish, French, and Chinese translations now document blocking and continuation examples while preserving axios's synchronous paired-handler semantics. ### Opt-in AxiosHeaders parameter parsing - **Change:** Document the additive `AxiosHeaders.parseParameters()` parser for normalized HTTP parameter values. - **Source:** `PRE_RELEASE_CHANGELOG.md` Features, #11051, closes #11050. - **Status:** Applied. - **Docs targets:** `README.md` `AxiosHeaders#get` section; `docs/pages/advanced/api-reference.md` and `docs/pages/advanced/header-methods.md`; translated docs after English docs are finalized. - **Required content:** Explain that callers can pass `AxiosHeaders.parseParameters` to `AxiosHeaders#get()` to produce a null-prototype map with case-insensitive parameter names, remove surrounding quoted-string delimiters, decode quoted-pair DQUOTE/backslash escapes, keep commas and semicolons inside quoted values, and remove only RFC optional whitespace around unquoted values. Note that unsafe object-materialization keys (`__proto__`, `constructor`, and `prototype`) are omitted. State explicitly that `get(name, true)` remains the legacy tokenizer and keeps its existing output for backward compatibility. - **Examples:** Show `headers.get('content-type', AxiosHeaders.parseParameters)` returning `{ boundary: 'a,b' }` for `multipart/form-data; boundary="a,b"`. - **Notes:** README, API/header-method docs, and Spanish, French, and Chinese translations now document the additive parser, its hardened output, quoted-value behavior, and the unchanged legacy `true` tokenizer. ### Malformed `http(s):` URL rejection - **Change:** Document that axios rejects `http:`/`https:` URLs that omit `//` after the protocol, and that the error now names the offending URL. - **Source:** `PRE_RELEASE_CHANGELOG.md` Bug Fixes, #11000 (rejection) and #11008 (improved message). - **Status:** Applied. - **Docs targets:** `README.md` errors / handling-errors section; migration/upgrade notes; `docs/pages/advanced/request-config.md` `url`/`baseURL` description; translated docs after English docs are finalized. - **Required content:** Explain that since this release a request `url` or `baseURL` of the form `https:example.com` or `https:/example.com` (scheme present, `//` missing) is rejected with an `AxiosError` whose code is `ERR_INVALID_URL`, instead of being silently normalized by the browser/Node URL parser. This is a security fix preventing `baseURL`/allowlist (SSRF) bypasses. Callers must pass a well-formed URL such as `https://example.com`. The error message now includes the offending URL: `Invalid URL "https:example.com": missing "//" after protocol`. The reported URL is the control-character-normalized form with userinfo (credentials), query parameter values, and fragment contents redacted (parameter names, host and path are preserved), because `AxiosError.message` is always serialized by `toJSON()` and the opt-in `config.redact` model cannot clean it. - **Examples:** None required. - **Notes:** README, request-config, error-handling, upgrade guidance, and Spanish, French, and Chinese translations now frame the rejection as an intentional security behavior change and describe the safely redacted error message. ### Symbol-keyed custom request config - **Change:** Document that custom request config fields can use own enumerable symbol keys and survive axios config merging. - **Source:** `PRE_RELEASE_CHANGELOG.md` Bug Fixes, #11043, closes #11042. - **Status:** Applied. - **Docs targets:** TypeScript/custom client docs; request config reference; interceptor examples if custom config fields are documented there; translated docs after English docs are finalized. - **Required content:** Explain that applications can module-augment `AxiosRequestConfig` with a specific symbol key and pass that symbol-keyed option in request config; axios preserves the own enumerable symbol property when merging defaults with request config so request interceptors and adapters can read it from `InternalAxiosRequestConfig`. - **Examples:** Include a short TypeScript example with `export const someFlag = Symbol('some flag used in request interceptor')`, `declare module 'axios' { interface AxiosRequestConfig { [someFlag]?: boolean } }`, and a request interceptor reading `config[someFlag]`. - **Notes:** README, TypeScript/request-config docs, and Spanish, French, and Chinese translations now show module augmentation and an interceptor example, limited explicitly to own enumerable symbol properties. ### FormData literal key parsing - **Change:** Document that `formToJSON`/`formDataToJSON` only split FormData field names on dot notation and bracket notation. - **Source:** `PRE_RELEASE_CHANGELOG.md` Bug Fixes, #11006, closes #5402. - **Status:** Applied. - **Docs targets:** `README.md` FormData serializer/formToJSON sections; `docs/pages/advanced/api-reference.md` `formToJSON`; generated docs pages for multipart/urlencoded form serialization; translated docs after English docs are finalized. - **Required content:** Explain that `.`, `[`, and `]` are structural path separators when converting FormData back to JSON, while other characters such as `-`, spaces, `+`, `*`, and `&` remain literal key characters. Mention that `foo[bar]`, `foo.bar`, and `foo[]` continue to create nested object/array paths. - **Examples:** Include a short example showing `form.append('user-name', 'johndoe')` converting to `{ 'user-name': 'johndoe' }`, and `form.append('user.name', 'john')` or `form.append('user[name]', 'john')` converting to `{ user: { name: 'john' } }`. - **Notes:** README, API/multipart/HTML-form docs, and Spanish, French, and Chinese translations now document dot/bracket path parsing and literal punctuation keys without presenting the previous splitting behavior as supported. ### Wrapped error stacks in `AxiosError` - **Change:** `AxiosError.from()` appends an available wrapped error stack to `error.stack` after a `Caused by:` marker, in addition to the existing non-enumerable `error.cause`. Reconstructed request/caller frames precede that section, including when only one or two frames are available. - **Source:** [PR #11142](https://github.com/axios/axios/pull/11142), related to [issue #6670](https://github.com/axios/axios/issues/6670); `PRE_RELEASE_CHANGELOG.md` Bug Fixes. - **Status:** Pending. - **Docs targets:** `README.md` error-handling section and the API error reference in `docs/pages/` describing `stack`, `cause`, and `toJSON()`. - **Required content:** Explain that reporters which read only `error.stack` can now see an existing wrapped stack. `error.cause` remains the same original object and the supported programmatic link; cause-aware tooling already has access to it and may display some stack content twice. The wrapper's stack and reconstructed caller frames come first, followed by the wrapped stack verbatim. Marker-like text inside a message or custom stack is preserved as text. Missing, empty, non-string, throwing, or unwritable stacks are skipped defensively. Re-wrapping adds one nested section per call and increases the string size; this is not a bounded or idempotent cause history. Explicit `customProps.stack` continues to override the generated stack. - **Examples:** Show a trimmed Node socket/DNS failure stack containing the `Caused by:` section, alongside a snippet reading `error.cause`. State that the diagnostic string is also exposed by `toJSON().stack`. - **Limitations:** Wrapping cannot recover details the platform never supplied. Browser XHR `Network Error` events generally have no underlying Error stack, and this change does not diagnose hidden CORS, DNS, or TLS causes. Only failures routed through `AxiosError.from()` with usable stacks gain a section. Appended stacks are verbatim diagnostics, not redacted metadata; existing `config.redact` rules do not sanitize stack strings. - **Notes:** Do not present the marker as a stable parsing contract. Preserve the structured cause reference for programmatic consumers; no public types or migration steps are needed. axios-axios-aa54a5e/README.md000066400000000000000000003147531526660636600157610ustar00rootroot00000000000000> [!IMPORTANT] > [**Get Security Updates for Axios v0 →**](https://www.herodevs.com/support/nes-for-axios?utm_source=axios-site&utm_medium=referral&utm_campaign=nes-for-axios_global&utm_content=landing-page_none_20260924_a)

💎 Platinum sponsors

Thanks.dev

We're passionate about making open source sustainable. Scan your dependency tree to better understand which open source projects need funding.

thanks.dev

💜 Become a sponsor
💜 Become a sponsor 💜 Become a sponsor

🥇 Gold sponsors

Principal Financial Group

Free tools to help with your financial planning needs!

principal.com

SAP

BSAP SE, a global software company, is one of the largest vendors of ERP and other enterprise applications.

opensource.sap.com

Descope

Reduce user friction, prevent account takeover, and get a 360° view of your customer and agentic identities with the Descope External IAM platform.

descope.com

RxDB

RxDB is a NoSQL database for JavaScript that runs directly in your app.

rxdb.info

Poprey

Buy Instagram Likes

poprey.com

Fun 88

Fun88 is a global online gambling and betting brand founded in 2009, offering a wide range of services including sports betting, live casino games, slots, and virtual gaming.

global.fun88.com

JBO Vietnam

JBO Vietnam is a prominent online entertainment brand in Vietnam, offering sports betting, esports, online casino games, and a wide range of other exciting games.

jbo88b.com

JBO Thailand

JBO Thailand is a prominent online entertainment brand in Thailand, offering sports betting, esports, online casino games, and a wide range of other exciting games.

jbo88b.com

ScrapingBee

Scrape websites with a powerful web scraping API without managing proxies, browsers, or anti-bot defenses.

scrapingbee.com

HeroDevs

Security and long-term support for end-of-life open source software.

herodevs.com

Sweepico Promo Code

Social Gaming Platform

Sweepico Promo Code



Axios

Promise based HTTP client for the browser and node.js

Website • Documentation

[![npm version](https://img.shields.io/npm/v/axios.svg?style=flat-square)](https://www.npmjs.org/package/axios) [![Build status](https://img.shields.io/github/actions/workflow/status/axios/axios/ci.yml?branch=v1.x&label=CI&logo=github&style=flat-square)](https://github.com/axios/axios/actions/workflows/ci.yml) [![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod&style=flat-square)](https://gitpod.io/#https://github.com/axios/axios) [![install size](https://img.shields.io/badge/dynamic/json?url=https://packagephobia.com/v2/api.json?p=axios&query=$.install.pretty&label=install%20size&style=flat-square)](https://packagephobia.now.sh/result?p=axios) [![npm bundle size](https://img.shields.io/bundlephobia/minzip/axios?style=flat-square)](https://bundlephobia.com/package/axios@latest) [![npm downloads](https://img.shields.io/npm/dm/axios.svg?style=flat-square)](https://npm-stat.com/charts.html?package=axios) [![gitter chat](https://img.shields.io/gitter/room/mzabriskie/axios.svg?style=flat-square)](https://gitter.im/mzabriskie/axios) [![code helpers](https://www.codetriage.com/axios/axios/badges/users.svg)](https://www.codetriage.com/axios/axios) [![Contributors](https://img.shields.io/github/contributors/axios/axios.svg?style=flat-square)](CONTRIBUTORS.md) [![Agent Friendly](https://agentfriendlycode.com/api/badge/github/axios/axios.svg)](https://agentfriendlycode.com/repo/32)
## Table of contents - [Features](#features) - [Browser support](#browser-support) - [Installing](#installing) - [Package manager](#package-manager) - [CDN](#cdn) - [Example](#example) - [Axios API](#axios-api) - [Request method aliases](#request-method-aliases) - [Concurrency](#concurrency-deprecated) - [Creating an instance](#creating-an-instance) - [Instance methods](#instance-methods) - [Request config](#request-config) - [Response schema](#response-schema) - [Config defaults](#config-defaults) - [Global axios defaults](#global-axios-defaults) - [Custom instance defaults](#custom-instance-defaults) - [Config order of precedence](#config-order-of-precedence) - [Interceptors](#interceptors) - [Multiple interceptors](#multiple-interceptors) - [Handling errors](#handling-errors) - [Handling timeouts](#handling-timeouts) - [Cancellation](#cancellation) - [AbortController](#abortcontroller) - [CancelToken](#canceltoken-deprecated) - [Using application/x-www-form-urlencoded format](#using-applicationx-www-form-urlencoded-format) - [URLSearchParams](#urlsearchparams) - [Query string](#query-string-older-browsers) - [Automatic serialization](#automatic-serialization-to-urlsearchparams) - [Using multipart/form-data format](#using-multipartform-data-format) - [FormData](#formdata) - [Automatic serialization](#automatic-serialization-to-formdata) - [Posting files](#posting-files) - [HTML form posting](#html-form-posting-browser) - [Progress capturing](#progress-capturing) - [Rate limiting](#rate-limiting) - [AxiosHeaders](#axiosheaders) - [Fetch adapter](#fetch-adapter) - [Custom fetch](#custom-fetch) - [Using with Tauri](#using-with-tauri) - [Using with SvelteKit](#using-with-sveltekit) - [HTTP/2 support](#http2-support) - [Semver](#semver) - [Promises](#promises) - [TypeScript](#typescript) - [Contributing](#contributing) - [Local setup](#local-setup) - [Resources](#resources) - [Credits](#credits) - [License](#license) ## Features - Make [XMLHttpRequests](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) from the browser. - Make [http](https://nodejs.org/api/http.html) requests from Node.js. - Use the [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) API for asynchronous request handling. - Intercept requests and responses to add custom logic or transform data. - Transform request and response data. - Cancel requests with built-in cancellation APIs. - Serialize and parse [JSON](https://www.json.org/json-en.html) data. - Serialize data objects to `multipart/form-data` or `application/x-www-form-urlencoded`. - Add client-side protection against [Cross-Site Request Forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery). ## Browser support | Chrome | Firefox | Safari | Opera | Edge | | :------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------: | | ![Chrome browser logo](https://raw.githubusercontent.com/alrra/browser-logos/main/src/chrome/chrome_48x48.png) | ![Firefox browser logo](https://raw.githubusercontent.com/alrra/browser-logos/main/src/firefox/firefox_48x48.png) | ![Safari browser logo](https://raw.githubusercontent.com/alrra/browser-logos/main/src/safari/safari_48x48.png) | ![Opera browser logo](https://raw.githubusercontent.com/alrra/browser-logos/main/src/opera/opera_48x48.png) | ![Edge browser logo](https://raw.githubusercontent.com/alrra/browser-logos/main/src/edge/edge_48x48.png) | | Latest ✔ | Latest ✔ | Latest ✔ | Latest ✔ | Latest ✔ | [![Browser Matrix](https://saucelabs.com/open_sauce/build_matrix/axios.svg)](https://saucelabs.com/u/axios) ## Installing ### Package manager Using npm: ```bash $ npm install axios ``` Using yarn: ```bash $ yarn add axios ``` Using pnpm: ```bash $ pnpm add axios ``` Using bun: ```bash $ bun add axios ``` Using Deno: ```bash $ deno add axios ``` Once the package is installed, import it with `import` or `require`: ```js import axios, { isCancel, AxiosError } from 'axios'; ``` You can also use the default export, since the named export is just a re-export from the Axios factory: ```js import axios from 'axios'; console.log(axios.isCancel('something')); ``` If you use `require` for importing, **only the default export is available**: ```js const axios = require('axios'); console.log(axios.isCancel('something')); ``` Some bundlers and ES6 linters need this form: ```js import { default as axios } from 'axios'; ``` In custom or legacy environments, you can import the bundle directly: ```js const axios = require('axios/dist/browser/axios.cjs'); // browser commonJS bundle (ES2017) // const axios = require('axios/dist/node/axios.cjs'); // node commonJS bundle (ES2017) ``` ### CDN Using jsDelivr CDN (ES5 UMD browser module): ```html ``` Using unpkg CDN: ```html ``` ## Example ```js import axios from 'axios'; //const axios = require('axios'); // legacy way try { const response = await axios.get('/user?ID=12345'); console.log(response); } catch (error) { console.error(error); } // Optionally the request above could also be done as axios .get('/user', { params: { ID: 12345, }, timeout: 5000, // 5 seconds. See "Handling Timeouts" below for matching error handling }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }) .finally(function () { // always executed }); // Want to use async/await? Add the `async` keyword to your outer function/method. async function getUser() { try { // Example: GET request with query parameters const response = await axios.get('/user', { params: { ID: 12345, }, }); // Using the `params` option improves readability and automatically formats query strings console.log(response); } catch (error) { console.error(error); } } ``` > Note: Set a `timeout` in production. Without one, a stalled request can hang > indefinitely. See [Handling Timeouts](#handling-timeouts) for the matching error handling. > Note: `async/await` is part of ECMAScript 2017 and is not supported in Internet > Explorer and older browsers, so use with caution. Performing a `POST` request ```js const response = await axios.post('/user', { firstName: 'Fred', lastName: 'Flintstone', }); console.log(response); ``` Performing multiple concurrent requests ```js function getUserAccount() { return axios.get('/user/12345'); } function getUserPermissions() { return axios.get('/user/12345/permissions'); } Promise.all([getUserAccount(), getUserPermissions()]).then(function (results) { const acct = results[0]; const perm = results[1]; }); ``` ## axios API Requests can be made by passing the relevant config to `axios`. ##### axios(config) ```js // Send a POST request axios({ method: 'post', url: '/user/12345', data: { firstName: 'Fred', lastName: 'Flintstone', }, }); ``` ```js // GET request for remote image in node.js import fs from 'fs'; const response = await axios({ method: 'get', url: 'https://bit.ly/2mTM3nY', responseType: 'stream', }); response.data.pipe(fs.createWriteStream('ada_lovelace.jpg')); ``` ##### axios(url[, config]) ```js // Send a GET request (default method) axios('/user/12345'); ``` ### Request method aliases For convenience, aliases have been provided for all common request methods. ##### axios.request(config) ##### axios.get(url[, config]) ##### axios.delete(url[, config]) ##### axios.head(url[, config]) ##### axios.options(url[, config]) ##### axios.post(url[, data[, config]]) ##### axios.put(url[, data[, config]]) ##### axios.patch(url[, data[, config]]) ###### Note When using the alias methods `url`, `method`, and `data` properties don't need to be specified in config. ### Concurrency (deprecated) Use `Promise.all` instead of these helpers. Helper functions for dealing with concurrent requests. axios.all(iterable) axios.spread(callback) ### Creating an instance You can create a new instance of axios with a custom config. ##### axios.create([config]) ```js const instance = axios.create({ baseURL: 'https://some-domain.com/api/', timeout: 1000, headers: { 'X-Custom-Header': 'foobar' }, }); ``` ### Instance methods The following instance methods are available. Axios merges the specified config with the instance config. ##### axios#request(config) ##### axios#get(url[, config]) ##### axios#delete(url[, config]) ##### axios#head(url[, config]) ##### axios#options(url[, config]) ##### axios#post(url[, data[, config]]) ##### axios#put(url[, data[, config]]) ##### axios#patch(url[, data[, config]]) ##### axios#getUri([config]) ## Request config ### Security notice: decompression-bomb protection is opt-in By default `maxContentLength` and `maxBodyLength` are `-1` (unlimited). A malicious or compromised server can return a tiny gzip/deflate/brotli/zstd body that expands to gigabytes and exhaust the Node.js process. If you call servers you do not fully trust, **set a cap**: ```js axios.defaults.maxContentLength = 10 * 1024 * 1024; // 10 MB axios.defaults.maxBodyLength = 10 * 1024 * 1024; ``` See the [security guide](https://axios.rest/pages/misc/security.html) for details. These config options are available for requests. Only `url` is required. Requests default to `GET` when `method` is not set. ```js { // `url` is the server URL for the request url: '/user', // `method` is the request method to be used when making the request method: 'get', // default // Axios prepends `baseURL` to `url` unless `url` is absolute and `allowAbsoluteUrls` is set to true. // It can be convenient to set `baseURL` for an instance of axios to pass relative URLs // to the methods of that instance. // `baseURL` is not a security boundary. If `url` is attacker-controlled, validate it // before passing it to axios. Relative URLs can contain `..` segments that resolve // outside an intended path prefix after the final URL is parsed. // HTTP(S) URLs must include `//` after the protocol. Values such as // `https:example.com` and `https:/example.com` are rejected with ERR_INVALID_URL. baseURL: 'https://some-domain.com/api/', // `allowAbsoluteUrls` determines whether or not absolute URLs will override a configured `baseUrl`. // When set to true (default), absolute values for `url` will override `baseUrl`. // When set to false, absolute values for `url` will always be prepended by `baseUrl`. allowAbsoluteUrls: true, // `transformRequest` allows changes to the request data before it is sent to the server // This is only applicable for request methods 'PUT', 'POST', 'PATCH' and 'DELETE' // The last function in the array must return a string or an instance of Buffer, ArrayBuffer, // FormData or Stream // You may modify the headers object. transformRequest: [function (data, headers) { // Do whatever you want to transform the data return data; }], // `transformResponse` allows changes to the response data to be made before // it is passed to then/catch transformResponse: [function (data) { // Do whatever you want to transform the data return data; }], // `parseReviver` is an optional function passed as the // second argument (reviver) to JSON.parse() parseReviver: function (key, value, context) { // In modern environments, context.source provides the raw JSON string // allowing for precision-safe parsing of BigInt if (typeof value === 'number' && context?.source) { const isInteger = Number.isInteger(value); const isUnsafe = !Number.isSafeInteger(value); const isValidIntegerString = /^-?\d+$/.test(context.source); if (isInteger && isUnsafe && isValidIntegerString) { try { return BigInt(context.source); } catch { // Fallback: return original value if parsing fails } } } return value; }, // `headers` are custom headers to be sent headers: {'X-Requested-With': 'XMLHttpRequest'}, // `params` are the URL parameters to be sent with the request // Must be a plain object or a URLSearchParams object params: { ID: 12345 }, // `paramsSerializer` is an optional config that allows you to customize serializing `params`. paramsSerializer: { // Custom encoder function which sends key/value pairs in an iterative fashion. encode?: (param: string): string => { /* Do custom operations here and return transformed string */ }, // Custom serializer function for the entire parameter. Allows the user to mimic pre 1.x behaviour. serialize?: (params: Record, options?: ParamsSerializerOptions ), // Configuration for formatting array indexes in the params. indexes: false, // Three available options: (1) indexes: null (leads to no brackets), (2) (default) indexes: false (leads to empty brackets), (3) indexes: true (leads to brackets with indexes). // Maximum object nesting depth when serializing params. Payloads deeper than this throw an // AxiosError with code ERR_FORM_DATA_DEPTH_EXCEEDED. Default: 100. Set to Infinity to disable. maxDepth: 100 }, // `data` is the data to be sent as the request body // Only applicable for request methods 'PUT', 'POST', 'DELETE', and 'PATCH' // `data` is request-specific: axios does not inherit or deep-merge it from defaults. // To add shared body fields, use a request interceptor or transformRequest. // When no `transformRequest` is set, it must be of one of the following types: // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams // - Browser only: FormData, File, Blob // - React Native: FormData // - Node only: Stream, Buffer, FormData (form-data package) data: { firstName: 'Fred' }, // `formDataHeaderPolicy` controls how node.js FormData#getHeaders() is copied. // 'legacy' (default) copies all returned headers for v1 compatibility. // 'content-only' copies only Content-Type and Content-Length. formDataHeaderPolicy: 'legacy', // syntax alternative to send data into the body // method post // only the value is sent, not the key data: 'Country=Brasil&City=Belo Horizonte', // `timeout` specifies the number of milliseconds before the request times out. // If the request takes longer than `timeout`, Axios aborts it. timeout: 1000, // default is `0` (no timeout) // `withCredentials` indicates whether or not cross-site Access-Control requests // should be made using credentials // This only controls whether the browser sends credentials. // It does not control whether the XSRF header is added. withCredentials: false, // default // `adapter` allows custom handling of requests which makes testing easier. // Return a promise and supply a valid response (see lib/adapters/README.md) adapter: function (config) { /* ... */ }, // Also, you can set the name of the built-in adapter, or provide an array with their names // to choose the first available in the environment adapter: 'xhr', // 'fetch' | 'http' | ['xhr', 'http', 'fetch'] // `auth` indicates that HTTP Basic auth should be used, and supplies credentials. // This will set an `Authorization` header, overwriting any existing // `Authorization` custom headers you have set using `headers`. // If `auth` is omitted, the Node.js HTTP and fetch adapters can read // HTTP Basic auth credentials from the request URL, for example // `https://user:pass@example.com`. Axios decodes percent-encoded URL // credentials, and `auth` takes precedence over URL-embedded credentials. // The Node.js HTTP adapter preserves Basic auth on same-origin redirects // and strips it on cross-origin redirects. // Please note that only HTTP Basic auth is configurable through this parameter. // For Bearer tokens and such, use `Authorization` custom headers instead. auth: { username: 'janedoe', password: 's00pers3cret' }, // `responseType` indicates the type of data that the server will respond with // options are: 'arraybuffer', 'document', 'json', 'text', 'stream' // browser only: 'blob' responseType: 'json', // default // `responseEncoding` indicates encoding to use for decoding responses (Node.js only) // Note: Ignored for `responseType` of 'stream' or client-side requests // options are: 'ascii', 'ASCII', 'ansi', 'ANSI', 'binary', 'BINARY', 'base64', 'BASE64', 'base64url', // 'BASE64URL', 'hex', 'HEX', 'latin1', 'LATIN1', 'ucs-2', 'UCS-2', 'ucs2', 'UCS2', 'utf-8', 'UTF-8', // 'utf8', 'UTF8', 'utf16le', 'UTF16LE' responseEncoding: 'utf8', // default // `xsrfCookieName` is the name of the cookie to use as a value for the xsrf token xsrfCookieName: 'XSRF-TOKEN', // default // `xsrfHeaderName` is the name of the http header that carries the xsrf token value xsrfHeaderName: 'X-XSRF-TOKEN', // default // `withXSRFToken` defines whether to send the XSRF header in browser requests. // `undefined` (default) - set XSRF header only for the same origin requests // `true` - always set XSRF header, including for cross-origin requests // `false` - never set XSRF header // function - resolve with custom logic; receives the internal config object withXSRFToken: boolean | undefined | ((config: InternalAxiosRequestConfig) => boolean | undefined), // `withXSRFToken` controls whether Axios reads the XSRF cookie and sets the XSRF header. // - `undefined` (default): the XSRF header is set only for same-origin requests. // - `true`: attempt to set the XSRF header for all requests (including cross-origin). // - `false`: never set the XSRF header. // - function: a callback that receives the request `config` and returns `true`, // `false`, or `undefined` to decide per-request behavior. // // Note about `withCredentials`: `withCredentials` controls whether cross-site // requests include credentials (cookies and HTTP auth). In older Axios versions, // setting `withCredentials: true` implicitly caused Axios to set the XSRF header // for cross-origin requests. Newer Axios separates these concerns: to allow the // XSRF header to be sent for cross-origin requests you should set both // `withCredentials: true` and `withXSRFToken: true`. // // Example: // axios.get('/user', { withCredentials: true, withXSRFToken: true }); // `onUploadProgress` allows handling of progress events for uploads // browser & node.js onUploadProgress: function ({loaded, total, progress, bytes, estimated, rate, upload = true}) { // Do whatever you want with the Axios progress event }, // `onDownloadProgress` allows handling of progress events for downloads // browser & node.js onDownloadProgress: function ({loaded, total, progress, bytes, estimated, rate, download = true}) { // Do whatever you want with the Axios progress event }, // `maxContentLength` defines the max size of the response content in bytes. // It is enforced by the Node.js HTTP adapter and the fetch adapter. maxContentLength: 2000, // `maxBodyLength` defines the max size of the request content in bytes. // It is enforced by the Node.js HTTP adapter and the fetch adapter when the body length can be determined. maxBodyLength: 2000, // `redact` masks matching config keys when AxiosError#toJSON() is called. // Matching is case-insensitive and recursive. It does not change the request. redact: ['authorization', 'password'], // `validateStatus` defines whether to resolve or reject the promise for a given // HTTP response status code. If `validateStatus` returns `true` or is set to // `null`, Axios resolves the promise; otherwise, Axios rejects it. // Explicit `validateStatus: undefined` resolves every status by default for // backward compatibility. Set `transitional.validateStatusUndefinedResolves` // to `false` to make explicit `undefined` behave as if this option was omitted. validateStatus: function (status) { return status >= 200 && status < 300; // default }, // `maxRedirects` defines the maximum number of redirects to follow in node.js. // If set to 0, Axios follows no redirects. maxRedirects: 21, // default // `sensitiveHeaders` (Node only option) lists custom secret-bearing headers // (such as `X-API-Key`) to remove from cross-origin redirects. Matching is // case-insensitive. Same-origin redirects keep these headers. If // `maxRedirects` is 0, this option is not used. sensitiveHeaders: ['X-API-Key'], // `beforeRedirect` defines a function that Axios calls before redirect. // Use this to adjust the request options upon redirecting, // to inspect the latest response headers, // or to cancel the request by throwing an error // If maxRedirects is set to 0, `beforeRedirect` is not used. beforeRedirect: (options, { headers }) => { if ( options.hostname === "example.com" && options.protocol === "https:" ) { options.auth = "user:password"; } }, // Security note: // The `beforeRedirect` hook runs after sensitive headers are stripped during redirects. // `follow-redirects` removes credentials on protocol downgrades // (HTTPS to HTTP). Because `beforeRedirect` runs after that step, // re-injecting credentials without checking the destination can expose // sensitive data. Only add credentials for trusted HTTPS destinations. // `socketPath` defines a UNIX Socket to be used in node.js. // e.g. '/var/run/docker.sock' to send requests to the docker daemon. // Only either `socketPath` or `proxy` can be specified. // If both are specified, `socketPath` is used. // // Security: when `socketPath` is set, hostname/port of the URL are ignored, // which bypasses hostname-based SSRF protections. Never derive `socketPath` // from untrusted input. Use `allowedSocketPaths` (below) to restrict accepted // socket paths for defense-in-depth. socketPath: null, // default // `allowedSocketPaths` restricts which `socketPath` values are accepted. // Accepts a string or array of strings. Entries and the incoming socketPath // are compared after path.resolve(). A mismatch throws AxiosError with code // `ERR_BAD_OPTION_VALUE`. When null/undefined, no restriction is applied. allowedSocketPaths: null, // default // `transport` determines the transport method for the request. // If defined, Axios uses it. Otherwise, if `maxRedirects` is 0, // Axios uses the default `http` or `https` library, depending on the protocol specified in `protocol`. // Otherwise, Axios uses the `httpFollow` or `httpsFollow` library, again depending on the protocol, // which can handle redirects. transport: undefined, // default // `httpAgent` and `httpsAgent` define a custom agent to be used when performing http // and https requests, respectively, in node.js. This allows options to be added like // `keepAlive` that are not enabled by default before Node.js v19.0.0. After Node.js // v19.0.0, you no longer need to customize the agent to enable `keepAlive` because // `http.globalAgent` has `keepAlive` enabled by default. httpAgent: new http.Agent({ keepAlive: true }), httpsAgent: new https.Agent({ keepAlive: true }), // `proxy` defines the hostname, port, and protocol of the proxy server. // You can also define your proxy using the conventional `http_proxy` and // `https_proxy` environment variables. If you are using environment variables // for your proxy configuration, you can also define a `no_proxy` environment // variable as a comma-separated list of domains that should not be proxied. // Use `false` to disable proxies, ignoring environment variables. // On Node.js versions with native environment proxy support, axios defers // environment proxy handling to Node when the selected agent has `proxyEnv` // enabled, including processes started with `NODE_USE_ENV_PROXY=1`, // `--use-env-proxy`, or `NODE_OPTIONS=--use-env-proxy`. Custom agents without // `proxyEnv` continue to use axios environment proxy resolution. Explicit // `proxy` config is still handled by axios. // `auth` indicates that HTTP Basic auth should be used to connect to the proxy, and // supplies credentials. // For `http://` targets, axios sends the request to the proxy in // forward-proxy mode and stamps `Proxy-Authorization` onto the request // headers (overwriting any user-supplied `Proxy-Authorization` header). // For `https://` targets, axios establishes a CONNECT tunnel through the // proxy and performs TLS end-to-end with the origin; `Proxy-Authorization` // is sent on the CONNECT request only, never on the wrapped TLS request, // so the proxy never sees the URL, headers, or body. Axios forwards // `httpsAgent` TLS options such as `ca`, `cert`, `key`, and // `rejectUnauthorized` to the generated tunneling agent, so they still apply // to the origin TLS connection. // If you supply an `HttpsProxyAgent`, axios leaves tunneling to that agent. // If the proxy server uses HTTPS, then you must set the protocol to `https`. // A user-supplied `Host` header in `headers` is preserved when forwarding // through a proxy (case-insensitive match on `host`/`Host`/`HOST`); this // lets you target a virtual host that differs from the request URL, for // example, hitting `127.0.0.1:4000` while having the proxy treat the // request as `example.com`. If no `Host` header is supplied, axios // defaults it to the request URL's `hostname:port` as before. The Host // header is only set in forward-proxy mode (HTTP targets); for HTTPS // tunneling the Host header is sent inside the TLS connection, not seen // by the proxy. proxy: { protocol: 'https', host: '127.0.0.1', // hostname: '127.0.0.1' // Takes precedence over 'host' if both are defined port: 9000, auth: { username: 'mikeymike', password: 'rapunz3l' } }, // `cancelToken` specifies a cancel token that can be used to cancel the request // (see Cancellation section below for details) cancelToken: new CancelToken(function (cancel) { }), // an alternative way to cancel Axios requests using AbortController signal: new AbortController().signal, // `decompress` indicates whether or not the response body should be decompressed // automatically. If set to `true` will also remove the 'content-encoding' header // from the responses objects of all decompressed responses // Axios supports gzip, deflate, brotli, and zstd when the current Node.js // runtime provides the corresponding zlib decompressor. // - Node only (XHR cannot turn off decompression) decompress: true, // default // `insecureHTTPParser` boolean. // Indicates where to use an insecure HTTP parser that accepts invalid HTTP headers. // This may allow interoperability with non-conformant HTTP implementations. // Using the insecure parser should be avoided. // see options https://nodejs.org/dist/latest-v12.x/docs/api/http.html#http_http_request_url_options_callback // see also https://nodejs.org/en/blog/vulnerability/february-2020-security-releases/#strict-http-header-parsing-none insecureHTTPParser: undefined, // default // transitional options for backward compatibility that may be removed in the newer versions transitional: { // silent JSON parsing mode // `true` - ignore JSON parsing errors and set response.data to null if parsing failed (old behaviour) // `false` - throw SyntaxError if JSON parsing failed // Important: this option only takes effect when `responseType` is explicitly set to 'json'. // When `responseType` is omitted (defaults to no value), axios uses `forcedJSONParsing` // to attempt JSON parsing, but will silently return the raw string on failure regardless // of this setting. To have invalid JSON throw errors, use: // { responseType: 'json', transitional: { silentJSONParsing: false } } silentJSONParsing: true, // default value for the current Axios version // try to parse the response string as JSON even if `responseType` is not 'json' forcedJSONParsing: true, // throw ETIMEDOUT error instead of generic ECONNABORTED on request timeouts clarifyTimeoutError: false, // keep explicit `validateStatus: undefined` resolving every response status // for backward compatibility. Set to false to make explicit undefined behave // as if validateStatus was omitted. validateStatusUndefinedResolves: true, // advertise `zstd` in the default Accept-Encoding header when the current // Node.js runtime supports zstd decompression. Axios still decompresses // zstd responses when support exists and `decompress` is true. advertiseZstdAcceptEncoding: false, // use the legacy interceptor request/response ordering legacyInterceptorReqResOrdering: true, // default }, env: { // The FormData class to be used to automatically serialize the payload into a FormData object FormData: window?.FormData || global?.FormData }, formSerializer: { visitor: (value, key, path, helpers) => {}; // custom visitor function to serialize form values dots: boolean; // use dots instead of brackets format metaTokens: boolean; // keep special endings like {} in parameter key indexes: boolean; // array indexes format null - no brackets, false - empty brackets, true - brackets with indexes maxDepth: 100; // maximum object nesting depth; throws AxiosError (ERR_FORM_DATA_DEPTH_EXCEEDED) if exceeded. Set to Infinity to disable. }, // http adapter only (node.js) maxRate: [ 100 * 1024, // 100KB/s upload limit, 100 * 1024 // 100KB/s download limit ] } ``` For custom secret-bearing headers in Node.js, list them in `sensitiveHeaders` so Axios removes them when following a redirect to another origin: ```js axios.get('https://api.example.com/users', { headers: { 'X-API-Key': 'secret' }, sensitiveHeaders: ['X-API-Key'], }); ``` ### Strict RFC 3986 percent-encoding for query params By default, axios decodes `%3A`, `%24`, `%2C` and `%20` back to `:`, `$`, `,` and `+` for readability (the `+` follows the `application/x-www-form-urlencoded` convention for spaces in query strings). These characters are valid in a query component under [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986#section-3.4), so the default output is correct, but some backends require strict percent-encoding and reject the readable form. Override the default encoder via `paramsSerializer.encode`: ```js // Per-request: emit strict RFC 3986 percent-encoding for query values axios.get('/foo', { params: { filter: JSON.stringify({ startedAt: '2026-01-23' }) }, paramsSerializer: { encode: encodeURIComponent }, }); // Or set it on the instance defaults const client = axios.create({ paramsSerializer: { encode: encodeURIComponent }, }); ``` ## HTTP/2 support Axios has experimental HTTP/2 support in the Node.js HTTP adapter. Support depends on the runtime environment and Node.js version. Redirects and some adapter behavior may differ from HTTP/1.1. Options like `httpVersion` and `http2Options` are adapter-specific and may not work the same way in every environment. If you need HTTP/2, check runtime support or use a custom adapter. ## Response schema The response to a request contains the following information. ```js { // `data` is the response that was provided by the server data: {}, // `status` is the HTTP status code from the server response status: 200, // `statusText` is the HTTP status message from the server response statusText: 'OK', // `headers` the HTTP headers that the server responded with // All header names are lowercase and can be accessed using the bracket notation. // Example: `response.headers['content-type']` headers: {}, // `config` is the config that was provided to `axios` for the request config: {}, // `request` is the request that generated this response // It is the last ClientRequest instance in node.js (in redirects) // and an XMLHttpRequest instance in the browser request: {} } ``` When using `then`, you receive the response like this: ```js const response = await axios.get('/user/12345'); console.log(response.data); console.log(response.status); console.log(response.statusText); console.log(response.headers); console.log(response.config); ``` When using `catch`, or passing a [rejection callback](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then) as the second parameter of `then`, read the response from the `error` object. See [Handling errors](#handling-errors). ## Config defaults Config defaults apply to every request. ### Global axios defaults ```js axios.defaults.baseURL = 'https://api.example.com'; // Important: If you use axios with multiple domains, Axios sends AUTH_TOKEN to all of them. // See below for an example using Custom instance defaults instead. axios.defaults.headers.common['Authorization'] = AUTH_TOKEN; axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'; ``` ### Custom instance defaults ```js // Set config defaults when creating the instance const instance = axios.create({ baseURL: 'https://api.example.com', }); // Alter defaults after instance has been created instance.defaults.headers.common['Authorization'] = AUTH_TOKEN; ``` ### Config order of precedence Axios merges config in this order: library defaults from [lib/defaults/index.js](https://github.com/axios/axios/blob/main/lib/defaults/index.js#L49), the instance `defaults` property, and the request `config` argument. Later values take precedence over earlier ones. Some options are request-specific and are only taken from the request `config`. `data` is one of those options: axios does not inherit or deep-merge request bodies from global or instance defaults. If every request needs shared body fields, add them with a request interceptor or `transformRequest`, and scope that logic carefully so sensitive values are not sent to the wrong endpoint. ```js // Create an instance using the config defaults provided by the library // At this point the timeout config value is `0` as is the default for the library const instance = axios.create(); // Override timeout default for the library // Now all requests using this instance will wait 2.5 seconds before timing out instance.defaults.timeout = 2500; // Override timeout for this request as it's known to take a long time instance.get('/longRequest', { timeout: 5000, }); ``` ## Interceptors You can intercept requests or responses before methods like `.get()` or `.post()` resolve their promises (before code inside `then` or `catch`, or after `await`) ```js const instance = axios.create(); // Add a request interceptor instance.interceptors.request.use( function (config) { // Do something before the request is sent return config; }, function (error) { // Do something with the request error return Promise.reject(error); } ); // Add a response interceptor instance.interceptors.response.use( function (response) { // Any status code that lies within the range of 2xx causes this function to trigger // Do something with response data return response; }, function (error) { // Any status codes that fall outside the range of 2xx cause this function to trigger // Do something with response error return Promise.reject(error); } ); ``` If you need to remove an interceptor later you can. ```js const instance = axios.create(); const myInterceptor = instance.interceptors.request.use(function () { /*...*/ }); instance.interceptors.request.eject(myInterceptor); ``` You can also clear all interceptors for requests or responses. ```js const instance = axios.create(); instance.interceptors.request.use(function () { /*...*/ }); instance.interceptors.request.clear(); // Removes interceptors from requests instance.interceptors.response.use(function () { /*...*/ }); instance.interceptors.response.clear(); // Removes interceptors from responses ``` You can add interceptors to a custom instance of axios. ```js const instance = axios.create(); instance.interceptors.request.use(function () { /*...*/ }); ``` When you add request interceptors, they are presumed to be asynchronous by default. This can cause a delay in the execution of your axios request when the main thread is blocked (a promise is created under the hood for the interceptor and your request gets put at the bottom of the call stack). If your request interceptors are synchronous you can add a flag to the options object that will tell axios to run the code synchronously and avoid any delays in request execution. ```js axios.interceptors.request.use( function (config) { config.headers.test = 'I am only a header!'; return config; }, null, { synchronous: true } ); ``` If a synchronous request interceptor throws, Axios calls that interceptor's paired rejection handler and stops running the remaining request interceptors. A rejection handler that returns normally, including one that returns `undefined` or a fulfilled Promise, treats the error as handled: Axios dispatches the request with the last valid config, and ignores the handler's return value. To block dispatch, omit the rejection handler or throw/return a rejected Promise from it. The final error then continues through response rejection interceptors. Use a rejected Promise when validation must prevent the request: ```js axios.interceptors.request.use( function validate(config) { if (!config.headers.has('Authorization')) { throw new Error('Authorization is required'); } return config; }, function rejectInvalidRequest(error) { return Promise.reject(error); }, { synchronous: true } ); ``` A logging-only rejection handler can return normally to preserve the existing request-continuation behavior: ```js axios.interceptors.request.use( function prepare(config) { throw new Error('Optional preparation failed'); }, function logPreparationFailure(error) { console.warn(error); // Returning normally dispatches with the last valid config. }, { synchronous: true } ); ``` If you want to execute a particular interceptor based on a runtime check, you can add a `runWhen` function to the options object. The request interceptor will not run **if and only if** the return of `runWhen` is `false`. Axios calls the function with the config object (don't forget that you can bind your own arguments to it as well.) This can be handy when you have an asynchronous request interceptor that only needs to run at certain times. ```js function onGetCall(config) { return config.method === 'get'; } axios.interceptors.request.use( function (config) { config.headers.test = 'special get headers'; return config; }, null, { runWhen: onGetCall } ); ``` > Note: The options parameter (with `synchronous` and `runWhen` properties) is only supported for request interceptors at the moment. ### Interceptor execution order Request and response interceptors use different execution orders. Request interceptors run in reverse order (LIFO: last in, first out). The last interceptor added runs first. Response interceptors run in the order they were added (FIFO: first in, first out). The first interceptor added runs first. Example: ```js const instance = axios.create(); const interceptor = (id) => (base) => { console.log(id); return base; }; instance.interceptors.request.use(interceptor('Request Interceptor 1')); instance.interceptors.request.use(interceptor('Request Interceptor 2')); instance.interceptors.request.use(interceptor('Request Interceptor 3')); instance.interceptors.response.use(interceptor('Response Interceptor 1')); instance.interceptors.response.use(interceptor('Response Interceptor 2')); instance.interceptors.response.use(interceptor('Response Interceptor 3')); // Console output: // Request Interceptor 3 // Request Interceptor 2 // Request Interceptor 1 // [HTTP request is made] // Response Interceptor 1 // Response Interceptor 2 // Response Interceptor 3 ``` ### Multiple interceptors When a response is fulfilled and multiple response interceptors are registered: - Each interceptor runs in registration order. - Each interceptor receives the result from the previous interceptor. - The chain returns the result from the last interceptor. - If a fulfillment interceptor throws, Axios skips the next fulfillment interceptor and calls the next rejection interceptor. - After the error is caught, later fulfillment interceptors run again, just like in a promise chain. Read [the interceptor tests](./tests/browser/interceptors.browser.test.js) to see all this in code. ## Error types Axios error messages include details that can help you debug the request. Axios errors use this structure: | Property | Definition | | -------- | ---------- | | message | A quick summary of the error message and the status it failed with. | | name | This defines where the error originated from. For axios, it will always be an 'AxiosError'. | | stack | Stack trace for the error. | | config | An axios config object with specific instance configurations defined by the user from when the request was made | | code | Axios error code. The table below lists internal Axios error codes. | | status | HTTP response status code. See [here](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) for common HTTP response status code meanings. These are the internal Axios error codes: | Code | Definition | | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ERR_BAD_OPTION_VALUE | Invalid value provided in axios configuration. | | ERR_BAD_OPTION | Invalid option provided in axios configuration. | | ERR_NOT_SUPPORT | Feature or method not supported in the current axios environment. | | ERR_DEPRECATED | Deprecated feature or method used in axios. | | ERR_INVALID_URL | Invalid URL provided for axios request. | | ECONNABORTED | Typically indicates that the request has been timed out (unless `transitional.clarifyTimeoutError` is set) or aborted by the browser or its plugin. | | ERR_CANCELED | The user explicitly canceled the request with an AbortSignal or CancelToken. | | ETIMEDOUT | Request timed out after exceeding the configured Axios timeout. Set `transitional.clarifyTimeoutError` to `true`; otherwise Axios throws a generic `ECONNABORTED` error. | | ERR_NETWORK | Network-related issue. In the browser, this error can also be caused by a [CORS](https://developer.mozilla.org/ru/docs/Web/HTTP/Guides/CORS) or [Mixed Content](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content) policy violation. The browser does not allow the JS code to clarify the real reason for the error caused by security issues, so please check the console. | | ERR_FR_TOO_MANY_REDIRECTS | Request exceeded the configured maximum number of redirects. | | ERR_BAD_RESPONSE | Response cannot be parsed properly or is in an unexpected format. Usually related to a response with `5xx` status code. | | ERR_BAD_REQUEST | The request has an unexpected format or is missing required parameters. Usually related to a response with `4xx` status code. | ## Handling errors By default, Axios rejects responses with status codes outside the 2xx range. ```js axios.get('/user/12345').catch(function (error) { if (error.response) { // The request was made and the server responded with a status code // that falls out of the range of 2xx console.log(error.response.data); console.log(error.response.status); console.log(error.response.headers); } else if (error.request) { // The request was made but no response was received // `error.request` is an instance of XMLHttpRequest in the browser and an instance of // http.ClientRequest in node.js console.log(error.request); } else { // Something happened in setting up the request that triggered an Error console.log('Error', error.message); } console.log(error.config); }); ``` Axios rejects an `http:` or `https:` request `url` or `baseURL` that omits `//` after the protocol. For example, `https:example.com` and `https:/example.com` reject with `AxiosError` code `ERR_INVALID_URL` instead of being silently normalized. Pass a well-formed URL such as `https://example.com`. The error identifies the malformed URL, for example `Invalid URL "https:example.com": missing "//" after protocol`. To keep logs useful without leaking secrets, Axios reports the control-character-normalized URL with credentials, query parameter values, and fragment contents redacted; the scheme, host, path, and query parameter names remain visible. Use `validateStatus` to override the default condition (`status >= 200 && status < 300`) and choose which HTTP status codes should reject. ```js axios.get('/user/12345', { validateStatus: function (status) { return status < 500; // Resolve only if the status code is less than 500 }, }); ``` By default, explicit `validateStatus: undefined` keeps legacy behavior and resolves every response status because `transitional.validateStatusUndefinedResolves` defaults to `true`. Set it to `false` to make explicit `validateStatus: undefined` behave like the option was omitted, so Axios uses the configured/default validator and rejects non-2xx responses by default. `validateStatus: null` still accepts every response status. If you disable the transitional behavior and intentionally want all statuses to resolve, use `null` or `() => true`. ```js axios.get('/user/12345', { validateStatus: undefined, transitional: { validateStatusUndefinedResolves: false, }, }); ``` Use `toJSON` to get more information about the HTTP error. ```js axios.get('/user/12345').catch(function (error) { console.log(error.toJSON()); }); ``` To avoid logging secrets from `error.config`, pass a `redact` array in the request config. Matching config keys are masked case-insensitively at any depth when `AxiosError#toJSON()` is called. ```js axios .get('/user/12345', { headers: { Authorization: 'Bearer token' }, redact: ['authorization'], }) .catch(function (error) { console.log(error.toJSON().config.headers.Authorization); // [REDACTED ****] }); ``` ## Handling timeouts ```js async function fetchWithTimeout() { try { const response = await axios.get('https://example.com/data', { timeout: 5000, // 5 seconds transitional: { // set to true if you prefer ETIMEDOUT over ECONNABORTED clarifyTimeoutError: false, }, }); console.log('Response:', response.data); } catch (error) { if (axios.isAxiosError(error)) { if (error.code === 'ECONNABORTED' || error.code === 'ETIMEDOUT') { console.error('Request timed out. Please try again.'); return; } console.error('Axios error:', error.message); return; } console.error('Unexpected error:', error); } } ``` ## Cancellation ### AbortController Since `v0.22.0`, Axios supports AbortController: ```js const controller = new AbortController(); axios .get('/foo/bar', { signal: controller.signal, }) .then(function (response) { //... }); // cancel the request controller.abort(); ``` ### CancelToken (deprecated) You can also cancel a request using a _CancelToken_. > The axios cancel token API is based on the withdrawn [cancellable promises proposal](https://github.com/tc39/proposal-cancelable-promises). > This API is deprecated since v0.22.0 and should not be used in new projects. Create a cancel token with the `CancelToken.source` factory: ```js const CancelToken = axios.CancelToken; const source = CancelToken.source(); axios .get('/user/12345', { cancelToken: source.token, }) .catch(function (thrown) { if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // handle error } }); axios.post( '/user/12345', { name: 'new name', }, { cancelToken: source.token, } ); // cancel the request (the message parameter is optional) source.cancel('Operation canceled by the user.'); ``` You can also pass an executor function to the `CancelToken` constructor: ```js const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { // An executor function receives a cancel function as a parameter cancel = c; }), }); // cancel the request cancel(); ``` `CancelToken` also exposes low-level helpers for legacy integrations: ```js const source = axios.CancelToken.source(); const listener = (cancel) => { console.log(cancel.message); }; source.token.subscribe(listener); const signal = source.token.toAbortSignal(); // Pass `signal` to APIs that accept AbortSignal. source.cancel('Operation canceled by the user.'); source.token.unsubscribe(listener); ``` Canceled requests reject with `axios.CanceledError`. The legacy `axios.Cancel` export is an alias of `axios.CanceledError`, and cancellation errors include `__CANCEL__` for `axios.isCancel` compatibility. > Note: You can cancel several requests with the same cancel token or abort controller. > If a cancellation token is already cancelled when an Axios request starts, Axios cancels the request immediately without making a real request. > During the transition period, you can use both cancellation APIs, even for the same request: ```js const controller = new AbortController(); const source = axios.CancelToken.source(); axios.get('/user/12345', { cancelToken: source.token, signal: controller.signal, }); controller.abort(); source.cancel('Operation canceled by the user.'); ``` ## Using `application/x-www-form-urlencoded` format ### URLSearchParams By default, axios serializes JavaScript objects to `JSON`. To send data as [`application/x-www-form-urlencoded`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST), use the [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) API. It works in most browsers and in [Node](https://nodejs.org/api/url.html#url_class_urlsearchparams) v10 and later. ```js const params = new URLSearchParams({ foo: 'bar' }); params.append('extraparam', 'value'); axios.post('/foo', params); ``` ### Query string (older browsers) For very old browsers, use a [polyfill](https://github.com/WebReflection/url-search-params) and make sure it patches the global environment. Alternatively, you can encode data using the [`qs`](https://github.com/ljharb/qs) library: ```js const qs = require('qs'); axios.post('/foo', qs.stringify({ bar: 123 })); ``` With ES modules: ```js import qs from 'qs'; const data = { bar: 123 }; const options = { method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, data: qs.stringify(data), url, }; axios(options); ``` ### Older Node.js versions For older Node.js engines, use the [`querystring`](https://nodejs.org/api/querystring.html) module: ```js const querystring = require('querystring'); axios.post('https://something.com/', querystring.stringify({ foo: 'bar' })); ``` You can also use the [`qs`](https://github.com/ljharb/qs) library. > Note: The `qs` library is preferable if you need to stringify nested objects, as the `querystring` method has [known issues](https://github.com/nodejs/node-v0.x-archive/issues/1665) with that use case. ### Automatic serialization to URLSearchParams Axios automatically serializes the data object to urlencoded format if the content-type header is set to "application/x-www-form-urlencoded". ```js const data = { x: 1, arr: [1, 2, 3], arr2: [1, [2], 3], users: [ { name: 'Peter', surname: 'Griffin' }, { name: 'Thomas', surname: 'Anderson' }, ], }; await axios.postForm('https://postman-echo.com/post', data, { headers: { 'content-type': 'application/x-www-form-urlencoded' }, }); ``` The server receives these fields: ```js { x: '1', 'arr[]': [ '1', '2', '3' ], 'arr2[0]': '1', 'arr2[1][0]': '2', 'arr2[2]': '3', 'arr3[]': [ '1', '2', '3' ], 'users[0][name]': 'Peter', 'users[0][surname]': 'griffin', 'users[1][name]': 'Thomas', 'users[1][surname]': 'Anderson' } ``` If your backend body parser, such as `body-parser` for `express.js`, supports nested object decoding, the server receives the same object structure: ```js const app = express(); app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies app.post('/', function (req, res, next) { // echo body as JSON res.send(JSON.stringify(req.body)); }); server = app.listen(3000); ``` ## Using `multipart/form-data` format ### FormData To send data as `multipart/form-data`, pass a FormData instance as the payload. You do not need to set the `Content-Type` header. Axios detects it from the payload type. For browser, web worker, and React Native `FormData`, leave `Content-Type` unset so the runtime can add the multipart boundary. ```js const formData = new FormData(); formData.append('foo', 'bar'); axios.post('https://httpbin.org/post', formData); ``` In node.js, use the [`form-data`](https://github.com/form-data/form-data) library: ```js const FormData = require('form-data'); const form = new FormData(); form.append('my_field', 'my value'); form.append('my_buffer', Buffer.alloc(10)); form.append('my_file', fs.createReadStream('/foo/bar.jpg')); axios.post('https://example.com', form); ``` In node.js, when a `FormData` object provides `getHeaders()`, axios copies all returned headers by default for v1 compatibility. If the `FormData` object is custom or not fully trusted, set `formDataHeaderPolicy: 'content-only'` to copy only `Content-Type` and `Content-Length`, and set any other request headers explicitly with the request `headers` config. ### Automatic serialization to FormData Since `v0.27.0`, Axios can serialize an object to FormData if the request `Content-Type` header is set to `multipart/form-data`. This request submits data as FormData in browsers and Node.js: ```js import axios from 'axios'; axios .post( 'https://httpbin.org/post', { x: 1 }, { headers: { 'Content-Type': 'multipart/form-data', }, } ) .then(({ data }) => console.log(data)); ``` The Node.js build uses the [`form-data`](https://github.com/form-data/form-data) polyfill by default. You can override the FormData class with the `env.FormData` config option, but most applications do not need this: ```js const axios = require('axios'); var FormData = require('form-data'); axios .post( 'https://httpbin.org/post', { x: 1, buf: Buffer.alloc(10) }, { headers: { 'Content-Type': 'multipart/form-data', }, } ) .then(({ data }) => console.log(data)); ``` The Axios FormData serializer supports these special endings: - `{}` - serialize the value with JSON.stringify - `[]` - unwrap the array-like object as separate fields with the same key > Note: Arrays and FileList objects are unwrapped by default. FormData serializer supports additional options via `config.formSerializer: object` property to handle rare cases: - `visitor: Function` - user-defined visitor function that Axios calls recursively to serialize the data object to a `FormData` object by following custom rules. - `dots: boolean = false` - use dot notation instead of brackets to serialize arrays and objects; - `metaTokens: boolean = true` - add the special ending (e.g `user{}: '{"name": "John"}'`) in the FormData key. A backend body parser can use this meta-information to parse the value as JSON. - `indexes: null|false|true = false` - controls how Axios adds indexes to unwrapped keys of `flat` array-like objects. - `null` - don't add brackets (`arr: 1`, `arr: 2`, `arr: 3`) - `false`(default) - add empty brackets (`arr[]: 1`, `arr[]: 2`, `arr[]: 3`) - `true` - add brackets with indexes (`arr[0]: 1`, `arr[1]: 2`, `arr[2]: 3`) - `maxDepth: number = 100` - maximum object nesting depth the serializer will recurse into. If the input object exceeds this depth, an `AxiosError` with `code: 'ERR_FORM_DATA_DEPTH_EXCEEDED'` is thrown instead of overflowing the call stack. This protects server applications from DoS attacks via deeply nested payloads. Set to `Infinity` to disable the limit and restore pre-fix behaviour. - `Blob: typeof Blob` - Blob constructor used when converting ArrayBuffer-like values for spec-compliant `FormData`. Override it only for runtimes that provide a compatible `Blob` constructor under a different binding. ```js // Raise the limit for a schema that genuinely nests deeper than 100 levels: axios.postForm('/api', data, { formSerializer: { maxDepth: 200 } }); // Same protection applies to params serialization: axios.get('/api', { params: data, paramsSerializer: { maxDepth: 200 } }); ``` Given this object: ```js const obj = { x: 1, arr: [1, 2, 3], arr2: [1, [2], 3], users: [ { name: 'Peter', surname: 'Griffin' }, { name: 'Thomas', surname: 'Anderson' }, ], 'obj2{}': [{ x: 1 }], }; ``` The Axios serializer appends these fields: ```js const formData = new FormData(); formData.append('x', '1'); formData.append('arr[]', '1'); formData.append('arr[]', '2'); formData.append('arr[]', '3'); formData.append('arr2[0]', '1'); formData.append('arr2[1][0]', '2'); formData.append('arr2[2]', '3'); formData.append('users[0][name]', 'Peter'); formData.append('users[0][surname]', 'Griffin'); formData.append('users[1][name]', 'Thomas'); formData.append('users[1][surname]', 'Anderson'); formData.append('obj2{}', '[{"x":1}]'); ``` ### Converting FormData to JSON `axios.formToJSON()` converts `FormData` field names that use dot or bracket notation into nested objects and arrays. Only `.`, `[`, and `]` are structural separators. Other characters, including `-`, spaces, `+`, `*`, and `&`, remain part of the literal key. ```js const form = new FormData(); form.append('user-name', 'johndoe'); form.append('user.name', 'john'); console.log(axios.formToJSON(form)); // { // 'user-name': 'johndoe', // user: { name: 'john' } // } ``` `user[name]` also creates `{ user: { name: ... } }`, while `items[]` creates an array. Axios supports `postForm`, `putForm`, and `patchForm` as shortcuts for the matching HTTP methods with the `Content-Type` header preset to `multipart/form-data`. ## Posting files Submit a single file: ```js await axios.postForm('https://httpbin.org/post', { myVar: 'foo', file: document.querySelector('#fileInput').files[0], }); ``` or multiple files as `multipart/form-data`: ```js await axios.postForm('https://httpbin.org/post', { 'files[]': document.querySelector('#fileInput').files, }); ``` `FileList` object can be passed directly: ```js await axios.postForm('https://httpbin.org/post', document.querySelector('#fileInput').files); ``` Axios sends all files with the same field name: `files[]`. ## HTML form posting (browser) Pass an HTML Form element as a payload to submit it as `multipart/form-data` content. ```js await axios.postForm('https://httpbin.org/post', document.querySelector('#htmlForm')); ``` `FormData` and `HTMLForm` objects can also be posted as `JSON` by explicitly setting the `Content-Type` header to `application/json`: ```js await axios.post('https://httpbin.org/post', document.querySelector('#htmlForm'), { headers: { 'Content-Type': 'application/json', }, }); ``` For example, the Form ```html
``` submits this JSON object: ```js { "foo": "1", "deep": { "prop": "2" }, "deep prop spaced": "3", "baz": [ "4", "5" ], "user": { "age": "value2" } } ``` Only dot and bracket notation create nested paths when a form is converted to JSON. Spaces and other punctuation remain part of literal field names. Sending `Blobs`/`Files` as JSON (`base64`) is not currently supported. ## Progress capturing Axios can capture request upload and download progress in browsers and Node.js. Progress events are limited to `3` times per second. ```js await axios.post(url, data, { onUploadProgress: function (axiosProgressEvent) { /*{ loaded: number; total?: number; progress?: number; // in range [0..1] bytes: number; // how many bytes have been transferred since the last trigger (delta) estimated?: number; // estimated time in seconds rate?: number; // upload speed in bytes upload: true; // upload sign }*/ }, onDownloadProgress: function (axiosProgressEvent) { /*{ loaded: number; total?: number; progress?: number; bytes: number; estimated?: number; rate?: number; // download speed in bytes download: true; // download sign }*/ }, }); ``` You can also track stream upload/download progress in node.js: ```js const { data } = await axios.post(SERVER_URL, readableStream, { onUploadProgress: ({ progress }) => { console.log((progress * 100).toFixed(2)); }, headers: { 'Content-Length': contentLength, }, maxRedirects: 0, // avoid buffering the entire stream }); ``` > Note: > Capturing FormData upload progress is not currently supported in node.js environments. > Warning: > Set `maxRedirects: 0` when uploading streams in node.js. > The follow-redirects package buffers the entire stream in RAM and does not follow the "backpressure" algorithm. ## Rate limiting Download and upload rate limits can only be set for the http adapter (node.js): ```js const { data } = await axios.post(LOCAL_SERVER_URL, myBuffer, { onUploadProgress: ({ progress, rate }) => { console.log(`Upload [${(progress * 100).toFixed(2)}%]: ${(rate / 1024).toFixed(2)}KB/s`); }, maxRate: [100 * 1024], // 100KB/s limit }); ``` ## AxiosHeaders Axios includes an `AxiosHeaders` class for working with headers through a Map-like API. HTTP header names are case-insensitive, but Axios keeps the original header case for style and for servers that incorrectly depend on case. Directly manipulating the headers object still works, but it is deprecated. ### Working with headers An `AxiosHeaders` instance can contain several internal value types that control setting and merging. Axios gets the final headers object with string values by calling `toJSON`. > Note: By JSON here we mean an object consisting only of string values intended to be sent over the network. The header value can be one of the following types: - `string` - normal string value sent to the server - `null` - skip header when rendering to JSON - `false` - skip header when rendering to JSON. Also indicates that the `set` method must be called with `rewrite` set to `true` to overwrite this value (Axios uses this internally to allow users to opt out of installing certain headers like `User-Agent` or `Content-Type`) - `undefined` - value is not set > Note: The header value is considered set if it is not equal to undefined. The headers object is always initialized inside interceptors and transformers: ```ts axios.interceptors.request.use((request: InternalAxiosRequestConfig) => { request.headers.set('My-header', 'value'); request.headers.set({ 'My-set-header1': 'my-set-value1', 'My-set-header2': 'my-set-value2', }); request.headers.set('User-Agent', false); // prevent Axios from setting this header later request.headers.setContentType('text/plain'); request.headers['My-set-header2'] = 'newValue'; // direct access is deprecated return request; }); ``` You can iterate over an `AxiosHeaders` instance using a `for...of` statement: ```js const headers = new AxiosHeaders({ foo: '1', bar: '2', baz: '3', }); for (const [header, value] of headers) { console.log(header, value); } // foo 1 // bar 2 // baz 3 ``` ### Preserving a specific header case Header names are case-insensitive, but `AxiosHeaders` keeps the case of the first matching key it sees. If you need a specific case for non-standard case-sensitive servers, define a case preset with `undefined` and then set the value later: ```js const api = axios.create(); api.defaults.headers.common = { 'content-type': undefined, accept: undefined, }; await api.put(url, data, { headers: { 'Content-Type': 'application/octet-stream', Accept: 'application/json', }, }); ``` You can also compose the same behavior with `AxiosHeaders.concat`: ```js const headers = axios.AxiosHeaders.concat( { 'content-type': undefined }, { 'Content-Type': 'application/octet-stream' } ); await axios.put(url, data, { headers }); ``` ### new AxiosHeaders(headers?) Constructs a new `AxiosHeaders` instance. ``` constructor(headers?: RawAxiosHeaders | AxiosHeaders | string); ``` If the headers object is a string, Axios parses it as raw HTTP headers. ```js const headers = new AxiosHeaders(` Host: www.bing.com User-Agent: curl/7.54.0 Accept: */*`); console.log(headers); // Object [AxiosHeaders] { // host: 'www.bing.com', // 'user-agent': 'curl/7.54.0', // accept: '*/*' // } ``` ### AxiosHeaders#set ```ts set(headerName, value: Axios, rewrite?: boolean); set(headerName, value, rewrite?: (this: AxiosHeaders, value: string, name: string, headers: RawAxiosHeaders) => boolean); set(headers?: RawAxiosHeaders | AxiosHeaders | string, rewrite?: boolean); set(headers?: Iterable<[string, AxiosHeaderValue]>, rewrite?: boolean); ``` The `rewrite` argument controls the overwriting behavior: - `false` - do not overwrite if the header's value is set (is not `undefined`) - `undefined` (default) - overwrite the header unless its value is set to `false` - `true` - rewrite anyway The option can also accept a user-defined function that determines whether to overwrite the value. Empty or whitespace-only header names are ignored. Iterable key/value pairs, such as a `Map`, are accepted: ```js const headers = new AxiosHeaders(); headers.set( new Map([ ['X-Trace-Id', 'abc123'], ['Accept', 'application/json'], ]) ); ``` Returns `this`. ### AxiosHeaders#get(header) ``` get(headerName: string, parser: typeof AxiosHeaders.parseParameters): AxiosHeaderParameters; get(headerName: string, parser: RegExp): RegExpExecArray | null; get(headerName: string, matcher?: true | AxiosHeaderParser): AxiosHeaderValue; ``` Returns the internal value of the header. It can take an extra argument to parse the header's value with `RegExp.exec`, matcher function or internal key-value parser. ```ts const headers = new AxiosHeaders({ 'Content-Type': 'multipart/form-data; boundary=Asrf456BGe4h', }); console.log(headers.get('Content-Type')); // multipart/form-data; boundary=Asrf456BGe4h console.log(headers.get('Content-Type', true)); // parse key-value pairs from a string separated with \s,;= delimiters: // [Object: null prototype] { // 'multipart/form-data': undefined, // boundary: 'Asrf456BGe4h' // } const quotedHeaders = new AxiosHeaders({ 'Content-Type': 'multipart/form-data; boundary="a,b"', }); console.log({ ...quotedHeaders.get('Content-Type', AxiosHeaders.parseParameters), }); // { boundary: 'a,b' } console.log( headers.get('Content-Type', (value, name, headers) => { return String(value).replace(/a/g, 'ZZZ'); }) ); // multipZZZrt/form-dZZZtZZZ; boundZZZry=Asrf456BGe4h console.log(headers.get('Content-Type', /boundary=(\w+)/)?.[0]); // boundary=Asrf456BGe4h ``` `AxiosHeaders.parseParameters` is the opt-in parser for normalized HTTP parameter values. It returns a null-prototype map, matches parameter names case-insensitively, removes quoted-string delimiters, decodes escaped quotes and backslashes, preserves commas and semicolons inside quoted values, and removes only HTTP optional whitespace around unquoted values. It omits the unsafe keys `__proto__`, `constructor`, and `prototype`. Passing `true` remains the legacy tokenizer and keeps its existing output for backward compatibility. Returns the value of the header. ### AxiosHeaders#has(header, matcher?) ``` has(header: string, matcher?: AxiosHeaderMatcher): boolean; ``` Returns `true` if the header is set (has no `undefined` value). ### AxiosHeaders#delete(header, matcher?) ``` delete(header: string | string[], matcher?: AxiosHeaderMatcher): boolean; ``` Returns `true` if at least one header has been removed. ### AxiosHeaders#clear(matcher?) ``` clear(matcher?: AxiosHeaderMatcher): boolean; ``` Removes all headers. Unlike the `delete` method matcher, this optional matcher matches the header name rather than the value. ```ts const headers = new AxiosHeaders({ foo: '1', 'x-foo': '2', 'x-bar': '3', }); console.log(headers.clear(/^x-/)); // true console.log(headers.toJSON()); // [Object: null prototype] { foo: '1' } ``` Returns `true` if at least one header has been cleared. ### AxiosHeaders#normalize(format); If the headers object was changed directly, it can have duplicates with the same name but in different cases. This method normalizes the headers object by combining duplicate keys into one. Axios uses this method internally after calling each interceptor. Set `format` to true for converting header names to lowercase and capitalizing the initial letters (`cOntEnt-type` => `Content-Type`) ```js const headers = new AxiosHeaders({ foo: '1', }); headers.Foo = '2'; headers.FOO = '3'; console.log(headers.toJSON()); // [Object: null prototype] { foo: '1', Foo: '2', FOO: '3' } console.log(headers.normalize().toJSON()); // [Object: null prototype] { foo: '3' } console.log(headers.normalize(true).toJSON()); // [Object: null prototype] { Foo: '3' } ``` Returns `this`. ### AxiosHeaders#concat(...targets) ``` concat(...targets: Array): AxiosHeaders; ``` Merges the instance with targets into a new `AxiosHeaders` instance. If the target is a string, Axios parses it as raw HTTP headers. Returns a new `AxiosHeaders` instance. ### AxiosHeaders#toJSON(asStrings?) ``` toJSON(asStrings: true): Record; toJSON(asStrings?: false): Record; ``` Resolves all internal header values into a new null prototype object. Set `asStrings` to true to resolve arrays as a string containing all elements, separated by commas. ### AxiosHeaders#toString() ``` toString(): string; ``` Returns the headers as a CRLF-free HTTP header block, one `name: value` pair per line. ### AxiosHeaders.from(thing?) ``` from(thing?: AxiosHeaders | RawAxiosHeaders | string): AxiosHeaders; ``` Returns a new `AxiosHeaders` instance created from the raw headers passed in, or returns the given headers object if it's already an `AxiosHeaders` instance. ### AxiosHeaders.concat(...targets) ``` concat(...targets: Array): AxiosHeaders; ``` Returns a new `AxiosHeaders` instance created by merging the target objects. ### Shortcuts The following shortcuts are available: - `setContentType`, `getContentType`, `hasContentType` - `setContentLength`, `getContentLength`, `hasContentLength` - `setAccept`, `getAccept`, `hasAccept` - `setUserAgent`, `getUserAgent`, `hasUserAgent` - `setContentEncoding`, `getContentEncoding`, `hasContentEncoding` ## Fetch adapter Axios introduced the fetch adapter in `v1.7.0`. By default, Axios uses it when the `xhr` and `http` adapters are not available in the build or not supported by the environment. To use it by default, select it explicitly: ```js const { data } = axios.get(url, { adapter: 'fetch', // by default ['xhr', 'http', 'fetch'] }); ``` You can create a separate instance for this: ```js const fetchAxios = axios.create({ adapter: 'fetch', }); const { data } = fetchAxios.get(url); ``` The adapter supports the same features as the `xhr` adapter, including upload and download progress capturing. It also supports response types such as `stream` and `formdata` when the environment supports them. When `auth` is omitted, the fetch adapter can read HTTP Basic auth credentials from the request URL, for example `https://user:pass@example.com`. Percent-encoded URL credentials are decoded before the `Authorization` header is generated, and `auth` takes precedence over URL-embedded credentials. ### Custom fetch Since `v1.12.0`, you can configure the fetch adapter to use a custom fetch API instead of environment globals. Pass a custom `fetch` function, `Request`, and `Response` constructors through `env` config. This helps in custom environments and app frameworks. When using a custom fetch, you may also need to set custom `Request` and `Response` constructors. If you do not set them, Axios uses the global objects. If your custom fetch API does not provide these objects and the globals are incompatible with it, pass `null` to disable them inside the fetch adapter. > Note: Setting `Request` and `Response` to `null` prevents the fetch adapter from capturing upload and download progress. Basic example: ```js import customFetchFunction from 'customFetchModule'; const instance = axios.create({ adapter: 'fetch', onDownloadProgress(e) { console.log('downloadProgress', e); }, env: { fetch: customFetchFunction, Request: null, // undefined -> use the global constructor Response: null, }, }); ``` #### Using with Tauri A minimal example of setting up Axios for use in a [Tauri](https://tauri.app/plugin/http-client/) app with a platform fetch function that ignores CORS policy for requests. ```js import { fetch } from '@tauri-apps/plugin-http'; import axios from 'axios'; const instance = axios.create({ adapter: 'fetch', onDownloadProgress(e) { console.log('downloadProgress', e); }, env: { fetch, }, }); const { data } = await instance.get('https://google.com'); ``` #### Using with SvelteKit [SvelteKit](https://svelte.dev/docs/kit/web-standards#Fetch-APIs) uses a custom fetch function for server rendering in `load` functions. It also uses relative paths, which are incompatible with the standard URL API. Configure Axios to use SvelteKit's custom fetch API: ```js export async function load({ fetch }) { const { data: post } = await axios.get('https://jsonplaceholder.typicode.com/posts/1', { adapter: 'fetch', env: { fetch, Request: null, Response: null, }, }); return { post }; } ``` #### HTTP/2 support Axios supports HTTP/2 through the Node.js `http` adapter, introduced in v1.13.0. Support depends on the runtime environment. Axios relies on Node.js APIs, so HTTP/2 works in supported Node.js versions but may not work in other environments such as Bun or Deno. Options like `httpVersion` and `http2Options` are adapter-specific and may not behave the same way in every environment. Note: HTTP/2 redirects are currently not supported by the HTTP/2 adapter. ```js const form = new FormData(); form.append('foo', '123'); const { data, headers, status } = await axios.post('https://httpbin.org/post', form, { onUploadProgress(e) { console.log('upload progress', e); }, onDownloadProgress(e) { console.log('download progress', e); }, responseType: 'arraybuffer', }); ``` ## Semver Axios follows [semver](https://semver.org/) since `v1.0.0`. ## Promises axios depends on a native ES6 Promise implementation to be [supported](https://caniuse.com/promises). If your environment doesn't support ES6 Promises, you can [polyfill](https://github.com/jakearchibald/es6-promise). ## TypeScript axios includes [TypeScript](https://typescriptlang.org) definitions and a type guard for axios errors. ```typescript let user: User = null; try { const { data } = await axios.get('/user?ID=12345'); user = data.userDetails; } catch (error) { if (axios.isAxiosError(error)) { handleAxiosError(error); } else { handleUnexpectedError(error); } } ``` Use `axios.isCancel()` to narrow cancellation errors to `CanceledError`: ```typescript const controller = new AbortController(); try { await axios.get('/user?ID=12345', { signal: controller.signal }); } catch (error) { if (axios.isCancel(error)) { handleCancellation(error); } } ``` ### Typing request data and query params `AxiosRequestConfig` uses `D` for the request body and `P` for query parameters. The params serializer receives the same `P`, and both types are preserved on the default response config: ```typescript import axios, { type AxiosPromise, type AxiosRequestConfig, type InternalAxiosRequestConfig, } from 'axios'; interface RequestBody { includeArchived: boolean; } interface SearchParams { query: string; page?: number; } interface SearchResponse { results: string[]; } const config: AxiosRequestConfig = { data: { includeArchived: false }, params: { query: 'axios', page: 1 }, paramsSerializer: (params) => `${params.query}:${params.page ?? 1}`, }; const response = await axios.get('/search', config); response.config.data; // RequestBody | undefined response.config.params; // SearchParams | undefined const invalidConfig: AxiosRequestConfig = { // @ts-expect-error `query` must be a string params: { query: 123 }, }; const adapter = ( adapterConfig: InternalAxiosRequestConfig ): AxiosPromise => Promise.resolve({ data: { results: [] }, status: 200, statusText: 'OK', headers: {}, config: adapterConfig, }); ``` The request methods place `P` last—``—so the existing response data (`T`), custom response (`R`), and request data (`D`) positions do not change. Explicit custom response types still determine the resolved value. `RawAxiosRequestConfig`, `InternalAxiosRequestConfig`, defaults, `AxiosResponse`, `AxiosPromise`, `AxiosError`, `CanceledError`, callable instances, adapters, and `mergeConfig()` also carry `P`. The default is `any` for backward compatibility. Use all three public error generics when cancellation narrowing must retain the response, request body, and params types: ```typescript declare const error: unknown; if (axios.isCancel(error)) { error.config?.data; // RequestBody | undefined error.config?.params; // SearchParams | undefined } ``` ### Symbol-keyed custom request config Own enumerable symbol properties survive config merging, so module-augmented options can reach request interceptors and adapters: ```typescript import axios from 'axios'; export const someFlag: unique symbol = Symbol( 'some flag used in request interceptor' ); declare module 'axios' { interface AxiosRequestConfig { [someFlag]?: boolean; } } axios.interceptors.request.use((config) => { if (config[someFlag]) { config.headers.set('X-Some-Flag', 'enabled'); } return config; }); await axios.get('/users', { [someFlag]: true }); ``` Only own enumerable symbol properties are copied; inherited and non-enumerable symbol properties are not. Because axios publishes an ESM default export and a CJS `module.exports`, TypeScript has a few caveats. The recommended setting is `"moduleResolution": "node16"`, which is implied by `"module": "node16"`. This requires TypeScript 4.7 or greater. If you use ESM, your settings should be fine. If you compile TypeScript to CJS and can't use `"moduleResolution": "node 16"`, enable `esModuleInterop`. If you use TypeScript to type check CJS JavaScript code, your only option is to use `"moduleResolution": "node16"`. You can also create a custom instance with typed interceptors: ```typescript import axios, { AxiosInstance, InternalAxiosRequestConfig } from 'axios'; const apiClient: AxiosInstance = axios.create({ baseURL: 'https://api.example.com', timeout: 10000, }); apiClient.interceptors.request.use((config: InternalAxiosRequestConfig) => { // Add auth token return config; }); ``` ## Online one-click setup You can use Gitpod, a free online IDE for open source projects, to contribute or run the examples online. [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/axios/axios/blob/main/examples/server.js) ## Contributing ### Local setup As a supply-chain hardening measure, this repository ships a project-level `.npmrc` that sets `ignore-scripts=true`. This blocks npm lifecycle scripts (`preinstall`, `install`, `postinstall`, `prepare`) from any direct or transitive dependency when you run `npm install` or `npm ci` inside the repo. See [THREATMODEL.md](./THREATMODEL.md) (threat T-S2) for the rationale. One consequence: the repository's own `prepare` hook (which installs Husky's git hooks) will **not** run automatically. After your first install, enable the git hooks manually: ```bash npm ci npm rebuild husky && npx husky ``` Run those two commands once per fresh checkout. You do **not** need to re-run them after every subsequent `npm install`. Do not remove `ignore-scripts=true` from `.npmrc` to "fix" this. That reopens the lifecycle-script attack surface for every other package in the tree. All CI workflows already invoke npm with `--ignore-scripts`, so local behaviour matches CI. ## Resources - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Ecosystem](https://github.com/axios/axios/blob/v1.x/ECOSYSTEM.md) - [Contributing Guide](https://github.com/axios/axios/blob/v1.x/CONTRIBUTING.md) - [Code of Conduct](https://github.com/axios/axios/blob/v1.x/CODE_OF_CONDUCT.md) ## Credits axios is heavily inspired by the [$http service](https://docs.angularjs.org/api/ng/service/$http) in [AngularJS](https://angularjs.org/). It provides a standalone `$http`-like service for use outside AngularJS. ## License [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) axios-axios-aa54a5e/SECURITY.md000066400000000000000000000120501526660636600162540ustar00rootroot00000000000000# Security policy ## Supported versions The maintainers provide security updates for these versions: | Version | Supported | | ------- | ------------------ | | 0.x.x | :white_check_mark: | | 1.x.x | :white_check_mark: | ## Threat model For details about the runtime attack surface, supply chain security, and development environment security, see [THREATMODEL.md](THREATMODEL.md). Researchers should read it before reporting. It documents what is in scope, what is an explicit non-goal, and where known gaps remain. The maintainer incident-response runbook, including session revocation, key rotation, and notification steps, is in [THREATMODEL.md §3.7](THREATMODEL.md#37-incident-response-runbook). ## Verifying a release `axios` tarballs on npm are published from GitHub Actions with an [npm provenance attestation](https://docs.npmjs.com/generating-provenance-statements) that cryptographically binds the package to the workflow and commit SHA that produced it. Attestation became the default at **v1.6.1** (1.x line) and **v0.31.0** (0.x line); releases before those points generally have none. A few releases are exceptions to that rule: - **v0.28.0** and **v0.28.1** carry an attestation even though they predate the 0.x default. - **v1.13.3** (1.x line) and **v0.29.0** through **v0.30.3** (0.x line) were published *without* one. Consumers can verify provenance locally: ```bash # Verify every package in your lockfile, including axios npm audit signatures ``` A successful verification proves the tarball was built in the `axios/axios` GitHub Actions environment on a known commit. It was not tampered with between build and registry. It does not prove the code in that commit is free of bugs. If `npm audit signatures` reports a missing or invalid attestation for an `axios` version listed above as attested, treat it as a potential supply-chain incident and report via the private channel below. ## Reporting a vulnerability If you believe you have found a security vulnerability in axios, report it through the private channel below. If the vulnerability is in a third-party library, report it to that library's maintainers. ## Reporting process Do not report security vulnerabilities through public GitHub issues. Use GitHub's private security channel by opening a [security advisory](https://github.com/axios/axios/security). ## Disclosure policy When we receive a security vulnerability report, we assign it a primary handler. The handler confirms the problem, determines affected versions, evaluates severity, develops and ships a fix, and coordinates public disclosure with the reporter. ### 60-day resolution and disclosure commitment We commit to resolving and publicly disclosing every valid security advisory within 60 calendar days of the initial report, measured from the moment a report is received through the [GitHub security advisory channel](https://github.com/axios/axios/security/advisories/new). The 60-day clock is a commitment to reporters and downstream consumers. It is a backstop, not an aspiration. If we cannot ship a fix in time, we still publish the advisory at day 60 with mitigation guidance so consumers can act. We then keep working on the fix and update the advisory with patch details when they are ready. We release the fix separately from the advisory, but we do not delay the advisory beyond day 60. We try to release the fix before publishing the advisory so users can patch before vulnerability details are public. Exceptions and extensions: - If a reporter requests a shorter embargo (e.g. they plan to present findings at a conference), we accommodate where possible. - If a fix requires a breaking change, coordinating with major downstream consumers, or a `follow-redirects` / `form-data` / `proxy-from-env` upstream release, we may extend beyond 60 days. Any extension is disclosed publicly at day 60 via the advisory, with a revised ETA and the reason. - If a report turns out to be out of scope (e.g. falls under an explicit non-goal in [THREATMODEL.md §2.6](THREATMODEL.md)), we close it with an explanation to the reporter within the triage window (≤ 3 days). Out-of-scope reports do not enter the 60-day queue. - Actively exploited vulnerabilities are treated as incidents. The fix and advisory ship as soon as a patch is validated, not on the 60-day schedule. Reporter expectations: While a report is under embargo, we ask reporters not to disclose it publicly until the earlier of the coordinated advisory publication or day 60. If the 60-day deadline passes without action from us, reporters are free to disclose independently. We treat that as a failure on our part, not on theirs. ## Security updates We release security updates after the patch is developed and tested. We notify users through the project's GitHub repository, publish release notes and security advisories on GitHub releases, and deprecate all versions that contain the vulnerability. ## Security partners and acknowledgements Thanks to these security researchers for working with us: - [Socket Dev](https://socket.dev/) - [GitHub Security Lab](https://securitylab.github.com/) axios-axios-aa54a5e/THREATMODEL.md000066400000000000000000002706231526660636600165710ustar00rootroot00000000000000# Axios threat model This document describes the threat model for axios: a library used at runtime by millions of applications, and an open-source project with a build pipeline, release infrastructure, and human maintainers. It is for maintainers, security researchers, and downstream consumers doing supply chain due diligence. If you find a gap, open a security advisory rather than a public issue. --- ## 1. Scope and methodology We model two distinct systems: | System | What is being protected | Who attacks it | | ------------------ | ------------------------------------------- | ----------------------------------------------------------------- | | Runtime | Applications that `import axios` | Malicious servers, network attackers, malicious application input | | Project / SDLC | The integrity of what gets published to npm | Supply-chain attackers, phishers, malicious contributors | For each system, we list assets, trust boundaries, threat actors, and threats, rated by likelihood x impact. When mitigations exist in the codebase, we cite the file. When they do not, we say so. The runtime model is general by design. axios is a transport library and cannot know what its callers consider sensitive. The project model is specific and actionable. --- ## 2. Runtime threat model ### 2.1 System overview ``` ┌─────────────────� │ Application │ � trusted: writes the config, owns the secrets │ (caller code) │ └────────┬────────┘ │ axios(config) ┌────────▼────────� │ Interceptors │ � caller-supplied code, runs in-process ├─────────────────┤ │ Config merge │ � lib/core/mergeConfig.js │ URL build │ � lib/core/buildFullPath.js, lib/helpers/buildURL.js │ Header build │ � lib/core/AxiosHeaders.js ├─────────────────┤ │ Adapter │ � http.js / xhr.js / fetch.js └────────┬────────┘ │ ���������▼��������� � TRUST BOUNDARY (network) │ ┌────────▼────────� │ Proxy (opt.) │ � partially trusted (sees plaintext if HTTP) └────────┬────────┘ ┌────────▼────────� │ Origin server │ � UNTRUSTED in the general case │ + redirects │ └─────────────────┘ ``` ### 2.2 Assets | Asset | Why it matters | | ------------------------------ | ------------------------------------------------------------ | | Credentials in transit | `config.auth`, `Authorization` headers, cookies, XSRF tokens | | Request/response bodies | May contain PII, business secrets | | The caller's process integrity | Prototype pollution can lead to RCE in some downstream gadgets | | The caller's internal network | SSRF can pivot through the host running axios | | Availability | Decompression bombs, redirect loops, slow-loris responses | ### 2.3 Trust boundaries 1. Caller to axios. The caller is fully trusted. Anything the caller passes in `config` is assumed intentional. axios does not defend against a malicious caller; that is a non-goal. 2. axios to network. Everything past the socket is untrusted: response status, headers, body, redirect `Location`, proxy responses. 3. axios to environment variables. `HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY` are read by `proxy-from-env`. An attacker who controls the environment can redirect all traffic. This is treated as trusted because it has the same privilege as the process, but it is a relevant pivot in container-escape and CI scenarios. 4. Caller-supplied hooks to axios internals. Interceptors, `transformRequest`, `transformResponse`, `paramsSerializer`, `beforeRedirect`, and custom adapters run with full process privilege. axios does not sandbox them. ### 2.4 Threat actors | Actor | Capability | | ----------------------------- | -------------------------------------------------------------------------------------------------------------------- | | Malicious server | Controls every byte of the response. Most common. | | On-path network attacker | MITM. Mitigated by TLS unless the caller disabled validation. | | Malicious redirect target | A trusted server redirects to an attacker. The attacker sees whatever axios forwards. | | Application user | Controls part of the request (e.g. a URL path segment, a query param, a header value) via the calling application. | ### 2.5 Threats > Severity = Likelihood x Impact, rated for a typical server-side deployment. Browser deployments inherit the browser's same-origin policy and are generally lower risk for SSRF and credential leakage. --- #### T-R1: SSRF via caller-controlled URL | | | | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Description** | Application interpolates user input into `config.url` or `config.baseURL`. Attacker supplies `http://169.254.169.254/`, `http://localhost:6379/`, `file://`, `gopher://`, etc. | | **Likelihood** | **High.** This is the #1 real-world axios misuse pattern. | | **Impact** | **High.** Cloud metadata theft, internal service access. | | **In scope?** | **Partially.** axios cannot know which URLs the caller intends to allow. | | **Mitigations** | • `allowAbsoluteUrls: false` prevents a relative `url` from overriding `baseURL` (`lib/core/buildFullPath.js`). Defaults to `true` for back-compat.
• The HTTP adapter only speaks `http:`/`https:`/`file:`/`data:` (Node) or `http:`/`https:`/`file:`/`blob:`/`url:`/`data:` (browser); exotic schemes like `gopher:` are rejected (`lib/platform/node/index.js`, `lib/platform/browser/index.js`).
• No built-in host allowlist. Callers must validate destinations themselves. | | **Residual risk** | Substantial. This is documented as caller responsibility. | --- #### T-R2: Credential leakage on cross-origin redirect | | | | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Description** | Caller sets `Authorization: Bearer …` and requests `https://api.trusted.com/x`. Server responds `302 Location: https://evil.com/`. Does the bearer token go to evil.com? | | **Likelihood** | Medium | | **Impact** | High (full credential theft) | | **Mitigations** | • Node adapter delegates to `follow-redirects@^1.16.0`, which strips `Authorization`, `Cookie`, and `Proxy-Authorization` on cross-host redirects and on HTTPS→HTTP downgrades.
• `sensitiveHeaders` lets callers list custom secret-bearing headers (for example `X-API-Key`) that axios strips on cross-origin redirects.
• `maxRedirects` defaults to 5; set to `0` to handle redirects manually.
• `beforeRedirect` callback allows custom inspection.
• Browser adapters (XHR/fetch) delegate to the browser, which applies its own cross-origin credential rules. | | **Residual risk** | Low for standard credential headers and configured custom secret headers. We inherit `follow-redirects`' security posture - it is a critical transitive dependency and its CVEs are our CVEs. Callers must list any custom secret headers they want stripped. | --- #### T-R3: Header injection (CRLF) | | | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Description** | Application puts user input into a header value: `headers: { 'X-User': req.query.name }`. Attacker supplies `foo\r\nX-Injected: bar\r\n\r\n`. A related surface is multipart per-part headers: attacker-controlled `blob.type` or `blob.name` flowing into the multipart body. | | **Likelihood** | Low | | **Impact** | Medium to High (request smuggling, response splitting, multipart parser confusion) | | **Mitigations** | • `lib/core/AxiosHeaders.js` rejects header values containing `\r` or `\n`, and validates header names against an RFC-7230-shaped charset. Node's own `http` module also rejects these.
• `lib/helpers/formDataToStream.js` strips CRLF from `value.type` and percent-encodes CRLF/`"` in `value.name` via `escapeName()` before interpolating them into per-part headers (GHSA-445q-vr5w-6q77). Node's `http` module does not defend here; multipart injection is in body bytes, not request headers. | | **Residual risk** | Very low for HTTP headers (defense in depth: axios + Node). Low for multipart body headers (single layer of defense; regressions here would be silent). | --- #### T-R4: Prototype pollution, write side (polluting response / merge into a target object) | | | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Description** | Server returns `{"__proto__": {"isAdmin": true}}`. If axios merged this into an object naively, every `{}` in the process would gain `.isAdmin`. | | **Likelihood** | Low (requires a downstream gadget to be exploitable) | | **Impact** | High (process-wide state corruption, sometimes RCE) | | **Mitigations** | • `JSON.parse` itself does not pollute (it creates own-properties named `__proto__`, not prototype links).
• Internal merge paths filter dangerous keys: `lib/utils.js` and `lib/core/mergeConfig.js` filter `__proto__` / `constructor` / `prototype`; `lib/helpers/formDataToJSON.js` filters `__proto__`.
• These were added in response to past advisories. A regression here is a P0. | | **Residual risk** | Low, but this is an area of active attacker interest. New merge helpers must go through the same filtering. | --- #### T-R4b: Prototype pollution, read-side gadgets (polluted `Object.prototype` drives axios behavior) | | | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Description** | A _different_ library in the caller's dependency tree pollutes `Object.prototype` (e.g. `Object.prototype.validateStatus = () => true`). axios code that reads a config property through the prototype chain then picks up the attacker's value and executes the associated behavior. Each reachable property is a distinct **gadget**: `validateStatus` (bypass HTTP error handling), `parseReviver` (silently tamper JSON response bodies), `transport` / `httpAgent` / `lookup` (MITM / intercept), `withXSRFToken` (leak XSRF token cross-origin), `transformResponse` (response replacement), and so on. | | **Likelihood** | Low to Medium (requires a polluted prototype somewhere in the process, historically common). | | **Impact** | High. Arbitrary behavior change across every axios call (auth bypass, response tampering, credential leakage). Unlike T-R4, this does not require axios itself to pollute; any polluted process is enough. | | **Mitigations** | Config reads that can drive behavior are routed through `hasOwnProp` guards so polluted prototype properties are not seen:
• `lib/core/mergeConfig.js`: per-prop reads from `config1`/`config2` guarded with `hasOwnProp`; `mergeDirectKeys` (used by `validateStatus`) uses `hasOwnProp` rather than the `in` operator which traverses the prototype chain (fix for GHSA-w9j2-pvgh-6h63).
• `lib/defaults/index.js`: `transformResponse` / `transformRequest` read `transitional`, `responseType`, `parseReviver`, `response` via an `own()` wrapper (fix for GHSA-3w6x-2g7m-8v23).
• `lib/adapters/http.js`: `transport`, `httpAgent`, `httpsAgent`, `lookup`, `family`, `http2Options`, etc. read via `hasOwnProp` (fix for GHSA-pf86-5x62-jrwf gadget set).
• `lib/helpers/resolveConfig.js`: `withXSRFToken` requires strict `=== true` to send the header cross-origin; non-boolean truthy values (`1`, `"false"`, `{}`) no longer short-circuit the same-origin check (fix for GHSA-xx6v-rp6x-q39c).
• Regression tests for the gadget class live in `tests/unit/prototypePollution.test.js` (both unit-level and end-to-end against `axios.get`). | | **Residual risk** | Low, but the surface is every config property read. Any new code path that reads `config.foo` / `this.foo` / destructures from a merged config must use a `hasOwnProp` guard. The non-goal that axios does not defend a caller with a polluted prototype is narrower than it sounds. The pollution typically comes from a transitive dependency, not from the caller's own intent, and the above mitigations neutralize the reachable gadgets even when the prototype is polluted. | --- #### T-R5: Decompression bomb | | | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Description** | Server sends `Content-Encoding: gzip` with a 10 KB body that decompresses to 10 GB. | | **Likelihood** | Low | | **Impact** | Medium (DoS, OOM kill of the calling process) | | **Mitigations** | • `maxContentLength` bounds the decompressed response size in the Node adapter (`lib/adapters/http.js`), enforced chunk-by-chunk on the decompressed stream for both buffered and `responseType: 'stream'` responses (stream path fixed in GHSA-vf2m-468p-8v99).
• `maxBodyLength` bounds the request side, including when `maxRedirects === 0` (previously bypassed).
• Both default to `-1` (unlimited). Callers handling untrusted servers should set these. The README carries a top-level "security notice" call-out and `docs/pages/misc/security.md` documents the exact mitigation snippet in all four locales.
• Decompression uses Node's `zlib`, which streams. Memory is bounded by the limit, not the full expansion. | | **Residual risk** | Medium when limits are not configured. The defaults favor compatibility over safety; the reasoning is that tightening the default would silently break every legitimate download larger than whatever cap were chosen. | --- #### T-R6: TLS validation bypass | | | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------- | | **Description** | Caller passes `httpsAgent: new https.Agent({ rejectUnauthorized: false })` to "fix" a certificate error in dev, ships it to prod. | | **Likelihood** | Medium (very common copy-paste anti-pattern) | | **Impact** | High (silent MITM) | | **In scope?** | **No.** axios delegates TLS entirely to Node's `https` module / the browser. We do not inspect or warn on agent configuration. | | **Mitigations** | None at the axios layer. Documentation responsibility only. | | **Residual risk** | High, but explicitly out of scope. This is caller misconfiguration, not an axios vulnerability. | --- #### T-R7: XSRF token sent cross-origin | | | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Description** | Browser deployment. `xsrfCookieName` is set; attacker tricks the app into requesting `https://evil.com` and the XSRF token cookie value is attached as a header. | | **Likelihood** | Low | | **Impact** | Medium | | **Mitigations** | `lib/helpers/resolveConfig.js` only attaches the XSRF header when `isURLSameOrigin()` passes (or when `withXSRFToken` is explicitly forced). This was the fix for **CVE-2023-45857**. | | **Residual risk** | Low. The same-origin check uses the WHATWG `URL` parser (`lib/helpers/isURLSameOrigin.js`), which is robust against parser-differential attacks. | --- #### T-R8: Sensitive data in error objects | | | | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Description** | Request fails. `AxiosError` includes `config`, which includes `config.auth`, `config.headers.Authorization`, `config.httpsAgent` (with embedded client cert/key). Caller logs the error, exposing secrets in logs. | | **Likelihood** | **High** | | **Impact** | Medium to High | | **Mitigations** | `AxiosError.toJSON()` (`lib/core/AxiosError.js`) produces a reduced view, but the live error object still carries the full config by reference. | | **Residual risk** | Medium. Callers using structured loggers that walk object graphs (Winston, Pino with serializers, Sentry) will capture credentials unless they configure redaction. This is a documented risk, not a vulnerability, but it is the most common way axios users leak secrets in practice. | --- #### T-R9: Proxy environment variable hijack | | | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Description** | Attacker controls the process environment (compromised CI step, container escape, `.env` injection) and sets `HTTPS_PROXY=http://evil.com:8080`. All axios traffic is now MITM'd. | | **Likelihood** | Low (requires prior foothold) | | **Impact** | High | | **Mitigations** | • `config.proxy: false` disables environment-based proxy detection entirely.
• `NO_PROXY` is honored (`lib/helpers/shouldBypassProxy.js`), with recent hardening for CIDR ranges, IPv6 literals, and wildcard patterns to close parser-differential edge cases.
• HTTPS through any proxy uses CONNECT tunneling via `https-proxy-agent` so the origin's cert is validated end-to-end and the proxy sees only SNI, never the URL, headers, or body. `Proxy-Authorization` is sent on the CONNECT request only, never on the wrapped TLS-protected request. | | **Residual risk** | Low for HTTPS. High for plain HTTP: the proxy sees and can modify everything. | --- #### T-R10: Malicious interceptor / adapter | | | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Description** | Caller installs a third-party "axios plugin" from npm that registers an interceptor exfiltrating every `Authorization` header. | | **Likelihood** | Low to Medium | | **Impact** | High | | **In scope?** | **No.** Interceptors are caller-supplied code running in the caller's process. axios provides the hook; vetting what goes into it is the caller's job. | | **Residual risk** | Out of scope, but worth documenting: there is no meaningful difference between `axios.interceptors.request.use(evil)` and `require('evil')`. | --- #### T-R11: Form-data recursion DoS (deeply nested input) | | | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Description** | Caller passes untrusted object input as request `data` in a context that serializes to `multipart/form-data` or `application/x-www-form-urlencoded`. A pathological input with thousands of nesting levels causes `lib/helpers/toFormData.js` to recurse until stack overflow or the process is killed. | | **Likelihood** | Low (requires the caller to serialize attacker-controlled object input without validation) | | **Impact** | Medium (DoS, stack overflow / process termination) | | **Mitigations** | • `formSerializer.maxDepth` caps recursion depth; default is 100, can be set to `Infinity` to disable.
• Exceeding the cap throws `AxiosError` with code `ERR_FORM_DATA_DEPTH_EXCEEDED` rather than crashing the process.
• Documented per locale in `docs/pages/advanced/multipart-form-data-format.md` and `docs/pages/advanced/x-www-form-urlencoded-format.md`. | | **Residual risk** | Low when callers leave the default in place. Setting `maxDepth: Infinity` reintroduces the risk. | --- ### 2.6 Explicit non-goals (runtime) axios will not: - Sandbox or validate caller-supplied functions (interceptors, transforms, adapters, serializers). - Validate that `config.url` points somewhere "safe." We don't know what safe means for your application. - Warn when TLS validation is disabled via a custom agent. - Redact `config` from thrown errors. The caller may legitimately need it for retry logic. - Defend against a fully compromised caller process (e.g. attacker-controlled code running inside the caller). For the narrower case of a polluted `Object.prototype` arriving via a transitive dependency, axios does defend the reachable config-read gadgets (see T-R4b), but any new config-read path must continue to use `hasOwnProp` guards to stay on this side of the line. - Defend against monkey-patched JavaScript or Node.js runtime APIs (`Object.keys`, `http.request`, `ClientRequest.prototype.setHeader`, `fetch`, etc.). If attacker-controlled code is already running in the same process, it can observe or alter requests below axios and this is outside axios' security boundary. --- ## 3. Project / supply chain threat model This model protects what gets published as `axios` on npm. A successful attack here compromises every downstream consumer at once. Given axios' install base, this is the higher-risk half of the document. ### 3.1 System overview ``` ┌──────────────────� ┌──────────────────� ┌──────────────────� │ Maintainer's │ │ Contributor's │ │ GitHub.com │ │ workstation │ │ fork + PR │ │ (source of │ │ │ │ │ │ truth) │ │ ! npm token? │ │ untrusted code │ │ │ │ ! SSH keys │ │ │ │ │ │ ! GPG keys │ │ │ │ │ └────────┬─────────┘ └────────┬─────────┘ └────────▲─────────┘ │ │ │ │ git push │ PR │ └───────────────────────┴───────────────────────┘ │ tag push: v1.x.y │ ┌──────────────▼─────────────� │ GitHub Actions │ │ .github/workflows/ │ │ publish.yml │ │ │ │ • npm ci --ignore-scripts │ │ • npm run build │ │ • npm publish │ │ --provenance │ │ │ │ OIDC to npm (no token) │ └──────────────┬─────────────┘ │ ���������������▼��������������� registry.npmjs.org axios@1.x.y + provenance attestation ``` ### 3.2 Assets | Asset | Compromise means… | | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | **The npm `axios` package name** | Attacker can publish malware as `axios@1.x.y+1`. Game over for the ecosystem. | | **npm publish capability** | Whether via token, OIDC trust, or account takeover. | | **GitHub `axios/axios` write access** | Attacker can push a tag, which triggers publish. Or modify `publish.yml` itself. | | **Maintainer GitHub accounts** | Transitively grants the above. | | **Maintainer workstation secrets** | SSH keys (GitHub push), `~/.npmrc` token if present (direct publish), GPG keys (signed commits), cloud creds (lateral movement). | | **Build determinism** | If `dist/` doesn't match `lib/`, a backdoor can hide in the minified bundle. | | **Runtime dependency integrity** | `follow-redirects`, `form-data`, `proxy-from-env`, `https-proxy-agent` ship inside every axios install. | ### 3.3 Trust boundaries 1. Contributor PRs to main branch. PRs from forks are untrusted. CI runs them, but `pull_request` workflows have no access to secrets and use a read-only `GITHUB_TOKEN`. 2. Main branch to release tag. Pushing to `v1.x` does not publish. Only pushing a `v1.*.*` tag does. Tag push requires write access. 3. GitHub Actions to npm. This boundary is crossed via OIDC (`id-token: write` to npm trusted publisher). The repo has no long-lived `NPM_TOKEN` secret. 4. Maintainer workstation to everything else. This is the softest boundary. A maintainer's laptop is a high-value, low-assurance environment. See §3.5. ### 3.4 Threat actors | Actor | Capability | Motivation | | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | --------------------------------------- | | Drive-by contributor | Open a PR. No secrets, no write. | Sneak a backdoor past review. | | Compromised dependency | Attempt to run code on `npm install` via lifecycle scripts. Blocked on maintainer workstations (project `.npmrc`) and in CI (`--ignore-scripts` on every job). Residual execution path: plugin code under `npm run build` / `test` / `lint`. | Steal tokens, inject into build. | | Phisher | Send convincing emails/DMs. No technical access. | Maintainer GitHub/npm credential theft. | | Compromised maintainer account | Full write. Can push tags. Can edit workflows. | Direct publish of malware. | | GitHub / npm insider or platform compromise | Out of scope. We trust the platforms. | - | ### 3.5 Threats --- #### T-S1: Malicious code in a contributor PR | | | | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Description** | Attacker opens a PR with a subtle backdoor: an obfuscated payload in a test fixture, a Unicode homoglyph in a comparison, or a malicious `rollup` plugin in the config. | | **Likelihood** | **High** (attempts are constant on high-profile repos) | | **Impact** | Critical, _if_ it lands | | **Mitigations** | • Mandatory review before merge.
• `pull_request` workflows run with no secrets and a read-only token, so a malicious test cannot exfiltrate anything from CI.
• `pull_request_target` is not used because it would grant secrets to fork code.
• `zizmor` lints workflow files for known-dangerous patterns.
• Branch protection on `v1.x`.
• Package, lockfile, and GitHub Actions update PRs are maintainer/bot-only; outside-collaborator PRs for those updates are closed.
• Path-scoped `.github/CODEOWNERS` flags sensitive paths explicitly: runtime source (`/lib/`, `/index.*`), build/release infrastructure (`rollup.config.js`, `package.json`, `package-lock.json`, `.npmrc`), CI automation (`.github/workflows/`, `.github/dependabot.yml`, `CODEOWNERS` itself), and security-critical docs (`THREATMODEL.md`, `SECURITY.md`). Changes to these paths surface the scoped ownership rule in the PR review UI distinct from the catch-all. The audit trail shows that the PR touched a sensitive path. | | **Gaps** | • Review is human and fallible. Obfuscated changes to `dist/` (if checked in) or to large test fixtures are hard to spot.
• No automated diffing of `lib/` to `dist/` to catch build-output tampering.
Single-maintainer constraint: with `@jasonsaayman` as sole owner on every scoped path, CODEOWNERS cannot enforce a second reviewer. Two-person review on sensitive paths remains unavailable until a co-maintainer is added. Path-scoping is pre-staged for that event. | --- #### T-S2: Compromised dev dependency steals maintainer keys > Historically the weakest link. The project-level `.npmrc` and hardware-backed maintainer keys materially improve it, but build-tool plugin execution (Rollup/Babel/Vitest/ESLint) is still the top residual investment area. `ignore-scripts` does not affect those tools, and they run whenever a maintainer builds or tests. | | | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Description** | One of the ~45 direct dev dependencies, or one of their thousands of transitive dependencies, is compromised (maintainer account takeover, expired domain re-registration, the usual). It ships a `postinstall` script that reads `~/.npmrc`, `~/.ssh/id_*`, `~/.config/gh/hosts.yml`, `~/.aws/credentials`, `~/.gnupg/` and POSTs them to an attacker.

The next time a maintainer runs `npm install` on their workstation, the script runs as the maintainer's user, with full filesystem access. No exploit needed. This is npm working as designed. | | **Likelihood** | Medium and rising. This exact pattern has hit `event-stream`, `ua-parser-js`, `coa`, `rc`, `node-ipc`, `@solana/web3.js`, the Ledger connect-kit, the 2024 polyfill.io incident, and dozens more. axios' dev tree includes Babel, Rollup, Gulp, ESLint, Vitest, and Playwright, each pulling hundreds of transitives. The attack surface is enormous and refreshes on every `npm install`. | | **Impact** | Critical. A stolen npm token with publish rights means direct malware publish. A stolen SSH key with GitHub push rights means tag push, then publish via CI. Either path ends the same way. | | **Current mitigations** | • CI is protected: `publish.yml` runs `npm ci --ignore-scripts`, so a malicious lifecycle script cannot execute during the release build.
• CI uses OIDC, not a stored token. There is no `NPM_TOKEN` secret in GitHub for a malicious workflow step to steal.
• `package-lock.json` pins versions and integrity hashes. A new malicious version won't arrive silently, only on explicit update.
• Project-local `.npmrc` sets `ignore-scripts=true`, so `npm install` / `npm ci` in a contributor or maintainer checkout does not execute lifecycle scripts (`preinstall`, `install`, `postinstall`, `prepare`) from any direct or transitive dependency.
• `husky` is the only `prepare` hook axios itself declares, and only writes `.git/hooks/`. With `ignore-scripts=true` it must be run manually (`npm rebuild husky && npx husky`), documented in the README "Contributing / Local setup" section. | | **Gaps: workstation** | `ignore-scripts=true` neutralizes the lifecycle-script path, but it does not neutralize build-time code execution. A malicious Rollup / Babel / Terser / ESLint / Vitest plugin still runs when a maintainer executes `npm run build` / `npm test` / `npm run lint`. Those are not lifecycle scripts; they are tools the maintainer explicitly invoked.

The lockfile pins which packages install, but if one of those pinned packages was already malicious when the lock was generated, or the maintainer runs `npm update` / `npm install ` without re-setting `ignore-scripts`, fresh lifecycle scripts can land.

The development environment still has full read access to every credential the maintainer's user can read once a build tool runs. Isolation (devcontainer / VM) remains the strongest control. | Mitigations adopted and recommended. Adopted items are enforced via the repo; others depend on per-maintainer discipline. 1. Don't keep a publish-capable npm token on your workstation. Publishing happens via GitHub Actions OIDC. There is no workflow that requires `npm publish` from a laptop. If `~/.npmrc` has a token, it should be read-only or scoped to unrelated packages. If there is nothing to steal, this attack path is defanged. 2. Run `npm install` / `npm ci` with `--ignore-scripts` locally. Adopted: project ships a `.npmrc` with `ignore-scripts=true`. All `npm install` / `npm ci` runs in a contributor or maintainer checkout skip lifecycle scripts by default. To set up git hooks after install, run the one trusted script manually: ``` npm rebuild husky && npx husky ``` The minor inconvenience of manually running known-good post-install steps is the price of not running thousands of unknown ones. Contributors adding a new dev dependency must not override this flag. 3. Develop in an isolated environment. A devcontainer, VM, or sandbox profile that does not have: - `~/.ssh/` mounted (use a separate deploy key or SSH agent forwarding only when pushing) - `~/.npmrc` with publish tokens - `~/.config/gh/` with a `repo`-scoped GitHub token - `~/.aws/`, `~/.config/gcloud/`, etc. The dev environment should be able to read/write the repo working tree and reach the network for tests. Nothing else. 4. Use hardware-backed keys for GitHub. Adopted project-wide. All maintainers use FIDO2/WebAuthn for GitHub auth and `sk-ssh-ed25519@openssh.com` for git push. A stolen `~/.ssh/id_ed25519_sk` is useless without the physical key. This converts "steal a file" into "steal a file and a physical object." Each maintainer should keep a backup key registered and stored separately. 5. Audit lockfile diffs on dependency-update PRs as carefully as code. A 4000-line `package-lock.json` diff hides a lot. Tooling: `npm diff`, `lockfile-lint`, Socket.dev's PR integration. Pay particular attention to new packages with install scripts (`hasInstallScript: true` in the lockfile). 6. Don't add dev dependencies casually. Each one is a recurring trust decision delegated to a stranger. Prefer tools that can run via `npx` on demand (not in `node_modules`) or that are already in the tree. --- #### T-S3: Phishing to maintainer account takeover | | | | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Description** | Maintainer receives a convincing email:
• "npm security alert: your axios package has been flagged, log in to verify ownership" links to a fake npm login; password + TOTP are captured and replayed in real time.
• "GitHub: @axios has been added to a new organization, review access" links to a fake GitHub OAuth consent screen; the attacker app gets `repo` scope.
• Social: a "recruiter" asks the maintainer to clone and `npm install` a "take-home assignment" repo.

npm and GitHub credentials for axios maintainers have been specifically targeted by these campaigns in the past. This is not theoretical. | | **Likelihood** | High. These campaigns are continuous. | | **Impact** | Critical. GitHub account to push tag to publish. npm account to publish directly. | | **Mitigations** | • npm 2FA is required for publish on the `axios` package.
• OIDC publishing means there is no maintainer npm session involved in a normal release. This narrows the attack to GitHub.
• All maintainers authenticate to GitHub with hardware-backed WebAuthn/passkeys (FIDO2 security keys / platform authenticators). Origin-bound credentials cannot be relayed by a phishing proxy (Evilginx, Modlishka). TOTP alone is not permitted for maintainer accounts.
• Git push uses `sk-ssh-ed25519@openssh.com` hardware-resident SSH keys where supported. A stolen key file is useless without the physical device. | | **Gaps** | • Enforcement is per-account policy, not verifiable from the repo itself. Onboarding/offboarding checklist should confirm hardware-key status.
• Incident-response runbook is documented in §3.7 and needs periodic rehearsal to stay useful.
• Each maintainer should register at least 2 hardware keys (primary + backup stored separately) to avoid lockout-driven fallback to weaker recovery methods. | --- #### T-S4: Compromised runtime dependency | | | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Description** | `follow-redirects`, `form-data`, `proxy-from-env`, or `https-proxy-agent` ships a malicious version. Unlike T-S2, this code ends up in the published axios bundle / runtime rather than being limited to maintainer machines. Every axios consumer runs it. | | **Likelihood** | Low (only 4 deps; all are mature, narrowly-scoped, and watched) | | **Impact** | Critical | | **Mitigations** | • Three runtime deps total, minimal by design.
• `^` ranges in `package.json` mean consumers may get newer patch versions than the lockfile pins. This is intentional, because consumers get security fixes, but it also means a malicious patch release of `follow-redirects` propagates without an axios release.
• `follow-redirects` is security-conscious and well-maintained; we track its advisories closely (multiple past axios releases were just `follow-redirects` bumps).
• Dependabot is configured (`.github/dependabot.yml`) for both npm and GitHub Actions, running weekly with grouped updates for production and development dependencies. The 7-day cooldown stays in place unless a critical vulnerability requires a maintainer-led manual update. | | **Gaps** | • No vendoring/inlining considered. The deps are small enough that vendoring is plausible, but it would forfeit upstream security fixes. Current judgment: not worth it. | --- #### T-S5: Build-output tampering (`dist/` != `lib/`) | | | | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Description** | The published tarball contains a `dist/axios.min.js` that does not match what `rollup` would produce from `lib/`. Nobody reads minified bundles. A backdoor here is invisible to source review.

Vectors: a malicious dev-dep Rollup/Babel/Terser plugin injects code at build time (T-S2 applied to CI), or a maintainer with a compromised workstation accidentally publishes a tampered local build. | | **Likelihood** | Low | | **Impact** | Critical | | **Mitigations** | • Builds run only in CI as part of `publish.yml`, from a clean `npm ci --ignore-scripts` checkout. There is no "publish from laptop" path.
• `--ignore-scripts` means a malicious dev dependency cannot tamper with `node_modules` before the build, but it can still tamper during the build if it is a Rollup/Babel plugin. Those run as part of `npm run build`, not as lifecycle scripts.
• npm provenance (`--provenance`) cryptographically attests which workflow on which commit produced the tarball. Consumers can verify with `npm audit signatures`. This proves the build ran in GitHub Actions on a known SHA. It does not prove the build is correct, only that it is traceable. | | **Gaps** | • The build is not currently reproducible in the strict sense. A third party cannot independently rebuild and get a byte-identical `dist/`. Timestamps, plugin ordering, and minifier nondeterminism would need to be locked down.
• `.github/workflows/verify-build-reproducibility.yml` performs a two-pass build-and-diff on PRs that touch build-related paths (`lib/**`, `rollup.config.js`, `package.json`, `package-lock.json`, and the workflow itself). It is currently non-blocking (`continue-on-error: true`). It surfaces divergence in the CI summary so reproducibility regressions are visible, without gating merges until the build is deterministic. Once divergence is eliminated, remove `continue-on-error` to promote this to a hard gate. | --- #### T-S6: Workflow file tampering | | | | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Description** | Attacker with write access (or a merged PR that was not reviewed carefully) modifies `.github/workflows/publish.yml` to `curl` the OIDC token somewhere, or to add a step that patches `dist/` after the build. | | **Likelihood** | Low | | **Impact** | Critical | | **Mitigations** | • All actions are pinned to full commit SHAs, not tags: `actions/checkout@de0fac...`, not `@v6`. A compromised action tag can't silently change behavior.
• `permissions:` are minimal (`contents: read`, `id-token: write`).
• `persist-credentials: false` on checkout, so the build steps cannot push back to the repo.
• `zizmor` lints workflows on every PR and push to `v1.x` (`.github/workflows/zizmor.yml`); results surface as GitHub code-scanning alerts via the `security-events: write` permission on that job. This job must remain in the required-checks set on `v1.x` branch protection for the mitigation to be binding.
• The `npm-publish` GitHub Environment can require designated reviewers before the job runs; a tampered workflow still pauses for human approval.
• CODEOWNERS carries a path-scoped rule for `/.github/workflows/` and `/.github/CODEOWNERS` itself, so workflow and ownership changes surface in the review UI as touching a scoped path rather than being folded into the default approval. | | **Gaps** | • Single-maintainer constraint (see T-S1): with one owner, the path-scoped rule cannot enforce a second reviewer on workflow changes. The rule surfaces the sensitivity but does not block single-maintainer approval. Closing this requires adding a co-maintainer. | --- #### T-S7: Tag confusion / replay | | | | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Description** | Attacker with write access force-pushes an existing tag to point at a malicious commit, or pushes `v1.99.99` so that a release is published out of band. | | **Likelihood** | Low (requires write access; assumed compromised at that point) | | **Impact** | High | | **Mitigations** | • npm rejects re-publishing an existing version. Re-tagging cannot overwrite the published `1.15.0`.
• Provenance attestation records the commit SHA the tag pointed to at publish time, which is forensically verifiable. Consumers can confirm with `npm audit signatures axios` (documented in SECURITY.md).
• Tag protection rules: repository setting must forbid tag deletion and force-push for the `v1.*.*` pattern. This is a GitHub UI setting (Settings > Tags > rulesets), not file-based; enforcement is auditable via the Rulesets REST API. | | **Gaps** | A new malicious version (`v1.x.x`) is still publishable by anyone with tag-push rights. This collapses back into T-S3 (account security). | --- #### T-S8: Typosquatting / dependency confusion | | | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Description** | Attacker publishes `axois`, `axios-http`, `@axios/core`, etc., and waits for typos. Or publishes a package shadowing an internal name used in a consumer's monorepo. | | **Likelihood** | High (these packages already exist) | | **Impact** | Medium. Affects confused consumers, not axios itself | | **In scope?** | Mostly out of scope; the axios project cannot police the npm namespace. | | **Mitigations** | • npm has typosquat detection at publish time (imperfect).
• The `@axios/` npm scope is not owned by the project. `@axios/anything` can be registered by anyone. This is a gap, not a mitigation.
• Provenance gives consumers a way to verify they got the real thing. | --- ### 3.6 Summary: project risk posture | Threat | Likelihood | Impact | Current Posture | Priority Gap | | ---------------------------- | ---------- | ------------ | --------------- | --------------------------------------------------------------------- | | T-S1 Malicious PR | High | Critical | Good | Second maintainer to enable two-person review on scoped paths | | T-S2 Dev-dep steals keys | Medium | Critical | Partial | Isolated dev environment (devcontainer/VM); no publish tokens on workstations. Lifecycle scripts now blocked via project `.npmrc`, but build-tool plugins still execute | | T-S3 Phishing | High | Critical | Good | Document phish-response runbook; require registered backup hardware key | | T-S4 Runtime dep compromise | Low | Critical | Good | - | | T-S5 Build tampering | Low | Critical | Adequate | Eliminate build non-determinism, then promote reproducibility check to blocking | | T-S6 Workflow tampering | Low | Critical | Good | Second maintainer (two-person review) for `/.github/workflows/` | | T-S7 Tag replay | Low | High | Good | - | | T-S8 Typosquat | High | Medium | Out of scope | - | The top remaining investment is T-S2 (dev-dependency compromise of maintainer workstations). Lifecycle-script execution is now blocked by the project-level `.npmrc`, and T-S3 phishing risk dropped materially once all maintainers moved to hardware-backed WebAuthn. Real-time credential relay no longer works. The residual T-S2 gap is build-tool plugin execution (Rollup/Babel/Vitest/ESLint), which `ignore-scripts` does not cover. Closing it requires running builds in an isolated environment without access to long-lived credentials. --- ### 3.7 Incident response runbook If a maintainer suspects credential compromise (phish clicked, lost hardware key, unexpected tag/publish, leaked token in logs), execute the steps below in order. Speed matters more than completeness. A published malicious version affects every downstream consumer. #### 1. Contain, minutes 0 to 15 - GitHub: revoke all active sessions (`https://github.com/settings/sessions`), revoke all OAuth/PAT tokens (`/settings/tokens`, `/settings/applications`), review authorized SSH keys and remove any unrecognised. If a PAT with `repo` or `admin:org` scope existed, assume leak. - npm: run `npm token list` and `npm token revoke ` for any publish-capable token. If no CLI access, revoke via `https://www.npmjs.com/settings//tokens`. Rotate npm password and force sign-out of all sessions. - Workstation: if a build/install ran malicious code, assume full-user compromise of the laptop. Unplug from trusted networks. Do not rely on AV; move to a clean machine for rotation steps. #### 2. Assess, minutes 15 to 60 - Check `https://github.com/axios/axios/settings/security-log` and `https://github.com//security/log` for unrecognised events (key adds, org changes, force-pushes, new tags). - Verify recent tags match intent: `git log --tags --oneline -n 20`. Compare with `https://www.npmjs.com/package/axios?activeTab=versions`. - For each recent publish, verify provenance: `npm audit signatures axios@` and cross-check the `sourceCommit` in the provenance attestation against the tag's SHA. Divergence = investigate. - Review `~/.npmrc`, `~/.ssh/`, `~/.config/gh/hosts.yml`, `~/.gnupg/` for tampering and unexpected files. #### 3. Rotate, hour 1 to 4 - Generate new SSH keys on clean hardware. Remove old keys from GitHub. If using `sk-ssh-ed25519@openssh.com`, register new hardware key first, then deregister the old one. Do not leave the account with zero registered keys. - Re-enrol WebAuthn authenticators (both primary and backup). Deregister lost/compromised authenticators. - Rotate GPG keys if signed commits are used; upload new key to GitHub. - Rotate any cloud credentials (`~/.aws/`, `~/.config/gcloud/`) and any tokens present on the compromised machine. #### 4. Notify, hour 1 onward - npm security: `security@npmjs.com`. Include package name, suspected versions, timeline. - GitHub security: `https://github.com/contact`, Security category. Request an investigation of the account. - Downstream: open a GitHub security advisory (`https://github.com/axios/axios/security/advisories/new`) as soon as a malicious version is confirmed published. Do not wait for a fix. Users need to pin away from the bad version. - Co-maintainers (when present): notify via out-of-band channel (phone/Signal), not through the compromised channel. #### 5. Unpublish / deprecate, hour 1 to 24 - npm allows `npm unpublish @` within 72 hours of publish. After that, use `npm deprecate @ ""` with a message pointing to the advisory. - Publish a patched version that bumps semver above the malicious one, so `^` consumers move forward automatically. #### 6. Post-mortem, within 1 week - Write up timeline: initial vector, dwell time, scope, mitigations applied. - Update this threat model if the incident reveals a gap not captured here. - File a PR if any mitigation can be codified (new CI check, new lint rule, new CODEOWNERS path). Keep this runbook current. A runbook no one has rehearsed is a document, not a control. --- _This document describes intent and current understanding. It does not constitute a security guarantee. To report a gap in the model itself, use the same private advisory channel as for code vulnerabilities._ axios-axios-aa54a5e/docs/000077500000000000000000000000001526660636600154155ustar00rootroot00000000000000axios-axios-aa54a5e/docs/.vitepress/000077500000000000000000000000001526660636600175175ustar00rootroot00000000000000axios-axios-aa54a5e/docs/.vitepress/config.mts000066400000000000000000000357201526660636600215200ustar00rootroot00000000000000import { defineConfig } from 'vitepress'; import { searchConfig } from './search.mts'; // https://vitepress.dev/reference/site-config // ─── English ───────────────────────────────────────────────────────────────── const enNav = [ { text: 'Guide', link: '/pages/getting-started/first-steps' }, { text: 'API', link: '/pages/advanced/api-reference' }, { text: 'Sponsors', link: '/pages/misc/sponsors' }, { text: 'v1.x', link: '#' }, ]; const enSidebar = [ { text: 'Getting Started', items: [ { text: 'First steps', link: '/pages/getting-started/first-steps' }, { text: 'Features', link: '/pages/getting-started/features' }, { text: 'Examples', items: [ { text: 'JavaScript', link: '/pages/getting-started/examples/commonjs' }, { text: 'TypeScript', link: '/pages/getting-started/examples/typescript' }, ], }, { text: 'Upgrade guide v0.x -> v1.x', link: '/pages/getting-started/upgrade-guide' }, ], }, { text: 'Advanced', items: [ { text: 'Public API', link: '/pages/advanced/api-reference' }, { text: 'Request method aliases', link: '/pages/advanced/request-method-aliases' }, { text: 'Creating an instance', link: '/pages/advanced/create-an-instance' }, { text: 'Request config', link: '/pages/advanced/request-config' }, { text: 'Adapters', link: '/pages/advanced/adapters' }, { text: 'Response schema', link: '/pages/advanced/response-schema' }, { text: 'Config defaults', link: '/pages/advanced/config-defaults' }, { text: 'Interceptors', link: '/pages/advanced/interceptors' }, { text: 'Error handling', link: '/pages/advanced/error-handling' }, { text: 'Cancellation', link: '/pages/advanced/cancellation' }, { text: 'Authentication', link: '/pages/advanced/authentication' }, { text: 'Retry & error recovery', link: '/pages/advanced/retry' }, { text: 'Testing', link: '/pages/advanced/testing' }, { text: 'x-www-form-urlencoded format', link: '/pages/advanced/x-www-form-urlencoded-format', }, { text: 'Multipart/form-data format', link: '/pages/advanced/multipart-form-data-format' }, { text: 'File posting', link: '/pages/advanced/file-posting' }, { text: 'HTML form processing 🔥', link: '/pages/advanced/html-form-processing' }, { text: 'Progress capturing 🔥', link: '/pages/advanced/progress-capturing' }, { text: 'Rate limiting 🔥', link: '/pages/advanced/rate-limiting' }, { text: 'Headers 🔥', items: [ { text: 'General usage', link: '/pages/advanced/headers' }, { text: 'Methods', link: '/pages/advanced/header-methods' }, ], }, { text: 'Fetch adapter 🔥', link: '/pages/advanced/fetch-adapter' }, { text: 'HTTP2 🔥', link: '/pages/advanced/http2' }, { text: 'Promises', link: '/pages/advanced/promises' }, { text: 'TypeScript', link: '/pages/advanced/type-script' }, ], }, { text: 'Miscellaneous', items: [ { text: 'SemVer', link: '/pages/misc/semver' }, { text: 'Security', link: '/pages/misc/security' }, ], }, ]; // ─── Mandarin Chinese ───────────────────────────────────────────────────────── const zhNav = [ { text: '指�', link: '/zh/pages/getting-started/first-steps' }, { text: 'API', link: '/zh/pages/advanced/api-reference' }, { text: '赞助商', link: '/zh/pages/misc/sponsors' }, { text: 'v1.x', link: '#' }, ]; const zhSidebar = [ { text: '入门指�', items: [ { text: '第一步', link: '/zh/pages/getting-started/first-steps' }, { text: '功能特性', link: '/zh/pages/getting-started/features' }, { text: '示例', items: [ { text: 'JavaScript', link: '/zh/pages/getting-started/examples/commonjs' }, { text: 'TypeScript', link: '/zh/pages/getting-started/examples/typescript' }, ], }, { text: '�级指� v0.x -> v1.x', link: '/zh/pages/getting-started/upgrade-guide' }, ], }, { text: '进阶', items: [ { text: '公共 API', link: '/zh/pages/advanced/api-reference' }, { text: '请求方法别�', link: '/zh/pages/advanced/request-method-aliases' }, { text: '创建实例', link: '/zh/pages/advanced/create-an-instance' }, { text: '请求�置', link: '/zh/pages/advanced/request-config' }, { text: '适�器', link: '/zh/pages/advanced/adapters' }, { text: '�应结构', link: '/zh/pages/advanced/response-schema' }, { text: '默认�置', link: '/zh/pages/advanced/config-defaults' }, { text: '拦截器', link: '/zh/pages/advanced/interceptors' }, { text: '错误处�', link: '/zh/pages/advanced/error-handling' }, { text: '�消请求', link: '/zh/pages/advanced/cancellation' }, { text: '身份验�', link: '/zh/pages/advanced/authentication' }, { text: '�试与错误��', link: '/zh/pages/advanced/retry' }, { text: '测试', link: '/zh/pages/advanced/testing' }, { text: 'x-www-form-urlencoded 格�', link: '/zh/pages/advanced/x-www-form-urlencoded-format', }, { text: 'Multipart/form-data 格�', link: '/zh/pages/advanced/multipart-form-data-format' }, { text: '文件上传', link: '/zh/pages/advanced/file-posting' }, { text: 'HTML 表�处� 🔥', link: '/zh/pages/advanced/html-form-processing' }, { text: '进度�获 🔥', link: '/zh/pages/advanced/progress-capturing' }, { text: '速率�制 🔥', link: '/zh/pages/advanced/rate-limiting' }, { text: '请求头 🔥', items: [ { text: '基本用法', link: '/zh/pages/advanced/headers' }, { text: '方法', link: '/zh/pages/advanced/header-methods' }, ], }, { text: 'Fetch 适�器 🔥', link: '/zh/pages/advanced/fetch-adapter' }, { text: 'HTTP2 🔥', link: '/zh/pages/advanced/http2' }, { text: 'Promises', link: '/zh/pages/advanced/promises' }, { text: 'TypeScript', link: '/zh/pages/advanced/type-script' }, ], }, { text: '其他', items: [ { text: '语义化版本', link: '/zh/pages/misc/semver' }, { text: '安全', link: '/zh/pages/misc/security' }, ], }, ]; // ─── Spanish ────────────────────────────────────────────────────────────────── const esNav = [ { text: 'Guía', link: '/es/pages/getting-started/first-steps' }, { text: 'API', link: '/es/pages/advanced/api-reference' }, { text: 'Patrocinadores', link: '/es/pages/misc/sponsors' }, { text: 'v1.x', link: '#' }, ]; const esSidebar = [ { text: 'Primeros pasos', items: [ { text: 'Inicio', link: '/es/pages/getting-started/first-steps' }, { text: 'Características', link: '/es/pages/getting-started/features' }, { text: 'Ejemplos', items: [ { text: 'JavaScript', link: '/es/pages/getting-started/examples/commonjs' }, { text: 'TypeScript', link: '/es/pages/getting-started/examples/typescript' }, ], }, { text: 'Guía de actualización v0.x -> v1.x', link: '/es/pages/getting-started/upgrade-guide', }, ], }, { text: 'Avanzado', items: [ { text: 'API pública', link: '/es/pages/advanced/api-reference' }, { text: 'Alias de métodos de solicitud', link: '/es/pages/advanced/request-method-aliases' }, { text: 'Crear una instancia', link: '/es/pages/advanced/create-an-instance' }, { text: 'Configuración de solicitud', link: '/es/pages/advanced/request-config' }, { text: 'Adaptadores', link: '/es/pages/advanced/adapters' }, { text: 'Esquema de respuesta', link: '/es/pages/advanced/response-schema' }, { text: 'Configuración predeterminada', link: '/es/pages/advanced/config-defaults' }, { text: 'Interceptores', link: '/es/pages/advanced/interceptors' }, { text: 'Manejo de errores', link: '/es/pages/advanced/error-handling' }, { text: 'Cancelación', link: '/es/pages/advanced/cancellation' }, { text: 'Autenticación', link: '/es/pages/advanced/authentication' }, { text: 'Reintento y recuperación de errores', link: '/es/pages/advanced/retry' }, { text: 'Pruebas', link: '/es/pages/advanced/testing' }, { text: 'Formato x-www-form-urlencoded', link: '/es/pages/advanced/x-www-form-urlencoded-format', }, { text: 'Formato Multipart/form-data', link: '/es/pages/advanced/multipart-form-data-format', }, { text: 'Publicación de archivos', link: '/es/pages/advanced/file-posting' }, { text: 'Procesamiento de formularios HTML 🔥', link: '/es/pages/advanced/html-form-processing', }, { text: 'Captura de progreso 🔥', link: '/es/pages/advanced/progress-capturing' }, { text: 'Limitación de velocidad 🔥', link: '/es/pages/advanced/rate-limiting' }, { text: 'Cabeceras 🔥', items: [ { text: 'Uso general', link: '/es/pages/advanced/headers' }, { text: 'Métodos', link: '/es/pages/advanced/header-methods' }, ], }, { text: 'Adaptador Fetch 🔥', link: '/es/pages/advanced/fetch-adapter' }, { text: 'HTTP2 🔥', link: '/es/pages/advanced/http2' }, { text: 'Promesas', link: '/es/pages/advanced/promises' }, { text: 'TypeScript', link: '/es/pages/advanced/type-script' }, ], }, { text: 'Miscelánea', items: [ { text: 'SemVer', link: '/es/pages/misc/semver' }, { text: 'Seguridad', link: '/es/pages/misc/security' }, ], }, ]; // ─── French ─────────────────────────────────────────────────────────────────── const frNav = [ { text: 'Guide', link: '/fr/pages/getting-started/first-steps' }, { text: 'API', link: '/fr/pages/advanced/api-reference' }, { text: 'Sponsors', link: '/fr/pages/misc/sponsors' }, { text: 'v1.x', link: '#' }, ]; const frSidebar = [ { text: 'Démarrage', items: [ { text: 'Premiers pas', link: '/fr/pages/getting-started/first-steps' }, { text: 'Fonctionnalités', link: '/fr/pages/getting-started/features' }, { text: 'Exemples', items: [ { text: 'JavaScript', link: '/fr/pages/getting-started/examples/commonjs' }, { text: 'TypeScript', link: '/fr/pages/getting-started/examples/typescript' }, ], }, { text: 'Guide de mise à niveau v0.x -> v1.x', link: '/fr/pages/getting-started/upgrade-guide', }, ], }, { text: 'Avancé', items: [ { text: 'API publique', link: '/fr/pages/advanced/api-reference' }, { text: 'Alias des méthodes de requête', link: '/fr/pages/advanced/request-method-aliases' }, { text: 'Créer une instance', link: '/fr/pages/advanced/create-an-instance' }, { text: 'Configuration des requêtes', link: '/fr/pages/advanced/request-config' }, { text: 'Adaptateurs', link: '/fr/pages/advanced/adapters' }, { text: 'Schéma de réponse', link: '/fr/pages/advanced/response-schema' }, { text: 'Configuration par défaut', link: '/fr/pages/advanced/config-defaults' }, { text: 'Intercepteurs', link: '/fr/pages/advanced/interceptors' }, { text: 'Gestion des erreurs', link: '/fr/pages/advanced/error-handling' }, { text: 'Annulation', link: '/fr/pages/advanced/cancellation' }, { text: 'Authentification', link: '/fr/pages/advanced/authentication' }, { text: "Nouvelles tentatives et récupération d'erreurs", link: '/fr/pages/advanced/retry' }, { text: 'Tests', link: '/fr/pages/advanced/testing' }, { text: 'Format x-www-form-urlencoded', link: '/fr/pages/advanced/x-www-form-urlencoded-format', }, { text: 'Format Multipart/form-data', link: '/fr/pages/advanced/multipart-form-data-format' }, { text: 'Envoi de fichiers', link: '/fr/pages/advanced/file-posting' }, { text: 'Traitement des formulaires HTML 🔥', link: '/fr/pages/advanced/html-form-processing', }, { text: 'Capture de la progression 🔥', link: '/fr/pages/advanced/progress-capturing' }, { text: 'Limitation du débit 🔥', link: '/fr/pages/advanced/rate-limiting' }, { text: 'En-têtes 🔥', items: [ { text: 'Utilisation générale', link: '/fr/pages/advanced/headers' }, { text: 'Méthodes', link: '/fr/pages/advanced/header-methods' }, ], }, { text: 'Adaptateur Fetch 🔥', link: '/fr/pages/advanced/fetch-adapter' }, { text: 'HTTP2 🔥', link: '/fr/pages/advanced/http2' }, { text: 'Promesses', link: '/fr/pages/advanced/promises' }, { text: 'TypeScript', link: '/fr/pages/advanced/type-script' }, ], }, { text: 'Divers', items: [ { text: 'SemVer', link: '/fr/pages/misc/semver' }, { text: 'Sécurité', link: '/fr/pages/misc/security' }, ], }, ]; // ─── Config ─────────────────────────────────────────────────────────────────── export default defineConfig({ title: 'axios | Promise based HTTP client', description: 'Documentation for the axios HTTP project', head: [ ['link', { rel: 'icon', href: '/favicon.ico' }], ['link', { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png' }], ['link', { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/favicon-32x32.png' }], ['link', { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/favicon-16x16.png' }], ['link', { rel: 'manifest', href: '/site.webmanifest' }], ], locales: { root: { label: 'English', lang: 'en-US', themeConfig: { nav: enNav, sidebar: enSidebar, }, }, zh: { label: '中文', lang: 'zh-CN', themeConfig: { nav: zhNav, sidebar: zhSidebar, }, }, es: { label: 'Español', lang: 'es-ES', themeConfig: { nav: esNav, sidebar: esSidebar, }, }, fr: { label: 'Français', lang: 'fr-FR', themeConfig: { nav: frNav, sidebar: frSidebar, }, }, }, themeConfig: { search: searchConfig, logo: { dark: '/words.svg', light: '/words-light.svg', }, siteTitle: false, socialLinks: [{ icon: 'github', link: 'https://github.com/axios/axios' }], footer: { message: 'axios is provided under MIT license', copyright: 'Copyright © 2015-present axios collective', }, }, }); axios-axios-aa54a5e/docs/.vitepress/search.mts000066400000000000000000000066731526660636600215250ustar00rootroot00000000000000import type { DefaultTheme } from 'vitepress'; import { tokenizeSearchText } from './tokenizeSearchText.js'; export const searchConfig = { provider: 'local', options: { detailedView: 'auto', disableQueryPersistence: false, miniSearch: { options: { tokenize: tokenizeSearchText, }, }, locales: { root: { translations: { button: { buttonText: 'Search', buttonAriaLabel: 'Search documentation', }, modal: { displayDetails: 'Display detailed list', resetButtonTitle: 'Reset search', backButtonTitle: 'Close search', noResultsText: 'No results for', footer: { selectText: 'to select', selectKeyAriaLabel: 'enter', navigateText: 'to navigate', navigateUpKeyAriaLabel: 'up arrow', navigateDownKeyAriaLabel: 'down arrow', closeText: 'to close', closeKeyAriaLabel: 'escape', }, }, }, }, zh: { translations: { button: { buttonText: '�索', buttonAriaLabel: '�索文档', }, modal: { displayDetails: '显示详细列表', resetButtonTitle: '�置�索', backButtonTitle: '关闭�索', noResultsText: '没有结果', footer: { selectText: '选择', selectKeyAriaLabel: '回车', navigateText: '导航', navigateUpKeyAriaLabel: '�上箭头', navigateDownKeyAriaLabel: '�下箭头', closeText: '关闭', closeKeyAriaLabel: 'Escape 键', }, }, }, }, es: { translations: { button: { buttonText: 'Buscar', buttonAriaLabel: 'Buscar en la documentación', }, modal: { displayDetails: 'Mostrar la lista detallada', resetButtonTitle: 'Restablecer la búsqueda', backButtonTitle: 'Cerrar la búsqueda', noResultsText: 'No hay resultados para', footer: { selectText: 'para seleccionar', selectKeyAriaLabel: 'Intro', navigateText: 'para navegar', navigateUpKeyAriaLabel: 'flecha hacia arriba', navigateDownKeyAriaLabel: 'flecha hacia abajo', closeText: 'para cerrar', closeKeyAriaLabel: 'Escape', }, }, }, }, fr: { translations: { button: { buttonText: 'Rechercher', buttonAriaLabel: 'Rechercher dans la documentation', }, modal: { displayDetails: 'Afficher la liste détaillée', resetButtonTitle: 'Réinitialiser la recherche', backButtonTitle: 'Fermer la recherche', noResultsText: 'Aucun résultat pour', footer: { selectText: 'pour sélectionner', selectKeyAriaLabel: 'entrée', navigateText: 'pour naviguer', navigateUpKeyAriaLabel: 'flèche vers le haut', navigateDownKeyAriaLabel: 'flèche vers le bas', closeText: 'pour fermer', closeKeyAriaLabel: 'échap', }, }, }, }, }, }, } satisfies DefaultTheme.Config['search']; axios-axios-aa54a5e/docs/.vitepress/theme/000077500000000000000000000000001526660636600206215ustar00rootroot00000000000000axios-axios-aa54a5e/docs/.vitepress/theme/SecurityUpdatesBanner.vue000066400000000000000000000064341526660636600256340ustar00rootroot00000000000000 axios-axios-aa54a5e/docs/.vitepress/theme/index.ts000066400000000000000000000010541526660636600223000ustar00rootroot00000000000000// https://vitepress.dev/guide/custom-theme import { h } from 'vue' import type { Theme } from 'vitepress' import DefaultTheme from 'vitepress/theme' import SecurityUpdatesBanner from './SecurityUpdatesBanner.vue' import './style.css' export default { extends: DefaultTheme, Layout: () => { return h(DefaultTheme.Layout, null, { // https://vitepress.dev/guide/extending-default-theme#layout-slots 'home-hero-before': () => h(SecurityUpdatesBanner) }) }, enhanceApp({ app, router, siteData }) { // ... } } satisfies Theme axios-axios-aa54a5e/docs/.vitepress/theme/style.css000066400000000000000000000102751526660636600225000ustar00rootroot00000000000000/** * Customize default theme styling by overriding CSS variables: * https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/styles/vars.css */ /** * Colors * * Each colors have exact same color scale system with 3 levels of solid * colors with different brightness, and 1 soft color. * * - `XXX-1`: The most solid color used mainly for colored text. It must * satisfy the contrast ratio against when used on top of `XXX-soft`. * * - `XXX-2`: The color used mainly for hover state of the button. * * - `XXX-3`: The color for solid background, such as bg color of the button. * It must satisfy the contrast ratio with pure white (#ffffff) text on * top of it. * * - `XXX-soft`: The color used for subtle background such as custom container * or badges. It must satisfy the contrast ratio when putting `XXX-1` colors * on top of it. * * The soft color must be semi transparent alpha channel. This is crucial * because it allows adding multiple "soft" colors on top of each other * to create a accent, such as when having inline code block inside * custom containers. * * - `default`: The color used purely for subtle indication without any * special meanings attached to it such as bg color for menu hover state. * * - `brand`: Used for primary brand colors, such as link text, button with * brand theme, etc. * * - `tip`: Used to indicate useful information. The default theme uses the * brand color for this by default. * * - `warning`: Used to indicate warning to the users. Used in custom * container, badges, etc. * * - `danger`: Used to show error, or dangerous message to the users. Used * in custom container, badges, etc. * -------------------------------------------------------------------------- */ :root { --vp-c-default-1: var(--vp-c-gray-1); --vp-c-default-2: var(--vp-c-gray-2); --vp-c-default-3: var(--vp-c-gray-3); --vp-c-default-soft: var(--vp-c-gray-soft); --vp-c-brand-1: var(--vp-c-indigo-1); --vp-c-brand-2: var(--vp-c-indigo-2); --vp-c-brand-3: var(--vp-c-indigo-3); --vp-c-brand-soft: var(--vp-c-indigo-soft); --vp-c-tip-1: var(--vp-c-brand-1); --vp-c-tip-2: var(--vp-c-brand-2); --vp-c-tip-3: var(--vp-c-brand-3); --vp-c-tip-soft: var(--vp-c-brand-soft); --vp-c-warning-1: var(--vp-c-yellow-1); --vp-c-warning-2: var(--vp-c-yellow-2); --vp-c-warning-3: var(--vp-c-yellow-3); --vp-c-warning-soft: var(--vp-c-yellow-soft); --vp-c-danger-1: var(--vp-c-red-1); --vp-c-danger-2: var(--vp-c-red-2); --vp-c-danger-3: var(--vp-c-red-3); --vp-c-danger-soft: var(--vp-c-red-soft); } /** * Component: Button * -------------------------------------------------------------------------- */ :root { --vp-button-brand-border: transparent; --vp-button-brand-text: var(--vp-c-white); --vp-button-brand-bg: var(--vp-c-brand-3); --vp-button-brand-hover-border: transparent; --vp-button-brand-hover-text: var(--vp-c-white); --vp-button-brand-hover-bg: var(--vp-c-brand-2); --vp-button-brand-active-border: transparent; --vp-button-brand-active-text: var(--vp-c-white); --vp-button-brand-active-bg: var(--vp-c-brand-1); } /** * Component: Home * -------------------------------------------------------------------------- */ :root { --vp-home-hero-name-color: transparent; --vp-home-hero-name-background: -webkit-linear-gradient( 120deg, #bd34fe 30%, #41d1ff ); --vp-home-hero-image-background-image: linear-gradient( -45deg, #bd34fe 50%, #47caff 50% ); --vp-home-hero-image-filter: blur(44px); } @media (min-width: 640px) { :root { --vp-home-hero-image-filter: blur(56px); } } @media (min-width: 960px) { :root { --vp-home-hero-image-filter: blur(68px); } } /** * Component: Custom Block * -------------------------------------------------------------------------- */ :root { --vp-custom-block-tip-border: transparent; --vp-custom-block-tip-text: var(--vp-c-text-1); --vp-custom-block-tip-bg: var(--vp-c-brand-soft); --vp-custom-block-tip-code-bg: var(--vp-c-brand-soft); } /** * Component: Algolia * -------------------------------------------------------------------------- */ .DocSearch { --docsearch-primary-color: var(--vp-c-brand-1) !important; } axios-axios-aa54a5e/docs/.vitepress/tokenizeSearchText.js000066400000000000000000000026271526660636600237070ustar00rootroot00000000000000/** * Tokenize search text while preserving MiniSearch's default behavior for * non-Han content and adding overlapping Han bigrams. * * MiniSearch passes a field name while indexing and omits it for queries. * Indexing Han unigrams keeps single-character searches useful, while queries * longer than one character use bigrams to avoid broad OR matches. * * @param {string} text * @param {string} [fieldName] * @returns {string[]} */ export function tokenizeSearchText(text, fieldName) { const separatorPattern = /[\n\r\p{Z}\p{P}]/u; const hanPattern = /\p{Script=Han}/u; const terms = []; let token = ''; let tokenIsHan = false; const flush = () => { if (!token) { return; } if (!tokenIsHan) { terms.push(token); token = ''; return; } const characters = [...token]; if (fieldName !== undefined || characters.length === 1) { terms.push(...characters); } for (let index = 0; index < characters.length - 1; index++) { terms.push(characters[index] + characters[index + 1]); } token = ''; }; for (const character of text) { if (separatorPattern.test(character)) { flush(); continue; } const characterIsHan = hanPattern.test(character); if (token && characterIsHan !== tokenIsHan) { flush(); } token += character; tokenIsHan = characterIsHan; } flush(); return terms; } axios-axios-aa54a5e/docs/.vitepress/tokenizeSearchText.test.js000066400000000000000000000022471526660636600246630ustar00rootroot00000000000000import assert from 'node:assert/strict'; import { readFile } from 'node:fs/promises'; import test from 'node:test'; import { tokenizeSearchText } from './tokenizeSearchText.js'; test('preserves MiniSearch tokenization for non-Han text', () => { assert.deepEqual(tokenizeSearchText('Request config: Axios.create()'), [ 'Request', 'config', 'Axios', 'create', ]); }); test('indexes Han unigrams and overlapping bigrams', () => { assert.deepEqual(tokenizeSearchText('请求�置', 'title'), [ '请', '求', '�', '置', '请求', '求�', '�置', ]); }); test('uses Han bigrams for multi-character queries', () => { assert.deepEqual(tokenizeSearchText('�置'), ['�置']); assert.deepEqual(tokenizeSearchText('�'), ['�']); }); test('matches queries within the Chinese request configuration docs', async () => { const requestConfig = await readFile( new URL('../zh/pages/advanced/request-config.md', import.meta.url), 'utf8', ); const indexTerms = new Set(tokenizeSearchText(requestConfig, 'text')); for (const queryTerm of tokenizeSearchText('�置')) { assert.ok(indexTerms.has(queryTerm)); } }); axios-axios-aa54a5e/docs/data/000077500000000000000000000000001526660636600163265ustar00rootroot00000000000000axios-axios-aa54a5e/docs/data/sponsors.json000066400000000000000000005434241526660636600211230ustar00rootroot00000000000000{ "backer": [ { "name": "Leonardo Kewitz", "imageUrl": "https://images.opencollective.com/leokewitz/f20630b/avatar.png", "description": "I'm a Software Engineer who advocates for simplicity and readability, I strive for elegant solutions leveraging different languages and paradigms.", "tier": "backer", "slug": "leokewitz", "website": "https://kewitz.js.org/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Benjamin Piouffle", "imageUrl": "https://images.opencollective.com/betree/eb1a3dd/avatar.png", "description": "Full-time @opencollective. Founder/core-contributor @CaptainFact. Usually working with Elixir, React, and NodeJS.", "tier": "backer", "slug": "betree", "website": "https://benjamin.piouffle.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Takeru Ichii", "imageUrl": "https://images.opencollective.com/takeru-ichii/c6be635/avatar.png", "description": null, "tier": "backer", "slug": "takeru-ichii", "website": null, "twitter": null, "active": false }, { "name": "Mesh Payments", "imageUrl": "https://images.opencollective.com/meshpayments/87e9336/logo.png", "description": "Mesh Payments cardless solution allows companies to enjoy full visibility, control, and in-depth payment intelligence", "tier": "backer", "slug": "meshpayments", "website": "https://meshpayments.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/meshpayments?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "Orbit", "imageUrl": "https://images.opencollective.com/orbit-love/328bf3b/logo.png", "description": "Mission control for communities", "tier": "backer", "slug": "orbit-love", "website": "https://www.orbit.love/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/orbitmodel?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "kohta ito", "imageUrl": "https://images.opencollective.com/kohta-ito/c5cc50b/avatar.png", "description": null, "tier": "backer", "slug": "kohta-ito", "website": null, "twitter": null, "active": false }, { "name": "Yuhei FUJITA", "imageUrl": "https://images.opencollective.com/fjt/3955659/avatar.png", "description": null, "tier": "backer", "slug": "fjt", "website": "https://fujita.dev/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/Yuhei_FUJITA?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "DLTx Labs Pty Ltd", "imageUrl": "https://images.opencollective.com/dltxio/d2c191e/logo.png", "description": "DLTx Labs is a blockchain focused software development house and venture studio based in Brisbane, Australia.", "tier": "backer", "slug": "dltxio", "website": "https://dltx.io/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/dltxio?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "kzhrk", "imageUrl": "https://images.opencollective.com/kzhrk/7b4ca3c/avatar.png", "description": null, "tier": "backer", "slug": "kzhrk", "website": null, "twitter": null, "active": false }, { "name": "38elements", "imageUrl": "https://images.opencollective.com/38elements/5dfbefe/avatar.png", "description": null, "tier": "backer", "slug": "38elements", "website": "https://japanese-document.github.io/lit/api-LitElement.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/38elements?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "Sentry", "imageUrl": "https://images.opencollective.com/sentry/9620d33/logo.png", "description": "We help software teams build better software, faster. From error tracking to performance monitoring, developers can see what actually matters, solve quicker, and learn continuously about their applications - from the frontend to the backend.", "tier": "backer", "slug": "sentry", "website": "https://sentry.io/welcome/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/getsentry?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "Corellium", "imageUrl": "https://images.opencollective.com/corellium/09cd5cb/logo.png", "description": "Accelerate your development work on iOS, Android and beyond with the power of ARM-based virtual devices.", "tier": "backer", "slug": "corellium", "website": "https://www.corellium.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/corelliumhq?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "Andy Loughran", "imageUrl": "https://images.opencollective.com/andylockran/5d59da6/avatar.png", "description": null, "tier": "backer", "slug": "andylockran", "website": "https://andylockran.dev/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/andylockran?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": true }, { "name": "kei sakamoto", "imageUrl": "https://images.opencollective.com/guest-8c25700c/avatar.png", "description": null, "tier": "backer", "slug": "guest-8c25700c", "website": null, "twitter": null, "active": false }, { "name": "Suhail Doshi", "imageUrl": "https://images.opencollective.com/guest-1ac61aed/avatar.png", "description": null, "tier": "backer", "slug": "guest-1ac61aed", "website": null, "twitter": null, "active": false }, { "name": "Alan Wang", "imageUrl": "https://images.opencollective.com/user-731a6d6b/avatar.png", "description": null, "tier": "backer", "slug": "user-731a6d6b", "website": null, "twitter": null, "active": false }, { "name": "Julio Jordán", "imageUrl": "https://images.opencollective.com/juliojordan/avatar.png", "description": "Sync person, async code. He/him.", "tier": "backer", "slug": "juliojordan", "website": null, "twitter": null, "active": false }, { "name": "neuland - Büro für Informatik", "imageUrl": "https://images.opencollective.com/neuland/2b0d93f/logo.png", "description": null, "tier": "backer", "slug": "neuland", "website": "https://www.neuland-bfi.de/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/neuland?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "Jeremy Combs", "imageUrl": "https://images.opencollective.com/jmcombs/22a314a/avatar.png", "description": "Cisco Collaboration Junkie who is enjoying (attempting) software development to further Collaboration.", "tier": "backer", "slug": "jmcombs", "website": "https://wwjcdo.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/dreinidaho?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "Guest", "imageUrl": "https://images.opencollective.com/guest-b81fd6d3/avatar.png", "description": null, "tier": "backer", "slug": "guest-b81fd6d3", "website": null, "twitter": null, "active": false }, { "name": "Raider.IO", "imageUrl": "https://images.opencollective.com/raiderio_wow/6935515/logo.png", "description": null, "tier": "backer", "slug": "raiderio_wow", "website": "https://raider.io/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/RaiderIO_WoW?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": true }, { "name": "Midtown Rulers", "imageUrl": "https://images.opencollective.com/midtownrulers/8c856fb/logo.png", "description": null, "tier": "backer", "slug": "midtownrulers", "website": "https://midtownrulers.org/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "SerpApi", "imageUrl": "https://images.opencollective.com/serp_api/19b487e/logo.png", "description": "API to get search engine results with ease.", "tier": "backer", "slug": "serp_api", "website": "https://serpapi.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/serp_api?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "GitHub Sponsors", "imageUrl": "https://images.opencollective.com/github-sponsors/dc0ae97/logo.png", "description": "Connect your Collective to GitHub Sponsors: https://docs.opencollective.com/help/collectives/github-sponsors", "tier": "backer", "slug": "github-sponsors", "website": "https://github.com/sponsors/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "intra-mart", "imageUrl": "https://images.opencollective.com/intra-mart/414f3ce/logo.png", "description": null, "tier": "backer", "slug": "intra-mart", "website": "https://dev.intra-mart.jp/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/intramart_dev?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "MFB Technologies", "imageUrl": "https://images.opencollective.com/mfbtech/3c62a0c/logo.png", "description": "Develop cutting edge software for litigators.", "tier": "backer", "slug": "mfbtech", "website": "https://mfbtech.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/mfb_tech?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": true }, { "name": "Tomasz", "imageUrl": "https://images.opencollective.com/tomasz-kleszczewski/avatar.png", "description": null, "tier": "backer", "slug": "tomasz-kleszczewski", "website": null, "twitter": null, "active": false }, { "name": "Lay", "imageUrl": "https://images.opencollective.com/brownsugar/c324dd1/avatar.png", "description": null, "tier": "backer", "slug": "brownsugar", "website": "https://brownsugar.tw/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Bervianto Leo Pratama", "imageUrl": "https://images.opencollective.com/berviantoleo/3437bf2/avatar.png", "description": null, "tier": "backer", "slug": "berviantoleo", "website": "https://berviantoleo.my.id/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/berviantoleo?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "Quicko", "imageUrl": "https://images.opencollective.com/quicko/91a46e3/logo.png", "description": "On a mission to help India save, pay & file taxes!", "tier": "backer", "slug": "quicko", "website": "https://quicko.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/howtoquicko?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "Lowdefy", "imageUrl": "https://images.opencollective.com/lowdefy/aa6698f/logo.png", "description": "Create custom web apps in minutes", "tier": "backer", "slug": "lowdefy", "website": "https://lowdefy.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/lowdefy?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "Guest", "imageUrl": "https://images.opencollective.com/guest-73375f18/avatar.png", "description": null, "tier": "backer", "slug": "guest-73375f18", "website": null, "twitter": null, "active": false }, { "name": "Byzic", "imageUrl": "https://images.opencollective.com/byzic/6422f54/logo.png", "description": "All Games, All Fun", "tier": "backer", "slug": "byzic", "website": "https://byzic.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Incognito", "imageUrl": "https://images.opencollective.com/user-5d607d62/avatar.png", "description": null, "tier": "backer", "slug": "user-5d607d62", "website": null, "twitter": null, "active": false }, { "name": "Gama Casino", "imageUrl": "https://images.opencollective.com/gama-casino/a085f51/logo.png", "description": "We offer entertainment for the rich, namely online casinos.", "tier": "backer", "slug": "gama-casino", "website": "https://gamasio.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Top Rating casino", "imageUrl": "https://images.opencollective.com/topratingcasino/bb0b43a/avatar.png", "description": "TopRating.casino ➢ Гід по онлайн-казино в Україні", "tier": "backer", "slug": "topratingcasino", "website": "https://toprating.casino/ua/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "THANKS.DEV", "imageUrl": "https://images.opencollective.com/thanks-dev/360b917/logo.png", "description": null, "tier": "backer", "slug": "thanks-dev", "website": "https://thanks.dev/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/thanks_dev?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "cocopon", "imageUrl": "https://images.opencollective.com/cocopon/9d8fd1a/avatar.png", "description": "Developer / Designer", "tier": "backer", "slug": "cocopon", "website": "https://cocopon.me/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/cocopon?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "Guest", "imageUrl": "https://images.opencollective.com/guest-4e08d94a/avatar.png", "description": null, "tier": "backer", "slug": "guest-4e08d94a", "website": null, "twitter": null, "active": false }, { "name": "OmniJobs", "imageUrl": "https://images.opencollective.com/omnijobs/735c6ef/logo.png", "description": "Speeding up your job search with database of curated job postings", "tier": "backer", "slug": "omnijobs", "website": "https://omnijobs.io/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Krakenstream", "imageUrl": "https://images.opencollective.com/krakenstream-com/5fda96b/avatar.png", "description": "Buy Twitch viewers", "tier": "backer", "slug": "krakenstream-com", "website": "https://krakenstream.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Automatio AI", "imageUrl": "https://images.opencollective.com/kinder/cd0b9b4/avatar.png", "description": "No-code Web Automation powered by AI", "tier": "backer", "slug": "kinder", "website": "https://automatio.ai/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/shtefcs?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "JavaScript Fund", "imageUrl": "https://images.opencollective.com/oc-javascript-fund/4e5d7a5/logo.png", "description": "Supporting maintainers and communities in the JavaScript ecosystem.", "tier": "backer", "slug": "oc-javascript-fund", "website": "https://funds.ecosyste.ms/funds/javascript?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "FameViso: Monthly Auto Likes & Automated Instagram Engagement", "imageUrl": "https://images.opencollective.com/buy-instagram-auto-likes/d1eeb7c/logo.png", "description": "Never worry about low engagement again. FameViso automatically detects your new posts and delivers high-quality likes instantly to trigger the algorithm.", "tier": "backer", "slug": "buy-instagram-auto-likes", "website": "https://fameviso.com/instagram-auto-likes/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Eduardo Rocha", "imageUrl": "https://images.opencollective.com/hockdudu/9a09eed/avatar.png", "description": null, "tier": "backer", "slug": "hockdudu", "website": "https://hockdudu.ch/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "TikTokFame", "imageUrl": "https://images.opencollective.com/tiktokfameco/501360b/logo.png", "description": "At TikTokFame, we take immense pride in our role as the premier platform for boosting TikTok engagement. Our expertise lies in enhancing likes.", "tier": "backer", "slug": "tiktokfameco", "website": "https://tiktokfame.co/buy-tiktok-followers?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "ViewSnapStories", "imageUrl": "https://images.opencollective.com/viewsnapstories/49b4577/avatar.png", "description": "Snapchat Story, Profile, Profile Photo, Spotlight Viewer and Downloader", "tier": "backer", "slug": "viewsnapstories", "website": "https://viewsnapstories.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Socialfollowers.io", "imageUrl": "https://images.opencollective.com/socialfollowers/99f79b4/avatar.png", "description": "SocialFollowers.io offers fast, affordable promotion services to boost your social media with real followers, likes, views, and more.", "tier": "backer", "slug": "socialfollowers", "website": "https://socialfollowers.io/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Instagram Reel Views", "imageUrl": "https://images.opencollective.com/the-yt-views/0a3a42e/avatar.png", "description": "The YT Views is the best YouTube marketing online platform designed to help you to skyrocket your YouTube channel to the next level. We have all type of YouTube promotion services which help you to influence your audience.", "tier": "backer", "slug": "the-yt-views", "website": "https://theytviews.com/buy-instagram-reels-views?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "StoryKeek", "imageUrl": "https://images.opencollective.com/storykeek/13e5794/avatar.png", "description": "Snapchat Viewer and Downloader for Stories, Profiles, Profile Photos & Spotlights", "tier": "backer", "slug": "storykeek", "website": "https://storykeek.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Snap Story View", "imageUrl": "https://images.opencollective.com/snap-story-view/99c0a07/logo.png", "description": "A free web utility tool for securely viewing and archiving social media stories and also download the videos for free.", "tier": "backer", "slug": "snap-story-view", "website": "https://snapstoryview.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "View TikTok Stories", "imageUrl": "https://images.opencollective.com/view-tiktok-stories/574567d/logo.png", "description": null, "tier": "backer", "slug": "view-tiktok-stories", "website": "https://viewtikstories.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "stillviral", "imageUrl": "https://images.opencollective.com/stillviral-buy-instagram-followers/06e6cb1/logo.png", "description": "Stillviral provides digital marketing and social media growth solutions for creators, brands, and online businesses. We support digital communities, open-source projects, and independent initiatives that contribute to a stronger and more accessible intern", "tier": "backer", "slug": "stillviral-buy-instagram-followers", "website": "https://www.stillviral.io/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Insviewers", "imageUrl": "https://images.opencollective.com/insviewers/51a2cda/logo.png", "description": null, "tier": "backer", "slug": "insviewers", "website": "https://insviewers.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Tulsi Ki Mala", "imageUrl": "https://images.opencollective.com/bhaktimala/013cf2a/avatar.png", "description": "Tulsi Ki Mala offers handmade Tulsi Mala, Kanthi Mala, Japa Mala, Tulsi Bracelet, Locket, Earrings, and Japa Bags from Vrindavan. Shop online with secure checkout, quick delivery, and trusted service.", "tier": "backer", "slug": "bhaktimala", "website": "https://www.bhaktimala.in/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Javier Perez", "imageUrl": "https://images.opencollective.com/javier-perez-7ded094c/64a1dc1/avatar.png", "description": null, "tier": "backer", "slug": "javier-perez-7ded094c", "website": null, "twitter": null, "active": false }, { "name": "instagram likes kaufen", "imageUrl": "https://images.opencollective.com/likeskaufen/logo.png", "description": null, "tier": "backer", "slug": "likeskaufen", "website": "https://www.hna.de/wirtschaft/beste-seiten-um-instagram-likes-zu-kaufen-pr-92070770.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "acheter des followers instagram", "imageUrl": "https://images.opencollective.com/acheter-des-followers-instagra/logo.png", "description": null, "tier": "backer", "slug": "acheter-des-followers-instagra", "website": "https://supremeboost.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "comprar seguidores instagram", "imageUrl": "https://images.opencollective.com/comprarseguidores/logo.png", "description": null, "tier": "backer", "slug": "comprarseguidores", "website": "https://www.diariosur.es/economia/empresas/sitios-seguros-comprar-20230202133249-nt.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "comprare follower tiktok", "imageUrl": "https://images.opencollective.com/comprarefollowertik/logo.png", "description": null, "tier": "backer", "slug": "comprarefollowertik", "website": "https://www.insidemarketing.it/su-quali-siti-acquistare-follower-tiktok-e-come/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "tiktok follower kaufen", "imageUrl": "https://images.opencollective.com/foilower-kaufen/logo.png", "description": null, "tier": "backer", "slug": "foilower-kaufen", "website": "https://www.hna.de/wirtschaft/sind-die-besten-seiten-pr-tiktok-follower-kaufen-das-92158423.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "nachrechnen", "imageUrl": "https://images.opencollective.com/nachrechnen/logo.png", "description": "It is an german calculator tool it has calculator for finance,maths.geometry and etc. ", "tier": "backer", "slug": "nachrechnen", "website": "https://nachrechnen24.de/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "youtube views kaufen", "imageUrl": "https://images.opencollective.com/views-kaufen/logo.png", "description": null, "tier": "backer", "slug": "views-kaufen", "website": "https://www.hna.de/wirtschaft/beste-seiten-um-youtube-views-zu-kaufen-92035479.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "comprar seguidores instagram", "imageUrl": "https://images.opencollective.com/comprar-seguidores-b/logo.png", "description": null, "tier": "backer", "slug": "comprar-seguidores-b", "website": "https://www.metropoles.com/conteudo-especial/os-melhores-sites-para-comprar-seguidores-brasileiros-no-instagram?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "instagram follower kaufen", "imageUrl": "https://images.opencollective.com/followerkauf/logo.png", "description": null, "tier": "backer", "slug": "followerkauf", "website": "https://www.hna.de/kassel/beste-seiten-um-instagram-follower-zu-kaufen-aktive-echte-pr-91728059.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "ViewTikStories", "imageUrl": "https://images.opencollective.com/incognito-223fa987/b2864d4/avatar.png", "description": null, "tier": "backer", "slug": "incognito-223fa987", "website": "https://viewtikstories.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "BackupDrill", "imageUrl": "https://images.opencollective.com/backupdrill/347d149/logo.png", "description": "Supabase backups you can prove are restorable", "tier": "backer", "slug": "backupdrill", "website": "https://backupdrill.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Tikpeek", "imageUrl": "https://images.opencollective.com/owais-ahmed/d3b1320/avatar.png", "description": "Tiktok Profile Viewer", "tier": "backer", "slug": "owais-ahmed", "website": null, "twitter": null, "active": false }, { "name": "ReadableResume", "imageUrl": "https://images.opencollective.com/readableresume/e2d7d3c/logo.png", "description": "Free resume templates for Google Docs and Word, each tested against resume parsers", "tier": "backer", "slug": "readableresume", "website": "https://readableresume.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Comment Scraper", "imageUrl": "https://images.opencollective.com/comment-scraper/034118b/logo.png", "description": "Free Chrome extension that exports Reddit threads to CSV, JSON or Markdown", "tier": "backer", "slug": "comment-scraper", "website": "https://comment-scraper.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "TeknikGeek", "imageUrl": "https://images.opencollective.com/teknik-geek/771b047/avatar.png", "description": null, "tier": "backer", "slug": "teknik-geek", "website": "https://www.teknikgeek.se/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "MonetizationKit", "imageUrl": "https://images.opencollective.com/monetizationkit/7e85e3f/logo.png", "description": "Free YouTube monetization checker that shows the public signals behind every verdict.", "tier": "backer", "slug": "monetizationkit", "website": "https://monetizationkit.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Snap Story Viewer", "imageUrl": "https://images.opencollective.com/snapstoryviewer/ea87335/avatar.png", "description": "Snap Story Viewer – View Snapchat Stories Anonymously", "tier": "backer", "slug": "snapstoryviewer", "website": "https://snapstoryviewer.net/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Anonystory", "imageUrl": "https://images.opencollective.com/anonystory/132d488/logo.png", "description": null, "tier": "backer", "slug": "anonystory", "website": "https://anonystory.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true } ], "bronze": [ { "name": "WebScraping.AI", "imageUrl": "https://images.opencollective.com/webscraping-ai/9538df4/logo.png", "description": null, "tier": "bronze", "slug": "webscraping-ai", "website": "https://webscraping.ai/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/webscraping_ai?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "inkedin", "imageUrl": "https://images.opencollective.com/inkedin/68185e6/logo.png", "description": null, "tier": "bronze", "slug": "inkedin", "website": "https://inkedin.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "nongamstopbets.com", "imageUrl": "https://images.opencollective.com/nongamstopbets-uk/7271d84/logo.png", "description": "Entertainment", "tier": "bronze", "slug": "nongamstopbets-uk", "website": "https://www.nongamstopbets.com/casinos-not-on-gamstop/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "NonStop Casino", "imageUrl": "https://images.opencollective.com/nonstop-casino/5a4c348/logo.png", "description": "Sites Not on GamStop", "tier": "bronze", "slug": "nonstop-casino", "website": "https://nongamstop.nonstopcasino.org/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Proxidize", "imageUrl": "https://images.opencollective.com/proxidize/b14928d/logo.png", "description": "Proxidize is an innovative proxy network platform offering residential, mobile, datacenter and ISP proxies.", "tier": "bronze", "slug": "proxidize", "website": "https://proxidize.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/proxidizeHQ?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "OnlineCasinoProfy", "imageUrl": "https://images.opencollective.com/onlinecasinoprofy/85c222c/avatar.png", "description": "OnlineCasinoProfy is your guide to the world of gambling.", "tier": "bronze", "slug": "onlinecasinoprofy", "website": "https://polskiekasynaonline24.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Casino Bonussen Nederland", "imageUrl": "https://images.opencollective.com/casino-bonussen-nederland/fa79b5b/logo.png", "description": "HelloBonuses.com bekijkt de beste Nederlandse online casino's en de eerlijke casino bonussen. We lezen de kleine lettertjes hardop voor je.", "tier": "bronze", "slug": "casino-bonussen-nederland", "website": "https://www.hellobonuses.com/nl/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "SidesMedia", "imageUrl": "https://images.opencollective.com/sidesmedia/75de5ff/logo.png", "description": null, "tier": "bronze", "slug": "sidesmedia", "website": "https://sidesmedia.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy YouTube Views by JeffBullas", "imageUrl": "https://images.opencollective.com/buy-youtube-views-by-jeffbullas/60dc577/logo.png", "description": null, "tier": "bronze", "slug": "buy-youtube-views-by-jeffbullas", "website": "https://www.jeffbullas.com/buy-youtube-views/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Proxy Coupons", "imageUrl": "https://images.opencollective.com/proxy-coupons/9dd588f/logo.png", "description": null, "tier": "bronze", "slug": "proxy-coupons", "website": "https://proxy.coupons/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Read more on the best sites to Buy YouTube Subscribers", "imageUrl": "https://images.opencollective.com/read-more-on-the-best-sites-to-buy-youtube-subscribers/f128580/logo.png", "description": null, "tier": "bronze", "slug": "read-more-on-the-best-sites-to-buy-youtube-subscribers", "website": "https://www.santacruzsentinel.com/2024/03/26/buy-youtube-subscribers-2/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Siti Non AAMS", "imageUrl": "https://images.opencollective.com/siti-non-aams/520019c/logo.png", "description": "Scopri i migliori siti non AAMS per un'esperienza di gioco senza limiti e con bonus irresistibili!", "tier": "bronze", "slug": "siti-non-aams", "website": "https://bsc.news/post/siti-scommesse-non-aams?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Instagram Followers Thunderclapit", "imageUrl": "https://images.opencollective.com/buy-instagram-followers-usa/79f7eac/logo.png", "description": "Get real, active Instagram followers delivered straight to your account in minutes. Buy Instagram followers at Thunderclap.it starting just at $2.79", "tier": "bronze", "slug": "buy-instagram-followers-usa", "website": "https://thunderclap.it/buy-instagram-followers?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Google Reviews", "imageUrl": "https://images.opencollective.com/buy-google-reviews-usa/78797fd/logo.png", "description": "Buyreviewz is the best online review service providing - 100% real and non-drop google reviews for local business profiles.", "tier": "bronze", "slug": "buy-google-reviews-usa", "website": "https://buyreviewz.com/buy-google-reviews?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Thunderclap.com", "imageUrl": "https://images.opencollective.com/thunderclapcom/779e7fa/logo.png", "description": "Thunderclap.com is the best TikTok growth service provider, helping brands and creators build a targeted and engaged TikTok audience.", "tier": "bronze", "slug": "thunderclapcom", "website": "https://thunderclap.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "meilleur casino en ligne", "imageUrl": "https://images.opencollective.com/meilleur-casino-en-ligne/258bed9/logo.png", "description": "meilleur casino en ligne", "tier": "bronze", "slug": "meilleur-casino-en-ligne", "website": "https://bsc.news/post/meilleur-casino-en-ligne-fiable?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "UpGrow", "imageUrl": "https://images.opencollective.com/upgrow/9eec4da/logo.png", "description": "UpGrow is the Best Instagram Growth Service in 2024. Get more real Instagram followers with our AI-powered growth engine to get 10x faster results. ", "tier": "bronze", "slug": "upgrow", "website": "https://www.upgrow.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Teravision Technologies", "imageUrl": "https://images.opencollective.com/teravision-technologies/a0bb15c/avatar.png", "description": null, "tier": "bronze", "slug": "teravision-technologies", "website": "https://www.teravisiontech.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "alova3", "imageUrl": "https://images.opencollective.com/alova3/488d588/avatar.png", "description": "We are on a mission to extremely streamline API integration workflow. only 1 step. 100% compatible with axios.", "tier": "bronze", "slug": "alova3", "website": "https://alova.js.org/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://x.com/alovajs?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "Buy Youtube Views", "imageUrl": "https://images.opencollective.com/buyyoutubviewscom/e011e4d/logo.png", "description": "https://buyyoutubviews.com", "tier": "bronze", "slug": "buyyoutubviewscom", "website": "https://buyyoutubviews.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Webogram.net", "imageUrl": "https://images.opencollective.com/webogramnet/04a87b9/logo.png", "description": "Increase Instagram followers", "tier": "bronze", "slug": "webogramnet", "website": "https://webogram.net/how-to-grow-instagram-followers?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Aussie Casino Experts", "imageUrl": "https://images.opencollective.com/aussie-casino-experts/270d615/logo.png", "description": null, "tier": "bronze", "slug": "aussie-casino-experts", "website": "https://aussiecasinoexperts.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Writing Metier", "imageUrl": "https://images.opencollective.com/writing-metier/762e081/logo.png", "description": "Writing is our job, our passion, our love and of course, our métier. Welcome to the world of Writing Metier OU - professional copywriting services.", "tier": "bronze", "slug": "writing-metier", "website": "https://writingmetier.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/writingmetier?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": true }, { "name": "Buy Youtube Views", "imageUrl": "https://images.opencollective.com/get-youtube-views/c92b6ff/logo.png", "description": "Buy Youtube Views from the #1 rated company. Our exclusive high quality Youtube views come with a lifetime guarantee. Free views trial available.", "tier": "bronze", "slug": "get-youtube-views", "website": "https://mysocialfollowing.com/youtube/views?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "MagicUGC – Best AI UGC Video Generator", "imageUrl": "https://images.opencollective.com/magicugc/770b9a2/logo.png", "description": "MagicUGC is the Best AI UGC Video Generator. Trained on billions of views, MagicUGC creates TikTok & IG-ready videos with 500+ AI actors and proven viral hooks. Generate AI videos in 35+ languages, auto-test unlimited variations, and scale UGC marketing.", "tier": "bronze", "slug": "magicugc", "website": "https://www.magicugc.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://www.x.com/magicugc?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "SocialWick", "imageUrl": "https://images.opencollective.com/socialwick/e244614/logo.png", "description": "SocialWick is a leading social media shop, offering a wide range of premium services and exceptional customer support. With SocialWick, you can quickly boost your social media followership and enhance your online presence.", "tier": "bronze", "slug": "socialwick", "website": "https://www.socialwick.com/instagram/followers?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Buy YouTube subscribers from the best sites visit Container News to read more", "imageUrl": "https://images.opencollective.com/buy-youtube-subscribers-from-the-best-sites-visit-container-news-to-read-more/c6efe6e/logo.png", "description": null, "tier": "bronze", "slug": "buy-youtube-subscribers-from-the-best-sites-visit-container-news-to-read-more", "website": "https://container-news.com/best-sites-to-buyyoutube-subscribers-real-cheap/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Purchase Facebook Likes", "imageUrl": "https://images.opencollective.com/realsubscriber/bd6838e/logo.png", "description": "With many years of operation, RealSubscriber promotes Facebook, YouTube, Instagram, Spotify, TikTok, Pinterest, SoundCloud, and Twitter services.", "tier": "bronze", "slug": "realsubscriber", "website": "https://realsubscriber.com/buy-facebook-likes/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Instagram Followers from MIXX.com", "imageUrl": "https://images.opencollective.com/buy-instagram-followers-mixx/40c0b65/logo.png", "description": "Buy Instagram Followers with Instant Delivery from MIXX.com. Boost your audience and followers; buy Instagram followers for fast results.", "tier": "bronze", "slug": "buy-instagram-followers-mixx", "website": "https://www.mixx.com/buy-instagram-followers?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Buy Instagram Followers", "imageUrl": "https://images.opencollective.com/buy-instagram-followers4/0dfb5fb/avatar.png", "description": "Buy Instagram Followers", "tier": "bronze", "slug": "buy-instagram-followers4", "website": "https://www.reddit.com/r/InstagramEmpire/comments/1o8v7zx/whats_the_best_site_to_buy_instagram_followers/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy TikTok Followers", "imageUrl": "https://images.opencollective.com/buy-tiktok-followers3/a055967/avatar.png", "description": null, "tier": "bronze", "slug": "buy-tiktok-followers3", "website": "https://www.reddit.com/r/MarketingMentor/comments/1mdwpyq/best_site_to_buy_tiktok_followers/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Instagram Likes", "imageUrl": "https://images.opencollective.com/buyinstagramlikes/af80d90/avatar.png", "description": null, "tier": "bronze", "slug": "buyinstagramlikes", "website": "https://www.reddit.com/r/MarketingMentor/comments/1id0x02/best_site_to_buy_instagram_likes_views/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Google Reviews", "imageUrl": "https://images.opencollective.com/buy-google-reviews1/be3bf00/avatar.png", "description": "Buy Google Reviews", "tier": "bronze", "slug": "buy-google-reviews1", "website": "https://www.reddit.com/r/growmybusiness/comments/1qh63qr/whats_the_best_site_to_buy_google_reviews/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Buy Real Media", "imageUrl": "https://images.opencollective.com/buy-real-media/54bdc34/logo.png", "description": "Buy Real Media helps individuals and businesses grow their social media presence by boosting followers, likes, views, and comments on platforms like Instagram, TikTok, YouTube, and Twitter with real engagement.", "tier": "bronze", "slug": "buy-real-media", "website": "https://buyrealmedia.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Instagram Followers", "imageUrl": "https://images.opencollective.com/buy-instagram-followers12/fc45f1f/avatar.png", "description": "Buy Instagram Followers", "tier": "bronze", "slug": "buy-instagram-followers12", "website": "https://www.reddit.com/r/growmybusiness/comments/1so2fld/what_is_the_best_site_to_buy_instagram_followers/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "buy Instagram followers reddit", "imageUrl": "https://images.opencollective.com/buy-instagram-followers-reddit/5f7a284/logo.png", "description": "Forum and Q&A platform", "tier": "bronze", "slug": "buy-instagram-followers-reddit", "website": "https://www.reddit.com/r/instagramLab/comments/1mnbszl/need_recommendations_for_buying_instagram/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "buy youtube views Punchng", "imageUrl": "https://images.opencollective.com/buy-youtube-views-punchng/cdb66e1/logo.png", "description": "News Website", "tier": "bronze", "slug": "buy-youtube-views-punchng", "website": "https://punchng.com/buy-youtube-views/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Socialift-Buy YouTube Views", "imageUrl": "https://images.opencollective.com/socialift-buy-youtube-views/d71b219/avatar.png", "description": "Socialift.io helps creators and brands grow fast with real followers, likes, and views. Boost your social proof and reach across Facebook, Instagram, YouTube, and more—safely and smartly.", "tier": "bronze", "slug": "socialift-buy-youtube-views", "website": "https://www.socialift.io/buy-youtube-views?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Freespinz.com", "imageUrl": "https://images.opencollective.com/maks8/3ff80bf/avatar.png", "description": "Licensed online gaming platform", "tier": "bronze", "slug": "maks8", "website": "https://freespinz.com/en?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Kasyno Online", "imageUrl": "https://images.opencollective.com/kasyno-online/e73980a/avatar.png", "description": "kasyno online polska", "tier": "bronze", "slug": "kasyno-online", "website": "https://kasyno.it.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "buy TikTok Likes reddit", "imageUrl": "https://images.opencollective.com/buy-tiktok-likes-reddit/a5ca748/logo.png", "description": "Forum and Q&A platform", "tier": "bronze", "slug": "buy-tiktok-likes-reddit", "website": "https://www.reddit.com/r/SocialMediaMarketing/comments/1n8xrqs/struggling_with_low_tiktok_likes_can_buying_likes/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "buy active Instagram followers", "imageUrl": "https://images.opencollective.com/buy-active-instagram-followers/aff556d/logo.png", "description": "Forum and Q&A platform", "tier": "bronze", "slug": "buy-active-instagram-followers", "website": "https://www.reddit.com/r/instagramTalk/comments/1ndcrdi/looking_for_reliable_sites_to_buy_instagram/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "buy YouTube subscribers Collegian", "imageUrl": "https://images.opencollective.com/buy-youtube-subscribers-collegian/f3136f9/logo.png", "description": "The Student News Website", "tier": "bronze", "slug": "buy-youtube-subscribers-collegian", "website": "https://collegian.com/sponsored/2025/03/buy-youtube-subscribers/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy YouTube Shorts Views", "imageUrl": "https://images.opencollective.com/buy-youtube-shorts-views/5a2044f/logo.png", "description": "Forum and Q&A platform", "tier": "bronze", "slug": "buy-youtube-shorts-views", "website": "https://www.reddit.com/r/PartneredYoutube/comments/1ncab59/does_buying_youtube_shorts_views_help_your_videos/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy TikTok Likes Cheap", "imageUrl": "https://images.opencollective.com/buy-tiktok-likes-cheap/0975d4e/logo.png", "description": "Sponsored Article", "tier": "bronze", "slug": "buy-tiktok-likes-cheap", "website": "https://omaha.com/exclusive/article_39670924-5bfa-5b48-8331-9ff954ae8c21.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Real TikTok Followers", "imageUrl": "https://images.opencollective.com/buy-real-tiktok-followers/logo.png", "description": "Forum and Q&A platform", "tier": "bronze", "slug": "buy-real-tiktok-followers", "website": "https://www.reddit.com/r/MarketingGeek/comments/1mw26gx/where_can_i_buy_tiktok_followers_to_boost_my/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "SMM Panel", "imageUrl": "https://images.opencollective.com/smm-panel19/80a629f/avatar.png", "description": "SMM Panel ", "tier": "bronze", "slug": "smm-panel19", "website": "https://www.reddit.com/r/branding/comments/1rar7q0/whats_the_best_smm_panel_right_now/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Free Instagram Views", "imageUrl": "https://images.opencollective.com/free-instagram-views3/c45ce80/logo.png", "description": null, "tier": "bronze", "slug": "free-instagram-views3", "website": "https://buylikesservices.com/free-instagram-views/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Instagram Story Viewer", "imageUrl": "https://images.opencollective.com/ig-story-viewer/fb59b9f/avatar.png", "description": null, "tier": "bronze", "slug": "ig-story-viewer", "website": "https://anonstories.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Basswin Casino", "imageUrl": "https://images.opencollective.com/basswincasino/bcbe203/logo.png", "description": "THIS PAGE lists the best online casinos entered the market in 2026 ", "tier": "bronze", "slug": "basswincasino", "website": "https://linkly.link/2pOao?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy YouTube Views", "imageUrl": "https://images.opencollective.com/buy-youtube-views-real/7e69032/logo.png", "description": null, "tier": "bronze", "slug": "buy-youtube-views-real", "website": "https://www.techloy.com/buy-youtube-views/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "BuyTikTokFollowers.co", "imageUrl": "https://images.opencollective.com/buy-tiktok-followers-co/82431fe/logo.png", "description": "Buy TikTok Followers is a leading provider of social media growth solutions for TikTok.com.", "tier": "bronze", "slug": "buy-tiktok-followers-co", "website": "https://buytiktokfollowers.co/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/buytiktokfans?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "Comprare Follower TikTok", "imageUrl": "https://images.opencollective.com/comprare-follower-tiktok/c460c18/logo.png", "description": null, "tier": "bronze", "slug": "comprare-follower-tiktok", "website": "https://www.bergamonews.it/dal-territorio/3-siti-piu-affidabili-per-comprare-follower-tiktok-nel-2025/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Instagram Followers", "imageUrl": "https://images.opencollective.com/buy-instagram-followers-real/45c52e9/logo.png", "description": null, "tier": "bronze", "slug": "buy-instagram-followers-real", "website": "https://dailyprogress.com/exclusive/article_6c326796-3db5-552d-a5f8-09c35de1e515.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Quality Instagram Followers", "imageUrl": "https://images.opencollective.com/buy-quality-instagram-follower/53ea854/logo.png", "description": null, "tier": "bronze", "slug": "buy-quality-instagram-follower", "website": "https://dailyprogress.com/exclusive/article_e7d0d9ad-9f95-53bb-a386-7dc6856a9040.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Facebook Likes", "imageUrl": "https://images.opencollective.com/buy-facebook-likes-cheap/52b47de/logo.png", "description": null, "tier": "bronze", "slug": "buy-facebook-likes-cheap", "website": "https://dailyillini.com/sponsored/2025/12/08/buy-facebook-likes/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy YouTube Subscribers", "imageUrl": "https://images.opencollective.com/buy-youtube-subscribers-cheap/6e003fc/logo.png", "description": null, "tier": "bronze", "slug": "buy-youtube-subscribers-cheap", "website": "https://riverjournalonline.com/business/best-sites-to-buy-youtube-subscribers-top-5-trusted-platforms/281439/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Comprare Like Instagram", "imageUrl": "https://images.opencollective.com/comprare-like-instagram/fc2ea27/logo.png", "description": null, "tier": "bronze", "slug": "comprare-like-instagram", "website": "https://www.bergamonews.it/dal-territorio/3-siti-affidabili-per-comprare-like-instagram-reali/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Comprare Follower Instagram", "imageUrl": "https://images.opencollective.com/comprare-follower-instagram/fa82b18/logo.png", "description": null, "tier": "bronze", "slug": "comprare-follower-instagram", "website": "https://www.targatocn.it/2025/08/07/leggi-notizia/argomenti/economia-7/articolo/top-3-piattaforme-per-comprare-follower-instagram.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Get Free TikTok Followers", "imageUrl": "https://images.opencollective.com/get-free-tiktok-followers/bbe749b/logo.png", "description": "Student News Website", "tier": "bronze", "slug": "get-free-tiktok-followers", "website": "https://www.idsnews.com/article/2025/12/get-free-tiktok-followers?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "buy YouTube views Collegian", "imageUrl": "https://images.opencollective.com/buy-youtube-views-collegian/7d7728a/logo.png", "description": "The Student News Website", "tier": "bronze", "slug": "buy-youtube-views-collegian", "website": "https://collegian.com/sponsored/2025/03/buy-youtube-views/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Facebook Likes", "imageUrl": "https://images.opencollective.com/buy-facebook-likes-low-cost/f3cc358/logo.png", "description": null, "tier": "bronze", "slug": "buy-facebook-likes-low-cost", "website": "https://omaha.com/exclusive/article_4eeec40b-29fe-5668-b82a-c7559a391fc6.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Facebook Likes", "imageUrl": "https://images.opencollective.com/buy-facebook-likes-real/9ed49a7/logo.png", "description": null, "tier": "bronze", "slug": "buy-facebook-likes-real", "website": "https://tucson.com/exclusive/article_d407a4ab-a8ef-5129-bca4-02f95328664c.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Instagram Reel Views", "imageUrl": "https://images.opencollective.com/buy-instagram-reel-views-real/240e00d/logo.png", "description": null, "tier": "bronze", "slug": "buy-instagram-reel-views-real", "website": "https://www.idsnews.com/article/2025/12/buy-instagram-reel-views?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy TikTok Followers", "imageUrl": "https://images.opencollective.com/buy-tiktok-followers-2293/6c91dd0/logo.png", "description": "social media marketing agency ", "tier": "bronze", "slug": "buy-tiktok-followers-2293", "website": "https://roughdraftatlanta.com/2025/09/29/get-tiktok-followers-real/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy TikTok Likes", "imageUrl": "https://images.opencollective.com/buy-tiktok-likes-8930/d06ac36/logo.png", "description": "digital marketing services related business", "tier": "bronze", "slug": "buy-tiktok-likes-8930", "website": "https://roughdraftatlanta.com/2025/10/02/top-site-buy-tiktok-likes/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy TikTok Custom Comments", "imageUrl": "https://images.opencollective.com/buy-tiktok-custom-comments/bf666e1/logo.png", "description": null, "tier": "bronze", "slug": "buy-tiktok-custom-comments", "website": "https://buylikesservices.com/buy-tiktok-custom-comments/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy YouTube subscribers", "imageUrl": "https://images.opencollective.com/buy-youtube-subscribers3/16536ae/avatar.png", "description": "Buy YouTube subscribers", "tier": "bronze", "slug": "buy-youtube-subscribers3", "website": "https://www.reddit.com/r/YouTubeCamp/comments/1pwtija/whats_the_best_site_to_buy_youtube_subscribers/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Instagram Followers - IDS News", "imageUrl": "https://images.opencollective.com/buy-instagram-followers-ids-ne/1943b52/logo.png", "description": "The Student News Website", "tier": "bronze", "slug": "buy-instagram-followers-ids-ne", "website": "https://www.idsnews.com/article/2025/09/buy-instagram-followers?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Purchase Real TikTok Likes", "imageUrl": "https://images.opencollective.com/purchase-real-tiktok-likes/df02a82/logo.png", "description": null, "tier": "bronze", "slug": "purchase-real-tiktok-likes", "website": "https://richmond.com/exclusive/article_b15db1e2-fa22-5577-850f-e9c26f3a5331.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Rolex super clone", "imageUrl": "https://images.opencollective.com/swiss-eta-watches/c29b99b/avatar.png", "description": null, "tier": "bronze", "slug": "swiss-eta-watches", "website": "https://etawatchesindia.in/rolex-eta-watches-india?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy LinkedIn Followers", "imageUrl": "https://images.opencollective.com/buy-linkedin-followers/bb23698/avatar.png", "description": "Buy LinkedIn Followers", "tier": "bronze", "slug": "buy-linkedin-followers", "website": "https://www.reddit.com/r/LinkedinAds/comments/1r8xhat/what_is_the_best_site_to_buy_linkedin_followers/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Real X Followers", "imageUrl": "https://images.opencollective.com/buy-real-x-followers/e4c1171/logo.png", "description": "The Student News Website", "tier": "bronze", "slug": "buy-real-x-followers", "website": "https://www.idsnews.com/article/2025/09/buy-x-followers?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Real Telegram Members", "imageUrl": "https://images.opencollective.com/buy-real-telegram-members/411a0c2/logo.png", "description": null, "tier": "bronze", "slug": "buy-real-telegram-members", "website": "https://www.idsnews.com/article/2025/12/buy-telegram-members?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Instagram Followers", "imageUrl": "https://images.opencollective.com/buy-instagram-followers10/44e6c85/avatar.png", "description": "Buy Instagram Followers", "tier": "bronze", "slug": "buy-instagram-followers10", "website": "https://www.reddit.com/r/influencermarketing/comments/1r8b8i6/where_do_people_buy_instagram_followers_that/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Buy Instagram Followers", "imageUrl": "https://images.opencollective.com/buy-instagram-followers5/acaea89/avatar.png", "description": "Buy Instagram Followers", "tier": "bronze", "slug": "buy-instagram-followers5", "website": "https://www.reddit.com/r/influencermarketing/comments/1qi4wjs/whats_the_best_site_to_buy_instagram_followers/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Real YouTube Likes", "imageUrl": "https://images.opencollective.com/buy-real-youtube-likes/85d3ea7/logo.png", "description": null, "tier": "bronze", "slug": "buy-real-youtube-likes", "website": "https://www.idsnews.com/article/2026/01/sponsored-top-5-sites-to-buy-youtube-likes-fast-01212026?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Real Spotify Plays", "imageUrl": "https://images.opencollective.com/buy-real-spotify-plays/c6658f0/logo.png", "description": "Student News Website", "tier": "bronze", "slug": "buy-real-spotify-plays", "website": "https://www.idsnews.com/article/2026/01/sponsored-where-to-buy-spotify-plays-01212026?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Active Telegram Members", "imageUrl": "https://images.opencollective.com/buy-active-telegram-members/24f0a2f/logo.png", "description": null, "tier": "bronze", "slug": "buy-active-telegram-members", "website": "http://www.zeebiz.com/brand-desk/news-top-3-best-sites-to-buy-telegram-members-real-and-active-299133?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Instagram Followers", "imageUrl": "https://images.opencollective.com/buy-instagram-followers-21/c154c59/avatar.png", "description": "Buy Instagram Followers", "tier": "bronze", "slug": "buy-instagram-followers-21", "website": "https://www.reddit.com/r/SmartBuying/comments/1spp3zl/whats_the_best_site_to_buy_instagram_followers/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buying Real YouTube Views", "imageUrl": "https://images.opencollective.com/buying-real-youtube-views/4ab53bf/logo.png", "description": "News Website", "tier": "bronze", "slug": "buying-real-youtube-views", "website": "https://theeagle.com/exclusive/article_8ed01593-e584-5509-860e-2b6fccb60125.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Real Instagram Reposts", "imageUrl": "https://images.opencollective.com/buy-real-instagram-reposts/76bf045/logo.png", "description": null, "tier": "bronze", "slug": "buy-real-instagram-reposts", "website": "https://dailyillini.com/sponsored/2026/02/04/best-sites-to-buy-instagram-reposts/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Instagram Reels Views", "imageUrl": "https://images.opencollective.com/buy-instagram-reels-views-cheap/ed3335e/logo.png", "description": null, "tier": "bronze", "slug": "buy-instagram-reels-views-cheap", "website": "https://www.techloy.com/buy-instagram-reel-views/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "FUN88 Thailand", "imageUrl": "https://images.opencollective.com/fun88-thailand-en/cacc530/avatar.png", "description": null, "tier": "bronze", "slug": "fun88-thailand-en", "website": "https://thai.fun88.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Fun88 Vietnam", "imageUrl": "https://images.opencollective.com/fun88-vietnam-en/2541290/avatar.png", "description": null, "tier": "bronze", "slug": "fun88-vietnam-en", "website": "https://viet.fun88.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Buy Youtube Subscribers", "imageUrl": "https://images.opencollective.com/buy-youtube-subs-1/405293b/logo.png", "description": null, "tier": "bronze", "slug": "buy-youtube-subs-1", "website": "https://www.reddit.com/r/MarketingMentor/comments/1h3yoyr/best_site_to_buy_youtube_subscribers_views/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Time Now", "imageUrl": "https://images.opencollective.com/time-now/e7347c5/logo.png", "description": "", "tier": "bronze", "slug": "time-now", "website": "https://time.now/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Buy Instagram Views", "imageUrl": "https://images.opencollective.com/buy-instagram-views-real/faea998/logo.png", "description": null, "tier": "bronze", "slug": "buy-instagram-views-real", "website": "https://billingsgazette.com/exclusive/article_23538784-3303-5a93-a69b-b2b09ce7580d.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy TikTok Comments", "imageUrl": "https://images.opencollective.com/buy-tiktok-comments/ab43299/logo.png", "description": null, "tier": "bronze", "slug": "buy-tiktok-comments", "website": "https://buylikesservices.com/buy-tiktok-custom-comments/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "elfontario.ca", "imageUrl": "https://images.opencollective.com/elfontario/e06542b/logo.png", "description": null, "tier": "bronze", "slug": "elfontario", "website": "https://elfontario.ca/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy TikTok Followers", "imageUrl": "https://images.opencollective.com/buy-tiktok-followers-6/c1247c2/avatar.png", "description": "Buy TikTok Followers", "tier": "bronze", "slug": "buy-tiktok-followers-6", "website": "https://www.reddit.com/r/TikTokLounge/comments/1rv7lfz/whats_the_best_site_to_buy_tiktok_followers_that/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Buy YouTube Views", "imageUrl": "https://images.opencollective.com/buy-youtube-views-instant/1da119d/logo.png", "description": null, "tier": "bronze", "slug": "buy-youtube-views-instant", "website": "https://onpattison.com/news/2026/mar/17/7-best-platforms-for-buying-youtube-views-in-2026-expert-picks/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy TikTok likes and views and comments", "imageUrl": "https://images.opencollective.com/buy-tiktok-likes-and-views-and-comments/5680706/avatar.png", "description": "Buy TikTok likes and views and comments", "tier": "bronze", "slug": "buy-tiktok-likes-and-views-and-comments", "website": "https://www.reddit.com/r/SocialBlueprint/comments/1rtiat6/what_is_the_best_site_to_buy_tiktok_likes_views/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Instagram followers and likes", "imageUrl": "https://images.opencollective.com/buy-instagram-followers-and-likes-2/973a674/avatar.png", "description": "Buy Instagram followers and likes", "tier": "bronze", "slug": "buy-instagram-followers-and-likes-2", "website": "https://www.reddit.com/r/BuildToAttract/comments/1sro5qq/where_can_i_buy_instagram_followers_and_likes_to/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Telegram Members", "imageUrl": "https://images.opencollective.com/buy-telegram-members-real/8098404/logo.png", "description": null, "tier": "bronze", "slug": "buy-telegram-members-real", "website": "https://aijourn.com/buy-telegram-members/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Spotify Monthly Listeners", "imageUrl": "https://images.opencollective.com/buy-spotify-monthly-listeners/logo.png", "description": "Forum and Q&A platform", "tier": "bronze", "slug": "buy-spotify-monthly-listeners", "website": "https://www.reddit.com/r/musicmarketing/comments/1rirr6a/best_site_to_buy_spotify_monthly_listeners_for/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Facebook Likes", "imageUrl": "https://images.opencollective.com/purchase-facebook-page-likes/logo.png", "description": "Forum and Q&A platform", "tier": "bronze", "slug": "purchase-facebook-page-likes", "website": "https://www.reddit.com/r/facebook/comments/1labf0e/legit_site_for_facebook_page_likes_heres_my/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Instagram Followers", "imageUrl": "https://images.opencollective.com/buy-instagram-followers-cc8dc394/fa7a6e9/avatar.png", "description": "Buy Instagram Followers", "tier": "bronze", "slug": "buy-instagram-followers-cc8dc394", "website": "https://www.reddit.com/r/MarketingHelp/comments/1pi8cvw/anyone_actually_bought_instagram_followers_what/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy TikTok Likes", "imageUrl": "https://images.opencollective.com/buy-tiktok-likes-instant/fd0e180/logo.png", "description": null, "tier": "bronze", "slug": "buy-tiktok-likes-instant", "website": "https://billingsgazette.com/exclusive/article_64390499-5a5a-58f3-990e-7c8f7853b30e.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Instagram Reels Views", "imageUrl": "https://images.opencollective.com/buy-instagram-reels-views-instant/72a3dd8/logo.png", "description": null, "tier": "bronze", "slug": "buy-instagram-reels-views-instant", "website": "https://container-news.com/buy-instagram-reel-views/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Instagram Likes", "imageUrl": "https://images.opencollective.com/buy-instagram-likes-fast/9a9c007/logo.png", "description": null, "tier": "bronze", "slug": "buy-instagram-likes-fast", "website": "https://tricityvoice.com/best-sites-to-buy-instagram-likes/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy TikTok Followers", "imageUrl": "https://images.opencollective.com/buy-tiktok-followers-cheap/f7b2b03/logo.png", "description": null, "tier": "bronze", "slug": "buy-tiktok-followers-cheap", "website": "https://billingsgazette.com/exclusive/article_979d2b0d-6f14-5c8a-8a0e-7e87442d4637.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Buy TikTok Followers", "imageUrl": "https://images.opencollective.com/buy-tiktok-followers-fast/0b9d103/logo.png", "description": null, "tier": "bronze", "slug": "buy-tiktok-followers-fast", "website": "https://richmond.com/exclusive/article_9412c54e-bb62-50ea-9c9e-1eeef6641b02.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Buy Instagram Likes", "imageUrl": "https://images.opencollective.com/buy-instagram-likes-safe/ce0fd70/logo.png", "description": null, "tier": "bronze", "slug": "buy-instagram-likes-safe", "website": "https://buffalonews.com/exclusive/article_7e87c825-35d1-541e-96c7-1afa1c207454.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy TikTok Likes", "imageUrl": "https://images.opencollective.com/buy-tiktok-likes-fast/d4c2354/logo.png", "description": null, "tier": "bronze", "slug": "buy-tiktok-likes-fast", "website": "https://journalnow.com/exclusive/article_c1d40116-25a1-55e9-8ab6-c955525a586b.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy TikTok Views and Likes", "imageUrl": "https://images.opencollective.com/buy-tiktok-views-and-likes/15ca5c6/avatar.png", "description": "Buy TikTok Views and Likes", "tier": "bronze", "slug": "buy-tiktok-views-and-likes", "website": "https://www.reddit.com/r/TikTokLounge/comments/1svf7r5/wheres_the_best_site_to_buy_tiktok_views_and/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "MatchdayReport", "imageUrl": "https://images.opencollective.com/chadski/9185b28/avatar.png", "description": null, "tier": "bronze", "slug": "chadski", "website": "https://matchdayreport.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false } ], "silver": [ { "name": "CasinoReviews", "imageUrl": "https://images.opencollective.com/casinoreviews/adca197/logo.png", "description": "Home to unbiased analysis on the biggest and the most aspiring names in iGaming,opinion pieces from experts, and interviews with industry leaders.", "tier": "silver", "slug": "casinoreviews", "website": "https://www.casinoreviews.net/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "BairesDev", "imageUrl": "https://images.opencollective.com/bairesdev/48bb773/logo.png", "description": "We are the leading Nearshore Technology Solutions company. We architect and engineer scalable and high-performing software solutions.", "tier": "silver", "slug": "bairesdev", "website": "https://www.bairesdev.com/sponsoring-open-source-projects/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/bairesdev?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": true }, { "name": "Enric Baltasar", "imageUrl": "https://images.opencollective.com/enric-baltasar/avatar.png", "description": null, "tier": "silver", "slug": "enric-baltasar", "website": null, "twitter": null, "active": false }, { "name": "Ігрові автомати", "imageUrl": "https://images.opencollective.com/igrovye-avtomaty-ua/96bfde3/logo.png", "description": "ігрові автомати беткінг", "tier": "silver", "slug": "igrovye-avtomaty-ua", "website": "https://betking.com.ua/games/all-slots/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Chudovo", "imageUrl": "https://images.opencollective.com/chudovo/3c866f5/logo.png", "description": "Chudovo - international software development company with representative offices in Kyiv, Cologne, New York, Tallinn and London. It has been working on the market since 2006. Company has domain expertise in video security, logistics, medicine, finance and", "tier": "silver", "slug": "chudovo", "website": "https://chudovo.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "OnlineCasinosSpelen", "imageUrl": "https://images.opencollective.com/onlinecasinosspelen/99ac6a2/logo.png", "description": "OnlineCasinosSpelen", "tier": "silver", "slug": "onlinecasinosspelen", "website": "https://onlinecasinosspelen.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "casino-zonder-cruks.eu.com", "imageUrl": "https://images.opencollective.com/czrnet/24e6252/logo.png", "description": "CasinoZonderRegistratie.net - Nederlandse Top Casino's", "tier": "silver", "slug": "czrnet", "website": "https://casinozonderregistratie.net/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Nieuwe-Casinos.net", "imageUrl": "https://images.opencollective.com/nieuwecasinos/c67d423/logo.png", "description": "Beoordelen van nieuwe online casino's 2023", "tier": "silver", "slug": "nieuwecasinos", "website": "https://nieuwe-casinos.net/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "CzC", "imageUrl": "https://images.opencollective.com/user-5b30aec2/f10fca3/avatar.png", "description": null, "tier": "silver", "slug": "user-5b30aec2", "website": "https://casinoszondercruks.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Онлайн казино 777", "imageUrl": "https://images.opencollective.com/casinoinua/c691599/logo.png", "description": "Онлайн казино 777 Україна", "tier": "silver", "slug": "casinoinua", "website": "https://777ua.com.ua/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "YTMP3", "imageUrl": "https://images.opencollective.com/ytmp3/4a8db94/logo.png", "description": null, "tier": "silver", "slug": "ytmp3", "website": "https://ytmp3.page/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "https://www.pieria.co.uk/", "imageUrl": "https://images.opencollective.com/https-nongamstopcasinos-net/01aa99b/logo.png", "description": null, "tier": "silver", "slug": "https-nongamstopcasinos-net", "website": "https://www.pieria.co.uk/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "NZ online casinos", "imageUrl": "https://images.opencollective.com/nz-online-casinos/7eb1102/logo.png", "description": null, "tier": "silver", "slug": "nz-online-casinos", "website": "https://onlinecasinoskiwi.co.nz/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "try bookies not on GamStop", "imageUrl": "https://images.opencollective.com/try-bookies-not-on-gamstop/3758aae/logo.png", "description": null, "tier": "silver", "slug": "try-bookies-not-on-gamstop", "website": "https://netto.co.uk/betting-sites-not-on-gamstop/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Best Non Gamstop Casinos", "imageUrl": "https://images.opencollective.com/user-a524d8be/9b18e4a/avatar.png", "description": "https://thelidocafe.co.uk/", "tier": "silver", "slug": "user-a524d8be", "website": "https://thelidocafe.co.uk/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "SSSTwitter", "imageUrl": "https://images.opencollective.com/ssstwitter/9924968/logo.png", "description": "Twitter Video Downloader HD Tool allows you to store tweets on your device (mobile or PC) for free.", "tier": "silver", "slug": "ssstwitter", "website": "https://ssstwitter.online/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Andre Vaughn", "imageUrl": "https://images.opencollective.com/twicsy-followers/avatar.png", "description": null, "tier": "silver", "slug": "twicsy-followers", "website": "https://twicsy.com/buy-instagram-followers/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Fortune Tiger", "imageUrl": "https://images.opencollective.com/fortune-tiger2/d821e05/logo.png", "description": null, "tier": "silver", "slug": "fortune-tiger2", "website": "https://fortune-tiger-br.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Parimatch", "imageUrl": "https://images.opencollective.com/parimatch3/c87e839/logo.png", "description": null, "tier": "silver", "slug": "parimatch3", "website": "https://parimattchbr.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Social followers", "imageUrl": "https://images.opencollective.com/socialfollowersuk/e327e52/avatar.png", "description": " Our diverse range of services are designed for popular social media platforms such as Instagram, TikTok, Facebook, Threads, and YouTube.", "tier": "silver", "slug": "socialfollowersuk", "website": "https://www.socialfollowers.uk/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "187990323015587", "imageUrl": "https://images.opencollective.com/adam-smith-1879903/avatar.png", "description": null, "tier": "silver", "slug": "adam-smith-1879903", "website": "https://qbtoolhub.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Youtube Views - Views4You", "imageUrl": "https://images.opencollective.com/buy-youtube-views-views4you/de7d37c/logo.png", "description": "Enhance your online presence and boost your video's performance with Views4You, the trusted source to buy YouTube views and skyrocket your engagement.", "tier": "silver", "slug": "buy-youtube-views-views4you", "website": "https://views4you.com/buy-youtube-views/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "EarthWeb", "imageUrl": "https://images.opencollective.com/earthweb1/1752601/logo.png", "description": null, "tier": "silver", "slug": "earthweb1", "website": "https://earthweb.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Instagram followers Insfollowpro", "imageUrl": "https://images.opencollective.com/insfollowpro/a070619/logo.png", "description": "Insfollowpro sells Instagram followers, likes, views.", "tier": "silver", "slug": "insfollowpro", "website": "https://insfollowpro.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Bountii", "imageUrl": "https://images.opencollective.com/bountii/7cd628b/logo.png", "description": "At Bountii, we believe that everyone deserves to get the most out of their hard-earned money. That’s why we’ve dedicated ourselves to scouring the internet to bring you the best deals, discounts, and coupons from your favorite stores and brands.", "tier": "silver", "slug": "bountii", "website": "https://bountii.co/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://x.com/bountii?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "legjobbmagyarcasino.com", "imageUrl": "https://images.opencollective.com/legjobbmagyarcasinocom/1ede028/logo.png", "description": "Online Casino Reviews", "tier": "silver", "slug": "legjobbmagyarcasinocom", "website": "https://legjobbmagyarcasino.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Buy X(Twitter) Followers from Bulkoid", "imageUrl": "https://images.opencollective.com/buy-x-twitter-followers/a623928/logo.png", "description": "Find the social proof you need to reach your audience! Boost conversions. Quickly buy Twitter Followers & more with no sign-up. Taking you to the next", "tier": "silver", "slug": "buy-x-twitter-followers", "website": "https://bulkoid.com/buy-twitter-followers?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "HHC Vaper", "imageUrl": "https://images.opencollective.com/hhc-vaper/9f9df9e/logo.png", "description": "HHCVaper.com bietet Premium HHC Vapes, Gummies und Cartridges für Konsum.", "tier": "silver", "slug": "hhc-vaper", "website": "https://hhcvaper.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy YouTube Subscribers on YouTube with SidesMedia", "imageUrl": "https://images.opencollective.com/buy-youtube-subscribers-on-youtube-with-sidesmedia/f0e3ec5/logo.png", "description": null, "tier": "silver", "slug": "buy-youtube-subscribers-on-youtube-with-sidesmedia", "website": "https://sidesmedia.com/buy-youtube-subscribers/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Viralyft", "imageUrl": "https://images.opencollective.com/viralyft/b853d22/avatar.png", "description": "Buy Instagram Followers, Likes, Views & Comments", "tier": "silver", "slug": "viralyft", "website": "https://viralyft.com/buy-instagram-followers?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "SocialBoss", "imageUrl": "https://images.opencollective.com/socialboss/a644520/logo.png", "description": "We help improve visibility in social networks. ", "tier": "silver", "slug": "socialboss", "website": "https://socialboss.org/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Airbnb", "imageUrl": "https://images.opencollective.com/airbnb/d327d66/logo.png", "description": "Airbnb was born in 2007 when two Hosts welcomed three guests to their San Francisco home, and has since grown to over 4 million Hosts who have welcomed more than 1 billion guest arrivals in almost every country across the globe.", "tier": "silver", "slug": "airbnb", "website": "https://www.airbnb.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/airbnbeng?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": true }, { "name": "Daily Star UK Casinos", "imageUrl": "https://images.opencollective.com/dailystar-ukcouk/fec3df7/avatar.png", "description": "https://dailystar-uk.co.uk", "tier": "silver", "slug": "dailystar-ukcouk", "website": "https://dailystar-uk.co.uk/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Instagram Likes from BuyTopLikes", "imageUrl": "https://images.opencollective.com/buytoplikes/e2d8fce/logo.png", "description": "We offer various services for Instagram users, including purchasing likes, followers, views, and comments to boost engagement and visibility.", "tier": "silver", "slug": "buytoplikes", "website": "https://buytoplikes.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Anonymous", "imageUrl": "https://images.opencollective.com/account-f1b3f0cf/logo.png", "description": null, "tier": "silver", "slug": "account-f1b3f0cf", "website": null, "twitter": null, "active": false }, { "name": "Free Chip No Deposit", "imageUrl": "https://images.opencollective.com/free-chip-no-deposit/dd08c50/avatar.png", "description": null, "tier": "silver", "slug": "free-chip-no-deposit", "website": "https://free-chip-no-deposit.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "casinochileonline.net", "imageUrl": "https://images.opencollective.com/casinochileonlinenet/52355f7/logo.png", "description": "Online Casino Review", "tier": "silver", "slug": "casinochileonlinenet", "website": "https://casinochileonline.net/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "nejlepsiceskacasina.com", "imageUrl": "https://images.opencollective.com/nejlepsiceskacasinacom/3eb2ad7/logo.png", "description": "Online Casino Review", "tier": "silver", "slug": "nejlepsiceskacasinacom", "website": "https://nejlepsiceskacasina.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "slovenskeonlinecasino.com", "imageUrl": "https://images.opencollective.com/slovenskeonlinecasinocom/524b90a/logo.png", "description": "Online casino reviews", "tier": "silver", "slug": "slovenskeonlinecasinocom", "website": "https://slovenskeonlinecasino.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Viralyft", "imageUrl": "https://images.opencollective.com/viralyft7/90a7ccb/logo.png", "description": "Buy YouTube Views", "tier": "silver", "slug": "viralyft7", "website": "https://viralyft.com/buy-youtube-views?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy TikTok Views Poprey", "imageUrl": "https://images.opencollective.com/poprey-likes/3391887/avatar.png", "description": null, "tier": "silver", "slug": "poprey-likes", "website": "https://poprey.com/buy-tiktok-views?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Slotozilla Deutschland", "imageUrl": "https://images.opencollective.com/slotozilla-deutschland/d137949/logo.png", "description": "SlotoZilla ist eine unabhängige Website mit kostenlosen Spielautomaten und Slotbewertungen. Wir bieten keine Glücksspiele um echtes Geld an.", "tier": "silver", "slug": "slotozilla-deutschland", "website": "https://www.slotozilla.com/de/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "FitclubFinder", "imageUrl": "https://images.opencollective.com/fitclubfinder/b6c5b09/avatar.png", "description": null, "tier": "silver", "slug": "fitclubfinder", "website": "https://fitclubfinder.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "CreditCaptain – Leading AI Credit Repair Service", "imageUrl": "https://images.opencollective.com/creditcaptain/7f7d6f2/logo.png", "description": "CreditCaptain is the leading credit repair service that helps consumers quickly improve their credit scores, using AI for 10x better & faster results.", "tier": "silver", "slug": "creditcaptain", "website": "https://www.creditcaptain.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Telegram Members @ Social Mention", "imageUrl": "https://images.opencollective.com/socialmention/6ba381a/logo.png", "description": "Buy Telegram Members", "tier": "silver", "slug": "socialmention", "website": "https://www.socialmention.com/buy-telegram-members/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Hashtags For Likes", "imageUrl": "https://images.opencollective.com/hashtags-for-likes/183740d/logo.png", "description": null, "tier": "silver", "slug": "hashtags-for-likes", "website": "https://hashtagsforlikes.co/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "payid casino", "imageUrl": "https://images.opencollective.com/payid-casino/4bfb336/logo.png", "description": "payid casinos", "tier": "silver", "slug": "payid-casino", "website": "https://payid-pokies.net/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy TikTok Likes & Views", "imageUrl": "https://images.opencollective.com/tiktok-expert-buy-tiktok-followers/36f9a9b/logo.png", "description": "Help users to find best and cheapest site to buy Instagram Followers", "tier": "silver", "slug": "tiktok-expert-buy-tiktok-followers", "website": "https://buffalonews.com/exclusive/article_76d1e70c-c87f-5d3b-aa96-e4f9812134a5.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "eestionlinekasiinod.org", "imageUrl": "https://images.opencollective.com/eestionlinekasiinod-org/a3c015c/logo.png", "description": "online casino review", "tier": "silver", "slug": "eestionlinekasiinod-org", "website": "https://eestionlinekasiinod.org/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "LikesID – Service to Grow Likes on Instagram", "imageUrl": "https://images.opencollective.com/likesid/eeaccce/logo.png", "description": "Buy likes and other services to boost your Instagram reach cheaply, with discounts of up to 30% on the LikesID website. Enjoy fast, high-quality services that increase real engagement and enhance your profile’s social proof.", "tier": "silver", "slug": "likesid", "website": "https://likesid.com/kupit-lajki-instagram/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Guidebook.BetWinner", "imageUrl": "https://images.opencollective.com/guidebook_betwinner_com/6babe60/avatar.png", "description": "Betwinner is an online bookmaker offering sports betting, casino games, and more.", "tier": "silver", "slug": "guidebook_betwinner_com", "website": "https://guidebook.betwinner.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Goread.io", "imageUrl": "https://images.opencollective.com/goread_io/eb6337d/logo.png", "description": null, "tier": "silver", "slug": "goread_io", "website": "https://goread.io/buy-instagram-followers?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/goread_io?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": true }, { "name": "Boostlikes", "imageUrl": "https://images.opencollective.com/booster/6cd5cd8/logo.png", "description": "Boostlikes helps small business get more visibility on Social Media Platforms e.g. Facebook, Twitter and Instagram.", "tier": "silver", "slug": "booster", "website": "https://boostlikes.uk/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Betwinner Partner", "imageUrl": "https://images.opencollective.com/betwinnerpartner/4782dcd/avatar.png", "description": "The Betwinner program allows individuals and businesses to earn commissions.", "tier": "silver", "slug": "betwinnerpartner", "website": "https://betwinnerpartner.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "PayID-Pokies-Sites.com", "imageUrl": "https://images.opencollective.com/payid-pokies-sitescom/aaa6701/logo.png", "description": "Useful guides about PayID pokies and casino sites for Australians", "tier": "silver", "slug": "payid-pokies-sitescom", "website": "https://payid-pokies-sites.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy TikTok Likes", "imageUrl": "https://images.opencollective.com/graming-tiktok-likes/0014918/logo.png", "description": "At Graming, we offer top-notch quality TikTok likes at the best prices! Check our deals below and order now!", "tier": "silver", "slug": "graming-tiktok-likes", "website": "https://graming.com/buy-tiktok-likes/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "c19.cl - Mejores Casinos Online", "imageUrl": "https://images.opencollective.com/casino-online-chile/91f1057/avatar.png", "description": "", "tier": "silver", "slug": "casino-online-chile", "website": "https://www.c19.cl/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Find the social proof you need to reach your audience! Boost conversions. Quickly buy Twitter Followers & more with no sign-up. Taking you to the next", "imageUrl": "https://images.opencollective.com/find-the-social-proof-you-need-to-reach-your-audience-boost-conversions-quickly-buy-twitter-followers-and-more-with-no-sign-up-taking-you-to-the-next/04baec7/logo.png", "description": null, "tier": "silver", "slug": "find-the-social-proof-you-need-to-reach-your-audience-boost-conversions-quickly-buy-twitter-followers-and-more-with-no-sign-up-taking-you-to-the-next", "website": "https://growthoid.com/buy-twitter-followers/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "UK-Exchange", "imageUrl": "https://images.opencollective.com/uk-exchange/4021be9/avatar.png", "description": null, "tier": "silver", "slug": "uk-exchange", "website": "https://uk-exchange.com/en/usdt-to-paypal.php?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "SocialBoss - Buy Instagram Likes", "imageUrl": "https://images.opencollective.com/socialboss-buy-instagram-likes/13fcede/logo.png", "description": "Promote your Instagram with SocialBoss. High-quality likes for your Instagram profile.", "tier": "silver", "slug": "socialboss-buy-instagram-likes", "website": "https://socialboss.org/buy-instagram-likes/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Playfortuneforfun.com", "imageUrl": "https://images.opencollective.com/playfortuneforfun-com/323be23/avatar.png", "description": null, "tier": "silver", "slug": "playfortuneforfun-com", "website": "https://playfortuneforfun.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "betking онлайн казино", "imageUrl": "https://images.opencollective.com/betking/59924a1/logo.png", "description": "Онлайн казино та БК (�тавки на �порт) в Україні", "tier": "silver", "slug": "betking", "website": "https://betking.com.ua/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Букмекер", "imageUrl": "https://images.opencollective.com/bukmeker/309f296/logo.png", "description": "Ставки на �порт, БК в Україні", "tier": "silver", "slug": "bukmeker", "website": "https://betking.com.ua/sports-book/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Ставки на �порт", "imageUrl": "https://images.opencollective.com/stavki-na-sport-ua/ecee984/logo.png", "description": "�ова україн�ька букмекер�ька контора", "tier": "silver", "slug": "stavki-na-sport-ua", "website": "https://betking.com.ua/sports-book/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Instagram Followers", "imageUrl": "https://images.opencollective.com/bestsitesbuyinstagramfollowers/d6844ba/logo.png", "description": "Adflee is the best place to buy Instagram followers, likes & views. You can buy Real & Non-Drop Instagram followers from Adflee.", "tier": "silver", "slug": "bestsitesbuyinstagramfollowers", "website": "https://www.reddit.com/r/MarketingGeek/comments/1jd6yre/where_to_buy_instagram_followers_any_best_site/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Ігрові автомати", "imageUrl": "https://images.opencollective.com/casino-ua/11d7447/avatar.png", "description": "Ігрові автомати онлайн", "tier": "silver", "slug": "casino-ua", "website": "https://casino.ua/casino/slots/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Graming", "imageUrl": "https://images.opencollective.com/graming/3eb6b3b/avatar.png", "description": null, "tier": "silver", "slug": "graming", "website": "https://graming.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Eldiario", "imageUrl": "https://images.opencollective.com/eldiario/027a498/logo.png", "description": "Generate QR codes", "tier": "silver", "slug": "eldiario", "website": "https://www.eldiario.es/madrid/somos/canal-empresas/crear-codigo-qr-de-un-pdf_1_11815280.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "buzzvoice.com", "imageUrl": "https://images.opencollective.com/buzzvoicecom/c7477dd/logo.png", "description": "Buzzvoice is your one-stop shop for all your social media marketing needs. With Buzzvoice, you can buy followers, comments, likes, video views and more!", "tier": "silver", "slug": "buzzvoicecom", "website": "https://buzzvoice.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Buy Twitter Followers Visit TweSocial", "imageUrl": "https://images.opencollective.com/buy-twitter-followers-visit-twesocial/6feef5c/logo.png", "description": null, "tier": "silver", "slug": "buy-twitter-followers-visit-twesocial", "website": "https://twesocial.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Instagram Followers & Likes", "imageUrl": "https://images.opencollective.com/leofame/2b658cd/avatar.png", "description": "Boost your social media presence effortlessly with top-quality Instagram and TikTok followers and likes.", "tier": "silver", "slug": "leofame", "website": "https://leofame.com/buy-instagram-followers?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "LITSLINK", "imageUrl": "https://images.opencollective.com/litslink/c82ffb5/logo.png", "description": "Supports open source projects", "tier": "silver", "slug": "litslink", "website": "https://litslink.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Graming", "imageUrl": "https://images.opencollective.com/gramingcom/e454bc9/logo.png", "description": "Make the difference with Gramy's Followers, Likes & Views – Premium Quality and Instant Delivery!", "tier": "silver", "slug": "gramingcom", "website": "https://graming.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Stellarlikes", "imageUrl": "https://images.opencollective.com/stellarlikes/3d93cf6/logo.png", "description": "��Best website to buy TikTok likes, views and followers", "tier": "silver", "slug": "stellarlikes", "website": "https://stellarlikes.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "UK casinos not on Gamstop", "imageUrl": "https://images.opencollective.com/1uk-casinos-not-on-gamstop/71d6590/logo.png", "description": "Best Casinos not on Gamstop in the UK 2025 – Safe & Trusted", "tier": "silver", "slug": "1uk-casinos-not-on-gamstop", "website": "https://www.vso.org.uk/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "AusGambler", "imageUrl": "https://images.opencollective.com/ausgambler/71427c1/logo.png", "description": "Best PayID Pokies in Australia", "tier": "silver", "slug": "ausgambler", "website": "https://ausgambler.net/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "LGC News", "imageUrl": "https://images.opencollective.com/lgc-news/ae23c57/logo.png", "description": "Providing News about Gambling & General in English Speaking world & Cyprus", "tier": "silver", "slug": "lgc-news", "website": "https://lgcnews.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Follower-Börse", "imageUrl": "https://images.opencollective.com/follower-borse2/4695e46/logo.png", "description": null, "tier": "silver", "slug": "follower-borse2", "website": "https://follower-boerse.de/produkt/tiktok-likes-kaufen/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Incognito", "imageUrl": "https://images.opencollective.com/user-3cc5c4ca/avatar.png", "description": null, "tier": "silver", "slug": "user-3cc5c4ca", "website": null, "twitter": null, "active": false }, { "name": "ViralQR", "imageUrl": "https://images.opencollective.com/viralqr/ff646e9/logo.png", "description": "Create QR codes", "tier": "silver", "slug": "viralqr", "website": "https://viralqr.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Twitter Followers", "imageUrl": "https://images.opencollective.com/buy-twitter-followers-buffalo-old/a405f1d/logo.png", "description": "Buy Twitter Followers", "tier": "silver", "slug": "buy-twitter-followers-buffalo-old", "website": "https://buffalonews.com/exclusive/article_e849bb68-a3cb-5f09-aec4-da95d2ed2c85.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "TikTok Likes", "imageUrl": "https://images.opencollective.com/tiktok-likes/0e0da66/logo.png", "description": null, "tier": "silver", "slug": "tiktok-likes", "website": "https://follower-boerse.de/product/tiktok-likes-kaufen/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "SOleks", "imageUrl": "https://images.opencollective.com/soleks/3b8bd31/logo.png", "description": "Helping Ukrainians to travel easier across the globe", "tier": "silver", "slug": "soleks", "website": "https://soleks.com.ua/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Casino Online Chile", "imageUrl": "https://images.opencollective.com/interactive-media-group-ltd/320a3f6/avatar.png", "description": null, "tier": "silver", "slug": "interactive-media-group-ltd", "website": "https://www.acee.cl/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Nove Casino", "imageUrl": "https://images.opencollective.com/novecasino-net/ad229be/avatar.png", "description": null, "tier": "silver", "slug": "novecasino-net", "website": "https://novecasino.net/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "slovenskecasino.net", "imageUrl": "https://images.opencollective.com/slovenskecasino-net/5e60c84/avatar.png", "description": null, "tier": "silver", "slug": "slovenskecasino-net", "website": "https://slovenskecasino.net/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "zahranicnicasino.net", "imageUrl": "https://images.opencollective.com/zahranicnicasino-net/5574f1d/avatar.png", "description": null, "tier": "silver", "slug": "zahranicnicasino-net", "website": "https://zahranicnicasino.net/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Casino Magyar", "imageUrl": "https://images.opencollective.com/casino-magyar/a718b4d/avatar.png", "description": null, "tier": "silver", "slug": "casino-magyar", "website": "https://kaszinomagyar.net/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy YouTube Subscribers Media Mister", "imageUrl": "https://images.opencollective.com/buy-youtube-subscribers-media-mister/a4d0210/logo.png", "description": null, "tier": "silver", "slug": "buy-youtube-subscribers-media-mister", "website": "https://www.mediamister.com/buy-youtube-subscribers?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "UK non GamStop casino sites", "imageUrl": "https://images.opencollective.com/uk-non-gamstop-casino-sites/f463b0c/logo.png", "description": null, "tier": "silver", "slug": "uk-non-gamstop-casino-sites", "website": "https://casinonotongamstop.uk/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Gambling sites not on GamStop", "imageUrl": "https://images.opencollective.com/gambling-sites-not-on-gamstop1/4e7068a/logo.png", "description": null, "tier": "silver", "slug": "gambling-sites-not-on-gamstop1", "website": "https://nogamstopcasinos.uk/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy TikTok Likes, Views, and Followers", "imageUrl": "https://images.opencollective.com/buy-tiktok-likes-views-followers-tsc/01d8ae8/logo.png", "description": "Buy TikTok Likes, Views, and Followers", "tier": "silver", "slug": "buy-tiktok-likes-views-followers-tsc", "website": "https://www.reddit.com/r/MrMarketing/comments/1sxe34y/where_can_i_buy_tiktok_likes_and_views_any/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Instagram Followers", "imageUrl": "https://images.opencollective.com/givemeboost/f42426a/avatar.png", "description": null, "tier": "silver", "slug": "givemeboost", "website": "https://www.reddit.com/r/MarketingMentor/comments/1cut7x5/where_to_buy_instagram_followers_likes/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Webisoft", "imageUrl": "https://images.opencollective.com/webisoft/e993730/logo.png", "description": null, "tier": "silver", "slug": "webisoft", "website": "https://webisoft.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "UK casinos not on GamStop", "imageUrl": "https://images.opencollective.com/uk-casinos-not-on-gamstop/c288b20/logo.png", "description": null, "tier": "silver", "slug": "uk-casinos-not-on-gamstop", "website": "https://www.stjames-theatre.co.uk/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Chilecasinoonline", "imageUrl": "https://images.opencollective.com/chilecasinoonline/43c3e6a/avatar.png", "description": "casino online chile", "tier": "silver", "slug": "chilecasinoonline", "website": "https://chilecasinoonline.cl/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Famoid", "imageUrl": "https://images.opencollective.com/famoid/b8421b3/logo.png", "description": "Famoid is a digital marketing agency that specializes in social media services and tools.", "tier": "silver", "slug": "famoid", "website": "https://famoid.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Noah", "imageUrl": "https://images.opencollective.com/noah23/62d928a/avatar.png", "description": null, "tier": "silver", "slug": "noah23", "website": "https://marsbahis.com/tr/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "PrestigeWatches", "imageUrl": "https://images.opencollective.com/prestigewatches/5654727/logo.png", "description": null, "tier": "silver", "slug": "prestigewatches", "website": "https://prestigewatches.co/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "SocialBoosting", "imageUrl": "https://images.opencollective.com/socialboosting/7b6b775/logo.png", "description": "SocialBoosting: Buy Instagram & TikTok Followers, Likes, Views", "tier": "silver", "slug": "socialboosting", "website": "https://www.socialboosting.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "PayID Pokies", "imageUrl": "https://images.opencollective.com/bestpayidpokies/07fc9d6/logo.png", "description": "We specialize in the online gambling industry, helping players access reliable and verified information about the best online casinos and pokies in Australia. Our team tests casinos and games, collects user reviews from Trustpilot, and organizes them in o", "tier": "silver", "slug": "bestpayidpokies", "website": "https://au.trustpilot.com/review/bestpayidpokies.net?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "parhaatuudetkasinot.com", "imageUrl": "https://images.opencollective.com/parhaatuudetkasinotcom/8979792/logo.png", "description": "Uudet Nettikasinot", "tier": "silver", "slug": "parhaatuudetkasinotcom", "website": "https://fi.parhaatuudetkasinot.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "BDG Win", "imageUrl": "https://images.opencollective.com/bdg-win-game/77ea287/logo.png", "description": "BDG Win Game is India's Growing Color prediction Game, Where You Can Play K3, Wingo , 5D And Aviator", "tier": "silver", "slug": "bdg-win-game", "website": "https://win.bdggame.io/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Tiranga Game", "imageUrl": "https://images.opencollective.com/tiranga-gameapp/12f77a3/logo.png", "description": "Tiranga Game is the Fastest Growing Colour Prediction Game in India. There are 10000 Games in it, Including Aviator Game, Wingo, k3 and 5D", "tier": "silver", "slug": "tiranga-gameapp", "website": "https://tiranga.bdggame.io/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "casino seo", "imageUrl": "https://images.opencollective.com/casino-seo/fef9515/avatar.png", "description": null, "tier": "silver", "slug": "casino-seo", "website": "https://seo.casino/en/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "super clone watches", "imageUrl": "https://images.opencollective.com/super-clone-prestige/e681745/logo.png", "description": "Information technology", "tier": "silver", "slug": "super-clone-prestige", "website": "https://finance.yahoo.com/news/best-website-super-clone-watches-073500706.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "FBPostLikes", "imageUrl": "https://images.opencollective.com/pankaj-jangir/c5dee33/avatar.png", "description": null, "tier": "silver", "slug": "pankaj-jangir", "website": "https://www.fbpostlikes.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Online Pokies in Australia", "imageUrl": "https://images.opencollective.com/online-pokies-au/7b2ecfc/logo.png", "description": "We collect all reviews about the best online pokies.", "tier": "silver", "slug": "online-pokies-au", "website": "https://au.trustpilot.com/review/aussiepokies.net?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "TrustlyR - Buy Google Reviews", "imageUrl": "https://images.opencollective.com/trustlyr/6696cb9/avatar.png", "description": " TrustlyR delivers authentic Google reviews from real people - no bots, no risks. Watch your rating climb, traffic grow, and customers trust you more. Simple, safe, and proven with 24 thousand + of happy businesses. ", "tier": "silver", "slug": "trustlyr", "website": null, "twitter": null, "active": false }, { "name": "PayID Pokies Sites", "imageUrl": "https://images.opencollective.com/payid-pokies-sites/c9cd78a/logo.png", "description": "We test dozens of casinos every month and select the coolest ones for Australian players. ", "tier": "silver", "slug": "payid-pokies-sites", "website": "https://au.trustpilot.com/review/payid-casino.net?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Online Pokies NZ", "imageUrl": "https://images.opencollective.com/betpokiesconz/6433b9d/logo.png", "description": "BetPokies.co.nz is your New Zealand guide in the world of online gambling. Our site was created by gamblers for gamblers.", "tier": "silver", "slug": "betpokiesconz", "website": "https://nz.trustpilot.com/review/betpokies.co.nz?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Daman Game", "imageUrl": "https://images.opencollective.com/daman-game-login/86bac66/logo.png", "description": "Colour Prediction Game", "tier": "silver", "slug": "daman-game-login", "website": "https://damanapp.download/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Online Pokies in Australia", "imageUrl": "https://images.opencollective.com/betpokies/147f64c/avatar.png", "description": "BetPokies.orgis your Australian guide in the world of online gambling. We attentively select the best brands for Aussie players and write honest reviews, provide useful tips, good bonuses and game demos, all that to assure that our readers get best exper", "tier": "silver", "slug": "betpokies", "website": "https://au.trustpilot.com/review/betpokies.com?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "super clone watches", "imageUrl": "https://images.opencollective.com/watchindiaa/4cbad3e/avatar.png", "description": null, "tier": "silver", "slug": "watchindiaa", "website": "https://www.freakforluxury.in/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buitenlandse Online Casino", "imageUrl": "https://images.opencollective.com/buitenlandse-online-casino/42caa25/logo.png", "description": "", "tier": "silver", "slug": "buitenlandse-online-casino", "website": "https://nl.trustpilot.com/review/buitenlandsecasino.vip?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Kasino Kurko: Uudet nettikasinot", "imageUrl": "https://images.opencollective.com/kurko-uudetnettikasinot/a293576/logo.png", "description": "KasinoKurko on Suomen uusien kasinoiden asiantuntija.", "tier": "silver", "slug": "kurko-uudetnettikasinot", "website": "https://kasinokurko.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://x.com/kasinokurko?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": true }, { "name": "Fastest Payout Online Casino", "imageUrl": "https://images.opencollective.com/fast-payout-casinos-nz/f973213/logo.png", "description": "BetPokies NZ features top fast payout casinos where winnings are processed within hours. The speed depends on the casino and payment method, with the platform highlighting the best options for smooth, reliable play.", "tier": "silver", "slug": "fast-payout-casinos-nz", "website": "https://betpokies.co.nz/casinos/fast-payout?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Casino Zonder Registratie", "imageUrl": "https://images.opencollective.com/casino-zonder-registratie/d7119d9/logo.png", "description": "We testen elke maand tientallen casino’s en kiezen de beste uit voor Nederlandse spelers.", "tier": "silver", "slug": "casino-zonder-registratie", "website": "https://nl.trustpilot.com/review/scandicasinos.com?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "iDealeCasinos", "imageUrl": "https://images.opencollective.com/idealecasinoscom/2c96acd/logo.png", "description": null, "tier": "silver", "slug": "idealecasinoscom", "website": "https://idealecasinos.nl/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://x.com/idealecasinosnl?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "BettingTop10 Thailand", "imageUrl": "https://images.opencollective.com/bettingtop10-thailand/8e02b81/avatar.png", "description": null, "tier": "silver", "slug": "bettingtop10-thailand", "website": null, "twitter": null, "active": false }, { "name": "Ok Win", "imageUrl": "https://images.opencollective.com/ok-win-online/6152b72/avatar.png", "description": null, "tier": "silver", "slug": "ok-win-online", "website": "https://okwin-official.uk.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Real Instagram Followers", "imageUrl": "https://images.opencollective.com/purchase-real-instagram-followers/logo.png", "description": "Forum and Q&A platform", "tier": "silver", "slug": "purchase-real-instagram-followers", "website": "https://www.reddit.com/r/MarketingHelp/comments/1opxhk1/has_anyone_here_ever_tried_to_buy_instagram/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy real YouTube views", "imageUrl": "https://images.opencollective.com/buy-real-youtube-views-cheap/logo.png", "description": "Forum and Q&A platform", "tier": "silver", "slug": "buy-real-youtube-views-cheap", "website": "https://www.reddit.com/r/YouTubeCreators/comments/1p804mi/looking_for_trusted_platforms_to_buy_youtube/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "one x bet - Arabic betting site", "imageUrl": "https://images.opencollective.com/one-x-bet-arabic-betting-site/ca63263/avatar.png", "description": null, "tier": "silver", "slug": "one-x-bet-arabic-betting-site", "website": "https://xn----ymcbek6cvgvaq.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Online Casino Zonder Registratie", "imageUrl": "https://images.opencollective.com/no-account-casino/121571f/logo.png", "description": null, "tier": "silver", "slug": "no-account-casino", "website": "https://nl.trustpilot.com/review/zonderregistratiecasinos.com?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Online casino ohne OASIS", "imageUrl": "https://images.opencollective.com/onlinecasinoohneoasisme/a680d7f/logo.png", "description": "Collect reviews", "tier": "silver", "slug": "onlinecasinoohneoasisme", "website": "https://de.trustpilot.com/review/onlinecasinoohneoasis.me?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "iDEAL Online Casino", "imageUrl": "https://images.opencollective.com/trust-ideal-casino/7bf9e7b/logo.png", "description": null, "tier": "silver", "slug": "trust-ideal-casino", "website": "https://nl.trustpilot.com/review/idealecasinos.com?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Tiranga Game", "imageUrl": "https://images.opencollective.com/tirangagames/0f4257d/avatar.png", "description": null, "tier": "silver", "slug": "tirangagames", "website": "https://tirangagamesapp.co/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Fun88", "imageUrl": "https://images.opencollective.com/fun88-thailand/81e1909/avatar.png", "description": "บริษัทรับ�ทงบอลออนไลน์ที่ดีที่สุดในประเทศไทย", "tier": "silver", "slug": "fun88-thailand", "website": "https://www.fun88tho.com/th/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://x.com/FUN88fans?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": true }, { "name": "Fun88", "imageUrl": "https://images.opencollective.com/fun88-vietnam/192b9db/avatar.png", "description": "Nhà cái cá cược thể thao trực tuyến tốt nhất tại Việt Nam", "tier": "silver", "slug": "fun88-vietnam", "website": "https://www.fun88va.com/vn/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://x.com/Fun88officialVN?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": true }, { "name": "Transparencia en Casinos Online", "imageUrl": "https://images.opencollective.com/educatransparenciacl/88a027e/logo.png", "description": "Mi misión es la educación y transparencia en el mundo de los casinos online", "tier": "silver", "slug": "educatransparenciacl", "website": "https://www.educatransparencia.cl/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "bestecasinozondercruks", "imageUrl": "https://images.opencollective.com/bestecasinozondercruks/d79d3c3/logo.png", "description": null, "tier": "silver", "slug": "bestecasinozondercruks", "website": "https://nl.trustpilot.com/review/bestecasinozondercruks.online?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "SoftOrbits", "imageUrl": "https://images.opencollective.com/softorbits/a56c38a/avatar.png", "description": "SoftOrbits is an award winning software development company.", "tier": "silver", "slug": "softorbits", "website": "https://www.softorbits.net/ai-undresser/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "DiuWin", "imageUrl": "https://images.opencollective.com/diuwin_login/80bdf28/logo.png", "description": "Colour Prediction Game", "tier": "silver", "slug": "diuwin_login", "website": "https://diuvin.com/register.php?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "No Account Casino", "imageUrl": "https://images.opencollective.com/no-account-casino1/784af78/logo.png", "description": "No Account Casino for Swedes ", "tier": "silver", "slug": "no-account-casino1", "website": "https://se.trustpilot.com/review/noaccountcasinos.co.com?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "iDEAL Casinos", "imageUrl": "https://images.opencollective.com/ideal-casinos/0047cad/logo.png", "description": null, "tier": "silver", "slug": "ideal-casinos", "website": "https://nl.trustpilot.com/review/idealcasinos.co.com?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Instant Withdrawal Casinos", "imageUrl": "https://images.opencollective.com/instant-withdrawal-pokies/196c2fa/logo.png", "description": "marketing in australia", "tier": "silver", "slug": "instant-withdrawal-pokies", "website": "https://au.trustpilot.com/review/instantpayoutcasinos.org?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Aviator Game Online", "imageUrl": "https://images.opencollective.com/aviator-game-online/31a7922/logo.png", "description": null, "tier": "silver", "slug": "aviator-game-online", "website": "https://www.trustpilot.com/review/aviatorplay.top?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Plinko Game", "imageUrl": "https://images.opencollective.com/plinko-game/35bc4b1/logo.png", "description": null, "tier": "silver", "slug": "plinko-game", "website": "https://www.trustpilot.com/review/plinkoplay.top?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "SocialDawn", "imageUrl": "https://images.opencollective.com/socialdawn/963a6d4/logo.png", "description": "SocialDawn is a digital marketing brand focused on social media growth and performance campaigns. We help creators and businesses improve visibility and engagement through data-driven strategies and campaign management.", "tier": "silver", "slug": "socialdawn", "website": "https://www.socialdawn.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Paras Pikakasino", "imageUrl": "https://images.opencollective.com/paras-pikakasino/eaf9ccb/logo.png", "description": "Paras-pikakasino.com tarjoaa asiantuntevaa tietoa Suomen pikakasinoista ja niiden nopeista palveluista.", "tier": "silver", "slug": "paras-pikakasino", "website": "https://paras-pikakasino.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "SuperCloneReps", "imageUrl": "https://images.opencollective.com/superclonewatch/6e34974/avatar.png", "description": null, "tier": "silver", "slug": "superclonewatch", "website": "https://superclonereps.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "POLi Pay Casinos", "imageUrl": "https://images.opencollective.com/poli-pay-online-casinos/39bdd37/logo.png", "description": null, "tier": "silver", "slug": "poli-pay-online-casinos", "website": "https://nz.trustpilot.com/review/surfpokies.com?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "TopKasynoOnline PL", "imageUrl": "https://images.opencollective.com/topkasynoonline-pl/714b70a/logo.png", "description": null, "tier": "silver", "slug": "topkasynoonline-pl", "website": "https://pl.topkasynoonline.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Najlepsi Bukmacherzy", "imageUrl": "https://images.opencollective.com/netpositivepl/34f836f/avatar.png", "description": null, "tier": "silver", "slug": "netpositivepl", "website": "https://najlepsibukmacherzy.pl/ranking-legalnych-bukmacherow/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy.Fans", "imageUrl": "https://images.opencollective.com/buyfans/8122fa8/logo.png", "description": "Create the most suitable order for your target audience from Buy.Fans, the world's most reliable and most preferred social media service platform for 12 years, and strengthen your page!", "tier": "silver", "slug": "buyfans", "website": "https://buy.fans/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/BuyFansOfficial?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": true }, { "name": "Buy Instagram Followers", "imageUrl": "https://images.opencollective.com/buy-instagram-followers-9/d7c422a/logo.png", "description": null, "tier": "silver", "slug": "buy-instagram-followers-9", "website": null, "twitter": null, "active": false }, { "name": "iedcdubai", "imageUrl": "https://images.opencollective.com/iedcdubai-ae/62ea64d/avatar.png", "description": null, "tier": "silver", "slug": "iedcdubai-ae", "website": "https://www.iedcdubai.ae/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Movers to Puerto Rico", "imageUrl": "https://images.opencollective.com/moverspuertorico-com/cf979d8/logo.png", "description": "Trusted moving company for US to Puerto Rico relocations. Licensed, reliable & stress-free. Your belongings arrive on time and in perfect condition.", "tier": "silver", "slug": "moverspuertorico-com", "website": "https://www.trustpilot.com/review/moverspuertorico.com?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "apuesdeportivas.es", "imageUrl": "https://images.opencollective.com/apuesdeportivas-es/6ecf644/avatar.png", "description": null, "tier": "silver", "slug": "apuesdeportivas-es", "website": "https://apuesdeportivas.es/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Likescafe", "imageUrl": "https://images.opencollective.com/likescafe/9468493/logo.png", "description": null, "tier": "silver", "slug": "likescafe", "website": "https://likescafe.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "$10 Deposit Casinos", "imageUrl": "https://images.opencollective.com/dollar10-deposit-casinos/fb16eff/logo.png", "description": null, "tier": "silver", "slug": "dollar10-deposit-casinos", "website": "https://au.trustpilot.com/review/10dollardepositcasinos.org?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Paysafecard Casino", "imageUrl": "https://images.opencollective.com/paysafecard-casino/a11fd0f/logo.png", "description": "marketing in germany", "tier": "silver", "slug": "paysafecard-casino", "website": "https://de.trustpilot.com/review/paysafecardcasinos.org?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "N-iX Ltd", "imageUrl": "https://images.opencollective.com/n-ix-ltd/575a7a5/logo.png", "description": "N-iX is a global software solutions and engineering services company that helps world’s leading organizations turn challenges into lasting business value, operational efficiency, and revenue growth using advanced technology.", "tier": "silver", "slug": "n-ix-ltd", "website": "https://www.n-ix.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://x.com/n_ix_ukraine?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": true }, { "name": "At Socialboom, you can buy youtube comments, views, likes and subscribers quickly and safely. We are rated #1 best YouTube Service by Entrepreneur.", "imageUrl": "https://images.opencollective.com/buy-youtube-views-0b8c54e5/1b35a1d/avatar.png", "description": null, "tier": "silver", "slug": "buy-youtube-views-0b8c54e5", "website": "https://socialboom.io/buy-youtube-comments?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Instagram Followers from Billingsgazette, rated best by LATimes", "imageUrl": "https://images.opencollective.com/buy-instagram-followers-from-billingsgazette-rated-best-by-latimes/91af1a1/avatar.png", "description": null, "tier": "silver", "slug": "buy-instagram-followers-from-billingsgazette-rated-best-by-latimes", "website": "https://billingsgazette.com/exclusive/article_2ed65e9c-fe39-5733-ad90-231f600a18a4.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Сoinsmania", "imageUrl": "https://images.opencollective.com/coinsmania/d46d179/logo.png", "description": "Social Gaming Platform", "tier": "silver", "slug": "coinsmania", "website": "https://coinsmania.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Buy YouTube Views from Billingsgazette where we rate the best sites to buy views", "imageUrl": "https://images.opencollective.com/buy-youtube-views-from-billingsgazette-where-we-rate-the-best-sites-to-buy-views/76da760/avatar.png", "description": null, "tier": "silver", "slug": "buy-youtube-views-from-billingsgazette-where-we-rate-the-best-sites-to-buy-views", "website": "https://billingsgazette.com/exclusive/article_e9c7feb3-dc7e-5b4f-97d9-e9b4e101d37c.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Global Software Companies", "imageUrl": "https://images.opencollective.com/global-software-companies-gsc/e9c4727/logo.png", "description": "Global Software Companies is a US-based industry intelligence firm that scores and ranks software development companies worldwide using proprietary data analysis across 80+ countries.", "tier": "silver", "slug": "global-software-companies-gsc", "website": "https://www.globalsoftwarecompanies.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://x.com/gsc_x0?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": true }, { "name": "SocialWick - Buy Instagram Likes", "imageUrl": "https://images.opencollective.com/socialwick-buy-instagram-likes/6aa2f32/logo.png", "description": "SocialWick is one of the biggest social media stores. We offer the best Instagram likes in the market.", "tier": "silver", "slug": "socialwick-buy-instagram-likes", "website": "https://www.socialwick.com/instagram/likes?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "SocialWick - Buy Instagram Likes", "imageUrl": "https://images.opencollective.com/socialwick-buy-real-instagram-likes/876cbe4/logo.png", "description": "SocialWick offers real Instagram Likes, delivered within minutes. A simple and affordable way to push your posts and grow your account organically.", "tier": "silver", "slug": "socialwick-buy-real-instagram-likes", "website": "https://www.socialwick.com/instagram/likes?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Buy TikTok Followers - Best Sites rated by Omaha", "imageUrl": "https://images.opencollective.com/buy-tiktok-followers-best-sites-rated-by-omaha/da84fbb/avatar.png", "description": null, "tier": "silver", "slug": "buy-tiktok-followers-best-sites-rated-by-omaha", "website": "https://omaha.com/exclusive/article_febc60b1-eee7-5fb6-a6cb-4bd4af93273e.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy TikTok Followers Media Mister", "imageUrl": "https://images.opencollective.com/tiktok-followers-media-mister/84c1c05/logo.png", "description": null, "tier": "silver", "slug": "tiktok-followers-media-mister", "website": "https://www.mediamister.com/buy-tiktok-followers?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "BloodyCase", "imageUrl": "https://images.opencollective.com/bloodycase/180b70c/avatar.png", "description": null, "tier": "silver", "slug": "bloodycase", "website": null, "twitter": null, "active": false }, { "name": "Buy YouTube Subscribers, Views & Likes at Socialboom, the best site for YouTube Channel & Video Growth", "imageUrl": "https://images.opencollective.com/buy-youtube-subscribers-views-and-likes/4fc3743/avatar.png", "description": null, "tier": "silver", "slug": "buy-youtube-subscribers-views-and-likes", "website": "https://socialboom.io/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Buy facebook followers and likes", "imageUrl": "https://images.opencollective.com/buy-facebook-followers-and-likes/13a949c/avatar.png", "description": null, "tier": "silver", "slug": "buy-facebook-followers-and-likes", "website": "https://betterfollowers.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Buy Youtube subscribers", "imageUrl": "https://images.opencollective.com/buy-youtube-subscribers-muddy-river-sports/9f2934c/logo.png", "description": null, "tier": "silver", "slug": "buy-youtube-subscribers-muddy-river-sports", "website": "https://muddyriversports.com/sponsored-content/buy-real-youtube-subscribers/20260918080225/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Fameoria", "imageUrl": "https://images.opencollective.com/fameoria/2d02fff/logo.png", "description": "Growth tools and services for creators on Instagram, TikTok and YouTube, in English, Turkish and Arabic.", "tier": "silver", "slug": "fameoria", "website": "https://fameoria.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Code for Japan", "imageUrl": "/sponsors/code-for-japan.jpg", "description": null, "tier": "silver", "slug": "code-for-japan", "website": "https://www.code4japan.org/", "twitter": "https://x.com/CodeforJapan", "active": true }, { "name": "superluxuryreps", "imageUrl": "https://images.opencollective.com/superluxuryreps/378b62f/avatar.png", "description": "super luxury reps", "tier": "silver", "slug": "superluxuryreps", "website": "https://superluxuryreps.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true } ], "gold": [ { "name": "Stytch", "imageUrl": "https://images.opencollective.com/stytch/f84ce43/logo.png", "description": null, "tier": "gold", "slug": "stytch", "website": "https://stytch.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/stytchauth?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "Principal Financial Group", "imageUrl": "https://images.opencollective.com/principal/431e690/logo.png", "description": null, "tier": "gold", "slug": "principal", "website": "https://www.principal.com/about-us?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/Principal?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": true }, { "name": "Buy Instagram Followers Twicsy", "imageUrl": "https://images.opencollective.com/buy-instagram-followers-twicsy/b4c5d7f/logo.png", "description": "Buy real Instagram followers from Twicsy starting at only $2.97. Twicsy has been voted the best site to buy followers from the likes of US Magazine.", "tier": "gold", "slug": "buy-instagram-followers-twicsy", "website": "https://twicsy.com/buy-instagram-followers?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Descope", "imageUrl": "https://images.opencollective.com/descope/b53243e/logo.png", "description": "Hi, we're Descope! We are building something in the authentication space for app developers and can’t wait to place it in your hands.", "tier": "gold", "slug": "descope", "website": "https://www.descope.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/descopeinc?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": true }, { "name": "Buzzoid - Buy Instagram Followers", "imageUrl": "https://images.opencollective.com/buy-instagram-followers-buzzoid/c4cf0b6/avatar.png", "description": "At Buzzoid, you can buy Instagram followers quickly, safely, and easily with just a few clicks. Rated world's #1 IG service since 2012.", "tier": "gold", "slug": "buy-instagram-followers-buzzoid", "website": null, "twitter": null, "active": false }, { "name": "Buzzoid - Buy Instagram Followers", "imageUrl": "https://images.opencollective.com/buzzoid-buy-instagram-followers/56a09fe/logo.png", "description": "At Buzzoid, you can buy Instagram followers quickly, safely, and easily with just a few clicks. Rated world's #1 IG service since 2012.", "tier": "gold", "slug": "buzzoid-buy-instagram-followers", "website": "https://buzzoid.com/buy-instagram-followers/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Famety - Buy Instagram Followers", "imageUrl": "https://images.opencollective.com/famety-buy-instagram-followers/37314f4/logo.png", "description": "At Famety, you can grow your social media following quickly, safely, and easily with just a few clicks. Rated the world’s #1 social media service since 2013.", "tier": "gold", "slug": "famety-buy-instagram-followers", "website": "https://www.famety.net/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Poprey - Buy Instagram Likes", "imageUrl": "https://images.opencollective.com/instagram-likes/2a72a03/avatar.png", "description": "Buy Instagram Likes", "tier": "gold", "slug": "instagram-likes", "website": "https://poprey.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "Tubidy", "imageUrl": "https://images.opencollective.com/musicza/d703107/logo.png", "description": "Tubidy: Free Music and Video Downloads | MP3, MP4", "tier": "gold", "slug": "musicza", "website": "https://musicza.co.za/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy Youtube Subscribers", "imageUrl": "https://images.opencollective.com/youtube-subscribers-ssmarket/5b126c4/logo.png", "description": "SS Market offers professional social media services that rapidly increase your YouTube subscriber count, elevating your channel to a powerful position.", "tier": "gold", "slug": "youtube-subscribers-ssmarket", "website": "https://ssmarket.net/buy-youtube-subscribers?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Smikky", "imageUrl": "https://images.opencollective.com/smikky/27df6a3/logo.png", "description": "Smikky is a social media growth service where you can buy TikTok followers, YouTube subscribers, Instagram followers, likes, and views to boost your online presence. ", "tier": "gold", "slug": "smikky", "website": "https://tokmax.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Buy YouTube Views and Likes", "imageUrl": "https://images.opencollective.com/buy-youtube-views-likes-reddit/9ca82f3/logo.png", "description": "Recommend trusted and best sites to buy YouTube subscribers, likes and views", "tier": "gold", "slug": "buy-youtube-views-likes-reddit", "website": "https://buffalonews.com/exclusive/article_e849bb68-a3cb-5f09-aec4-da95d2ed2c85.html?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "smmpanelserver", "imageUrl": "https://images.opencollective.com/smmpanelserver123/7810726/avatar.png", "description": "smmpanelserver - Best and Cheapest Smm Panel", "tier": "gold", "slug": "smmpanelserver123", "website": "https://affmaven.com/smmpanelserver-review/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "polskie kasyno online", "imageUrl": "https://images.opencollective.com/polskie-kasyno-online/c25e456/avatar.png", "description": "kasyno online polska", "tier": "gold", "slug": "polskie-kasyno-online", "website": "https://kasyno.us.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": false }, { "name": "Requestly", "imageUrl": "https://images.opencollective.com/requestly/433bbf1/logo.png", "description": "A lightweight open-source API Development, Testing & Mocking platform", "tier": "gold", "slug": "requestly", "website": "https://requestly.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://x.com/requestlyio?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "Fun88", "imageUrl": "https://images.opencollective.com/fun88-official/bf2843c/logo.png", "description": "Best online sports betting and casino company.", "tier": "gold", "slug": "fun88-official", "website": "https://global.fun88.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "RxDB", "imageUrl": "https://images.opencollective.com/rxdbjs/2aec389/logo.png", "description": null, "tier": "gold", "slug": "rxdbjs", "website": "https://rxdb.info/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship&utm_content=logo", "twitter": "https://twitter.com/rxdbjs?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": true }, { "name": "JBO", "imageUrl": "https://images.opencollective.com/jbo-vietnam/5e66f3a/avatar.png", "description": "JBO Vietnam là thương hiệu giải trí trực tuyến nổi bật tại Việt Nam, cung cấp cá cược thể thao, esports, casino trực tuyến và nhi�u trò chơi hấp dẫn khác.", "tier": "gold", "slug": "jbo-vietnam", "website": "https://www.jbo88vk.com/vn/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "JBO", "imageUrl": "https://images.opencollective.com/jbo-thailand/d17e84f/avatar.png", "description": "JBO Thailand คือ�พลตฟอร์มความบันเทิงออนไลน์ครบวงจร ให้บริ�าร�ารเดิมพัน�ีฬา อีสปอร์ต คาสิโนสด �ละเ�มยอดนิยม ผ่านระบบทันสมัย ใช้งานง่าย รองรับทุ�อุป�รณ์ พร้อมมอบประสบ�ารณ์ที่มีคุณภาพ ปลอดภัย �ละน่าเชื่อถือสำหรับผู้ใช้งานชาวไทย.", "tier": "gold", "slug": "jbo-thailand", "website": "https://www.jbo579.com/th/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": null, "active": true }, { "name": "ScrapingBee", "imageUrl": "https://images.opencollective.com/scrapingbee/6d5dc95/logo.png", "description": null, "tier": "gold", "slug": "scrapingbee", "website": "https://www.scrapingbee.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "twitter": "https://twitter.com/scrapingbee?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship", "active": false }, { "name": "Sweepico Promo Code", "imageUrl": "https://images.opencollective.com/sweepico-promo-code/4dd517a/logo.png", "description": "Social Gaming Platform", "tier": "gold", "slug": "sweepico-promo-code", "website": null, "twitter": null, "active": true }, { "name": "HeroDevs", "imageUrl": "/sponsors/herodevs.svg", "description": "Security and long-term support for end-of-life open source software.", "tier": "gold", "slug": "herodevs", "website": "https://www.herodevs.com/", "twitter": null, "active": true } ], "platinum": [ { "name": "Hopper Security", "imageUrl": "https://images.opencollective.com/hopper-security/c4f7de2/avatar.png", "description": null, "tier": "platinum", "slug": "hopper-security", "website": null, "twitter": null, "active": false } ] }axios-axios-aa54a5e/docs/es/000077500000000000000000000000001526660636600160245ustar00rootroot00000000000000axios-axios-aa54a5e/docs/es/index.md000066400000000000000000000236161526660636600174650ustar00rootroot00000000000000--- # https://vitepress.dev/reference/default-theme-home-page layout: home search: false hero: name: 'axios docs' text: 'axios es un cliente HTTP simple para el navegador y Node.js' image: dark: /logo.svg light: /logo-light.svg alt: axios actions: - theme: brand text: Comenzar link: /es/pages/getting-started/first-steps - theme: alt text: Referencia de API link: /es/pages/advanced/api-reference features: - title: Implementación sencilla details: Comenzar con axios es tan simple como una sola línea de código. Las solicitudes API básicas se pueden realizar en 2 líneas de código. - title: Interceptores potentes details: Nuestro innovador sistema de interceptores le permite controlar el ciclo de vida de solicitudes y respuestas. Puede modificar solicitudes, respuestas y errores. - title: Soporte para TypeScript details: axios declara tipos y tiene soporte completo para TypeScript. Esto significa que puede usar axios con confianza en sus proyectos TypeScript. ---

Patrocinadores

{{ sponsor.name }}

{{ capitalizeFirstLetter(sponsor.tier) }}
axios-axios-aa54a5e/docs/es/pages/000077500000000000000000000000001526660636600171235ustar00rootroot00000000000000axios-axios-aa54a5e/docs/es/pages/advanced/000077500000000000000000000000001526660636600206705ustar00rootroot00000000000000axios-axios-aa54a5e/docs/es/pages/advanced/adapters.md000066400000000000000000000076701526660636600230270ustar00rootroot00000000000000# Adaptadores Los adaptadores te permiten personalizar la forma en que axios maneja los datos de la solicitud. De forma predeterminada, axios usa una lista de prioridad ordenada de `['xhr', 'http', 'fetch']` y selecciona el primer adaptador que sea compatible con el entorno actual. En la práctica, esto significa que `xhr` se usa en los navegadores, `http` en Node.js y `fetch` en entornos donde ninguno de los dos está disponible (como Cloudflare Workers o Deno). Escribir tu propio adaptador te permite controlar completamente cómo axios realiza una solicitud y procesa la respuesta — útil para pruebas, transportes personalizados o entornos no estándar. ## Adaptadores integrados Puedes seleccionar un adaptador integrado por nombre usando la opción de configuración `adapter`: ```js // Use the fetch adapter const instance = axios.create({ adapter: "fetch" }); // Use the XHR adapter (browser default) const instance = axios.create({ adapter: "xhr" }); // Use the HTTP adapter (Node.js default) const instance = axios.create({ adapter: "http" }); ``` También puedes pasar un arreglo de nombres de adaptadores. axios usará el primero que sea compatible con el entorno actual: ```js const instance = axios.create({ adapter: ["fetch", "xhr", "http"] }); ``` Para más detalles sobre el adaptador `fetch`, consulta la página del [Adaptador Fetch](/pages/advanced/fetch-adapter). ## Crear un adaptador personalizado Para crear un adaptador personalizado, escribe una función que acepte un objeto `config` y devuelva una Promise que se resuelva en un objeto de respuesta de axios válido. ```js import axios from "axios"; import { settle } from "axios/unsafe/core/settle.js"; function myAdapter(config) { /** * At this point: * - config has been merged with defaults * - request transformers have run * - request interceptors have run * * The adapter is now responsible for making the request * and returning a valid response object. */ return new Promise((resolve, reject) => { // Perform your custom request logic here. // This example uses the native fetch API as a starting point. fetch(config.url, { method: config.method?.toUpperCase() ?? "GET", headers: config.headers?.toJSON() ?? {}, body: config.data, signal: config.signal, }) .then(async (fetchResponse) => { const responseData = await fetchResponse.text(); const response = { data: responseData, status: fetchResponse.status, statusText: fetchResponse.statusText, headers: Object.fromEntries(fetchResponse.headers.entries()), config, request: null, }; // settle resolves or rejects the promise based on the HTTP status settle(resolve, reject, response); /** * After this point: * - response transformers will run * - response interceptors will run */ }) .catch(reject); }); } const instance = axios.create({ adapter: myAdapter }); ``` Los adaptadores TypeScript pueden conservar tanto los datos de solicitud como los parámetros de consulta en la configuración de la respuesta usando los genéricos correspondientes: ```ts import type { AxiosPromise, InternalAxiosRequestConfig, } from "axios"; interface RequestBody { includeArchived: boolean; } interface SearchParams { query: string; } interface SearchResponse { results: string[]; } const searchAdapter = ( config: InternalAxiosRequestConfig ): AxiosPromise => Promise.resolve({ data: { results: [] }, status: 200, statusText: "OK", headers: {}, config, }); ``` ::: tip El helper `settle` resuelve la Promise para códigos de estado 2xx y la rechaza para todo lo demás, siguiendo el comportamiento predeterminado de axios. Si deseas una validación de estado personalizada, usa la opción de configuración `validateStatus`. ::: axios-axios-aa54a5e/docs/es/pages/advanced/api-reference.md000066400000000000000000000340141526660636600237210ustar00rootroot00000000000000# Referencia de la API A continuación se presenta una lista de todas las funciones y clases disponibles en el paquete axios. Estas funciones pueden usarse e importarse en tu proyecto. Todas ellas están protegidas por nuestro renovado compromiso de seguir el versionado semántico. Esto significa que puedes confiar en que estas funciones y clases permanecerán estables y sin cambios en futuras versiones, salvo que se realice un cambio de versión mayor. ## Instancia La instancia `axios` es el objeto principal que usarás para realizar solicitudes HTTP. Es una función de fábrica que crea una nueva instancia de la clase `Axios`. La instancia `axios` cuenta con una serie de métodos para hacer solicitudes HTTP, los cuales están documentados en la [sección de alias de solicitud](/pages/advanced/request-method-aliases) de la documentación. ## Tipos de solicitud de TypeScript Los tipos públicos de solicitud usan genéricos separados para los datos de solicitud y los parámetros de consulta: ```ts AxiosRequestConfig RawAxiosRequestConfig InternalAxiosRequestConfig AxiosDefaults CreateAxiosDefaults AxiosResponse AxiosPromise AxiosError CanceledError ``` `D` es el tipo del cuerpo de la solicitud y `P` el tipo de los parámetros de consulta. `AxiosResponse`, `AxiosPromise`, los errores, los valores predeterminados, las instancias invocables, los alias de solicitud, los adaptadores y `mergeConfig()` conservan ambos en la configuración. Los serializadores de parámetros personalizados reciben el mismo `P`. Los métodos de solicitud usan el orden genérico ``, con `P` al final para mantener compatibles los argumentos genéricos explícitos existentes. Sin un tipo de respuesta personalizado `R`, el `AxiosResponse` predeterminado conserva `D` y `P` en `response.config`; un `R` explícito sigue controlando el valor resuelto. Los genéricos de datos y parámetros usan `any` por defecto para mantener la compatibilidad. ## Clases ### `Axios` La clase `Axios` es la clase principal que usarás para realizar solicitudes HTTP. Es una función de fábrica que crea una nueva instancia de la clase `Axios`. La clase `Axios` cuenta con una serie de métodos para hacer solicitudes HTTP, los cuales están documentados en la [sección de alias de solicitud](/pages/advanced/request-method-aliases) de la documentación. #### `constructor` Crea una nueva instancia de la clase `Axios`. El constructor acepta un objeto de configuración opcional como argumento. ```ts constructor(instanceConfig?: AxiosRequestConfig); ``` #### `request` Gestiona la invocación de la solicitud y la resolución de la respuesta. Este es el método principal que usarás para hacer solicitudes HTTP. Acepta un objeto de configuración como argumento y devuelve una Promise que se resuelve en el objeto de respuesta. ```ts request(config: AxiosRequestConfig): Promise; ``` ### `CancelToken` La clase `CancelToken` estaba basada en la propuesta `tc39/proposal-cancelable-promises`. Se usaba para crear un token que permitiera cancelar una solicitud HTTP. La clase `CancelToken` está ahora obsoleta en favor de la API `AbortController`. A partir de la versión 0.22.0, la clase `CancelToken` está obsoleta y será eliminada en una versión futura. Se recomienda usar la API `AbortController` en su lugar. La clase se exporta principalmente por compatibilidad con versiones anteriores y será eliminada en una versión futura. Desaconsejamos fuertemente su uso en proyectos nuevos; los helpers de interoperabilidad heredados que aparecen abajo se listan solo para código existente. Los métodos heredados siguen tipados para integraciones existentes: ```ts subscribe(listener: (cancel: Cancel | any) => void): void; unsubscribe(listener: (cancel: Cancel | any) => void): void; toAbortSignal(): AbortSignal; ``` ## Funciones ### `AxiosError` La clase `AxiosError` es una clase de error que se lanza cuando una solicitud HTTP falla. Extiende la clase `Error` y añade propiedades adicionales al objeto de error. #### `constructor` Crea una nueva instancia de la clase `AxiosError`. El constructor acepta opcionalmente un mensaje, un código, una configuración, una solicitud y una respuesta como argumentos. ```ts constructor(message?: string, code?: string, config?: InternalAxiosRequestConfig, request?: any, response?: AxiosResponse); ``` #### `properties` La clase `AxiosError` proporciona las siguientes propiedades: ```ts // Instancia de configuración. config?: InternalAxiosRequestConfig; // Código de error. code?: string; // Instancia de solicitud. request?: any; // Instancia de respuesta. response?: AxiosResponse; // Booleano que indica si el error es un `AxiosError`. isAxiosError: boolean; // Código de estado HTTP del error. status?: number; // Método auxiliar para convertir el error a un objeto JSON. toJSON: () => object; // Causa del error. cause?: Error; ``` ### `AxiosHeaders` La clase `AxiosHeaders` es una clase de utilidad que se usa para gestionar encabezados HTTP. Provee métodos para manipular encabezados, como añadir, eliminar y obtener encabezados. Aquí solo se documentan los métodos principales. Para una lista completa de métodos, consulta el archivo de declaración de tipos. #### `constructor` Crea una nueva instancia de la clase `AxiosHeaders`. El constructor acepta opcionalmente un objeto de encabezados como argumento. ```ts constructor(headers?: RawAxiosHeaders | AxiosHeaders | string); ``` #### `set` Agrega un encabezado al objeto de encabezados. Los nombres de encabezado vacíos o compuestos solo por espacios se ignoran. ```ts set(headerName?: string, value?: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders; set(headers?: RawAxiosHeaders | AxiosHeaders | string, rewrite?: boolean): AxiosHeaders; set(headers?: Iterable<[string, AxiosHeaderValue]>, rewrite?: boolean): AxiosHeaders; ``` #### `get` Obtiene un encabezado del objeto de encabezados. ```ts get(headerName: string, parser: typeof AxiosHeaders.parseParameters): AxiosHeaderParameters; get(headerName: string, parser: RegExp): RegExpExecArray | null; get(headerName: string, matcher?: true | AxiosHeaderParser): AxiosHeaderValue; ``` Pasa `AxiosHeaders.parseParameters` para analizar parámetros HTTP normalizados en un mapa reforzado con prototipo nulo: ```js const headers = new AxiosHeaders({ "Content-Type": 'multipart/form-data; boundary="a,b"', }); console.log({ ...headers.get("Content-Type", AxiosHeaders.parseParameters), }); // { boundary: "a,b" } ``` Los nombres de parámetros no distinguen mayúsculas de minúsculas. El analizador elimina delimitadores de cadenas entre comillas, decodifica comillas y barras invertidas escapadas, conserva comas y puntos y coma dentro de valores entre comillas y recorta solo el espacio en blanco opcional de RFC alrededor de valores sin comillas. Omite `__proto__`, `constructor` y `prototype`. `get(name, true)` sigue siendo el tokenizador heredado. #### `has` Verifica si un encabezado existe en el objeto de encabezados. ```ts has(header: string, matcher?: AxiosHeaderMatcher): boolean; ``` #### `delete` Elimina un encabezado del objeto de encabezados. ```ts delete(header: string | string[], matcher?: AxiosHeaderMatcher): boolean; ``` #### `clear` Elimina todos los encabezados del objeto de encabezados. ```ts clear(matcher?: AxiosHeaderMatcher): boolean; ``` #### `normalize` Normaliza el objeto de encabezados. ```ts normalize(format: boolean): AxiosHeaders; ``` #### `concat` Concatena objetos de encabezados. ```ts concat(...targets: Array): AxiosHeaders; ``` #### `toJSON` Convierte el objeto de encabezados a un objeto JSON. ```ts toJSON(asStrings: true): Record; toJSON(asStrings?: false): Record; ``` #### `toString` Devuelve los encabezados como un bloque HTTP sin CRLF, con un par `nombre: valor` por línea. ```ts toString(): string; ``` ### `CanceledError` La clase `CanceledError` es una clase de error que se lanza cuando se cancela una solicitud HTTP. Extiende la clase `AxiosError`. ```ts constructor(message?: string, config?: InternalAxiosRequestConfig, request?: any); __CANCEL__?: boolean; ``` ### `Cancel` La clase `Cancel` es un alias de la clase `CanceledError`. Se exporta por compatibilidad con versiones anteriores y será eliminada en una versión futura. ```ts Cancel: typeof CanceledError; ``` ### `isCancel` Una función que verifica si un error es un `CanceledError`. Es útil para distinguir cancelaciones intencionales de errores inesperados. ```ts isCancel(value: any): value is CanceledError; ``` ```js import axios from 'axios'; const controller = new AbortController(); axios.get('/api/data', { signal: controller.signal }).catch((error) => { if (axios.isCancel(error)) { console.log('Request was cancelled:', error.message); } else { console.error('Unexpected error:', error); } }); controller.abort('User navigated away'); ``` ### `isAxiosError` Una función que verifica si un error es un `AxiosError`. Úsala en bloques `catch` para acceder de forma segura a las propiedades específicas de axios como `error.response` y `error.config`. ```ts isAxiosError(value: any): value is AxiosError; ``` ```js import axios from 'axios'; try { await axios.get('/api/resource'); } catch (error) { if (axios.isAxiosError(error)) { // error.response, error.config, error.code are all available console.error('HTTP error', error.response?.status, error.message); } else { // A non-axios error (e.g. a programming mistake) throw error; } } ``` ### `all` La función `all` es una función de utilidad que acepta un arreglo de promises y devuelve una única Promise que se resuelve cuando todas las promises del arreglo se han resuelto. La función `all` está ahora obsoleta en favor del método `Promise.all`. Se recomienda usar el método `Promise.all` en su lugar. A partir de la versión 0.22.0, la función `all` está obsoleta y será eliminada en una versión futura. Se recomienda usar el método `Promise.all` en su lugar. ### `spread` La función `spread` es una función de utilidad que puede usarse para distribuir un arreglo de argumentos en una llamada a función. Es útil cuando tienes un arreglo de argumentos que deseas pasar a una función que acepta múltiples argumentos. ```ts spread(callback: (...args: T[]) => R): (array: T[]) => R; ``` ### `toFormData` Convierte un objeto JavaScript plano (o anidado) a una instancia de `FormData`. Es útil cuando deseas construir datos de formulario multipart de forma programática a partir de un objeto. ```ts toFormData(sourceObj: object, formData?: FormData, options?: FormSerializerOptions): FormData; ``` ```js import { toFormData } from 'axios'; const data = { name: 'Jay', avatar: fileBlob }; const form = toFormData(data); // form is now a FormData instance ready to post await axios.post('/api/users', form); ``` ### `formToJSON` Convierte una instancia de `FormData` de vuelta a un objeto JavaScript plano. Es útil para leer datos de formulario en un formato estructurado. Solo la notación con puntos y corchetes es estructural: `.`, `[` y `]` dividen rutas, mientras que `-`, espacios, `+`, `*` y `&` permanecen en claves literales. `foo.bar` y `foo[bar]` crean objetos anidados, y `foo[]` crea un array. ```ts formToJSON(form: FormData): object; ``` ```js import { formToJSON } from 'axios'; const form = new FormData(); form.append('user-name', 'johndoe'); form.append('user.name', 'john'); const obj = formToJSON(form); console.log(obj); // { "user-name": "johndoe", user: { name: "john" } } ``` ### `getAdapter` Resuelve y devuelve una función de adaptador por nombre o pasando un arreglo de nombres candidatos. axios usa esto internamente para seleccionar el mejor adaptador disponible para el entorno actual. ```ts getAdapter(adapters: string | string[]): AxiosAdapter; ``` ```js import { getAdapter } from 'axios'; // Get the fetch adapter explicitly const fetchAdapter = getAdapter('fetch'); // Get the best available adapter from a priority list const adapter = getAdapter(['fetch', 'xhr', 'http']); ``` ### `mergeConfig` Combina dos objetos de configuración de axios, aplicando la misma estrategia de fusión profunda que axios usa internamente al combinar los valores predeterminados con las opciones por solicitud. Los valores posteriores tienen precedencia. ```ts mergeConfig( config1: AxiosRequestConfig, config2: AxiosRequestConfig ): AxiosRequestConfig; ``` ```js import { mergeConfig } from 'axios'; const base = { baseURL: 'https://api.example.com', timeout: 5000 }; const override = { timeout: 10000, headers: { 'X-Custom': 'value' } }; const merged = mergeConfig(base, override); // { baseURL: "https://api.example.com", timeout: 10000, headers: { "X-Custom": "value" } } ``` ## Constantes ### `HttpStatusCode` Un objeto que contiene una lista de códigos de estado HTTP como constantes con nombre. Úsalo para escribir condicionales legibles en lugar de números directos. ```js import axios, { HttpStatusCode } from 'axios'; try { const response = await axios.get('/api/resource'); } catch (error) { if (axios.isAxiosError(error)) { if (error.response?.status === HttpStatusCode.NotFound) { console.error('Resource not found'); } else if (error.response?.status === HttpStatusCode.Unauthorized) { console.error('Authentication required'); } } } ``` ## Miscelánea ### `VERSION` La versión actual del paquete `axios`. Es una cadena que representa el número de versión del paquete. Se actualiza con cada nueva versión del paquete. axios-axios-aa54a5e/docs/es/pages/advanced/authentication.md000066400000000000000000000111571526660636600242360ustar00rootroot00000000000000# Autenticación La mayoría de las APIs requieren alguna forma de autenticación. Esta página cubre los patrones más comunes para adjuntar credenciales a las solicitudes de axios. ## Tokens Bearer (JWT) El enfoque más común es adjuntar un JWT en el encabezado `Authorization`. La forma más limpia de hacerlo es a través de un interceptor de solicitud en tu instancia de axios, de manera que el token se lea de nuevo en cada solicitud: ```js import axios from "axios"; const api = axios.create({ baseURL: "https://api.example.com" }); api.interceptors.request.use((config) => { const token = localStorage.getItem("access_token"); if (token) { config.headers.set("Authorization", `Bearer ${token}`); } return config; }); ``` ## Autenticación HTTP Basic Para APIs que usan autenticación HTTP Basic, pasa la opción `auth`. axios codificará las credenciales y establecerá el encabezado `Authorization` automáticamente: ```js const response = await axios.get("https://api.example.com/data", { auth: { username: "myUser", password: "myPassword", }, }); ``` Si no se proporciona `auth`, los adaptadores HTTP de Node.js y fetch también pueden obtener credenciales Basic desde la URL de la solicitud, por ejemplo `https://myUser:myPassword@api.example.com/data`. Las credenciales codificadas con porcentaje en la URL se decodifican antes de generar el encabezado `Authorization`. Para código nuevo, prefiere la opción explícita `auth`; tiene prioridad sobre las credenciales incluidas en la URL. ::: tip Para tokens Bearer y claves de API, usa un encabezado `Authorization` personalizado en lugar de la opción `auth` — `auth` es solo para HTTP Basic. ::: ## Claves de API Las claves de API generalmente se pasan como un encabezado o como un parámetro de consulta, dependiendo de lo que espere la API: ```js // As a header const api = axios.create({ baseURL: "https://api.example.com", headers: { "X-API-Key": "your-api-key-here" }, }); // As a query parameter const response = await axios.get("https://api.example.com/data", { params: { apiKey: "your-api-key-here" }, }); ``` ## Renovación de token Cuando los tokens de acceso expiran, es necesario renovarlos de forma silenciosa y reintentar la solicitud fallida. Un interceptor de respuesta es el lugar adecuado para implementar esto: ```js import axios from "axios"; const api = axios.create({ baseURL: "https://api.example.com" }); // Track whether a refresh is already in progress to avoid parallel refresh calls let isRefreshing = false; let failedQueue = []; const processQueue = (error, token = null) => { failedQueue.forEach((prom) => { if (error) { prom.reject(error); } else { prom.resolve(token); } }); failedQueue = []; }; api.interceptors.response.use( (response) => response, async (error) => { const originalRequest = error.config; if (error.response?.status === 401 && !originalRequest._retry) { if (isRefreshing) { // Queue the request until the refresh completes return new Promise((resolve, reject) => { failedQueue.push({ resolve, reject }); }) .then((token) => { originalRequest.headers["Authorization"] = `Bearer ${token}`; return api(originalRequest); }) .catch((err) => Promise.reject(err)); } originalRequest._retry = true; isRefreshing = true; try { const { data } = await axios.post("/auth/refresh", { refreshToken: localStorage.getItem("refresh_token"), }); const newToken = data.access_token; localStorage.setItem("access_token", newToken); api.defaults.headers.common["Authorization"] = `Bearer ${newToken}`; processQueue(null, newToken); return api(originalRequest); } catch (refreshError) { processQueue(refreshError, null); // Redirect to login or emit an event localStorage.removeItem("access_token"); window.location.href = "/login"; return Promise.reject(refreshError); } finally { isRefreshing = false; } } return Promise.reject(error); } ); ``` ## Autenticación basada en cookies Para APIs basadas en sesiones que dependen de cookies, establece `withCredentials: true` para incluir cookies en solicitudes de origen cruzado: ```js const api = axios.create({ baseURL: "https://api.example.com", withCredentials: true, // send cookies with every request }); ``` ::: warning `withCredentials: true` requiere que el servidor responda con `Access-Control-Allow-Credentials: true` y un valor específico (no comodín) en `Access-Control-Allow-Origin`. ::: axios-axios-aa54a5e/docs/es/pages/advanced/cancellation.md000066400000000000000000000065741526660636600236620ustar00rootroot00000000000000# Cancelación A partir de la versión v0.22.0, Axios es compatible con AbortController para cancelar solicitudes de forma limpia. Esta característica está disponible en el navegador y en Node.js cuando se usa una versión de Axios que admite AbortController. Para cancelar una solicitud, debes crear una instancia de `AbortController` y pasar su `signal` a la opción `signal` de la solicitud. ```js const controller = new AbortController(); axios .get('/foo/bar', { signal: controller.signal, }) .then(function (response) { //... }); // cancel the request controller.abort(); ``` ## CancelToken También puedes usar la API `CancelToken` para cancelar solicitudes. Esta API está obsoleta y será eliminada en la próxima versión mayor. Se recomienda usar `AbortController` en su lugar. Puedes crear un token de cancelación usando la fábrica `CancelToken.source` como se muestra a continuación: ```js const CancelToken = axios.CancelToken; const source = CancelToken.source(); axios .get('/user/12345', { cancelToken: source.token, }) .catch(function (thrown) { if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // handle error } }); axios.post( '/user/12345', { name: 'new name', }, { cancelToken: source.token, } ); // cancel the request (the message parameter is optional) source.cancel('Operation canceled by the user.'); ``` También puedes crear un token de cancelación pasando una función ejecutora al constructor de `CancelToken`: ```js const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { // An executor function receives a cancel function as a parameter cancel = c; }), }); // cancel the request cancel(); ``` `CancelToken` también expone helpers de bajo nivel para integraciones heredadas: ```js const source = axios.CancelToken.source(); const listener = (cancel) => { console.log(cancel.message); }; source.token.subscribe(listener); const signal = source.token.toAbortSignal(); // Pasa `signal` a APIs que acepten AbortSignal. source.cancel('Operation canceled by the user.'); source.token.unsubscribe(listener); ``` Las solicitudes canceladas se rechazan con `axios.CanceledError`. La exportación heredada `axios.Cancel` es un alias de `axios.CanceledError`, y los errores de cancelación incluyen `__CANCEL__` para compatibilidad con `axios.isCancel`. En TypeScript, `isCancel()` conserva los tipos de los datos de respuesta, los datos de solicitud y los parámetros de consulta al acotar un error `unknown`: ```ts interface SearchResponse { results: string[]; } interface RequestBody { includeArchived: boolean; } interface SearchParams { query: string; } try { await axios.get("/search"); } catch (error) { if (axios.isCancel(error)) { error.response?.data; // SearchResponse | undefined error.config?.data; // RequestBody | undefined error.config?.params; // SearchParams | undefined } } ``` Puedes cancelar varias solicitudes con el mismo token de cancelación o controlador de cancelación. Si un token de cancelación ya fue cancelado en el momento en que se inicia una solicitud de Axios, la solicitud se cancela inmediatamente, sin intentar realizar ninguna solicitud real. axios-axios-aa54a5e/docs/es/pages/advanced/config-defaults.md000066400000000000000000000061071526660636600242700ustar00rootroot00000000000000# Valores predeterminados de configuración axios te permite especificar valores predeterminados de configuración que se aplicarán a cada solicitud. Puedes definir valores predeterminados para `baseURL`, `headers`, `timeout` y otras propiedades. A continuación se muestra un ejemplo de cómo usar los valores predeterminados de configuración: ```js axios.defaults.baseURL = "https://jsonplaceholder.typicode.com/posts"; axios.defaults.headers.common["Authorization"] = AUTH_TOKEN; axios.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded"; ``` ::: warning Los encabezados globales se envían a todos los hosts Si tu aplicación se comunica con más de un dominio, establecer `axios.defaults.headers.common["Authorization"]` enviará el token a **todos** ellos, incluidas APIs de terceros que tal vez no controles. Usa una [instancia personalizada](#valores-predeterminados-personalizados-por-instancia) con un `baseURL` acotado para cualquier cliente que transporte credenciales. ::: ## Valores predeterminados personalizados por instancia Las instancias de axios se declaran con sus propios valores predeterminados al ser creadas. Estos valores pueden sobreescribirse estableciendo la propiedad `defaults` en la instancia. A continuación se muestra un ejemplo de cómo usar valores predeterminados personalizados por instancia: ```js var instance = axios.create({ baseURL: "https://jsonplaceholder.typicode.com/posts", timeout: 1000, headers: { Authorization: "foobar" }, }); instance.defaults.headers.common["Authorization"] = AUTH_TOKEN; ``` ## Orden de precedencia de la configuración La configuración se combinará con un orden de precedencia. El orden es el siguiente: primero se establecen los valores predeterminados de la librería, luego las propiedades predeterminadas de la instancia y, finalmente, el argumento de configuración de la solicitud. A continuación se muestra un ejemplo del orden de precedencia. Algunas opciones son específicas de cada solicitud y solo se toman de la configuración de la solicitud. `data` es una de ellas: axios no hereda ni fusiona en profundidad cuerpos de solicitud desde los valores predeterminados globales o de instancia. Si todas las solicitudes necesitan campos compartidos en el cuerpo, agrégalos con un interceptor de solicitud o `transformRequest`, y limita cuidadosamente ese comportamiento para no enviar valores sensibles al endpoint equivocado. Primero, vamos a crear una instancia con los valores predeterminados que proporciona la librería. En este punto, el valor de configuración de timeout es `0`, que es el valor predeterminado de la librería. ```js const instance = axios.create(); ``` Ahora sobreescribiremos el timeout predeterminado de la instancia a `2500` milisegundos. A partir de ahora, todas las solicitudes que usen esta instancia esperarán 2.5 segundos antes de expirar. ```js instance.defaults.timeout = 2500; ``` Finalmente, haremos una solicitud con un timeout de `5000` milisegundos. Esta solicitud esperará 5 segundos antes de expirar. ```js instance.get("/longRequest", { timeout: 5000, }); ``` axios-axios-aa54a5e/docs/es/pages/advanced/create-an-instance.md000066400000000000000000000056551526660636600246660ustar00rootroot00000000000000# Crear una instancia `axios.create()` te permite crear una instancia de axios preconfigurada. La instancia comparte la misma API de solicitud y respuesta que el objeto `axios` predeterminado, pero utiliza la configuración que proporciones como base para cada solicitud. Esta es la forma recomendada de usar axios en cualquier aplicación que conste de más de un solo archivo. ```ts import axios from "axios"; const instance = axios.create({ baseURL: "https://api.example.com", timeout: 5000, headers: { "X-Custom-Header": "foobar" }, }); ``` El método `create` acepta el objeto completo de [Configuración de solicitud](/pages/advanced/request-config). Luego puedes usar la instancia igual que el objeto axios predeterminado: ```js const response = await instance.get("/users/1"); ``` ## ¿Por qué usar una instancia? ### URL base por servicio En la mayoría de las aplicaciones se interactúa con más de una API. Crear una instancia separada por servicio evita repetir la URL base en cada llamada: ```js const githubApi = axios.create({ baseURL: "https://api.github.com" }); const internalApi = axios.create({ baseURL: "https://api.internal.example.com" }); const { data: repos } = await githubApi.get("/users/axios/repos"); const { data: users } = await internalApi.get("/users"); ``` ### Encabezados de autenticación compartidos Adjunta un token de autenticación a cada solicitud de una instancia sin afectar a las demás: ```js const authApi = axios.create({ baseURL: "https://api.example.com", headers: { Authorization: `Bearer ${getToken()}`, }, }); ``` ### Tiempos de espera y reintentos por servicio Los distintos servicios tienen características de fiabilidad diferentes. Define un tiempo de espera corto para servicios en tiempo real y uno más largo para trabajos por lotes: ```js const realtimeApi = axios.create({ baseURL: "https://realtime.example.com", timeout: 2000 }); const batchApi = axios.create({ baseURL: "https://batch.example.com", timeout: 60000 }); ``` ### Interceptores aislados Los interceptores añadidos a una instancia solo se aplican a esa instancia, manteniendo tus responsabilidades separadas: ```js const loggingApi = axios.create({ baseURL: "https://api.example.com" }); loggingApi.interceptors.request.use((config) => { console.log(`→ ${config.method?.toUpperCase()} ${config.url}`); return config; }); ``` ## Sobreescribir los valores predeterminados por solicitud La configuración pasada en el momento de la solicitud siempre tiene prioridad sobre los valores predeterminados de la instancia: ```js const api = axios.create({ timeout: 5000 }); // This specific request uses a 30-second timeout instead await api.get("/slow-endpoint", { timeout: 30000 }); ``` ::: tip Los valores predeterminados de la instancia también pueden cambiarse después de su creación escribiendo en `instance.defaults`: ```js instance.defaults.headers.common["Authorization"] = `Bearer ${newToken}`; ``` ::: axios-axios-aa54a5e/docs/es/pages/advanced/error-handling.md000066400000000000000000000213261526660636600241310ustar00rootroot00000000000000# Manejo de errores axios puede lanzar muchos tipos diferentes de errores. Algunos de estos errores son causados por el propio axios, mientras que otros son causados por el servidor o el cliente. La siguiente tabla lista la estructura general del error lanzado: | Propiedad | Definición | | --------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | message | Un resumen rápido del mensaje de error y el estado con el que falló. | | name | Define el origen del error. Para axios, siempre será un `AxiosError`. | | stack | Proporciona el seguimiento de la pila (stack trace) del error. | | config | Un objeto de configuración de axios con configuraciones específicas de la instancia definidas por el usuario al momento de realizar la solicitud. | | code | Representa un error identificado por axios. La tabla a continuación lista definiciones específicas para errores internos de axios. | | status | Código de estado HTTP de la respuesta. Consulta [aquí](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) para conocer los significados comunes de los códigos de estado HTTP. | A continuación se lista una relación de posibles errores identificados por axios: | Código | Definición | | ------------------------- | --------------------------------------------------------------------------------------------- | | ERR_BAD_OPTION_VALUE | Valor inválido o no compatible proporcionado en la configuración de axios. | | ERR_BAD_OPTION | Opción inválida proporcionada en la configuración de axios. | | ECONNABORTED | Generalmente indica que la solicitud ha expirado (salvo que `transitional.clarifyTimeoutError` esté activado) o fue abortada por el navegador o un complemento del mismo. | | ETIMEDOUT | La solicitud expiró al superar el límite de tiempo predeterminado de axios. Se debe activar `transitional.clarifyTimeoutError` en `true`, de lo contrario se lanzará un error genérico `ECONNABORTED`. | | ERR_NETWORK | Problema relacionado con la red. En el navegador, este error también puede ser causado por una violación de la política [CORS](https://developer.mozilla.org/ru/docs/Web/HTTP/Guides/CORS) o [Mixed Content](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content). El navegador no permite que el código JS aclare la razón real del error por motivos de seguridad; por eso, revisa la consola. | | ERR_FR_TOO_MANY_REDIRECTS | La solicitud fue redirigida demasiadas veces; supera el número máximo de redirecciones especificado en la configuración de axios. | | ERR_DEPRECATED | Se usó una característica o método obsoleto en axios. | | ERR_BAD_RESPONSE | La respuesta no puede ser analizada correctamente o tiene un formato inesperado. Generalmente relacionado con una respuesta con código de estado `5xx`. | | ERR_BAD_REQUEST | La solicitud tiene un formato inesperado o le faltan parámetros requeridos. Generalmente relacionado con una respuesta con código de estado `4xx`. | | ERR_CANCELED | La característica o método fue cancelado explícitamente por el usuario usando un AbortSignal (o un CancelToken). | | ERR_NOT_SUPPORT | Característica o método no compatible en el entorno actual de axios. | | ERR_INVALID_URL | URL inválida proporcionada para la solicitud de axios. | | ERR_FORM_DATA_DEPTH_EXCEEDED | Un objeto excede el `maxDepth` configurado al serializar `params` o datos de formulario. El límite predeterminado es de 100 niveles. Consulta [`paramsSerializer`](/pages/advanced/request-config#paramsserializer) y [`formSerializer`](/pages/advanced/request-config#formserializer). | ## Manejo de errores El comportamiento predeterminado de axios es rechazar la Promise si la solicitud falla. Sin embargo, también puedes capturar el error y manejarlo según lo consideres apropiado. A continuación se muestra un ejemplo de cómo capturar un error: ```js axios.get("/user/12345").catch(function (error) { if (error.response) { // The request was made and the server responded with a status code // that falls out of the range of 2xx console.log(error.response.data); console.log(error.response.status); console.log(error.response.headers); } else if (error.request) { // The request was made but no response was received // `error.request` is an instance of XMLHttpRequest in the browser and an instance of // http.ClientRequest in node.js console.log(error.request); } else { // Something happened in setting up the request that triggered an Error console.log("Error", error.message); } console.log(error.config); }); ``` ## URLs HTTP(S) malformadas axios rechaza una `url` o `baseURL` de solicitud `http:` o `https:` que omita `//` después del protocolo. Por ejemplo, `https:example.com` y `https:/example.com` se rechazan con un `AxiosError` cuyo código es `ERR_INVALID_URL`, en lugar de ser normalizadas silenciosamente por el analizador de URL del navegador o de Node.js. Usa una URL bien formada como `https://example.com`. El mensaje identifica la URL incorrecta, por ejemplo: ```text Invalid URL "https:example.com": missing "//" after protocol ``` Este comportamiento impide que las URL malformadas eludan `baseURL` o las listas de direcciones permitidas. La URL incluida en el mensaje se normaliza eliminando caracteres de control y oculta las credenciales, los valores de parámetros de consulta y el contenido del fragmento. Conserva el esquema, host, ruta y nombres de parámetros para que la solicitud siga siendo identificable. Esta ocultación del mensaje es incondicional porque `AxiosError.message` siempre forma parte de `toJSON()`; la opción `redact` de la configuración no puede limpiar un mensaje ya creado. Usando la opción de configuración `validateStatus`, puedes sobreescribir la condición predeterminada (status >= 200 && status < 300) y definir los códigos HTTP que deben lanzar un error. ```js axios.get("/user/12345", { validateStatus: function (status) { return status < 500; // Resolve only if the status code is less than 500 }, }); ``` Usando el método `toJSON`, puedes obtener un objeto con más información sobre el error. ```js axios.get("/user/12345").catch(function (error) { console.log(error.toJSON()); }); ``` Para evitar registrar secretos desde `error.config`, pasa un arreglo `redact` en la configuración de la solicitud. Las claves de configuración coincidentes se enmascaran de forma insensible a mayúsculas/minúsculas a cualquier profundidad cuando se llama a `AxiosError#toJSON()`. ```js axios.get("/user/12345", { headers: { Authorization: "Bearer token" }, redact: ["authorization"] }).catch(function (error) { console.log(error.toJSON().config.headers.Authorization); // [REDACTED ****] }); ``` ## Manejo de tiempos de espera Cuando una solicitud excede su `timeout` configurado, axios rechaza con `ECONNABORTED` por defecto. Establece `transitional.clarifyTimeoutError: true` para recibir `ETIMEDOUT` en su lugar, lo que facilita distinguir los errores de tiempo de espera de otros abortos. ```js async function fetchWithTimeout() { try { const response = await axios.get("https://example.com/data", { timeout: 5000, // 5 segundos transitional: { // establecer en true si prefieres ETIMEDOUT en lugar de ECONNABORTED clarifyTimeoutError: true, }, }); console.log("Response:", response.data); } catch (error) { if (axios.isAxiosError(error)) { if (error.code === "ECONNABORTED" || error.code === "ETIMEDOUT") { console.error("Request timed out. Please try again."); return; } console.error("Axios error:", error.message); return; } console.error("Unexpected error:", error); } } ``` ::: tip Establece siempre un `timeout` en producción Sin uno, una solicitud detenida puede colgarse indefinidamente. Consulta [`timeout`](/pages/advanced/request-config#timeout) y [`transitional.clarifyTimeoutError`](/pages/advanced/request-config#transitional) para las opciones de configuración correspondientes. ::: axios-axios-aa54a5e/docs/es/pages/advanced/fetch-adapter.md000066400000000000000000000074151526660636600237300ustar00rootroot00000000000000# Adaptador Fetch El adaptador `fetch` es un nuevo adaptador que introdujimos a partir de la versión 1.7.0. Proporciona una forma de usar axios con la API `fetch`, dándote lo mejor de ambos mundos. De forma predeterminada, `fetch` se usará si los adaptadores `xhr` y `http` no están disponibles en la compilación o no son compatibles con el entorno. Para usarlo de forma predeterminada, debe seleccionarse explícitamente estableciendo la opción `adapter` en `fetch` al crear una instancia de axios. ```js import axios from 'axios'; const instance = axios.create({ adapter: 'fetch', }); ``` El adaptador admite la misma funcionalidad que el adaptador `xhr`, incluyendo la captura del progreso de carga y descarga. También admite tipos de respuesta adicionales como `stream` y `formdata` (si el entorno lo soporta). Cuando `auth` se omite, el adaptador fetch puede leer credenciales de autenticación HTTP Basic desde la URL de la solicitud, por ejemplo `https://user:pass@example.com`. Las credenciales de URL codificadas con porcentaje se decodifican antes de generar el encabezado `Authorization`, y `auth` tiene prioridad sobre las credenciales incluidas en la URL. ## Fetch personalizado A partir de `v1.12.0`, puedes personalizar el adaptador fetch para que use una función `fetch` personalizada en lugar de la global del entorno. Puedes pasar una función `fetch` personalizada, y los constructores `Request` y `Response` a través de la opción de configuración `env`. Esto es útil cuando trabajas con entornos personalizados o frameworks de aplicación que proporcionan su propia implementación de `fetch`. ::: info Al usar una función `fetch` personalizada, es posible que también necesites proporcionar constructores `Request` y `Response` correspondientes. Si los omites, se usarán los constructores globales. Si tu `fetch` personalizado es incompatible con los globales, pasa `null` para deshabilitarlos. **Nota:** Establecer `Request` y `Response` en `null` hará imposible que el adaptador fetch capture el progreso de carga y descarga. ::: ### Ejemplo básico ```js import customFetchFunction from 'customFetchModule'; const instance = axios.create({ adapter: 'fetch', onDownloadProgress(e) { console.log('downloadProgress', e); }, env: { fetch: customFetchFunction, Request: null, // null -> disable the constructor Response: null, }, }); ``` ### Usando con Tauri [Tauri](https://tauri.app/plugin/http-client/) proporciona una función `fetch` de plataforma que omite las restricciones CORS del navegador para las solicitudes realizadas desde la capa nativa. El ejemplo a continuación muestra una configuración mínima para usar axios dentro de una aplicación Tauri con ese fetch personalizado. ```js import { fetch } from '@tauri-apps/plugin-http'; import axios from 'axios'; const instance = axios.create({ adapter: 'fetch', onDownloadProgress(e) { console.log('downloadProgress', e); }, env: { fetch, }, }); const { data } = await instance.get('https://google.com'); ``` ### Usando con SvelteKit [SvelteKit](https://svelte.dev/docs/kit/web-standards#Fetch-APIs) proporciona una implementación personalizada de `fetch` para las funciones `load` del lado del servidor que gestiona el reenvío de cookies y URLs relativas. Dado que su `fetch` es incompatible con la API estándar de `URL`, axios debe configurarse para usarlo explícitamente, y los constructores globales `Request` y `Response` deben deshabilitarse. ```js export async function load({ fetch }) { const { data: post } = await axios.get('https://jsonplaceholder.typicode.com/posts/1', { adapter: 'fetch', env: { fetch, Request: null, Response: null, }, }); return { post }; } ``` axios-axios-aa54a5e/docs/es/pages/advanced/file-posting.md000066400000000000000000000062021526660636600236120ustar00rootroot00000000000000# Publicación de archivos axios facilita la subida de archivos. Usa `postForm` o `FormData` cuando necesites subidas en formato `multipart/form-data`. ## Archivo único (navegador) Pasa un objeto `File` directamente como valor de campo — axios lo detectará y usará automáticamente el tipo de contenido correcto: ```js await axios.postForm("https://httpbin.org/post", { description: "My profile photo", file: document.querySelector("#fileInput").files[0], }); ``` ## Múltiples archivos (navegador) Pasa un `FileList` para subir todos los archivos seleccionados a la vez. Todos se enviarán bajo el mismo nombre de campo (`files[]`): ```js await axios.postForm( "https://httpbin.org/post", document.querySelector("#fileInput").files ); ``` También puedes pasar el `FileList` (o un arreglo de objetos `File`) explícitamente bajo un nombre de campo personalizado añadiendo `[]` a la clave: ```js await axios.postForm("https://httpbin.org/post", { "files[]": document.querySelector("#fileInput").files, }); ``` Para usar nombres de campo distintos para cada archivo, construye un objeto `FormData` manualmente: ```js const formData = new FormData(); formData.append("avatar", avatarFile); formData.append("cover", coverFile); await axios.post("https://httpbin.org/post", formData); ``` ## Seguimiento del progreso de la carga (navegador) Usa el callback `onUploadProgress` para mostrar una barra de progreso o un porcentaje a tus usuarios: ```js await axios.postForm("https://httpbin.org/post", { file: document.querySelector("#fileInput").files[0], }, { onUploadProgress: (progressEvent) => { const percent = Math.round( (progressEvent.loaded * 100) / progressEvent.total ); console.log(`Upload progress: ${percent}%`); }, }); ``` Consulta [Captura de progreso](/pages/advanced/progress-capturing) para ver la lista completa de campos disponibles en el evento de progreso. ## Archivos en Node.js En Node.js, usa `fs.createReadStream` para subir un archivo desde el sistema de archivos sin cargarlo completamente en memoria: ```js import fs from "fs"; import FormData from "form-data"; import axios from "axios"; const form = new FormData(); form.append("file", fs.createReadStream("/path/to/file.jpg")); form.append("description", "My uploaded file"); await axios.post("https://httpbin.org/post", form); ``` ::: tip El paquete npm `form-data` es necesario en entornos Node.js para crear objetos `FormData`. En versiones modernas de Node.js (v18+), el `FormData` global está disponible de forma nativa. ::: ## Subir un Buffer (Node.js) También puedes subir un `Buffer` en memoria directamente: ```js const buffer = Buffer.from("Hello, world!"); const form = new FormData(); form.append("file", buffer, { filename: "hello.txt", contentType: "text/plain", knownLength: buffer.length, }); await axios.post("https://httpbin.org/post", form); ``` ::: warning La captura del progreso de carga de `FormData` no está disponible actualmente en entornos Node.js. ::: ::: danger Al subir un stream legible en Node.js, establece `maxRedirects: 0` para evitar que el paquete `follow-redirects` almacene todo el stream en memoria RAM. ::: axios-axios-aa54a5e/docs/es/pages/advanced/header-methods.md000066400000000000000000000221611526660636600241050ustar00rootroot00000000000000# Métodos de encabezados Con la introducción de la nueva clase `AxiosHeaders`, Axios ofrece un conjunto de métodos para manipular encabezados. Estos métodos se usan para establecer, obtener y eliminar encabezados de una forma más conveniente que manipular directamente el objeto de encabezados. ## Constructor `new AxiosHeaders(headers?)` El constructor de la clase `AxiosHeaders` acepta un objeto opcional con encabezados para inicializar la instancia. El objeto de encabezados puede contener cualquier número de encabezados, y las claves son insensibles a mayúsculas y minúsculas. ```js constructor(headers?: RawAxiosHeaders | AxiosHeaders | string); ``` Por conveniencia, puedes pasar una cadena de texto con encabezados separados por un carácter de nueva línea. Los encabezados se analizan y se añaden a la instancia. ```js const headers = new AxiosHeaders(` Host: www.bing.com User-Agent: curl/7.54.0 Accept: */*`); console.log(headers); // Object [AxiosHeaders] { // host: 'www.bing.com', // 'user-agent': 'curl/7.54.0', // accept: '*/*' // } ``` ## Set (Establecer) El método `set` se usa para establecer encabezados en la instancia de `AxiosHeaders`. El método puede ser llamado con un nombre de encabezado y un valor únicos, un objeto con múltiples encabezados, o una cadena de texto con encabezados separados por una nueva línea. El método también acepta un parámetro opcional `rewrite` que controla el comportamiento al establecer el encabezado. ```js set(headerName, value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher); set(headerName, value, rewrite?: (this: AxiosHeaders, value: string, name: string) => boolean); set(headers?: RawAxiosHeaders | AxiosHeaders | string, rewrite?: boolean); set(headers?: Iterable<[string, AxiosHeaderValue]>, rewrite?: boolean); ``` El argumento `rewrite` controla el comportamiento de sobreescritura: - `false` - no sobreescribir si el valor del encabezado ya está definido (es decir, no es `undefined`) - `undefined` (predeterminado) - sobreescribir el encabezado a menos que su valor esté establecido en `false` - `true` - sobreescribir siempre La opción también puede aceptar una función definida por el usuario que determina si el valor debe ser sobreescrito o no. La función recibe el valor actual, el nombre del encabezado y el objeto de encabezados como argumentos. Los nombres de encabezado vacíos o compuestos solo por espacios se ignoran. Se aceptan pares clave/valor iterables, como un `Map`: ```js const headers = new AxiosHeaders(); headers.set( new Map([ ['X-Trace-Id', 'abc123'], ['Accept', 'application/json'], ]) ); ``` `AxiosHeaders` conserva el formato de la primera clave coincidente que encuentra. Puedes usar esto para preservar el formato específico de un encabezado inicializando una clave con `undefined` y luego estableciendo los valores posteriormente. Consulta [Preservar el formato de un encabezado específico](/pages/advanced/headers#preserving-a-specific-header-case). ## Get (Obtener) El método `get` se usa para recuperar el valor de un encabezado. El método puede ser llamado con un nombre de encabezado único, un matcher opcional o un parser. El matcher tiene valor predeterminado `true`. El parser puede ser una expresión regular que se usa para extraer el valor del encabezado. ```js get(headerName: string, parser: typeof AxiosHeaders.parseParameters): AxiosHeaderParameters; get(headerName: string, parser: RegExp): RegExpExecArray | null; get(headerName: string, matcher?: true | AxiosHeaderParser): AxiosHeaderValue; ``` A continuación se muestra un ejemplo de algunos de los posibles usos del método `get`: ```js const headers = new AxiosHeaders({ 'Content-Type': 'multipart/form-data; boundary=Asrf456BGe4h', }); console.log(headers.get('Content-Type')); // multipart/form-data; boundary=Asrf456BGe4h console.log(headers.get('Content-Type', true)); // parse key-value pairs from a string separated with \s,;= delimiters: // [Object: null prototype] { // 'multipart/form-data': undefined, // boundary: 'Asrf456BGe4h' // } const quotedHeaders = new AxiosHeaders({ 'Content-Type': 'multipart/form-data; boundary="a,b"', }); console.log({ ...quotedHeaders.get('Content-Type', AxiosHeaders.parseParameters), }); // { boundary: 'a,b' } console.log( headers.get('Content-Type', (value, name, headers) => { return String(value).replace(/a/g, 'ZZZ'); }) ); // multipZZZrt/form-dZZZtZZZ; boundZZZry=Asrf456BGe4h console.log(headers.get('Content-Type', /boundary=(\w+)/)?.[0]); // boundary=Asrf456BGe4h ``` `AxiosHeaders.parseParameters` es un analizador opcional para valores normalizados de parámetros HTTP. Devuelve un mapa con prototipo nulo y nombres de parámetros sin distinción entre mayúsculas y minúsculas. Elimina los delimitadores de cadenas entre comillas, decodifica pares escapados de comillas dobles y barras invertidas, y conserva las comas o los puntos y coma dentro de valores entre comillas. En valores sin comillas solo elimina el espacio en blanco opcional de RFC (espacio y tabulación horizontal) alrededor del valor. El analizador omite las claves inseguras para materializar objetos (`__proto__`, `constructor` y `prototype`). Pasar `true` sigue usando el tokenizador heredado y conserva su salida por compatibilidad. ## Has (Verificar existencia) El método `has` se usa para verificar si un encabezado existe en la instancia de `AxiosHeaders`. El método puede ser llamado con un nombre de encabezado único y un matcher opcional. ```js has(header: string, matcher?: AxiosHeaderMatcher): boolean; ``` ::: info Devuelve `true` si el encabezado está definido (tiene un valor que no es `undefined`). ::: ## Delete (Eliminar) El método `delete` se usa para eliminar un encabezado de la instancia de `AxiosHeaders`. El método puede ser llamado con un nombre de encabezado único y un matcher opcional. ```js delete(header: string | string[], matcher?: AxiosHeaderMatcher): boolean; ``` ::: info Devuelve `true` si al menos un encabezado fue eliminado. ::: ## Clear (Limpiar) El método `clear` se usa para eliminar todos los encabezados de la instancia de `AxiosHeaders` si no se pasa ningún argumento. Si se pasa un matcher, solo se eliminan los encabezados que coincidan con él; en este caso, el matcher se compara contra el nombre del encabezado en lugar del valor. ```js clear(matcher?: AxiosHeaderMatcher): boolean; ``` ::: info Devuelve `true` si al menos un encabezado fue eliminado. ::: ## Normalize (Normalizar) Si el objeto de encabezados fue modificado directamente, puede generar duplicados con el mismo nombre pero en diferentes formatos. Este método normaliza el objeto de encabezados combinando claves duplicadas en una sola. Axios usa este método internamente después de llamar a cada interceptor. Establece `format` en `true` para convertir los nombres de los encabezados a minúsculas y capitalizar las letras iniciales (`cOntEnt-type` => `Content-Type`) o en `false` para mantener el formato original. ```js const headers = new AxiosHeaders({ foo: '1', }); headers.Foo = '2'; headers.FOO = '3'; console.log(headers.toJSON()); // [Object: null prototype] { foo: '1', Foo: '2', FOO: '3' } console.log(headers.normalize().toJSON()); // [Object: null prototype] { foo: '3' } console.log(headers.normalize(true).toJSON()); // [Object: null prototype] { Foo: '3' } ``` ::: info Devuelve `this` para encadenamiento. ::: ## Concat (Concatenar) Combina la instancia con los objetivos en una nueva instancia de `AxiosHeaders`. Si el objetivo es una cadena de texto, se analizará como encabezados HTTP en formato RAW. Si el objetivo es una instancia de `AxiosHeaders`, se combinará con la instancia actual. Esto es útil para formatos predefinidos de mayúsculas/minúsculas al componer encabezados. Por ejemplo: ```js const headers = AxiosHeaders.concat( { 'content-type': undefined }, { 'Content-Type': 'application/octet-stream' } ); ``` ```js concat(...targets: Array): AxiosHeaders; ``` ::: info Devuelve una nueva instancia de `AxiosHeaders`. ::: ## toJSON Resuelve todos los valores de encabezados internos en un nuevo objeto de prototipo nulo. Establece `asStrings` en `true` para resolver los arreglos como una cadena que contiene todos los elementos, separados por comas. ```js toJSON(asStrings: true): Record; toJSON(asStrings?: false): Record; ``` ## toString Devuelve los encabezados como un bloque HTTP sin CRLF, con un par `nombre: valor` por línea. ```js toString(): string; ``` ## From (Desde) Devuelve una nueva instancia de `AxiosHeaders` creada a partir de los encabezados en bruto pasados, o simplemente devuelve el objeto de encabezados dado si ya es una instancia de `AxiosHeaders`. ```js from(thing?: AxiosHeaders | RawAxiosHeaders | string): AxiosHeaders; ``` ## Atajos Los siguientes atajos están disponibles: - `setContentType`, `getContentType`, `hasContentType` - `setContentLength`, `getContentLength`, `hasContentLength` - `setAccept`, `getAccept`, `hasAccept` - `setUserAgent`, `getUserAgent`, `hasUserAgent` - `setContentEncoding`, `getContentEncoding`, `hasContentEncoding` axios-axios-aa54a5e/docs/es/pages/advanced/headers.md000066400000000000000000000126141526660636600226310ustar00rootroot00000000000000# Encabezados Axios expone su propia clase `AxiosHeaders` para manipular encabezados usando una API similar a Map que garantiza claves insensibles a mayúsculas y minúsculas. Esta clase es usada internamente por Axios para gestionar encabezados, pero también está disponible para el usuario por conveniencia. Aunque los encabezados HTTP no son sensibles a mayúsculas y minúsculas, Axios conservará el formato original del encabezado por razones estéticas y como solución alternativa cuando los servidores consideran erróneamente el caso del encabezado. La forma antigua de manipular directamente el objeto de encabezados sigue disponible, pero está obsoleta y no se recomienda para uso futuro. ## Trabajar con encabezados La instancia del objeto `AxiosHeaders` puede contener diferentes tipos de valores internos que controlan la lógica de configuración y combinación. El objeto de encabezados final es obtenido por Axios mediante la llamada al método `toJSON`. El objeto `AxiosHeaders` también es iterable, por lo que puedes usarlo en bucles o convertirlo en un arreglo u objeto. Los valores de los encabezados pueden ser de los siguientes tipos: - `string` - valor de cadena de texto normal que se enviará al servidor - `null` - omitir el encabezado al convertir a JSON - `false` - omitir el encabezado al convertir a JSON; indica además que el método `set` debe ser llamado con la opción `rewrite` en `true` para sobreescribir este valor (Axios usa esto internamente para permitir que los usuarios opten por no instalar ciertos encabezados como `User-Agent` o `Content-Type`) - `undefined` - el valor no está definido ::: warning El valor del encabezado se considera definido si no es `undefined`. ::: El objeto de encabezados siempre se inicializa dentro de los interceptores y transformadores, como se muestra en el siguiente ejemplo: ```js axios.interceptors.request.use((request: InternalAxiosRequestConfig) => { request.headers.set("My-header", "value"); request.headers.set({ "My-set-header1": "my-set-value1", "My-set-header2": "my-set-value2", }); // Disable subsequent setting of this header by Axios request.headers.set("User-Agent", false); request.headers.setContentType("text/plain"); // Direct access like this is deprecated request.headers["My-set-header2"] = "newValue"; return request; }); ``` Puedes iterar sobre un `AxiosHeaders` usando cualquier método iterable, como un bucle for-of, forEach o el operador spread: ```js const headers = new AxiosHeaders({ foo: '1', bar: '2', baz: '3', }); for (const [header, value] of headers) { console.log(header, value); } // foo 1 // bar 2 // baz 3 ``` ## Establecer encabezados en una solicitud El lugar más común para establecer encabezados es la opción `headers` en la configuración de tu solicitud o en la configuración de la instancia: ```js // On a single request await axios.get('/api/data', { headers: { 'Accept-Language': 'en-US', 'X-Request-ID': 'abc123', }, }); // On an instance (applied to every request) const api = axios.create({ headers: { 'X-App-Version': '2.0.0', }, }); ``` ## Preservar el formato de un encabezado específico Los nombres de encabezados de Axios son insensibles a mayúsculas y minúsculas, pero `AxiosHeaders` conserva el formato de la primera clave coincidente que encuentra. Si necesitas un formato específico para un servidor con comportamiento sensible a mayúsculas y minúsculas no estándar, define un formato predeterminado en `defaults` y luego establece los valores normalmente. ```js const api = axios.create(); api.defaults.headers.common = { 'content-type': undefined, accept: undefined, }; await api.put(url, data, { headers: { 'Content-Type': 'application/octet-stream', Accept: 'application/json', }, }); ``` También puedes hacerlo directamente con `AxiosHeaders` al componer encabezados: ```js import axios, { AxiosHeaders } from 'axios'; const headers = AxiosHeaders.concat( { 'content-type': undefined }, { 'Content-Type': 'application/octet-stream' } ); await axios.put(url, data, { headers }); ``` ## Establecer encabezados en un interceptor Los interceptores son el lugar adecuado para adjuntar encabezados dinámicos como tokens de autenticación, ya que el token puede no estar disponible cuando la instancia se crea por primera vez: ```js api.interceptors.request.use((config) => { const token = getAuthToken(); // read at request time config.headers.set('Authorization', `Bearer ${token}`); return config; }); ``` ## Leer encabezados de respuesta Los encabezados de respuesta están disponibles en `response.headers` como una instancia de `AxiosHeaders`. Todos los nombres de encabezados están en minúsculas: ```js const response = await axios.get('/api/data'); console.log(response.headers['content-type']); // application/json; charset=utf-8 console.log(response.headers.get('x-request-id')); // abc123 ``` ## Eliminar un encabezado predeterminado Para optar por no incluir un encabezado que axios establece por defecto (como `Content-Type` o `User-Agent`), establece su valor en `false`: ```js await axios.post('/api/data', payload, { headers: { 'Content-Type': false, // let the browser set it automatically (e.g. for FormData) }, }); ``` Para más detalles sobre la API completa de métodos de `AxiosHeaders`, consulta la página de [Métodos de encabezados](/pages/advanced/header-methods). axios-axios-aa54a5e/docs/es/pages/advanced/html-form-processing.md000066400000000000000000000035751526660636600253030ustar00rootroot00000000000000# Envío de formularios HTML (navegador) También puedes enviar un formulario directamente desde un elemento de formulario HTML. Esto es útil cuando tienes un formulario en tu página y deseas enviarlo sin escribir código JavaScript adicional. ```js await axios.postForm('https://httpbin.org/post', document.querySelector('#htmlForm')); ``` Los objetos `FormData` y `HTMLForm` también pueden enviarse como `JSON` estableciendo explícitamente el encabezado `Content-Type` en `application/json`: ```js await axios.post('https://httpbin.org/post', document.querySelector('#htmlForm'), { headers: { 'Content-Type': 'application/json', }, }); ``` Un ejemplo de un formulario válido que puede ser enviado con el código anterior es: ```html
``` El formulario anterior se enviará como: ```json { "foo": "1", "deep": { "prop": "2" }, "deep prop spaced": "3", "baz": ["4", "5"], "user": { "age": "value2" } } ``` ::: tip Notación de rutas en nombres de campo Solo la notación con puntos y corchetes crea rutas de propiedades. Los demás caracteres —incluidos espacios, `-`, `+`, `*` y `&`— permanecen en nombres de campo literales. Por ejemplo, `deep.prop` crea una ruta anidada, mientras que `deep prop spaced` permanece como una clave de nivel superior. ::: ::: warning Actualmente no se admite el envío de Blobs/Files como JSON (base64). ::: axios-axios-aa54a5e/docs/es/pages/advanced/http2.md000066400000000000000000000052351526660636600222600ustar00rootroot00000000000000# HTTP2 El soporte experimental de HTTP/2 fue añadido al adaptador `http` en la versión `1.13.0`. Solo está disponible en entornos Node.js. ## Uso básico Usa la opción `httpVersion` para seleccionar la versión del protocolo para una solicitud. Establecerla en `2` habilita HTTP/2. ```js const { data, headers, status } = await axios.post( "https://httpbin.org/post", form, { httpVersion: 2, }, ); ``` ## `http2Options` Las opciones nativas adicionales para la llamada interna `session.request()` pueden pasarse a través del objeto de configuración `http2Options`. Esto también incluye el parámetro personalizado `sessionTimeout`, que controla cuánto tiempo (en milisegundos) una sesión HTTP/2 inactiva se mantiene viva antes de cerrarse. Su valor predeterminado es `1000ms`. ```js { httpVersion: 2, http2Options: { rejectUnauthorized: false, // accept self-signed certificates (dev only) sessionTimeout: 5000, // keep idle session alive for 5 seconds }, } ``` ::: warning El soporte de HTTP/2 es actualmente experimental. La API puede cambiar en futuras versiones menores o de parche. ::: ::: warning Las redirecciones no se admiten sobre HTTP/2 El adaptador HTTP/2 actualmente no sigue redirecciones. Si una solicitud emitida con `httpVersion: 2` recibe una respuesta `3xx`, la redirección no se sigue automáticamente. Maneja estas respuestas manualmente o permanece en HTTP/1.x para endpoints que dependen de redirecciones. ::: ## Ejemplo completo El ejemplo a continuación envía una solicitud POST con `multipart/form-data` sobre HTTP/2 y rastrea tanto el progreso de carga como el de descarga. ```js const form = new FormData(); form.append("foo", "123"); const { data, headers, status } = await axios.post( "https://httpbin.org/post", form, { httpVersion: 2, http2Options: { // rejectUnauthorized: false, // sessionTimeout: 1000 }, onUploadProgress(e) { console.log("upload progress", e); }, onDownloadProgress(e) { console.log("download progress", e); }, responseType: "arraybuffer", }, ); ``` ## Referencia de configuración | Opción | Tipo | Predeterminado | Descripción | |---|---|---|---| | `httpVersion` | `number` | `1` | Versión del protocolo HTTP a usar. Establece en `2` para habilitar HTTP/2. | | `http2Options.sessionTimeout` | `number` | `1000` | Tiempo en milisegundos antes de que una sesión HTTP/2 inactiva se cierre. | Todas las demás opciones nativas de `session.request()` compatibles con el módulo `http2` integrado de Node.js también pueden pasarse dentro de `http2Options`. axios-axios-aa54a5e/docs/es/pages/advanced/interceptors.md000066400000000000000000000170551526660636600237430ustar00rootroot00000000000000# Interceptores Los interceptores son un mecanismo poderoso que puede usarse para interceptar y modificar solicitudes y respuestas HTTP. Son muy similares al middleware en Express.js. Un interceptor es una función que se ejecuta antes de enviar una solicitud y antes de recibir una respuesta. Los interceptores son útiles para una variedad de tareas como el registro de eventos (logging), la modificación de encabezados de solicitud y la modificación de la respuesta. El uso básico de los interceptores es el siguiente: ```js // Add a request interceptor axios.interceptors.request.use( function (config) { // Do something before request is sent return config; }, function (error) { // Do something with request error return Promise.reject(error); } ); // Add a response interceptor axios.interceptors.response.use( function (response) { // Any status code that lie within the range of 2xx cause this function to trigger // Do something with response data return response; }, function (error) { // Any status codes that falls outside the range of 2xx cause this function to trigger // Do something with response error return Promise.reject(error); } ); ``` ## Eliminar interceptores Puedes eliminar cualquier interceptor usando el método `eject` sobre el interceptor que deseas eliminar. También puedes eliminar todos los interceptores llamando al método `clear` sobre el objeto `axios.interceptors`. A continuación se muestra un ejemplo de cómo eliminar un interceptor: ```js // Eject the request interceptor const myInterceptor = axios.interceptors.request.use(function () { /*...*/ }); axios.interceptors.request.eject(myInterceptor); // Eject the response interceptor const myInterceptor = axios.interceptors.response.use(function () { /*...*/ }); axios.interceptors.response.eject(myInterceptor); ``` A continuación se muestra un ejemplo de cómo eliminar todos los interceptores: ```js const instance = axios.create(); instance.interceptors.request.use(function () { /*...*/ }); instance.interceptors.request.clear(); // Removes interceptors from requests instance.interceptors.response.use(function () { /*...*/ }); instance.interceptors.response.clear(); // Removes interceptors from responses ``` ## Comportamiento predeterminado de los interceptores Cuando añades interceptores de solicitud, se asume que son asíncronos de forma predeterminada. Esto puede causar un retraso en la ejecución de tu solicitud axios cuando el hilo principal está bloqueado (se crea una Promise internamente para el interceptor y tu solicitud queda al final de la pila de llamadas). Si tus interceptores de solicitud son síncronos, puedes añadir un indicador al objeto de opciones que le indicará a axios que ejecute el código de forma síncrona y evite cualquier retraso en la ejecución de la solicitud. ```js axios.interceptors.request.use( function (config) { config.headers.test = "I am only a header!"; return config; }, null, { synchronous: true } ); ``` ### Errores en interceptores síncronos Cuando un interceptor de solicitud síncrono lanza un error, axios llama al manejador `onRejected` emparejado con ese interceptor y deja de ejecutar los interceptores de solicitud restantes. Si el manejador retorna normalmente —incluido `undefined` o una Promise resuelta— el error se considera manejado y axios envía la solicitud con la última configuración válida. El valor devuelto por el manejador no reemplaza esa configuración. Para impedir el envío, omite el manejador de rechazo o haz que lance un error o devuelva una Promise rechazada. El error terminal continúa entonces por los interceptores de rechazo de respuesta. ```js axios.interceptors.request.use( function validate(config) { if (!config.headers.has("Authorization")) { throw new Error("Authorization is required"); } return config; }, function rejectInvalidRequest(error) { return Promise.reject(error); }, { synchronous: true } ); ``` Un manejador que solo registra el error puede retornar normalmente para conservar el comportamiento existente de continuación: ```js axios.interceptors.request.use( function prepare(config) { throw new Error("Optional preparation failed"); }, function logPreparationFailure(error) { console.warn(error); // Retornar normalmente envía la solicitud con la última configuración válida. }, { synchronous: true } ); ``` ## Interceptores usando `runWhen` Si deseas ejecutar un interceptor particular basándote en una verificación en tiempo de ejecución, puedes añadir una función `runWhen` al objeto de opciones. El interceptor no se ejecutará si y solo si el retorno de `runWhen` es `false`. La función se llamará con el objeto de configuración (recuerda que también puedes vincularle tus propios argumentos). Esto puede ser útil cuando tienes un interceptor de solicitud asíncrono que solo necesita ejecutarse en ciertos momentos. ```js function onGetCall(config) { return config.method === "get"; } axios.interceptors.request.use( function (config) { config.headers.test = "special get headers"; return config; }, null, { runWhen: onGetCall } ); ``` ## Orden de ejecución de los interceptores ::: warning Los interceptores de solicitud y respuesta se ejecutan en órdenes **opuestos** Los interceptores de solicitud se ejecutan en **orden inverso** (LIFO — último en entrar, primero en salir). El _último_ interceptor de solicitud añadido se ejecuta _primero_. Los interceptores de respuesta se ejecutan en el **orden en que fueron añadidos** (FIFO — primero en entrar, primero en salir). El _primer_ interceptor de respuesta añadido se ejecuta _primero_. ::: El siguiente ejemplo muestra el orden completo de ejecución para tres interceptores de solicitud y tres interceptores de respuesta: ```js const instance = axios.create(); const interceptor = (id) => (base) => { console.log(id); return base; }; instance.interceptors.request.use(interceptor("Request Interceptor 1")); instance.interceptors.request.use(interceptor("Request Interceptor 2")); instance.interceptors.request.use(interceptor("Request Interceptor 3")); instance.interceptors.response.use(interceptor("Response Interceptor 1")); instance.interceptors.response.use(interceptor("Response Interceptor 2")); instance.interceptors.response.use(interceptor("Response Interceptor 3")); // Salida en consola: // Request Interceptor 3 // Request Interceptor 2 // Request Interceptor 1 // [Se realiza la solicitud HTTP] // Response Interceptor 1 // Response Interceptor 2 // Response Interceptor 3 ``` ## Múltiples interceptores Puedes añadir múltiples interceptores a la misma solicitud o respuesta. Lo siguiente aplica para múltiples interceptores en la misma cadena, en el orden indicado a continuación: - Cada interceptor se ejecuta - Los interceptores de solicitud se ejecutan en orden inverso (LIFO). - Los interceptores de respuesta se ejecutan en el orden en que fueron añadidos (FIFO). - Solo se devuelve el resultado del último interceptor - Cada interceptor recibe el resultado de su predecesor - Cuando el interceptor de cumplimiento lanza una excepción: - El siguiente interceptor de cumplimiento no es llamado - El siguiente interceptor de rechazo es llamado - Una vez capturado, el siguiente interceptor de cumplimiento es llamado nuevamente (igual que en una cadena de promises). ::: tip Para obtener una comprensión profunda de cómo funcionan los interceptores, puedes leer los casos de prueba [aquí](https://github.com/axios/axios/blob/v1.x/tests/browser/interceptors.browser.test.js). ::: axios-axios-aa54a5e/docs/es/pages/advanced/multipart-form-data-format.md000066400000000000000000000170661526660636600264030ustar00rootroot00000000000000# Formato multipart-form-data axios puede enviar solicitudes en el formato `multipart/form-data`. Este formato se usa comúnmente al subir archivos. Para enviar una solicitud en este formato, debes crear un objeto `FormData` y agregarle los datos. Luego puedes pasar el objeto `FormData` a la propiedad `data` de la configuración de solicitud de axios. ```js const formData = new FormData(); formData.append('foo', 'bar'); axios.post('https://httpbin.org/post', formData); ``` No establezcas manualmente el encabezado `Content-Type` para `FormData` en navegador, web worker o React Native; esos entornos agregan el boundary multipart por sí mismos. En Node.js, puedes usar la librería `form-data` de la siguiente manera: ```js const FormData = require('form-data'); const form = new FormData(); form.append('my_field', 'my value'); form.append('my_buffer', Buffer.alloc(10)); form.append('my_file', fs.createReadStream('/foo/bar.jpg')); axios.post('https://example.com', form); ``` ## Serialización automática a FormData A partir de la versión v0.27.0, Axios admite la serialización automática de objetos a un objeto FormData si el encabezado `Content-Type` de la solicitud está establecido en `multipart/form-data`. Esto significa que puedes pasar un objeto JavaScript directamente a la propiedad `data` de la configuración de solicitud de axios. Por ejemplo, al pasar datos a una solicitud POST: ```js import axios from 'axios'; axios .post( 'https://httpbin.org/post', { x: 1 }, { headers: { 'Content-Type': 'multipart/form-data', }, } ) .then(({ data }) => console.log(data)); ``` En el entorno de Node.js, el polyfill ([`form-data`](https://github.com/form-data/form-data)) se usa de forma predeterminada. Puedes sobrescribir la clase FormData estableciendo la variable de configuración `env.FormData`, aunque en la mayoría de los casos no lo necesitarás: ```js const axios = require('axios'); var FormData = require('form-data'); axios .post( 'https://httpbin.org/post', { x: 1, buf: Buffer.alloc(10) }, { headers: { 'Content-Type': 'multipart/form-data', }, } ) .then(({ data }) => console.log(data)); ``` ## Política de encabezados para `FormData` de Node.js Cuando pasas un objeto `FormData` de Node.js que expone `getHeaders()` (como el paquete [`form-data`](https://github.com/form-data/form-data)), axios copia por defecto todos los encabezados que devuelve a la solicitud. Esto preserva la compatibilidad con v1, pero puede ser problemático cuando el objeto `FormData` proviene de una fuente no confiable — `getHeaders()` podría sobrescribir encabezados como `Authorization` o inyectar encabezados arbitrarios. Establece `formDataHeaderPolicy: 'content-only'` para copiar **únicamente** `Content-Type` y `Content-Length` desde `getHeaders()`, y luego define cualquier otro encabezado explícitamente a través de la configuración `headers` de la solicitud: ```js await axios.post('https://example.com/upload', form, { formDataHeaderPolicy: 'content-only', headers: { Authorization: 'Bearer my-token', }, }); ``` El valor predeterminado es `'legacy'`. Consulta [`formDataHeaderPolicy`](/pages/advanced/request-config#formdataheaderpolicy) en la referencia de configuración de solicitud para más detalles. ## Terminaciones admitidas El serializador de FormData de Axios admite algunas terminaciones especiales para realizar las siguientes operaciones: - `{}` - serializa el valor con JSON.stringify - `[]` - desenvuelve el objeto tipo arreglo como campos separados con la misma clave ::: warning Nota: la operación de desenvolvimiento/expansión se usará de forma predeterminada en arreglos y objetos FileList. ::: ## Configurar el serializador de FormData El serializador de FormData admite opciones adicionales a través de la propiedad de objeto `config.formSerializer` para manejar casos especiales: - `visitor: Function` - función de visitante definida por el usuario que se llamará de forma recursiva para serializar el objeto de datos a un objeto FormData siguiendo reglas personalizadas. - `dots: boolean = false` - usa notación de punto en lugar de corchetes para serializar arreglos y objetos; - `metaTokens: boolean = true` - añade la terminación especial (por ejemplo, `user{}: '{"name": "John"}'`) en la clave de FormData. El analizador de cuerpo del backend podría usar esta meta-información para analizar automáticamente el valor como JSON. - `indexes: null|false|true = false` - controla cómo se añadirán los índices a las claves desenvueltas de objetos planos tipo arreglo: - `null` - no añadir corchetes (`arr: 1`, `arr: 2`, `arr: 3`) - `false` (predeterminado) - añadir corchetes vacíos (`arr[]: 1`, `arr[]: 2`, `arr[]: 3`) - `true` - añadir corchetes con índices (`arr[0]: 1`, `arr[1]: 2`, `arr[2]: 3`) - `maxDepth: number = 100` - profundidad máxima de anidación de objetos en la que el serializador recursará. Si la entrada excede esta profundidad, se lanza un `AxiosError` con `code: 'ERR_FORM_DATA_DEPTH_EXCEEDED'`. Esto protege las aplicaciones del lado del servidor contra ataques DoS mediante cargas útiles profundamente anidadas. Establece en `Infinity` para desactivar el límite. - `Blob: typeof Blob` - constructor de Blob usado al convertir valores tipo ArrayBuffer para `FormData` compatible con la especificación. Sobrescríbelo solo en runtimes que proporcionen un constructor `Blob` compatible bajo otro identificador. ```js // Aumentar el límite para esquemas que legítimamente exceden 100 niveles: axios.postForm('/api', data, { formSerializer: { maxDepth: 200 } }); ``` ::: warning Nota de seguridad El límite predeterminado de 100 es intencional. El código del lado del servidor que reenvía JSON controlado por el cliente a axios como `data` es vulnerable a un desbordamiento de pila de llamadas sin esta protección. Solo aumenta `maxDepth` si tu esquema realmente lo requiere. ::: Por ejemplo, si tenemos un objeto como este: ```js const obj = { x: 1, arr: [1, 2, 3], arr2: [1, [2], 3], users: [ { name: 'Peter', surname: 'Griffin' }, { name: 'Thomas', surname: 'Anderson' }, ], 'obj2{}': [{ x: 1 }], }; ``` El serializador de Axios ejecutará internamente los siguientes pasos: ```js const formData = new FormData(); formData.append('x', '1'); formData.append('arr[]', '1'); formData.append('arr[]', '2'); formData.append('arr[]', '3'); formData.append('arr2[0]', '1'); formData.append('arr2[1][0]', '2'); formData.append('arr2[2]', '3'); formData.append('users[0][name]', 'Peter'); formData.append('users[0][surname]', 'Griffin'); formData.append('users[1][name]', 'Thomas'); formData.append('users[1][surname]', 'Anderson'); formData.append('obj2{}', '[{"x":1}]'); ``` ## Convertir FormData de nuevo a JSON `axios.formToJSON()` convierte la notación con puntos y corchetes de los nombres de campo en objetos y arrays anidados. Solo `.`, `[` y `]` son separadores estructurales. Los caracteres como `-`, espacios, `+`, `*` y `&` permanecen en las claves literales. ```js const form = new FormData(); form.append('user-name', 'johndoe'); form.append('user.name', 'john'); console.log(axios.formToJSON(form)); // { // 'user-name': 'johndoe', // user: { name: 'john' } // } ``` `user[name]` también crea una ruta de objeto anidada, mientras que `items[]` crea un array. Axios admite los siguientes métodos abreviados: `postForm`, `putForm`, `patchForm`, que son simplemente los métodos HTTP correspondientes con el encabezado `Content-Type` preestablecido en `multipart/form-data`. axios-axios-aa54a5e/docs/es/pages/advanced/progress-capturing.md000066400000000000000000000037111526660636600250520ustar00rootroot00000000000000# Captura de progreso Axios admite la captura del progreso de carga y descarga en entornos de navegador y Node.js. La frecuencia de los eventos de progreso está limitada a 3 veces por segundo para evitar saturar el navegador con eventos de progreso. A continuación se muestra un ejemplo de cómo capturar eventos de progreso: ```js await axios.post(url, data, { onUploadProgress: function (axiosProgressEvent) { /*{ loaded: number; total?: number; progress?: number; // in range [0..1] bytes: number; // how many bytes have been transferred since the last trigger (delta) estimated?: number; // estimated time in seconds rate?: number; // upload speed in bytes upload: true; // upload sign }*/ }, onDownloadProgress: function (axiosProgressEvent) { /*{ loaded: number; total?: number; progress?: number; bytes: number; estimated?: number; rate?: number; // download speed in bytes download: true; // download sign }*/ }, }); ``` También puedes transmitir los eventos de progreso de carga y descarga a un stream legible en Node.js. Esto es útil cuando deseas mostrar el progreso de una forma personalizada. A continuación se muestra un ejemplo de cómo transmitir eventos de progreso: ```js const { data } = await axios.post(SERVER_URL, readableStream, { onUploadProgress: ({ progress }) => { console.log((progress * 100).toFixed(2)); }, headers: { "Content-Length": contentLength, }, maxRedirects: 0, // avoid buffering the entire stream }); ``` ::: warning La captura del progreso de carga de FormData no está disponible actualmente en entornos Node.js. ::: ::: danger Se recomienda deshabilitar las redirecciones estableciendo `maxRedirects: 0` para subir el stream en el entorno Node.js, ya que el paquete `follow-redirects` almacenará todo el stream en memoria RAM sin seguir el algoritmo de "backpressure". ::: axios-axios-aa54a5e/docs/es/pages/advanced/promises.md000066400000000000000000000053021526660636600230530ustar00rootroot00000000000000# Promises axios está construido sobre la API nativa de Promise de ES6. Cada solicitud de axios devuelve una Promise que se resuelve en un objeto de respuesta o se rechaza con un error. Si tu entorno no admite Promises de ES6, necesitarás añadir un polyfill — por ejemplo con [es6-promise](https://github.com/stefanpenner/es6-promise). Para integraciones TypeScript, `AxiosPromise` es una promesa de `AxiosResponse`. Conserva los datos de solicitud y los parámetros de consulta en `response.config`: ```ts declare const search: AxiosPromise; search.then((response) => { response.data; // SearchResponse response.config.data; // RequestBody | undefined response.config.params; // SearchParams | undefined }); ``` ## then / catch / finally Dado que axios devuelve una Promise estándar, puedes usar `.then()`, `.catch()` y `.finally()` para manejar el resultado: ```js axios.get("/api/users") .then((response) => { console.log(response.data); }) .catch((error) => { console.error("Request failed:", error.message); }) .finally(() => { console.log("Request finished"); }); ``` ## async / await El enfoque recomendado para la mayoría de las bases de código es `async/await`, que hace que el código asíncrono se lea como código síncrono: ```js async function fetchUser(id) { try { const response = await axios.get(`/api/users/${id}`); return response.data; } catch (error) { console.error("Failed to fetch user:", error.message); throw error; } } ``` ## Solicitudes en paralelo Dado que axios devuelve una Promise estándar, puedes usar `Promise.all` para hacer múltiples solicitudes al mismo tiempo y esperar a que todas se completen: ```js const [users, posts] = await Promise.all([ axios.get("/api/users"), axios.get("/api/posts"), ]); console.log(users.data, posts.data); ``` ::: tip `Promise.all` se rechazará tan pronto como cualquiera de las solicitudes falle. Si deseas manejar fallos parciales, usa `Promise.allSettled` en su lugar. ::: ```js const results = await Promise.allSettled([ axios.get("/api/users"), axios.get("/api/posts"), ]); results.forEach((result) => { if (result.status === "fulfilled") { console.log(result.value.data); } else { console.error("Request failed:", result.reason.message); } }); ``` ## Encadenar solicitudes Puedes encadenar llamadas `.then()` para ejecutar solicitudes secuencialmente, pasando datos de una a la siguiente: ```js axios.get("/api/user/1") .then(({ data: user }) => axios.get(`/api/posts?userId=${user.id}`)) .then(({ data: posts }) => { console.log("Posts for user:", posts); }) .catch(console.error); ``` axios-axios-aa54a5e/docs/es/pages/advanced/rate-limiting.md000066400000000000000000000043031526660636600237570ustar00rootroot00000000000000# Limitación de velocidad axios admite la limitación del ancho de banda en el entorno Node.js a través del adaptador HTTP. Esto te permite controlar la velocidad de carga o descarga de datos, lo que es útil para operaciones masivas, trabajos en segundo plano o scraping educado que no debe saturar una conexión. ## `maxRate` La opción `maxRate` acepta un número (bytes por segundo) o un arreglo donde el primer valor es el límite de carga y el segundo es el límite de descarga. Usa `[uploadRate]` para limitar solo la carga, o `[uploadRate, downloadRate]` para limitar ambas direcciones. Cuando se pasa un número único, el mismo límite se aplica tanto a la carga como a la descarga. ```js // Limit both upload and download to 100 KB/s await axios.get(URL, { maxRate: 100 * 1024 }); // Limit upload to 100 KB/s, download to 500 KB/s await axios.get(URL, { maxRate: [100 * 1024, 500 * 1024] }); ``` ::: warning `maxRate` solo es compatible con el adaptador HTTP de Node.js. No tiene efecto en entornos de navegador. ::: ## Limitación de velocidad de carga Limita la velocidad de carga y registra el progreso al mismo tiempo: ```js const { data } = await axios.post(SERVER_URL, myBuffer, { onUploadProgress: ({ progress, rate }) => { const percent = (progress * 100).toFixed(1); const kbps = (rate / 1024).toFixed(1); console.log(`Upload [${percent}%] at ${kbps} KB/s`); }, maxRate: [100 * 1024], // cap upload at 100 KB/s }); ``` ## Limitación de velocidad de descarga Limita la velocidad de descarga para respuestas de gran tamaño: ```js const { data } = await axios.get(FILE_URL, { onDownloadProgress: ({ progress, rate }) => { const percent = (progress * 100).toFixed(1); const kbps = (rate / 1024).toFixed(1); console.log(`Download [${percent}%] at ${kbps} KB/s`); }, maxRate: [Infinity, 200 * 1024], // no upload limit, 200 KB/s download limit responseType: "arraybuffer", }); ``` ## Limitación combinada de carga y descarga Pasa ambos límites como un arreglo para controlar ambas direcciones simultáneamente: ```js await axios.post(SERVER_URL, largeBuffer, { maxRate: [50 * 1024, 500 * 1024], // 50 KB/s up, 500 KB/s down }); ``` axios-axios-aa54a5e/docs/es/pages/advanced/request-config.md000066400000000000000000001013651526660636600241530ustar00rootroot00000000000000# Configuración de solicitud La configuración de solicitud se usa para configurar la solicitud. Existe una amplia gama de opciones disponibles, pero la única opción requerida es `url`. Si el objeto de configuración no contiene un campo `method`, el método predeterminado es `GET`. ::: warning Seguridad: la protección contra bombas de descompresión es opcional Por defecto, `maxContentLength` y `maxBodyLength` están en `-1` (sin límite). Un servidor malicioso o comprometido puede devolver un cuerpo pequeño comprimido con gzip/deflate/brotli/zstd que se expande a gigabytes y agota el proceso de Node.js. Si haces solicitudes a servidores en los que no confías plenamente, **establece un tope**: ```js axios.defaults.maxContentLength = 10 * 1024 * 1024; // 10 MB axios.defaults.maxBodyLength = 10 * 1024 * 1024; ``` Consulta la [guía de seguridad](/pages/misc/security) para más detalles. ::: ### `url` La `url` es la URL a la que se realiza la solicitud. Puede ser una cadena de texto o una instancia de `URL`. Una URL `http:` o `https:` debe incluir `//` después del protocolo. Los valores malformados como `https:example.com` y `https:/example.com` se rechazan con `ERR_INVALID_URL`; usa una URL bien formada como `https://example.com`. ### `method` El `method` es el método HTTP a usar para la solicitud. El método predeterminado es `GET`. ### `baseURL` La `baseURL` es la URL base que se antepondrá a la `url` a menos que la `url` sea una URL absoluta. Es útil para hacer solicitudes al mismo dominio sin tener que repetir el nombre de dominio, ni ningún prefijo de API o versión. `baseURL` es una comodidad para construir URLs, no un límite de seguridad. Si la `url` de una solicitud proviene de entrada no confiable, valídala antes de pasarla a axios. Una `url` relativa puede contener segmentos `..`; después de que axios la combine con `baseURL`, el parser de URL de la plataforma normaliza la ruta y puede resolver la solicitud fuera del prefijo de ruta previsto. `allowAbsoluteUrls: false` evita que las URLs absolutas reemplacen `baseURL`, pero no valida ni restringe rutas relativas. La misma regla de URL bien formada se aplica a `baseURL`: los valores `http:` y `https:` deben incluir `//` después del protocolo. ### `allowAbsoluteUrls` `allowAbsoluteUrls` determina si las URLs absolutas sobrescribirán un `baseUrl` configurado. Cuando se establece en `true` (valor predeterminado), los valores absolutos para `url` sobrescribirán `baseUrl`. Cuando se establece en `false`, los valores absolutos para `url` siempre serán antepuestos por `baseUrl`. ### `transformRequest` La función `transformRequest` te permite modificar los datos de la solicitud antes de enviarlos al servidor. Esta función se llama con los datos de la solicitud como único argumento. Solo aplica para los métodos de solicitud `PUT`, `POST`, `PATCH` y `DELETE`. La última función del arreglo debe devolver una cadena de texto o una instancia de Buffer, ArrayBuffer, FormData o Stream. ### `transformResponse` La función `transformResponse` te permite modificar los datos de la respuesta antes de que sean pasados a las funciones `then` o `catch`. Esta función se llama con los datos de la respuesta como único argumento. ### `parseReviver` La función `parseReviver` te permite proporcionar una función "reviver" personalizada directamente a la llamada nativa `JSON.parse()` que utiliza el `transformResponse` predeterminado. Esto resulta especialmente útil para realizar hidratación de tipos de alto rendimiento (por ejemplo, convertir cadenas ISO a objetos `Temporal` o `Date`) o para evitar la pérdida de precisión durante el parseo. En entornos modernos (ES2023+), la función reviver recibe un tercer argumento `context`. Esto proporciona acceso a la fuente JSON cruda (`source`), permitiendo la conversión precisa de enteros grandes (BigInt) que de otro modo perderían precisión al ser parseados como números estándar de JavaScript. > Nota: `Temporal` aún no está disponible en todos los entornos. Considera usar un polyfill si es necesario. ```js const client = axios.create({ parseReviver: (key, value, context) => { // Ejemplo: parseo de BigInt seguro en precisión if (typeof value === 'number' && context?.source) { const isInteger = Number.isInteger(value); const isUnsafe = !Number.isSafeInteger(value); const isValidIntegerString = /^-?\d+$/.test(context.source); if (isInteger && isUnsafe && isValidIntegerString) { try { return BigInt(context.source); } catch { // Alternativa: devolver el valor original si el parseo falla } } } // Ejemplo: hidratar fechas en objetos Temporal if ( typeof value === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(value) && typeof Temporal !== 'undefined' && Temporal?.PlainDate ) { return Temporal.PlainDate.from(value); } return value; }, }); ``` ### `headers` Los `headers` son los encabezados HTTP que se enviarán con la solicitud. El encabezado `Content-Type` se establece en `application/json` de forma predeterminada. ### `params` Los `params` son los parámetros de URL que se enviarán con la solicitud. Debe ser un objeto plano o un objeto URLSearchParams. Si la `url` contiene parámetros de consulta, se combinarán con el objeto `params`. ### `paramsSerializer` La función `paramsSerializer` te permite serializar el objeto `params` antes de enviarlo al servidor. Hay varias opciones disponibles para esta función; consulta el ejemplo completo de configuración de solicitud al final de esta página. En TypeScript, `AxiosRequestConfig` usa `P` para `params`, y un `paramsSerializer` personalizado recibe ese mismo tipo: ```ts interface SearchParams { query: string; page?: number; } const config: AxiosRequestConfig = { params: { query: "axios", page: 1 }, paramsSerializer: (params) => `${params.query}:${params.page ?? 1}`, }; ``` #### Codificación porcentual estricta RFC 3986 De forma predeterminada, axios decodifica `%3A`, `%24`, `%2C` y `%20` de vuelta a `:`, `$`, `,` y `+` por legibilidad (el `+` sigue la convención `application/x-www-form-urlencoded` para representar un espacio en una cadena de consulta). Estos caracteres son válidos dentro de un componente de consulta según la [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986#section-3.4), por lo que la salida predeterminada es correcta. Sin embargo, algunos backends requieren codificación porcentual estricta y rechazan la forma legible. Usa la opción `encode` para sobrescribir el codificador predeterminado: ```js // Por solicitud: emitir codificación porcentual estricta RFC 3986 para los valores de consulta axios.get('/foo', { params: { filter: JSON.stringify({ startedAt: '2026-01-23' }) }, paramsSerializer: { encode: encodeURIComponent }, }); // O establecerlo en los valores predeterminados de la instancia const client = axios.create({ paramsSerializer: { encode: encodeURIComponent }, }); ``` ### `data` El `data` son los datos que se enviarán como cuerpo de la solicitud. Puede ser una cadena de texto, un objeto plano, un Buffer, ArrayBuffer, FormData, Stream o URLSearchParams. Solo aplica para los métodos de solicitud `PUT`, `POST`, `DELETE` y `PATCH`. Cuando no se establece `transformRequest`, debe ser de uno de los siguientes tipos: - cadena de texto, objeto plano, ArrayBuffer, ArrayBufferView, URLSearchParams - Solo en el navegador: FormData, File, Blob - React Native: FormData - Solo en Node.js: Stream, Buffer, FormData (paquete form-data) Para `FormData` en navegador, web worker y React Native, no establezcas manualmente `Content-Type`; el entorno agrega el boundary multipart. Para los objetos `FormData` de Node.js que proporcionan un método `getHeaders()`, axios copia por defecto todos los encabezados devueltos para mantener la compatibilidad con v1. Si el objeto `FormData` es personalizado o no es de plena confianza, establece `formDataHeaderPolicy: 'content-only'` para copiar únicamente `Content-Type` y `Content-Length`, y define cualquier otro encabezado de la solicitud explícitamente mediante la configuración `headers`. ### `formDataHeaderPolicy` Controla cómo axios copia los encabezados devueltos por `FormData#getHeaders()` de Node.js. El valor por defecto es `'legacy'`, que copia todos los encabezados devueltos para preservar el comportamiento existente de v1. Establece `'content-only'` para copiar únicamente `Content-Type` y `Content-Length` desde `getHeaders()`. ### Opciones personalizadas con claves de tipo símbolo La combinación de configuración conserva las propiedades de símbolo propias y enumerables. Las aplicaciones TypeScript pueden ampliar `AxiosRequestConfig` con una clave de símbolo específica y leer esa opción desde `InternalAxiosRequestConfig` en interceptores de solicitud o adaptadores. Las propiedades de símbolo heredadas o no enumerables no se copian. Consulta la [guía de TypeScript](/pages/advanced/type-script#configuracion-de-solicitud-personalizada-con-claves-de-tipo-simbolo) para ver un ejemplo. ### `timeout` El `timeout` es el número de milisegundos antes de que la solicitud expire. Si la solicitud tarda más que `timeout`, se abortará. ### `withCredentials` La propiedad `withCredentials` indica si las solicitudes de Access-Control entre sitios deben hacerse usando credenciales como cookies, encabezados de autorización o certificados de cliente TLS. Establecer `withCredentials` no tiene efecto en solicitudes del mismo sitio. ### `adapter` `adapter` permite el manejo personalizado de solicitudes, lo que facilita las pruebas. Devuelve una Promise y proporciona una respuesta válida; consulta los [adaptadores](/pages/advanced/adapters) para más información. También proporcionamos una serie de adaptadores integrados. El adaptador predeterminado es `http` para Node.js y `xhr` para navegadores. La lista completa de adaptadores integrados es la siguiente: - fetch - http - xhr También puedes pasar un arreglo de adaptadores. axios usará el primero que sea compatible con el entorno. ### `auth` `auth` indica que se debe usar autenticación HTTP Basic y proporciona las credenciales. Esto establecerá un encabezado `Authorization`, sobrescribiendo cualquier encabezado `Authorization` personalizado que hayas definido usando `headers`. Si `auth` se omite, los adaptadores HTTP de Node.js y fetch pueden obtener credenciales Basic desde la URL de la solicitud, por ejemplo `https://user:pass@example.com`; las credenciales codificadas con porcentaje en la URL se decodifican, y `auth` siempre tiene prioridad sobre las credenciales incluidas en la URL. En el adaptador HTTP de Node.js, la autenticación Basic se conserva en redirecciones del mismo origen y se elimina en redirecciones de origen cruzado. Ten en cuenta que solo la autenticación HTTP Basic es configurable a través de este parámetro. Para tokens Bearer y similares, usa encabezados `Authorization` personalizados. ### `responseType` El `responseType` indica el tipo de datos con el que el servidor responderá. Puede ser uno de los siguientes: - arraybuffer - document - json - text - stream - blob (solo en el navegador) - formdata (solo con el adaptador fetch) ### `responseEncoding` El `responseEncoding` indica la codificación a usar para decodificar las respuestas. Se admiten las siguientes opciones: - ascii - ASCII - ansi - ANSI - binary - BINARY - base64 - BASE64 - base64url - BASE64URL - hex - HEX - latin1 - LATIN1 - ucs-2 - UCS-2 - ucs2 - UCS2 - utf-8 - UTF-8 - utf8 - UTF8 - utf16le - UTF16LE ::: tip Nota: Se ignora para `responseType` de tipo `stream` o solicitudes del lado del cliente. ::: ### `xsrfCookieName` `xsrfCookieName` es el nombre de la cookie que se usará como valor para el token `XSRF`. ### `xsrfHeaderName` `xsrfHeaderName` es el nombre del encabezado que se usará como valor para el token `XSRF`. ### `withXSRFToken` `withXSRFToken` controla si axios lee la cookie XSRF y establece el encabezado XSRF en las solicitudes del navegador. Acepta: - `undefined` _(predeterminado)_ — establece el encabezado XSRF solo para solicitudes del mismo origen. - `true` — siempre establece el encabezado XSRF, incluso para solicitudes de origen cruzado. - `false` — nunca establece el encabezado XSRF. - `(config: InternalAxiosRequestConfig) => boolean | undefined` — un callback que decide por solicitud, recibiendo el objeto de configuración interna. ```ts withXSRFToken: boolean | undefined | ((config: InternalAxiosRequestConfig) => boolean | undefined); ``` ::: warning XSRF de origen cruzado y `withCredentials` `withCredentials` controla si las solicitudes de origen cruzado incluyen credenciales (cookies, autenticación HTTP). En versiones anteriores de axios, establecer `withCredentials: true` provocaba implícitamente que axios estableciera el encabezado XSRF para solicitudes de origen cruzado. Las versiones más recientes de axios separan estas responsabilidades: para permitir que el encabezado XSRF se envíe en solicitudes de origen cruzado debes establecer **ambos** `withCredentials: true` y `withXSRFToken: true`. ```js axios.get('/user', { withCredentials: true, withXSRFToken: true }); ``` ::: ### `onUploadProgress` La función `onUploadProgress` te permite escuchar el progreso de una carga. ### `onDownloadProgress` La función `onDownloadProgress` te permite escuchar el progreso de una descarga. ### `maxContentLength` La propiedad `maxContentLength` define el tamaño máximo de la respuesta en bytes. El adaptador HTTP de Node.js lo aplica en respuestas almacenadas en búfer y en streaming. El adaptador fetch lo aplica cuando la longitud de la respuesta está declarada, se puede rastrear el stream de respuesta o se puede determinar el tamaño de la respuesta. > ⚠� **Seguridad:** el valor por defecto es `-1` (sin límite). Las respuestas sin límite combinadas con la descompresión gzip/deflate/brotli/zstd permiten ataques de denegación de servicio por bomba de descompresión. > Establece un límite explícito al consumir servidores en los que no confíes plenamente. ### `maxBodyLength` La propiedad `maxBodyLength` define el tamaño máximo del cuerpo de la solicitud en bytes. El adaptador HTTP de Node.js lo aplica, y el adaptador fetch lo aplica cuando se puede determinar la longitud del cuerpo de la solicitud. ### `redact` La propiedad `redact` es un arreglo opcional de nombres de claves de configuración que se enmascararán cuando un `AxiosError` se serialice con `toJSON()`. La coincidencia es insensible a mayúsculas/minúsculas y recursiva a lo largo de la configuración de la solicitud serializada. Los valores coincidentes se reemplazan por `[REDACTED ****]`. `redact` solo afecta a la serialización del error. No modifica los datos de la solicitud, los encabezados ni el objeto de configuración original. ```js axios .get('/user/12345', { headers: { Authorization: 'Bearer token' }, auth: { username: 'me', password: 'secret' }, redact: ['authorization', 'password'], }) .catch((error) => { console.log(error.toJSON().config); }); ``` ### `validateStatus` La función `validateStatus` te permite sobreescribir la validación predeterminada del código de estado. Por defecto, axios rechazará la Promise si el código de estado no está en el rango 200-299. Puedes sobreescribir este comportamiento proporcionando una función `validateStatus` personalizada. La función debe devolver `true` si el código de estado está dentro del rango que deseas aceptar. Por defecto, un `validateStatus: undefined` explícito conserva el comportamiento heredado y resuelve todos los estados de respuesta, porque `transitional.validateStatusUndefinedResolves` tiene `true` como valor predeterminado. Establece `transitional.validateStatusUndefinedResolves` en `false` si quieres que un `validateStatus: undefined` explícito se comporte como si `validateStatus` se hubiera omitido; así axios usa el validador configurado/predeterminado y rechaza las respuestas que no sean 2xx por defecto. `validateStatus: null` sigue aceptando todos los estados de respuesta. Si desactivas el comportamiento transicional y quieres intencionalmente resolver todos los estados, usa `validateStatus: null` o un validador que devuelva `true`. ```js axios.get('/user/12345', { validateStatus: undefined, transitional: { validateStatusUndefinedResolves: false, }, }); ``` ### `maxRedirects` La propiedad `maxRedirects` define el número máximo de redirecciones a seguir. Si se establece en 0, no se seguirá ninguna redirección. ### `sensitiveHeaders` La propiedad `sensitiveHeaders` es un arreglo opcional de nombres de encabezados personalizados que contienen secretos, como `X-API-Key`, que el adaptador HTTP de Node.js elimina al seguir una redirección a un origen diferente. La coincidencia no distingue mayúsculas/minúsculas. Las redirecciones al mismo origen conservan estos encabezados. Si `maxRedirects` es `0`, axios no sigue redirecciones y `sensitiveHeaders` no se usa. ```js axios.get('https://api.example.com/users', { headers: { 'X-API-Key': 'secret' }, sensitiveHeaders: ['X-API-Key'], }); ``` ### `beforeRedirect` La función `beforeRedirect` te permite modificar la solicitud antes de que sea redirigida. Úsala para ajustar las opciones de la solicitud al redirigir, para inspeccionar los últimos encabezados de respuesta, o para cancelar la solicitud lanzando un error. Si `maxRedirects` se establece en 0, `beforeRedirect` no se usa. ```js beforeRedirect: (options, { headers }) => { if (options.hostname === 'example.com' && options.protocol === 'https:') { options.auth = 'user:password'; } }; ``` ::: warning Seguridad: reinyección de credenciales en redirecciones El hook `beforeRedirect` se ejecuta **después** de que se eliminan los encabezados sensibles durante las redirecciones. La librería `follow-redirects` elimina las credenciales en una bajada de protocolo (HTTPS → HTTP) por seguridad. Como `beforeRedirect` se ejecuta después, reinyectar credenciales sin verificar el protocolo de destino puede exponer datos sensibles. Reinyecta credenciales únicamente para destinos HTTPS de confianza, y evita reinyectarlas en redirecciones con bajada de protocolo. ::: ### `socketPath` La propiedad `socketPath` define un socket UNIX que se usará en lugar de una conexión TCP. Por ejemplo, `/var/run/docker.sock` para enviar solicitudes al daemon de Docker. Solo se puede especificar `socketPath` o `proxy`. Si ambos se especifican, se usa `socketPath`. :::warning Seguridad Cuando se establece `socketPath`, el hostname y el puerto de la URL se ignoran y axios se comunica directamente con el socket Unix indicado. Si cualquier parte de la configuración de la solicitud proviene de entrada del usuario (por ejemplo, en un proxy o manejador de webhooks que reenvía opciones), un atacante puede inyectar `socketPath` para redirigir el tráfico a sockets locales privilegiados como `/var/run/docker.sock`, `/run/containerd/containerd.sock` o `/run/systemd/private`, eludiendo por completo las protecciones SSRF basadas en hostname (CWE-918). Filtra la configuración recibida desde entradas no confiables y/o restringe las rutas de socket aceptadas con `allowedSocketPaths` (ver más abajo). ::: ### `allowedSocketPaths` Restringe qué rutas de socket pueden usarse a través de `socketPath`. Acepta un string o un array de strings. Cuando está definido, axios resuelve el `socketPath` y lo compara con cada entrada (también resuelta); la solicitud se rechaza con un `AxiosError` de código `ERR_BAD_OPTION_VALUE` si no hay coincidencia. Si no se define (valor por defecto), `socketPath` se comporta igual que antes. ```js const client = axios.create({ allowedSocketPaths: ['/var/run/docker.sock'], }); // permitido await client.get('http://localhost/v1.45/info', { socketPath: '/var/run/docker.sock' }); // rechazado — no está en la lista await client.get('http://localhost/pods', { socketPath: '/var/run/kubelet.sock' }); ``` Un array vacío (`allowedSocketPaths: []`) bloquea todas las rutas de socket. ### `transport` La propiedad `transport` define el transporte a usar para la solicitud. Es útil para hacer solicitudes sobre un protocolo diferente, como `http2`. ### `httpAgent` y `httpsAgent` `httpAgent` y `httpsAgent` definen un agente personalizado para usar al realizar solicitudes http y https respectivamente en Node.js. Esto permite añadir opciones como `keepAlive` que no están habilitadas por defecto. ### `proxy` `proxy` define el nombre de host, puerto y protocolo del servidor proxy que deseas usar. También puedes definir tu proxy usando las variables de entorno convencionales `http_proxy` y `https_proxy`. Si usas variables de entorno para tu configuración de proxy, también puedes definir una variable de entorno `no_proxy` como una lista separada por comas de dominios que no deben ser enviados a través del proxy. En versiones de Node.js con soporte nativo para proxy mediante variables de entorno, axios delega el manejo del proxy de entorno a Node cuando el `httpAgent` o `httpsAgent` seleccionado tiene `proxyEnv` habilitado, incluidos procesos iniciados con `NODE_USE_ENV_PROXY=1`, `--use-env-proxy` o `NODE_OPTIONS=--use-env-proxy`. Los agentes personalizados sin `proxyEnv` siguen usando la resolución de proxy de entorno de axios. La configuración explícita `proxy` sigue siendo manejada por axios. Usa `false` para deshabilitar los proxies, ignorando las variables de entorno. `auth` indica que se debe usar autenticación HTTP Basic para conectarse al proxy, y proporciona las credenciales. Esto establecerá un encabezado `Proxy-Authorization`, sobrescribiendo cualquier encabezado `Proxy-Authorization` personalizado que hayas definido usando `headers`. Si el servidor proxy usa HTTPS, debes establecer el protocolo en `https`. Un encabezado `Host` proporcionado por el usuario en `headers` se preserva al reenviar a través de un proxy (coincidencia insensible a mayúsculas en `host` / `Host` / `HOST`). Esto te permite apuntar a un host virtual distinto al de la URL de la solicitud — por ejemplo, llegar a `127.0.0.1:4000` mientras el proxy trata la solicitud como `example.com`. Si no se proporciona ningún encabezado `Host`, axios lo establece por defecto al `hostname:port` de la URL de la solicitud, como antes. Para destinos `https://`, axios establece un túnel CONNECT a través del proxy y realiza TLS de extremo a extremo con el origen. `Proxy-Authorization` se envía solo en la solicitud CONNECT, nunca en la solicitud TLS encapsulada. Las opciones TLS de `httpsAgent`, como `ca`, `cert`, `key` y `rejectUnauthorized`, se reenvían al agente de túnel generado para que sigan aplicándose a la conexión TLS con el origen. Si proporcionas un `HttpsProxyAgent`, axios deja el túnel a cargo de ese agente. ```js proxy: { protocol: "https", host: "127.0.0.1", hostname: "localhost", // Takes precedence over "host" if both are defined port: 9000, auth: { username: "mikeymike", password: "rapunz3l" } }, ``` ### `cancelToken` La propiedad `cancelToken` te permite crear un token de cancelación que puede usarse para cancelar la solicitud. Para más información, consulta la documentación de [cancelación](/pages/advanced/cancellation). ### `signal` La propiedad `signal` te permite pasar una instancia de `AbortSignal` a la solicitud. Esto te permite cancelar la solicitud usando la API `AbortController`. ### `decompress` La propiedad `decompress` indica si los datos de la respuesta deben descomprimirse automáticamente. El valor predeterminado es `true`. El adaptador HTTP de Node.js admite gzip, deflate, brotli y zstd cuando el runtime actual de Node.js proporciona el descompresor zlib correspondiente. ### `insecureHTTPParser` Indica si se debe usar un parser HTTP inseguro que acepta encabezados HTTP inválidos. Esto puede permitir la interoperabilidad con implementaciones HTTP no conformes. Se debe evitar el uso del parser inseguro. Ten en cuenta que la opción `insecureHTTPParser` solo está disponible en Node.js 12.10.0 y versiones posteriores. Consulta la [documentación de Node.js](https://nodejs.org/en/blog/vulnerability/february-2020-security-releases/#strict-http-header-parsing-none) para más información. Consulta el conjunto completo de opciones [aquí](https://nodejs.org/dist/latest-v12.x/docs/api/http.html#http_http_request_url_options_callback). ### `transitional` La propiedad `transitional` te permite habilitar o deshabilitar ciertas características de transición. Las siguientes opciones están disponibles: - `silentJSONParsing`: Si se establece en `true` _(predeterminado)_, axios ignora silenciosamente los errores de parseo de JSON y establece `response.data` en `null` cuando el parseo falla. Establécelo en `false` para que se lance `SyntaxError` en su lugar. ::: tip Importante Esta opción solo tiene efecto cuando `responseType` se establece **explícitamente** en `'json'`. Cuando `responseType` se omite, axios usa `forcedJSONParsing` para intentar el parseo de JSON y devuelve silenciosamente la cadena cruda en caso de fallo, sin importar este ajuste. Para hacer que el JSON inválido lance un error, establece ambos: ```js { responseType: 'json', transitional: { silentJSONParsing: false } } ``` ::: - `forcedJSONParsing`: Fuerza a axios a analizar la cadena de respuesta como JSON incluso si `responseType` no es `'json'`. - `clarifyTimeoutError`: Clarifica el mensaje de error cuando una solicitud expira. Es útil cuando depuras problemas de timeout. - `validateStatusUndefinedResolves`: Si se establece en `true` _(predeterminado)_, un `validateStatus: undefined` explícito resuelve todos los estados de respuesta por compatibilidad. Establécelo en `false` para tratar `undefined` explícito como si `validateStatus` se hubiera omitido, de modo que axios use el validador configurado/predeterminado. Usa `validateStatus: null` o un validador que devuelva `true` cuando quieras intencionalmente resolver todos los estados. - `advertiseZstdAcceptEncoding`: Cuando se establece en `true`, axios añade `zstd` al encabezado `Accept-Encoding` predeterminado cuando el runtime actual de Node.js soporta descompresión zstd. Las respuestas zstd se descomprimen automáticamente cuando son compatibles y `decompress` es `true`. - `legacyInterceptorReqResOrdering`: Cuando se establece en `true`, se usará el orden de solicitud/respuesta de interceptores heredado. ### `env` La propiedad `env` te permite establecer algunas opciones de configuración. Por ejemplo, la clase FormData que se usa para serializar automáticamente el payload en un objeto FormData. - FormData: window?.FormData || global?.FormData ### `formSerializer` La opción `formSerializer` te permite configurar cómo se serializan los objetos planos a `multipart/form-data` cuando se usan como `data` de solicitud. Opciones disponibles: - `visitor` — función visitante personalizada llamada recursivamente para cada valor - `dots` — usar notación de punto en lugar de notación de corchetes - `metaTokens` — preservar terminaciones especiales de clave como `{}` - `indexes` — controlar el formato de corchetes para claves de arreglo (`null` / `false` / `true`) - `maxDepth` _(predeterminado: `100`)_ — profundidad máxima de anidación antes de lanzar un `AxiosError` con código `ERR_FORM_DATA_DEPTH_EXCEEDED`. Establece en `Infinity` para desactivar. - `Blob` — constructor de Blob usado al convertir valores tipo ArrayBuffer para `FormData` compatible con la especificación. Consulta la página [multipart/form-data](/pages/advanced/multipart-form-data-format) para todos los detalles, y el ejemplo completo de configuración de solicitud al final de esta página. ### `maxRate` La propiedad `maxRate` define el **ancho de banda** máximo (en bytes por segundo) para la carga y/o descarga. Acepta un número único (aplicado a ambas direcciones) o un arreglo de dos elementos `[uploadRate, downloadRate]` donde cada elemento es un límite en bytes por segundo. Por ejemplo, `100 * 1024` significa 100 KB/s. Consulta [Limitación de velocidad](/pages/advanced/rate-limiting) para ver ejemplos. ## Ejemplo completo de configuración de solicitud ```js { url: "/posts", method: "get", baseURL: "https://jsonplaceholder.typicode.com", allowAbsoluteUrls: true, transformRequest: [function (data, headers) { return data; }], transformResponse: [function (data) { return data; }], headers: {"X-Requested-With": "XMLHttpRequest"}, params: { postId: 5 }, paramsSerializer: { // Custom encoder function which sends key/value pairs in an iterative fashion. encode?: (param: string): string => { /* Do custom operations here and return transformed string */ }, // Custom serializer function for the entire parameter. Allows user to mimic pre 1.x behaviour. serialize?: (params: Record, options?: ParamsSerializerOptions ), // Configuration for formatting array indexes in the params. // Three available options: // (1) indexes: null (leads to no brackets) // (2) (default) indexes: false (leads to empty brackets) // (3) indexes: true (leads to brackets with indexes). indexes: false, // Profundidad máxima de anidación de objetos al serializar params. Lanza AxiosError // (ERR_FORM_DATA_DEPTH_EXCEEDED) si se excede. Predeterminado: 100. Establecer en Infinity para desactivar. maxDepth: 100 }, data: { firstName: "Fred" }, // `data` es específico de cada solicitud: axios no lo hereda ni lo fusiona en profundidad desde los valores predeterminados. // Para agregar campos compartidos al cuerpo, usa un interceptor de solicitud o transformRequest. formDataHeaderPolicy: "legacy", // Syntax alternative to send data into the body method post only the value is sent, not the key data: "Country=Brasil&City=Belo Horizonte", timeout: 1000, withCredentials: false, adapter: function (config) { // Do whatever you want }, adapter: "xhr", auth: { username: "janedoe", password: "s00pers3cret" }, responseType: "json", responseEncoding: "utf8", xsrfCookieName: "XSRF-TOKEN", xsrfHeaderName: "X-XSRF-TOKEN", withXSRFToken: boolean | undefined | ((config: InternalAxiosRequestConfig) => boolean | undefined), onUploadProgress: function ({loaded, total, progress, bytes, estimated, rate, upload = true}) { // Do whatever you want with the Axios progress event }, onDownloadProgress: function ({loaded, total, progress, bytes, estimated, rate, download = true}) { // Do whatever you want with the Axios progress event }, maxContentLength: 2000, maxBodyLength: 2000, redact: ['authorization', 'password'], validateStatus: function (status) { return status >= 200 && status < 300; }, maxRedirects: 21, sensitiveHeaders: ['X-API-Key'], beforeRedirect: (options, { headers }) => { if (options.hostname === "typicode.com") { options.auth = "user:password"; } }, socketPath: null, allowedSocketPaths: null, transport: undefined, httpAgent: new http.Agent({ keepAlive: true }), httpsAgent: new https.Agent({ keepAlive: true }), proxy: { protocol: "https", host: "127.0.0.1", // hostname: "127.0.0.1" // Takes precedence over "host" if both are defined port: 9000, auth: { username: "mikeymike", password: "rapunz3l" } }, cancelToken: new CancelToken(function (cancel) { cancel("Operation has been canceled."); }), signal: new AbortController().signal, decompress: true, insecureHTTPParser: undefined, transitional: { silentJSONParsing: true, forcedJSONParsing: true, clarifyTimeoutError: false, validateStatusUndefinedResolves: true, advertiseZstdAcceptEncoding: false, legacyInterceptorReqResOrdering: true, }, env: { FormData: window?.FormData || global?.FormData }, formSerializer: { // Custom visitor function to serialize form values visitor: (value, key, path, helpers) => {}; // Use dots instead of brackets format dots: boolean; // Keep special endings like {} in parameter key metaTokens: boolean; // Usar formato de índices de arreglo: // null - sin corchetes // false - corchetes vacíos // true - corchetes con índices indexes: boolean; // Profundidad máxima de anidación de objetos. Lanza AxiosError (ERR_FORM_DATA_DEPTH_EXCEEDED) // si se excede. Predeterminado: 100. Establecer en Infinity para desactivar. maxDepth: 100; }, maxRate: [ 100 * 1024, // 100KB/s upload limit, 100 * 1024 // 100KB/s download limit ] } ``` axios-axios-aa54a5e/docs/es/pages/advanced/request-method-aliases.md000066400000000000000000000161341526660636600256040ustar00rootroot00000000000000# Alias de solicitud axios proporciona un conjunto de alias para realizar solicitudes HTTP. Estos alias son atajos para hacer solicitudes usando el método `request`. Están diseñados para ser fáciles de usar y ofrecer una forma más conveniente de hacer solicitudes. axios se esfuerza por seguir las RFC 7231 y RFC 5789 de la manera más fiel posible. Los alias están diseñados para ser consistentes con los métodos HTTP definidos en dichas RFC. ### `axios` axios puede usarse para hacer una solicitud HTTP pasando únicamente el objeto de configuración. El objeto de configuración completo está documentado [aquí](/pages/advanced/request-config). ```ts axios( url: string | AxiosRequestConfig, config?: AxiosRequestConfig ): Promise; ``` Los métodos de solicitud usan parámetros genéricos en el orden ``: datos de respuesta, respuesta personalizada, datos de solicitud y parámetros de consulta. `P` se añade al final para conservar el significado de los argumentos explícitos `T`, `R` y `D`. Si no proporcionas un `R` personalizado, el `AxiosResponse` predeterminado conserva `D` y `P` en `response.config`. ## Alias de método Los siguientes alias están disponibles para hacer solicitudes: ### `request` El método `request` es el método principal que usarás para hacer solicitudes HTTP. Acepta un objeto de configuración como argumento y devuelve una Promise que se resuelve en el objeto de respuesta. Es un método genérico que puede usarse para cualquier tipo de solicitud HTTP. ```ts axios.request(config: AxiosRequestConfig): Promise; ``` ### `get` El método `get` se usa para hacer una solicitud GET. Acepta una URL y un objeto de configuración opcional como argumentos, y devuelve una Promise que se resuelve en el objeto de respuesta. ```ts axios.get(url: string, config?: AxiosRequestConfig): Promise; ``` ### `delete` El método `delete` se usa para hacer una solicitud DELETE. Acepta una URL y un objeto de configuración opcional como argumentos, y devuelve una Promise que se resuelve en el objeto de respuesta. ```ts axios.delete(url: string, config?: AxiosRequestConfig): Promise; ``` ### `head` El método `head` se usa para hacer una solicitud HEAD. Acepta una URL y un objeto de configuración opcional como argumentos, y devuelve una Promise que se resuelve en el objeto de respuesta. ```ts axios.head(url: string, config?: AxiosRequestConfig): Promise; ``` ### `options` El método `options` se usa para hacer una solicitud OPTIONS. Acepta una URL y un objeto de configuración opcional como argumentos, y devuelve una Promise que se resuelve en el objeto de respuesta. ```ts axios.options(url: string, config?: AxiosRequestConfig): Promise; ``` ### `post` El método `post` se usa para hacer una solicitud POST. Acepta una URL, un objeto de datos opcional y un objeto de configuración opcional como argumentos, y devuelve una Promise que se resuelve en el objeto de respuesta. ```ts axios.post(url: string, data?: D, config?: AxiosRequestConfig): Promise; ``` ### `put` El método `put` se usa para hacer una solicitud PUT. Acepta una URL, un objeto de datos opcional y un objeto de configuración opcional como argumentos, y devuelve una Promise que se resuelve en el objeto de respuesta. ```ts axios.put(url: string, data?: D, config?: AxiosRequestConfig): Promise; ``` ### `patch` El método `patch` se usa para hacer una solicitud PATCH. Acepta una URL, un objeto de datos opcional y un objeto de configuración opcional como argumentos, y devuelve una Promise que se resuelve en el objeto de respuesta. ```ts axios.patch(url: string, data?: D, config?: AxiosRequestConfig): Promise; ``` ### `query` El método `query` se usa para hacer una solicitud QUERY, un método seguro e idempotente que transporta un cuerpo. Acepta una URL, un objeto de datos opcional y un objeto de configuración opcional como argumentos, y devuelve una Promise que se resuelve en el objeto de respuesta. Úsalo para operaciones de tipo lectura cuyos parámetros sean demasiado complejos o sensibles para ir en la URL. ```ts axios.query(url: string, data?: D, config?: AxiosRequestConfig): Promise; ``` ```js // Enviar un filtro de búsqueda complejo como cuerpo de la solicitud const { data } = await axios.query("/api/search", { selector: ["name", "email"], filter: { active: true, role: "admin" }, }); ``` ::: warning Especificación en borrador El método QUERY está definido por un [Internet-Draft](https://datatracker.ietf.org/doc/draft-ietf-httpbis-safe-method-w-body/) del IETF y todavía no ha sido estandarizado. La semántica e incluso el propio nombre del método pueden cambiar antes de la publicación final, y el soporte en servidores, proxies y CDN es desigual. Verifica que tu infraestructura acepte `QUERY` de extremo a extremo antes de usarlo en producción. ::: ### `getUri` El método `getUri` devuelve la URL que se enviaría para una configuración dada sin realizar realmente la solicitud. Aplica `baseURL`, `paramsSerializer` y `params`, así que recibes la misma cadena que axios pondría en el cable. Útil para construir enlaces, depurar la serialización o reutilizar la URL resuelta en otra solicitud. ```ts axios.getUri(config?: AxiosRequestConfig): string; ``` ```js const url = axios.getUri({ url: "/users", baseURL: "https://api.example.com", params: { active: true, role: "admin" }, }); // "https://api.example.com/users?active=true&role=admin" ``` ::: tip Usa `getUri` en una instancia (`instance.getUri(config)`) para heredar los valores predeterminados de `baseURL`, `params` y `paramsSerializer` de la instancia. ::: ## Métodos abreviados para datos de formulario Estos métodos son equivalentes a sus contrapartes anteriores, pero predefinen `Content-Type` como `multipart/form-data`. Son la forma recomendada de subir archivos o enviar formularios HTML. ### `postForm` ```ts axios.postForm(url: string, data?: D, config?: AxiosRequestConfig): Promise; ``` ```js // Upload a file from a browser file input await axios.postForm("/api/upload", { file: document.querySelector("#fileInput").files[0], description: "Profile photo", }); ``` ### `putForm` ```ts axios.putForm(url: string, data?: D, config?: AxiosRequestConfig): Promise; ``` ```js // Replace a resource with form data await axios.putForm("/api/users/1/avatar", { avatar: document.querySelector("#avatarInput").files[0], }); ``` ### `patchForm` ```ts axios.patchForm(url: string, data?: D, config?: AxiosRequestConfig): Promise; ``` ```js // Update specific fields using form data await axios.patchForm("/api/users/1", { displayName: "New Name", avatar: document.querySelector("#avatarInput").files[0], }); ``` ::: tip `postForm`, `putForm` y `patchForm` aceptan los mismos tipos de datos que sus métodos base: objetos planos, `FormData`, `FileList` y `HTMLFormElement`. Consulta [Publicación de archivos](/pages/advanced/file-posting) para más ejemplos. ::: axios-axios-aa54a5e/docs/es/pages/advanced/response-schema.md000066400000000000000000000054141526660636600243120ustar00rootroot00000000000000# Esquema de respuesta Cada solicitud de axios se resuelve en un objeto de respuesta con la siguiente estructura. El esquema es consistente tanto en el entorno del navegador como en Node.js. ```js { // Los datos de respuesta proporcionados por el servidor. // Al usar `transformResponse`, este será el resultado de la última transformación. data: {}, // El código de estado HTTP de la respuesta del servidor (por ejemplo: 200, 404, 500). status: 200, // El mensaje de estado HTTP correspondiente al código de estado (por ejemplo: "OK", "Not Found"). statusText: "OK", // Los encabezados de respuesta enviados por el servidor. // Los nombres de los encabezados están en minúsculas. Puedes acceder a ellos con notación de corchetes o de punto. headers: {}, // La configuración de axios que se usó para esta solicitud, incluyendo baseURL, // headers, timeout, params y cualquier otra opción que hayas proporcionado. config: {}, // El objeto de solicitud subyacente. // En Node.js: la última instancia de `http.ClientRequest` (después de cualquier redirección). // En el navegador: la instancia de `XMLHttpRequest`. request: {}, } ``` En TypeScript, `AxiosResponse` tipa los datos de respuesta (`T`), los datos de solicitud (`D`), los encabezados de respuesta (`H`) y los parámetros de consulta (`P`). La configuración conserva ambos tipos de la solicitud: ```ts interface AxiosResponse { data: T; status: number; statusText: string; headers: (H & RawAxiosResponseHeaders) | AxiosResponseHeaders; config: InternalAxiosRequestConfig; request?: any; } ``` ## Acceder a los campos de la respuesta En la práctica, generalmente desestructurarás solo las partes que necesites: ```js const { data, status, headers } = await axios.get("/api/users/1"); console.log(status); // 200 console.log(headers["content-type"]); // "application/json; charset=utf-8" console.log(data); // { id: 1, name: "Jay", email: "jay@example.com" } ``` ## Verificar el código de estado axios resuelve la Promise para cualquier respuesta 2xx y la rechaza para cualquier cosa fuera de ese rango de forma predeterminada. Puedes personalizar esto con la opción de configuración `validateStatus`: ```js const response = await axios.get("/api/resource", { validateStatus: (status) => status < 500, // resolve for anything below 500 }); ``` ## Acceder a los encabezados de respuesta Todos los nombres de encabezados de respuesta están en minúsculas, independientemente de cómo los haya enviado el servidor: ```js const response = await axios.get("/api/resource"); // These are equivalent const contentType = response.headers["content-type"]; const contentType2 = response.headers.get("content-type"); ``` axios-axios-aa54a5e/docs/es/pages/advanced/retry.md000066400000000000000000000074751526660636600223740ustar00rootroot00000000000000# Reintentos y recuperación de errores Las solicitudes de red pueden fallar por razones transitorias — una falla momentánea del servidor, una breve interrupción de la red o una respuesta de límite de tasa. Implementar una estrategia de reintento en un interceptor te permite manejar estos fallos de forma transparente, sin ensuciar el código de tu aplicación. ## Reintento básico con un interceptor de respuesta El enfoque más sencillo es capturar códigos de estado de error específicos y reenviar inmediatamente la solicitud original un número limitado de veces: ```js import axios from "axios"; const api = axios.create({ baseURL: "https://api.example.com" }); const MAX_RETRIES = 3; api.interceptors.response.use( (response) => response, async (error) => { const config = error.config; // Only retry on network errors or 5xx server errors const shouldRetry = !error.response || (error.response.status >= 500 && error.response.status < 600); if (!shouldRetry) { return Promise.reject(error); } config._retryCount = config._retryCount ?? 0; if (config._retryCount >= MAX_RETRIES) { return Promise.reject(error); } config._retryCount += 1; return api(config); } ); ``` ## Retroceso exponencial Reintentar inmediatamente después de un fallo puede sobrecargar a un servidor que ya tiene dificultades. El retroceso exponencial espera progresivamente más tiempo entre cada intento: ```js const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); api.interceptors.response.use( (response) => response, async (error) => { const config = error.config; const shouldRetry = !error.response || (error.response.status >= 500 && error.response.status < 600); if (!shouldRetry) return Promise.reject(error); config._retryCount = config._retryCount ?? 0; if (config._retryCount >= 3) return Promise.reject(error); config._retryCount += 1; // Wait 200ms, 400ms, 800ms, ... before each retry const backoff = 100 * 2 ** config._retryCount; await delay(backoff); return api(config); } ); ``` ## Reintento en 429 (límite de tasa) con Retry-After Cuando el servidor responde con `429 Too Many Requests`, a menudo incluye un encabezado `Retry-After` que indica exactamente cuánto tiempo esperar: ```js api.interceptors.response.use( (response) => response, async (error) => { const config = error.config; if (error.response?.status !== 429) return Promise.reject(error); config._retryCount = config._retryCount ?? 0; if (config._retryCount >= 3) return Promise.reject(error); config._retryCount += 1; const retryAfterHeader = error.response.headers["retry-after"]; const waitMs = retryAfterHeader ? parseFloat(retryAfterHeader) * 1000 // header is in seconds : 1000; // default to 1 second await new Promise((resolve) => setTimeout(resolve, waitMs)); return api(config); } ); ``` ## Desactivar reintentos por solicitud Si algunas solicitudes nunca deben reintentarse (por ejemplo, mutaciones no idempotentes que no deseas duplicar), añade un indicador a la configuración de la solicitud: ```js // Add this to your interceptor before the retry logic: if (config._noRetry) return Promise.reject(error); // Then opt out on specific calls: await api.post("/payments/charge", body, { _noRetry: true }); ``` ## Combinar reintentos con cancelación Usa un `AbortController` para cancelar una solicitud que está esperando un retardo de retroceso: ```js const controller = new AbortController(); try { await api.get("/api/data", { signal: controller.signal }); } catch (error) { if (axios.isCancel(error)) { console.log("Request aborted by user"); } } // Cancel the request (and any pending retry delay) from elsewhere: controller.abort(); ``` axios-axios-aa54a5e/docs/es/pages/advanced/testing.md000066400000000000000000000102101526660636600226610ustar00rootroot00000000000000# Pruebas Probar código que realiza solicitudes HTTP con axios es sencillo. El enfoque recomendado es simular (mock) axios directamente, de modo que las pruebas se ejecuten sin acceder a la red real, dándote control total sobre las respuestas que recibe tu código. ## Simulación con Vitest o Jest Tanto Vitest como Jest admiten la simulación de módulos con `vi.mock` / `jest.mock`. Puedes simular todo el módulo de axios y controlar lo que devuelve cada método: ```js // user-service.js import axios from "axios"; export async function getUser(id) { const { data } = await axios.get(`/api/users/${id}`); return data; } ``` ```js // user-service.test.js import { describe, it, expect, vi } from "vitest"; import axios from "axios"; import { getUser } from "./user-service"; vi.mock("axios"); describe("getUser", () => { it("returns user data on success", async () => { const mockUser = { id: 1, name: "Jay" }; // Make axios.get resolve with our fake response axios.get.mockResolvedValueOnce({ data: mockUser }); const result = await getUser(1); expect(result).toEqual(mockUser); expect(axios.get).toHaveBeenCalledWith("/api/users/1"); }); it("throws when the request fails", async () => { axios.get.mockRejectedValueOnce(new Error("Network error")); await expect(getUser(1)).rejects.toThrow("Network error"); }); }); ``` ## Simular un AxiosError Para probar rutas de manejo de errores que inspeccionan `error.response`, crea una instancia de `AxiosError` directamente: ```js import axios, { AxiosError } from "axios"; import { vi } from "vitest"; const mockError = new AxiosError( "Not Found", "ERR_BAD_REQUEST", {}, // config {}, // request { // response status: 404, statusText: "Not Found", data: { message: "User not found" }, headers: {}, config: {}, } ); axios.get.mockRejectedValueOnce(mockError); ``` ## Usando axios-mock-adapter [axios-mock-adapter](https://github.com/ctimmerm/axios-mock-adapter) es una librería que instala un adaptador personalizado en tu instancia de axios, interceptando las solicitudes a nivel del adaptador. Esto significa que tus interceptores siguen ejecutándose, lo que la hace más adecuada para pruebas de integración. ```bash npm install --save-dev axios-mock-adapter ``` ```js import axios from "axios"; import MockAdapter from "axios-mock-adapter"; const mock = new MockAdapter(axios); // Mock a GET request mock.onGet("/api/users/1").reply(200, { id: 1, name: "Jay" }); // Mock a POST request mock.onPost("/api/users").reply(201, { id: 2, name: "New User" }); // Mock a network error mock.onGet("/api/failing").networkError(); // Mock a timeout mock.onGet("/api/slow").timeout(); ``` Reinicia los mocks entre pruebas: ```js afterEach(() => { mock.reset(); // clear all registered handlers }); ``` ## Probar interceptores Para probar interceptores de forma aislada, crea una nueva instancia de axios en tu prueba: ```js import axios from "axios"; import MockAdapter from "axios-mock-adapter"; describe("auth interceptor", () => { it("attaches a Bearer token to every request", async () => { const instance = axios.create(); const mock = new MockAdapter(instance); // Add your interceptor instance.interceptors.request.use((config) => { config.headers.set("Authorization", "Bearer test-token"); return config; }); // Capture the request config by inspecting what mock received let capturedConfig; mock.onGet("/api/data").reply((config) => { capturedConfig = config; return [200, {}]; }); await instance.get("/api/data"); expect(capturedConfig.headers["Authorization"]).toBe("Bearer test-token"); }); }); ``` ## Consejos - Siempre simula a nivel de módulo (o usa `MockAdapter`) — evita simular métodos individuales en una instancia compartida, ya que el estado puede filtrarse entre pruebas. - Usa `mockResolvedValueOnce` / `mockRejectedValueOnce` en lugar de `mockResolvedValue` para que las pruebas estén aisladas y no se afecten entre sí. - Al probar lógica de reintento, usa `MockAdapter` para que el interceptor bajo prueba realmente se ejecute en cada intento. axios-axios-aa54a5e/docs/es/pages/advanced/type-script.md000066400000000000000000000141241526660636600234770ustar00rootroot00000000000000# TypeScript `axios` incluye definiciones de TypeScript en el paquete npm a través de `index.d.ts` (ESM) e `index.d.cts` (CJS), por lo que la verificación de tipos y el soporte del editor funcionan de manera nativa para ambos formatos de módulo. ## Consideraciones sobre la resolución de módulos Dado que axios publica de forma dual con una exportación por defecto ESM y un `module.exports` CJS, hay algunas consideraciones de configuración: - La configuración recomendada es `"moduleResolution": "node16"` (implícita en `"module": "node16"`). Esto requiere TypeScript 4.7 o superior. - Si usas ESM, tu configuración debería estar bien. - Si compilas TypeScript a CJS y no puedes usar `"moduleResolution": "node16"`, debes habilitar `esModuleInterop`. - Si usas TypeScript para verificar tipos en código JavaScript CJS, tu única opción es `"moduleResolution": "node16"`. ## Type guards para errores de axios Usa el type guard `axios.isAxiosError` para reducir de forma segura los errores `unknown` en bloques `catch`. Tras la reducción, puedes acceder a propiedades específicas de axios como `error.response`, `error.config` y `error.code` con seguridad de tipos completa. ```ts import axios from "axios"; let user: User | null = null; try { const { data } = await axios.get("/user?ID=12345"); user = data.userDetails; } catch (error) { if (axios.isAxiosError(error)) { handleAxiosError(error); } else { handleUnexpectedError(error); } } ``` Usa `axios.isCancel()` para reducir los errores de cancelación a `CanceledError`: ```ts const controller = new AbortController(); try { await axios.get("/user?ID=12345", { signal: controller.signal }); } catch (error) { if (axios.isCancel(error)) { handleCancellation(error); } } ``` ## Tipado de los datos de solicitud y los parámetros de consulta `AxiosRequestConfig` usa `D` para los datos de la solicitud y `P` para los parámetros de consulta. Un serializador de parámetros personalizado recibe el mismo `P`: ```ts import axios, { type AxiosPromise, type AxiosRequestConfig, type InternalAxiosRequestConfig, } from "axios"; interface RequestBody { includeArchived: boolean; } interface SearchParams { query: string; page?: number; } interface SearchResponse { results: string[]; } const searchConfig: AxiosRequestConfig = { data: { includeArchived: false }, params: { query: "axios", page: 1 }, paramsSerializer: (params) => `${params.query}:${params.page ?? 1}`, }; const response = await axios.get("/search", searchConfig); response.config.data; // RequestBody | undefined response.config.params; // SearchParams | undefined const invalidConfig: AxiosRequestConfig = { // @ts-expect-error `query` debe ser una cadena params: { query: 123 }, }; ``` Los resultados predeterminados conservan `D` y `P` en `response.config`, incluso cuando los alias de solicitud infieren esos tipos a partir de una configuración tipada. `RawAxiosRequestConfig`, `InternalAxiosRequestConfig`, `AxiosDefaults`, `CreateAxiosDefaults`, `AxiosResponse`, `AxiosPromise`, `AxiosError`, `CanceledError`, las instancias invocables, los adaptadores y `mergeConfig()` también conservan el tipo de los parámetros. Los métodos de solicitud añaden `P` como último genérico —``— para mantener sin cambios las posiciones existentes de datos de respuesta (`T`), respuesta personalizada (`R`) y datos de solicitud (`D`). Un tipo de respuesta personalizado proporcionado explícitamente sigue controlando el valor resuelto. `P` vale `any` por defecto para mantener la compatibilidad. Un adaptador u otra promesa tipada explícitamente puede conservar ambos tipos de la solicitud: ```ts const searchAdapter = ( config: InternalAxiosRequestConfig ): AxiosPromise => Promise.resolve({ data: { results: [] }, status: 200, statusText: "OK", headers: {}, config, }); declare const error: unknown; if (axios.isCancel(error)) { error.config?.data; // RequestBody | undefined error.config?.params; // SearchParams | undefined } ``` ## Instancias e interceptores tipados Anota el resultado de `axios.create` con `AxiosInstance`, y anota los interceptores de solicitud con `InternalAxiosRequestConfig` para obtener verificación de tipos de extremo a extremo en un cliente personalizado: ```ts import axios, { AxiosInstance, InternalAxiosRequestConfig } from "axios"; const apiClient: AxiosInstance = axios.create({ baseURL: "https://api.example.com", timeout: 10000, }); apiClient.interceptors.request.use((config: InternalAxiosRequestConfig) => { // Añadir token de autenticación, registrar, etc. return config; }); ``` ## Configuración de solicitud personalizada con claves de tipo símbolo axios conserva las propiedades de símbolo propias y enumerables al combinar la configuración predeterminada con la de cada solicitud. Las aplicaciones pueden ampliar `AxiosRequestConfig` con una clave de símbolo específica y leer la opción desde `InternalAxiosRequestConfig` en un interceptor o adaptador: ```ts import axios from "axios"; export const someFlag: unique symbol = Symbol( "some flag used in request interceptor" ); declare module "axios" { interface AxiosRequestConfig { [someFlag]?: boolean; } } axios.interceptors.request.use((config) => { if (config[someFlag]) { config.headers.set("X-Some-Flag", "enabled"); } return config; }); await axios.get("/users", { [someFlag]: true }); ``` Solo se copian las propiedades de símbolo propias y enumerables. Las propiedades de símbolo heredadas o no enumerables no se copian. ## Tipado de los datos de respuesta Los métodos de solicitud de Axios son genéricos sobre el tipo de los datos de respuesta. Pasa un parámetro de tipo a `axios.get` (y a los demás alias) para tipar `response.data`: ```ts interface User { id: number; name: string; } const { data } = await apiClient.get("/users/1"); // `data` está tipado como `User` ``` axios-axios-aa54a5e/docs/es/pages/advanced/x-www-form-urlencoded-format.md000066400000000000000000000114271526660636600266610ustar00rootroot00000000000000# Formato x-www-form-urlencoded ## URLSearchParams De forma predeterminada, axios serializa los objetos JavaScript a `JSON`. Para enviar datos en el formato [`application/x-www-form-urlencoded`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) en su lugar, puedes usar la API [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams), que es [compatible](http://www.caniuse.com/#feat=urlsearchparams) con la gran mayoría de los navegadores, y con [Node.js](https://nodejs.org/api/url.html#url_class_urlsearchparams) a partir de la versión v10 (lanzada en 2018). ```js const params = new URLSearchParams({ foo: 'bar' }); params.append('extraparam', 'value'); axios.post('/foo', params); ``` ## Cadena de consulta Para navegadores más antiguos o entornos sin `URLSearchParams`, puedes usar la librería [`qs`](https://github.com/ljharb/qs) para serializar objetos al formato `application/x-www-form-urlencoded`. ```js const qs = require('qs'); axios.post('/foo', qs.stringify({ bar: 123 })); ``` Para tener control total sobre los encabezados y el método, pasa la salida de `qs.stringify` como `data` de la solicitud y establece `Content-Type` explícitamente: ```js import qs from 'qs'; const data = { bar: 123 }; const options = { method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, data: qs.stringify(data), + url: 'https://example.com/', }; axios(options); ``` En versiones muy antiguas de Node.js, puedes usar el módulo integrado `querystring` que viene con Node.js. Ten en cuenta que este módulo fue marcado como obsoleto en Node.js v16 — prefiere `URLSearchParams` o `qs` para código nuevo. ```js const querystring = require('querystring'); axios.post('https://something.com/', querystring.stringify({ foo: 'bar' })); ``` ::: tip Prefiere `qs` para objetos anidados La librería `qs` es preferible si necesitas serializar objetos anidados, ya que el método `querystring` tiene [problemas conocidos](https://github.com/nodejs/node-v0.x-archive/issues/1665) con ese caso de uso. ::: ## Serialización automática a URLSearchParams A partir de la versión v0.21.0, axios serializa automáticamente los objetos JavaScript a `URLSearchParams` si el encabezado `Content-Type` está establecido en `application/x-www-form-urlencoded`. Esto significa que puedes pasar un objeto JavaScript directamente a la propiedad `data` de la configuración de solicitud de axios. Por ejemplo, al pasar datos a una solicitud `POST`: ```js const data = { x: 1, arr: [1, 2, 3], arr2: [1, [2], 3], users: [ { name: 'Peter', surname: 'Griffin' }, { name: 'Thomas', surname: 'Anderson' }, ], }; await axios.postForm('https://postman-echo.com/post', data, { headers: { 'content-type': 'application/x-www-form-urlencoded' }, }); ``` El objeto `data` será serializado automáticamente a `URLSearchParams` y enviado en el formato `application/x-www-form-urlencoded`. El servidor recibirá los siguientes datos: ```json { "x": "1", "arr[]": ["1", "2", "3"], "arr2[0]": "1", "arr2[1][0]": "2", "arr2[2]": "3", "users[0][name]": "Peter", "users[0][surname]": "Griffin", "users[1][name]": "Thomas", "users[1][surname]": "Anderson" } ``` Si el analizador de cuerpo de tu backend (como `body-parser` de `express.js`) admite la decodificación de objetos anidados, recibirás el mismo objeto en el lado del servidor automáticamente. ## Límite de profundidad para la serialización de parámetros Cuando axios serializa un objeto `params` mediante `AxiosURLSearchParams`, se llama al mismo recorrido recursivo utilizado por el serializador de FormData. Una opción `maxDepth` (predeterminado `100`) limita la profundidad de recursión. Las cargas útiles que exceden el límite lanzan un `AxiosError` con `code: 'ERR_FORM_DATA_DEPTH_EXCEEDED'` en lugar de desbordar la pila de llamadas. El miembro compartido de tipo `SerializerOptions.Blob` solo afecta la serialización a `FormData` compatible con la especificación; no tiene efecto al serializar a `URLSearchParams`. ```js // Aumentar el límite si tu objeto params legítimamente anida más de 100 niveles: axios.get('/api', { params: deepObject, paramsSerializer: { maxDepth: 200 } }); ``` ::: warning Nota de seguridad Solo aumenta `maxDepth` si tu esquema realmente lo requiere. El valor predeterminado de 100 protege el código del lado del servidor que reenvía datos controlados por el cliente a axios como `params` contra ataques DoS mediante objetos profundamente anidados. ::: ```js var app = express(); app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies app.post('/', function (req, res, next) { // echo body as JSON res.send(JSON.stringify(req.body)); }); server = app.listen(3000); ``` axios-axios-aa54a5e/docs/es/pages/getting-started/000077500000000000000000000000001526660636600222305ustar00rootroot00000000000000axios-axios-aa54a5e/docs/es/pages/getting-started/examples/000077500000000000000000000000001526660636600240465ustar00rootroot00000000000000axios-axios-aa54a5e/docs/es/pages/getting-started/examples/commonjs.md000066400000000000000000000104451526660636600262210ustar00rootroot00000000000000# Ejemplos en JavaScript ## Importar la librería Para importar la librería en un entorno CommonJS, puedes usar la función `require`, o la declaración `import` si estás usando un empaquetador como Webpack o Rollup. #### Sin empaquetador ```js const axios = require("axios"); ``` #### Con empaquetador (webpack, rollup, vite, etc) ```js import axios from "axios"; ``` ## Usando then/catch/finally Dado que axios devuelve una Promise en su núcleo, puedes optar por usar callbacks con `then`, `catch` y `finally` para manejar los datos de la respuesta, los errores y la finalización. ### Solicitud Get ```js axios .get("https://jsonplaceholder.typicode.com/posts", { params: { postId: 5, }, }) .then((response) => { console.log(response.data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Request completed"); }); ``` ### Solicitud Post ```js axios .post("https://jsonplaceholder.typicode.com/posts", { title: "foo", body: "bar", userId: 1, }) .then((response) => { console.log(response.data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Request completed"); }); ``` ### Solicitud Put ```js axios .put("https://jsonplaceholder.typicode.com/posts/1", { title: "foo", body: "bar", userId: 1, }) .then((response) => { console.log(response.data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Request completed"); }); ``` ### Solicitud Patch ```js axios .patch("https://jsonplaceholder.typicode.com/posts/1", { title: "foo", }) .then((response) => { console.log(response.data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Request completed"); }); ``` ### Solicitud Delete ```js axios .delete("https://jsonplaceholder.typicode.com/posts/1") .then((response) => { console.log(response.data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Request completed"); }); ``` ## Usando async/await Otra forma de manejar promises es mediante `async` y `await`. Esto te permite usar bloques try/catch/finally para manejar errores y la finalización. Esto puede hacer tu código más legible y fácil de entender, y también ayuda a evitar el llamado "callback hell". ::: tip Nota: async/await forma parte de ECMAScript 2017 y no está disponible en Internet Explorer ni en navegadores más antiguos, así que úsalo con precaución. ::: ### Solicitud Get ```js const getPosts = async () => { try { const response = await axios.get( "https://jsonplaceholder.typicode.com/posts", { params: { postId: 5, }, } ); console.log(response.data); } catch (error) { console.error(error); } finally { console.log("Request completed"); } }; ``` ### Solicitud Post ```js const createPost = async () => { try { const response = await axios.post( "https://jsonplaceholder.typicode.com/posts", { title: "foo", body: "bar", userId: 1, } ); console.log(response.data); } catch (error) { console.error(error); } finally { console.log("Request completed"); } }; ``` ### Solicitud Put ```js const updatePost = async () => { try { const response = await axios.put( "https://jsonplaceholder.typicode.com/posts/1", { title: "foo", body: "bar", userId: 1, } ); console.log(response.data); } catch (error) { console.error(error); } finally { console.log("Request completed"); } }; ``` ### Solicitud Patch ```js const updatePost = async () => { try { const response = await axios.patch( "https://jsonplaceholder.typicode.com/posts/1", { title: "foo", } ); console.log(response.data); } catch (error) { console.error(error); } finally { console.log("Request completed"); } }; ``` ### Solicitud Delete ```js const deletePost = async () => { try { const response = await axios.delete( "https://jsonplaceholder.typicode.com/posts/1" ); console.log(response.data); } catch (error) { console.error(error); } finally { console.log("Request completed"); } }; ``` axios-axios-aa54a5e/docs/es/pages/getting-started/examples/typescript.md000066400000000000000000000066551526660636600266120ustar00rootroot00000000000000# Ejemplo en TypeScript ## Importar tipos axios incluye definiciones de TypeScript de forma nativa. Puedes importar los tipos que necesites directamente desde `"axios"`: ```ts import axios from "axios"; import type { AxiosRequestConfig, AxiosResponse, AxiosError } from "axios"; ``` ## Tipar una solicitud Usa un parámetro de tipo genérico en la respuesta para indicarle a TypeScript la forma que tendrán tus datos: ```ts import axios from "axios"; type Post = { userId: number; id: number; title: string; body: string; }; const response = await axios.get("https://jsonplaceholder.typicode.com/posts/1"); console.log(response.data.title); // TypeScript knows this is a string ``` ## Tipar una función Envuelve las solicitudes en funciones con tipos de retorno explícitos para maximizar la seguridad de tipos: ```ts import axios, { AxiosResponse } from "axios"; type Post = { userId: number; id: number; title: string; body: string; }; const getPost = async (id: number): Promise => { const response = await axios.get( `https://jsonplaceholder.typicode.com/posts/${id}` ); return response.data; }; ``` ## Tipar una solicitud POST Puedes tipar tanto el cuerpo de la solicitud como la respuesta esperada: ```ts type CreatePostBody = { title: string; body: string; userId: number; }; type CreatePostResponse = CreatePostBody & { id: number }; const createPost = async (data: CreatePostBody): Promise => { const response = await axios.post( "https://jsonplaceholder.typicode.com/posts", data ); return response.data; }; ``` ## Instancia de axios tipada Crea una instancia tipada para que la URL base y los encabezados queden definidos desde el inicio: ```ts import axios from "axios"; import type { AxiosInstance } from "axios"; const api: AxiosInstance = axios.create({ baseURL: "https://api.example.com", timeout: 5000, }); ``` ## Interceptores tipados Usa `InternalAxiosRequestConfig` (no `AxiosRequestConfig`) para los interceptores de solicitud en v1.x: ```ts import axios from "axios"; import type { InternalAxiosRequestConfig, AxiosResponse } from "axios"; api.interceptors.request.use((config: InternalAxiosRequestConfig) => { config.headers.set("Authorization", `Bearer ${getToken()}`); return config; }); api.interceptors.response.use( (response: AxiosResponse) => response, (error) => Promise.reject(error) ); ``` ## Tipar errores Usa `axios.isAxiosError()` para acotar el tipo de un error capturado: ```ts import axios, { AxiosError } from "axios"; type ApiError = { message: string; code: number; }; try { await axios.get("/api/protected-resource"); } catch (error) { if (axios.isAxiosError(error)) { // error.response?.data is typed as ApiError console.error(error.response?.data.message); console.error(error.response?.status); } else { throw error; } } ``` ## Notas sobre la configuración de TypeScript Dado que axios publica tanto en formato ESM como CJS, hay algunas consideraciones según tu configuración: - La configuración recomendada es `"moduleResolution": "node16"` (implícita en `"module": "node16"`). Esto requiere TypeScript 4.7 o superior. - Si compilas TypeScript a CJS y no puedes usar `"moduleResolution": "node16"`, habilita `"esModuleInterop": true`. - Si usas TypeScript para verificar tipos en código JavaScript CJS, tu única opción es `"moduleResolution": "node16"`. axios-axios-aa54a5e/docs/es/pages/getting-started/features.md000066400000000000000000000061241526660636600243730ustar00rootroot00000000000000# Características axios es un potente cliente HTTP que proporciona una API simple y fácil de usar para realizar solicitudes HTTP. Es compatible con todos los navegadores modernos y es ampliamente utilizado en la comunidad JavaScript. A continuación se presentan algunas de las características que hacen de axios una excelente opción para tu próximo proyecto. ## Isomórfico axios es un cliente HTTP universal que puede usarse tanto en el navegador como en Node.js. Esto significa que puedes utilizar axios para hacer solicitudes a APIs desde tu código frontend y también desde tu código backend. Esto convierte a axios en una gran opción para construir progressive web apps, aplicaciones de una sola página (SPA) y aplicaciones con renderizado del lado del servidor. axios también es una excelente opción para equipos que trabajan tanto en frontend como en backend. Al usar axios en ambos lados, puedes contar con una API consistente para realizar solicitudes HTTP, lo que ayuda a reducir la complejidad de tu base de código. ## Soporte para Fetch axios ofrece soporte de primera clase para la Fetch API, que es un reemplazo moderno de la API XHR. El adaptador es opcional y puede habilitarse mediante configuración. Se mantiene la misma API tanto para el adaptador XHR como para el adaptador Fetch, lo que facilita adoptar la Fetch API en tu base de código sin necesidad de modificar el código existente. ## Soporte de navegadores axios es compatible con todos los navegadores modernos y algunos más antiguos, incluyendo Chrome, Firefox, Safari y Edge. axios es una excelente opción para construir aplicaciones web que necesiten soportar una amplia variedad de navegadores. ## Soporte de Node.js axios también es compatible con una amplia gama de versiones de Node.js, con compatibilidad probada hasta la versión v12.x, lo que lo convierte en una buena opción en entornos donde actualizar a la última versión de Node.js puede no ser posible o práctico. Además de Node.js, axios cuenta con pruebas de humo para Bun y Deno que validan comportamientos clave en tiempo de ejecución y mejoran la confianza en la compatibilidad entre distintos entornos. ## Características adicionales - Compatible con la API de Promise - Interceptar solicitudes y respuestas - Transformar datos de solicitudes y respuestas - Controlador de cancelación (Abort controller) - Tiempos de espera (timeouts) - Serialización de parámetros de consulta con soporte para entradas anidadas - Serialización automática del cuerpo de la solicitud a: - JSON (application/json) - Multipart / FormData (multipart/form-data) - Formulario codificado en URL (application/x-www-form-urlencoded) - Envío de formularios HTML como JSON - Manejo automático de datos JSON en la respuesta - Captura de progreso para navegadores y Node.js con información adicional (velocidad de transferencia, tiempo restante) - Configuración de límites de ancho de banda para Node.js - Compatible con FormData y Blob conformes a la especificación (incluyendo Node.js) - Soporte del lado del cliente para protección contra XSRF axios-axios-aa54a5e/docs/es/pages/getting-started/first-steps.md000066400000000000000000000100711526660636600250340ustar00rootroot00000000000000# Primeros pasos ¡Bienvenido a la documentación de axios! Esta guía te ayudará a comenzar con axios y a realizar tu primera solicitud a una API. Si eres nuevo en axios, te recomendamos empezar aquí. ## Instalación Puedes usar axios en tu proyecto de varias formas. La más común es instalarlo desde npm e incluirlo en tu proyecto. También ofrecemos soporte para jsDelivr, unpkg y más. #### Usando npm ```bash npm install axios ``` #### Usando pnpm ```bash pnpm install axios ``` #### Usando yarn ```bash yarn add axios ``` #### Usando bun ```bash bun add axios ``` #### Usando deno ```bash deno install npm:axios ``` #### Usando jsDelivr Al usar jsDelivr, recomendamos utilizar la versión minificada y fijar el número de versión para evitar cambios inesperados. Si deseas usar la última versión, puedes hacerlo omitiendo el número de versión. Esto está fuertemente desaconsejado para uso en producción, ya que puede ocasionar cambios inesperados en tu aplicación. ```html ``` #### Usando unpkg Al usar unpkg, recomendamos utilizar la versión minificada y fijar el número de versión para evitar cambios inesperados. Si deseas usar la última versión, puedes hacerlo omitiendo el número de versión. Esto está fuertemente desaconsejado para uso en producción, ya que puede ocasionar cambios inesperados en tu aplicación. ```html ``` ## Importación de axios Una vez instalado, puedes importar la librería usando `import` o `require`: ```js import axios, { isCancel, AxiosError } from "axios"; ``` También puedes usar la exportación por defecto, ya que las exportaciones nombradas son simplemente re-exportaciones desde la fábrica de axios: ```js import axios from "axios"; console.log(axios.isCancel("something")); ``` Si usas `require` para importar, **solo está disponible la exportación por defecto**: ```js const axios = require("axios"); console.log(axios.isCancel("something")); ``` Para algunos bundlers y linters de ES6 puede que necesites: ```js import { default as axios } from "axios"; ``` Para entornos personalizados o heredados donde la resolución de módulos no se comporta correctamente, puedes importar el bundle preconstruido directamente: ```js const axios = require("axios/dist/browser/axios.cjs"); // bundle CommonJS para navegador (ES2017) // const axios = require("axios/dist/node/axios.cjs"); // bundle CommonJS para node (ES2017) ``` ## Tu primera solicitud Una solicitud con axios puede realizarse en tan solo dos líneas de código. Hacer tu primera solicitud con axios es muy sencillo. Puedes hacer una solicitud a cualquier API indicando la URL y el método. Por ejemplo, para hacer una solicitud GET a la API de JSONPlaceholder, puedes usar el siguiente código: ```js import axios from "axios"; const response = await axios.get( "https://jsonplaceholder.typicode.com/posts/1" ); console.log(response.data); ``` axios ofrece una API sencilla para realizar solicitudes. Puedes usar el método `axios.get` para hacer una solicitud GET, el método `axios.post` para hacer una solicitud POST, y así sucesivamente. También puedes usar el método `axios.request` para hacer una solicitud con cualquier método. ::: tip Establece un timeout en producción Sin un `timeout`, una solicitud detenida puede colgarse indefinidamente. Pásalo en la configuración de la solicitud: ```js const response = await axios.get("https://example.com/data", { timeout: 5000, // 5 segundos }); ``` Consulta [`timeout` en la configuración de solicitud](/pages/advanced/request-config#timeout) y [Manejo de errores](/pages/advanced/error-handling) para los códigos `ECONNABORTED` / `ETIMEDOUT` correspondientes. ::: ## Próximos pasos Ahora que has realizado tu primera solicitud con axios, estás listo para explorar el resto de la documentación. Puedes aprender más sobre cómo hacer solicitudes, manejar respuestas y usar axios en tus proyectos. Consulta el resto de la documentación para saber más. axios-axios-aa54a5e/docs/es/pages/getting-started/upgrade-guide.md000066400000000000000000000133471526660636600253040ustar00rootroot00000000000000# Guía de actualización Esta guía tiene como objetivo ayudarte a actualizar tu proyecto de una versión del framework a otra. Se recomienda leer las notas de la versión de cada versión mayor desde la que estás migrando, ya que pueden contener información importante sobre cambios que rompen la compatibilidad. ## Actualización a v1.19.0 ### Se rechazan las URL HTTP(S) malformadas Ahora las solicitudes rechazan una `url` o `baseURL` `http:` o `https:` que omita `//` después del protocolo. Sustituye valores como `https:example.com` o `https:/example.com` por una URL bien formada como `https://example.com`. El `AxiosError` resultante usa el código `ERR_INVALID_URL` e identifica de forma segura la URL incorrecta con sus secretos ocultos. Este cambio de seguridad intencional evita que la normalización de URL malformadas eluda `baseURL` o las listas de direcciones permitidas. ### Errores de interceptores de solicitud síncronos Cuando un interceptor de solicitud síncrono lanza un error, axios invoca su manejador de rechazo emparejado y detiene los interceptores de solicitud restantes. Un manejador de rechazo que retorna normalmente trata el error como resuelto y envía la solicitud con la última configuración válida; su valor de retorno no se convierte en la configuración. Si una validación debe impedir el envío, omite el manejador de rechazo o haz que lance o devuelva una Promise rechazada. Los errores terminales continúan por los interceptores de rechazo de respuesta. ## Actualización de v0.x a v1.x ### Cambios en la declaración de importación En v1.x, la declaración de importación fue modificada para usar la exportación `default`. Esto significa que deberás actualizar tus importaciones para usar la exportación `default`. ```diff - import { axios } from "axios"; + import axios from "axios"; ``` ### Cambios en el sistema de interceptores En v1.x debes usar el tipo `InternalAxiosRequestConfig` para tipar el parámetro `config` en el interceptor de `request`. Esto se debe a que el parámetro `config` ahora es del tipo `InternalAxiosRequestConfig` en lugar del tipo público `AxiosRequestConfig`. ```diff - axios.interceptors.request.use((config: AxiosRequestConfig) => { + axios.interceptors.request.use((config: InternalAxiosRequestConfig) => { return config; }); ``` ### Cambios en la estructura de los encabezados de solicitud En v1.x, la estructura de los encabezados de solicitud fue modificada para eliminar la propiedad `common`. Esto significa que deberás actualizar tu código para usar la nueva estructura de encabezados de solicitud de la siguiente manera: ```diff - if (request.headers?.common?.Authorization) { - request.headers.common.Authorization = ... + if (request.headers?.Authorization) { + request.headers.Authorization = ... ``` Los encabezados predeterminados que antes estaban bajo `common`, `get`, `post`, etc., ahora se definen directamente en `axios.defaults.headers`: ```diff - axios.defaults.headers.common["Accept"] = "application/json"; + axios.defaults.headers["Accept"] = "application/json"; ``` ### Datos multipart en formularios Si una solicitud incluye un payload de tipo `FormData`, el encabezado `Content-Type: multipart/form-data` ahora se establece automáticamente. Elimina cualquier encabezado manual para evitar duplicados: ```diff - axios.post("/upload", formData, { - headers: { "Content-Type": "multipart/form-data" }, - }); + axios.post("/upload", formData); ``` Si defines explícitamente `Content-Type: application/json`, axios ahora serializará automáticamente los datos a JSON. ### Serialización de parámetros v1.x introdujo varios cambios que rompen la compatibilidad en la forma en que se serializan los parámetros de URL. Los más importantes son: **Los `params` ahora se codifican por porcentaje (percent-encoded) de forma predeterminada.** Si tu backend esperaba corchetes sin codificar al estilo qs, puede que necesites configurar un serializador personalizado: ```js import qs from 'qs'; axios.create({ paramsSerializer: { serialize: (params) => qs.stringify(params, { arrayFormat: 'brackets' }), }, }); ``` **Los objetos anidados en `params` ahora se serializan con notación de corchetes** (`foo[bar]=1`) en lugar de notación de punto. Si tu backend esperaba notación de punto, usa un serializador personalizado. **Los parámetros con valor `null` o `undefined`** ahora se manejan de forma consistente: los valores `null` se serializan como cadenas vacías, mientras que los valores `undefined` se omiten completamente. Para ver todas las opciones de configuración de serialización de parámetros, consulta la página de [Configuración de solicitud](/pages/advanced/request-config). ### Los internos ya no se exportan Hemos decidido dejar de exportar los internos de axios. Esto significa que deberás actualizar tu código para usar únicamente la API pública de axios. Este cambio se realizó para simplificar la API y reducir la superficie expuesta de axios, permitiéndonos modificar los internos sin declararlos como cambios que rompen la compatibilidad. Consulta la [Referencia de la API](/pages/advanced/api-reference) en este sitio para obtener la información más actualizada sobre la API pública de axios. ### Configuración de solicitud Hemos realizado cambios en el objeto de configuración de solicitud. Consulta la [referencia de configuración](/pages/advanced/request-config) en este sitio para obtener la información más actualizada. ### Cambios que rompen la compatibilidad no cubiertos Esta guía no es exhaustiva y puede no cubrir todos los cambios que rompen la compatibilidad. Si encuentras algún problema, por favor abre un issue en el [repositorio GitHub de la documentación](https://github.com/axios/docs) con la etiqueta `breaking change`. axios-axios-aa54a5e/docs/es/pages/misc/000077500000000000000000000000001526660636600200565ustar00rootroot00000000000000axios-axios-aa54a5e/docs/es/pages/misc/security.md000066400000000000000000000305341526660636600222540ustar00rootroot00000000000000# Política de seguridad ## ⚠� Bomba de descompresión / almacenamiento de respuesta sin límites Por defecto, `maxContentLength` y `maxBodyLength` están configurados en `-1` (sin límite). Un servidor malicioso o comprometido puede devolver un cuerpo pequeño comprimido con gzip/deflate/brotli/zstd que se expande a gigabytes, agotando la memoria del proceso Node.js. **Si realizas solicitudes a servidores en los que no confías plenamente, DEBES establecer un `maxContentLength` (y `maxBodyLength`) adecuado para tu carga de trabajo.** El límite se aplica chunk a chunk durante la descompresión en flujo, así que basta con configurarlo para neutralizar ataques de bomba de descompresión. ```js axios.get('https://example.com/data', { maxContentLength: 10 * 1024 * 1024, // 10 MB maxBodyLength: 10 * 1024 * 1024, }); // O globalmente: axios.defaults.maxContentLength = 10 * 1024 * 1024; axios.defaults.maxBodyLength = 10 * 1024 * 1024; ``` El valor por defecto no se ha endurecido porque hacerlo romperá silenciosamente cualquier descarga legítima mayor al límite elegido. La responsabilidad de escoger un tope seguro para fuentes no confiables recae en la aplicación. ## Otras opciones sensibles a la seguridad Las siguientes opciones de configuración de solicitud tienen implicaciones directas de seguridad. Están documentadas en detalle junto al resto de la [configuración de solicitud](/pages/advanced/request-config), y se resumen aquí para que aparezcan en un solo lugar. | Opción | Riesgo | Mitigación | | --- | --- | --- | | [`baseURL`](/pages/advanced/request-config#baseurl) | Algunas aplicaciones tratan un prefijo de ruta en `baseURL`, como `https://api.example.com/v1/`, como un límite de solicitud. Los valores relativos de `url` controlados por usuarios pueden contener segmentos `..` que el parser final de URL normaliza y resuelve fuera de ese prefijo de ruta. | No dependas de `baseURL` para aislar rutas. Valida las rutas no confiables antes de pasarlas a axios; rechaza URLs absolutas o relativas al protocolo y segmentos `..`, o verifica el origen y el pathname de la URL resuelta contra una allowlist. | | [`socketPath`](/pages/advanced/request-config#socketpath) | Si proviene de entrada no confiable, un atacante puede redirigir el tráfico a sockets locales privilegiados como `/var/run/docker.sock`, eludiendo las protecciones SSRF basadas en hostname (CWE-918). | Filtra o restringe con allowlist las claves de configuración provenientes de entradas no confiables. Usa [`allowedSocketPaths`](/pages/advanced/request-config#allowedsocketpaths) para restringir las rutas de socket aceptadas. | | [`beforeRedirect`](/pages/advanced/request-config#beforeredirect) | Se ejecuta después de que `follow-redirects` elimina las credenciales en una bajada de protocolo. Reinyectar credenciales sin verificar el protocolo de destino puede filtrarlas sobre HTTP en texto plano. | Reinyecta credenciales únicamente para destinos HTTPS de confianza. Verifica `options.protocol === "https:"` antes de asignar `auth`. | | [`sensitiveHeaders`](/pages/advanced/request-config#sensitiveheaders) | Encabezados secretos personalizados como `X-API-Key` pueden reenviarse por el adaptador HTTP de Node.js al seguir redirecciones a un origen diferente. | Lista esos nombres de encabezado en `sensitiveHeaders`; axios elimina las coincidencias sin distinguir mayúsculas/minúsculas en redirecciones de origen cruzado. Las redirecciones al mismo origen los conservan. | | [`withXSRFToken`](/pages/advanced/request-config#withxsrftoken) | Establecerlo en `true` fuerza el encabezado XSRF en solicitudes de origen cruzado. Versiones anteriores de axios lo habilitaban implícitamente con `withCredentials: true`; las versiones más recientes requieren ambos indicadores. | Déjalo en `undefined` (solo mismo origen) salvo que tu backend valide explícitamente XSRF en solicitudes de origen cruzado. | | [`redact`](/pages/advanced/request-config#redact) | `AxiosError#toJSON()` incluye la configuración de la solicitud por defecto, lo que puede filtrar encabezados `Authorization` o credenciales `auth` en logs y telemetría de errores. | Pasa un arreglo `redact` con los nombres de claves de configuración sensibles. La coincidencia es insensible a mayúsculas y recursiva. | | [`formDataHeaderPolicy`](/pages/advanced/request-config#formdataheaderpolicy) | Un `FormData` personalizado cuyo `getHeaders()` devuelve valores controlados por un atacante puede sobrescribir encabezados como `Authorization` o inyectar otros arbitrarios en Node.js. | Establece `'content-only'` para copiar únicamente `Content-Type` y `Content-Length`, y luego define los demás encabezados explícitamente a través de la configuración `headers` de la solicitud. | ## Endurecimiento de la cadena de suministro: `ignore-scripts` y scripts de ciclo de vida El repositorio incluye un `.npmrc` a nivel de proyecto que establece `ignore-scripts=true`. Esto bloquea los scripts de ciclo de vida de npm (`preinstall`, `install`, `postinstall`, `prepare`) de cualquier dependencia directa o transitiva al ejecutar `npm install` o `npm ci` dentro del repositorio. Consulta [THREATMODEL.md](https://github.com/axios/axios/blob/v1.x/THREATMODEL.md) (amenaza T-S2) para conocer la justificación. Una consecuencia: el propio hook `prepare` del repositorio (que instala los git hooks de Husky) **no** se ejecuta automáticamente. Tras tu primera instalación, habilita los git hooks manualmente: ```bash npm ci npm rebuild husky && npx husky ``` Ejecuta esos dos comandos una vez por checkout limpio. **No** necesitas volver a ejecutarlos tras cada `npm install` posterior. ::: danger No elimines `ignore-scripts=true` Eliminar `ignore-scripts=true` de `.npmrc` para "arreglar" la configuración de husky reabre la superficie de ataque de los scripts de ciclo de vida para todos los demás paquetes del árbol. Todos los workflows de CI ya invocan npm con `--ignore-scripts`, así que el comportamiento local coincide con CI. ::: Recomendamos el mismo ajuste `ignore-scripts=true` en cualquier proyecto consumidor que incorpore axios (o cualquier otra dependencia) en un entorno de compilación que maneje secretos. ## Verificar una publicación Cada tarball de `axios` publicado en npm proviene de GitHub Actions y lleva una [atestación de provenance de npm](https://docs.npmjs.com/generating-provenance-statements) que vincula criptográficamente el paquete al workflow y al SHA del commit que lo generó. Los consumidores pueden verificar la provenance localmente: ```bash # Verifica todos los paquetes de tu lockfile, incluido axios npm audit signatures ``` Una verificación exitosa demuestra que el tarball fue construido en el entorno de GitHub Actions de `axios/axios` sobre un commit conocido — no fue alterado entre la compilación y el registro. **No** demuestra que el código de ese commit esté libre de bugs. Si `npm audit signatures` reporta una atestación ausente o inválida para una versión reciente de `axios`, trátalo como un posible incidente de cadena de suministro y repórtalo por el canal privado indicado abajo. ## Reportar una vulnerabilidad Si crees haber encontrado una vulnerabilidad de seguridad en el proyecto, por favor repórtanosla como se describe a continuación. Tomamos todas las vulnerabilidades de seguridad con seriedad. Si has encontrado una vulnerabilidad en una librería de terceros, por favor repórtala a los responsables de esa librería. ## Proceso de reporte Por favor, no reportes vulnerabilidades de seguridad a través de los issues públicos de GitHub. Usa el canal oficial de seguridad en GitHub enviando un [aviso de seguridad](https://github.com/axios/axios/security/advisories/new). ## Política de divulgación Cuando recibimos un reporte de vulnerabilidad, asignamos un responsable principal. El responsable confirma el problema, determina las versiones afectadas, evalúa la gravedad, desarrolla y publica una corrección, y coordina la divulgación pública con quien reportó. ### Compromiso de resolución y divulgación en 60 días Nos comprometemos a **resolver y divulgar públicamente cada aviso de seguridad válido dentro de los 60 días naturales posteriores al reporte inicial**, contados desde el momento en que se recibe el reporte a través del [canal de avisos de seguridad de GitHub](https://github.com/axios/axios/security/advisories/new). El plazo de 60 días es un compromiso con quienes reportan y con los consumidores aguas abajo — es un mínimo exigible, no una meta. Si no podemos entregar una corrección a tiempo, publicamos de todos modos el aviso el día 60 con la mejor guía de mitigación disponible para que los consumidores puedan actuar. **Hitos dentro de la ventana de 60 días:** | Día | Hito | | ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | 0 | Reporte recibido. Aviso privado abierto en GitHub. | | ≤ 3 | Acuse de recibo enviado a quien reporta. Decisión de triaje: dentro de alcance / fuera de alcance / duplicado / falta información. | | ≤ 10 | Gravedad evaluada (CVSS v4 cuando aplique). Versiones afectadas confirmadas. CVE solicitado vía GitHub si procede un identificador público. | | ≤ 45 | Corrección desarrollada, revisada y probada. Candidata de versión preparada en rama privada. Se ofrece vista previa a quien reporta para validación. | | ≤ 60 | Versión parcheada publicada en npm. Aviso público + CVE publicados. Se acredita a quien reportó salvo solicitud contraria. CHANGELOG actualizado. | **Excepciones y prórrogas.** - Si quien reporta solicita un embargo más corto (por ejemplo, planea presentar los hallazgos en una conferencia), lo acomodamos cuando sea posible. - Si la corrección requiere un cambio disruptivo, coordinación con consumidores aguas abajo importantes, o una publicación upstream de `follow-redirects` / `form-data` / `proxy-from-env`, podemos extender más allá de los 60 días. Cualquier prórroga se divulga públicamente el día 60 vía el aviso, con un ETA revisado y el motivo. - Si un reporte está **fuera de alcance** (por ejemplo, cae bajo un non-goal explícito documentado en el [modelo de amenazas](https://github.com/axios/axios/blob/v1.x/THREATMODEL.md) del proyecto), lo cerramos con una explicación dentro de la ventana de triaje (≤ 3 días). Los reportes fuera de alcance no entran en la cola de 60 días. - Las **vulnerabilidades activamente explotadas** se tratan como incidentes: la corrección y el aviso salen tan pronto como se valide un parche, no según el calendario de 60 días. **Expectativas sobre quien reporta.** Mientras un reporte esté bajo embargo, pedimos que se abstengan de divulgación pública hasta el primero de: (a) la publicación coordinada del aviso, o (b) el día 60. Si la fecha límite de 60 días pasa sin acción de nuestra parte, quien reportó queda libre de divulgar por su cuenta — consideraremos eso un fallo nuestro, no suyo. ## Actualizaciones de seguridad Las actualizaciones de seguridad se publican tan pronto como sea posible después de desarrollar y probar el parche. Notificamos a los usuarios a través del repositorio GitHub del proyecto y publicamos las notas de la versión y los avisos de seguridad en la página de versiones de GitHub. Además, marcamos como obsoletas todas las versiones que contengan la vulnerabilidad. ## Respuesta a incidentes del lado del mantenedor Para escenarios de compromiso que afecten cuentas de mantenedores, estaciones de trabajo o infraestructura de publicación (phishing, pérdida de llave hardware, tag o publicación inesperados), el proyecto mantiene un runbook interno de respuesta a incidentes en [THREATMODEL.md §3.7](https://github.com/axios/axios/blob/v1.x/THREATMODEL.md#37-incident-response-runbook). Cubre revocación de sesiones, rotación de llaves, notificación aguas abajo y procedimientos de unpublish/deprecate. ## Colaboradores y reconocimientos de seguridad Nos gustaría agradecer a los siguientes investigadores de seguridad por trabajar con nosotros para ayudar a que el proyecto sea seguro para todos: - [Socket Dev](https://socket.dev/) - [GitHub Security Lab](https://securitylab.github.com/) axios-axios-aa54a5e/docs/es/pages/misc/semver.md000066400000000000000000000051221526660636600217010ustar00rootroot00000000000000# Versionado semántico El versionado semántico es un esquema de versionado que se usa para comunicar la naturaleza de los cambios en un paquete de software. Es un conjunto simple de reglas y requisitos que dictan cómo se asignan e incrementan los números de versión. ## Versionado de axios axios sigue el esquema de versionado semántico. Esto significa que cada versión de axios se asigna con un número de versión compuesto por tres partes: mayor, menor y parche. El número de versión se incrementa según la naturaleza de los cambios en la versión. En el pasado, axios puede que no haya seguido estrictamente el versionado semántico en todo momento; sin embargo, de aquí en adelante se adoptará una adherencia mucho más estricta al esquema de versionado semántico para garantizar que los usuarios puedan confiar en los números de versión para comunicar la naturaleza de los cambios en la librería. A continuación se proporciona un breve resumen del esquema de versionado. ## Formato de versión Un número de versión semántico consta de tres partes: 1. Versión mayor 2. Versión menor 3. Versión de parche El número de versión se escribe como `MAYOR.MENOR.PARCHE`. Cada parte del número de versión tiene un significado específico: - **Versión mayor**: Se incrementa cuando se realizan cambios incompatibles en la API. - **Versión menor**: Se incrementa cuando se añade funcionalidad de manera retrocompatible. - **Versión de parche**: Se incrementa cuando se realizan correcciones de errores retrocompatibles. ## Versiones de prelanzamiento Además de las tres partes del número de versión, puedes añadir una versión de prelanzamiento. Esto se hace añadiendo un guion y una serie de identificadores separados por puntos inmediatamente después de la versión de parche. Por ejemplo, `1.0.0-alpha.1`. Las versiones de prelanzamiento se usan para indicar que una versión es inestable y puede no satisfacer los requisitos de compatibilidad indicados por el número de versión. Las versiones de prelanzamiento se ordenan según el orden de los identificadores. Por ejemplo, `1.0.0-alpha.1` viene antes de `1.0.0-alpha.2`. ## Rangos de versiones Cuando especificas un rango de versiones para un paquete, puedes usar una variedad de operadores para indicar el rango de versiones aceptables. Los siguientes operadores están disponibles: - `>`: Mayor que - `<`: Menor que - `>=`: Mayor o igual que - `<=`: Menor o igual que - `~`: Aproximadamente igual a - `^`: Compatible con Por ejemplo, `^1.0.0` significa que cualquier versión mayor o igual a `1.0.0` y menor que `2.0.0` es aceptable. axios-axios-aa54a5e/docs/es/pages/misc/sponsors.md000066400000000000000000000115761526660636600223000ustar00rootroot00000000000000--- layout: page search: false ---

Patrocinadores

Axios cuenta con el apoyo de las siguientes organizaciones. Si deseas patrocinar Axios, visita nuestra página de Open Collective para más información.

{{ capitalizeFirstLetter(sponsor.tier) }}
{{ sponsor.name }}
axios-axios-aa54a5e/docs/favicon.ico000066400000000000000000000360561526660636600175500ustar00rootroot00000000000000 h6  (ž00 h&Æ(  è.] ä*\Vä+Zqä*\Vä*[—ã+[eæ&Yä*\Vä*[—ä*[£ä*ZÝã([-ä*\Vä*[—ä*[£å)\ˆå+]Mÿä*\Vä*[—ä*[£ã*[Iä*\Vä*[—ä*[£ã*[Iÿã,Y.ã)[vä*[—ä*[£ã*[Iä)\Kã)Zðä*[—ä*[£ã*[Iæ-Y(ä)Zqä*[£ã*[Iå)Zˆã*[IÿÔ*U ( @ ç*[*ä*[«å*[‘Ì3fä*[«ä)Zÿä*Z0ä*[«ä)Zÿä*Z0ã,^.â)W,ä*[«ä)Zÿä*Z0ã*\Hä)[òå*\Nä*[«ä)Zÿä*Z0ã*\Hä)Zÿä)Zýä*Zzÿä*[«ä)Zÿä*Z0ã*\Hä)Zÿä)Zÿä)Zÿä)Z§æ3f ä*[«ä)Zÿä*Z0ã*\Hä)Zÿå)[Þä)[³ä)[³å*\€ªUUä*[«ä)Zÿä*Z0ã*\Hä)Zÿå*[‘ä*[«ä)Zÿä*Z0ã*\Hä)Zÿå*[‘ä*[«ä)Zÿä*Z0ã*\Hä)Zÿå*[‘ä*[«ä)Zÿä*Z0ã*\Hä)Zÿå*[‘ä*[«ä)Zÿä*Z0ã*\Hä)Zÿå*[‘ä*[«ä)Zÿä*Z0ã*\Hä)Zÿå*[‘ªUUä*\Vä*Z`ä*Z`ã)ZËä)Zÿä*Z0ã*\Hä)Zÿå*[‘å)\2ä)[áä)Zÿä)Zÿä)Zÿä*Z0ã*\Hä)Zÿå*[‘Þ,Yå*[Âä)Zÿä)Zÿä*Z0ã*\Hä)Zÿå*[‘Ô*Uä*Z™ä)Zÿä*Z0ã*\Hä)Zÿå*[‘å)[jç*Y+ã*\Hä)Zÿå*[‘ã*\Hä)Zÿå*[‘é,Yå)[Áå*[‘ÿ€€ã,^.(0` ç(` ã*[Iè.],ä*Zâå)[‰ñ*Uè.],ä*Zãä)Zùå*[Šæ3M è.],ä*Zãä)Zÿå*Z¯ß0`è.],ä*Zãä)Zÿå*Z¯ß0`æ3f â)W,Ô*Uè.],ä*Zãä)Zÿå*Z¯ß0`ê*`å*[°ä)[{è.] è.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çã)Zøä*Zªé,Yè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)Zþã)Z¹æ&Y(è.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)Zÿä)Zÿä*ZÏå*\Nÿ@€è.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)Zÿä)Zÿä)Zÿå)Zèâ)[kÿ3fè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿå)Zûå)[çã)Zæã)Zæä)Zßå,Zcÿÿè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)Zâä*]Bæ,]4æ,]4æ,]4å.\2ÿ@€è.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cÛ$mä*[„ä*[Ÿä*[Ÿä*[Ÿå*Z¯ä)Zôä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cã*\$å)ZÁå)Zûä)Zÿä)Zÿä)Zÿä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*câ,W#ã)Z›ä*Zûä)Zÿä)Zÿä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cØ'b å*Zlä)Zóä)Zÿä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cÿUUã*Y\ä)Zâä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cÿ€ä+ZAå)ZÁå)[®ß0`ê*`ä)[Çä)Zÿä)ZÚã*cæ)Zâ+ZXë'b ê*`ä)[Çä)Zÿä)ZÚã*cê*`ä)[Çä)Zÿä)ZÚã*cæ&Yã)[¹ä)Zÿä)ZÚã*cªUå)\2ä)ZÇä)ZÚã*cÿ*Uå)]kÝ3Uaxios-axios-aa54a5e/docs/fr/000077500000000000000000000000001526660636600160245ustar00rootroot00000000000000axios-axios-aa54a5e/docs/fr/index.md000066400000000000000000000236211526660636600174610ustar00rootroot00000000000000--- # https://vitepress.dev/reference/default-theme-home-page layout: home search: false hero: name: 'axios docs' text: 'axios est un client HTTP simple pour le navigateur et Node.js' image: dark: /logo.svg light: /logo-light.svg alt: axios actions: - theme: brand text: Démarrer link: /fr/pages/getting-started/first-steps - theme: alt text: Référence API link: /fr/pages/advanced/api-reference features: - title: Implémentation simple details: Démarrer avec axios est aussi simple qu'une seule ligne de code. Les requêtes API basiques peuvent être effectuées en 2 lignes de code. - title: Intercepteurs puissants details: Notre système d'intercepteurs innovant vous permet de contrôler le cycle de vie des requêtes et des réponses. Vous pouvez modifier les requêtes, les réponses et les erreurs. - title: Support TypeScript details: axios déclare des types et supporte pleinement TypeScript. Vous pouvez utiliser axios en toute confiance dans vos projets TypeScript. ---

Sponsors

{{ sponsor.name }}

{{ capitalizeFirstLetter(sponsor.tier) }}
axios-axios-aa54a5e/docs/fr/pages/000077500000000000000000000000001526660636600171235ustar00rootroot00000000000000axios-axios-aa54a5e/docs/fr/pages/advanced/000077500000000000000000000000001526660636600206705ustar00rootroot00000000000000axios-axios-aa54a5e/docs/fr/pages/advanced/adapters.md000066400000000000000000000102761526660636600230230ustar00rootroot00000000000000# Adaptateurs Les adaptateurs vous permettent de personnaliser la façon dont axios gère les données de la requête. Par défaut, axios utilise une liste de priorité ordonnée `['xhr', 'http', 'fetch']` et sélectionne le premier adaptateur pris en charge par l'environnement actuel. En pratique, cela signifie que `xhr` est utilisé dans les navigateurs, `http` dans Node.js, et `fetch` dans les environnements où ni l'un ni l'autre n'est disponible (comme Cloudflare Workers ou Deno). Écrire votre propre adaptateur vous donne un contrôle total sur la façon dont axios effectue une requête et traite la réponse — utile pour les tests, les transports personnalisés ou les environnements non standard. ## Adaptateurs intégrés Vous pouvez sélectionner un adaptateur intégré par nom en utilisant l'option de configuration `adapter` : ```js // Utiliser l'adaptateur fetch const instance = axios.create({ adapter: "fetch" }); // Utiliser l'adaptateur XHR (par défaut dans les navigateurs) const instance = axios.create({ adapter: "xhr" }); // Utiliser l'adaptateur HTTP (par défaut dans Node.js) const instance = axios.create({ adapter: "http" }); ``` Vous pouvez également passer un tableau de noms d'adaptateurs. axios utilisera le premier pris en charge par l'environnement actuel : ```js const instance = axios.create({ adapter: ["fetch", "xhr", "http"] }); ``` Pour plus de détails sur l'adaptateur `fetch`, consultez la page [Adaptateur Fetch](/pages/advanced/fetch-adapter). ## Créer un adaptateur personnalisé Pour créer un adaptateur personnalisé, écrivez une fonction qui accepte un objet `config` et retourne une Promise qui se résout vers un objet de réponse axios valide. ```js import axios from "axios"; import { settle } from "axios/unsafe/core/settle.js"; function myAdapter(config) { /** * À ce stade : * - la configuration a été fusionnée avec les valeurs par défaut * - les transformateurs de requête ont été exécutés * - les intercepteurs de requête ont été exécutés * * L'adaptateur est maintenant responsable de l'exécution de la requête * et du retour d'un objet de réponse valide. */ return new Promise((resolve, reject) => { // Effectuez votre logique de requête personnalisée ici. // Cet exemple utilise l'API native fetch comme point de départ. fetch(config.url, { method: config.method?.toUpperCase() ?? "GET", headers: config.headers?.toJSON() ?? {}, body: config.data, signal: config.signal, }) .then(async (fetchResponse) => { const responseData = await fetchResponse.text(); const response = { data: responseData, status: fetchResponse.status, statusText: fetchResponse.statusText, headers: Object.fromEntries(fetchResponse.headers.entries()), config, request: null, }; // settle résout ou rejette la promise selon le statut HTTP settle(resolve, reject, response); /** * Après ce point : * - les transformateurs de réponse seront exécutés * - les intercepteurs de réponse seront exécutés */ }) .catch(reject); }); } const instance = axios.create({ adapter: myAdapter }); ``` Les adaptateurs TypeScript peuvent conserver les données de requête et les paramètres de requête dans la configuration de la réponse grâce aux génériques correspondants : ```ts import type { AxiosPromise, InternalAxiosRequestConfig, } from "axios"; interface RequestBody { includeArchived: boolean; } interface SearchParams { query: string; } interface SearchResponse { results: string[]; } const searchAdapter = ( config: InternalAxiosRequestConfig ): AxiosPromise => Promise.resolve({ data: { results: [] }, status: 200, statusText: "OK", headers: {}, config, }); ``` ::: tip Le helper `settle` résout la promise pour les codes de statut 2xx et la rejette pour tout le reste, conformément au comportement par défaut d'axios. Si vous souhaitez une validation de statut personnalisée, utilisez plutôt l'option de configuration `validateStatus`. ::: axios-axios-aa54a5e/docs/fr/pages/advanced/api-reference.md000066400000000000000000000343301526660636600237220ustar00rootroot00000000000000# Référence API Vous trouverez ci-dessous la liste de toutes les fonctions et classes disponibles dans le package axios. Ces fonctions peuvent être utilisées et importées dans votre projet. Elles sont toutes protégées par notre engagement renouvelé à respecter le versionnage sémantique. Vous pouvez donc compter sur leur stabilité dans les versions futures, sauf en cas de changement de version majeure. ## Instance L'instance `axios` est l'objet principal que vous utiliserez pour effectuer des requêtes HTTP. C'est une fonction fabrique qui crée une nouvelle instance de la classe `Axios`. L'instance `axios` dispose d'un certain nombre de méthodes pour effectuer des requêtes HTTP. Ces méthodes sont documentées dans la [section Alias de requête](/pages/advanced/request-method-aliases) de la documentation. ## Types de requête TypeScript Les types de requête publics utilisent des génériques distincts pour les données de requête et les paramètres de requête : ```ts AxiosRequestConfig RawAxiosRequestConfig InternalAxiosRequestConfig AxiosDefaults CreateAxiosDefaults AxiosResponse AxiosPromise AxiosError CanceledError ``` `D` est le type du corps de la requête et `P` celui des paramètres de requête. `AxiosResponse`, `AxiosPromise`, les erreurs, les valeurs par défaut, les instances appelables, les alias de requête, les adaptateurs et `mergeConfig()` conservent les deux dans la configuration. Les sérialiseurs de paramètres personnalisés reçoivent le même `P`. Les méthodes de requête utilisent l'ordre générique ``, avec `P` ajouté en dernier afin que les arguments génériques explicites existants restent compatibles. Sans type de réponse personnalisé `R`, l'`AxiosResponse` par défaut conserve `D` et `P` dans `response.config` ; un `R` explicite continue de contrôler la valeur résolue. Les génériques des données et paramètres valent `any` par défaut pour préserver la compatibilité. ## Classes ### `Axios` La classe `Axios` est la classe principale que vous utiliserez pour effectuer des requêtes HTTP. C'est une fonction fabrique qui crée une nouvelle instance de la classe `Axios`. La classe `Axios` dispose d'un certain nombre de méthodes pour effectuer des requêtes HTTP. Ces méthodes sont documentées dans la [section Alias de requête](/pages/advanced/request-method-aliases) de la documentation. #### `constructor` Crée une nouvelle instance de la classe `Axios`. Le constructeur accepte un objet de configuration optionnel en argument. ```ts constructor(instanceConfig?: AxiosRequestConfig); ``` #### `request` Gère l'invocation de la requête et la résolution de la réponse. C'est la méthode principale pour effectuer des requêtes HTTP. Elle accepte un objet de configuration en argument et retourne une promise qui se résout vers l'objet de réponse. ```ts request(config: AxiosRequestConfig): Promise; ``` ### `CancelToken` La classe `CancelToken` était basée sur la proposition `tc39/proposal-cancelable-promises`. Elle était utilisée pour créer un token permettant d'annuler une requête HTTP. La classe `CancelToken` est désormais dépréciée en faveur de l'API `AbortController`. Depuis la version 0.22.0, la classe `CancelToken` est dépréciée et sera supprimée dans une prochaine version. Il est recommandé d'utiliser l'API `AbortController` à la place. La classe est exportée principalement pour des raisons de rétrocompatibilité et sera supprimée dans une prochaine version. Nous déconseillons fortement son utilisation dans de nouveaux projets ; les helpers d'interopérabilité hérités ci-dessous ne sont listés que pour le code existant. Les méthodes héritées restent typées pour les intégrations existantes : ```ts subscribe(listener: (cancel: Cancel | any) => void): void; unsubscribe(listener: (cancel: Cancel | any) => void): void; toAbortSignal(): AbortSignal; ``` ## Fonctions ### `AxiosError` La classe `AxiosError` est une classe d'erreur levée lorsqu'une requête HTTP échoue. Elle étend la classe `Error` et ajoute des propriétés supplémentaires à l'objet d'erreur. #### `constructor` Crée une nouvelle instance de la classe `AxiosError`. Le constructeur accepte en argument un message, un code, une configuration, une requête et une réponse optionnels. ```ts constructor(message?: string, code?: string, config?: InternalAxiosRequestConfig, request?: any, response?: AxiosResponse); ``` #### `properties` La classe `AxiosError` fournit les propriétés suivantes : ```ts // Instance de config. config?: InternalAxiosRequestConfig; // Code d'erreur. code?: string; // Instance de requête. request?: any; // Instance de réponse. response?: AxiosResponse; // Booléen indiquant si l'erreur est une `AxiosError`. isAxiosError: boolean; // Code de statut HTTP de l'erreur. status?: number; // Méthode utilitaire pour convertir l'erreur en objet JSON. toJSON: () => object; // Cause de l'erreur. cause?: Error; ``` ### `AxiosHeaders` La classe `AxiosHeaders` est une classe utilitaire permettant de gérer les en-têtes HTTP. Elle fournit des méthodes pour manipuler les en-têtes, comme l'ajout, la suppression et la récupération d'en-têtes. Seules les méthodes principales sont documentées ici. Pour la liste complète des méthodes, référez-vous au fichier de déclaration de types. #### `constructor` Crée une nouvelle instance de la classe `AxiosHeaders`. Le constructeur accepte un objet d'en-têtes optionnel en argument. ```ts constructor(headers?: RawAxiosHeaders | AxiosHeaders | string); ``` #### `set` Ajoute un en-tête à l'objet d'en-têtes. Les noms d'en-têtes vides ou composés uniquement d'espaces sont ignorés. ```ts set(headerName?: string, value?: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders; set(headers?: RawAxiosHeaders | AxiosHeaders | string, rewrite?: boolean): AxiosHeaders; set(headers?: Iterable<[string, AxiosHeaderValue]>, rewrite?: boolean): AxiosHeaders; ``` #### `get` Récupère un en-tête depuis l'objet d'en-têtes. ```ts get(headerName: string, parser: typeof AxiosHeaders.parseParameters): AxiosHeaderParameters; get(headerName: string, parser: RegExp): RegExpExecArray | null; get(headerName: string, matcher?: true | AxiosHeaderParser): AxiosHeaderValue; ``` Passez `AxiosHeaders.parseParameters` pour analyser des paramètres HTTP normalisés dans une map renforcée à prototype nul : ```js const headers = new AxiosHeaders({ "Content-Type": 'multipart/form-data; boundary="a,b"', }); console.log({ ...headers.get("Content-Type", AxiosHeaders.parseParameters), }); // { boundary: "a,b" } ``` Les noms de paramètres sont insensibles à la casse. L'analyseur retire les délimiteurs des chaînes entre guillemets, décode les guillemets et barres obliques inverses échappés, conserve les virgules et points-virgules dans les valeurs entre guillemets et ne retire que les espaces optionnels RFC autour des valeurs sans guillemets. Il omet `__proto__`, `constructor` et `prototype`. `get(name, true)` reste le tokenizer historique. #### `has` Vérifie si un en-tête existe dans l'objet d'en-têtes. ```ts has(header: string, matcher?: AxiosHeaderMatcher): boolean; ``` #### `delete` Supprime un en-tête de l'objet d'en-têtes. ```ts delete(header: string | string[], matcher?: AxiosHeaderMatcher): boolean; ``` #### `clear` Supprime tous les en-têtes de l'objet d'en-têtes. ```ts clear(matcher?: AxiosHeaderMatcher): boolean; ``` #### `normalize` Normalise l'objet d'en-têtes. ```ts normalize(format: boolean): AxiosHeaders; ``` #### `concat` Concatène des objets d'en-têtes. ```ts concat(...targets: Array): AxiosHeaders; ``` #### `toJSON` Convertit l'objet d'en-têtes en objet JSON. ```ts toJSON(asStrings: true): Record; toJSON(asStrings?: false): Record; ``` #### `toString` Retourne les en-têtes sous forme de bloc d'en-têtes HTTP sans CRLF, avec une paire `nom: valeur` par ligne. ```ts toString(): string; ``` ### `CanceledError` La classe `CanceledError` est une classe d'erreur levée lorsqu'une requête HTTP est annulée. Elle étend la classe `AxiosError`. ```ts constructor(message?: string, config?: InternalAxiosRequestConfig, request?: any); __CANCEL__?: boolean; ``` ### `Cancel` La classe `Cancel` est un alias de la classe `CanceledError`. Elle est exportée pour des raisons de rétrocompatibilité et sera supprimée dans une prochaine version. ```ts Cancel: typeof CanceledError; ``` ### `isCancel` Une fonction qui vérifie si une erreur est une `CanceledError`. Utile pour distinguer les annulations intentionnelles des erreurs inattendues. ```ts isCancel(value: any): value is CanceledError; ``` ```js import axios from 'axios'; const controller = new AbortController(); axios.get('/api/data', { signal: controller.signal }).catch((error) => { if (axios.isCancel(error)) { console.log('Request was cancelled:', error.message); } else { console.error('Unexpected error:', error); } }); controller.abort('User navigated away'); ``` ### `isAxiosError` Une fonction qui vérifie si une erreur est une `AxiosError`. Utilisez-la dans les blocs `catch` pour accéder en toute sécurité aux propriétés spécifiques d'axios comme `error.response` et `error.config`. ```ts isAxiosError(value: any): value is AxiosError; ``` ```js import axios from 'axios'; try { await axios.get('/api/resource'); } catch (error) { if (axios.isAxiosError(error)) { // error.response, error.config, error.code sont tous disponibles console.error('HTTP error', error.response?.status, error.message); } else { // Une erreur non-axios (ex. une erreur de programmation) throw error; } } ``` ### `all` La fonction `all` est une fonction utilitaire qui prend un tableau de promises et retourne une promise unique qui se résout lorsque toutes les promises du tableau sont résolues. La fonction `all` est désormais dépréciée en faveur de la méthode `Promise.all`. Il est recommandé d'utiliser `Promise.all` à la place. Depuis la version 0.22.0, la fonction `all` est dépréciée et sera supprimée dans une prochaine version. ### `spread` La fonction `spread` est une fonction utilitaire qui peut être utilisée pour décomposer un tableau d'arguments dans un appel de fonction. Utile lorsque vous avez un tableau d'arguments à passer à une fonction qui en accepte plusieurs. ```ts spread(callback: (...args: T[]) => R): (array: T[]) => R; ``` ### `toFormData` Convertit un objet JavaScript simple (ou imbriqué) en instance `FormData`. Utile pour construire programmatiquement des données de formulaire multipart à partir d'un objet. ```ts toFormData(sourceObj: object, formData?: FormData, options?: FormSerializerOptions): FormData; ``` ```js import { toFormData } from 'axios'; const data = { name: 'Jay', avatar: fileBlob }; const form = toFormData(data); // form est maintenant une instance FormData prête à être envoyée await axios.post('/api/users', form); ``` ### `formToJSON` Convertit une instance `FormData` en objet JavaScript simple. Utile pour lire les données d'un formulaire dans un format structuré. Seules les notations par points et crochets sont structurelles : `.`, `[` et `]` séparent les chemins, tandis que `-`, les espaces, `+`, `*` et `&` restent dans les clés littérales. `foo.bar` et `foo[bar]` créent des objets imbriqués, et `foo[]` crée un tableau. ```ts formToJSON(form: FormData): object; ``` ```js import { formToJSON } from 'axios'; const form = new FormData(); form.append('user-name', 'johndoe'); form.append('user.name', 'john'); const obj = formToJSON(form); console.log(obj); // { "user-name": "johndoe", user: { name: "john" } } ``` ### `getAdapter` Résout et retourne une fonction d'adaptateur par nom ou en passant un tableau de noms candidats. axios utilise ceci en interne pour sélectionner le meilleur adaptateur disponible pour l'environnement actuel. ```ts getAdapter(adapters: string | string[]): AxiosAdapter; ``` ```js import { getAdapter } from 'axios'; // Obtenir explicitement l'adaptateur fetch const fetchAdapter = getAdapter('fetch'); // Obtenir le meilleur adaptateur disponible depuis une liste de priorité const adapter = getAdapter(['fetch', 'xhr', 'http']); ``` ### `mergeConfig` Fusionne deux objets de configuration axios, en appliquant la même stratégie de fusion profonde qu'axios utilise en interne lors de la combinaison des valeurs par défaut avec les options par requête. Les valeurs ultérieures ont la priorité. ```ts mergeConfig( config1: AxiosRequestConfig, config2: AxiosRequestConfig ): AxiosRequestConfig; ``` ```js import { mergeConfig } from 'axios'; const base = { baseURL: 'https://api.example.com', timeout: 5000 }; const override = { timeout: 10000, headers: { 'X-Custom': 'value' } }; const merged = mergeConfig(base, override); // { baseURL: "https://api.example.com", timeout: 10000, headers: { "X-Custom": "value" } } ``` ## Constantes ### `HttpStatusCode` Un objet contenant une liste de codes de statut HTTP sous forme de constantes nommées. Utilisez-le pour écrire des conditions lisibles plutôt que des nombres bruts. ```js import axios, { HttpStatusCode } from 'axios'; try { const response = await axios.get('/api/resource'); } catch (error) { if (axios.isAxiosError(error)) { if (error.response?.status === HttpStatusCode.NotFound) { console.error('Resource not found'); } else if (error.response?.status === HttpStatusCode.Unauthorized) { console.error('Authentication required'); } } } ``` ## Divers ### `VERSION` La version actuelle du package `axios`. Il s'agit d'une chaîne représentant le numéro de version du package, mise à jour à chaque nouvelle version. axios-axios-aa54a5e/docs/fr/pages/advanced/authentication.md000066400000000000000000000113541526660636600242350ustar00rootroot00000000000000# Authentification La plupart des APIs requièrent une forme d'authentification. Cette page couvre les schémas les plus courants pour attacher des identifiants aux requêtes axios. ## Tokens Bearer (JWT) L'approche la plus courante consiste à attacher un JWT dans l'en-tête `Authorization`. La façon la plus propre de procéder est via un intercepteur de requête sur votre instance axios, afin que le token soit lu à jour à chaque requête : ```js import axios from "axios"; const api = axios.create({ baseURL: "https://api.example.com" }); api.interceptors.request.use((config) => { const token = localStorage.getItem("access_token"); if (token) { config.headers.set("Authorization", `Bearer ${token}`); } return config; }); ``` ## Authentification HTTP Basic Pour les APIs utilisant l'authentification HTTP Basic, passez l'option `auth`. axios encodera les identifiants et définira automatiquement l'en-tête `Authorization` : ```js const response = await axios.get("https://api.example.com/data", { auth: { username: "myUser", password: "myPassword", }, }); ``` Si `auth` n'est pas fourni, les adaptateurs HTTP Node.js et fetch peuvent aussi déduire les identifiants Basic depuis l'URL de requête, par exemple `https://myUser:myPassword@api.example.com/data`. Les identifiants encodés en pourcentage dans l'URL sont décodés avant la génération de l'en-tête `Authorization`. Pour le nouveau code, préférez l'option explicite `auth` ; elle prend le dessus sur les identifiants intégrés à l'URL. ::: tip Pour les tokens Bearer et les clés API, utilisez un en-tête `Authorization` personnalisé plutôt que l'option `auth` — `auth` est réservé à l'authentification HTTP Basic. ::: ## Clés API Les clés API sont généralement passées sous forme d'en-tête ou de paramètre de requête, selon ce qu'attend l'API : ```js // En tant qu'en-tête const api = axios.create({ baseURL: "https://api.example.com", headers: { "X-API-Key": "your-api-key-here" }, }); // En tant que paramètre de requête const response = await axios.get("https://api.example.com/data", { params: { apiKey: "your-api-key-here" }, }); ``` ## Renouvellement de token Lorsque les tokens d'accès expirent, vous devez les renouveler silencieusement et réessayer la requête échouée. Un intercepteur de réponse est l'endroit idéal pour implémenter cela : ```js import axios from "axios"; const api = axios.create({ baseURL: "https://api.example.com" }); // Suivre si un renouvellement est déjà en cours pour éviter des appels parallèles let isRefreshing = false; let failedQueue = []; const processQueue = (error, token = null) => { failedQueue.forEach((prom) => { if (error) { prom.reject(error); } else { prom.resolve(token); } }); failedQueue = []; }; api.interceptors.response.use( (response) => response, async (error) => { const originalRequest = error.config; if (error.response?.status === 401 && !originalRequest._retry) { if (isRefreshing) { // Mettre la requête en file d'attente jusqu'à la fin du renouvellement return new Promise((resolve, reject) => { failedQueue.push({ resolve, reject }); }) .then((token) => { originalRequest.headers["Authorization"] = `Bearer ${token}`; return api(originalRequest); }) .catch((err) => Promise.reject(err)); } originalRequest._retry = true; isRefreshing = true; try { const { data } = await axios.post("/auth/refresh", { refreshToken: localStorage.getItem("refresh_token"), }); const newToken = data.access_token; localStorage.setItem("access_token", newToken); api.defaults.headers.common["Authorization"] = `Bearer ${newToken}`; processQueue(null, newToken); return api(originalRequest); } catch (refreshError) { processQueue(refreshError, null); // Rediriger vers la connexion ou émettre un événement localStorage.removeItem("access_token"); window.location.href = "/login"; return Promise.reject(refreshError); } finally { isRefreshing = false; } } return Promise.reject(error); } ); ``` ## Authentification par cookie Pour les APIs basées sur les sessions qui s'appuient sur les cookies, définissez `withCredentials: true` pour inclure les cookies dans les requêtes cross-origin : ```js const api = axios.create({ baseURL: "https://api.example.com", withCredentials: true, // envoyer les cookies avec chaque requête }); ``` ::: warning `withCredentials: true` exige que le serveur réponde avec `Access-Control-Allow-Credentials: true` et un `Access-Control-Allow-Origin` spécifique (pas de joker). ::: axios-axios-aa54a5e/docs/fr/pages/advanced/cancellation.md000066400000000000000000000067641526660636600236630ustar00rootroot00000000000000# Annulation À partir de la version v0.22.0, Axios prend en charge AbortController pour annuler les requêtes de manière propre. Cette fonctionnalité est disponible dans le navigateur et dans Node.js lorsque vous utilisez une version d'Axios qui prend en charge AbortController. Pour annuler une requête, vous devez créer une instance d'`AbortController` et passer son `signal` à l'option `signal` de la requête. ```js const controller = new AbortController(); axios .get('/foo/bar', { signal: controller.signal, }) .then(function (response) { //... }); // annuler la requête controller.abort(); ``` ## CancelToken Vous pouvez également utiliser l'API `CancelToken` pour annuler les requêtes. Cette API est dépréciée et sera supprimée dans la prochaine version majeure. Il est recommandé d'utiliser `AbortController` à la place. Vous pouvez créer un token d'annulation en utilisant la factory `CancelToken.source` comme indiqué ci-dessous : ```js const CancelToken = axios.CancelToken; const source = CancelToken.source(); axios .get('/user/12345', { cancelToken: source.token, }) .catch(function (thrown) { if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // gérer l'erreur } }); axios.post( '/user/12345', { name: 'new name', }, { cancelToken: source.token, } ); // annuler la requête (le paramètre message est optionnel) source.cancel('Operation canceled by the user.'); ``` Vous pouvez également créer un token d'annulation en passant une fonction d'exécution au constructeur `CancelToken` : ```js const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { // Une fonction d'exécution reçoit une fonction d'annulation comme paramètre cancel = c; }), }); // annuler la requête cancel(); ``` `CancelToken` expose aussi des helpers de bas niveau pour les intégrations héritées : ```js const source = axios.CancelToken.source(); const listener = (cancel) => { console.log(cancel.message); }; source.token.subscribe(listener); const signal = source.token.toAbortSignal(); // Passez `signal` aux APIs qui acceptent AbortSignal. source.cancel('Operation canceled by the user.'); source.token.unsubscribe(listener); ``` Les requêtes annulées sont rejetées avec `axios.CanceledError`. L'export hérité `axios.Cancel` est un alias de `axios.CanceledError`, et les erreurs d'annulation incluent `__CANCEL__` pour la compatibilité avec `axios.isCancel`. En TypeScript, `isCancel()` conserve les types des données de réponse, des données de requête et des paramètres de requête lors du narrowing d'une erreur `unknown` : ```ts interface SearchResponse { results: string[]; } interface RequestBody { includeArchived: boolean; } interface SearchParams { query: string; } try { await axios.get("/search"); } catch (error) { if (axios.isCancel(error)) { error.response?.data; // SearchResponse | undefined error.config?.data; // RequestBody | undefined error.config?.params; // SearchParams | undefined } } ``` Vous pouvez annuler plusieurs requêtes avec le même token d'annulation ou le même abort controller. Si un token d'annulation est déjà annulé au moment où une requête Axios démarre, alors la requête est annulée immédiatement, sans aucune tentative d'effectuer une vraie requête. axios-axios-aa54a5e/docs/fr/pages/advanced/config-defaults.md000066400000000000000000000061041526660636600242650ustar00rootroot00000000000000# Valeurs par défaut de configuration axios vous permet de spécifier des valeurs par défaut de configuration qui seront appliquées à toutes les requêtes. Vous pouvez définir des valeurs par défaut pour `baseURL`, `headers`, `timeout` et d'autres propriétés. Voici un exemple d'utilisation des valeurs par défaut de configuration : ```js axios.defaults.baseURL = "https://jsonplaceholder.typicode.com/posts"; axios.defaults.headers.common["Authorization"] = AUTH_TOKEN; axios.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded"; ``` ::: warning Les en-têtes globaux sont envoyés à chaque hôte Si votre application communique avec plus d'un domaine, définir `axios.defaults.headers.common["Authorization"]` enverra le token à **tous** ces domaines, y compris à des APIs tierces que vous ne contrôlez peut-être pas. Utilisez une [instance personnalisée](#valeurs-par-défaut-d-une-instance-personnalisée) avec une `baseURL` limitée pour tout client qui transporte des identifiants. ::: ## Valeurs par défaut d'une instance personnalisée Les instances axios sont déclarées avec leurs propres valeurs par défaut lors de leur création. Ces valeurs par défaut peuvent être remplacées en définissant la propriété `defaults` de l'instance. Voici un exemple d'utilisation des valeurs par défaut d'une instance personnalisée : ```js var instance = axios.create({ baseURL: "https://jsonplaceholder.typicode.com/posts", timeout: 1000, headers: { Authorization: "foobar" }, }); instance.defaults.headers.common["Authorization"] = AUTH_TOKEN; ``` ## Ordre de priorité de la configuration La configuration est fusionnée selon un ordre de priorité. L'ordre est le suivant : d'abord les valeurs par défaut de la bibliothèque, puis les propriétés par défaut de l'instance, et enfin l'argument de configuration de la requête. Voici un exemple de cet ordre de priorité : Certaines options sont propres à chaque requête et ne sont lues que depuis la configuration de la requête. `data` en fait partie : axios n'hérite pas des corps de requête depuis les valeurs par défaut globales ou d'instance et ne les fusionne pas en profondeur. Si chaque requête doit inclure des champs de corps communs, ajoutez-les avec un intercepteur de requête ou `transformRequest`, en limitant soigneusement cette logique pour éviter d'envoyer des valeurs sensibles au mauvais point de terminaison. Créons d'abord une instance avec les valeurs par défaut fournies par la bibliothèque. À ce stade, la valeur de configuration du timeout est `0`, valeur par défaut de la bibliothèque. ```js const instance = axios.create(); ``` Nous allons maintenant remplacer la valeur par défaut du timeout pour l'instance par `2500` millisecondes. Désormais, toutes les requêtes utilisant cette instance attendront 2,5 secondes avant d'expirer. ```js instance.defaults.timeout = 2500; ``` Enfin, nous allons effectuer une requête avec un timeout de `5000` millisecondes. Cette requête attendra 5 secondes avant d'expirer. ```js instance.get("/longRequest", { timeout: 5000, }); ``` axios-axios-aa54a5e/docs/fr/pages/advanced/create-an-instance.md000066400000000000000000000057101526660636600246560ustar00rootroot00000000000000# Créer une instance `axios.create()` vous permet de créer une instance axios préconfigurée. L'instance partage la même API de requête et de réponse que l'objet `axios` par défaut, mais utilise la configuration que vous fournissez comme base pour chaque requête. C'est la façon recommandée d'utiliser axios dans toute application dépassant un seul fichier. ```ts import axios from "axios"; const instance = axios.create({ baseURL: "https://api.example.com", timeout: 5000, headers: { "X-Custom-Header": "foobar" }, }); ``` La méthode `create` accepte l'objet complet de [Configuration de requête](/pages/advanced/request-config). Vous pouvez ensuite utiliser l'instance exactement comme l'objet axios par défaut : ```js const response = await instance.get("/users/1"); ``` ## Pourquoi utiliser une instance ? ### URL de base par service Dans la plupart des applications, vous communiquez avec plusieurs API. Créer une instance distincte par service évite de répéter l'URL de base à chaque appel : ```js const githubApi = axios.create({ baseURL: "https://api.github.com" }); const internalApi = axios.create({ baseURL: "https://api.internal.example.com" }); const { data: repos } = await githubApi.get("/users/axios/repos"); const { data: users } = await internalApi.get("/users"); ``` ### En-têtes d'authentification partagés Attachez un token d'authentification à chaque requête d'une instance sans toucher aux autres : ```js const authApi = axios.create({ baseURL: "https://api.example.com", headers: { Authorization: `Bearer ${getToken()}`, }, }); ``` ### Délais d'attente et nouvelles tentatives par service Différents services ont des caractéristiques de fiabilité différentes. Définissez un délai court pour les services temps réel et un délai long pour les traitements par lots : ```js const realtimeApi = axios.create({ baseURL: "https://realtime.example.com", timeout: 2000 }); const batchApi = axios.create({ baseURL: "https://batch.example.com", timeout: 60000 }); ``` ### Intercepteurs isolés Les intercepteurs ajoutés à une instance ne s'appliquent qu'à cette instance, ce qui permet de bien séparer les responsabilités : ```js const loggingApi = axios.create({ baseURL: "https://api.example.com" }); loggingApi.interceptors.request.use((config) => { console.log(`→ ${config.method?.toUpperCase()} ${config.url}`); return config; }); ``` ## Surcharger les valeurs par défaut par requête La configuration passée au moment de la requête remplace toujours les valeurs par défaut de l'instance : ```js const api = axios.create({ timeout: 5000 }); // Cette requête spécifique utilise un délai de 30 secondes à la place await api.get("/slow-endpoint", { timeout: 30000 }); ``` ::: tip Les valeurs par défaut de l'instance peuvent également être modifiées après sa création en écrivant dans `instance.defaults` : ```js instance.defaults.headers.common["Authorization"] = `Bearer ${newToken}`; ``` ::: axios-axios-aa54a5e/docs/fr/pages/advanced/error-handling.md000066400000000000000000000214671526660636600241370ustar00rootroot00000000000000# Gestion des erreurs axios peut lever de nombreux types d'erreurs différents. Certaines de ces erreurs sont causées par axios lui-même, tandis que d'autres sont causées par le serveur ou le client. Le tableau suivant liste la structure générale de l'erreur levée : | Propriété | Définition | | --------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | message | Un résumé rapide du message d'erreur et du statut avec lequel elle a échoué. | | name | Définit l'origine de l'erreur. Pour axios, ce sera toujours une `AxiosError`. | | stack | Fournit la trace de pile de l'erreur. | | config | Un objet de configuration axios avec les configurations d'instance spécifiques définies par l'utilisateur au moment de la requête. | | code | Représente une erreur identifiée par axios. Le tableau ci-dessous liste les définitions spécifiques des erreurs internes d'axios. | | status | Code de statut de la réponse HTTP. Consultez [ici](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) pour la signification des codes de statut HTTP courants. | Voici une liste des erreurs potentielles identifiées par axios : | Code | Définition | | ------------------------- | --------------------------------------------------------------------------------------------- | | ERR_BAD_OPTION_VALUE | Valeur invalide ou non supportée fournie dans la configuration axios. | | ERR_BAD_OPTION | Option invalide fournie dans la configuration axios. | | ECONNABORTED | Indique généralement que la requête a expiré (sauf si `transitional.clarifyTimeoutError` est défini) ou a été abandonnée par le navigateur ou son plugin. | | ETIMEDOUT | La requête a expiré en dépassant la limite de temps par défaut d'axios. `transitional.clarifyTimeoutError` doit être défini à `true`, sinon une erreur générique `ECONNABORTED` sera levée à la place. | | ERR_NETWORK | Problème lié au réseau. Dans le navigateur, cette erreur peut également être causée par une violation de politique [CORS](https://developer.mozilla.org/ru/docs/Web/HTTP/Guides/CORS) ou de [contenu mixte](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content). Le navigateur ne permet pas au code JS de clarifier la raison réelle de l'erreur pour des raisons de sécurité ; veuillez vérifier la console. | | ERR_FR_TOO_MANY_REDIRECTS | La requête est redirigée trop de fois ; dépasse le nombre maximum de redirections spécifié dans la configuration axios. | | ERR_DEPRECATED | Fonctionnalité ou méthode dépréciée utilisée dans axios. | | ERR_BAD_RESPONSE | La réponse ne peut pas être analysée correctement ou est dans un format inattendu. Généralement lié à une réponse avec un code de statut `5xx`. | | ERR_BAD_REQUEST | La requête a un format inattendu ou des paramètres requis manquants. Généralement lié à une réponse avec un code de statut `4xx`. | | ERR_CANCELED | Fonctionnalité ou méthode annulée explicitement par l'utilisateur via un AbortSignal (ou un CancelToken). | | ERR_NOT_SUPPORT | Fonctionnalité ou méthode non supportée dans l'environnement axios actuel. | | ERR_INVALID_URL | URL invalide fournie pour la requête axios. | | ERR_FORM_DATA_DEPTH_EXCEEDED | Un objet dépasse la `maxDepth` configurée lors de la sérialisation de `params` ou des données de formulaire. La limite par défaut est de 100 niveaux. Voir [`paramsSerializer`](/pages/advanced/request-config#paramsserializer) et [`formSerializer`](/pages/advanced/request-config#formserializer). | ## Gérer les erreurs Le comportement par défaut d'axios est de rejeter la promise si la requête échoue. Cependant, vous pouvez également capturer l'erreur et la gérer comme bon vous semble. Voici un exemple de capture d'une erreur : ```js axios.get("/user/12345").catch(function (error) { if (error.response) { // La requête a été effectuée et le serveur a répondu avec un code de statut // qui n'est pas dans la plage 2xx console.log(error.response.data); console.log(error.response.status); console.log(error.response.headers); } else if (error.request) { // La requête a été effectuée mais aucune réponse n'a été reçue // `error.request` est une instance de XMLHttpRequest dans le navigateur et une instance de // http.ClientRequest dans Node.js console.log(error.request); } else { // Quelque chose s'est produit lors de la configuration de la requête qui a déclenché une erreur console.log("Error", error.message); } console.log(error.config); }); ``` ## URL HTTP(S) mal formées axios rejette une `url` ou une `baseURL` de requête `http:` ou `https:` qui omet `//` après le protocole. Par exemple, `https:example.com` et `https:/example.com` sont rejetées avec une `AxiosError` dont le code est `ERR_INVALID_URL`, au lieu d'être normalisées silencieusement par le parseur d'URL du navigateur ou de Node.js. Utilisez une URL bien formée comme `https://example.com`. Le message identifie l'URL concernée, par exemple : ```text Invalid URL "https:example.com": missing "//" after protocol ``` Ce comportement empêche les URL mal formées de contourner `baseURL` ou les listes d'URL autorisées. L'URL affichée est normalisée pour les caractères de contrôle et masque les identifiants, les valeurs des paramètres de requête et le contenu du fragment. Elle conserve le schéma, l'hôte, le chemin et les noms des paramètres afin que la requête reste identifiable. Ce masquage du message est systématique car `AxiosError.message` est toujours inclus par `toJSON()` ; l'option `redact` de la configuration ne peut pas nettoyer un message déjà créé. En utilisant l'option de configuration `validateStatus`, vous pouvez remplacer la condition par défaut (status >= 200 && status < 300) et définir le ou les codes HTTP qui doivent lever une erreur. ```js axios.get("/user/12345", { validateStatus: function (status) { return status < 500; // Résoudre uniquement si le code de statut est inférieur à 500 }, }); ``` En utilisant la méthode `toJSON`, vous pouvez obtenir un objet avec plus d'informations sur l'erreur. ```js axios.get("/user/12345").catch(function (error) { console.log(error.toJSON()); }); ``` Pour éviter de journaliser des secrets depuis `error.config`, passez un tableau `redact` dans la configuration de la requête. Les clés de configuration correspondantes sont masquées de manière insensible à la casse à n'importe quelle profondeur lorsque `AxiosError#toJSON()` est appelée. ```js axios.get("/user/12345", { headers: { Authorization: "Bearer token" }, redact: ["authorization"] }).catch(function (error) { console.log(error.toJSON().config.headers.Authorization); // [REDACTED ****] }); ``` ## Gestion des délais d'attente Lorsqu'une requête dépasse son `timeout` configuré, axios rejette avec `ECONNABORTED` par défaut. Définissez `transitional.clarifyTimeoutError: true` pour recevoir `ETIMEDOUT` à la place, ce qui rend les erreurs de délai d'attente plus faciles à distinguer des autres abandons. ```js async function fetchWithTimeout() { try { const response = await axios.get("https://example.com/data", { timeout: 5000, // 5 secondes transitional: { // définir à true si vous préférez ETIMEDOUT à ECONNABORTED clarifyTimeoutError: true, }, }); console.log("Response:", response.data); } catch (error) { if (axios.isAxiosError(error)) { if (error.code === "ECONNABORTED" || error.code === "ETIMEDOUT") { console.error("Request timed out. Please try again."); return; } console.error("Axios error:", error.message); return; } console.error("Unexpected error:", error); } } ``` ::: tip Définissez toujours un `timeout` en production Sans cela, une requête bloquée peut rester en attente indéfiniment. Consultez [`timeout`](/pages/advanced/request-config#timeout) et [`transitional.clarifyTimeoutError`](/pages/advanced/request-config#transitional) pour les options de configuration correspondantes. ::: axios-axios-aa54a5e/docs/fr/pages/advanced/fetch-adapter.md000066400000000000000000000076711526660636600237340ustar00rootroot00000000000000# Adaptateur Fetch L'adaptateur `fetch` est un nouvel adaptateur introduit à partir de la version 1.7.0. Il permet d'utiliser axios avec l'API `fetch`, vous offrant ainsi le meilleur des deux mondes. Par défaut, `fetch` sera utilisé si les adaptateurs `xhr` et `http` ne sont pas disponibles dans le build, ou non supportés par l'environnement. Pour l'utiliser par défaut, il doit être sélectionné explicitement en définissant l'option `adapter` à `fetch` lors de la création d'une instance axios. ```js import axios from 'axios'; const instance = axios.create({ adapter: 'fetch', }); ``` L'adaptateur supporte les mêmes fonctionnalités que l'adaptateur `xhr`, notamment la capture de la progression des envois et téléchargements. Il supporte également des types de réponse supplémentaires tels que `stream` et `formdata` (si l'environnement les prend en charge). Lorsque `auth` est omis, l'adaptateur fetch peut lire les identifiants HTTP Basic depuis l'URL de requête, par exemple `https://user:pass@example.com`. Les identifiants d'URL encodés en pourcentage sont décodés avant la génération de l'en-tête `Authorization`, et `auth` prend le dessus sur les identifiants intégrés à l'URL. ## Fetch personnalisé À partir de `v1.12.0`, vous pouvez personnaliser l'adaptateur fetch pour utiliser une fonction `fetch` personnalisée au lieu de celle de l'environnement global. Vous pouvez passer une fonction `fetch`, ainsi que des constructeurs `Request` et `Response` personnalisés via l'option de configuration `env`. Cela est utile lorsque vous travaillez avec des environnements personnalisés ou des frameworks d'application qui fournissent leur propre implémentation de `fetch`. ::: info Lorsque vous utilisez une fonction `fetch` personnalisée, vous devrez peut-être également fournir des constructeurs `Request` et `Response` correspondants. Si vous les omettez, les constructeurs globaux seront utilisés. Si votre `fetch` personnalisé est incompatible avec les constructeurs globaux, passez `null` pour les désactiver. **Remarque :** Définir `Request` et `Response` à `null` rendra impossible pour l'adaptateur fetch de capturer la progression des envois et téléchargements. ::: ### Exemple de base ```js import customFetchFunction from 'customFetchModule'; const instance = axios.create({ adapter: 'fetch', onDownloadProgress(e) { console.log('downloadProgress', e); }, env: { fetch: customFetchFunction, Request: null, // null -> désactiver le constructeur Response: null, }, }); ``` ### Utilisation avec Tauri [Tauri](https://tauri.app/plugin/http-client/) fournit une fonction `fetch` de plateforme qui contourne les restrictions CORS du navigateur pour les requêtes effectuées depuis la couche native. L'exemple ci-dessous montre une configuration minimale pour utiliser axios dans une application Tauri avec ce fetch personnalisé. ```js import { fetch } from '@tauri-apps/plugin-http'; import axios from 'axios'; const instance = axios.create({ adapter: 'fetch', onDownloadProgress(e) { console.log('downloadProgress', e); }, env: { fetch, }, }); const { data } = await instance.get('https://google.com'); ``` ### Utilisation avec SvelteKit [SvelteKit](https://svelte.dev/docs/kit/web-standards#Fetch-APIs) fournit une implémentation `fetch` personnalisée pour les fonctions `load` côté serveur qui gère la transmission des cookies et les URLs relatives. Comme son `fetch` est incompatible avec l'API `URL` standard, axios doit être configuré pour l'utiliser explicitement, et les constructeurs `Request` et `Response` globaux doivent être désactivés. ```js export async function load({ fetch }) { const { data: post } = await axios.get('https://jsonplaceholder.typicode.com/posts/1', { adapter: 'fetch', env: { fetch, Request: null, Response: null, }, }); return { post }; } ``` axios-axios-aa54a5e/docs/fr/pages/advanced/file-posting.md000066400000000000000000000064011526660636600236130ustar00rootroot00000000000000# Envoi de fichiers axios simplifie l'envoi de fichiers. Utilisez `postForm` ou `FormData` lorsque vous avez besoin d'envois `multipart/form-data`. ## Fichier unique (navigateur) Passez un objet `File` directement comme valeur de champ — axios le détectera et utilisera automatiquement le type de contenu correct : ```js await axios.postForm("https://httpbin.org/post", { description: "My profile photo", file: document.querySelector("#fileInput").files[0], }); ``` ## Plusieurs fichiers (navigateur) Passez une `FileList` pour envoyer tous les fichiers sélectionnés en une seule fois. Ils seront tous envoyés sous le même nom de champ (`files[]`) : ```js await axios.postForm( "https://httpbin.org/post", document.querySelector("#fileInput").files ); ``` Vous pouvez également passer la `FileList` (ou un tableau d'objets `File`) explicitement sous un nom de champ personnalisé en ajoutant `[]` à la clé : ```js await axios.postForm("https://httpbin.org/post", { "files[]": document.querySelector("#fileInput").files, }); ``` Pour utiliser des noms de champs distincts pour chaque fichier, construisez un objet `FormData` manuellement : ```js const formData = new FormData(); formData.append("avatar", avatarFile); formData.append("cover", coverFile); await axios.post("https://httpbin.org/post", formData); ``` ## Suivi de la progression de l'envoi (navigateur) Utilisez le callback `onUploadProgress` pour afficher une barre de progression ou un pourcentage à vos utilisateurs : ```js await axios.postForm("https://httpbin.org/post", { file: document.querySelector("#fileInput").files[0], }, { onUploadProgress: (progressEvent) => { const percent = Math.round( (progressEvent.loaded * 100) / progressEvent.total ); console.log(`Upload progress: ${percent}%`); }, }); ``` Consultez [Capture de progression](/pages/advanced/progress-capturing) pour la liste complète des champs disponibles sur l'événement de progression. ## Fichiers dans Node.js Dans Node.js, utilisez `fs.createReadStream` pour envoyer un fichier depuis le système de fichiers sans le charger entièrement en mémoire : ```js import fs from "fs"; import FormData from "form-data"; import axios from "axios"; const form = new FormData(); form.append("file", fs.createReadStream("/path/to/file.jpg")); form.append("description", "My uploaded file"); await axios.post("https://httpbin.org/post", form); ``` ::: tip Le package npm `form-data` est nécessaire dans les environnements Node.js pour créer des objets `FormData`. Dans Node.js moderne (v18+), le `FormData` global est disponible nativement. ::: ## Envoi d'un Buffer (Node.js) Vous pouvez également envoyer directement un `Buffer` en mémoire : ```js const buffer = Buffer.from("Hello, world!"); const form = new FormData(); form.append("file", buffer, { filename: "hello.txt", contentType: "text/plain", knownLength: buffer.length, }); await axios.post("https://httpbin.org/post", form); ``` ::: warning La capture de la progression d'envoi de `FormData` n'est actuellement pas supportée dans les environnements Node.js. ::: ::: danger Lors de l'envoi d'un stream lisible dans Node.js, définissez `maxRedirects: 0` pour empêcher le package `follow-redirects` de buffériser l'intégralité du stream en RAM. ::: axios-axios-aa54a5e/docs/fr/pages/advanced/header-methods.md000066400000000000000000000221001526660636600240760ustar00rootroot00000000000000# Méthodes d'en-têtes Avec l'introduction de la nouvelle classe `AxiosHeaders`, Axios fournit un ensemble de méthodes pour manipuler les en-têtes. Ces méthodes sont utilisées pour définir, récupérer et supprimer des en-têtes de manière plus pratique que la manipulation directe de l'objet d'en-têtes. ## Constructeur `new AxiosHeaders(headers?)` Le constructeur de la classe `AxiosHeaders` accepte un objet optionnel avec des en-têtes pour initialiser l'instance. L'objet d'en-têtes peut contenir un nombre quelconque d'en-têtes, et les clés sont insensibles à la casse. ```js constructor(headers?: RawAxiosHeaders | AxiosHeaders | string); ``` Pour plus de commodité, vous pouvez passer une chaîne avec des en-têtes séparés par un caractère de nouvelle ligne. Les en-têtes sont alors analysés et ajoutés à l'instance. ```js const headers = new AxiosHeaders(` Host: www.bing.com User-Agent: curl/7.54.0 Accept: */*`); console.log(headers); // Object [AxiosHeaders] { // host: 'www.bing.com', // 'user-agent': 'curl/7.54.0', // accept: '*/*' // } ``` ## Set La méthode `set` est utilisée pour définir des en-têtes sur l'instance d'`AxiosHeaders`. La méthode peut être appelée avec un seul nom d'en-tête et une valeur, un objet avec plusieurs en-têtes, ou une chaîne avec des en-têtes séparés par un caractère de nouvelle ligne. La méthode accepte également un paramètre optionnel `rewrite` qui contrôle le comportement de définition de l'en-tête. ```js set(headerName, value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher); set(headerName, value, rewrite?: (this: AxiosHeaders, value: string, name: string) => boolean); set(headers?: RawAxiosHeaders | AxiosHeaders | string, rewrite?: boolean); set(headers?: Iterable<[string, AxiosHeaderValue]>, rewrite?: boolean); ``` L'argument `rewrite` contrôle le comportement d'écrasement : - `false` - ne pas écraser si la valeur de l'en-tête est définie (n'est pas undefined) - `undefined` (défaut) - écraser l'en-tête sauf si sa valeur est définie à false - `true` - écraser dans tous les cas L'option peut également accepter une fonction définie par l'utilisateur qui détermine si la valeur doit être écrasée ou non. La fonction reçoit la valeur actuelle, le nom de l'en-tête et l'objet d'en-têtes comme arguments. Les noms d'en-têtes vides ou composés uniquement d'espaces sont ignorés. Les paires clé/valeur itérables, comme un `Map`, sont acceptées : ```js const headers = new AxiosHeaders(); headers.set( new Map([ ['X-Trace-Id', 'abc123'], ['Accept', 'application/json'], ]) ); ``` `AxiosHeaders` conserve la casse de la première clé correspondante qu'il voit. Vous pouvez utiliser cela pour préserver la casse spécifique d'un en-tête en initialisant une clé avec `undefined` puis en définissant les valeurs ultérieurement. Voir [Préserver la casse d'un en-tête spécifique](/pages/advanced/headers#preserving-a-specific-header-case). ## Get La méthode `get` est utilisée pour récupérer la valeur d'un en-tête. La méthode peut être appelée avec un seul nom d'en-tête, un matcher optionnel ou un analyseur. Le matcher est par défaut `true`. L'analyseur peut être une expression régulière utilisée pour extraire la valeur de l'en-tête. ```js get(headerName: string, parser: typeof AxiosHeaders.parseParameters): AxiosHeaderParameters; get(headerName: string, parser: RegExp): RegExpExecArray | null; get(headerName: string, matcher?: true | AxiosHeaderParser): AxiosHeaderValue; ``` Voici un exemple de quelques-unes des utilisations possibles de la méthode `get` : ```js const headers = new AxiosHeaders({ 'Content-Type': 'multipart/form-data; boundary=Asrf456BGe4h', }); console.log(headers.get('Content-Type')); // multipart/form-data; boundary=Asrf456BGe4h console.log(headers.get('Content-Type', true)); // analyser les paires clé-valeur depuis une chaîne séparée par des délimiteurs \s,;= : // [Object: null prototype] { // 'multipart/form-data': undefined, // boundary: 'Asrf456BGe4h' // } const quotedHeaders = new AxiosHeaders({ 'Content-Type': 'multipart/form-data; boundary="a,b"', }); console.log({ ...quotedHeaders.get('Content-Type', AxiosHeaders.parseParameters), }); // { boundary: 'a,b' } console.log( headers.get('Content-Type', (value, name, headers) => { return String(value).replace(/a/g, 'ZZZ'); }) ); // multipZZZrt/form-dZZZtZZZ; boundZZZry=Asrf456BGe4h console.log(headers.get('Content-Type', /boundary=(\w+)/)?.[0]); // boundary=Asrf456BGe4h ``` `AxiosHeaders.parseParameters` est un analyseur opt-in pour les valeurs normalisées des paramètres HTTP. Il retourne une map à prototype nul avec des noms de paramètres insensibles à la casse. Il retire les délimiteurs des chaînes entre guillemets, décode les guillemets doubles et barres obliques inverses échappés, et préserve les virgules ou points-virgules dans les valeurs entre guillemets. Pour les valeurs sans guillemets, seuls les espaces optionnels RFC (espace et tabulation horizontale) autour de la valeur sont retirés. L'analyseur omet les clés dangereuses lors de la matérialisation d'objets (`__proto__`, `constructor` et `prototype`). Passer `true` continue d'utiliser le tokenizer historique et conserve sa sortie pour la rétrocompatibilité. ## Has La méthode `has` est utilisée pour vérifier si un en-tête existe dans l'instance d'`AxiosHeaders`. La méthode peut être appelée avec un seul nom d'en-tête et un matcher optionnel. ```js has(header: string, matcher?: AxiosHeaderMatcher): boolean; ``` ::: info Retourne true si l'en-tête est défini (n'a pas de valeur undefined). ::: ## Delete La méthode `delete` est utilisée pour supprimer un en-tête de l'instance d'`AxiosHeaders`. La méthode peut être appelée avec un seul nom d'en-tête et un matcher optionnel. ```js delete(header: string | string[], matcher?: AxiosHeaderMatcher): boolean; ``` ::: info Retourne true si au moins un en-tête a été supprimé. ::: ## Clear La méthode `clear` est utilisée pour supprimer tous les en-têtes de l'instance d'`AxiosHeaders` si rien n'est passé. Si un matcher est passé, seuls les en-têtes correspondant au matcher sont supprimés ; dans ce cas, le matcher est utilisé pour correspondre au nom de l'en-tête plutôt qu'à sa valeur. ```js clear(matcher?: AxiosHeaderMatcher): boolean; ``` ::: info Retourne true si au moins un en-tête a été effacé. ::: ## Normalize Si l'objet d'en-têtes a été modifié directement, cela peut créer des doublons avec le même nom mais dans des casses différentes. Cette méthode normalise l'objet d'en-têtes en combinant les clés dupliquées en une seule. Axios utilise cette méthode en interne après l'appel de chaque intercepteur. Définissez `format` à `true` pour convertir les noms d'en-têtes en minuscules et capitaliser les premières lettres (cOntEnt-type => Content-Type) ou `false` pour conserver le format original. ```js const headers = new AxiosHeaders({ foo: '1', }); headers.Foo = '2'; headers.FOO = '3'; console.log(headers.toJSON()); // [Object: null prototype] { foo: '1', Foo: '2', FOO: '3' } console.log(headers.normalize().toJSON()); // [Object: null prototype] { foo: '3' } console.log(headers.normalize(true).toJSON()); // [Object: null prototype] { Foo: '3' } ``` ::: info Retourne `this` pour le chaînage. ::: ## Concat Fusionne l'instance avec des cibles dans une nouvelle instance AxiosHeaders. Si la cible est une chaîne, elle sera analysée comme des en-têtes HTTP bruts. Si la cible est une instance AxiosHeaders, elle sera fusionnée avec l'instance actuelle. Utile pour les préréglages de casse lors de la composition d'en-têtes. Par exemple : ```js const headers = AxiosHeaders.concat( { 'content-type': undefined }, { 'Content-Type': 'application/octet-stream' } ); ``` ```js concat(...targets: Array): AxiosHeaders; ``` ::: info Retourne une nouvelle instance AxiosHeaders. ::: ## toJSON Résout toutes les valeurs d'en-têtes internes dans un nouvel objet à prototype null. Définissez `asStrings` à true pour résoudre les tableaux en une chaîne contenant tous les éléments, séparés par des virgules. ```js toJSON(asStrings: true): Record; toJSON(asStrings?: false): Record; ``` ## toString Retourne les en-têtes sous forme de bloc d'en-têtes HTTP sans CRLF, avec une paire `nom: valeur` par ligne. ```js toString(): string; ``` ## From Retourne une nouvelle instance d'`AxiosHeaders` créée à partir des en-têtes bruts passés, ou retourne simplement l'objet d'en-têtes donné s'il s'agit déjà d'une instance d'`AxiosHeaders`. ```js from(thing?: AxiosHeaders | RawAxiosHeaders | string): AxiosHeaders; ``` ## Raccourcis Les raccourcis suivants sont disponibles : - `setContentType`, `getContentType`, `hasContentType` - `setContentLength`, `getContentLength`, `hasContentLength` - `setAccept`, `getAccept`, `hasAccept` - `setUserAgent`, `getUserAgent`, `hasUserAgent` - `setContentEncoding`, `getContentEncoding`, `hasContentEncoding` axios-axios-aa54a5e/docs/fr/pages/advanced/headers.md000066400000000000000000000127731526660636600226370ustar00rootroot00000000000000# En-têtes Axios expose sa propre classe AxiosHeaders pour manipuler les en-têtes en utilisant une API de type Map qui garantit des clés insensibles à la casse. Cette classe est utilisée en interne par Axios pour gérer les en-têtes, mais elle est également exposée à l'utilisateur pour plus de commodité. Bien que les en-têtes HTTP soient insensibles à la casse, Axios conservera la casse de l'en-tête original pour des raisons stylistiques et comme solution de contournement lorsque des serveurs tiennent incorrectement compte de la casse des en-têtes. L'ancienne méthode de manipulation directe de l'objet d'en-têtes est toujours disponible, mais dépréciée et non recommandée pour un usage futur. ## Travailler avec les en-têtes L'instance d'objet AxiosHeaders peut contenir différents types de valeurs internes qui contrôlent la logique de définition et de fusion. L'objet d'en-têtes final est obtenu par Axios en appelant la méthode toJSON. L'objet AxiosHeaders est également itérable, vous pouvez donc l'utiliser dans des boucles ou le convertir en tableau ou en objet. Les valeurs d'en-tête peuvent être de l'un des types suivants : - `string` - valeur de chaîne normale qui sera envoyée au serveur - `null` - ignorer l'en-tête lors de la conversion en JSON - `false` - ignorer l'en-tête lors de la conversion en JSON, indique également que la méthode set doit être appelée avec l'option rewrite définie à true pour écraser cette valeur (Axios l'utilise en interne pour permettre aux utilisateurs de refuser l'installation de certains en-têtes comme User-Agent ou Content-Type) - `undefined` - la valeur n'est pas définie ::: warning La valeur de l'en-tête est considérée comme définie si elle n'est pas undefined. ::: L'objet d'en-têtes est toujours initialisé à l'intérieur des intercepteurs et des transformateurs, comme illustré dans l'exemple suivant : ```js axios.interceptors.request.use((request: InternalAxiosRequestConfig) => { request.headers.set("My-header", "value"); request.headers.set({ "My-set-header1": "my-set-value1", "My-set-header2": "my-set-value2", }); // Désactiver la définition ultérieure de cet en-tête par Axios request.headers.set("User-Agent", false); request.headers.setContentType("text/plain"); // L'accès direct comme celui-ci est déprécié request.headers["My-set-header2"] = "newValue"; return request; }); ``` Vous pouvez itérer sur un AxiosHeaders en utilisant n'importe quelle méthode itérable, comme une boucle for-of, forEach, ou l'opérateur spread : ```js const headers = new AxiosHeaders({ foo: '1', bar: '2', baz: '3', }); for (const [header, value] of headers) { console.log(header, value); } // foo 1 // bar 2 // baz 3 ``` ## Définir des en-têtes sur une requête L'endroit le plus courant pour définir des en-têtes est l'option `headers` dans votre configuration de requête ou de configuration d'instance : ```js // Sur une seule requête await axios.get('/api/data', { headers: { 'Accept-Language': 'en-US', 'X-Request-ID': 'abc123', }, }); // Sur une instance (appliqué à chaque requête) const api = axios.create({ headers: { 'X-App-Version': '2.0.0', }, }); ``` ## Préserver la casse d'un en-tête spécifique Les noms d'en-têtes Axios sont insensibles à la casse, mais `AxiosHeaders` conserve la casse de la première clé correspondante qu'il voit. Si vous avez besoin d'une casse spécifique pour un serveur avec un comportement non standard sensible à la casse, définissez un préréglage de casse dans les valeurs par défaut puis définissez les valeurs normalement. ```js const api = axios.create(); api.defaults.headers.common = { 'content-type': undefined, accept: undefined, }; await api.put(url, data, { headers: { 'Content-Type': 'application/octet-stream', Accept: 'application/json', }, }); ``` Vous pouvez également le faire avec `AxiosHeaders` directement lors de la composition d'en-têtes : ```js import axios, { AxiosHeaders } from 'axios'; const headers = AxiosHeaders.concat( { 'content-type': undefined }, { 'Content-Type': 'application/octet-stream' } ); await axios.put(url, data, { headers }); ``` ## Définir des en-têtes dans un intercepteur Les intercepteurs sont l'endroit approprié pour attacher des en-têtes dynamiques comme les tokens d'authentification, car le token peut ne pas être disponible au moment où l'instance est créée : ```js api.interceptors.request.use((config) => { const token = getAuthToken(); // lire au moment de la requête config.headers.set('Authorization', `Bearer ${token}`); return config; }); ``` ## Lire les en-têtes de réponse Les en-têtes de réponse sont disponibles sur `response.headers` en tant qu'instance d'`AxiosHeaders`. Tous les noms d'en-têtes sont en minuscules : ```js const response = await axios.get('/api/data'); console.log(response.headers['content-type']); // application/json; charset=utf-8 console.log(response.headers.get('x-request-id')); // abc123 ``` ## Supprimer un en-tête par défaut Pour refuser un en-tête qu'axios définit par défaut (comme `Content-Type` ou `User-Agent`), définissez sa valeur à `false` : ```js await axios.post('/api/data', payload, { headers: { 'Content-Type': false, // laisser le navigateur le définir automatiquement (ex. pour FormData) }, }); ``` Pour plus de détails sur l'API complète des méthodes `AxiosHeaders`, consultez la page [Méthodes d'en-têtes](/pages/advanced/header-methods). axios-axios-aa54a5e/docs/fr/pages/advanced/html-form-processing.md000066400000000000000000000036741526660636600253030ustar00rootroot00000000000000# Traitement de formulaires HTML (navigateur) Vous pouvez également envoyer un formulaire directement depuis un élément de formulaire HTML. Cela est utile lorsque vous avez un formulaire dans votre page et que vous souhaitez le soumettre sans aucun code JavaScript. ```js await axios.postForm('https://httpbin.org/post', document.querySelector('#htmlForm')); ``` Les objets `FormData` et `HTMLForm` peuvent également être envoyés en `JSON` en définissant explicitement l'en-tête `Content-Type` à `application/json` : ```js await axios.post('https://httpbin.org/post', document.querySelector('#htmlForm'), { headers: { 'Content-Type': 'application/json', }, }); ``` Voici un exemple de formulaire valide pouvant être soumis par le code ci-dessus : ```html
``` Le formulaire ci-dessus sera soumis sous la forme : ```json { "foo": "1", "deep": { "prop": "2" }, "deep prop spaced": "3", "baz": ["4", "5"], "user": { "age": "value2" } } ``` ::: tip Notation des chemins dans les noms de champs Seules les notations par points et crochets créent des chemins de propriétés. Les autres caractères —y compris les espaces, `-`, `+`, `*` et `&`— restent dans les noms de champs littéraux. Par exemple, `deep.prop` crée un chemin imbriqué, tandis que `deep prop spaced` reste une clé de premier niveau. ::: ::: warning L'envoi de Blobs/Fichiers en JSON (base64) n'est actuellement pas supporté. ::: axios-axios-aa54a5e/docs/fr/pages/advanced/http2.md000066400000000000000000000054341526660636600222610ustar00rootroot00000000000000# HTTP2 Le support expérimental de HTTP/2 a été ajouté à l'adaptateur `http` dans la version `1.13.0`. Il est disponible uniquement dans les environnements Node.js. ## Utilisation de base Utilisez l'option `httpVersion` pour sélectionner la version du protocole pour une requête. En la définissant à `2`, vous activez HTTP/2. ```js const { data, headers, status } = await axios.post( "https://httpbin.org/post", form, { httpVersion: 2, }, ); ``` ## `http2Options` Des options natives supplémentaires pour l'appel interne `session.request()` peuvent être passées via l'objet de configuration `http2Options`. Cela inclut également le paramètre personnalisé `sessionTimeout`, qui contrôle la durée (en millisecondes) pendant laquelle une session HTTP/2 inactive est maintenue avant d'être fermée. La valeur par défaut est `1000ms`. ```js { httpVersion: 2, http2Options: { rejectUnauthorized: false, // accepter les certificats auto-signés (développement uniquement) sessionTimeout: 5000, // maintenir la session inactive pendant 5 secondes }, } ``` ::: warning Le support HTTP/2 est actuellement expérimental. L'API peut changer dans les prochaines versions mineures ou de correctifs. ::: ::: warning Les redirections ne sont pas supportées via HTTP/2 L'adaptateur HTTP/2 ne suit actuellement pas les redirections. Si une requête émise avec `httpVersion: 2` reçoit une réponse `3xx`, la redirection n'est pas suivie automatiquement. Gérez ces réponses manuellement ou restez sur HTTP/1.x pour les endpoints qui s'appuient sur les redirections. ::: ## Exemple complet L'exemple ci-dessous envoie une requête POST `multipart/form-data` via HTTP/2 et suit à la fois la progression de l'envoi et du téléchargement. ```js const form = new FormData(); form.append("foo", "123"); const { data, headers, status } = await axios.post( "https://httpbin.org/post", form, { httpVersion: 2, http2Options: { // rejectUnauthorized: false, // sessionTimeout: 1000 }, onUploadProgress(e) { console.log("upload progress", e); }, onDownloadProgress(e) { console.log("download progress", e); }, responseType: "arraybuffer", }, ); ``` ## Référence de configuration | Option | Type | Défaut | Description | |---|---|---|---| | `httpVersion` | `number` | `1` | Version du protocole HTTP à utiliser. Définissez à `2` pour activer HTTP/2. | | `http2Options.sessionTimeout` | `number` | `1000` | Durée en millisecondes avant qu'une session HTTP/2 inactive soit fermée. | Toutes les autres options natives de `session.request()` supportées par le module `http2` intégré de Node.js peuvent également être passées dans `http2Options`. axios-axios-aa54a5e/docs/fr/pages/advanced/interceptors.md000066400000000000000000000173341526660636600237430ustar00rootroot00000000000000# Intercepteurs Les intercepteurs sont un mécanisme puissant permettant d'intercepter et de modifier les requêtes et réponses HTTP. Ils sont très similaires aux middlewares dans Express.js. Un intercepteur est une fonction exécutée avant l'envoi d'une requête et avant la réception d'une réponse. Les intercepteurs sont utiles pour de nombreuses tâches telles que la journalisation, la modification des en-têtes de requête et la modification de la réponse. L'utilisation de base des intercepteurs est la suivante : ```js // Ajouter un intercepteur de requête axios.interceptors.request.use( function (config) { // Effectuez une action avant l'envoi de la requête return config; }, function (error) { // Traitez l'erreur de requête return Promise.reject(error); } ); // Ajouter un intercepteur de réponse axios.interceptors.response.use( function (response) { // Tout code de statut dans la plage 2xx déclenchera cette fonction // Traitez les données de réponse return response; }, function (error) { // Tout code de statut en dehors de la plage 2xx déclenchera cette fonction // Traitez l'erreur de réponse return Promise.reject(error); } ); ``` ## Supprimer des intercepteurs Vous pouvez supprimer n'importe quel intercepteur en utilisant la méthode `eject` sur l'intercepteur que vous souhaitez supprimer. Vous pouvez également supprimer tous les intercepteurs en appelant la méthode `clear` sur l'objet `axios.interceptors`. Voici un exemple de suppression d'un intercepteur : ```js // Éjecter l'intercepteur de requête const myInterceptor = axios.interceptors.request.use(function () { /*...*/ }); axios.interceptors.request.eject(myInterceptor); // Éjecter l'intercepteur de réponse const myInterceptor = axios.interceptors.response.use(function () { /*...*/ }); axios.interceptors.response.eject(myInterceptor); ``` Voici un exemple de suppression de tous les intercepteurs : ```js const instance = axios.create(); instance.interceptors.request.use(function () { /*...*/ }); instance.interceptors.request.clear(); // Supprime les intercepteurs des requêtes instance.interceptors.response.use(function () { /*...*/ }); instance.interceptors.response.clear(); // Supprime les intercepteurs des réponses ``` ## Comportement par défaut des intercepteurs Lorsque vous ajoutez des intercepteurs de requête, ils sont considérés comme asynchrones par défaut. Cela peut provoquer un délai dans l'exécution de votre requête axios lorsque le thread principal est bloqué (une promise est créée en coulisses pour l'intercepteur et votre requête est placée en bas de la pile d'appels). Si vos intercepteurs de requête sont synchrones, vous pouvez ajouter un indicateur à l'objet d'options qui indiquera à axios d'exécuter le code de manière synchrone et d'éviter tout délai dans l'exécution des requêtes. ```js axios.interceptors.request.use( function (config) { config.headers.test = "I am only a header!"; return config; }, null, { synchronous: true } ); ``` ### Erreurs des intercepteurs synchrones Lorsqu'un intercepteur de requête synchrone lève une erreur, axios appelle le gestionnaire `onRejected` associé à cet intercepteur et cesse d'exécuter les intercepteurs de requête restants. Si le gestionnaire retourne normalement —y compris `undefined` ou une Promise résolue— l'erreur est considérée comme gérée et axios envoie la requête avec la dernière configuration valide. La valeur retournée par le gestionnaire ne remplace pas cette configuration. Pour empêcher l'envoi, omettez le gestionnaire de rejet ou faites-lui lever une erreur ou retourner une Promise rejetée. L'erreur terminale continue alors à travers les intercepteurs de rejet de réponse. ```js axios.interceptors.request.use( function validate(config) { if (!config.headers.has("Authorization")) { throw new Error("Authorization is required"); } return config; }, function rejectInvalidRequest(error) { return Promise.reject(error); }, { synchronous: true } ); ``` Un gestionnaire qui se contente de journaliser l'erreur peut retourner normalement pour conserver le comportement existant de poursuite : ```js axios.interceptors.request.use( function prepare(config) { throw new Error("Optional preparation failed"); }, function logPreparationFailure(error) { console.warn(error); // Un retour normal envoie la requête avec la dernière configuration valide. }, { synchronous: true } ); ``` ## Intercepteurs avec `runWhen` Si vous souhaitez exécuter un intercepteur particulier en fonction d'une vérification au moment de l'exécution, vous pouvez ajouter une fonction `runWhen` à l'objet d'options. L'intercepteur ne sera pas exécuté si et seulement si le résultat de `runWhen` est `false`. La fonction sera appelée avec l'objet de configuration (n'oubliez pas que vous pouvez également y lier vos propres arguments). Cela peut être utile lorsque vous avez un intercepteur de requête asynchrone qui ne doit s'exécuter que dans certaines conditions. ```js function onGetCall(config) { return config.method === "get"; } axios.interceptors.request.use( function (config) { config.headers.test = "special get headers"; return config; }, null, { runWhen: onGetCall } ); ``` ## Ordre d'exécution des intercepteurs ::: warning Les intercepteurs de requête et de réponse s'exécutent dans des ordres **opposés** Les intercepteurs de requête sont exécutés dans l'**ordre inverse** (LIFO — last in, first out). Le _dernier_ intercepteur de requête ajouté est exécuté en _premier_. Les intercepteurs de réponse sont exécutés dans l'**ordre où ils ont été ajoutés** (FIFO — first in, first out). Le _premier_ intercepteur de réponse ajouté est exécuté en _premier_. ::: L'exemple suivant montre l'ordre d'exécution complet pour trois intercepteurs de requête et trois intercepteurs de réponse : ```js const instance = axios.create(); const interceptor = (id) => (base) => { console.log(id); return base; }; instance.interceptors.request.use(interceptor("Request Interceptor 1")); instance.interceptors.request.use(interceptor("Request Interceptor 2")); instance.interceptors.request.use(interceptor("Request Interceptor 3")); instance.interceptors.response.use(interceptor("Response Interceptor 1")); instance.interceptors.response.use(interceptor("Response Interceptor 2")); instance.interceptors.response.use(interceptor("Response Interceptor 3")); // Sortie console : // Request Interceptor 3 // Request Interceptor 2 // Request Interceptor 1 // [La requête HTTP est effectuée] // Response Interceptor 1 // Response Interceptor 2 // Response Interceptor 3 ``` ## Intercepteurs multiples Vous pouvez ajouter plusieurs intercepteurs à la même requête ou réponse. Les règles suivantes s'appliquent pour plusieurs intercepteurs dans la même chaîne, dans l'ordre indiqué ci-dessous : - Chaque intercepteur est exécuté - Les intercepteurs de requête sont exécutés dans l'ordre inverse (LIFO). - Les intercepteurs de réponse sont exécutés dans l'ordre où ils ont été ajoutés (FIFO). - Seul le résultat du dernier intercepteur est retourné - Chaque intercepteur reçoit le résultat de son prédécesseur - Lorsqu'un intercepteur de réussite lève une exception : - L'intercepteur de réussite suivant n'est pas appelé - L'intercepteur d'échec suivant est appelé - Une fois capturée, un autre intercepteur de réussite suivant est à nouveau appelé (comme dans une chaîne de promises). ::: tip Pour une compréhension approfondie du fonctionnement des intercepteurs, vous pouvez lire les cas de test disponibles [ici](https://github.com/axios/axios/blob/v1.x/tests/browser/interceptors.browser.test.js). ::: axios-axios-aa54a5e/docs/fr/pages/advanced/multipart-form-data-format.md000066400000000000000000000173261526660636600264020ustar00rootroot00000000000000# Format multipart/form-data axios peut envoyer des requêtes au format `multipart/form-data`. Ce format est couramment utilisé lors de l'envoi de fichiers. Pour envoyer une requête dans ce format, vous devez créer un objet `FormData` et y ajouter les données. Vous pouvez ensuite passer l'objet `FormData` à la propriété `data` de la configuration de requête axios. ```js const formData = new FormData(); formData.append('foo', 'bar'); axios.post('https://httpbin.org/post', formData); ``` Ne définissez pas manuellement l'en-tête `Content-Type` pour les objets `FormData` de navigateur, web worker ou React Native ; ces environnements ajoutent eux-mêmes la boundary multipart. Dans Node.js, vous pouvez utiliser la bibliothèque `form-data` comme suit : ```js const FormData = require('form-data'); const form = new FormData(); form.append('my_field', 'my value'); form.append('my_buffer', Buffer.alloc(10)); form.append('my_file', fs.createReadStream('/foo/bar.jpg')); axios.post('https://example.com', form); ``` ## Sérialisation automatique vers FormData À partir de la version v0.27.0, Axios prend en charge la sérialisation automatique d'objets en objet FormData si l'en-tête Content-Type de la requête est défini à multipart/form-data. Cela signifie que vous pouvez passer directement un objet JavaScript à la propriété data de la configuration de requête axios. Par exemple lors de l'envoi de données vers une requête POST : ```js import axios from 'axios'; axios .post( 'https://httpbin.org/post', { x: 1 }, { headers: { 'Content-Type': 'multipart/form-data', }, } ) .then(({ data }) => console.log(data)); ``` Dans la version Node.js, le polyfill ([`form-data`](https://github.com/form-data/form-data)) est utilisé par défaut. Vous pouvez remplacer la classe FormData en définissant la variable de configuration env.FormData, mais vous n'en aurez probablement pas besoin dans la plupart des cas : ```js const axios = require('axios'); var FormData = require('form-data'); axios .post( 'https://httpbin.org/post', { x: 1, buf: Buffer.alloc(10) }, { headers: { 'Content-Type': 'multipart/form-data', }, } ) .then(({ data }) => console.log(data)); ``` ## Politique d'en-têtes pour `FormData` Node.js Lorsque vous passez un objet `FormData` Node.js qui expose `getHeaders()` (comme le package [`form-data`](https://github.com/form-data/form-data)), axios copie par défaut tous les en-têtes qu'il retourne sur la requête. Cela préserve la compatibilité v1 mais peut être problématique lorsque l'objet `FormData` provient d'une source non fiable — `getHeaders()` pourrait écraser des en-têtes comme `Authorization` ou en injecter des arbitraires. Définissez `formDataHeaderPolicy: 'content-only'` pour copier **uniquement** `Content-Type` et `Content-Length` depuis `getHeaders()`, puis définissez tout autre en-tête explicitement via la configuration `headers` de la requête : ```js await axios.post('https://example.com/upload', form, { formDataHeaderPolicy: 'content-only', headers: { Authorization: 'Bearer my-token', }, }); ``` La valeur par défaut est `'legacy'`. Voir [`formDataHeaderPolicy`](/pages/advanced/request-config#formdataheaderpolicy) dans la référence de configuration de requête pour plus de détails. ## Terminaisons supportées Le sérialiseur FormData d'Axios supporte quelques terminaisons spéciales pour effectuer les opérations suivantes : - `{}` - sérialiser la valeur avec JSON.stringify - `[]` - décomposer l'objet de type tableau en champs séparés avec la même clé ::: warning Remarque : l'opération de décomposition/expansion sera utilisée par défaut sur les tableaux et les objets FileList ::: ## Configurer le sérialiseur FormData Le sérialiseur FormData supporte des options supplémentaires via la propriété d'objet config.formSerializer pour gérer les cas particuliers : - `visitor: Function` - fonction visiteur définie par l'utilisateur qui sera appelée récursivement pour sérialiser l'objet de données en objet FormData en suivant des règles personnalisées. - `dots: boolean = false` - utiliser la notation pointée au lieu de crochets pour sérialiser les tableaux et les objets ; - `metaTokens: boolean = true` - ajouter la terminaison spéciale (ex. `user{}: '{"name": "John"}'`) dans la clé FormData. Le body-parser du backend pourrait potentiellement utiliser ces méta-informations pour analyser automatiquement la valeur en JSON. - `indexes: null|false|true = false` - contrôle comment les index seront ajoutés aux clés décomposées d'objets de type tableau plat - `null` - ne pas ajouter de crochets (`arr: 1`, `arr: 2`, `arr: 3`) - `false` (défaut) - ajouter des crochets vides (`arr[]: 1`, `arr[]: 2`, `arr[]: 3`) - `true` - ajouter des crochets avec index (`arr[0]: 1`, `arr[1]: 2`, `arr[2]: 3`) - `maxDepth: number = 100` - profondeur maximale d'imbrication des objets dans laquelle le sérialiseur va récurser. Si l'entrée dépasse cette profondeur, une `AxiosError` avec `code: 'ERR_FORM_DATA_DEPTH_EXCEEDED'` est levée. Cela protège les applications côté serveur contre les attaques DoS via des charges utiles profondément imbriquées. Définir à `Infinity` pour désactiver la limite. - `Blob: typeof Blob` - constructeur Blob utilisé lors de la conversion de valeurs de type ArrayBuffer pour un `FormData` conforme à la spécification. Ne le remplacez que dans les runtimes qui fournissent un constructeur `Blob` compatible sous un autre identifiant. ```js // Autoriser une imbrication plus profonde pour les schémas qui dépassent légitimement 100 niveaux : axios.postForm('/api', data, { formSerializer: { maxDepth: 200 } }); ``` ::: warning Note de sécurité La limite par défaut de 100 est intentionnelle. Le code côté serveur qui transfère du JSON contrôlé par le client vers axios en tant que `data` est vulnérable à un débordement de pile d'appels sans cette protection. N'augmentez `maxDepth` que si votre schéma le nécessite réellement. ::: Par exemple, si nous avons un objet comme celui-ci : ```js const obj = { x: 1, arr: [1, 2, 3], arr2: [1, [2], 3], users: [ { name: 'Peter', surname: 'Griffin' }, { name: 'Thomas', surname: 'Anderson' }, ], 'obj2{}': [{ x: 1 }], }; ``` Les étapes suivantes seront exécutées en interne par le sérialiseur Axios : ```js const formData = new FormData(); formData.append('x', '1'); formData.append('arr[]', '1'); formData.append('arr[]', '2'); formData.append('arr[]', '3'); formData.append('arr2[0]', '1'); formData.append('arr2[1][0]', '2'); formData.append('arr2[2]', '3'); formData.append('users[0][name]', 'Peter'); formData.append('users[0][surname]', 'Griffin'); formData.append('users[1][name]', 'Thomas'); formData.append('users[1][surname]', 'Anderson'); formData.append('obj2{}', '[{"x":1}]'); ``` ## Reconvertir FormData en JSON `axios.formToJSON()` convertit la notation par points et crochets des noms de champs en objets et tableaux imbriqués. Seuls `.`, `[` et `]` sont des séparateurs structurels. Les caractères tels que `-`, les espaces, `+`, `*` et `&` restent dans les clés littérales. ```js const form = new FormData(); form.append('user-name', 'johndoe'); form.append('user.name', 'john'); console.log(axios.formToJSON(form)); // { // 'user-name': 'johndoe', // user: { name: 'john' } // } ``` `user[name]` crée également un chemin d'objet imbriqué, tandis que `items[]` crée un tableau. Axios supporte les méthodes raccourcies suivantes : `postForm`, `putForm`, `patchForm` qui sont simplement les méthodes HTTP correspondantes avec l'en-tête `Content-Type` prédéfini à `multipart/form-data`. axios-axios-aa54a5e/docs/fr/pages/advanced/progress-capturing.md000066400000000000000000000042111526660636600250460ustar00rootroot00000000000000# Capture de progression Axios prend en charge la capture de la progression des envois et téléchargements dans les environnements navigateur et Node.js. La fréquence des événements de progression est limitée à 3 fois par seconde. Cela permet d'éviter de surcharger le navigateur avec des événements de progression. Voici un exemple de capture d'événements de progression : ```js await axios.post(url, data, { onUploadProgress: function (axiosProgressEvent) { /*{ loaded: number; total?: number; progress?: number; // dans la plage [0..1] bytes: number; // nombre d'octets transférés depuis le dernier déclenchement (delta) estimated?: number; // temps estimé en secondes rate?: number; // vitesse d'envoi en octets upload: true; // indicateur d'envoi }*/ }, onDownloadProgress: function (axiosProgressEvent) { /*{ loaded: number; total?: number; progress?: number; bytes: number; estimated?: number; rate?: number; // vitesse de téléchargement en octets download: true; // indicateur de téléchargement }*/ }, }); ``` Vous pouvez également transmettre les événements de progression d'envoi et de téléchargement vers un stream lisible dans Node.js. Cela est utile lorsque vous souhaitez afficher la progression de manière personnalisée. Voici un exemple de transmission des événements de progression : ```js const { data } = await axios.post(SERVER_URL, readableStream, { onUploadProgress: ({ progress }) => { console.log((progress * 100).toFixed(2)); }, headers: { "Content-Length": contentLength, }, maxRedirects: 0, // éviter de buffériser l'intégralité du stream }); ``` ::: warning La capture de la progression d'envoi de FormData n'est actuellement pas supportée dans les environnements Node.js ::: ::: danger Il est recommandé de désactiver les redirections en définissant maxRedirects: 0 pour envoyer le stream dans l'environnement Node.js, car le package follow-redirects bufférisera l'intégralité du stream en RAM sans suivre l'algorithme de « backpressure » ::: axios-axios-aa54a5e/docs/fr/pages/advanced/promises.md000066400000000000000000000053431526660636600230600ustar00rootroot00000000000000# Promises axios est construit sur l'API Promise native d'ES6. Chaque requête axios retourne une Promise qui se résout vers un objet de réponse ou se rejette avec une erreur. Si votre environnement ne supporte pas les Promises ES6, vous devrez les polyfiller — par exemple avec [es6-promise](https://github.com/stefanpenner/es6-promise). Pour les intégrations TypeScript, `AxiosPromise` est une promise de `AxiosResponse`. Elle conserve les données et paramètres de requête dans `response.config` : ```ts declare const search: AxiosPromise; search.then((response) => { response.data; // SearchResponse response.config.data; // RequestBody | undefined response.config.params; // SearchParams | undefined }); ``` ## then / catch / finally Comme axios retourne une Promise standard, vous pouvez utiliser `.then()`, `.catch()` et `.finally()` pour gérer le résultat : ```js axios.get("/api/users") .then((response) => { console.log(response.data); }) .catch((error) => { console.error("Request failed:", error.message); }) .finally(() => { console.log("Request finished"); }); ``` ## async / await L'approche recommandée pour la plupart des bases de code est `async/await`, qui rend le code asynchrone lisible comme du code synchrone : ```js async function fetchUser(id) { try { const response = await axios.get(`/api/users/${id}`); return response.data; } catch (error) { console.error("Failed to fetch user:", error.message); throw error; } } ``` ## Requêtes parallèles Comme axios retourne une Promise standard, vous pouvez utiliser `Promise.all` pour effectuer plusieurs requêtes simultanément et attendre qu'elles se terminent toutes : ```js const [users, posts] = await Promise.all([ axios.get("/api/users"), axios.get("/api/posts"), ]); console.log(users.data, posts.data); ``` ::: tip `Promise.all` rejettera dès que l'une des requêtes échoue. Si vous souhaitez gérer les échecs partiels, utilisez plutôt `Promise.allSettled`. ::: ```js const results = await Promise.allSettled([ axios.get("/api/users"), axios.get("/api/posts"), ]); results.forEach((result) => { if (result.status === "fulfilled") { console.log(result.value.data); } else { console.error("Request failed:", result.reason.message); } }); ``` ## Chaînage de requêtes Vous pouvez chaîner des appels `.then()` pour exécuter des requêtes séquentiellement, en passant les données d'une requête à la suivante : ```js axios.get("/api/user/1") .then(({ data: user }) => axios.get(`/api/posts?userId=${user.id}`)) .then(({ data: posts }) => { console.log("Posts for user:", posts); }) .catch(console.error); ``` axios-axios-aa54a5e/docs/fr/pages/advanced/rate-limiting.md000066400000000000000000000045701526660636600237650ustar00rootroot00000000000000# Limitation de débit axios prend en charge la limitation de bande passante dans l'environnement Node.js via l'adaptateur HTTP. Cela vous permet de plafonner la vitesse d'envoi ou de téléchargement des données, ce qui est utile pour les opérations en masse, les tâches en arrière-plan ou le scraping respectueux qui ne doit pas saturer une connexion. ## `maxRate` L'option `maxRate` accepte soit un nombre (octets par seconde) soit un tableau où la première valeur est la limite d'envoi et la deuxième valeur est la limite de téléchargement. Utilisez `[uploadRate]` pour limiter uniquement l'envoi, ou `[uploadRate, downloadRate]` pour limiter les deux sens. Lorsqu'un nombre unique est passé, la même limite s'applique à l'envoi et au téléchargement. ```js // Limiter l'envoi et le téléchargement à 100 Ko/s await axios.get(URL, { maxRate: 100 * 1024 }); // Limiter l'envoi à 100 Ko/s, le téléchargement à 500 Ko/s await axios.get(URL, { maxRate: [100 * 1024, 500 * 1024] }); ``` ::: warning `maxRate` n'est supporté que par l'adaptateur HTTP Node.js. Il n'a aucun effet dans les environnements navigateur. ::: ## Limitation du débit d'envoi Plafonnez la vitesse d'envoi tout en journalisant la progression en même temps : ```js const { data } = await axios.post(SERVER_URL, myBuffer, { onUploadProgress: ({ progress, rate }) => { const percent = (progress * 100).toFixed(1); const kbps = (rate / 1024).toFixed(1); console.log(`Upload [${percent}%] at ${kbps} KB/s`); }, maxRate: [100 * 1024], // plafonner l'envoi à 100 Ko/s }); ``` ## Limitation du débit de téléchargement Plafonnez la vitesse de téléchargement pour les réponses volumineuses : ```js const { data } = await axios.get(FILE_URL, { onDownloadProgress: ({ progress, rate }) => { const percent = (progress * 100).toFixed(1); const kbps = (rate / 1024).toFixed(1); console.log(`Download [${percent}%] at ${kbps} KB/s`); }, maxRate: [Infinity, 200 * 1024], // pas de limite d'envoi, 200 Ko/s en téléchargement responseType: "arraybuffer", }); ``` ## Limitation combinée envoi et téléchargement Passez les deux limites sous forme de tableau pour contrôler les deux sens simultanément : ```js await axios.post(SERVER_URL, largeBuffer, { maxRate: [50 * 1024, 500 * 1024], // 50 Ko/s en envoi, 500 Ko/s en téléchargement }); ``` axios-axios-aa54a5e/docs/fr/pages/advanced/request-config.md000066400000000000000000001036601526660636600241530ustar00rootroot00000000000000# Configuration de requête La configuration de requête est utilisée pour paramétrer la requête. Un large éventail d'options est disponible, mais la seule option obligatoire est `url`. Si l'objet de configuration ne contient pas de champ `method`, la méthode par défaut est `GET`. ::: warning Sécurité : la protection contre les bombes de décompression est optionnelle Par défaut, `maxContentLength` et `maxBodyLength` valent `-1` (illimité). Un serveur malveillant ou compromis peut renvoyer un petit corps compressé en gzip/deflate/brotli/zstd qui s'étend à plusieurs gigaoctets et épuise le processus Node.js. Si vous appelez des serveurs auxquels vous ne faites pas pleinement confiance, **définissez un plafond** : ```js axios.defaults.maxContentLength = 10 * 1024 * 1024; // 10 Mo axios.defaults.maxBodyLength = 10 * 1024 * 1024; ``` Consultez le [guide de sécurité](/pages/misc/security) pour plus de détails. ::: ### `url` L'`url` est l'URL vers laquelle la requête est envoyée. Il peut s'agir d'une chaîne de caractères ou d'une instance de `URL`. Une URL `http:` ou `https:` doit inclure `//` après le protocole. Les valeurs mal formées telles que `https:example.com` et `https:/example.com` sont rejetées avec `ERR_INVALID_URL` ; utilisez une URL bien formée comme `https://example.com`. ### `method` La `method` est la méthode HTTP à utiliser pour la requête. La méthode par défaut est `GET`. ### `baseURL` La `baseURL` est l'URL de base à ajouter en préfixe à l'`url`, sauf si celle-ci est une URL absolue. Utile pour effectuer des requêtes vers le même domaine sans avoir à répéter le nom de domaine et tout préfixe d'API ou de version. `baseURL` est une commodité de construction d'URL, pas une limite de sécurité. Si l'`url` d'une requête provient d'une entrée non fiable, validez-la avant de la transmettre à axios. Une `url` relative peut contenir des segments `..` ; après qu'axios l'a combinée avec `baseURL`, le parseur d'URL de la plateforme normalise le chemin et peut résoudre la requête en dehors du préfixe de chemin prévu. `allowAbsoluteUrls: false` empêche les URLs absolues de remplacer `baseURL`, mais ne valide ni ne limite les chemins relatifs. La même règle d'URL bien formée s'applique à `baseURL` : les valeurs `http:` et `https:` doivent inclure `//` après le protocole. ### `allowAbsoluteUrls` `allowAbsoluteUrls` détermine si les URLs absolues peuvent remplacer une `baseUrl` configurée. Lorsqu'elle est définie à `true` (valeur par défaut), les valeurs absolues de `url` remplacent `baseUrl`. Lorsqu'elle est définie à `false`, les valeurs absolues de `url` sont toujours précédées de `baseUrl`. ### `transformRequest` La fonction `transformRequest` vous permet de modifier les données de la requête avant leur envoi au serveur. Cette fonction est appelée avec les données de la requête comme seul argument. Elle ne s'applique que pour les méthodes de requête `PUT`, `POST`, `PATCH` et `DELETE`. La dernière fonction du tableau doit retourner une chaîne ou une instance de Buffer, ArrayBuffer, FormData ou Stream. ### `transformResponse` La fonction `transformResponse` vous permet de modifier les données de la réponse avant qu'elles ne soient transmises aux fonctions `then` ou `catch`. Cette fonction est appelée avec les données de la réponse comme seul argument. ### `parseReviver` La fonction `parseReviver` vous permet de fournir une fonction « reviver » personnalisée directement à l'appel natif `JSON.parse()` utilisé par le `transformResponse` par défaut. C'est particulièrement utile pour effectuer une hydratation de types haute performance (par exemple, convertir des chaînes ISO en objets `Temporal` ou `Date`) ou pour éviter une perte de précision lors de l'analyse. Dans les environnements modernes (ES2023+), la fonction reviver reçoit un troisième argument `context`. Celui-ci donne accès au `source` JSON brut, permettant la conversion précise de grands entiers (BigInt) qui perdraient autrement en précision s'ils étaient analysés comme des nombres JavaScript standards. > Remarque : `Temporal` n'est pas encore disponible dans tous les environnements. Envisagez l'utilisation d'un polyfill si nécessaire. ```js const client = axios.create({ parseReviver: (key, value, context) => { // Exemple : analyse BigInt sans perte de précision if (typeof value === 'number' && context?.source) { const isInteger = Number.isInteger(value); const isUnsafe = !Number.isSafeInteger(value); const isValidIntegerString = /^-?\d+$/.test(context.source); if (isInteger && isUnsafe && isValidIntegerString) { try { return BigInt(context.source); } catch { // Solution de repli : retourne la valeur d'origine si l'analyse échoue } } } // Exemple : hydratation des dates en objets Temporal if ( typeof value === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(value) && typeof Temporal !== 'undefined' && Temporal?.PlainDate ) { return Temporal.PlainDate.from(value); } return value; }, }); ``` ### `headers` Les `headers` sont les en-têtes HTTP à envoyer avec la requête. L'en-tête `Content-Type` est défini à `application/json` par défaut. ### `params` Les `params` sont les paramètres d'URL à envoyer avec la requête. Il doit s'agir d'un objet simple ou d'un objet URLSearchParams. Si l'`url` contient des paramètres de requête, ils seront fusionnés avec l'objet `params`. ### `paramsSerializer` La fonction `paramsSerializer` vous permet de sérialiser l'objet `params` avant son envoi au serveur. Plusieurs options sont disponibles pour cette fonction ; veuillez vous référer à l'exemple de configuration complète en bas de cette page. En TypeScript, `AxiosRequestConfig` utilise `P` pour `params`, et un `paramsSerializer` personnalisé reçoit ce même type : ```ts interface SearchParams { query: string; page?: number; } const config: AxiosRequestConfig = { params: { query: "axios", page: 1 }, paramsSerializer: (params) => `${params.query}:${params.page ?? 1}`, }; ``` #### Encodage pour-cent strict RFC 3986 Par défaut, axios redécode `%3A`, `%24`, `%2C` et `%20` vers `:`, `$`, `,` et `+` pour la lisibilité (le `+` suit la convention `application/x-www-form-urlencoded` pour représenter une espace dans une chaîne de requête). Ces caractères sont valides dans un composant de requête selon la [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986#section-3.4), donc la sortie par défaut est correcte. Cependant, certains backends exigent un encodage pour-cent strict et rejettent la forme lisible. Utilisez l'option `encode` pour remplacer l'encodeur par défaut : ```js // Par requête : émettre un encodage pour-cent strict RFC 3986 pour les valeurs de requête axios.get('/foo', { params: { filter: JSON.stringify({ startedAt: '2026-01-23' }) }, paramsSerializer: { encode: encodeURIComponent }, }); // Ou définir cela sur les valeurs par défaut de l'instance const client = axios.create({ paramsSerializer: { encode: encodeURIComponent }, }); ``` ### `data` Les `data` sont les données à envoyer comme corps de la requête. Il peut s'agir d'une chaîne, d'un objet simple, d'un Buffer, d'un ArrayBuffer, d'un FormData, d'un Stream ou d'un URLSearchParams. Ne s'applique que pour les méthodes de requête `PUT`, `POST`, `DELETE` et `PATCH`. Sans `transformRequest`, doit être de l'un des types suivants : - chaîne, objet simple, ArrayBuffer, ArrayBufferView, URLSearchParams - Navigateur uniquement : FormData, File, Blob - React Native : FormData - Node uniquement : Stream, Buffer, FormData (package form-data) Pour les objets `FormData` de navigateur, web worker et React Native, ne définissez pas manuellement `Content-Type` ; l'environnement ajoute lui-même la boundary multipart. Pour les objets `FormData` Node.js qui fournissent une méthode `getHeaders()`, axios copie tous les en-têtes retournés par défaut pour assurer la compatibilité avec la v1. Si l'objet `FormData` est personnalisé ou n'est pas pleinement de confiance, définissez `formDataHeaderPolicy: 'content-only'` pour ne copier que `Content-Type` et `Content-Length`, et définissez explicitement tout autre en-tête de requête via la configuration `headers` de la requête. ### `formDataHeaderPolicy` Contrôle la manière dont axios copie les en-têtes retournés par `FormData#getHeaders()` de Node.js. La valeur par défaut est `'legacy'`, qui copie tous les en-têtes retournés afin de préserver le comportement existant de la v1. Définissez `'content-only'` pour ne copier que `Content-Type` et `Content-Length` depuis `getHeaders()`. ### Options personnalisées avec des clés symboles La fusion des configurations préserve les propriétés symboles propres et énumérables. Les applications TypeScript peuvent augmenter `AxiosRequestConfig` avec une clé symbole précise, puis lire cette option depuis `InternalAxiosRequestConfig` dans un intercepteur de requête ou un adaptateur. Les propriétés symboles héritées ou non énumérables ne sont pas copiées. Consultez le [guide TypeScript](/pages/advanced/type-script#configuration-de-requete-personnalisee-avec-des-cles-symboles) pour un exemple. ### `timeout` Le `timeout` est le nombre de millisecondes avant l'expiration de la requête. Si la requête dure plus longtemps que `timeout`, elle sera annulée. ### `withCredentials` La propriété `withCredentials` indique si les requêtes Cross-site Access-Control doivent être effectuées avec des informations d'identification telles que des cookies, des en-têtes d'autorisation ou des certificats client TLS. La définition de `withCredentials` n'a aucun effet sur les requêtes du même site. ### `adapter` `adapter` permet une gestion personnalisée des requêtes, ce qui facilite les tests. Retournez une promise et fournissez une réponse valide ; consultez [les adaptateurs](/pages/advanced/adapters) pour plus d'informations. Nous fournissons également un certain nombre d'adaptateurs intégrés. L'adaptateur par défaut est `http` pour Node et `xhr` pour les navigateurs. La liste complète des adaptateurs intégrés est la suivante : - fetch - http - xhr Vous pouvez également passer un tableau d'adaptateurs ; axios utilisera le premier pris en charge par l'environnement. ### `auth` `auth` indique que l'authentification HTTP Basic doit être utilisée, et fournit les identifiants. Cela définira un en-tête `Authorization`, en écrasant tout en-tête `Authorization` personnalisé que vous auriez défini via `headers`. Si `auth` est omis, les adaptateurs HTTP Node.js et fetch peuvent déduire les identifiants Basic depuis l'URL de requête, par exemple `https://user:pass@example.com` ; les identifiants encodés en pourcentage dans l'URL sont décodés, et `auth` prend toujours le dessus sur les identifiants intégrés à l'URL. Dans l'adaptateur HTTP Node.js, l'authentification Basic est conservée lors des redirections de même origine et supprimée lors des redirections cross-origin. Notez que seule l'authentification HTTP Basic est configurable via ce paramètre. Pour les tokens Bearer et similaires, utilisez plutôt des en-têtes `Authorization` personnalisés. ### `responseType` Le `responseType` indique le type de données que le serveur retournera. Il peut s'agir de l'un des types suivants : - arraybuffer - document - json - text - stream - blob (navigateur uniquement) - formdata (adaptateur fetch uniquement) ### `responseEncoding` Le `responseEncoding` indique l'encodage à utiliser pour décoder les réponses. Les options suivantes sont prises en charge : - ascii - ASCII - ansi - ANSI - binary - BINARY - base64 - BASE64 - base64url - BASE64URL - hex - HEX - latin1 - LATIN1 - ucs-2 - UCS-2 - ucs2 - UCS2 - utf-8 - UTF-8 - utf8 - UTF8 - utf16le - UTF16LE ::: tip Remarque : ignoré pour un `responseType` de `stream` ou pour les requêtes côté client ::: ### `xsrfCookieName` Le `xsrfCookieName` est le nom du cookie à utiliser comme valeur pour le token `XSRF`. ### `xsrfHeaderName` Le `xsrfHeaderName` est le nom de l'en-tête à utiliser comme valeur pour le token `XSRF`. ### `withXSRFToken` `withXSRFToken` contrôle si axios lit le cookie XSRF et définit l'en-tête XSRF sur les requêtes du navigateur. Accepte : - `undefined` _(par défaut)_ — définit l'en-tête XSRF uniquement pour les requêtes du même site (same-origin). - `true` — définit toujours l'en-tête XSRF, y compris pour les requêtes cross-origin. - `false` — ne définit jamais l'en-tête XSRF. - `(config: InternalAxiosRequestConfig) => boolean | undefined` — un callback qui décide par requête, en recevant l'objet de configuration interne. ```ts withXSRFToken: boolean | undefined | ((config: InternalAxiosRequestConfig) => boolean | undefined); ``` ::: warning XSRF cross-origin et `withCredentials` `withCredentials` contrôle si les requêtes cross-site incluent des informations d'identification (cookies, authentification HTTP). Dans les anciennes versions d'axios, définir `withCredentials: true` provoquait implicitement l'envoi de l'en-tête XSRF pour les requêtes cross-origin. Les versions plus récentes d'axios séparent ces préoccupations : pour autoriser l'envoi de l'en-tête XSRF sur des requêtes cross-origin, vous devez définir **à la fois** `withCredentials: true` et `withXSRFToken: true`. ```js axios.get('/user', { withCredentials: true, withXSRFToken: true }); ``` ::: ### `onUploadProgress` La fonction `onUploadProgress` vous permet d'écouter la progression d'un envoi. ### `onDownloadProgress` La fonction `onDownloadProgress` vous permet d'écouter la progression d'un téléchargement. ### `maxContentLength` La propriété `maxContentLength` définit la taille maximale de la réponse en octets. L'adaptateur HTTP Node.js l'applique aux réponses mises en mémoire tampon et aux réponses streamées. L'adaptateur fetch l'applique lorsque la longueur de la réponse est déclarée, lorsque le stream de réponse peut être suivi ou lorsque la taille de la réponse peut être déterminée. > ⚠� **Sécurité :** la valeur par défaut est `-1` (illimitée). Des réponses non bornées combinées à la décompression gzip/deflate/brotli/zstd rendent possible un déni de service par bombe de décompression. > Définissez une limite explicite lorsque vous consommez des serveurs auxquels vous ne faites pas pleinement confiance. ### `maxBodyLength` La propriété `maxBodyLength` définit la taille maximale du corps de requête en octets. L'adaptateur HTTP Node.js l'applique, et l'adaptateur fetch l'applique lorsque la longueur du corps de requête peut être déterminée. ### `redact` La propriété `redact` est un tableau optionnel de noms de clés de configuration à masquer lorsqu'une `AxiosError` est sérialisée avec `toJSON()`. La correspondance est insensible à la casse et récursive sur l'ensemble de la configuration de requête sérialisée. Les valeurs correspondantes sont remplacées par `[REDACTED ****]`. `redact` n'affecte que la sérialisation des erreurs. Elle ne modifie ni les données de la requête, ni les en-têtes, ni l'objet de configuration original. ```js axios .get('/user/12345', { headers: { Authorization: 'Bearer token' }, auth: { username: 'me', password: 'secret' }, redact: ['authorization', 'password'], }) .catch((error) => { console.log(error.toJSON().config); }); ``` ### `validateStatus` La fonction `validateStatus` vous permet de remplacer la validation du code de statut par défaut. Par défaut, axios rejette la promise si le code de statut n'est pas dans la plage 200-299. Vous pouvez remplacer ce comportement en fournissant une fonction `validateStatus` personnalisée. La fonction doit retourner `true` si le code de statut est dans la plage que vous souhaitez accepter. Par défaut, définir explicitement `validateStatus: undefined` conserve le comportement historique et résout tous les statuts de réponse, car `transitional.validateStatusUndefinedResolves` vaut `true` par défaut. Définissez `transitional.validateStatusUndefinedResolves` à `false` si vous voulez qu'un `validateStatus: undefined` explicite se comporte comme si `validateStatus` était omis : axios utilise alors le validateur configuré/par défaut et rejette les réponses non-2xx par défaut. `validateStatus: null` accepte toujours tous les statuts de réponse. Si vous désactivez le comportement de transition et souhaitez intentionnellement résoudre tous les statuts, utilisez `validateStatus: null` ou un validateur qui retourne `true`. ```js axios.get('/user/12345', { validateStatus: undefined, transitional: { validateStatusUndefinedResolves: false, }, }); ``` ### `maxRedirects` La propriété `maxRedirects` définit le nombre maximum de redirections à suivre. Si défini à 0, aucune redirection ne sera suivie. ### `sensitiveHeaders` La propriété `sensitiveHeaders` est un tableau optionnel de noms d'en-têtes personnalisés contenant des secrets, comme `X-API-Key`, que l'adaptateur HTTP Node.js retire lorsqu'il suit une redirection vers une origine différente. La correspondance est insensible à la casse. Les redirections same-origin conservent ces en-têtes. Si `maxRedirects` vaut `0`, axios ne suit pas les redirections et `sensitiveHeaders` n'est pas utilisée. ```js axios.get('https://api.example.com/users', { headers: { 'X-API-Key': 'secret' }, sensitiveHeaders: ['X-API-Key'], }); ``` ### `beforeRedirect` La fonction `beforeRedirect` vous permet de modifier la requête avant qu'elle ne soit redirigée. Utilisez-la pour ajuster les options de requête lors d'une redirection, inspecter les derniers en-têtes de réponse, ou annuler la requête en levant une erreur. Si `maxRedirects` est défini à 0, `beforeRedirect` n'est pas utilisé. ```js beforeRedirect: (options, { headers }) => { if (options.hostname === 'example.com' && options.protocol === 'https:') { options.auth = 'user:password'; } }; ``` ::: warning Sécurité : réinjection d'identifiants lors d'une redirection Le hook `beforeRedirect` s'exécute **après** que les en-têtes sensibles aient été retirés pendant les redirections. La bibliothèque `follow-redirects` supprime les identifiants en cas de rétrogradation de protocole (HTTPS → HTTP) pour des raisons de sécurité. Comme `beforeRedirect` s'exécute après cela, réinjecter des identifiants sans vérifier le protocole de destination peut exposer des données sensibles. Ne réinjectez les identifiants que pour des destinations HTTPS de confiance, et évitez de les réinjecter sur des redirections rétrogradées. ::: ### `socketPath` La propriété `socketPath` définit un socket UNIX à utiliser à la place d'une connexion TCP. Par exemple `/var/run/docker.sock` pour envoyer des requêtes au daemon Docker. Seul `socketPath` ou `proxy` peut être spécifié. Si les deux sont spécifiés, `socketPath` est utilisé. :::warning Sécurité Lorsque `socketPath` est défini, le hostname et le port de l'URL de la requête sont ignorés et axios communique directement avec le socket Unix indiqué. Si une partie de la configuration de la requête provient d'une entrée utilisateur (par exemple dans un proxy ou un gestionnaire de webhooks qui transfère des options), un attaquant peut injecter `socketPath` pour rediriger le trafic vers des sockets locaux privilégiés tels que `/var/run/docker.sock`, `/run/containerd/containerd.sock` ou `/run/systemd/private`, contournant entièrement les protections SSRF basées sur le hostname (CWE-918). Filtrez la configuration provenant d'entrées non fiables et/ou restreignez les chemins de socket acceptés avec `allowedSocketPaths` (voir ci-dessous). ::: ### `allowedSocketPaths` Restreint les chemins de socket pouvant être utilisés via `socketPath`. Accepte une chaîne ou un tableau de chaînes. Lorsqu'elle est définie, axios résout le `socketPath` et le compare à chaque entrée (également résolue) ; la requête est rejetée avec une `AxiosError` de code `ERR_BAD_OPTION_VALUE` s'il n'y a aucune correspondance. Lorsque non définie (par défaut), `socketPath` se comporte comme avant. ```js const client = axios.create({ allowedSocketPaths: ['/var/run/docker.sock'], }); // autorisé await client.get('http://localhost/v1.45/info', { socketPath: '/var/run/docker.sock' }); // rejeté — pas dans la liste await client.get('http://localhost/pods', { socketPath: '/var/run/kubelet.sock' }); ``` Un tableau vide (`allowedSocketPaths: []`) bloque tous les chemins de socket. ### `transport` La propriété `transport` définit le transport à utiliser pour la requête. Utile pour effectuer des requêtes via un protocole différent, comme `http2`. ### `httpAgent` et `httpsAgent` Les `httpAgent` et `httpsAgent` définissent un agent personnalisé à utiliser pour les requêtes http et https respectivement dans Node.js. Cela permet d'ajouter des options comme `keepAlive` qui ne sont pas activées par défaut. ### `proxy` Le `proxy` définit le nom d'hôte, le port et le protocole d'un serveur proxy que vous souhaitez utiliser. Vous pouvez également définir votre proxy en utilisant les variables d'environnement conventionnelles `http_proxy` et `https_proxy`. Si vous utilisez des variables d'environnement pour la configuration de votre proxy, vous pouvez également définir une variable d'environnement `no_proxy` sous la forme d'une liste de domaines séparés par des virgules qui ne doivent pas être mandatés. Dans les versions de Node.js avec prise en charge native des proxies d'environnement, axios délègue la gestion du proxy d'environnement à Node lorsque le `httpAgent` ou `httpsAgent` sélectionné a `proxyEnv` activé, y compris pour les processus démarrés avec `NODE_USE_ENV_PROXY=1`, `--use-env-proxy` ou `NODE_OPTIONS=--use-env-proxy`. Les agents personnalisés sans `proxyEnv` continuent d'utiliser la résolution de proxy d'environnement d'axios. La configuration explicite `proxy` reste gérée par axios. Utilisez `false` pour désactiver les proxies, en ignorant les variables d'environnement. `auth` indique que l'authentification HTTP Basic doit être utilisée pour se connecter au proxy, et fournit les identifiants. Cela définira un en-tête `Proxy-Authorization`, en écrasant tout en-tête `Proxy-Authorization` personnalisé que vous auriez défini via `headers`. Si le serveur proxy utilise HTTPS, vous devez définir le protocole à `https`. Un en-tête `Host` fourni par l'utilisateur dans `headers` est préservé lorsqu'il est transféré via un proxy (correspondance insensible à la casse sur `host` / `Host` / `HOST`). Cela vous permet de cibler un hôte virtuel différent de l'URL de la requête — par exemple, atteindre `127.0.0.1:4000` tout en faisant traiter la requête par le proxy comme provenant de `example.com`. Si aucun en-tête `Host` n'est fourni, axios utilise par défaut le `hostname:port` de l'URL de la requête comme auparavant. Pour les cibles `https://`, axios établit un tunnel CONNECT via le proxy et effectue TLS de bout en bout avec l'origine. `Proxy-Authorization` est envoyé uniquement sur la requête CONNECT, jamais sur la requête TLS encapsulée. Les options TLS de `httpsAgent`, comme `ca`, `cert`, `key` et `rejectUnauthorized`, sont transmises à l'agent de tunnel généré afin qu'elles continuent de s'appliquer à la connexion TLS avec l'origine. Si vous fournissez un `HttpsProxyAgent`, axios laisse cet agent gérer le tunnel. ```js proxy: { protocol: "https", host: "127.0.0.1", hostname: "localhost", // Prend le dessus sur "host" si les deux sont définis port: 9000, auth: { username: "mikeymike", password: "rapunz3l" } }, ``` ### `cancelToken` La propriété `cancelToken` vous permet de créer un token d'annulation pouvant être utilisé pour annuler la requête. Pour plus d'informations, consultez la documentation sur l'[annulation](/pages/advanced/cancellation). ### `signal` La propriété `signal` vous permet de passer une instance d'`AbortSignal` à la requête. Cela vous permet d'annuler la requête en utilisant l'API `AbortController`. ### `decompress` La propriété `decompress` indique si les données de la réponse doivent être automatiquement décompressées. La valeur par défaut est `true`. L'adaptateur HTTP Node.js prend en charge gzip, deflate, brotli et zstd lorsque le runtime Node.js actuel fournit le décompresseur zlib correspondant. ### `insecureHTTPParser` Indique s'il faut utiliser un analyseur HTTP non sécurisé qui accepte des en-têtes HTTP invalides. Cela peut permettre l'interopérabilité avec des implémentations HTTP non conformes. L'utilisation de l'analyseur non sécurisé doit être évitée. Notez que l'option `insecureHTTPParser` n'est disponible que dans Node.js 12.10.0 et ultérieur. Consultez la [documentation Node.js](https://nodejs.org/en/blog/vulnerability/february-2020-security-releases/#strict-http-header-parsing-none) pour plus d'informations. Voir l'ensemble complet des options [ici](https://nodejs.org/dist/latest-v12.x/docs/api/http.html#http_http_request_url_options_callback) ### `transitional` La propriété `transitional` vous permet d'activer ou de désactiver certaines fonctionnalités de transition. Les options suivantes sont disponibles : - `silentJSONParsing` : Si défini à `true` _(par défaut)_, axios ignore silencieusement les erreurs d'analyse JSON et définit `response.data` à `null` lorsque l'analyse échoue. Définissez à `false` pour lever une `SyntaxError` à la place. ::: tip Important Cette option ne prend effet que lorsque `responseType` est **explicitement** défini à `'json'`. Lorsque `responseType` est omis, axios utilise `forcedJSONParsing` pour tenter l'analyse JSON et retourne silencieusement la chaîne brute en cas d'échec, indépendamment de ce paramètre. Pour qu'un JSON invalide lève une erreur, définissez les deux : ```js { responseType: 'json', transitional: { silentJSONParsing: false } } ``` ::: - `forcedJSONParsing` : Force axios à analyser la chaîne de réponse comme du JSON même si `responseType` n'est pas `'json'`. - `clarifyTimeoutError` : Clarifie le message d'erreur lorsqu'une requête expire. Utile lors du débogage de problèmes de délai d'attente. - `validateStatusUndefinedResolves` : Si défini à `true` _(par défaut)_, un `validateStatus: undefined` explicite résout tous les statuts de réponse pour préserver la compatibilité. Définissez à `false` pour traiter `undefined` explicite comme si `validateStatus` était omis, afin qu'axios utilise le validateur configuré/par défaut. Utilisez `validateStatus: null` ou un validateur qui retourne `true` lorsque vous voulez intentionnellement résoudre tous les statuts. - `advertiseZstdAcceptEncoding` : Lorsqu'elle vaut `true`, axios ajoute `zstd` à l'en-tête `Accept-Encoding` par défaut lorsque le runtime Node.js actuel prend en charge la décompression zstd. Les réponses zstd sont tout de même décompressées automatiquement lorsqu'elles sont prises en charge et que `decompress` vaut `true`. - `legacyInterceptorReqResOrdering` : Lorsque défini à true, l'ordre hérité de traitement requête/réponse des intercepteurs sera utilisé. ### `env` La propriété `env` vous permet de définir certaines options de configuration. Par exemple, la classe FormData qui est utilisée pour sérialiser automatiquement le payload en objet FormData. - FormData: window?.FormData || global?.FormData ### `formSerializer` L'option `formSerializer` vous permet de configurer comment les objets simples sont sérialisés en `multipart/form-data` lorsqu'ils sont utilisés comme `data` de requête. Options disponibles : - `visitor` — fonction visiteur personnalisée appelée récursivement pour chaque valeur - `dots` — utiliser la notation pointée au lieu de la notation entre crochets - `metaTokens` — conserver les terminaisons spéciales de clé telles que `{}` - `indexes` — contrôler le format des crochets pour les clés de tableau (`null` / `false` / `true`) - `maxDepth` _(par défaut : `100`)_ — profondeur maximale d'imbrication avant de lever une `AxiosError` avec le code `ERR_FORM_DATA_DEPTH_EXCEEDED`. Définir à `Infinity` pour désactiver. - `Blob` — constructeur Blob utilisé lors de la conversion de valeurs de type ArrayBuffer pour un `FormData` conforme à la spécification. Consultez la page [multipart/form-data](/pages/advanced/multipart-form-data-format) pour tous les détails, et l'exemple de configuration complète en bas de cette page. ### `maxRate` La propriété `maxRate` définit la **bande passante** maximale (en octets par seconde) pour l'envoi et/ou le téléchargement. Elle accepte soit un nombre unique (appliqué dans les deux sens) soit un tableau de deux éléments `[uploadRate, downloadRate]` où chaque élément est une limite en octets par seconde. Par exemple, `100 * 1024` signifie 100 Ko/s. Consultez [Limitation de débit](/pages/advanced/rate-limiting) pour des exemples. ## Exemple de configuration complète ```js { url: "/posts", method: "get", baseURL: "https://jsonplaceholder.typicode.com", allowAbsoluteUrls: true, transformRequest: [function (data, headers) { return data; }], transformResponse: [function (data) { return data; }], headers: {"X-Requested-With": "XMLHttpRequest"}, params: { postId: 5 }, paramsSerializer: { // Fonction d'encodage personnalisée qui envoie les paires clé/valeur de façon itérative. encode?: (param: string): string => { /* Effectuez des opérations personnalisées ici et retournez la chaîne transformée */ }, // Fonction de sérialisation personnalisée pour l'ensemble du paramètre. Permet à l'utilisateur de reproduire le comportement antérieur à la v1.x. serialize?: (params: Record, options?: ParamsSerializerOptions ), // Configuration du format des index de tableaux dans les params. // Trois options disponibles : // (1) indexes: null (pas de crochets) // (2) (défaut) indexes: false (crochets vides) // (3) indexes: true (crochets avec index). indexes: false, // Profondeur maximale d'imbrication des objets lors de la sérialisation des params. Lève une AxiosError // (ERR_FORM_DATA_DEPTH_EXCEEDED) si dépassée. Par défaut : 100. Définir à Infinity pour désactiver. maxDepth: 100 }, data: { firstName: "Fred" }, // `data` est propre à chaque requête : axios ne l'hérite pas et ne le fusionne pas en profondeur depuis les valeurs par défaut. // Pour ajouter des champs de corps communs, utilisez un intercepteur de requête ou transformRequest. formDataHeaderPolicy: "legacy", // Syntaxe alternative pour envoyer des données dans le corps de la méthode post : seule la valeur est envoyée, pas la clé data: "Country=Brasil&City=Belo Horizonte", timeout: 1000, withCredentials: false, adapter: function (config) { // Faites ce que vous voulez }, adapter: "xhr", auth: { username: "janedoe", password: "s00pers3cret" }, responseType: "json", responseEncoding: "utf8", xsrfCookieName: "XSRF-TOKEN", xsrfHeaderName: "X-XSRF-TOKEN", withXSRFToken: boolean | undefined | ((config: InternalAxiosRequestConfig) => boolean | undefined), onUploadProgress: function ({loaded, total, progress, bytes, estimated, rate, upload = true}) { // Faites ce que vous voulez avec l'événement de progression Axios }, onDownloadProgress: function ({loaded, total, progress, bytes, estimated, rate, download = true}) { // Faites ce que vous voulez avec l'événement de progression Axios }, maxContentLength: 2000, maxBodyLength: 2000, redact: ['authorization', 'password'], validateStatus: function (status) { return status >= 200 && status < 300; }, maxRedirects: 21, sensitiveHeaders: ['X-API-Key'], beforeRedirect: (options, { headers }) => { if (options.hostname === "typicode.com") { options.auth = "user:password"; } }, socketPath: null, allowedSocketPaths: null, transport: undefined, httpAgent: new http.Agent({ keepAlive: true }), httpsAgent: new https.Agent({ keepAlive: true }), proxy: { protocol: "https", host: "127.0.0.1", // hostname: "127.0.0.1" // Prend le dessus sur "host" si les deux sont définis port: 9000, auth: { username: "mikeymike", password: "rapunz3l" } }, cancelToken: new CancelToken(function (cancel) { cancel("Operation has been canceled."); }), signal: new AbortController().signal, decompress: true, insecureHTTPParser: undefined, transitional: { silentJSONParsing: true, forcedJSONParsing: true, clarifyTimeoutError: false, validateStatusUndefinedResolves: true, advertiseZstdAcceptEncoding: false, legacyInterceptorReqResOrdering: true, }, env: { FormData: window?.FormData || global?.FormData }, formSerializer: { // Fonction visiteur personnalisée pour sérialiser les valeurs du formulaire visitor: (value, key, path, helpers) => {}; // Utiliser des points au lieu de crochets dots: boolean; // Conserver les terminaisons spéciales comme {} dans la clé de paramètre metaTokens: boolean; // Utiliser le format des index de tableau : // null - pas de crochets // false - crochets vides // true - crochets avec index indexes: boolean; // Profondeur maximale d'imbrication des objets. Lève une AxiosError (ERR_FORM_DATA_DEPTH_EXCEEDED) // si dépassée. Par défaut : 100. Définir à Infinity pour désactiver. maxDepth: 100; }, maxRate: [ 100 * 1024, // Limite d'envoi de 100Ko/s, 100 * 1024 // Limite de téléchargement de 100Ko/s ] } ``` axios-axios-aa54a5e/docs/fr/pages/advanced/request-method-aliases.md000066400000000000000000000166501526660636600256070ustar00rootroot00000000000000# Alias de requête axios fournit un ensemble d'alias pour effectuer des requêtes HTTP. Ces alias sont des raccourcis pour effectuer des requêtes via la méthode `request`. Ils sont conçus pour être faciles à utiliser et offrir une façon plus pratique d'effectuer des requêtes. axios s'efforce de suivre les RFC 7231 et RFC 5789 aussi fidèlement que possible. Les alias sont conçus pour être cohérents avec les méthodes HTTP définies dans ces RFC. ### `axios` axios peut être utilisé pour effectuer une requête HTTP en passant uniquement l'objet de configuration. L'objet de configuration complet est documenté [ici](/pages/advanced/request-config) ```ts axios( url: string | AxiosRequestConfig, config?: AxiosRequestConfig ): Promise; ``` Les méthodes de requête utilisent les paramètres génériques dans l'ordre `` : données de réponse, réponse personnalisée, données de requête et paramètres de requête. `P` est ajouté en dernier afin de préserver la signification des arguments explicites `T`, `R` et `D`. Sans `R` personnalisé, l'`AxiosResponse` par défaut conserve `D` et `P` dans `response.config`. ## Alias de méthode Les alias suivants sont disponibles pour effectuer des requêtes : ### `request` La méthode `request` est la méthode principale pour effectuer des requêtes HTTP. Elle accepte un objet de configuration en argument et retourne une promise qui se résout vers l'objet de réponse. C'est une méthode générique pouvant être utilisée pour tout type de requête HTTP. ```ts axios.request(config: AxiosRequestConfig): Promise; ``` ### `get` La méthode `get` est utilisée pour effectuer une requête GET. Elle accepte une URL et un objet de configuration optionnel en arguments et retourne une promise qui se résout vers l'objet de réponse. ```ts axios.get(url: string, config?: AxiosRequestConfig): Promise; ``` ### `delete` La méthode `delete` est utilisée pour effectuer une requête DELETE. Elle accepte une URL et un objet de configuration optionnel en arguments et retourne une promise qui se résout vers l'objet de réponse. ```ts axios.delete(url: string, config?: AxiosRequestConfig): Promise; ``` ### `head` La méthode `head` est utilisée pour effectuer une requête HEAD. Elle accepte une URL et un objet de configuration optionnel en arguments et retourne une promise qui se résout vers l'objet de réponse. ```ts axios.head(url: string, config?: AxiosRequestConfig): Promise; ``` ### `options` La méthode `options` est utilisée pour effectuer une requête OPTIONS. Elle accepte une URL et un objet de configuration optionnel en arguments et retourne une promise qui se résout vers l'objet de réponse. ```ts axios.options(url: string, config?: AxiosRequestConfig): Promise; ``` ### `post` La méthode `post` est utilisée pour effectuer une requête POST. Elle accepte une URL, un objet de données optionnel et un objet de configuration optionnel en arguments et retourne une promise qui se résout vers l'objet de réponse. ```ts axios.post(url: string, data?: D, config?: AxiosRequestConfig): Promise; ``` ### `put` La méthode `put` est utilisée pour effectuer une requête PUT. Elle accepte une URL, un objet de données optionnel et un objet de configuration optionnel en arguments et retourne une promise qui se résout vers l'objet de réponse. ```ts axios.put(url: string, data?: D, config?: AxiosRequestConfig): Promise; ``` ### `patch` La méthode `patch` est utilisée pour effectuer une requête PATCH. Elle accepte une URL, un objet de données optionnel et un objet de configuration optionnel en arguments et retourne une promise qui se résout vers l'objet de réponse. ```ts axios.patch(url: string, data?: D, config?: AxiosRequestConfig): Promise; ``` ### `query` La méthode `query` est utilisée pour effectuer une requête QUERY, une méthode sûre et idempotente qui transporte un corps. Elle accepte une URL, un objet de données optionnel et un objet de configuration optionnel en arguments et retourne une promise qui se résout vers l'objet de réponse. Utilisez-la pour des opérations de type lecture dont les paramètres sont trop complexes ou trop sensibles pour figurer dans l'URL. ```ts axios.query(url: string, data?: D, config?: AxiosRequestConfig): Promise; ``` ```js // Envoyer un filtre de recherche complexe dans le corps de la requête const { data } = await axios.query("/api/search", { selector: ["name", "email"], filter: { active: true, role: "admin" }, }); ``` ::: warning Spécification en cours d'élaboration La méthode QUERY est définie par un [Internet-Draft](https://datatracker.ietf.org/doc/draft-ietf-httpbis-safe-method-w-body/) de l'IETF et n'a pas encore été standardisée. La sémantique et le nom même de la méthode peuvent évoluer avant la publication finale, et la prise en charge par les serveurs, proxys et CDN est inégale. Vérifiez que votre pile accepte `QUERY` de bout en bout avant de vous en servir en production. ::: ### `getUri` La méthode `getUri` retourne l'URL qui serait envoyée pour une configuration donnée sans réellement effectuer la requête. Elle applique `baseURL`, `paramsSerializer` et `params`, vous obtenez donc la même chaîne qu'axios mettrait sur le réseau. Utile pour construire des liens, déboguer la sérialisation ou réutiliser l'URL résolue dans une autre requête. ```ts axios.getUri(config?: AxiosRequestConfig): string; ``` ```js const url = axios.getUri({ url: "/users", baseURL: "https://api.example.com", params: { active: true, role: "admin" }, }); // "https://api.example.com/users?active=true&role=admin" ``` ::: tip Utilisez `getUri` sur une instance (`instance.getUri(config)`) pour hériter des valeurs par défaut de l'instance pour `baseURL`, `params` et `paramsSerializer`. ::: ## Méthodes raccourcies pour les données de formulaire Ces méthodes sont équivalentes à leurs homologues ci-dessus, mais prédéfinissent le `Content-Type` à `multipart/form-data`. Elles constituent la façon recommandée d'envoyer des fichiers ou de soumettre des formulaires HTML. ### `postForm` ```ts axios.postForm(url: string, data?: D, config?: AxiosRequestConfig): Promise; ``` ```js // Envoyer un fichier depuis un input file du navigateur await axios.postForm("/api/upload", { file: document.querySelector("#fileInput").files[0], description: "Profile photo", }); ``` ### `putForm` ```ts axios.putForm(url: string, data?: D, config?: AxiosRequestConfig): Promise; ``` ```js // Remplacer une ressource avec des données de formulaire await axios.putForm("/api/users/1/avatar", { avatar: document.querySelector("#avatarInput").files[0], }); ``` ### `patchForm` ```ts axios.patchForm(url: string, data?: D, config?: AxiosRequestConfig): Promise; ``` ```js // Mettre à jour des champs spécifiques avec des données de formulaire await axios.patchForm("/api/users/1", { displayName: "New Name", avatar: document.querySelector("#avatarInput").files[0], }); ``` ::: tip `postForm`, `putForm` et `patchForm` acceptent les mêmes types de données que leurs méthodes de base — objets simples, `FormData`, `FileList` et `HTMLFormElement`. Consultez [Envoi de fichiers](/pages/advanced/file-posting) pour plus d'exemples. ::: axios-axios-aa54a5e/docs/fr/pages/advanced/response-schema.md000066400000000000000000000053741526660636600243170ustar00rootroot00000000000000# Schéma de réponse Chaque requête axios se résout vers un objet de réponse ayant la structure suivante. Le schéma est cohérent aussi bien dans les environnements navigateur que Node.js. ```js { // Les données de réponse fournies par le serveur. // Lors de l'utilisation de `transformResponse`, ce sera le résultat de la dernière transformation. data: {}, // Le code de statut HTTP de la réponse du serveur (ex. 200, 404, 500). status: 200, // Le message de statut HTTP correspondant au code de statut (ex. "OK", "Not Found"). statusText: "OK", // Les en-têtes de réponse envoyés par le serveur. // Les noms d'en-têtes sont en minuscules. Vous pouvez y accéder par notation crochet ou point. headers: {}, // La configuration axios utilisée pour cette requête, incluant baseURL, // headers, timeout, params, et toutes autres options que vous avez fournies. config: {}, // L'objet de requête sous-jacent. // Dans Node.js : la dernière instance de `http.ClientRequest` (après toute redirection). // Dans le navigateur : l'instance de `XMLHttpRequest`. request: {}, } ``` En TypeScript, `AxiosResponse` type les données de réponse (`T`), les données de requête (`D`), les en-têtes de réponse (`H`) et les paramètres de requête (`P`). La configuration conserve les deux types liés à la requête : ```ts interface AxiosResponse { data: T; status: number; statusText: string; headers: (H & RawAxiosResponseHeaders) | AxiosResponseHeaders; config: InternalAxiosRequestConfig; request?: any; } ``` ## Accéder aux champs de la réponse En pratique, vous déstructurerez généralement uniquement les parties dont vous avez besoin : ```js const { data, status, headers } = await axios.get("/api/users/1"); console.log(status); // 200 console.log(headers["content-type"]); // "application/json; charset=utf-8" console.log(data); // { id: 1, name: "Jay", email: "jay@example.com" } ``` ## Vérifier le code de statut axios résout la promise pour toute réponse 2xx et rejette pour tout ce qui est en dehors de cette plage par défaut. Vous pouvez personnaliser ce comportement avec l'option de configuration `validateStatus` : ```js const response = await axios.get("/api/resource", { validateStatus: (status) => status < 500, // résoudre pour tout statut inférieur à 500 }); ``` ## Accéder aux en-têtes de réponse Tous les noms d'en-têtes de réponse sont en minuscules, quelle que soit la façon dont le serveur les a envoyés : ```js const response = await axios.get("/api/resource"); // Ces deux lignes sont équivalentes const contentType = response.headers["content-type"]; const contentType2 = response.headers.get("content-type"); ``` axios-axios-aa54a5e/docs/fr/pages/advanced/retry.md000066400000000000000000000101031526660636600223520ustar00rootroot00000000000000# Nouvelles tentatives et récupération sur erreur Les requêtes réseau peuvent échouer pour des raisons transitoires — une défaillance momentanée du serveur, une brève interruption du réseau, ou une réponse de limitation de débit. Implémenter une stratégie de nouvelle tentative dans un intercepteur vous permet de gérer ces échecs de manière transparente, sans polluer votre code applicatif. ## Nouvelle tentative de base avec un intercepteur de réponse L'approche la plus simple consiste à intercepter des codes de statut d'erreur spécifiques et à renvoyer immédiatement la requête originale un nombre limité de fois : ```js import axios from "axios"; const api = axios.create({ baseURL: "https://api.example.com" }); const MAX_RETRIES = 3; api.interceptors.response.use( (response) => response, async (error) => { const config = error.config; // Ne réessayer que pour les erreurs réseau ou les erreurs serveur 5xx const shouldRetry = !error.response || (error.response.status >= 500 && error.response.status < 600); if (!shouldRetry) { return Promise.reject(error); } config._retryCount = config._retryCount ?? 0; if (config._retryCount >= MAX_RETRIES) { return Promise.reject(error); } config._retryCount += 1; return api(config); } ); ``` ## Délai exponentiel Réessayer immédiatement après un échec peut surcharger un serveur déjà en difficulté. Le délai exponentiel attend progressivement plus longtemps entre chaque tentative : ```js const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); api.interceptors.response.use( (response) => response, async (error) => { const config = error.config; const shouldRetry = !error.response || (error.response.status >= 500 && error.response.status < 600); if (!shouldRetry) return Promise.reject(error); config._retryCount = config._retryCount ?? 0; if (config._retryCount >= 3) return Promise.reject(error); config._retryCount += 1; // Attendre 200ms, 400ms, 800ms, ... avant chaque nouvelle tentative const backoff = 100 * 2 ** config._retryCount; await delay(backoff); return api(config); } ); ``` ## Nouvelle tentative sur 429 (limite de débit) avec Retry-After Lorsque le serveur répond avec `429 Too Many Requests`, il inclut souvent un en-tête `Retry-After` indiquant exactement combien de temps attendre : ```js api.interceptors.response.use( (response) => response, async (error) => { const config = error.config; if (error.response?.status !== 429) return Promise.reject(error); config._retryCount = config._retryCount ?? 0; if (config._retryCount >= 3) return Promise.reject(error); config._retryCount += 1; const retryAfterHeader = error.response.headers["retry-after"]; const waitMs = retryAfterHeader ? parseFloat(retryAfterHeader) * 1000 // l'en-tête est en secondes : 1000; // par défaut 1 seconde await new Promise((resolve) => setTimeout(resolve, waitMs)); return api(config); } ); ``` ## Désactiver les nouvelles tentatives pour une requête spécifique Si certaines requêtes ne doivent jamais être réessayées (ex. des mutations non idempotentes que vous ne voulez pas dupliquer), ajoutez un indicateur à la configuration de la requête : ```js // Ajoutez ceci dans votre intercepteur avant la logique de nouvelle tentative : if (config._noRetry) return Promise.reject(error); // Puis désactivez les nouvelles tentatives pour des appels spécifiques : await api.post("/payments/charge", body, { _noRetry: true }); ``` ## Combiner nouvelles tentatives et annulation Utilisez un `AbortController` pour annuler une requête qui attend un délai de backoff : ```js const controller = new AbortController(); try { await api.get("/api/data", { signal: controller.signal }); } catch (error) { if (axios.isCancel(error)) { console.log("Request aborted by user"); } } // Annuler la requête (et tout délai de nouvelle tentative en attente) depuis un autre endroit : controller.abort(); ``` axios-axios-aa54a5e/docs/fr/pages/advanced/testing.md000066400000000000000000000105351526660636600226730ustar00rootroot00000000000000# Tests Tester du code qui effectue des requêtes HTTP avec axios est simple. L'approche recommandée consiste à simuler (mocker) axios lui-même afin que vos tests s'exécutent sans toucher un vrai réseau, vous donnant un contrôle total sur les réponses que reçoit votre code. ## Simulation avec Vitest ou Jest Vitest et Jest supportent tous deux la simulation de modules avec `vi.mock` / `jest.mock`. Vous pouvez simuler l'ensemble du module axios et contrôler ce que chaque méthode retourne : ```js // user-service.js import axios from "axios"; export async function getUser(id) { const { data } = await axios.get(`/api/users/${id}`); return data; } ``` ```js // user-service.test.js import { describe, it, expect, vi } from "vitest"; import axios from "axios"; import { getUser } from "./user-service"; vi.mock("axios"); describe("getUser", () => { it("returns user data on success", async () => { const mockUser = { id: 1, name: "Jay" }; // Faire en sorte que axios.get se résolve avec notre fausse réponse axios.get.mockResolvedValueOnce({ data: mockUser }); const result = await getUser(1); expect(result).toEqual(mockUser); expect(axios.get).toHaveBeenCalledWith("/api/users/1"); }); it("throws when the request fails", async () => { axios.get.mockRejectedValueOnce(new Error("Network error")); await expect(getUser(1)).rejects.toThrow("Network error"); }); }); ``` ## Simuler une AxiosError Pour tester les chemins de gestion d'erreurs qui inspectent `error.response`, créez directement une instance d'`AxiosError` : ```js import axios, { AxiosError } from "axios"; import { vi } from "vitest"; const mockError = new AxiosError( "Not Found", "ERR_BAD_REQUEST", {}, // config {}, // request { // response status: 404, statusText: "Not Found", data: { message: "User not found" }, headers: {}, config: {}, } ); axios.get.mockRejectedValueOnce(mockError); ``` ## Utiliser axios-mock-adapter [axios-mock-adapter](https://github.com/ctimmerm/axios-mock-adapter) est une bibliothèque qui installe un adaptateur personnalisé sur votre instance axios, interceptant les requêtes au niveau de l'adaptateur. Cela signifie que vos intercepteurs continuent de s'exécuter, ce qui la rend plus adaptée aux tests d'intégration. ```bash npm install --save-dev axios-mock-adapter ``` ```js import axios from "axios"; import MockAdapter from "axios-mock-adapter"; const mock = new MockAdapter(axios); // Simuler une requête GET mock.onGet("/api/users/1").reply(200, { id: 1, name: "Jay" }); // Simuler une requête POST mock.onPost("/api/users").reply(201, { id: 2, name: "New User" }); // Simuler une erreur réseau mock.onGet("/api/failing").networkError(); // Simuler un délai d'attente dépassé mock.onGet("/api/slow").timeout(); ``` Réinitialisez les simulations entre les tests : ```js afterEach(() => { mock.reset(); // effacer tous les gestionnaires enregistrés }); ``` ## Tester les intercepteurs Pour tester les intercepteurs de manière isolée, créez une nouvelle instance axios dans votre test : ```js import axios from "axios"; import MockAdapter from "axios-mock-adapter"; describe("auth interceptor", () => { it("attaches a Bearer token to every request", async () => { const instance = axios.create(); const mock = new MockAdapter(instance); // Ajoutez votre intercepteur instance.interceptors.request.use((config) => { config.headers.set("Authorization", "Bearer test-token"); return config; }); // Capturez la configuration de la requête en inspectant ce que mock a reçu let capturedConfig; mock.onGet("/api/data").reply((config) => { capturedConfig = config; return [200, {}]; }); await instance.get("/api/data"); expect(capturedConfig.headers["Authorization"]).toBe("Bearer test-token"); }); }); ``` ## Conseils - Simulez toujours au niveau du module (ou utilisez `MockAdapter`) — évitez de simuler des méthodes individuelles sur une instance partagée, car l'état peut fuiter entre les tests. - Préférez `mockResolvedValueOnce` / `mockRejectedValueOnce` à `mockResolvedValue` pour que les tests soient isolés et ne s'affectent pas mutuellement. - Pour tester la logique de nouvelle tentative, utilisez `MockAdapter` afin que l'intercepteur testé s'exécute réellement à chaque tentative. axios-axios-aa54a5e/docs/fr/pages/advanced/type-script.md000066400000000000000000000144101526660636600234750ustar00rootroot00000000000000# TypeScript `axios` fournit des définitions de types TypeScript dans le package npm via `index.d.ts` (ESM) et `index.d.cts` (CJS), de sorte que la vérification de types et le support de l'éditeur fonctionnent immédiatement pour les deux formats de modules. ## Nuances de résolution de modules Comme axios publie à la fois avec un export par défaut ESM et un `module.exports` CJS, il existe quelques nuances de configuration à prendre en compte : - Le paramètre recommandé est `"moduleResolution": "node16"` (impliqué par `"module": "node16"`). Cela nécessite TypeScript 4.7 ou supérieur. - Si vous utilisez ESM, vos paramètres devraient convenir. - Si vous compilez TypeScript en CJS et ne pouvez pas utiliser `"moduleResolution": "node16"`, vous devez activer `esModuleInterop`. - Si vous utilisez TypeScript pour vérifier les types de code JavaScript CJS, votre seule option est `"moduleResolution": "node16"`. ## Type guards pour les erreurs axios Utilisez le type guard `axios.isAxiosError` pour affiner en toute sécurité les erreurs `unknown` dans les blocs `catch`. Après l'affinement, vous pouvez accéder aux propriétés spécifiques à axios telles que `error.response`, `error.config` et `error.code` avec une sécurité de type complète. ```ts import axios from "axios"; let user: User | null = null; try { const { data } = await axios.get("/user?ID=12345"); user = data.userDetails; } catch (error) { if (axios.isAxiosError(error)) { handleAxiosError(error); } else { handleUnexpectedError(error); } } ``` Utilisez `axios.isCancel()` pour affiner les erreurs d'annulation en `CanceledError` : ```ts const controller = new AbortController(); try { await axios.get("/user?ID=12345", { signal: controller.signal }); } catch (error) { if (axios.isCancel(error)) { handleCancellation(error); } } ``` ## Typage des données de requête et des paramètres de requête `AxiosRequestConfig` utilise `D` pour les données de requête et `P` pour les paramètres de requête. Un sérialiseur de paramètres personnalisé reçoit le même `P` : ```ts import axios, { type AxiosPromise, type AxiosRequestConfig, type InternalAxiosRequestConfig, } from "axios"; interface RequestBody { includeArchived: boolean; } interface SearchParams { query: string; page?: number; } interface SearchResponse { results: string[]; } const searchConfig: AxiosRequestConfig = { data: { includeArchived: false }, params: { query: "axios", page: 1 }, paramsSerializer: (params) => `${params.query}:${params.page ?? 1}`, }; const response = await axios.get("/search", searchConfig); response.config.data; // RequestBody | undefined response.config.params; // SearchParams | undefined const invalidConfig: AxiosRequestConfig = { // @ts-expect-error `query` doit être une chaîne params: { query: 123 }, }; ``` Les résultats de requête par défaut conservent `D` et `P` dans `response.config`, y compris lorsque les alias de requête infèrent ces types depuis une configuration typée. `RawAxiosRequestConfig`, `InternalAxiosRequestConfig`, `AxiosDefaults`, `CreateAxiosDefaults`, `AxiosResponse`, `AxiosPromise`, `AxiosError`, `CanceledError`, les instances appelables, les adaptateurs et `mergeConfig()` conservent également le type des paramètres. Les méthodes de requête ajoutent `P` comme dernier générique —``— afin que les positions existantes des données de réponse (`T`), de la réponse personnalisée (`R`) et des données de requête (`D`) restent inchangées. Un type de réponse personnalisé fourni explicitement continue de contrôler la valeur résolue. `P` vaut `any` par défaut pour préserver la compatibilité. Un adaptateur ou une autre promise explicitement typée peut conserver les deux types de requête : ```ts const searchAdapter = ( config: InternalAxiosRequestConfig ): AxiosPromise => Promise.resolve({ data: { results: [] }, status: 200, statusText: "OK", headers: {}, config, }); declare const error: unknown; if (axios.isCancel(error)) { error.config?.data; // RequestBody | undefined error.config?.params; // SearchParams | undefined } ``` ## Instances et intercepteurs typés Annotez le résultat de `axios.create` avec `AxiosInstance`, et annotez les intercepteurs de requête avec `InternalAxiosRequestConfig` pour obtenir une vérification de types de bout en bout sur un client personnalisé : ```ts import axios, { AxiosInstance, InternalAxiosRequestConfig } from "axios"; const apiClient: AxiosInstance = axios.create({ baseURL: "https://api.example.com", timeout: 10000, }); apiClient.interceptors.request.use((config: InternalAxiosRequestConfig) => { // Ajouter un token d'authentification, journaliser, etc. return config; }); ``` ## Configuration de requête personnalisée avec des clés symboles axios préserve les propriétés symboles propres et énumérables lorsqu'il fusionne les valeurs par défaut et la configuration de chaque requête. Une application peut augmenter le module `AxiosRequestConfig` avec une clé symbole précise, puis lire cette option depuis `InternalAxiosRequestConfig` dans un intercepteur ou un adaptateur : ```ts import axios from "axios"; export const someFlag: unique symbol = Symbol( "some flag used in request interceptor" ); declare module "axios" { interface AxiosRequestConfig { [someFlag]?: boolean; } } axios.interceptors.request.use((config) => { if (config[someFlag]) { config.headers.set("X-Some-Flag", "enabled"); } return config; }); await axios.get("/users", { [someFlag]: true }); ``` Seules les propriétés symboles propres et énumérables sont copiées. Les propriétés symboles héritées ou non énumérables ne le sont pas. ## Typage des données de réponse Les méthodes de requête axios sont génériques par rapport au type de données de réponse. Passez un paramètre de type à `axios.get` (et aux autres alias) pour typer `response.data` : ```ts interface User { id: number; name: string; } const { data } = await apiClient.get("/users/1"); // `data` est typé comme `User` ``` axios-axios-aa54a5e/docs/fr/pages/advanced/x-www-form-urlencoded-format.md000066400000000000000000000115751526660636600266650ustar00rootroot00000000000000# Format x-www-form-urlencoded ## URLSearchParams Par défaut, axios sérialise les objets JavaScript en `JSON`. Pour envoyer des données au format [`application/x-www-form-urlencoded`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) à la place, vous pouvez utiliser l'API [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams), [supportée](http://www.caniuse.com/#feat=urlsearchparams) par la grande majorité des navigateurs, et [Node.js](https://nodejs.org/api/url.html#url_class_urlsearchparams) depuis la version v10 (publiée en 2018). ```js const params = new URLSearchParams({ foo: 'bar' }); params.append('extraparam', 'value'); axios.post('/foo', params); ``` ## Chaîne de requête Pour les navigateurs plus anciens ou les environnements sans `URLSearchParams`, vous pouvez utiliser la bibliothèque [`qs`](https://github.com/ljharb/qs) pour sérialiser des objets au format `application/x-www-form-urlencoded`. ```js const qs = require('qs'); axios.post('/foo', qs.stringify({ bar: 123 })); ``` Pour un contrôle complet sur les en-têtes et la méthode, passez la sortie de `qs.stringify` comme `data` de la requête et définissez `Content-Type` explicitement : ```js import qs from 'qs'; const data = { bar: 123 }; const options = { method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, data: qs.stringify(data), url: '/foo', }; axios(options); ``` Dans les très anciennes versions de Node.js, vous pouvez utiliser le module natif `querystring` fourni avec Node.js. Notez que ce module a été déprécié dans Node.js v16 — préférez `URLSearchParams` ou `qs` pour le nouveau code. ```js const querystring = require('querystring'); axios.post('https://something.com/', querystring.stringify({ foo: 'bar' })); ``` ::: tip Préférez `qs` pour les objets imbriqués La bibliothèque `qs` est préférable si vous devez sérialiser des objets imbriqués, car la méthode `querystring` présente des [problèmes connus](https://github.com/nodejs/node-v0.x-archive/issues/1665) avec ce cas d'usage. ::: ## Sérialisation automatique vers URLSearchParams À partir de la version v0.21.0, axios sérialise automatiquement les objets JavaScript en `URLSearchParams` si l'en-tête `Content-Type` est défini à `application/x-www-form-urlencoded`. Cela signifie que vous pouvez passer directement un objet JavaScript à la propriété `data` de la configuration de requête axios. Par exemple lors de l'envoi de données vers une requête `POST` : ```js const data = { x: 1, arr: [1, 2, 3], arr2: [1, [2], 3], users: [ { name: 'Peter', surname: 'Griffin' }, { name: 'Thomas', surname: 'Anderson' }, ], }; await axios.postForm('https://postman-echo.com/post', data, { headers: { 'content-type': 'application/x-www-form-urlencoded' }, }); ``` L'objet `data` sera automatiquement sérialisé en `URLSearchParams` et envoyé au format `application/x-www-form-urlencoded`. Le serveur recevra les données suivantes : ```json { "x": "1", "arr[]": ["1", "2", "3"], "arr2[0]": "1", "arr2[1][0]": "2", "arr2[2]": "3", "users[0][name]": "Peter", "users[0][surname]": "Griffin", "users[1][name]": "Thomas", "users[1][surname]": "Anderson" } ``` Si le body-parser de votre backend (comme `body-parser` d'`express.js`) prend en charge le décodage des objets imbriqués, vous obtiendrez automatiquement le même objet côté serveur. ## Limite de profondeur pour la sérialisation des paramètres Lorsqu'axios sérialise un objet `params` via `AxiosURLSearchParams`, le même parcours récursif utilisé par le sérialiseur FormData est appelé. Une option `maxDepth` (par défaut `100`) limite la profondeur de récursion. Les charges utiles dépassant la limite lèvent une `AxiosError` avec `code: 'ERR_FORM_DATA_DEPTH_EXCEEDED'` au lieu de provoquer un débordement de pile d'appels. Le membre de type partagé `SerializerOptions.Blob` n'affecte que la sérialisation vers un `FormData` conforme à la spécification ; il n'a aucun effet lors de la sérialisation vers `URLSearchParams`. ```js // Augmenter la limite si votre objet params nécessite légitimement plus de 100 niveaux d'imbrication : axios.get('/api', { params: deepObject, paramsSerializer: { maxDepth: 200 } }); ``` ::: warning Note de sécurité N'augmentez `maxDepth` que si votre schéma le nécessite réellement. La valeur par défaut de 100 protège le code côté serveur qui transfère des données contrôlées par le client vers axios en tant que `params` contre les attaques DoS via des objets profondément imbriqués. ::: ```js var app = express(); app.use(bodyParser.urlencoded({ extended: true })); // support des corps encodés app.post('/', function (req, res, next) { // écho du corps en JSON res.send(JSON.stringify(req.body)); }); server = app.listen(3000); ``` axios-axios-aa54a5e/docs/fr/pages/getting-started/000077500000000000000000000000001526660636600222305ustar00rootroot00000000000000axios-axios-aa54a5e/docs/fr/pages/getting-started/examples/000077500000000000000000000000001526660636600240465ustar00rootroot00000000000000axios-axios-aa54a5e/docs/fr/pages/getting-started/examples/commonjs.md000066400000000000000000000105701526660636600262200ustar00rootroot00000000000000# Exemples JavaScript ## Importer la bibliothèque Pour importer la bibliothèque dans un environnement CommonJS, vous pouvez utiliser la fonction `require`, ou l'instruction `import` si vous utilisez un bundler comme Webpack ou Rollup. #### Sans bundler ```js const axios = require("axios"); ``` #### Avec bundler (webpack, rollup, vite, etc.) ```js import axios from "axios"; ``` ## Utiliser then/catch/finally Comme axios retourne une Promise en son coeur, vous pouvez choisir d'utiliser des callbacks avec `then`, `catch` et `finally` pour gérer vos données de réponse, les erreurs et la fin de la requête. ### Requête GET ```js axios .get("https://jsonplaceholder.typicode.com/posts", { params: { postId: 5, }, }) .then((response) => { console.log(response.data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Request completed"); }); ``` ### Requête POST ```js axios .post("https://jsonplaceholder.typicode.com/posts", { title: "foo", body: "bar", userId: 1, }) .then((response) => { console.log(response.data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Request completed"); }); ``` ### Requête PUT ```js axios .put("https://jsonplaceholder.typicode.com/posts/1", { title: "foo", body: "bar", userId: 1, }) .then((response) => { console.log(response.data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Request completed"); }); ``` ### Requête PATCH ```js axios .patch("https://jsonplaceholder.typicode.com/posts/1", { title: "foo", }) .then((response) => { console.log(response.data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Request completed"); }); ``` ### Requête DELETE ```js axios .delete("https://jsonplaceholder.typicode.com/posts/1") .then((response) => { console.log(response.data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Request completed"); }); ``` ## Utiliser async/await Une autre façon de gérer les promises est d'utiliser `async` et `await`. Cela vous permet d'utiliser des blocs try/catch/finally pour gérer les erreurs et la fin de la requête. Cette approche rend votre code plus lisible et plus facile à comprendre, et permet également d'éviter ce qu'on appelle le « callback hell ». ::: tip Remarque : async/await fait partie d'ECMAScript 2017 et n'est pas supporté par Internet Explorer et les navigateurs plus anciens. À utiliser avec précaution. ::: ### Requête GET ```js const getPosts = async () => { try { const response = await axios.get( "https://jsonplaceholder.typicode.com/posts", { params: { postId: 5, }, } ); console.log(response.data); } catch (error) { console.error(error); } finally { console.log("Request completed"); } }; ``` ### Requête POST ```js const createPost = async () => { try { const response = await axios.post( "https://jsonplaceholder.typicode.com/posts", { title: "foo", body: "bar", userId: 1, } ); console.log(response.data); } catch (error) { console.error(error); } finally { console.log("Request completed"); } }; ``` ### Requête PUT ```js const updatePost = async () => { try { const response = await axios.put( "https://jsonplaceholder.typicode.com/posts/1", { title: "foo", body: "bar", userId: 1, } ); console.log(response.data); } catch (error) { console.error(error); } finally { console.log("Request completed"); } }; ``` ### Requête PATCH ```js const updatePost = async () => { try { const response = await axios.patch( "https://jsonplaceholder.typicode.com/posts/1", { title: "foo", } ); console.log(response.data); } catch (error) { console.error(error); } finally { console.log("Request completed"); } }; ``` ### Requête DELETE ```js const deletePost = async () => { try { const response = await axios.delete( "https://jsonplaceholder.typicode.com/posts/1" ); console.log(response.data); } catch (error) { console.error(error); } finally { console.log("Request completed"); } }; ``` axios-axios-aa54a5e/docs/fr/pages/getting-started/examples/typescript.md000066400000000000000000000067751526660636600266150ustar00rootroot00000000000000# Exemple TypeScript ## Importer les types axios inclut des définitions TypeScript prêtes à l'emploi. Vous pouvez importer les types dont vous avez besoin directement depuis `"axios"` : ```ts import axios from "axios"; import type { AxiosRequestConfig, AxiosResponse, AxiosError } from "axios"; ``` ## Typer une requête Utilisez un paramètre de type générique sur la réponse pour indiquer à TypeScript la forme que prendront vos données : ```ts import axios from "axios"; type Post = { userId: number; id: number; title: string; body: string; }; const response = await axios.get("https://jsonplaceholder.typicode.com/posts/1"); console.log(response.data.title); // TypeScript sait que c'est une string ``` ## Typer une fonction Encapsulez les requêtes dans des fonctions avec des types de retour explicites pour une sécurité de type maximale : ```ts import axios, { AxiosResponse } from "axios"; type Post = { userId: number; id: number; title: string; body: string; }; const getPost = async (id: number): Promise => { const response = await axios.get( `https://jsonplaceholder.typicode.com/posts/${id}` ); return response.data; }; ``` ## Typer une requête POST Vous pouvez typer à la fois le corps de la requête et la réponse attendue : ```ts type CreatePostBody = { title: string; body: string; userId: number; }; type CreatePostResponse = CreatePostBody & { id: number }; const createPost = async (data: CreatePostBody): Promise => { const response = await axios.post( "https://jsonplaceholder.typicode.com/posts", data ); return response.data; }; ``` ## Instance axios typée Créez une instance typée afin d'y intégrer votre URL de base et vos en-têtes : ```ts import axios from "axios"; import type { AxiosInstance } from "axios"; const api: AxiosInstance = axios.create({ baseURL: "https://api.example.com", timeout: 5000, }); ``` ## Intercepteurs typés Utilisez `InternalAxiosRequestConfig` (et non `AxiosRequestConfig`) pour les intercepteurs de requête dans v1.x : ```ts import axios from "axios"; import type { InternalAxiosRequestConfig, AxiosResponse } from "axios"; api.interceptors.request.use((config: InternalAxiosRequestConfig) => { config.headers.set("Authorization", `Bearer ${getToken()}`); return config; }); api.interceptors.response.use( (response: AxiosResponse) => response, (error) => Promise.reject(error) ); ``` ## Typer les erreurs Utilisez `axios.isAxiosError()` pour affiner le type d'une erreur capturée : ```ts import axios, { AxiosError } from "axios"; type ApiError = { message: string; code: number; }; try { await axios.get("/api/protected-resource"); } catch (error) { if (axios.isAxiosError(error)) { // error.response?.data est typé comme ApiError console.error(error.response?.data.message); console.error(error.response?.status); } else { throw error; } } ``` ## Notes sur la configuration TypeScript Comme axios publie à la fois des versions ESM et CJS, il existe quelques nuances selon votre configuration : - Le paramètre recommandé est `"moduleResolution": "node16"` (impliqué par `"module": "node16"`). Cela nécessite TypeScript 4.7 ou supérieur. - Si vous compilez TypeScript vers CJS et ne pouvez pas utiliser `"moduleResolution": "node16"`, activez `"esModuleInterop": true`. - Si vous utilisez TypeScript pour vérifier les types de code JavaScript CJS, votre seule option est `"moduleResolution": "node16"`. axios-axios-aa54a5e/docs/fr/pages/getting-started/features.md000066400000000000000000000061311526660636600243710ustar00rootroot00000000000000# Fonctionnalités axios est un client HTTP puissant qui propose une API simple et facile à utiliser pour effectuer des requêtes HTTP. Il prend en charge tous les navigateurs modernes et est largement utilisé dans la communauté JavaScript. Voici quelques-unes des fonctionnalités qui font d'axios un excellent choix pour votre prochain projet. ## Isomorphique axios est un client HTTP universel qui peut être utilisé aussi bien dans le navigateur que dans Node.js. Cela signifie que vous pouvez utiliser axios pour effectuer des requêtes API depuis votre code frontend aussi bien que depuis votre code backend. Cela fait d'axios un excellent choix pour développer des applications web progressives, des applications monopages et des applications avec rendu côté serveur. axios est également un excellent choix pour les équipes qui travaillent à la fois sur le frontend et le backend. En utilisant axios pour les deux, vous disposez d'une API cohérente pour effectuer des requêtes HTTP, ce qui peut contribuer à réduire la complexité de votre code. ## Support Fetch axios offre une prise en charge de premier plan de l'API Fetch, qui est un remplacement moderne de l'API XHR. L'adaptateur est optionnel et peut être activé via la configuration. La même API est maintenue pour les adaptateurs XHR et Fetch, ce qui facilite l'adoption de l'API Fetch dans votre code sans modifier votre code existant. ## Support des navigateurs axios prend en charge tous les navigateurs modernes et certains navigateurs plus anciens, notamment Chrome, Firefox, Safari et Edge. axios est un excellent choix pour développer des applications web devant prendre en charge un large éventail de navigateurs. ## Support de Node.js axios supporte également un large éventail de versions de Node.js, avec une compatibilité testée jusqu'à la version v12.x, ce qui en fait un bon choix dans les environnements où la mise à jour vers la dernière version de Node.js n'est pas possible ou pratique. En plus de Node.js, axios dispose de tests de fumée pour Bun et Deno qui valident les comportements clés de l'exécution et renforcent la confiance dans la compatibilité multi-environnements. ## Fonctionnalités supplémentaires - Support de l'API Promise - Interception des requêtes et des réponses - Transformation des données de requête et de réponse - Abort controller - Délais d'attente (timeouts) - Sérialisation des paramètres de requête avec support des entrées imbriquées - Sérialisation automatique du corps de la requête vers : - JSON (application/json) - Multipart / FormData (multipart/form-data) - Formulaire encodé en URL (application/x-www-form-urlencoded) - Envoi de formulaires HTML en JSON - Gestion automatique des données JSON dans la réponse - Capture de la progression pour les navigateurs et Node.js avec des informations supplémentaires (vitesse de transfert, temps restant) - Limitation de la bande passante pour Node.js - Compatible avec les implémentations conformes de FormData et Blob (y compris Node.js) - Protection côté client contre les attaques XSRF axios-axios-aa54a5e/docs/fr/pages/getting-started/first-steps.md000066400000000000000000000105621526660636600250410ustar00rootroot00000000000000# Premiers pas Bienvenue dans la documentation d'axios ! Ce guide vous aidera à démarrer avec axios et à effectuer votre première requête API. Si vous débutez avec axios, nous vous recommandons de commencer ici. ## Installation Vous pouvez utiliser axios dans votre projet de plusieurs façons. La méthode la plus courante consiste à l'installer depuis npm et à l'inclure dans votre projet. Nous supportons également jsDelivr, unpkg, et d'autres options. #### Avec npm ```bash npm install axios ``` #### Avec pnpm ```bash pnpm install axios ``` #### Avec yarn ```bash yarn add axios ``` #### Avec bun ```bash bun add axios ``` #### Avec deno ```bash deno install npm:axios ``` #### Avec jsDelivr Lors de l'utilisation de jsDelivr, nous recommandons d'utiliser la version minifiée ainsi que d'épingler le numéro de version afin d'éviter des changements inattendus. Si vous souhaitez utiliser la dernière version, vous pouvez le faire en omettant le numéro de version. Ceci est fortement déconseillé en production car cela peut entraîner des modifications inattendues dans votre application. ```html ``` #### Avec unpkg Lors de l'utilisation d'unpkg, nous recommandons d'utiliser la version minifiée ainsi que d'épingler le numéro de version afin d'éviter des changements inattendus. Si vous souhaitez utiliser la dernière version, vous pouvez le faire en omettant le numéro de version. Ceci est fortement déconseillé en production car cela peut entraîner des modifications inattendues dans votre application. ```html ``` ## Importer axios Une fois installé, vous pouvez importer la bibliothèque en utilisant `import` ou `require` : ```js import axios, { isCancel, AxiosError } from "axios"; ``` Vous pouvez également utiliser l'export par défaut, puisque l'export nommé est juste une réexportation depuis la fabrique axios : ```js import axios from "axios"; console.log(axios.isCancel("something")); ``` Si vous utilisez `require` pour l'importation, **seul l'export par défaut est disponible** : ```js const axios = require("axios"); console.log(axios.isCancel("something")); ``` Pour certains bundlers et linters ES6, vous pourriez avoir besoin de : ```js import { default as axios } from "axios"; ``` Pour les environnements personnalisés ou hérités où la résolution de modules ne se comporte pas correctement, vous pouvez importer le bundle préconstruit directement : ```js const axios = require("axios/dist/browser/axios.cjs"); // bundle CommonJS navigateur (ES2017) // const axios = require("axios/dist/node/axios.cjs"); // bundle CommonJS Node (ES2017) ``` ## Votre première requête Une requête axios peut être effectuée en seulement deux lignes de code. Envoyer votre première requête avec axios est très simple. Vous pouvez interroger n'importe quelle API en fournissant l'URL et la méthode. Par exemple, pour effectuer une requête GET vers l'API JSONPlaceholder, vous pouvez utiliser le code suivant : ```js import axios from "axios"; const response = await axios.get( "https://jsonplaceholder.typicode.com/posts/1" ); console.log(response.data); ``` axios propose une API simple pour effectuer des requêtes. Vous pouvez utiliser la méthode `axios.get` pour une requête GET, la méthode `axios.post` pour une requête POST, et ainsi de suite. Vous pouvez également utiliser la méthode `axios.request` pour effectuer une requête avec n'importe quelle méthode HTTP. ::: tip Définissez un timeout en production Sans `timeout`, une requête bloquée peut rester en attente indéfiniment. Passez-en un via la configuration de requête : ```js const response = await axios.get("https://example.com/data", { timeout: 5000, // 5 secondes }); ``` Voir [`timeout` dans la configuration de requête](/pages/advanced/request-config#timeout) et [Gestion des erreurs](/pages/advanced/error-handling) pour les codes `ECONNABORTED` / `ETIMEDOUT` correspondants. ::: ## Prochaines étapes Maintenant que vous avez effectué votre première requête avec axios, vous êtes prêt à explorer le reste de la documentation. Vous pouvez en apprendre davantage sur l'envoi de requêtes, la gestion des réponses et l'utilisation d'axios dans vos projets. Consultez le reste de la documentation pour en savoir plus. axios-axios-aa54a5e/docs/fr/pages/getting-started/upgrade-guide.md000066400000000000000000000135151526660636600253010ustar00rootroot00000000000000# Guide de migration Ce guide a pour but de vous aider à migrer votre projet d'une version du framework à une autre. Il est recommandé de lire les notes de version de chaque version majeure que vous migrez, car elles peuvent contenir des informations importantes sur les changements incompatibles. ## Migration vers v1.19.0 ### Les URL HTTP(S) mal formées sont rejetées Les requêtes rejettent désormais une `url` ou une `baseURL` `http:` ou `https:` qui omet `//` après le protocole. Remplacez les valeurs telles que `https:example.com` ou `https:/example.com` par une URL bien formée comme `https://example.com`. L'`AxiosError` résultante utilise le code `ERR_INVALID_URL` et identifie l'URL concernée après masquage sécurisé des secrets. Ce changement de sécurité intentionnel empêche la normalisation des URL mal formées de contourner `baseURL` ou les listes d'URL autorisées. ### Erreurs des intercepteurs de requête synchrones Lorsqu'un intercepteur de requête synchrone lève une erreur, axios appelle son gestionnaire de rejet associé et arrête les intercepteurs de requête restants. Un gestionnaire de rejet qui retourne normalement traite l'erreur comme résolue et envoie la requête avec la dernière configuration valide ; sa valeur de retour ne devient pas la configuration. Si une validation doit empêcher l'envoi, omettez le gestionnaire de rejet ou faites-lui lever une erreur ou retourner une Promise rejetée. Les erreurs terminales continuent à travers les intercepteurs de rejet de réponse. ## Migration de v0.x vers v1.x ### Modification de l'instruction d'importation Dans v1.x, l'instruction d'importation a été modifiée pour utiliser l'export `default`. Vous devrez donc mettre à jour vos instructions d'importation en conséquence. ```diff - import { axios } from "axios"; + import axios from "axios"; ``` ### Modifications du système d'intercepteurs Dans v1.x, vous devez utiliser le type `InternalAxiosRequestConfig` pour typer le paramètre `config` dans l'intercepteur de `request`. En effet, le paramètre `config` est désormais typé comme `InternalAxiosRequestConfig` au lieu du type public `AxiosRequestConfig`. ```diff - axios.interceptors.request.use((config: AxiosRequestConfig) => { + axios.interceptors.request.use((config: InternalAxiosRequestConfig) => { return config; }); ``` ### Modifications de la structure des en-têtes de requête Dans v1.x, la structure des en-têtes de requête a été modifiée pour supprimer la propriété `common`. Vous devrez donc mettre à jour votre code pour utiliser la nouvelle structure des en-têtes de requête comme suit : ```diff - if (request.headers?.common?.Authorization) { - request.headers.common.Authorization = ... + if (request.headers?.Authorization) { + request.headers.Authorization = ... ``` Les en-têtes par défaut qui se trouvaient précédemment sous `common`, `get`, `post`, etc. sont désormais définis directement sur `axios.defaults.headers` : ```diff - axios.defaults.headers.common["Accept"] = "application/json"; + axios.defaults.headers["Accept"] = "application/json"; ``` ### Données multipart/form-data Si une requête inclut un payload `FormData`, l'en-tête `Content-Type: multipart/form-data` est désormais défini automatiquement. Supprimez tout en-tête manuel pour éviter les doublons : ```diff - axios.post("/upload", formData, { - headers: { "Content-Type": "multipart/form-data" }, - }); + axios.post("/upload", formData); ``` Si vous définissez explicitement `Content-Type: application/json`, axios sérialisera désormais automatiquement les données en JSON. ### Sérialisation des paramètres v1.x a introduit plusieurs changements incompatibles dans la façon dont les paramètres d'URL sont sérialisés. Les plus importants : **Les `params` sont désormais encodés en pourcentage par défaut.** Si votre backend attendait des crochets bruts issus de l'encodage de style qs, vous devrez peut-être configurer un sérialiseur personnalisé : ```js import qs from 'qs'; axios.create({ paramsSerializer: { serialize: (params) => qs.stringify(params, { arrayFormat: 'brackets' }), }, }); ``` **Les objets imbriqués dans `params` sont désormais sérialisés avec la notation entre crochets** (`foo[bar]=1`) plutôt que la notation pointée. Si votre backend attendait la notation pointée, utilisez un sérialiseur personnalisé. **Les paramètres `null` et `undefined`** sont désormais gérés de manière cohérente : les valeurs `null` sont sérialisées comme des chaînes vides, tandis que les valeurs `undefined` sont entièrement omises. Pour les options complètes de configuration de la sérialisation des paramètres, consultez la page [Configuration de requête](/pages/advanced/request-config). ### Les éléments internes ne sont plus exportés Nous avons décidé de ne plus exporter les éléments internes d'axios. Vous devrez donc mettre à jour votre code pour n'utiliser que l'API publique d'axios. Cette modification a été apportée pour simplifier l'API et réduire la surface exposée d'axios, nous permettant ainsi d'effectuer des modifications internes sans les déclarer comme des changements incompatibles. Veuillez consulter la [référence API](/pages/advanced/api-reference) sur ce site pour obtenir les dernières informations sur l'API publique d'axios. ### Configuration de requête Nous avons apporté des modifications à l'objet de configuration de requête. Veuillez consulter la [référence de configuration](/pages/advanced/request-config) sur ce site pour obtenir les dernières informations. ### Changements incompatibles non répertoriés Ce guide n'est pas exhaustif et peut ne pas couvrir tous les changements incompatibles. Si vous rencontrez un problème, veuillez ouvrir un ticket sur le [dépôt GitHub de la documentation](https://github.com/axios/docs) avec le label `breaking change`. axios-axios-aa54a5e/docs/fr/pages/misc/000077500000000000000000000000001526660636600200565ustar00rootroot00000000000000axios-axios-aa54a5e/docs/fr/pages/misc/security.md000066400000000000000000000311741526660636600222550ustar00rootroot00000000000000# Politique de sécurité ## ⚠� Bombe de décompression / mise en tampon de réponse sans limite Par défaut, `maxContentLength` et `maxBodyLength` valent `-1` (illimité). Un serveur malveillant ou compromis peut renvoyer un petit corps compressé en gzip/deflate/brotli/zstd qui s'étend à plusieurs gigaoctets, épuisant la mémoire du processus Node.js. **Si vous effectuez des requêtes vers des serveurs que vous ne contrôlez pas totalement, vous DEVEZ définir `maxContentLength` en fonction de votre charge.** La limite est appliquée chunk par chunk pendant la décompression en flux, il suffit donc de la définir pour neutraliser les attaques de bombe de décompression. ```js axios.get('https://example.com/data', { maxContentLength: 10 * 1024 * 1024, // 10 Mo maxBodyLength: 10 * 1024 * 1024, }); // Ou globalement : axios.defaults.maxContentLength = 10 * 1024 * 1024; axios.defaults.maxBodyLength = 10 * 1024 * 1024; ``` La valeur par défaut n'a pas été durcie car cela casserait silencieusement tout téléchargement légitime dépassant le plafond choisi. Le choix d'un plafond sûr pour des sources non fiables incombe à l'application. ## Autres options sensibles à la sécurité Les options de configuration de requête suivantes ont des implications directes en matière de sécurité. Elles sont documentées intégralement avec le reste de la [configuration de requête](/pages/advanced/request-config), et résumées ici pour qu'elles apparaissent en un seul endroit. | Option | Risque | Atténuation | | --- | --- | --- | | [`baseURL`](/pages/advanced/request-config#baseurl) | Certaines applications traitent un préfixe de chemin dans `baseURL`, comme `https://api.example.com/v1/`, comme une limite de requête. Des valeurs relatives de `url` contrôlées par l'utilisateur peuvent contenir des segments `..` que le parseur final d'URL normalise et résout en dehors de ce préfixe de chemin. | Ne vous appuyez pas sur `baseURL` pour isoler les chemins. Validez les chemins non fiables avant de les transmettre à axios ; rejetez les URLs absolues ou relatives au protocole et les segments `..`, ou vérifiez l'origine et le pathname de l'URL résolue avec une liste d'autorisation. | | [`socketPath`](/pages/advanced/request-config#socketpath) | Si dérivée d'une entrée non fiable, un attaquant peut rediriger le trafic vers des sockets locaux privilégiés tels que `/var/run/docker.sock`, contournant les protections SSRF basées sur le hostname (CWE-918). | Filtrez ou créez une liste blanche des clés de configuration provenant d'entrées non fiables. Utilisez [`allowedSocketPaths`](/pages/advanced/request-config#allowedsocketpaths) pour restreindre les chemins de socket acceptés. | | [`beforeRedirect`](/pages/advanced/request-config#beforeredirect) | S'exécute après que `follow-redirects` ait retiré les identifiants en cas de rétrogradation de protocole. Réinjecter des identifiants sans vérifier le protocole de destination peut les exposer en HTTP en clair. | Ne réinjectez les identifiants que pour des destinations HTTPS de confiance. Vérifiez `options.protocol === "https:"` avant d'assigner `auth`. | | [`sensitiveHeaders`](/pages/advanced/request-config#sensitiveheaders) | Des en-têtes secrets personnalisés comme `X-API-Key` peuvent être transmis par l'adaptateur HTTP Node.js lors d'une redirection vers une origine différente. | Listez ces noms d'en-têtes dans `sensitiveHeaders` ; axios retire les correspondances sans tenir compte de la casse lors des redirections cross-origin. Les redirections same-origin les conservent. | | [`withXSRFToken`](/pages/advanced/request-config#withxsrftoken) | La définir à `true` force l'en-tête XSRF sur les requêtes cross-origin. Les anciennes versions d'axios l'activaient implicitement avec `withCredentials: true` ; les versions plus récentes nécessitent les deux indicateurs. | Laissez à `undefined` (same-origin uniquement) sauf si votre backend valide explicitement XSRF sur les requêtes cross-origin. | | [`redact`](/pages/advanced/request-config#redact) | `AxiosError#toJSON()` inclut la configuration de requête par défaut, ce qui peut faire fuiter des en-têtes `Authorization` ou des identifiants `auth` dans les logs d'erreurs et la télémétrie. | Passez un tableau `redact` avec les noms de clés de configuration sensibles. La correspondance est insensible à la casse et récursive. | | [`formDataHeaderPolicy`](/pages/advanced/request-config#formdataheaderpolicy) | Un `FormData` personnalisé dont `getHeaders()` retourne des valeurs contrôlées par un attaquant peut écraser des en-têtes comme `Authorization` ou en injecter des arbitraires dans Node.js. | Définissez `'content-only'` pour ne copier que `Content-Type` et `Content-Length`, puis définissez les autres en-têtes explicitement via la configuration `headers` de la requête. | ## Durcissement de la chaîne d'approvisionnement : `ignore-scripts` et scripts de cycle de vie Le dépôt fournit un `.npmrc` au niveau du projet qui définit `ignore-scripts=true`. Cela bloque les scripts de cycle de vie npm (`preinstall`, `install`, `postinstall`, `prepare`) de toute dépendance directe ou transitive lors de l'exécution de `npm install` ou `npm ci` à l'intérieur du dépôt. Voir [THREATMODEL.md](https://github.com/axios/axios/blob/v1.x/THREATMODEL.md) (menace T-S2) pour la justification. Une conséquence : le hook `prepare` du dépôt lui-même (qui installe les git hooks de Husky) ne s'exécute **pas** automatiquement. Après votre première installation, activez les git hooks manuellement : ```bash npm ci npm rebuild husky && npx husky ``` Exécutez ces deux commandes une fois par checkout fraîchement effectué. Vous n'avez **pas** besoin de les ré-exécuter après chaque `npm install` suivant. ::: danger Ne supprimez pas `ignore-scripts=true` Supprimer `ignore-scripts=true` de `.npmrc` pour « réparer » l'installation de husky rouvre la surface d'attaque des scripts de cycle de vie pour tout autre paquet de l'arborescence. Tous les workflows CI invoquent déjà npm avec `--ignore-scripts`, donc le comportement local correspond au CI. ::: Nous recommandons le même paramètre `ignore-scripts=true` dans tout projet consommateur qui intègre axios (ou toute autre dépendance) dans un environnement de build manipulant des secrets. ## Vérifier une version publiée Chaque tarball `axios` publié sur npm provient de GitHub Actions et comporte une [attestation de provenance npm](https://docs.npmjs.com/generating-provenance-statements) qui lie cryptographiquement le paquet au workflow et au SHA de commit qui l'a produit. Les consommateurs peuvent vérifier la provenance localement : ```bash # Vérifier chaque paquet de votre lockfile, y compris axios npm audit signatures ``` Une vérification réussie prouve que le tarball a été construit dans l'environnement GitHub Actions de `axios/axios` à partir d'un commit connu — il n'a pas été altéré entre la construction et le registre. Elle ne prouve **pas** que le code de ce commit est exempt de bugs. Si `npm audit signatures` signale une attestation manquante ou invalide pour une version récente d'`axios`, traitez-le comme un incident potentiel de chaîne d'approvisionnement et signalez-le via le canal privé ci-dessous. ## Signaler une vulnérabilité Si vous pensez avoir trouvé une vulnérabilité de sécurité dans le projet, veuillez nous la signaler comme décrit ci-dessous. Nous prenons toutes les vulnérabilités de sécurité au sérieux. Si vous avez trouvé une vulnérabilité dans une bibliothèque tierce, veuillez la signaler aux mainteneurs de cette bibliothèque. ## Processus de signalement Veuillez ne pas signaler les vulnérabilités de sécurité via des issues GitHub publiques. Veuillez utiliser le canal de sécurité officiel sur GitHub en créant un [avis de sécurité](https://github.com/axios/axios/security/advisories/new). ## Politique de divulgation Lorsque nous recevons un rapport de vulnérabilité, nous lui assignons un responsable principal. Le responsable confirme le problème, détermine les versions affectées, évalue la gravité, développe et publie un correctif, et coordonne la divulgation publique avec le rapporteur. ### Engagement de résolution et de divulgation sous 60 jours Nous nous engageons à **résoudre et divulguer publiquement chaque avis de sécurité valide dans les 60 jours calendaires suivant le rapport initial**, à compter du moment où un rapport est reçu via le [canal des avis de sécurité GitHub](https://github.com/axios/axios/security/advisories/new). L'horloge des 60 jours est un engagement envers les rapporteurs et les consommateurs en aval — un filet de sécurité, pas un objectif. Si nous ne pouvons pas livrer un correctif à temps, nous publions tout de même l'avis au jour 60 avec les meilleures recommandations d'atténuation disponibles, afin que les consommateurs puissent agir. **Jalons dans la fenêtre de 60 jours :** | Jour | Jalon | | ---- | ----------------------------------------------------------------------------------------------------------------------------------------- | | 0 | Rapport reçu. Avis privé ouvert sur GitHub. | | ≤ 3 | Accusé de réception envoyé au rapporteur. Décision de triage : dans le périmètre / hors du périmètre / doublon / informations manquantes. | | ≤ 10 | Gravité évaluée (CVSS v4 le cas échéant). Versions affectées confirmées. CVE demandé via GitHub si un identifiant public est justifié. | | ≤ 45 | Correctif développé, revu, testé. Release candidate préparée sur une branche privée. Aperçu proposé au rapporteur pour validation. | | ≤ 60 | Version corrigée publiée sur npm. Avis public + CVE publiés. Rapporteur crédité sauf demande contraire. CHANGELOG mis à jour. | **Exceptions et prolongations.** - Si un rapporteur demande un embargo plus court (par exemple pour présenter ses résultats à une conférence), nous nous adaptons dans la mesure du possible. - Si un correctif nécessite un changement cassant, la coordination avec des consommateurs en aval majeurs, ou une publication en amont de `follow-redirects` / `form-data` / `proxy-from-env`, nous pouvons prolonger au-delà de 60 jours. Toute prolongation est divulguée publiquement au jour 60 via l'avis, avec une ETA révisée et la raison. - Si un rapport est **hors du périmètre** (par exemple, il relève d'un non-objectif explicite documenté dans le [modèle de menaces](https://github.com/axios/axios/blob/v1.x/THREATMODEL.md) du projet), nous le clôturons avec une explication au rapporteur dans la fenêtre de triage (≤ 3 jours). Les rapports hors périmètre n'entrent pas dans la file des 60 jours. - Les **vulnérabilités activement exploitées** sont traitées comme des incidents : le correctif et l'avis sont publiés dès qu'un patch est validé, hors du calendrier des 60 jours. **Attentes vis-à-vis du rapporteur.** Pendant qu'un rapport est sous embargo, nous demandons aux rapporteurs de s'abstenir de toute divulgation publique jusqu'à la plus proche de : (a) la publication coordonnée de l'avis, ou (b) le jour 60. Si l'échéance des 60 jours passe sans action de notre part, les rapporteurs sont libres de divulguer indépendamment — nous considérerons cela comme un échec de notre part, pas du leur. ## Mises à jour de sécurité Les mises à jour de sécurité sont publiées dès que possible après que le correctif a été développé et testé. Nous informons les utilisateurs via le dépôt GitHub du projet et publions les notes de version et les avis de sécurité sur la page des versions GitHub. Nous déprécions également toutes les versions contenant la vulnérabilité. ## Réponse à incident côté mainteneur Pour les scénarios de compromission affectant les comptes mainteneurs, les postes de travail ou l'infrastructure de publication (phishing, clé matérielle volée, tag ou publication inattendus), le projet maintient un runbook interne de réponse à incident dans [THREATMODEL.md §3.7](https://github.com/axios/axios/blob/v1.x/THREATMODEL.md#37-incident-response-runbook). Il couvre la révocation des sessions, la rotation des clés, la notification en aval et les procédures de dépublication/dépréciation. ## Partenaires de sécurité et remerciements Nous tenons à remercier les chercheurs en sécurité suivants pour leur collaboration afin de contribuer à la sécurité du projet pour tous : - [Socket Dev](https://socket.dev/) - [GitHub Security Lab](https://securitylab.github.com/) axios-axios-aa54a5e/docs/fr/pages/misc/semver.md000066400000000000000000000053721526660636600217100ustar00rootroot00000000000000# Versionnage sémantique Le versionnage sémantique est un schéma de versionnage utilisé pour communiquer la nature des changements dans un package logiciel. C'est un ensemble simple de règles et d'exigences qui dictent comment les numéros de version sont attribués et incrémentés. ## Versionnage d'axios axios suit le schéma de versionnage sémantique. Cela signifie que chaque version d'axios se voit attribuer un numéro de version composé de trois parties : majeure, mineure et correctif. Le numéro de version est incrémenté en fonction de la nature des changements apportés dans la version. Dans le passé, axios n'a pas toujours strictement suivi le versionnage sémantique, mais à l'avenir, une adhérence beaucoup plus stricte au schéma de versionnage sémantique sera maintenue pour garantir que les utilisateurs peuvent se fier aux numéros de version pour communiquer la nature des changements dans la bibliothèque. Un bref aperçu du schéma de versionnage est fourni ci-dessous. ## Format de version Un numéro de version sémantique est composé de trois parties : 1. Version majeure 2. Version mineure 3. Version de correctif Le numéro de version est écrit sous la forme `MAJEURE.MINEURE.CORRECTIF`. Chaque partie du numéro de version a une signification spécifique : - **Version majeure** : Incrémentée lorsque vous effectuez des changements d'API incompatibles. - **Version mineure** : Incrémentée lorsque vous ajoutez des fonctionnalités de manière rétrocompatible. - **Version de correctif** : Incrémentée lorsque vous effectuez des corrections de bugs rétrocompatibles. ## Versions de pré-publication En plus des trois parties du numéro de version, vous pouvez ajouter une version de pré-publication. Cela se fait en ajoutant un tiret et une série d'identifiants séparés par des points immédiatement après la version de correctif. Par exemple, `1.0.0-alpha.1`. Les versions de pré-publication sont utilisées pour indiquer qu'une version est instable et peut ne pas satisfaire les exigences de compatibilité prévues telles qu'indiquées par le numéro de version. Les versions de pré-publication sont ordonnées en fonction de l'ordre des identifiants. Par exemple, `1.0.0-alpha.1` vient avant `1.0.0-alpha.2`. ## Plages de versions Lorsque vous spécifiez une plage de versions pour un package, vous pouvez utiliser une variété d'opérateurs pour spécifier la plage de versions acceptables. Les opérateurs suivants sont disponibles : - `>` : Supérieur à - `<` : Inférieur à - `>=` : Supérieur ou égal à - `<=` : Inférieur ou égal à - `~` : Approximativement égal à - `^` : Compatible avec Par exemple, `^1.0.0` signifie que toute version supérieure ou égale à `1.0.0` et inférieure à `2.0.0` est acceptable. axios-axios-aa54a5e/docs/fr/pages/misc/sponsors.md000066400000000000000000000115651526660636600222760ustar00rootroot00000000000000--- layout: page search: false ---

Sponsors

Axios est soutenu par les organisations suivantes. Si vous souhaitez sponsoriser Axios, consultez notre page Open Collective pour plus d'informations.

{{ capitalizeFirstLetter(sponsor.tier) }}
{{ sponsor.name }}
axios-axios-aa54a5e/docs/index.md000066400000000000000000000235111526660636600170500ustar00rootroot00000000000000--- # https://vitepress.dev/reference/default-theme-home-page layout: home search: false hero: name: 'axios docs' text: 'axios is a simple HTTP client for the browser and Node.js' image: dark: /logo.svg light: /logo-light.svg alt: axios actions: - theme: brand text: Get started link: /pages/getting-started/first-steps - theme: alt text: API reference link: /pages/advanced/api-reference features: - title: Simple implementation details: Getting started with axios is as simple as a single line of code. Making simple API requests can be done in 2 lines of code. - title: Powerful interceptors details: Our innovative interceptor system allows you to control the request and response lifecycle. You can modify requests, responses, and errors. - title: TypeScript support details: axios declares types and has full support for TypeScript. This means you can use axios with confidence in your TypeScript projects. ---

Sponsors

{{ sponsor.name }}

{{ capitalizeFirstLetter(sponsor.tier) }}
axios-axios-aa54a5e/docs/package-lock.json000066400000000000000000003473311526660636600206440ustar00rootroot00000000000000{ "name": "axios", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "axios", "version": "1.0.0", "hasInstallScript": true, "license": "MIT", "dependencies": { "@splidejs/splide": "^4.1.4", "axios": "^1.15.2", "vitepress": "^1.6.4" }, "devDependencies": { "chalk": "^5.3.0", "patch-package": "^8.0.1", "vue": "^3.5.43" } }, "node_modules/@algolia/abtesting": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.16.1.tgz", "integrity": "sha512-Xxk4l00pYI+jE0PNw8y0MvsQWh5278WRtZQav8/BMMi3HKi2xmeuqe11WJ3y8/6nuBHdv39w76OpJb09TMfAVQ==", "license": "MIT", "dependencies": { "@algolia/client-common": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/autocomplete-core": { "version": "1.17.7", "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.17.7.tgz", "integrity": "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==", "license": "MIT", "dependencies": { "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", "@algolia/autocomplete-shared": "1.17.7" } }, "node_modules/@algolia/autocomplete-plugin-algolia-insights": { "version": "1.17.7", "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.17.7.tgz", "integrity": "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==", "license": "MIT", "dependencies": { "@algolia/autocomplete-shared": "1.17.7" }, "peerDependencies": { "search-insights": ">= 1 < 3" } }, "node_modules/@algolia/autocomplete-preset-algolia": { "version": "1.17.7", "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.17.7.tgz", "integrity": "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==", "license": "MIT", "dependencies": { "@algolia/autocomplete-shared": "1.17.7" }, "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "node_modules/@algolia/autocomplete-shared": { "version": "1.17.7", "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.17.7.tgz", "integrity": "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==", "license": "MIT", "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "node_modules/@algolia/client-abtesting": { "version": "5.50.1", "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.50.1.tgz", "integrity": "sha512-4peZlPXMwTOey9q1rQKMdCnwZb/E95/1e+7KujXpLLSh0FawJzg//U2NM+r4AiJy4+naT2MTBhj0K30yshnVTA==", "license": "MIT", "dependencies": { "@algolia/client-common": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-analytics": { "version": "5.50.1", "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.50.1.tgz", "integrity": "sha512-i+aWHHG8NZvGFHtPeMZkxL2Loc6Fm7iaRo15lYSMx8gFL+at9vgdWxhka7mD1fqxkrxXsQstUBCIsSY8FvkEOw==", "license": "MIT", "dependencies": { "@algolia/client-common": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-common": { "version": "5.50.1", "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.50.1.tgz", "integrity": "sha512-Hw52Fwapyk/7hMSV/fI4+s3H9MGZEUcRh4VphyXLAk2oLYdndVUkc6KBi0zwHSzwPAr+ZBwFPe2x6naUt9mZGw==", "license": "MIT", "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-insights": { "version": "5.50.1", "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.50.1.tgz", "integrity": "sha512-Bn/wtwhJ7p1OD/6pY+Zzn+zlu2N/SJnH46md/PAbvqIzmjVuwjNwD4y0vV5Ov8naeukXdd7UU9v550+v8+mtlg==", "license": "MIT", "dependencies": { "@algolia/client-common": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-personalization": { "version": "5.50.1", "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.50.1.tgz", "integrity": "sha512-0V4Tu0RWR8YxkgI9EPVOZHGE4K5pEIhkLNN0CTkP/rnPsqaaSQpNMYW3/mGWdiKOWbX0iVmwLB9QESk3H0jS5g==", "license": "MIT", "dependencies": { "@algolia/client-common": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-query-suggestions": { "version": "5.50.1", "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.50.1.tgz", "integrity": "sha512-jofcWNYMXJDDr87Z2eivlWY6o71Zn7F7aOvQCXSDAo9QTlyf7BhXEsZymLUvF0O1yU9Q9wvrjAWn8uVHYnAvgw==", "license": "MIT", "dependencies": { "@algolia/client-common": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-search": { "version": "5.50.1", "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.50.1.tgz", "integrity": "sha512-OteRb8WubcmEvU0YlMJwCXs3Q6xrdkb0v50/qZBJP1TF0CvujFZQM++9BjEkTER/Jr9wbPHvjSFKnbMta0b4dQ==", "license": "MIT", "dependencies": { "@algolia/client-common": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/ingestion": { "version": "1.50.1", "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.50.1.tgz", "integrity": "sha512-0GmfSgDQK6oiIVXnJvGxtNFOfosBspRTR7csCOYCTL1P8QtxX2vDCIKwTM7xdSAEbJaZ43QlWg25q0Qdsndz8Q==", "license": "MIT", "dependencies": { "@algolia/client-common": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/monitoring": { "version": "1.50.1", "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.50.1.tgz", "integrity": "sha512-ySuigKEe4YjYV3si8NVk9BHQpFj/1B+ON7DhhvTvbrZJseHQQloxzq0yHwKmznSdlO6C956fx4pcfOKkZClsyg==", "license": "MIT", "dependencies": { "@algolia/client-common": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/recommend": { "version": "5.50.1", "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.50.1.tgz", "integrity": "sha512-Cp8T/B0gVmjFlzzp6eP47hwKh5FGyeqQp1N48/ANDdvdiQkPqLyFHQVDwLBH0LddfIPQE+yqmZIgmKc82haF4A==", "license": "MIT", "dependencies": { "@algolia/client-common": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-browser-xhr": { "version": "5.50.1", "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.50.1.tgz", "integrity": "sha512-XKdGGLikfrlK66ZSXh/vWcXZZ8Vg3byDFbJD8pwEvN1FoBRGxhxya476IY2ohoTymLa4qB5LBRlIa+2TLHx3Uw==", "license": "MIT", "dependencies": { "@algolia/client-common": "5.50.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-fetch": { "version": "5.50.1", "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.50.1.tgz", "integrity": "sha512-mBAU6WyVsDwhHyGM+nodt1/oebHxgvuLlOAoMGbj/1i6LygDHZWDgL1t5JEs37x9Aywv7ZGhqbM1GsfZ54sU6g==", "license": "MIT", "dependencies": { "@algolia/client-common": "5.50.1" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-node-http": { "version": "5.50.1", "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.50.1.tgz", "integrity": "sha512-qmo1LXrNKLHvJE6mdQbLnsZAoZvj7VyF2ft4xmbSGWI2WWm87fx/CjUX4kEExt4y0a6T6nEts6ofpUfH5TEE1A==", "license": "MIT", "dependencies": { "@algolia/client-common": "5.50.1" }, "engines": { "node": ">= 14.0.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==", "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==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { "version": "7.29.9", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", "license": "MIT", "dependencies": { "@babel/types": "^7.29.8" }, "bin": { "parser": "bin/babel-parser.js" }, "engines": { "node": ">=6.0.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==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@docsearch/css": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.8.2.tgz", "integrity": "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==", "license": "MIT" }, "node_modules/@docsearch/js": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/@docsearch/js/-/js-3.8.2.tgz", "integrity": "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==", "license": "MIT", "dependencies": { "@docsearch/react": "3.8.2", "preact": "^10.0.0" } }, "node_modules/@docsearch/react": { "version": "3.8.2", "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.8.2.tgz", "integrity": "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==", "license": "MIT", "dependencies": { "@algolia/autocomplete-core": "1.17.7", "@algolia/autocomplete-preset-algolia": "1.17.7", "@docsearch/css": "3.8.2", "algoliasearch": "^5.14.2" }, "peerDependencies": { "@types/react": ">= 16.8.0 < 19.0.0", "react": ">= 16.8.0 < 19.0.0", "react-dom": ">= 16.8.0 < 19.0.0", "search-insights": ">= 1 < 3" }, "peerDependenciesMeta": { "@types/react": { "optional": true }, "react": { "optional": true }, "react-dom": { "optional": true }, "search-insights": { "optional": true } } }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", "cpu": [ "ppc64" ], "license": "MIT", "optional": true, "os": [ "aix" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/android-arm": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", "cpu": [ "arm" ], "license": "MIT", "optional": true, "os": [ "android" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "android" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/android-x64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "android" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", "cpu": [ "arm" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", "cpu": [ "ia32" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", "cpu": [ "loong64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", "cpu": [ "mips64el" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", "cpu": [ "ppc64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", "cpu": [ "riscv64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", "cpu": [ "s390x" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/netbsd-arm64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "netbsd" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "netbsd" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/openbsd-arm64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "openbsd" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "openbsd" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/openharmony-arm64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "openharmony" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "sunos" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", "cpu": [ "ia32" ], "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { "node": ">=18" } }, "node_modules/@iconify-json/simple-icons": { "version": "1.2.76", "resolved": "https://registry.npmjs.org/@iconify-json/simple-icons/-/simple-icons-1.2.76.tgz", "integrity": "sha512-lLRlA8yaf+1L5VCPRvR9lynoSklsddKHEylchmZJKdj/q2xVQ1ZAEJ8SCQlv9cbgtMefnlyM98U+8Si2aoFZPA==", "license": "CC0-1.0", "dependencies": { "@iconify/types": "*" } }, "node_modules/@iconify/types": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", "license": "MIT" }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", "cpu": [ "arm" ], "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-freebsd-arm64": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-freebsd-x64": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", "cpu": [ "arm" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", "cpu": [ "arm" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", "cpu": [ "loong64" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", "cpu": [ "loong64" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", "cpu": [ "ppc64" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", "cpu": [ "ppc64" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", "cpu": [ "riscv64" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", "cpu": [ "riscv64" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", "cpu": [ "s390x" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-openbsd-x64": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "openbsd" ] }, "node_modules/@rollup/rollup-openharmony-arm64": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "openharmony" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", "cpu": [ "ia32" ], "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@shikijs/core": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-2.5.0.tgz", "integrity": "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==", "license": "MIT", "dependencies": { "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.4" } }, "node_modules/@shikijs/engine-javascript": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-2.5.0.tgz", "integrity": "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==", "license": "MIT", "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^3.1.0" } }, "node_modules/@shikijs/engine-oniguruma": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-2.5.0.tgz", "integrity": "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==", "license": "MIT", "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "node_modules/@shikijs/langs": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-2.5.0.tgz", "integrity": "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==", "license": "MIT", "dependencies": { "@shikijs/types": "2.5.0" } }, "node_modules/@shikijs/themes": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-2.5.0.tgz", "integrity": "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==", "license": "MIT", "dependencies": { "@shikijs/types": "2.5.0" } }, "node_modules/@shikijs/transformers": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-2.5.0.tgz", "integrity": "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==", "license": "MIT", "dependencies": { "@shikijs/core": "2.5.0", "@shikijs/types": "2.5.0" } }, "node_modules/@shikijs/types": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-2.5.0.tgz", "integrity": "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==", "license": "MIT", "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "node_modules/@shikijs/vscode-textmate": { "version": "10.0.2", "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", "license": "MIT" }, "node_modules/@splidejs/splide": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/@splidejs/splide/-/splide-4.1.4.tgz", "integrity": "sha512-5I30evTJcAJQXt6vJ26g2xEkG+l1nXcpEw4xpKh0/FWQ8ozmAeTbtniVtVmz2sH1Es3vgfC4SS8B2X4o5JMptA==", "license": "MIT" }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "license": "MIT" }, "node_modules/@types/hast": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", "license": "MIT", "dependencies": { "@types/unist": "*" } }, "node_modules/@types/linkify-it": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", "license": "MIT" }, "node_modules/@types/markdown-it": { "version": "14.1.2", "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", "license": "MIT", "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", "license": "MIT", "dependencies": { "@types/unist": "*" } }, "node_modules/@types/mdurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", "license": "MIT" }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, "node_modules/@types/web-bluetooth": { "version": "0.0.21", "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz", "integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==", "license": "MIT" }, "node_modules/@ungap/structured-clone": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "license": "ISC" }, "node_modules/@vitejs/plugin-vue": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", "license": "MIT", "engines": { "node": "^18.0.0 || >=20.0.0" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0", "vue": "^3.2.25" } }, "node_modules/@vue/compiler-core": { "version": "3.5.43", "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.43.tgz", "integrity": "sha512-zdiLhnbe1QQqgDT8xZMpNmyqZ3qlI+/Q/FHQco57Kwl/b05HhCzN6eVGN9QU9rbga4CrS0H5SYY8VGHZCt/1Hg==", "license": "MIT", "dependencies": { "@babel/parser": "^7.29.8", "@vue/shared": "3.5.43", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-dom": { "version": "3.5.43", "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.43.tgz", "integrity": "sha512-PEZoAk3NQmsn/ejMzSOCyTYqwGqczrWm70PuhBKjjv1+TCoQAaO/zOqNwjV+honlNstT5ILxtc+8r8UUfj+iEQ==", "license": "MIT", "dependencies": { "@vue/compiler-core": "3.5.43", "@vue/shared": "3.5.43" } }, "node_modules/@vue/compiler-sfc": { "version": "3.5.43", "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.43.tgz", "integrity": "sha512-FCbrG3XNCRl+js3huuKx4IVHBLTvMkJhVepjbxSPu1gn4yWLaYtGNQdjJGZaMytXB6qb76qQDDmDSLy/vkmleQ==", "license": "MIT", "dependencies": { "@babel/parser": "^7.29.8", "@vue/compiler-core": "3.5.43", "@vue/compiler-dom": "3.5.43", "@vue/compiler-ssr": "3.5.43", "@vue/shared": "3.5.43", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.28", "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-ssr": { "version": "3.5.43", "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.43.tgz", "integrity": "sha512-GF62orf7KiJX9RqrHNGrYBudsQGD0OhJ5nDs90O8UiDuS40+YMYomiXu6w6EuvtXuRDc3MSNis3EaaSKAVSWpg==", "license": "MIT", "dependencies": { "@vue/compiler-dom": "3.5.43", "@vue/shared": "3.5.43" } }, "node_modules/@vue/devtools-api": { "version": "7.7.9", "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz", "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==", "license": "MIT", "dependencies": { "@vue/devtools-kit": "^7.7.9" } }, "node_modules/@vue/devtools-kit": { "version": "7.7.9", "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz", "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==", "license": "MIT", "dependencies": { "@vue/devtools-shared": "^7.7.9", "birpc": "^2.3.0", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^1.0.0", "speakingurl": "^14.0.1", "superjson": "^2.2.2" } }, "node_modules/@vue/devtools-shared": { "version": "7.7.9", "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz", "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==", "license": "MIT", "dependencies": { "rfdc": "^1.4.1" } }, "node_modules/@vue/reactivity": { "version": "3.5.43", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.43.tgz", "integrity": "sha512-G/c9GyOZNI2jVaaS6OX1EF1SSFSv7H0ERqNTl4+DTFMlZmB5eVAB53aLQNam/7NL2NPtaDD7RdVrzf8uJzMuOA==", "license": "MIT", "dependencies": { "@vue/shared": "3.5.43" } }, "node_modules/@vue/runtime-core": { "version": "3.5.43", "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.43.tgz", "integrity": "sha512-hU6U6VnVhBGQDpvlnnDlIB8ZGJBiOcgk2lh/0InltHiz3D8oSkluvuvY+do1G2H3+udeKFsmaBlgVYP7gXQzEw==", "license": "MIT", "dependencies": { "@vue/reactivity": "3.5.43", "@vue/shared": "3.5.43" } }, "node_modules/@vue/runtime-dom": { "version": "3.5.43", "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.43.tgz", "integrity": "sha512-Bb2Jc0YjjJdMt1SJmb9b2L/IWd3I8lIT9x9eS/xvvP9CiVgna0ffua74xKRmt4/uSJ+0r4iN8ex1jrqkhQGWQw==", "license": "MIT", "dependencies": { "@vue/reactivity": "3.5.43", "@vue/runtime-core": "3.5.43", "@vue/shared": "3.5.43", "csstype": "^3.2.3" } }, "node_modules/@vue/server-renderer": { "version": "3.5.43", "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.43.tgz", "integrity": "sha512-l2Ygjv9NehV94PSBxNWsAHC0j/eIIKbn92mBuWXAPGLnn6HfJ6MH5ubsd+Nk0YoZ5FRuxWI1P2VSoh+dbPQhCQ==", "license": "MIT", "dependencies": { "@vue/compiler-ssr": "3.5.43", "@vue/runtime-dom": "3.5.43", "@vue/shared": "3.5.43" } }, "node_modules/@vue/shared": { "version": "3.5.43", "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.43.tgz", "integrity": "sha512-uksS7YGMR5NZyr4JNq0Rp+QyLns0ueaz20KwzIPW9R0LH1Vnt4E+XUM29PNseEbf1www2gOuhuDi5AKOIXag9Q==", "license": "MIT" }, "node_modules/@vueuse/core": { "version": "12.8.2", "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-12.8.2.tgz", "integrity": "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==", "license": "MIT", "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "12.8.2", "@vueuse/shared": "12.8.2", "vue": "^3.5.13" }, "funding": { "url": "https://github.com/sponsors/antfu" } }, "node_modules/@vueuse/integrations": { "version": "12.8.2", "resolved": "https://registry.npmjs.org/@vueuse/integrations/-/integrations-12.8.2.tgz", "integrity": "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==", "license": "MIT", "dependencies": { "@vueuse/core": "12.8.2", "@vueuse/shared": "12.8.2", "vue": "^3.5.13" }, "funding": { "url": "https://github.com/sponsors/antfu" }, "peerDependencies": { "async-validator": "^4", "axios": "^1", "change-case": "^5", "drauu": "^0.4", "focus-trap": "^7", "fuse.js": "^7", "idb-keyval": "^6", "jwt-decode": "^4", "nprogress": "^0.2", "qrcode": "^1.5", "sortablejs": "^1", "universal-cookie": "^7" }, "peerDependenciesMeta": { "async-validator": { "optional": true }, "axios": { "optional": true }, "change-case": { "optional": true }, "drauu": { "optional": true }, "focus-trap": { "optional": true }, "fuse.js": { "optional": true }, "idb-keyval": { "optional": true }, "jwt-decode": { "optional": true }, "nprogress": { "optional": true }, "qrcode": { "optional": true }, "sortablejs": { "optional": true }, "universal-cookie": { "optional": true } } }, "node_modules/@vueuse/metadata": { "version": "12.8.2", "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-12.8.2.tgz", "integrity": "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/antfu" } }, "node_modules/@vueuse/shared": { "version": "12.8.2", "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-12.8.2.tgz", "integrity": "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==", "license": "MIT", "dependencies": { "vue": "^3.5.13" }, "funding": { "url": "https://github.com/sponsors/antfu" } }, "node_modules/@yarnpkg/lockfile": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", "dev": true, "license": "BSD-2-Clause" }, "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==", "license": "MIT", "dependencies": { "debug": "4" }, "engines": { "node": ">= 6.0.0" } }, "node_modules/algoliasearch": { "version": "5.50.1", "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.50.1.tgz", "integrity": "sha512-/bwdue1/8LWELn/DBalGRfuLsXBLXULJo/yOeavJtDu8rBwxIzC6/Rz9Jg19S21VkJvRuZO1k8CZXBMS73mYbA==", "license": "MIT", "dependencies": { "@algolia/abtesting": "1.16.1", "@algolia/client-abtesting": "5.50.1", "@algolia/client-analytics": "5.50.1", "@algolia/client-common": "5.50.1", "@algolia/client-insights": "5.50.1", "@algolia/client-personalization": "5.50.1", "@algolia/client-query-suggestions": "5.50.1", "@algolia/client-search": "5.50.1", "@algolia/ingestion": "1.50.1", "@algolia/monitoring": "1.50.1", "@algolia/recommend": "5.50.1", "@algolia/requester-browser-xhr": "5.50.1", "@algolia/requester-fetch": "5.50.1", "@algolia/requester-node-http": "5.50.1" }, "engines": { "node": ">= 14.0.0" } }, "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/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT" }, "node_modules/axios": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/axios/-/axios-1.20.0.tgz", "integrity": "sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==", "license": "MIT", "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.6", "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, "node_modules/birpc": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz", "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/antfu" } }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "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==", "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/ccount": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "node_modules/chalk": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "dev": true, "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/character-entities-html4": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "node_modules/character-entities-legacy": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/sibiraj-s" } ], "license": "MIT", "engines": { "node": ">=8" } }, "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/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "node_modules/copy-anything": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz", "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==", "license": "MIT", "dependencies": { "is-what": "^5.2.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/mesqueeb" } }, "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/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "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==", "license": "MIT", "engines": { "node": ">=0.4.0" } }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/devlop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", "license": "MIT", "dependencies": { "dequal": "^2.0.0" }, "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "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==", "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/emoji-regex-xs": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz", "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==", "license": "MIT" }, "node_modules/entities": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", "license": "BSD-2-Clause", "engines": { "node": ">=0.12" }, "funding": { "url": "https://github.com/fb55/entities?sponsor=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==", "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==", "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "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==", "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.25.12", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", "hasInstallScript": true, "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, "engines": { "node": ">=18" }, "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" } }, "node_modules/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "license": "MIT" }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/find-yarn-workspace-root": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", "dev": true, "license": "Apache-2.0", "dependencies": { "micromatch": "^4.0.2" } }, "node_modules/focus-trap": { "version": "7.8.0", "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.8.0.tgz", "integrity": "sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==", "license": "MIT", "dependencies": { "tabbable": "^6.4.0" } }, "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==", "funding": [ { "type": "individual", "url": "https://github.com/sponsors/RubenVerborgh" } ], "license": "MIT", "engines": { "node": ">=4.0" }, "peerDependenciesMeta": { "debug": { "optional": true } } }, "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==", "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": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, "engines": { "node": ">=12" } }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "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==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "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==", "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-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "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/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==", "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==", "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==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/hast-util-to-html": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, "node_modules/hast-util-whitespace": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, "node_modules/hookable": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", "license": "MIT" }, "node_modules/html-void-elements": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "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==", "license": "MIT", "dependencies": { "agent-base": "6", "debug": "4" }, "engines": { "node": ">= 6" } }, "node_modules/is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true, "license": "MIT", "bin": { "is-docker": "cli.js" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" } }, "node_modules/is-what": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz", "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==", "license": "MIT", "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/mesqueeb" } }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, "license": "MIT", "dependencies": { "is-docker": "^2.0.0" }, "engines": { "node": ">=8" } }, "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/json-stable-stringify": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.3.0.tgz", "integrity": "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg==", "dev": true, "license": "MIT", "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "isarray": "^2.0.5", "jsonify": "^0.0.1", "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/jsonfile": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", "dev": true, "license": "MIT", "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "node_modules/jsonify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", "dev": true, "license": "Public Domain", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/klaw-sync": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.1.11" } }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/mark.js": { "version": "8.11.1", "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", "integrity": "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==", "license": "MIT" }, "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==", "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/mdast-util-to-hast": { "version": "13.2.1", "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, "node_modules/micromark-util-character": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", "funding": [ { "type": "GitHub Sponsors", "url": "https://github.com/sponsors/unifiedjs" }, { "type": "OpenCollective", "url": "https://opencollective.com/unified" } ], "license": "MIT", "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "node_modules/micromark-util-encode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", "funding": [ { "type": "GitHub Sponsors", "url": "https://github.com/sponsors/unifiedjs" }, { "type": "OpenCollective", "url": "https://opencollective.com/unified" } ], "license": "MIT" }, "node_modules/micromark-util-sanitize-uri": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", "funding": [ { "type": "GitHub Sponsors", "url": "https://github.com/sponsors/unifiedjs" }, { "type": "OpenCollective", "url": "https://opencollective.com/unified" } ], "license": "MIT", "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "node_modules/micromark-util-symbol": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", "funding": [ { "type": "GitHub Sponsors", "url": "https://github.com/sponsors/unifiedjs" }, { "type": "OpenCollective", "url": "https://opencollective.com/unified" } ], "license": "MIT" }, "node_modules/micromark-util-types": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", "funding": [ { "type": "GitHub Sponsors", "url": "https://github.com/sponsors/unifiedjs" }, { "type": "OpenCollective", "url": "https://opencollective.com/unified" } ], "license": "MIT" }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { "node": ">=8.6" } }, "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==", "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==", "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" } }, "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/minisearch": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.2.0.tgz", "integrity": "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==", "license": "MIT" }, "node_modules/mitt": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "license": "MIT" }, "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==", "license": "MIT" }, "node_modules/nanoid": { "version": "3.3.19", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, "engines": { "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/oniguruma-to-es": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-3.1.1.tgz", "integrity": "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==", "license": "MIT", "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "node_modules/open": { "version": "7.4.2", "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", "dev": true, "license": "MIT", "dependencies": { "is-docker": "^2.0.0", "is-wsl": "^2.1.1" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/patch-package": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.1.tgz", "integrity": "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw==", "dev": true, "license": "MIT", "dependencies": { "@yarnpkg/lockfile": "^1.1.0", "chalk": "^4.1.2", "ci-info": "^3.7.0", "cross-spawn": "^7.0.3", "find-yarn-workspace-root": "^2.0.0", "fs-extra": "^10.0.0", "json-stable-stringify": "^1.0.2", "klaw-sync": "^6.0.0", "minimist": "^1.2.6", "open": "^7.4.2", "semver": "^7.5.3", "slash": "^2.0.0", "tmp": "^0.2.4", "yaml": "^2.2.2" }, "bin": { "patch-package": "index.js" }, "engines": { "node": ">=14", "npm": ">5" } }, "node_modules/patch-package/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/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/perfect-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", "license": "MIT" }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "license": "ISC" }, "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/postcss": { "version": "8.5.29", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz", "integrity": "sha512-49cGhUbXj8Qenv0iTMxA1cFBzxXoctpC9Ujd77t1WcbJIr6nF/eI7g/8MgxrYldFRuAXvja7xQRwavoW7kgrxQ==", "funding": [ { "type": "opencollective", "url": "https://opencollective.com/postcss/" }, { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" }, { "type": "github", "url": "https://github.com/sponsors/ai" } ], "license": "MIT", "dependencies": { "nanoid": "^3.3.19", "picocolors": "^1.1.1", "source-map-js": "^1.2.2" }, "engines": { "node": "^10 || ^12 || >=14" } }, "node_modules/preact": { "version": "10.29.1", "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.1.tgz", "integrity": "sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg==", "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" } }, "node_modules/property-information": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "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==", "license": "MIT", "engines": { "node": ">=10" } }, "node_modules/regex": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", "license": "MIT", "dependencies": { "regex-utilities": "^2.3.0" } }, "node_modules/regex-recursion": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", "license": "MIT", "dependencies": { "regex-utilities": "^2.3.0" } }, "node_modules/regex-utilities": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", "license": "MIT" }, "node_modules/rfdc": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "license": "MIT" }, "node_modules/rollup": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", "license": "MIT", "dependencies": { "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" }, "engines": { "node": ">=18.0.0", "npm": ">=8.0.0" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.1", "@rollup/rollup-android-arm64": "4.60.1", "@rollup/rollup-darwin-arm64": "4.60.1", "@rollup/rollup-darwin-x64": "4.60.1", "@rollup/rollup-freebsd-arm64": "4.60.1", "@rollup/rollup-freebsd-x64": "4.60.1", "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", "@rollup/rollup-linux-arm-musleabihf": "4.60.1", "@rollup/rollup-linux-arm64-gnu": "4.60.1", "@rollup/rollup-linux-arm64-musl": "4.60.1", "@rollup/rollup-linux-loong64-gnu": "4.60.1", "@rollup/rollup-linux-loong64-musl": "4.60.1", "@rollup/rollup-linux-ppc64-gnu": "4.60.1", "@rollup/rollup-linux-ppc64-musl": "4.60.1", "@rollup/rollup-linux-riscv64-gnu": "4.60.1", "@rollup/rollup-linux-riscv64-musl": "4.60.1", "@rollup/rollup-linux-s390x-gnu": "4.60.1", "@rollup/rollup-linux-x64-gnu": "4.60.1", "@rollup/rollup-linux-x64-musl": "4.60.1", "@rollup/rollup-openbsd-x64": "4.60.1", "@rollup/rollup-openharmony-arm64": "4.60.1", "@rollup/rollup-win32-arm64-msvc": "4.60.1", "@rollup/rollup-win32-ia32-msvc": "4.60.1", "@rollup/rollup-win32-x64-gnu": "4.60.1", "@rollup/rollup-win32-x64-msvc": "4.60.1", "fsevents": "~2.3.2" } }, "node_modules/search-insights": { "version": "2.17.3", "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.3.tgz", "integrity": "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==", "license": "MIT", "peer": true }, "node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "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/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/shiki": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/shiki/-/shiki-2.5.0.tgz", "integrity": "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==", "license": "MIT", "dependencies": { "@shikijs/core": "2.5.0", "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/langs": "2.5.0", "@shikijs/themes": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "node_modules/slash": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/source-map-js": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/space-separated-tokens": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "node_modules/speakingurl": { "version": "14.0.1", "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/stringify-entities": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", "license": "MIT", "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" }, "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "node_modules/superjson": { "version": "2.2.6", "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz", "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", "license": "MIT", "dependencies": { "copy-anything": "^4" }, "engines": { "node": ">=16" } }, "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/tabbable": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", "license": "MIT" }, "node_modules/tmp": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", "dev": true, "license": "MIT", "engines": { "node": ">=14.14" } }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, "engines": { "node": ">=8.0" } }, "node_modules/trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "node_modules/unist-util-is": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, "node_modules/unist-util-position": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, "node_modules/unist-util-stringify-position": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, "node_modules/unist-util-visit": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, "node_modules/unist-util-visit-parents": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, "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/vfile": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, "node_modules/vfile-message": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", "license": "MIT", "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/unified" } }, "node_modules/vite": { "version": "5.4.21", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "license": "MIT", "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" }, "engines": { "node": "^18.0.0 || >=20.0.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, "less": { "optional": true }, "lightningcss": { "optional": true }, "sass": { "optional": true }, "sass-embedded": { "optional": true }, "stylus": { "optional": true }, "sugarss": { "optional": true }, "terser": { "optional": true } } }, "node_modules/vitepress": { "version": "1.6.4", "resolved": "https://registry.npmjs.org/vitepress/-/vitepress-1.6.4.tgz", "integrity": "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==", "license": "MIT", "dependencies": { "@docsearch/css": "3.8.2", "@docsearch/js": "3.8.2", "@iconify-json/simple-icons": "^1.2.21", "@shikijs/core": "^2.1.0", "@shikijs/transformers": "^2.1.0", "@shikijs/types": "^2.1.0", "@types/markdown-it": "^14.1.2", "@vitejs/plugin-vue": "^5.2.1", "@vue/devtools-api": "^7.7.0", "@vue/shared": "^3.5.13", "@vueuse/core": "^12.4.0", "@vueuse/integrations": "^12.4.0", "focus-trap": "^7.6.4", "mark.js": "8.11.1", "minisearch": "^7.1.1", "shiki": "^2.1.0", "vite": "^5.4.14", "vue": "^3.5.13" }, "bin": { "vitepress": "bin/vitepress.js" }, "peerDependencies": { "markdown-it-mathjax3": "^4", "postcss": "^8" }, "peerDependenciesMeta": { "markdown-it-mathjax3": { "optional": true }, "postcss": { "optional": true } } }, "node_modules/vue": { "version": "3.5.43", "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.43.tgz", "integrity": "sha512-o5qZoksdnjIKvW1srZ3ab7pcDNYAerBjRe54D0LBLfRdCYFrSgBHVXokMas35czQc0//lmx4/tuY4ZNQ+Rf2Ng==", "license": "MIT", "dependencies": { "@vue/compiler-dom": "3.5.43", "@vue/compiler-sfc": "3.5.43", "@vue/runtime-dom": "3.5.43", "@vue/server-renderer": "3.5.43", "@vue/shared": "3.5.43" }, "peerDependencies": { "typescript": "*" }, "peerDependenciesMeta": { "typescript": { "optional": true } } }, "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/yaml": { "version": "2.8.3", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", "dev": true, "license": "ISC", "bin": { "yaml": "bin.mjs" }, "engines": { "node": ">= 14.6" }, "funding": { "url": "https://github.com/sponsors/eemeli" } }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } } } } axios-axios-aa54a5e/docs/package.json000066400000000000000000000015101526660636600177000ustar00rootroot00000000000000{ "name": "axios", "version": "1.0.0", "description": "Documentation for the Axios HTTP client", "license": "MIT", "type": "module", "private": true, "scripts": { "docs:dev": "vitepress dev", "docs:build": "npm run test:search && vitepress build", "docs:preview": "vitepress preview", "docs:update:sponsors": "node ./scripts/process-sponsors.js", "prod:build": "npm run docs:update:sponsors && npm run docs:build", "test:search": "node --test .vitepress/tokenizeSearchText.test.js", "postinstall": "patch-package --exclude nothing" }, "dependencies": { "@splidejs/splide": "^4.1.4", "axios": "^1.15.2", "vitepress": "^1.6.4" }, "devDependencies": { "chalk": "^5.3.0", "patch-package": "^8.0.1", "vue": "^3.5.43" }, "overrides": { "esbuild": "^0.25.12" } } axios-axios-aa54a5e/docs/pages/000077500000000000000000000000001526660636600165145ustar00rootroot00000000000000axios-axios-aa54a5e/docs/pages/advanced/000077500000000000000000000000001526660636600202615ustar00rootroot00000000000000axios-axios-aa54a5e/docs/pages/advanced/adapters.md000066400000000000000000000072351526660636600224150ustar00rootroot00000000000000# Adapters Adapters allow you to customize the way axios handles the request data. By default, axios uses an ordered priority list of `['xhr', 'http', 'fetch']` and selects the first adapter that is supported by the current environment. In practice this means `xhr` is used in browsers, `http` in Node.js, and `fetch` in environments where neither is available (such as Cloudflare Workers or Deno). Writing your own adapter lets you fully control how axios makes a request and processes the response — useful for testing, custom transports, or non-standard environments. ## Built-in adapters You can select a built-in adapter by name using the `adapter` config option: ```js // Use the fetch adapter const instance = axios.create({ adapter: "fetch" }); // Use the XHR adapter (browser default) const instance = axios.create({ adapter: "xhr" }); // Use the HTTP adapter (Node.js default) const instance = axios.create({ adapter: "http" }); ``` You can also pass an array of adapter names. axios will use the first one supported by the current environment: ```js const instance = axios.create({ adapter: ["fetch", "xhr", "http"] }); ``` For more details on the `fetch` adapter, see the [Fetch adapter](/pages/advanced/fetch-adapter) page. ## Creating a custom adapter To create a custom adapter, write a function that accepts a `config` object and returns a Promise that resolves to a valid axios response object. ```js import axios from "axios"; import { settle } from "axios/unsafe/core/settle.js"; function myAdapter(config) { /** * At this point: * - config has been merged with defaults * - request transformers have run * - request interceptors have run * * The adapter is now responsible for making the request * and returning a valid response object. */ return new Promise((resolve, reject) => { // Perform your custom request logic here. // This example uses the native fetch API as a starting point. fetch(config.url, { method: config.method?.toUpperCase() ?? "GET", headers: config.headers?.toJSON() ?? {}, body: config.data, signal: config.signal, }) .then(async (fetchResponse) => { const responseData = await fetchResponse.text(); const response = { data: responseData, status: fetchResponse.status, statusText: fetchResponse.statusText, headers: Object.fromEntries(fetchResponse.headers.entries()), config, request: null, }; // settle resolves or rejects the promise based on the HTTP status settle(resolve, reject, response); /** * After this point: * - response transformers will run * - response interceptors will run */ }) .catch(reject); }); } const instance = axios.create({ adapter: myAdapter }); ``` TypeScript adapters can preserve both request data and query params on the response config by using the matching generics: ```ts import type { AxiosPromise, InternalAxiosRequestConfig, } from "axios"; interface RequestBody { includeArchived: boolean; } interface SearchParams { query: string; } interface SearchResponse { results: string[]; } const searchAdapter = ( config: InternalAxiosRequestConfig ): AxiosPromise => Promise.resolve({ data: { results: [] }, status: 200, statusText: "OK", headers: {}, config, }); ``` ::: tip The `settle` helper resolves the promise for 2xx status codes and rejects it for everything else, matching axios's default behaviour. If you want custom status validation, use the `validateStatus` config option instead. ::: axios-axios-aa54a5e/docs/pages/advanced/api-reference.md000066400000000000000000000321711526660636600233140ustar00rootroot00000000000000# API reference Below is a list of all the available functions and classes in the axios package. These functions may be used and imported in your project. All of these functions and classes are protected by our renewed promise to follow semantic versioning. This means that you can rely on these functions and classes to remain stable and unchanged in future releases unless a major version change is made. ## Instance The `axios` instance is the main object that you will use to make HTTP requests. It is a factory function that creates a new instance of the `Axios` class. The `axios` instance has a number of methods that you can use to make HTTP requests. These methods are documented in the [Request aliases section](/pages/advanced/request-method-aliases) of the documentation. ## TypeScript request types The public request types use separate generics for request data and query params: ```ts AxiosRequestConfig RawAxiosRequestConfig InternalAxiosRequestConfig AxiosDefaults CreateAxiosDefaults AxiosResponse AxiosPromise AxiosError CanceledError ``` `D` is the request body type and `P` is the query params type. `AxiosResponse`, `AxiosPromise`, errors, defaults, callable instances, request aliases, adapters, and `mergeConfig()` preserve both on the request config. Custom params serializers receive the same `P`. Request methods use the generic order ``, with `P` added last so existing explicit generic arguments remain compatible. When no custom response type `R` is supplied, the resolved `AxiosResponse` keeps `D` and `P` on `response.config`; an explicitly supplied `R` continues to control the resolved value. All request-data and params generics default to `any` for backward compatibility. ## Classes ### `Axios` The `Axios` class is the main class that you will use to make HTTP requests. It is a factory function that creates a new instance of the `Axios` class. The `Axios` class has a number of methods that you can use to make HTTP requests. These methods are documented in the [Request aliases section](/pages/advanced/request-method-aliases) of the documentation. #### `constructor` Creates a new instance of the `Axios` class. The constructor takes an optional configuration object as an argument. ```ts constructor(instanceConfig?: AxiosRequestConfig); ``` #### `request` Handles request invocation and response resolution. This is the main method that you will use to make HTTP requests. It takes a configuration object as an argument and returns a promise that resolves to the response object. ```ts request(config: AxiosRequestConfig): Promise; ``` ### `CancelToken` The `CancelToken` class was based on the `tc39/proposal-cancelable-promises` proposal. It was used to create a token that could be used to cancel an HTTP request. The `CancelToken` class is now deprecated in favour of the `AbortController` API. As of version 0.22.0, the `CancelToken` class is deprecated and will be removed in a future release. It is recommended that you use the `AbortController` API instead. The class is exported mainly for backwards compatibility and will be removed in a future release. We strongly discourage its use in new projects; the legacy interop helpers below are listed only for existing code. The legacy methods remain typed for existing integrations: ```ts subscribe(listener: (cancel: Cancel | any) => void): void; unsubscribe(listener: (cancel: Cancel | any) => void): void; toAbortSignal(): AbortSignal; ``` ## Functions ### `AxiosError` The `AxiosError` class is an error class that is thrown when an HTTP request fails. It extends the `Error` class and adds additional properties to the error object. #### `constructor` Creates a new instance of the `AxiosError` class. The constructor takes an optional message, code, config, request, and response as arguments. ```ts constructor(message?: string, code?: string, config?: InternalAxiosRequestConfig, request?: any, response?: AxiosResponse); ``` #### `properties` The `AxiosError` class provides the following properties: ```ts // Config instance. config?: InternalAxiosRequestConfig; // Error code. code?: string; // Request instance. request?: any; // Response instance. response?: AxiosResponse; // Boolean indicating if the error is an `AxiosError`. isAxiosError: boolean; // Error status code. status?: number; // Helper method to convert the error to a JSON object. toJSON: () => object; // Error cause. cause?: Error; ``` ### `AxiosHeaders` The `AxiosHeaders` class is a utility class that is used to manage HTTP headers. It provides methods for manipulating headers, such as adding, removing, and getting headers. Only the main methods are documented here. For a full list of methods, please refer to the type declaration file. #### `constructor` Creates a new instance of the `AxiosHeaders` class. The constructor takes an optional headers object as an argument. ```ts constructor(headers?: RawAxiosHeaders | AxiosHeaders | string); ``` #### `set` Adds a header to the headers object. Empty or whitespace-only header names are ignored. ```ts set(headerName?: string, value?: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders; set(headers?: RawAxiosHeaders | AxiosHeaders | string, rewrite?: boolean): AxiosHeaders; set(headers?: Iterable<[string, AxiosHeaderValue]>, rewrite?: boolean): AxiosHeaders; ``` #### `get` Gets a header from the headers object. ```ts get(headerName: string, parser: typeof AxiosHeaders.parseParameters): AxiosHeaderParameters; get(headerName: string, parser: RegExp): RegExpExecArray | null; get(headerName: string, matcher?: true | AxiosHeaderParser): AxiosHeaderValue; ``` Pass `AxiosHeaders.parseParameters` to parse normalized HTTP parameters into a hardened null-prototype map: ```js const headers = new AxiosHeaders({ "Content-Type": 'multipart/form-data; boundary="a,b"', }); console.log({ ...headers.get("Content-Type", AxiosHeaders.parseParameters), }); // { boundary: "a,b" } ``` Parameter names are case-insensitive. The parser removes quoted-string delimiters, decodes escaped quotes and backslashes, preserves commas and semicolons inside quoted values, and trims only RFC optional whitespace around unquoted values. It omits `__proto__`, `constructor`, and `prototype`. `get(name, true)` remains the legacy tokenizer. #### `has` Checks if a header exists in the headers object. ```ts has(header: string, matcher?: AxiosHeaderMatcher): boolean; ``` #### `delete` Removes a header from the headers object. ```ts delete(header: string | string[], matcher?: AxiosHeaderMatcher): boolean; ``` #### `clear` Removes all headers from the headers object. ```ts clear(matcher?: AxiosHeaderMatcher): boolean; ``` #### `normalize` Normalizes the headers object. ```ts normalize(format: boolean): AxiosHeaders; ``` #### `concat` Concatenates headers objects. ```ts concat(...targets: Array): AxiosHeaders; ``` #### `toJSON` Converts the headers object to a JSON object. ```ts toJSON(asStrings: true): Record; toJSON(asStrings?: false): Record; ``` #### `toString` Returns the headers as a CRLF-free HTTP header block, one `name: value` pair per line. ```ts toString(): string; ``` ### `CanceledError` The `CanceledError` class is an error class that is thrown when an HTTP request is canceled. It extends the `AxiosError` class. ```ts constructor(message?: string, config?: InternalAxiosRequestConfig, request?: any); __CANCEL__?: boolean; ``` ### `Cancel` The `Cancel` class is an alias for the `CanceledError` class. It is exported for backwards compatibility and will be removed in a future release. ```ts Cancel: typeof CanceledError; ``` ### `isCancel` A function that checks if an error is a `CanceledError`. Useful for distinguishing intentional cancellations from unexpected errors. ```ts isCancel(value: any): value is CanceledError; ``` ```js import axios from 'axios'; const controller = new AbortController(); axios.get('/api/data', { signal: controller.signal }).catch((error) => { if (axios.isCancel(error)) { console.log('Request was cancelled:', error.message); } else { console.error('Unexpected error:', error); } }); controller.abort('User navigated away'); ``` ### `isAxiosError` A function that checks if an error is an `AxiosError`. Use this in `catch` blocks to safely access axios-specific error properties like `error.response` and `error.config`. ```ts isAxiosError(value: any): value is AxiosError; ``` ```js import axios from 'axios'; try { await axios.get('/api/resource'); } catch (error) { if (axios.isAxiosError(error)) { // error.response, error.config, error.code are all available console.error('HTTP error', error.response?.status, error.message); } else { // A non-axios error (e.g. a programming mistake) throw error; } } ``` ### `all` The `all` function is a utility function that takes an array of promises and returns a single promise that resolves when all of the promises in the array have resolved. The `all` function is now deprecated in favour of the `Promise.all` method. It is recommended that you use the `Promise.all` method instead. As of version 0.22.0, the `all` function is deprecated and will be removed in a future release. It is recommended that you use the `Promise.all` method instead. ### `spread` The `spread` function is a utility function that can be used to spread an array of arguments into a function call. This is useful when you have an array of arguments that you want to pass to a function that takes multiple arguments. ```ts spread(callback: (...args: T[]) => R): (array: T[]) => R; ``` ### `toFormData` Converts a plain JavaScript object (or a nested one) to a `FormData` instance. Useful when you want to programmatically build multipart form data from an object. ```ts toFormData(sourceObj: object, formData?: FormData, options?: FormSerializerOptions): FormData; ``` ```js import { toFormData } from 'axios'; const data = { name: 'Jay', avatar: fileBlob }; const form = toFormData(data); // form is now a FormData instance ready to post await axios.post('/api/users', form); ``` ### `formToJSON` Converts a `FormData` instance back to a plain JavaScript object. Useful for reading form data in a structured format. Only dot notation and bracket notation are structural: `.`, `[`, and `]` split paths, while `-`, spaces, `+`, `*`, and `&` stay in literal keys. `foo.bar` and `foo[bar]` create nested objects, and `foo[]` creates an array. ```ts formToJSON(form: FormData): object; ``` ```js import { formToJSON } from 'axios'; const form = new FormData(); form.append('user-name', 'johndoe'); form.append('user.name', 'john'); const obj = formToJSON(form); console.log(obj); // { "user-name": "johndoe", user: { name: "john" } } ``` ### `getAdapter` Resolves and returns an adapter function by name or by passing an array of candidate names. axios uses this internally to select the best available adapter for the current environment. ```ts getAdapter(adapters: string | string[]): AxiosAdapter; ``` ```js import { getAdapter } from 'axios'; // Get the fetch adapter explicitly const fetchAdapter = getAdapter('fetch'); // Get the best available adapter from a priority list const adapter = getAdapter(['fetch', 'xhr', 'http']); ``` ### `mergeConfig` Merges two axios config objects together, applying the same deep-merge strategy that axios uses internally when combining defaults with per-request options. Later values take precedence. ```ts mergeConfig( config1: AxiosRequestConfig, config2: AxiosRequestConfig ): AxiosRequestConfig; ``` ```js import { mergeConfig } from 'axios'; const base = { baseURL: 'https://api.example.com', timeout: 5000 }; const override = { timeout: 10000, headers: { 'X-Custom': 'value' } }; const merged = mergeConfig(base, override); // { baseURL: "https://api.example.com", timeout: 10000, headers: { "X-Custom": "value" } } ``` ## Constants ### `HttpStatusCode` An object that contains a list of HTTP status codes as named constants. Use this to write readable conditionals instead of bare numbers. ```js import axios, { HttpStatusCode } from 'axios'; try { const response = await axios.get('/api/resource'); } catch (error) { if (axios.isAxiosError(error)) { if (error.response?.status === HttpStatusCode.NotFound) { console.error('Resource not found'); } else if (error.response?.status === HttpStatusCode.Unauthorized) { console.error('Authentication required'); } } } ``` ## Miscellaneous ### `VERSION` The current version of the `axios` package. This is a string that represents the version number of the package. It is updated with each release of the package. axios-axios-aa54a5e/docs/pages/advanced/authentication.md000066400000000000000000000105111526660636600236200ustar00rootroot00000000000000# Authentication Most APIs require some form of authentication. This page covers the most common patterns for attaching credentials to axios requests. ## Bearer tokens (JWT) The most common approach is to attach a JWT in the `Authorization` header. The cleanest way to do this is via a request interceptor on your axios instance, so the token is read fresh on every request: ```js import axios from "axios"; const api = axios.create({ baseURL: "https://api.example.com" }); api.interceptors.request.use((config) => { const token = localStorage.getItem("access_token"); if (token) { config.headers.set("Authorization", `Bearer ${token}`); } return config; }); ``` ## HTTP Basic auth For APIs that use HTTP Basic authentication, pass the `auth` option. axios will encode the credentials and set the `Authorization` header automatically: ```js const response = await axios.get("https://api.example.com/data", { auth: { username: "myUser", password: "myPassword", }, }); ``` If `auth` is not supplied, the Node.js HTTP and fetch adapters can also derive Basic auth credentials from the request URL, for example `https://myUser:myPassword@api.example.com/data`. Percent-encoded URL credentials are decoded before the `Authorization` header is generated. Prefer the explicit `auth` option for new code; it takes precedence over URL-embedded credentials. ::: tip For Bearer tokens and API keys, use a custom `Authorization` header rather than the `auth` option — `auth` is only for HTTP Basic. ::: ## API keys API keys are typically passed as a header or a query parameter, depending on what the API expects: ```js // As a header const api = axios.create({ baseURL: "https://api.example.com", headers: { "X-API-Key": "your-api-key-here" }, }); // As a query parameter const response = await axios.get("https://api.example.com/data", { params: { apiKey: "your-api-key-here" }, }); ``` ## Token refresh When access tokens expire, you need to silently refresh them and retry the failed request. A response interceptor is the right place to implement this: ```js import axios from "axios"; const api = axios.create({ baseURL: "https://api.example.com" }); // Track whether a refresh is already in progress to avoid parallel refresh calls let isRefreshing = false; let failedQueue = []; const processQueue = (error, token = null) => { failedQueue.forEach((prom) => { if (error) { prom.reject(error); } else { prom.resolve(token); } }); failedQueue = []; }; api.interceptors.response.use( (response) => response, async (error) => { const originalRequest = error.config; if (error.response?.status === 401 && !originalRequest._retry) { if (isRefreshing) { // Queue the request until the refresh completes return new Promise((resolve, reject) => { failedQueue.push({ resolve, reject }); }) .then((token) => { originalRequest.headers["Authorization"] = `Bearer ${token}`; return api(originalRequest); }) .catch((err) => Promise.reject(err)); } originalRequest._retry = true; isRefreshing = true; try { const { data } = await axios.post("/auth/refresh", { refreshToken: localStorage.getItem("refresh_token"), }); const newToken = data.access_token; localStorage.setItem("access_token", newToken); api.defaults.headers.common["Authorization"] = `Bearer ${newToken}`; processQueue(null, newToken); return api(originalRequest); } catch (refreshError) { processQueue(refreshError, null); // Redirect to login or emit an event localStorage.removeItem("access_token"); window.location.href = "/login"; return Promise.reject(refreshError); } finally { isRefreshing = false; } } return Promise.reject(error); } ); ``` ## Cookie-based authentication For session-based APIs that rely on cookies, set `withCredentials: true` to include cookies in cross-origin requests: ```js const api = axios.create({ baseURL: "https://api.example.com", withCredentials: true, // send cookies with every request }); ``` ::: warning `withCredentials: true` requires the server to respond with `Access-Control-Allow-Credentials: true` and a specific (non-wildcard) `Access-Control-Allow-Origin`. ::: axios-axios-aa54a5e/docs/pages/advanced/cancellation.md000066400000000000000000000062671526660636600232520ustar00rootroot00000000000000# Cancellation Starting from v0.22.0 Axios supports AbortController to cancel requests in a clean way. This feature is available in the browser and in Node.js when using a version of Axios that supports AbortController. To cancel a request, you need to create an instance of `AbortController` and pass its `signal` to the request's `signal` option. ```js const controller = new AbortController(); axios .get('/foo/bar', { signal: controller.signal, }) .then(function (response) { //... }); // cancel the request controller.abort(); ``` ## CancelToken You can also use the `CancelToken` API to cancel requests. This API is deprecated and will be removed in the next major release. It is recommended to use `AbortController` instead. You can create a cancel token using the `CancelToken.source` factory as shown below: ```js const CancelToken = axios.CancelToken; const source = CancelToken.source(); axios .get('/user/12345', { cancelToken: source.token, }) .catch(function (thrown) { if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // handle error } }); axios.post( '/user/12345', { name: 'new name', }, { cancelToken: source.token, } ); // cancel the request (the message parameter is optional) source.cancel('Operation canceled by the user.'); ``` You can also create a cancel token by passing an executor function to the `CancelToken` constructor: ```js const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { // An executor function receives a cancel function as a parameter cancel = c; }), }); // cancel the request cancel(); ``` `CancelToken` also exposes low-level helpers for legacy integrations: ```js const source = axios.CancelToken.source(); const listener = (cancel) => { console.log(cancel.message); }; source.token.subscribe(listener); const signal = source.token.toAbortSignal(); // Pass `signal` to APIs that accept AbortSignal. source.cancel('Operation canceled by the user.'); source.token.unsubscribe(listener); ``` Canceled requests reject with `axios.CanceledError`. The legacy `axios.Cancel` export is an alias of `axios.CanceledError`, and cancellation errors include `__CANCEL__` for `axios.isCancel` compatibility. In TypeScript, `isCancel()` preserves the response data, request data, and query params types while narrowing an `unknown` error: ```ts interface SearchResponse { results: string[]; } interface RequestBody { includeArchived: boolean; } interface SearchParams { query: string; } try { await axios.get("/search"); } catch (error) { if (axios.isCancel(error)) { error.response?.data; // SearchResponse | undefined error.config?.data; // RequestBody | undefined error.config?.params; // SearchParams | undefined } } ``` You can cancel several requests with the same cancel token/abort controller. If a cancellation token is already cancelled at the moment of starting an Axios request, then the request is cancelled immediately, without any attempts to make a real request. axios-axios-aa54a5e/docs/pages/advanced/config-defaults.md000066400000000000000000000050021526660636600236520ustar00rootroot00000000000000# Config defaults axios allows you to specify config defaults that will be applied to every request. You can specify defaults for the `baseURL`, `headers`, `timeout`, and other properties. An example of using config defaults is shown below: ```js axios.defaults.baseURL = "https://jsonplaceholder.typicode.com/posts"; axios.defaults.headers.common["Authorization"] = AUTH_TOKEN; axios.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded"; ``` ::: warning Global headers are sent to every host If your application talks to more than one domain, setting `axios.defaults.headers.common["Authorization"]` will send the token to **all** of them, including third-party APIs you may not control. Use a [custom instance](#custom-instance-defaults) with a scoped `baseURL` for any client that carries credentials. ::: ## Custom instance defaults axios instances are declared with their own defaults when created. These defaults may be overridden setting the `defaults` property to the instance. An example of using custom instance defaults is shown below: ```js var instance = axios.create({ baseURL: "https://jsonplaceholder.typicode.com/posts", timeout: 1000, headers: { Authorization: "foobar" }, }); instance.defaults.headers.common["Authorization"] = AUTH_TOKEN; ``` ## Config order of precedence Config will be merged with an order of precedence. The order is as follows, first the library defaults are set, then default properties of the instance, and finally config argument for the request. An example of the order of precedence is shown below: Some options are request-specific and are only taken from the request config. `data` is one of those options: axios does not inherit or deep-merge request bodies from global or instance defaults. If every request needs shared body fields, add them with a request interceptor or `transformRequest`, and scope that logic carefully so sensitive values are not sent to the wrong endpoint. First lets create an instance with the defaults provided by the library. At this point the timeout config value is `0` as is the default for the library. ```js const instance = axios.create(); ``` Now we will override the timeout default for the instance to `2500` milliseconds. Now all requests using this instance will wait 2.5 seconds before timing out. ```js instance.defaults.timeout = 2500; ``` Finally we will make a request with a timeout of `5000` milliseconds. This request will wait 5 seconds before timing out. ```js instance.get("/longRequest", { timeout: 5000, }); ``` axios-axios-aa54a5e/docs/pages/advanced/create-an-instance.md000066400000000000000000000051251526660636600242470ustar00rootroot00000000000000# Creating an instance `axios.create()` lets you create a pre-configured axios instance. The instance shares the same request and response API as the default `axios` object, but uses the config you provide as its baseline for every request. This is the recommended way to use axios in any application larger than a single file. ```ts import axios from "axios"; const instance = axios.create({ baseURL: "https://api.example.com", timeout: 5000, headers: { "X-Custom-Header": "foobar" }, }); ``` The `create` method accepts the full [Request Config](/pages/advanced/request-config) object. You can then use the instance just like the default axios object: ```js const response = await instance.get("/users/1"); ``` ## Why use an instance? ### Per-service base URL In most apps you talk to more than one API. Creating a separate instance per service avoids repeating the base URL on every call: ```js const githubApi = axios.create({ baseURL: "https://api.github.com" }); const internalApi = axios.create({ baseURL: "https://api.internal.example.com" }); const { data: repos } = await githubApi.get("/users/axios/repos"); const { data: users } = await internalApi.get("/users"); ``` ### Shared authentication headers Attach an auth token to every request from one instance without touching others: ```js const authApi = axios.create({ baseURL: "https://api.example.com", headers: { Authorization: `Bearer ${getToken()}`, }, }); ``` ### Per-service timeouts and retries Different services have different reliability characteristics. Set a tight timeout for real-time services and a relaxed one for batch jobs: ```js const realtimeApi = axios.create({ baseURL: "https://realtime.example.com", timeout: 2000 }); const batchApi = axios.create({ baseURL: "https://batch.example.com", timeout: 60000 }); ``` ### Isolated interceptors Interceptors added to an instance only apply to that instance, keeping your concerns separate: ```js const loggingApi = axios.create({ baseURL: "https://api.example.com" }); loggingApi.interceptors.request.use((config) => { console.log(`→ ${config.method?.toUpperCase()} ${config.url}`); return config; }); ``` ## Overriding defaults per request Config passed at request time always overrides the instance defaults: ```js const api = axios.create({ timeout: 5000 }); // This specific request uses a 30-second timeout instead await api.get("/slow-endpoint", { timeout: 30000 }); ``` ::: tip Instance defaults can also be changed after creation by writing to `instance.defaults`: ```js instance.defaults.headers.common["Authorization"] = `Bearer ${newToken}`; ``` ::: axios-axios-aa54a5e/docs/pages/advanced/error-handling.md000066400000000000000000000177661526660636600235370ustar00rootroot00000000000000# Error handling axios may throw many different types of errors. Some of these errors are caused by axios itself, while others are caused by the server or the client. The following table lists the general structure of the thrown error: | Property | Definition | | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | message | A quick summary of the error message and the status it failed with. | | name | This defines where the error originated from. For axios, it will always be an `AxiosError`. | | stack | Provides the stack trace of the error. | | config | An axios config object with specific instance configurations defined by the user from when the request was made. | | code | Represents an axios identified error. The table below lists out specific definitions for internal axios error. | | status | HTTP response status code. See [here](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) for common HTTP response status code meanings. | Below is a list of potential axios identified error | Code | Definition | | ------------------------- | --------------------------------------------------------------------------------------------- | | ERR_BAD_OPTION_VALUE | Invalid or unsupported value provided in axios configuration. | | ERR_BAD_OPTION | Invalid option provided in axios configuration. | | ECONNABORTED | Typically indicates that the request has been timed out (unless `transitional.clarifyTimeoutError` is set) or aborted by the browser or its plugin. | | ETIMEDOUT | Request timed out due to exceeding the default axios timelimit. `transitional.clarifyTimeoutError` must be set to `true`, otherwise a generic `ECONNABORTED` error will be thrown instead | | ERR_NETWORK | Network-related issue. In the browser, this error can also be caused by a [CORS](https://developer.mozilla.org/ru/docs/Web/HTTP/Guides/CORS) or [Mixed Content](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content) policy violation. The browser does not allow the JS code to clarify the real reason for the error caused by security issues, so please check the console. | | ERR_FR_TOO_MANY_REDIRECTS | Request is redirected too many times; exceeds max redirects specified in axios configuration. | | ERR_DEPRECATED | Deprecated feature or method used in axios. | | ERR_BAD_RESPONSE | Response cannot be parsed properly or is in an unexpected format. Usually related to a response with `5xx` status code. | | ERR_BAD_REQUEST | The request has an unexpected format or is missing required parameters. Usually related to a response with `4xx` status code. | | ERR_CANCELED | Feature or method is canceled explicitly by the user using an AbortSignal (or a CancelToken). | | ERR_NOT_SUPPORT | Feature or method not supported in the current axios environment. | | ERR_INVALID_URL | Invalid URL provided for axios request. | | ERR_FORM_DATA_DEPTH_EXCEEDED | An object exceeds the configured `maxDepth` while serializing `params` or form data. Default limit is 100 levels. See [`paramsSerializer`](/pages/advanced/request-config#paramsserializer) and [`formSerializer`](/pages/advanced/request-config#formserializer). | ## Handling errors The default behaviour of axios is to reject the promise if the request fails. However, you can also catch the error and handle it as you see fit. Below is an example of how to catch an error: ```js axios.get("/user/12345").catch(function (error) { if (error.response) { // The request was made and the server responded with a status code // that falls out of the range of 2xx console.log(error.response.data); console.log(error.response.status); console.log(error.response.headers); } else if (error.request) { // The request was made but no response was received // `error.request` is an instance of XMLHttpRequest in the browser and an instance of // http.ClientRequest in node.js console.log(error.request); } else { // Something happened in setting up the request that triggered an Error console.log("Error", error.message); } console.log(error.config); }); ``` ## Malformed HTTP(S) URLs axios rejects an `http:` or `https:` request `url` or `baseURL` that omits `//` after the protocol. For example, `https:example.com` and `https:/example.com` reject with an `AxiosError` whose code is `ERR_INVALID_URL`, instead of being silently normalized by the browser or Node.js URL parser. Use a well-formed URL such as `https://example.com`. The message names the offending URL, for example: ```text Invalid URL "https:example.com": missing "//" after protocol ``` This behavior prevents malformed URLs from bypassing `baseURL` or URL allowlists. The reported URL is control-character-normalized and redacts credentials, query parameter values, and fragment contents. It retains the scheme, host, path, and query parameter names so the request remains identifiable. Message-level redaction is unconditional because `AxiosError.message` is always included by `toJSON()`; the request config's `redact` option cannot clean an already-created message. Using the `validateStatus` config option, you can override the default condition (status >= 200 && status < 300) and define HTTP code(s) that should throw an error. ```js axios.get("/user/12345", { validateStatus: function (status) { return status < 500; // Resolve only if the status code is less than 500 }, }); ``` Using the `toJSON` method, you can get an object with more information about the error. ```js axios.get("/user/12345").catch(function (error) { console.log(error.toJSON()); }); ``` To avoid logging secrets from `error.config`, pass a `redact` array in the request config. Matching config keys are masked case-insensitively at any depth when `AxiosError#toJSON()` is called. ```js axios.get("/user/12345", { headers: { Authorization: "Bearer token" }, redact: ["authorization"] }).catch(function (error) { console.log(error.toJSON().config.headers.Authorization); // [REDACTED ****] }); ``` ## Handling timeouts When a request exceeds its configured `timeout`, axios rejects with `ECONNABORTED` by default. Set `transitional.clarifyTimeoutError: true` to receive `ETIMEDOUT` instead, which makes timeout errors easier to distinguish from other aborts. ```js async function fetchWithTimeout() { try { const response = await axios.get("https://example.com/data", { timeout: 5000, // 5 seconds transitional: { // set to true if you prefer ETIMEDOUT over ECONNABORTED clarifyTimeoutError: true, }, }); console.log("Response:", response.data); } catch (error) { if (axios.isAxiosError(error)) { if (error.code === "ECONNABORTED" || error.code === "ETIMEDOUT") { console.error("Request timed out. Please try again."); return; } console.error("Axios error:", error.message); return; } console.error("Unexpected error:", error); } } ``` ::: tip Always set a `timeout` in production Without one, a stalled request can hang indefinitely. See [`timeout`](/pages/advanced/request-config#timeout) and [`transitional.clarifyTimeoutError`](/pages/advanced/request-config#transitional) for the matching config options. ::: axios-axios-aa54a5e/docs/pages/advanced/fetch-adapter.md000066400000000000000000000066721526660636600233250ustar00rootroot00000000000000# Fetch adapter The `fetch` adapter is a new adapter that we have introduced as of version 1.7.0. This provides a way to use axios with the `fetch` API thus giving you the best of both worlds. By default, `fetch` will be used if `xhr` and `http` adapters are not available in the build, or not supported by the environment. To use it by default, it must be selected explicitly by setting the `adapter` option to `fetch` when creating an instance of axios. ```js import axios from 'axios'; const instance = axios.create({ adapter: 'fetch', }); ``` The adapter supports the same functionality as the `xhr` adapter, including upload and download progress capturing. It also supports additional response types such as `stream` and `formdata` (if supported by the environment). When `auth` is omitted, the fetch adapter can read HTTP Basic auth credentials from the request URL, for example `https://user:pass@example.com`. Percent-encoded URL credentials are decoded before the `Authorization` header is generated, and `auth` takes precedence over URL-embedded credentials. ## Custom fetch Starting from `v1.12.0`, you can customise the fetch adapter to use a custom `fetch` function instead of the environment global. You can pass a custom `fetch` function, `Request`, and `Response` constructors via the `env` config option. This is useful when working with custom environments or app frameworks that provide their own `fetch` implementation. ::: info When using a custom `fetch` function, you may also need to supply matching `Request` and `Response` constructors. If you omit them, the global constructors will be used. If your custom `fetch` is incompatible with the globals, pass `null` to disable them. **Note:** Setting `Request` and `Response` to `null` will make it impossible for the fetch adapter to capture upload and download progress. ::: ### Basic example ```js import customFetchFunction from 'customFetchModule'; const instance = axios.create({ adapter: 'fetch', onDownloadProgress(e) { console.log('downloadProgress', e); }, env: { fetch: customFetchFunction, Request: null, // null -> disable the constructor Response: null, }, }); ``` ### Using with Tauri [Tauri](https://tauri.app/plugin/http-client/) provides a platform `fetch` function that bypasses browser CORS restrictions for requests made from the native layer. The example below shows a minimal setup for using axios inside a Tauri app with that custom fetch. ```js import { fetch } from '@tauri-apps/plugin-http'; import axios from 'axios'; const instance = axios.create({ adapter: 'fetch', onDownloadProgress(e) { console.log('downloadProgress', e); }, env: { fetch, }, }); const { data } = await instance.get('https://google.com'); ``` ### Using with SvelteKit [SvelteKit](https://svelte.dev/docs/kit/web-standards#Fetch-APIs) provides a custom `fetch` implementation for server-side `load` functions that handles cookie forwarding and relative URLs. Because its `fetch` is incompatible with the standard `URL` API, axios must be configured to use it explicitly, and the global `Request` and `Response` constructors must be disabled. ```js export async function load({ fetch }) { const { data: post } = await axios.get('https://jsonplaceholder.typicode.com/posts/1', { adapter: 'fetch', env: { fetch, Request: null, Response: null, }, }); return { post }; } ``` axios-axios-aa54a5e/docs/pages/advanced/file-posting.md000066400000000000000000000056551526660636600232160ustar00rootroot00000000000000# File posting axios makes file uploads straightforward. Use `postForm` or `FormData` when you need `multipart/form-data` uploads. ## Single file (browser) Pass a `File` object directly as a field value — axios will detect it and use the correct content type automatically: ```js await axios.postForm("https://httpbin.org/post", { description: "My profile photo", file: document.querySelector("#fileInput").files[0], }); ``` ## Multiple files (browser) Pass a `FileList` to upload all selected files at once. They will all be sent under the same field name (`files[]`): ```js await axios.postForm( "https://httpbin.org/post", document.querySelector("#fileInput").files ); ``` You can also pass the `FileList` (or array of `File` objects) explicitly under a custom field name by appending `[]` to the key: ```js await axios.postForm("https://httpbin.org/post", { "files[]": document.querySelector("#fileInput").files, }); ``` To use distinct field names for each file, build a `FormData` object manually: ```js const formData = new FormData(); formData.append("avatar", avatarFile); formData.append("cover", coverFile); await axios.post("https://httpbin.org/post", formData); ``` ## Tracking upload progress (browser) Use the `onUploadProgress` callback to show a progress bar or percentage to your users: ```js await axios.postForm("https://httpbin.org/post", { file: document.querySelector("#fileInput").files[0], }, { onUploadProgress: (progressEvent) => { const percent = Math.round( (progressEvent.loaded * 100) / progressEvent.total ); console.log(`Upload progress: ${percent}%`); }, }); ``` See [Progress capturing](/pages/advanced/progress-capturing) for the full list of fields available on the progress event. ## Files in Node.js In Node.js, use `fs.createReadStream` to upload a file from the filesystem without loading it entirely into memory: ```js import fs from "fs"; import FormData from "form-data"; import axios from "axios"; const form = new FormData(); form.append("file", fs.createReadStream("/path/to/file.jpg")); form.append("description", "My uploaded file"); await axios.post("https://httpbin.org/post", form); ``` ::: tip The `form-data` npm package is required in Node.js environments to create `FormData` objects. In modern Node.js (v18+), the global `FormData` is available natively. ::: ## Uploading a Buffer (Node.js) You can also upload an in-memory `Buffer` directly: ```js const buffer = Buffer.from("Hello, world!"); const form = new FormData(); form.append("file", buffer, { filename: "hello.txt", contentType: "text/plain", knownLength: buffer.length, }); await axios.post("https://httpbin.org/post", form); ``` ::: warning Capturing `FormData` upload progress is not currently supported in Node.js environments. ::: ::: danger When uploading a readable stream in Node.js, set `maxRedirects: 0` to prevent the `follow-redirects` package from buffering the entire stream in RAM. ::: axios-axios-aa54a5e/docs/pages/advanced/header-methods.md000066400000000000000000000201641526660636600234770ustar00rootroot00000000000000# Header methods With the introduction of the new `AxiosHeaders` class, Axios provides a set of methods to manipulate headers. These methods are used to set, get, and delete headers in a more convenient way than directly manipulating the headers object. ## Constructor `new AxiosHeaders(headers?)` The `AxiosHeaders` class constructor accepts an optional object with headers to initialize the instance. The headers object can contain any number of headers, and the keys are case-insensitive. ```js constructor(headers?: RawAxiosHeaders | AxiosHeaders | string); ``` For convenience, you can pass a string with headers separated by a newline character. The headers are then parsed and added to the instance. ```js const headers = new AxiosHeaders(` Host: www.bing.com User-Agent: curl/7.54.0 Accept: */*`); console.log(headers); // Object [AxiosHeaders] { // host: 'www.bing.com', // 'user-agent': 'curl/7.54.0', // accept: '*/*' // } ``` ## Set The `set` method is used to set headers on the instance of `AxiosHeaders`. The method can be called with a single header name and value, an object with multiple headers, or a string with headers separated by a newline character. The method also accepts an optional `rewrite` parameter that controls the behaviour of setting the header. ```js set(headerName, value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher); set(headerName, value, rewrite?: (this: AxiosHeaders, value: string, name: string) => boolean); set(headers?: RawAxiosHeaders | AxiosHeaders | string, rewrite?: boolean); set(headers?: Iterable<[string, AxiosHeaderValue]>, rewrite?: boolean); ``` The rewrite argument controls the overwriting behaviour: - `false` - do not overwrite if header's value is set (is not undefined) - `undefined` (default) - overwrite the header unless its value is set to false - `true` - rewrite anyway The option can also accept a user-defined function that determines whether the value should be overwritten or not. The function receives the current value, header name, and the headers object as arguments. Empty or whitespace-only header names are ignored. Iterable key/value pairs, such as a `Map`, are accepted: ```js const headers = new AxiosHeaders(); headers.set( new Map([ ['X-Trace-Id', 'abc123'], ['Accept', 'application/json'], ]) ); ``` `AxiosHeaders` keeps the case of the first matching key it sees. You can use this to preserve specific header casing by seeding a key with `undefined` and then setting values later. See [Preserving a specific header case](/pages/advanced/headers#preserving-a-specific-header-case). ## Get The `get` method is used to retrieve the value of a header. The method can be called with a single header name, an optional matcher, or a parser. The matcher is defaulted to `true`. The parser can be a regular expression that is used to extract the value from the header. ```js get(headerName: string, parser: typeof AxiosHeaders.parseParameters): AxiosHeaderParameters; get(headerName: string, parser: RegExp): RegExpExecArray | null; get(headerName: string, matcher?: true | AxiosHeaderParser): AxiosHeaderValue; ``` An example of some of the possible usages of the `get` method is shown below: ```js const headers = new AxiosHeaders({ 'Content-Type': 'multipart/form-data; boundary=Asrf456BGe4h', }); console.log(headers.get('Content-Type')); // multipart/form-data; boundary=Asrf456BGe4h console.log(headers.get('Content-Type', true)); // parse key-value pairs from a string separated with \s,;= delimiters: // [Object: null prototype] { // 'multipart/form-data': undefined, // boundary: 'Asrf456BGe4h' // } const quotedHeaders = new AxiosHeaders({ 'Content-Type': 'multipart/form-data; boundary="a,b"', }); console.log({ ...quotedHeaders.get('Content-Type', AxiosHeaders.parseParameters), }); // { boundary: 'a,b' } console.log( headers.get('Content-Type', (value, name, headers) => { return String(value).replace(/a/g, 'ZZZ'); }) ); // multipZZZrt/form-dZZZtZZZ; boundZZZry=Asrf456BGe4h console.log(headers.get('Content-Type', /boundary=(\w+)/)?.[0]); // boundary=Asrf456BGe4h ``` `AxiosHeaders.parseParameters` is an opt-in parser for normalized HTTP parameter values. It returns a null-prototype map with case-insensitive parameter names. Quoted-string delimiters are removed, escaped DQUOTE and backslash pairs are decoded, and commas or semicolons inside quoted values remain part of the value. For unquoted values, only RFC optional whitespace (space and horizontal tab) around the value is removed. The parser omits unsafe object-materialization keys (`__proto__`, `constructor`, and `prototype`). Passing `true` remains the legacy tokenizer and keeps its existing output for backward compatibility. ## Has The `has` method is used to check if a header exists in the instance of `AxiosHeaders`. The method can be called with a single header name and an optional matcher. ```js has(header: string, matcher?: AxiosHeaderMatcher): boolean; ``` ::: info Returns true if the header is set (has no undefined value). ::: ## Delete The `delete` method is used to delete a header from the instance of `AxiosHeaders`. The method can be called with a single header name and an optional matcher. ```js delete(header: string | string[], matcher?: AxiosHeaderMatcher): boolean; ``` ::: info Returns true if at least one header has been removed. ::: ## Clear The `clear` method is used to delete all headers from the instance of `AxiosHeaders` if nothing is passed. If a matcher is passed, only the headers that match the matcher are removed, in this case, the matcher is used to match against the header name rather than the value. ```js clear(matcher?: AxiosHeaderMatcher): boolean; ``` ::: info Returns true if at least one header has been cleared. ::: ## Normalize If the headers object was changed directly, it can cause duplicates with the same name but in different cases. This method normalizes the headers object by combining duplicate keys into one. Axios uses this method internally after calling each interceptor. Set format to true for converting headers name to lowercase and capitalize the initial letters (cOntEnt-type => Content-Type) or false to keep the original format. ```js const headers = new AxiosHeaders({ foo: '1', }); headers.Foo = '2'; headers.FOO = '3'; console.log(headers.toJSON()); // [Object: null prototype] { foo: '1', Foo: '2', FOO: '3' } console.log(headers.normalize().toJSON()); // [Object: null prototype] { foo: '3' } console.log(headers.normalize(true).toJSON()); // [Object: null prototype] { Foo: '3' } ``` ::: info Returns `this` for chaining. ::: ## Concat Merges the instance with targets into a new AxiosHeaders instance. If the target is a string, it will be parsed as RAW HTTP headers. If the target is an AxiosHeaders instance, it will be merged with the current instance. This is useful for case presets when composing headers. For example: ```js const headers = AxiosHeaders.concat( { 'content-type': undefined }, { 'Content-Type': 'application/octet-stream' } ); ``` ```js concat(...targets: Array): AxiosHeaders; ``` ::: info Returns a new AxiosHeaders instance. ::: ## toJSON Resolve all internal headers values into a new null prototype object. Set `asStrings` to true to resolve arrays as a string containing all elements, separated by commas. ```js toJSON(asStrings: true): Record; toJSON(asStrings?: false): Record; ``` ## toString Returns the headers as a CRLF-free HTTP header block, one `name: value` pair per line. ```js toString(): string; ``` ## From Returns a new `AxiosHeaders` instance created from the raw headers passed in, or simply returns the given headers object if it's an `AxiosHeaders` instance. ```js from(thing?: AxiosHeaders | RawAxiosHeaders | string): AxiosHeaders; ``` ## Shortcuts The following shortcuts are available: - `setContentType`, `getContentType`, `hasContentType` - `setContentLength`, `getContentLength`, `hasContentLength` - `setAccept`, `getAccept`, `hasAccept` - `setUserAgent`, `getUserAgent`, `hasUserAgent` - `setContentEncoding`, `getContentEncoding`, `hasContentEncoding` axios-axios-aa54a5e/docs/pages/advanced/headers.md000066400000000000000000000134411526660636600222210ustar00rootroot00000000000000# Headers Axios exposes its own AxiosHeaders class to manipulate headers using a Map-like API that guarantees case-insensitive keys. This class is used internally by Axios to manage headers, but it's also exposed to the user for convenience. Although HTTP headers are case-insensitive, Axios will retain the case of the original header for stylistic reasons and for a workaround when servers mistakenly consider the header's case. The old method of directly manipulating the headers object is still available, but deprecated and not recommended for future usage. ## Working with headers The AxiosHeaders object instance can contain different types of internal values that control the setting and merging logic. The final headers object is obtained by Axios by calling the toJSON method. The AxiosHeaders object is also iterable, so you can use it in loops or convert it to an array or object. The header values can be one of the following types: - `string` - normal string value that will be sent to the server - `null` - skip header when converting to JSON - `false` - skip header when converting to JSON, additionally indicates that set method must be called with rewrite option set to true to overwrite this value (Axios uses this internally to allow users to opt out of installing certain headers like User-Agent or Content-Type) - `undefined` - value is not set ::: warning The header value is considered set if it is not undefined. ::: The headers object is always initialized inside interceptors and transformers as seen in the following example: ```js axios.interceptors.request.use((request: InternalAxiosRequestConfig) => { request.headers.set("My-header", "value"); request.headers.set({ "My-set-header1": "my-set-value1", "My-set-header2": "my-set-value2", }); // Disable subsequent setting of this header by Axios request.headers.set("User-Agent", false); request.headers.setContentType("text/plain"); // Direct access like this is deprecated request.headers["My-set-header2"] = "newValue"; return request; }); ``` You can iterate over an AxiosHeaders using any iterable method, like for-of loop, forEach, or spread operator: ```js const headers = new AxiosHeaders({ foo: '1', bar: '2', baz: '3', }); for (const [header, value] of headers) { console.log(header, value); } // foo 1 // bar 2 // baz 3 ``` ## Setting headers on a request The most common place to set headers is the `headers` option in your request config or instance config: ```js // On a single request await axios.get('/api/data', { headers: { 'Accept-Language': 'en-US', 'X-Request-ID': 'abc123', }, }); // On an instance (applied to every request) const api = axios.create({ headers: { 'X-App-Version': '2.0.0', }, }); ``` ## Preserving a specific header case Axios header names are case-insensitive, but `AxiosHeaders` keeps the case of the first matching key it sees. If you need a specific case for a server with non-standard case-sensitive behavior, define a case preset in defaults and then set values as usual. ```js const api = axios.create(); api.defaults.headers.common = { 'content-type': undefined, accept: undefined, }; await api.put(url, data, { headers: { 'Content-Type': 'application/octet-stream', Accept: 'application/json', }, }); ``` You can also do this with `AxiosHeaders` directly when composing headers: ```js import axios, { AxiosHeaders } from 'axios'; const headers = AxiosHeaders.concat( { 'content-type': undefined }, { 'Content-Type': 'application/octet-stream' } ); await axios.put(url, data, { headers }); ``` ## Setting headers in an interceptor Interceptors are the right place to attach dynamic headers like auth tokens, because the token may not be available when the instance is first created: ```js api.interceptors.request.use((config) => { const token = getAuthToken(); // read at request time config.headers.set('Authorization', `Bearer ${token}`); return config; }); ``` ## Unicode header values `AxiosHeaders` preserves non-control Unicode characters in header values so request interceptors can transform them before the request is sent. CR/LF and other C0 control bytes are still stripped at set time to prevent header injection. Adapters sanitize header values to byte-safe (HT, printable ASCII, and Latin-1 supplement) right before handing them to the platform — Node's `http.request`, the browser's `XMLHttpRequest.setRequestHeader`, and `fetch`'s `Headers`. If a header value contains characters outside that range and you have not encoded it, those characters are stripped, which can produce an empty value on the wire. If you need to send non-ASCII data in a header, encode it in a request interceptor: ```js api.interceptors.request.use((config) => { if (config.headers.has('X-Name')) { config.headers.set('X-Name', encodeURIComponent(config.headers.get('X-Name'))); } return config; }); await api.get('/api/data', { headers: { 'X-Name': '请求用户', }, }); // → request is sent with X-Name: %E8%AF%B7%E6%B1%82%E7%94%A8%E6%88%B7 ``` ## Reading response headers Response headers are available on `response.headers` as an `AxiosHeaders` instance. All header names are lower-cased: ```js const response = await axios.get('/api/data'); console.log(response.headers['content-type']); // application/json; charset=utf-8 console.log(response.headers.get('x-request-id')); // abc123 ``` ## Removing a default header To opt out of a header that axios sets by default (such as `Content-Type` or `User-Agent`), set its value to `false`: ```js await axios.post('/api/data', payload, { headers: { 'Content-Type': false, // let the browser set it automatically (e.g. for FormData) }, }); ``` For more detail on the full `AxiosHeaders` method API, see the [Header methods](/pages/advanced/header-methods) page. axios-axios-aa54a5e/docs/pages/advanced/html-form-processing.md000066400000000000000000000033361526660636600246670ustar00rootroot00000000000000# HTML form posting (browser) You can also post a form directly from a HTML form element. This is useful when you have a form in your page and you want to submit it without any JavaScript code. ```js await axios.postForm('https://httpbin.org/post', document.querySelector('#htmlForm')); ``` `FormData` and `HTMLForm` objects can also be posted as `JSON` by explicitly setting the `Content-Type` header to `application/json`: ```js await axios.post('https://httpbin.org/post', document.querySelector('#htmlForm'), { headers: { 'Content-Type': 'application/json', }, }); ``` An example of a form that is valid and can be submitted by the above code is: ```html
``` The above form will be submitted as: ```json { "foo": "1", "deep": { "prop": "2" }, "deep prop spaced": "3", "baz": ["4", "5"], "user": { "age": "value2" } } ``` ::: tip Field-name path notation Only dot and bracket notation create property paths. Other characters—including spaces, `-`, `+`, `*`, and `&`—remain part of literal field names. For example, `deep.prop` creates a nested path, while `deep prop spaced` remains a top-level key. ::: ::: warning Sending Blobs/Files as JSON (base64) is not currently supported. ::: axios-axios-aa54a5e/docs/pages/advanced/http2.md000066400000000000000000000046461526660636600216560ustar00rootroot00000000000000# HTTP2 Experimental HTTP/2 support was added to the `http` adapter in version `1.13.0`. It is available in Node.js environments only. ## Basic usage Use the `httpVersion` option to select the protocol version for a request. Setting it to `2` enables HTTP/2. ```js const { data, headers, status } = await axios.post( "https://httpbin.org/post", form, { httpVersion: 2, }, ); ``` ## `http2Options` Additional native options for the internal `session.request()` call can be passed via the `http2Options` config object. This also includes the custom `sessionTimeout` parameter, which controls how long (in milliseconds) an idle HTTP/2 session is kept alive before being closed. It defaults to `1000ms`. ```js { httpVersion: 2, http2Options: { rejectUnauthorized: false, // accept self-signed certificates (dev only) sessionTimeout: 5000, // keep idle session alive for 5 seconds }, } ``` ::: warning HTTP/2 support is currently experimental. The API may change in future minor or patch releases. ::: ::: warning Redirects are not supported over HTTP/2 The HTTP/2 adapter does not currently follow redirects. If a request issued with `httpVersion: 2` receives a `3xx` response, the redirect is not followed automatically. Handle these responses manually or stay on HTTP/1.x for endpoints that rely on redirects. ::: ## Full example The example below sends a `multipart/form-data` POST request over HTTP/2 and tracks both upload and download progress. ```js const form = new FormData(); form.append("foo", "123"); const { data, headers, status } = await axios.post( "https://httpbin.org/post", form, { httpVersion: 2, http2Options: { // rejectUnauthorized: false, // sessionTimeout: 1000 }, onUploadProgress(e) { console.log("upload progress", e); }, onDownloadProgress(e) { console.log("download progress", e); }, responseType: "arraybuffer", }, ); ``` ## Config reference | Option | Type | Default | Description | |---|---|---|---| | `httpVersion` | `number` | `1` | HTTP protocol version to use. Set to `2` to enable HTTP/2. | | `http2Options.sessionTimeout` | `number` | `1000` | Time in milliseconds before an idle HTTP/2 session is closed. | All other native `session.request()` options supported by Node.js's built-in `http2` module can also be passed inside `http2Options`. axios-axios-aa54a5e/docs/pages/advanced/interceptors.md000066400000000000000000000156741526660636600233410ustar00rootroot00000000000000# Interceptors Interceptors are a powerful mechanism that can be used to intercept and modify HTTP requests and responses. They are very similar to middleware in Express.js. The interceptor is a function that gets executed before a request is sent and before a response is received. Interceptors are useful for a variety of tasks such as logging, modifying request headers, and modifying the response. Basic usage of interceptors is as follows: ```js // Add a request interceptor axios.interceptors.request.use( function (config) { // Do something before request is sent return config; }, function (error) { // Do something with request error return Promise.reject(error); } ); // Add a response interceptor axios.interceptors.response.use( function (response) { // Any status code that lie within the range of 2xx cause this function to trigger // Do something with response data return response; }, function (error) { // Any status codes that falls outside the range of 2xx cause this function to trigger // Do something with response error return Promise.reject(error); } ); ``` ## Removing Interceptors You can remove any interceptor by using the `eject` method on the interceptor you want to remove. You can also remove all interceptors by calling the `clear` method on the `axios.interceptors` object. Here is an example of how to remove an interceptor: ```js // Eject the request interceptor const myInterceptor = axios.interceptors.request.use(function () { /*...*/ }); axios.interceptors.request.eject(myInterceptor); // Eject the response interceptor const myInterceptor = axios.interceptors.response.use(function () { /*...*/ }); axios.interceptors.response.eject(myInterceptor); ``` Here is an example of how to remove all interceptors: ```js const instance = axios.create(); instance.interceptors.request.use(function () { /*...*/ }); instance.interceptors.request.clear(); // Removes interceptors from requests instance.interceptors.response.use(function () { /*...*/ }); instance.interceptors.response.clear(); // Removes interceptors from responses ``` ## Interceptors default behaviour When you add request interceptors, they are presumed to be asynchronous by default. This can cause a delay in the execution of your axios request when the main thread is blocked (a promise is created under the hood for the interceptor and your request gets put on the bottom of the call stack). If your request interceptors are synchronous you can add a flag to the options object that will tell axios to run the code synchronously and avoid any delays in request execution. ```js axios.interceptors.request.use( function (config) { config.headers.test = "I am only a header!"; return config; }, null, { synchronous: true } ); ``` ### Synchronous interceptor errors When a synchronous request interceptor throws, axios calls that interceptor's paired `onRejected` handler and stops running the remaining request interceptors. If the handler returns normally—including returning `undefined` or a fulfilled Promise—the error is handled and axios dispatches with the last valid config. The handler's return value does not replace that config. To prevent dispatch, omit the rejection handler or have it throw or return a rejected Promise. The terminal error then continues through response rejection interceptors. Use a rejected Promise when validation must block the request: ```js axios.interceptors.request.use( function validate(config) { if (!config.headers.has("Authorization")) { throw new Error("Authorization is required"); } return config; }, function rejectInvalidRequest(error) { return Promise.reject(error); }, { synchronous: true } ); ``` A logging-only rejection handler can return normally to retain the existing continuation behavior: ```js axios.interceptors.request.use( function prepare(config) { throw new Error("Optional preparation failed"); }, function logPreparationFailure(error) { console.warn(error); // Returning normally dispatches with the last valid config. }, { synchronous: true } ); ``` ## Interceptors using `runWhen` If you want to execute a particular interceptor based on a runtime check, you can add a runWhen function to the options object. The interceptor will not be executed if and only if the return of runWhen is false. The function will be called with the config object (don't forget that you can bind your own arguments to it as well.) This can be handy when you have an asynchronous request interceptor that only needs to run at certain times. ```js function onGetCall(config) { return config.method === "get"; } axios.interceptors.request.use( function (config) { config.headers.test = "special get headers"; return config; }, null, { runWhen: onGetCall } ); ``` ## Interceptor execution order ::: warning Request and response interceptors run in **opposite** orders Request interceptors are executed in **reverse order** (LIFO — last in, first out). The _last_ request interceptor added is executed _first_. Response interceptors are executed in the **order they were added** (FIFO — first in, first out). The _first_ response interceptor added is executed _first_. ::: The following example shows the full execution order for three request interceptors and three response interceptors: ```js const instance = axios.create(); const interceptor = (id) => (base) => { console.log(id); return base; }; instance.interceptors.request.use(interceptor("Request Interceptor 1")); instance.interceptors.request.use(interceptor("Request Interceptor 2")); instance.interceptors.request.use(interceptor("Request Interceptor 3")); instance.interceptors.response.use(interceptor("Response Interceptor 1")); instance.interceptors.response.use(interceptor("Response Interceptor 2")); instance.interceptors.response.use(interceptor("Response Interceptor 3")); // Console output: // Request Interceptor 3 // Request Interceptor 2 // Request Interceptor 1 // [HTTP request is made] // Response Interceptor 1 // Response Interceptor 2 // Response Interceptor 3 ``` ## Multiple interceptors You may add multiple interceptors to the same request or response. The following will hold true for multiple interceptors in the same chain in the order below: - Each interceptor is executed - Request interceptors are executed in reverse order (LIFO). - Response interceptors are executed in the order they were added (FIFO). - only the last interceptor's result is returned - every interceptor receives the result of its predecessor - when the fulfilment-interceptor throws - the following fulfilment-interceptor is not called - the following rejection-interceptor is called - once caught, another following fulfil-interceptor is called again (just like in a promise chain). ::: tip To gain an in-depth understanding of how interceptors work, you can read the test cases over [here](https://github.com/axios/axios/blob/v1.x/tests/browser/interceptors.browser.test.js). ::: axios-axios-aa54a5e/docs/pages/advanced/multipart-form-data-format.md000066400000000000000000000154671526660636600257770ustar00rootroot00000000000000# multipart-form-data format axios can send requests in the `multipart/form-data` format. This format is commonly used when uploading files. To send a request in this format, you need to create a `FormData` object and append the data to it. Then you can pass the `FormData` object to the `data` property of the axios request config. ```js const formData = new FormData(); formData.append('foo', 'bar'); axios.post('https://httpbin.org/post', formData); ``` Do not manually set the `Content-Type` header for browser, web worker, or React Native `FormData`; those runtimes add the multipart boundary themselves. In node.js, you can use the `form-data` library as follows: ```js const FormData = require('form-data'); const form = new FormData(); form.append('my_field', 'my value'); form.append('my_buffer', Buffer.alloc(10)); form.append('my_file', fs.createReadStream('/foo/bar.jpg')); axios.post('https://example.com', form); ``` ## Automatic serialization to FormData Starting from v0.27.0, Axios supports automatic object serialization to a FormData object if the request Content-Type header is set to multipart/form-data. This means that you can pass a JavaScript object directly to the data property of the axios request config. For example when passing data to a POST request: ```js import axios from 'axios'; axios .post( 'https://httpbin.org/post', { x: 1 }, { headers: { 'Content-Type': 'multipart/form-data', }, } ) .then(({ data }) => console.log(data)); ``` In the node.js build, the ([`form-data`](https://github.com/form-data/form-data)) polyfill is used by default. You can overload the FormData class by setting the env.FormData config variable, but you probably won't need it in most cases: ```js const axios = require('axios'); var FormData = require('form-data'); axios .post( 'https://httpbin.org/post', { x: 1, buf: Buffer.alloc(10) }, { headers: { 'Content-Type': 'multipart/form-data', }, } ) .then(({ data }) => console.log(data)); ``` ## Header policy for Node.js `FormData` When you pass a Node.js `FormData` object that exposes `getHeaders()` (such as the [`form-data`](https://github.com/form-data/form-data) package), axios copies all headers it returns onto the request by default. This preserves v1 compatibility but can be problematic when the `FormData` object comes from an untrusted source — `getHeaders()` could overwrite headers like `Authorization` or inject arbitrary ones. Set `formDataHeaderPolicy: 'content-only'` to copy **only** `Content-Type` and `Content-Length` from `getHeaders()`, then set any other headers explicitly via the request `headers` config: ```js await axios.post('https://example.com/upload', form, { formDataHeaderPolicy: 'content-only', headers: { Authorization: 'Bearer my-token', }, }); ``` The default value is `'legacy'`. See [`formDataHeaderPolicy`](/pages/advanced/request-config#formdataheaderpolicy) in the request config reference for details. ## Supported endings Axios FormData serializer supports some special endings to perform the following operations: - `{}` - serialize the value with JSON.stringify - `[]` - unwrap the array-like object as separate fields with the same key ::: warning Note: unwrap/expand operation will be used by default on arrays and FileList objects ::: ## Configuring the FormData serializer FormData serializer supports additional options via config.formSerializer: object property to handle rare cases: - `visitor: Function` - user-defined visitor function that will be called recursively to serialize the data object to a FormData object by following custom rules. - `dots: boolean = false` - use dot notation instead of brackets to serialize arrays and objects; - `metaTokens: boolean = true` - add the special ending (e.g `user{}: '{"name": "John"}'`) in the FormData key. The back-end body-parser could potentially use this meta-information to automatically parse the value as JSON. - `indexes: null|false|true = false` - controls how indexes will be added to unwrapped keys of flat array-like objects - `null` - don't add brackets (`arr: 1`, `arr: 2`, `arr: 3`) - `false` (default) - add empty brackets (`arr[]: 1`, `arr[]: 2`, `arr[]: 3`) - `true` - add brackets with indexes (`arr[0]: 1`, `arr[1]: 2`, `arr[2]: 3`) - `maxDepth: number = 100` - maximum object nesting depth the serializer will recurse into. If the input exceeds this depth, an `AxiosError` with `code: 'ERR_FORM_DATA_DEPTH_EXCEEDED'` is thrown. This protects server-side applications from DoS attacks via deeply nested payloads. Set to `Infinity` to disable the limit. - `Blob: typeof Blob` - Blob constructor used when converting ArrayBuffer-like values for spec-compliant `FormData`. Override it only for runtimes that provide a compatible `Blob` constructor under a different binding. ```js // Allow deeper nesting for schemas that legitimately exceed 100 levels: axios.postForm('/api', data, { formSerializer: { maxDepth: 200 } }); ``` ::: warning Security note The default limit of 100 is intentional. Server-side code that forwards client-controlled JSON to axios as `data` is vulnerable to a call-stack overflow without this guard. Only raise `maxDepth` if your schema genuinely requires it. ::: For example, if we have an object like this: ```js const obj = { x: 1, arr: [1, 2, 3], arr2: [1, [2], 3], users: [ { name: 'Peter', surname: 'Griffin' }, { name: 'Thomas', surname: 'Anderson' }, ], 'obj2{}': [{ x: 1 }], }; ``` The following steps will be executed by the Axios serializer internally: ```js const formData = new FormData(); formData.append('x', '1'); formData.append('arr[]', '1'); formData.append('arr[]', '2'); formData.append('arr[]', '3'); formData.append('arr2[0]', '1'); formData.append('arr2[1][0]', '2'); formData.append('arr2[2]', '3'); formData.append('users[0][name]', 'Peter'); formData.append('users[0][surname]', 'Griffin'); formData.append('users[1][name]', 'Thomas'); formData.append('users[1][surname]', 'Anderson'); formData.append('obj2{}', '[{"x":1}]'); ``` ## Converting FormData back to JSON `axios.formToJSON()` converts dot and bracket notation in field names into nested objects and arrays. Only `.`, `[`, and `]` are structural separators. Characters such as `-`, spaces, `+`, `*`, and `&` remain part of literal keys. ```js const form = new FormData(); form.append('user-name', 'johndoe'); form.append('user.name', 'john'); console.log(axios.formToJSON(form)); // { // 'user-name': 'johndoe', // user: { name: 'john' } // } ``` `user[name]` also creates a nested object path, while `items[]` creates an array. Axios supports the following shortcut methods: `postForm`, `putForm`, `patchForm` which are just the corresponding http methods with the `Content-Type` header preset to `multipart/form-data`. axios-axios-aa54a5e/docs/pages/advanced/progress-capturing.md000066400000000000000000000036101526660636600244410ustar00rootroot00000000000000# Progress capturing Axios supports both browser and node environments to capture request upload/download progress. The frequency of progress events is forced to be limited to 3 times per second. This is to prevent the browser from being overwhelmed with progress events. An example of capturing progress events is shown below: ```js await axios.post(url, data, { onUploadProgress: function (axiosProgressEvent) { /*{ loaded: number; total?: number; progress?: number; // in range [0..1] bytes: number; // how many bytes have been transferred since the last trigger (delta) estimated?: number; // estimated time in seconds rate?: number; // upload speed in bytes upload: true; // upload sign }*/ }, onDownloadProgress: function (axiosProgressEvent) { /*{ loaded: number; total?: number; progress?: number; bytes: number; estimated?: number; rate?: number; // download speed in bytes download: true; // download sign }*/ }, }); ``` You can also stream the upload and download progress events to a readable stream in Node.js. This is useful when you want to display the progress in a custom way. An example of streaming progress events is shown below: ```js const { data } = await axios.post(SERVER_URL, readableStream, { onUploadProgress: ({ progress }) => { console.log((progress * 100).toFixed(2)); }, headers: { "Content-Length": contentLength, }, maxRedirects: 0, // avoid buffering the entire stream }); ``` ::: warning Capturing FormData upload progress is not currently supported in node.js environments ::: ::: danger It is recommended to disable redirects by setting maxRedirects: 0 to upload the stream in the node.js environment, as the follow-redirects package will buffer the entire stream in RAM without following the "backpressure" algorithm ::: axios-axios-aa54a5e/docs/pages/advanced/promises.md000066400000000000000000000051021526660636600224420ustar00rootroot00000000000000# Promises axios is built on top of the native ES6 Promise API. Every axios request returns a Promise that resolves to a response object or rejects with an error. If your environment doesn't support ES6 Promises, you will need to polyfill them — for example with [es6-promise](https://github.com/stefanpenner/es6-promise). For TypeScript integrations, `AxiosPromise` is a promise of `AxiosResponse`. It preserves request data and query params on `response.config`: ```ts declare const search: AxiosPromise; search.then((response) => { response.data; // SearchResponse response.config.data; // RequestBody | undefined response.config.params; // SearchParams | undefined }); ``` ## then / catch / finally Because axios returns a standard Promise, you can use `.then()`, `.catch()`, and `.finally()` to handle the result: ```js axios.get("/api/users") .then((response) => { console.log(response.data); }) .catch((error) => { console.error("Request failed:", error.message); }) .finally(() => { console.log("Request finished"); }); ``` ## async / await The recommended approach for most codebases is `async/await`, which makes asynchronous code read like synchronous code: ```js async function fetchUser(id) { try { const response = await axios.get(`/api/users/${id}`); return response.data; } catch (error) { console.error("Failed to fetch user:", error.message); throw error; } } ``` ## Parallel requests Because axios returns a standard Promise, you can use `Promise.all` to make multiple requests at the same time and wait for all of them to complete: ```js const [users, posts] = await Promise.all([ axios.get("/api/users"), axios.get("/api/posts"), ]); console.log(users.data, posts.data); ``` ::: tip `Promise.all` will reject as soon as any one of the requests fails. If you want to handle partial failures, use `Promise.allSettled` instead. ::: ```js const results = await Promise.allSettled([ axios.get("/api/users"), axios.get("/api/posts"), ]); results.forEach((result) => { if (result.status === "fulfilled") { console.log(result.value.data); } else { console.error("Request failed:", result.reason.message); } }); ``` ## Chaining requests You can chain `.then()` calls to run requests sequentially, passing data from one to the next: ```js axios.get("/api/user/1") .then(({ data: user }) => axios.get(`/api/posts?userId=${user.id}`)) .then(({ data: posts }) => { console.log("Posts for user:", posts); }) .catch(console.error); ``` axios-axios-aa54a5e/docs/pages/advanced/rate-limiting.md000066400000000000000000000040471526660636600233550ustar00rootroot00000000000000# Rate limiting axios supports bandwidth limiting in the Node.js environment via the HTTP adapter. This lets you cap how fast data is uploaded or downloaded, which is useful for bulk operations, background jobs, or polite scraping that shouldn't saturate a connection. ## `maxRate` The `maxRate` option accepts either a number (bytes per second) or an array where the first value is the upload limit and the second value is the download limit. Use `[uploadRate]` to limit upload only, or `[uploadRate, downloadRate]` to limit both directions. When a single number is passed, the same limit applies to both upload and download. ```js // Limit both upload and download to 100 KB/s await axios.get(URL, { maxRate: 100 * 1024 }); // Limit upload to 100 KB/s, download to 500 KB/s await axios.get(URL, { maxRate: [100 * 1024, 500 * 1024] }); ``` ::: warning `maxRate` is only supported by the Node.js HTTP adapter. It has no effect in browser environments. ::: ## Upload rate limiting Cap the upload speed and log progress at the same time: ```js const { data } = await axios.post(SERVER_URL, myBuffer, { onUploadProgress: ({ progress, rate }) => { const percent = (progress * 100).toFixed(1); const kbps = (rate / 1024).toFixed(1); console.log(`Upload [${percent}%] at ${kbps} KB/s`); }, maxRate: [100 * 1024], // cap upload at 100 KB/s }); ``` ## Download rate limiting Cap the download speed for large responses: ```js const { data } = await axios.get(FILE_URL, { onDownloadProgress: ({ progress, rate }) => { const percent = (progress * 100).toFixed(1); const kbps = (rate / 1024).toFixed(1); console.log(`Download [${percent}%] at ${kbps} KB/s`); }, maxRate: [Infinity, 200 * 1024], // no upload limit, 200 KB/s download limit responseType: "arraybuffer", }); ``` ## Combined upload and download limiting Pass both limits as an array to control both directions simultaneously: ```js await axios.post(SERVER_URL, largeBuffer, { maxRate: [50 * 1024, 500 * 1024], // 50 KB/s up, 500 KB/s down }); ``` axios-axios-aa54a5e/docs/pages/advanced/request-config.md000066400000000000000000000730231526660636600235430ustar00rootroot00000000000000# Request config The request config is used to configure the request. There is a wide range of options available, but the only required option is `url`. If the configuration object does not contain a `method` field, the default method is `GET`. ::: warning Security: decompression-bomb protection is opt-in By default `maxContentLength` and `maxBodyLength` are `-1` (unlimited). A malicious or compromised server can return a tiny gzip/deflate/brotli/zstd body that expands to gigabytes and exhaust the Node.js process. If you call servers you do not fully trust, **set a cap**: ```js axios.defaults.maxContentLength = 10 * 1024 * 1024; // 10 MB axios.defaults.maxBodyLength = 10 * 1024 * 1024; ``` See the [security guide](/pages/misc/security) for details. ::: ### `url` The `url` is the URL to which the request is made. It can be a string or an instance of `URL`. An `http:` or `https:` URL must include `//` after the protocol. Malformed values such as `https:example.com` and `https:/example.com` are rejected with `ERR_INVALID_URL`; use a well-formed URL such as `https://example.com`. ### `method` The `method` is the HTTP method to use for the request. The default method is `GET`. ### `baseURL` The `baseURL` is the base URL to be prepended to the `url` unless the `url` is an absolute URL. This is useful for making requests to the same domain without having to repeat the domain name and any api or version prefix. `baseURL` is a URL-construction convenience, not a security boundary. If a request `url` comes from untrusted input, validate it before passing it to axios. A relative `url` can contain `..` segments; after axios combines it with `baseURL`, the platform URL parser normalizes the path and may resolve the request outside the intended path prefix. `allowAbsoluteUrls: false` prevents absolute URLs from replacing `baseURL`, but it does not validate or constrain relative paths. The same well-formed URL rule applies to `baseURL`: `http:` and `https:` values must include `//` after the protocol. ### `allowAbsoluteUrls` The `allowAbsoluteUrls` determines whether or not absolute URLs will override a configured `baseUrl`. When set to true (default), absolute values for `url` will override `baseUrl`. When set to false, absolute values for `url` will always be prepended by `baseUrl`. ### `transformRequest` The `transformRequest` function allows you to modify the request data before it is sent to the server. This function is called with the request data as its only argument. This is only applicable for request methods `PUT`, `POST`, `PATCH` and `DELETE`. The last function in the array must return a string or an instance of Buffer, ArrayBuffer, FormData or Stream. ### `transformResponse` The `transformResponse` function allows you to modify the response data before it is passed to the `then` or `catch` functions. This function is called with the response data as its only argument. ### `parseReviver` The `parseReviver` function allows you to provide a custom "reviver" function directly to the native `JSON.parse()` call used by the default `transformResponse`. This is particularly useful for performing high-performance type hydration (e.g., converting ISO strings to `Temporal` or `Date` objects) or preventing precision loss during parsing. In modern environments (ES2023+), the reviver function receives a third `context` argument. This provides access to the raw JSON `source`, allowing for precise conversion of large integers (BigInt) that would otherwise lose precision if parsed as standard JavaScript numbers. > Note: `Temporal` is not yet available in all environments. Consider using a polyfill if needed. ```js const client = axios.create({ parseReviver: (key, value, context) => { // Example: Precision-safe BigInt parsing if (typeof value === 'number' && context?.source) { const isInteger = Number.isInteger(value); const isUnsafe = !Number.isSafeInteger(value); const isValidIntegerString = /^-?\d+$/.test(context.source); if (isInteger && isUnsafe && isValidIntegerString) { try { return BigInt(context.source); } catch { // Fallback: return original value if parsing fails } } } // Example: Hydrating dates into Temporal objects if ( typeof value === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(value) && typeof Temporal !== 'undefined' && Temporal?.PlainDate ) { return Temporal.PlainDate.from(value); } return value; }, }); ``` ### `headers` The `headers` are the HTTP headers to be sent with the request. The `Content-Type` header is set to `application/json` by default. ### `params` The `params` are the URL parameters to be sent with the request. This must be a plain object or a URLSearchParams object. If the `url` contains query parameters, they will be merged with the `params` object. ### `paramsSerializer` The `paramsSerializer` function allows you to serialize the `params` object before it is sent to the server. There are a few options available for this function, so please refer to the full request config example at the end of this page. In TypeScript, `AxiosRequestConfig` uses `P` for `params`, and a custom `paramsSerializer` receives that same type: ```ts interface SearchParams { query: string; page?: number; } const config: AxiosRequestConfig = { params: { query: "axios", page: 1 }, paramsSerializer: (params) => `${params.query}:${params.page ?? 1}`, }; ``` #### Strict RFC 3986 percent-encoding By default, axios decodes `%3A`, `%24`, `%2C` and `%20` back to `:`, `$`, `,` and `+` for readability (the `+` follows the `application/x-www-form-urlencoded` convention for spaces in query strings). These characters are valid in a query component under [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986#section-3.4), so the default output is correct. However, some backends require strict percent-encoding and reject the readable form. Use the `encode` option to override the default encoder: ```js // Per-request: emit strict RFC 3986 percent-encoding for query values axios.get('/foo', { params: { filter: JSON.stringify({ startedAt: '2026-01-23' }) }, paramsSerializer: { encode: encodeURIComponent }, }); // Or set it on the instance defaults const client = axios.create({ paramsSerializer: { encode: encodeURIComponent }, }); ``` ### `data` The `data` is the data to be sent as the request body. This can be a string, a plain object, a Buffer, ArrayBuffer, FormData, Stream, or URLSearchParams. Only applicable for request methods `PUT`, `POST`, `DELETE` , and `PATCH`. When no `transformRequest` is set, must be of one of the following types: - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams - Browser only: FormData, File, Blob - React Native: FormData - Node only: Stream, Buffer, FormData (form-data package) For browser, web worker, and React Native `FormData`, do not manually set `Content-Type`; the runtime adds the multipart boundary. For Node.js `FormData` objects that provide a `getHeaders()` method, axios copies all returned headers by default for v1 compatibility. If the `FormData` object is custom or not fully trusted, set `formDataHeaderPolicy: 'content-only'` to copy only `Content-Type` and `Content-Length`, and set any other request headers explicitly via the request `headers` config. ### `formDataHeaderPolicy` Controls how axios copies headers returned by Node.js `FormData#getHeaders()`. The default is `'legacy'`, which copies all returned headers to preserve existing v1 behavior. Set `'content-only'` to copy only `Content-Type` and `Content-Length` from `getHeaders()`. ### Custom symbol-keyed options Config merging preserves own enumerable symbol properties. TypeScript applications can module-augment `AxiosRequestConfig` with a specific symbol key, then read that option from `InternalAxiosRequestConfig` in request interceptors or adapters. Inherited and non-enumerable symbol properties are not copied. See the [TypeScript guide](/pages/advanced/type-script#symbol-keyed-custom-request-config) for an example. ### `timeout` The `timeout` is the number of milliseconds before the request times out. If the request takes longer than `timeout`, the request will be aborted. ### `withCredentials` The `withCredentials` property indicates whether or not cross-site Access-Control requests should be made using credentials such as cookies, authorization headers, or TLS client certificates. Setting withCredentials has no effect on same-site requests. ### `adapter` `adapter` allows custom handling of requests which makes testing easier. Return a promise and supply a valid response see [adapters](/pages/advanced/adapters) for more information. We also provide a number of built-in adapters. The default adapter is `http` for node and `xhr` for browsers. The full list of built-in adapters as follows: - fetch - http - xhr You may also pass an array of adapters to be used, axios will use the first adapter that is supported by the environment. ### `auth` `auth` indicates that HTTP Basic auth should be used, and supplies credentials. This will set an `Authorization` header, overwriting any existing `Authorization` custom headers you have set using `headers`. If `auth` is omitted, the Node.js HTTP and fetch adapters can derive Basic auth credentials from the request URL, for example `https://user:pass@example.com`; percent-encoded URL credentials are decoded, and `auth` always takes precedence over URL-embedded credentials. In the Node.js HTTP adapter, Basic auth is preserved on same-origin redirects and stripped on cross-origin redirects. Please note that only HTTP Basic auth is configurable through this parameter. For Bearer tokens and such, use `Authorization` custom headers instead. ### `responseType` The `responseType` indicates the type of data that the server will respond with. This can be one of the following: - arraybuffer - document - json - text - stream - blob (browser only) - formdata (fetch adapter only) ### `responseEncoding` The `responseEncoding` indicates encoding to use for decoding responses. The following options are supported: - ascii - ASCII - ansi - ANSI - binary - BINARY - base64 - BASE64 - base64url - BASE64URL - hex - HEX - latin1 - LATIN1 - ucs-2 - UCS-2 - ucs2 - UCS2 - utf-8 - UTF-8 - utf8 - UTF8 - utf16le - UTF16LE ::: tip Note: Ignored for `responseType` of `stream` or client-side requests ::: ### `xsrfCookieName` The `xsrfCookieName` is the name of the cookie to use as a value for `XSRF` token. ### `xsrfHeaderName` The `xsrfHeaderName` is the name of the header to use as a value for `XSRF` token. ### `withXSRFToken` `withXSRFToken` controls whether axios reads the XSRF cookie and sets the XSRF header on browser requests. It accepts: - `undefined` _(default)_ — set the XSRF header only for same-origin requests. - `true` — always set the XSRF header, including for cross-origin requests. - `false` — never set the XSRF header. - `(config: InternalAxiosRequestConfig) => boolean | undefined` — a callback that decides per-request, receiving the internal config object. ```ts withXSRFToken: boolean | undefined | ((config: InternalAxiosRequestConfig) => boolean | undefined); ``` ::: warning Cross-origin XSRF and `withCredentials` `withCredentials` controls whether cross-site requests include credentials (cookies, HTTP auth). `withXSRFToken` controls whether axios sets the XSRF header. For cross-origin requests, set `withXSRFToken: true` to force the header; additionally set `withCredentials: true` only when the request also needs credentials/cookies. ```js axios.get('/user', { withCredentials: true, withXSRFToken: true }); ``` ::: ### `onUploadProgress` The `onUploadProgress` function allows you to listen to the progress of an upload. ### `onDownloadProgress` The `onDownloadProgress` function allows you to listen to the progress of a download. ### `maxContentLength` The `maxContentLength` property defines the maximum response size in bytes. The Node.js HTTP adapter enforces it for buffered and streamed responses. The fetch adapter enforces it when the response length is declared, the response stream can be tracked, or the response size can otherwise be determined. > ⚠� **Security:** defaults to `-1` (unlimited). Unbounded responses combined with gzip/deflate/brotli/zstd decompression allow decompression-bomb DoS. > Set an explicit limit when requesting servers you do not fully trust. ### `maxBodyLength` The `maxBodyLength` property defines the maximum request body size in bytes. The Node.js HTTP adapter enforces it, and the fetch adapter enforces it when the request body length can be determined. ### `redact` The `redact` property is an optional array of config key names to mask when an `AxiosError` is serialized with `toJSON()`. Matching is case-insensitive and recursive across the serialized request config. Matching values are replaced with `[REDACTED ****]`. `redact` only affects error serialization. It does not change request data, headers, or the original config object. ```js axios .get('/user/12345', { headers: { Authorization: 'Bearer token' }, auth: { username: 'me', password: 'secret' }, redact: ['authorization', 'password'], }) .catch((error) => { console.log(error.toJSON().config); }); ``` ### `validateStatus` The `validateStatus` function allows you to override the default status code validation. By default, axios will reject the promise if the status code is not in the range of 200-299. You can override this behavior by providing a custom `validateStatus` function. The function should return `true` if the status code is within the range you want to accept. By default, explicit `validateStatus: undefined` keeps legacy behavior and resolves every response status because `transitional.validateStatusUndefinedResolves` defaults to `true`. Set `transitional.validateStatusUndefinedResolves` to `false` when you want an explicit `validateStatus: undefined` to behave as if `validateStatus` was omitted, so axios uses the configured/default validator and rejects non-2xx responses by default. `validateStatus: null` still accepts every response status. If you disable the transitional behavior and intentionally want all statuses to resolve, use `validateStatus: null` or a validator that returns `true`. ```js axios.get('/user/12345', { validateStatus: undefined, transitional: { validateStatusUndefinedResolves: false, }, }); ``` ### `maxRedirects` The `maxRedirects` property defines the maximum number of redirects to follow. If set to 0, no redirects will be followed. ### `sensitiveHeaders` The `sensitiveHeaders` property is an optional array of custom secret-bearing header names, such as `X-API-Key`, that the Node.js HTTP adapter removes when following a redirect to a different origin. Matching is case-insensitive. Same-origin redirects keep these headers. If `maxRedirects` is `0`, axios does not follow redirects and `sensitiveHeaders` is not used. ```js axios.get('https://api.example.com/users', { headers: { 'X-API-Key': 'secret' }, sensitiveHeaders: ['X-API-Key'], }); ``` ### `beforeRedirect` The `beforeRedirect` function allows you to modify the request before it is redirected. Use this to adjust the request options upon redirecting, to inspect the latest response headers, or to cancel the request by throwing an error. If maxRedirects is set to 0, `beforeRedirect` is not used. ```js beforeRedirect: (options, { headers }) => { if (options.hostname === 'example.com' && options.protocol === 'https:') { options.auth = 'user:password'; } }; ``` ::: warning Security: re-injecting credentials on redirect The `beforeRedirect` hook runs **after** sensitive headers are stripped during redirects. The `follow-redirects` library removes credentials on protocol downgrade (HTTPS → HTTP) for security. Since `beforeRedirect` runs after this, re-injecting credentials without checking the destination protocol can expose sensitive data. Only re-add credentials for trusted HTTPS destinations, and avoid re-adding them on downgraded redirects. ::: ### `socketPath` The `socketPath` property defines a UNIX socket to use instead of a TCP connection. e.g. `/var/run/docker.sock` to send requests to the docker daemon. Only `socketPath` or `proxy` can be specified. If both are specified, `socketPath` is used. :::warning Security When `socketPath` is set, the hostname and port of the request URL are ignored and axios communicates directly with the specified Unix domain socket. If any part of the request config is derived from user input (for example, when forwarding or merging request options in a proxy/webhook handler), an attacker can inject `socketPath` to redirect traffic to privileged local sockets such as `/var/run/docker.sock`, `/run/containerd/containerd.sock`, or `/run/systemd/private` — bypassing hostname-based SSRF protections entirely (CWE-918). Strip or allowlist config keys from untrusted input, and/or restrict accepted socket paths with `allowedSocketPaths` (see below). ::: ### `allowedSocketPaths` Restricts which socket paths may be used via `socketPath`. Accepts a string or an array of strings. When set, axios resolves the `socketPath` and compares it against each entry (also resolved); the request is rejected with `AxiosError` code `ERR_BAD_OPTION_VALUE` when there is no match. When unset (default), `socketPath` behaves as before. ```js const client = axios.create({ allowedSocketPaths: ['/var/run/docker.sock'], }); // allowed await client.get('http://localhost/v1.45/info', { socketPath: '/var/run/docker.sock' }); // rejected — not in allowlist await client.get('http://localhost/pods', { socketPath: '/var/run/kubelet.sock' }); ``` An empty array (`allowedSocketPaths: []`) blocks all socket paths. ### `transport` The `transport` property defines the transport to use for the request. This is useful for making requests over a different protocol, such as `http2`. ### `httpAgent` and `httpsAgent` The `httpAgent` and `httpsAgent` define a custom agent to be used when performing http and https requests, respectively, in node.js. This allows options to be added like `keepAlive` that are not enabled by default. ### `proxy` The `proxy` defines the hostname, port, and protocol of a proxy server you would like to use. You can also define your proxy using the conventional `http_proxy` and `https_proxy` environment variables. If you are using environment variables for your proxy configuration, you can also define a `no_proxy` environment variable as a comma-separated list of domains that should not be proxied. On Node.js versions with native environment proxy support, axios defers environment proxy handling to Node when the selected `httpAgent` or `httpsAgent` has `proxyEnv` enabled, including processes started with `NODE_USE_ENV_PROXY=1`, `--use-env-proxy`, or `NODE_OPTIONS=--use-env-proxy`. Custom agents without `proxyEnv` continue to use axios environment proxy resolution. Explicit `proxy` config is still handled by axios. Use `false` to disable proxies, ignoring environment variables. `auth` indicates that HTTP Basic auth should be used to connect to the proxy, and supplies credentials. This will set an `Proxy-Authorization` header, overwriting any existing `Proxy-Authorization` custom headers you have set using `headers`. If the proxy server uses HTTPS, then you must set the protocol to `https`. A user-supplied `Host` header in `headers` is preserved when forwarding through a proxy (case-insensitive match on `host` / `Host` / `HOST`). This lets you target a virtual host that differs from the request URL — for example, hitting `127.0.0.1:4000` while having the proxy treat the request as `example.com`. If no `Host` header is supplied, axios defaults it to the request URL's `hostname:port` as before. For `https://` targets, axios establishes a CONNECT tunnel through the proxy and performs TLS end-to-end with the origin. `Proxy-Authorization` is sent only on the CONNECT request, never on the wrapped TLS request. `httpsAgent` TLS options such as `ca`, `cert`, `key`, and `rejectUnauthorized` are forwarded to the generated tunneling agent so they still apply to the origin TLS connection. If you supply an `HttpsProxyAgent`, axios leaves tunneling to that agent. ```js proxy: { protocol: "https", host: "127.0.0.1", hostname: "localhost", // Takes precedence over "host" if both are defined port: 9000, auth: { username: "mikeymike", password: "rapunz3l" } }, ``` ### `cancelToken` The `cancelToken` property allows you to create a cancel token that can be used to cancel the request. For more information, see the [cancellation](/pages/advanced/cancellation) documentation. ### `signal` The `signal` property allows you to pass an instance of `AbortSignal` to the request. This allows you to cancel the request using the `AbortController` API. ### `decompress` The `decompress` property indicates whether or not to automatically decompress the response data. The default value is `true`. The Node.js HTTP adapter supports gzip, deflate, brotli, and zstd when the current Node.js runtime provides the corresponding zlib decompressor. ### `insecureHTTPParser` Indicates where to use an insecure HTTP parser that accepts invalid HTTP headers. This may allow interoperability with non-conformant HTTP implementations. Using the insecure parser should be avoided. Please note that the `insecureHTTPParser` option is only available in Node.js 12.10.0 and later. Please read the [Node.js documentation](https://nodejs.org/en/blog/vulnerability/february-2020-security-releases/#strict-http-header-parsing-none) for more information. See the full set of options [here](https://nodejs.org/dist/latest-v12.x/docs/api/http.html#http_http_request_url_options_callback) ### `transitional` The `transitional` property allows you to enable or disable certain transitional features. The following options are available: - `silentJSONParsing`: If set to `true` _(default)_, axios silently ignores JSON parsing errors and sets `response.data` to `null` when parsing fails. Set to `false` to throw `SyntaxError` instead. ::: tip Important This option only takes effect when `responseType` is **explicitly** set to `'json'`. When `responseType` is omitted, axios uses `forcedJSONParsing` to attempt JSON parsing and silently returns the raw string on failure regardless of this setting. To make invalid JSON throw, set both: ```js { responseType: 'json', transitional: { silentJSONParsing: false } } ``` ::: - `forcedJSONParsing`: Forces axios to parse the response string as JSON even if `responseType` is not `'json'`. - `clarifyTimeoutError`: Clarifies the error message when a request times out. This is useful when you are debugging timeout issues. - `validateStatusUndefinedResolves`: If set to `true` _(default)_, explicit `validateStatus: undefined` resolves every response status for backward compatibility. Set to `false` to treat explicit `undefined` as if `validateStatus` was omitted, so axios uses the configured/default validator. Use `validateStatus: null` or a validator that returns `true` when you intentionally want all statuses to resolve. - `advertiseZstdAcceptEncoding`: When set to `true`, axios adds `zstd` to the default `Accept-Encoding` request header when the current Node.js runtime supports zstd decompression. zstd responses are still decompressed automatically when supported and `decompress` is `true`. - `legacyInterceptorReqResOrdering`: When set to true we will use the legacy interceptor request/response ordering. ### `env` The `env` property allows you to set some configuration options. For example the FormData class which is used to automatically serialize the payload into a FormData object. - FormData: window?.FormData || global?.FormData ### `formSerializer` The `formSerializer` option allows you to configure how plain objects are serialized to `multipart/form-data` when used as request `data`. Available options: - `visitor` — custom visitor function called recursively for each value - `dots` — use dot notation instead of bracket notation - `metaTokens` — preserve special key endings such as `{}` - `indexes` — control bracket format for array keys (`null` / `false` / `true`) - `maxDepth` _(default: `100`)_ — maximum nesting depth before throwing `AxiosError` with code `ERR_FORM_DATA_DEPTH_EXCEEDED`. Set to `Infinity` to disable. - `Blob` — Blob constructor used when converting ArrayBuffer-like values for spec-compliant `FormData`. See the [multipart/form-data](/pages/advanced/multipart-form-data-format) page for full details, and the full request config example at the end of this page. ### `maxRate` The `maxRate` property defines the maximum **bandwidth** (in bytes per second) for upload and/or download. It accepts either a single number (applied to both directions) or a two-element array `[uploadRate, downloadRate]` where each element is a byte-per-second limit. For example, `100 * 1024` means 100 KB/s. See [Rate limiting](/pages/advanced/rate-limiting) for examples. ## Full request config example ```js { url: "/posts", method: "get", baseURL: "https://jsonplaceholder.typicode.com", allowAbsoluteUrls: true, transformRequest: [function (data, headers) { return data; }], transformResponse: [function (data) { return data; }], headers: {"X-Requested-With": "XMLHttpRequest"}, params: { postId: 5 }, paramsSerializer: { // Custom encoder function which sends key/value pairs in an iterative fashion. encode?: (param: string): string => { /* Do custom operations here and return transformed string */ }, // Custom serializer function for the entire parameter. Allows user to mimic pre 1.x behaviour. serialize?: (params: Record, options?: ParamsSerializerOptions ), // Configuration for formatting array indexes in the params. // Three available options: // (1) indexes: null (leads to no brackets) // (2) (default) indexes: false (leads to empty brackets) // (3) indexes: true (leads to brackets with indexes). indexes: false, // Maximum object nesting depth when serializing params. Throws AxiosError // (ERR_FORM_DATA_DEPTH_EXCEEDED) if exceeded. Default: 100. Set to Infinity to disable. maxDepth: 100 }, data: { firstName: "Fred" }, // `data` is request-specific: axios does not inherit or deep-merge it from defaults. // To add shared body fields, use a request interceptor or transformRequest. formDataHeaderPolicy: "legacy", // Syntax alternative to send data into the body method post only the value is sent, not the key data: "Country=Brasil&City=Belo Horizonte", timeout: 1000, withCredentials: false, adapter: function (config) { // Do whatever you want }, adapter: "xhr", auth: { username: "janedoe", password: "s00pers3cret" }, responseType: "json", responseEncoding: "utf8", xsrfCookieName: "XSRF-TOKEN", xsrfHeaderName: "X-XSRF-TOKEN", withXSRFToken: boolean | undefined | ((config: InternalAxiosRequestConfig) => boolean | undefined), onUploadProgress: function ({loaded, total, progress, bytes, estimated, rate, upload = true}) { // Do whatever you want with the Axios progress event }, onDownloadProgress: function ({loaded, total, progress, bytes, estimated, rate, download = true}) { // Do whatever you want with the Axios progress event }, maxContentLength: 2000, maxBodyLength: 2000, redact: ['authorization', 'password'], validateStatus: function (status) { return status >= 200 && status < 300; }, maxRedirects: 21, sensitiveHeaders: ['X-API-Key'], beforeRedirect: (options, { headers }) => { if (options.hostname === "typicode.com") { options.auth = "user:password"; } }, socketPath: null, allowedSocketPaths: null, transport: undefined, httpAgent: new http.Agent({ keepAlive: true }), httpsAgent: new https.Agent({ keepAlive: true }), proxy: { protocol: "https", host: "127.0.0.1", // hostname: "127.0.0.1" // Takes precedence over "host" if both are defined port: 9000, auth: { username: "mikeymike", password: "rapunz3l" } }, cancelToken: new CancelToken(function (cancel) { cancel("Operation has been canceled."); }), signal: new AbortController().signal, decompress: true, insecureHTTPParser: undefined, transitional: { silentJSONParsing: true, forcedJSONParsing: true, clarifyTimeoutError: false, validateStatusUndefinedResolves: true, advertiseZstdAcceptEncoding: false, legacyInterceptorReqResOrdering: true, }, env: { FormData: window?.FormData || global?.FormData }, formSerializer: { // Custom visitor function to serialize form values visitor: (value, key, path, helpers) => {}; // Use dots instead of brackets format dots: boolean; // Keep special endings like {} in parameter key metaTokens: boolean; // Use array indexes format: // null - no brackets // false - empty brackets // true - brackets with indexes indexes: boolean; // Maximum object nesting depth. Throws AxiosError (ERR_FORM_DATA_DEPTH_EXCEEDED) // if exceeded. Default: 100. Set to Infinity to disable. maxDepth: 100; }, maxRate: [ 100 * 1024, // 100KB/s upload limit, 100 * 1024 // 100KB/s download limit ] } ``` axios-axios-aa54a5e/docs/pages/advanced/request-method-aliases.md000066400000000000000000000152461526660636600252000ustar00rootroot00000000000000# Request aliases axios provides a set of aliases for making HTTP requests. These aliases are shortcuts for making requests using the `request` method. The aliases are designed to be easy to use and to provide a more convenient way to make requests. axios endeavours to follow RFC 7231 and RFC 5789, as closely as possible. The aliases are designed to be consistent with the HTTP methods defined in these RFCs. ### `axios` axios can be used to make HTTP request by passing only the config object. The full config object is documented [here](/pages/advanced/request-config) ```ts axios( url: string | AxiosRequestConfig, config?: AxiosRequestConfig ): Promise; ``` Request methods use generic parameters in the order ``: response data, custom response, request data, and query params. The params type `P` is last so existing explicit `T`, `R`, and `D` arguments retain their meaning. When you do not supply a custom `R`, the default `AxiosResponse` preserves `D` and `P` on `response.config`. ## Method aliases The following aliases are available for making requests: ### `request` The `request` method is the main method that you will use to make HTTP requests. It takes a configuration object as an argument and returns a promise that resolves to the response object. The `request` method is a generic method that can be used to make any type of HTTP request. ```ts axios.request(config: AxiosRequestConfig): Promise; ``` ### `get` The `get` method is used to make a GET request. It takes a URL and an optional configuration object as arguments and returns a promise that resolves to the response object. ```ts axios.get(url: string, config?: AxiosRequestConfig): Promise; ``` ### `delete` The `delete` method is used to make a DELETE request. It takes a URL and an optional configuration object as arguments and returns a promise that resolves to the response object. ```ts axios.delete(url: string, config?: AxiosRequestConfig): Promise; ``` ### `head` The `head` method is used to make a HEAD request. It takes a URL and an optional configuration object as arguments and returns a promise that resolves to the response object. ```ts axios.head(url: string, config?: AxiosRequestConfig): Promise; ``` ### `options` The `options` method is used to make an OPTIONS request. It takes a URL and an optional configuration object as arguments and returns a promise that resolves to the response object. ```ts axios.options(url: string, config?: AxiosRequestConfig): Promise; ``` ### `post` The `post` method is used to make a POST request. It takes a URL, an optional data object, and an optional configuration object as arguments and returns a promise that resolves to the response object. ```ts axios.post( url: string, data?: D, config?: AxiosRequestConfig ): Promise; ``` ### `put` The `put` method is used to make a PUT request. It takes a URL, an optional data object, and an optional configuration object as arguments and returns a promise that resolves to the response object. ```ts axios.put( url: string, data?: D, config?: AxiosRequestConfig ): Promise; ``` ### `patch` The `patch` method is used to make a PATCH request. It takes a URL, an optional data object, and an optional configuration object as arguments and returns a promise that resolves to the response object. ```ts axios.patch( url: string, data?: D, config?: AxiosRequestConfig ): Promise; ``` ### `query` The `query` method is used to make a QUERY request, a safe and idempotent method that carries a body. It takes a URL, an optional data object, and an optional configuration object as arguments and returns a promise that resolves to the response object. Use it for read-style operations whose parameters are too complex or sensitive to fit in the URL. ```ts axios.query( url: string, data?: D, config?: AxiosRequestConfig ): Promise; ``` ```js // Send a complex search filter as a request body const { data } = await axios.query("/api/search", { selector: ["name", "email"], filter: { active: true, role: "admin" }, }); ``` ::: warning Draft specification The QUERY method is defined by an IETF [Internet-Draft](https://datatracker.ietf.org/doc/draft-ietf-httpbis-safe-method-w-body/) and has not yet been standardized. Semantics and the method name itself may change before final publication, and server, proxy, and CDN support is uneven. Verify your stack accepts `QUERY` end to end before relying on it in production. ::: ### `getUri` The `getUri` method returns the URL that would be sent for a given config without actually making the request. It applies `baseURL`, `paramsSerializer`, and `params`, so you get back the same string axios would put on the wire. Useful for building links, debugging serialization, or reusing the resolved URL in another request. ```ts axios.getUri(config?: AxiosRequestConfig): string; ``` ```js const url = axios.getUri({ url: "/users", baseURL: "https://api.example.com", params: { active: true, role: "admin" }, }); // "https://api.example.com/users?active=true&role=admin" ``` ::: tip Use `getUri` on an instance (`instance.getUri(config)`) to inherit the instance's `baseURL`, `params`, and `paramsSerializer` defaults. ::: ## Form data shorthand methods These methods are equivalent to their counterparts above, but preset `Content-Type` to `multipart/form-data`. They are the recommended way to upload files or submit HTML forms. ### `postForm` ```ts axios.postForm( url: string, data?: D, config?: AxiosRequestConfig ): Promise; ``` ```js // Upload a file from a browser file input await axios.postForm("/api/upload", { file: document.querySelector("#fileInput").files[0], description: "Profile photo", }); ``` ### `putForm` ```ts axios.putForm( url: string, data?: D, config?: AxiosRequestConfig ): Promise; ``` ```js // Replace a resource with form data await axios.putForm("/api/users/1/avatar", { avatar: document.querySelector("#avatarInput").files[0], }); ``` ### `patchForm` ```ts axios.patchForm( url: string, data?: D, config?: AxiosRequestConfig ): Promise; ``` ```js // Update specific fields using form data await axios.patchForm("/api/users/1", { displayName: "New Name", avatar: document.querySelector("#avatarInput").files[0], }); ``` ::: tip `postForm`, `putForm`, and `patchForm` accept all the same data types as their base methods — plain objects, `FormData`, `FileList`, and `HTMLFormElement`. See [File posting](/pages/advanced/file-posting) for more examples. ::: axios-axios-aa54a5e/docs/pages/advanced/response-schema.md000066400000000000000000000046421526660636600237050ustar00rootroot00000000000000# Response schema Every axios request resolves to a response object with the following shape. The schema is consistent across both browser and Node.js environments. ```js { // The response data provided by the server. // When using `transformResponse`, this will be the result of the last transform. data: {}, // The HTTP status code from the server response (e.g. 200, 404, 500). status: 200, // The HTTP status message matching the status code (e.g. "OK", "Not Found"). statusText: "OK", // The response headers sent by the server. // Header names are lower-cased. You can access them using bracket or dot notation. headers: {}, // The axios config that was used for this request, including baseURL, // headers, timeout, params, and any other options you provided. config: {}, // The underlying request object. // In Node.js: the last `http.ClientRequest` instance (after any redirects). // In the browser: the `XMLHttpRequest` instance. request: {}, } ``` In TypeScript, `AxiosResponse` types the response data (`T`), request data (`D`), response headers (`H`), and query params (`P`). The config retains both request-side types: ```ts interface AxiosResponse { data: T; status: number; statusText: string; headers: (H & RawAxiosResponseHeaders) | AxiosResponseHeaders; config: InternalAxiosRequestConfig; request?: any; } ``` ## Accessing response fields In practice you will usually destructure just the parts you need: ```js const { data, status, headers } = await axios.get("/api/users/1"); console.log(status); // 200 console.log(headers["content-type"]); // "application/json; charset=utf-8" console.log(data); // { id: 1, name: "Jay", email: "jay@example.com" } ``` ## Checking the status code axios resolves the promise for any 2xx response and rejects for anything outside that range by default. You can customise this with the `validateStatus` config option: ```js const response = await axios.get("/api/resource", { validateStatus: (status) => status < 500, // resolve for anything below 500 }); ``` ## Accessing response headers All response header names are lower-cased, regardless of how the server sent them: ```js const response = await axios.get("/api/resource"); // These are equivalent const contentType = response.headers["content-type"]; const contentType2 = response.headers.get("content-type"); ``` axios-axios-aa54a5e/docs/pages/advanced/retry.md000066400000000000000000000071201526660636600217500ustar00rootroot00000000000000# Retry and error recovery Network requests can fail for transient reasons — a server blip, a brief network drop, or a rate-limit response. Implementing a retry strategy in an interceptor lets you handle these failures transparently, without cluttering your application code. ## Basic retry with a response interceptor The simplest approach is to catch specific error status codes and immediately re-send the original request a limited number of times: ```js import axios from "axios"; const api = axios.create({ baseURL: "https://api.example.com" }); const MAX_RETRIES = 3; api.interceptors.response.use( (response) => response, async (error) => { const config = error.config; // Only retry on network errors or 5xx server errors const shouldRetry = !error.response || (error.response.status >= 500 && error.response.status < 600); if (!shouldRetry) { return Promise.reject(error); } config._retryCount = config._retryCount ?? 0; if (config._retryCount >= MAX_RETRIES) { return Promise.reject(error); } config._retryCount += 1; return api(config); } ); ``` ## Exponential backoff Retrying immediately after a failure can overload an already-struggling server. Exponential backoff waits progressively longer between each attempt: ```js const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); api.interceptors.response.use( (response) => response, async (error) => { const config = error.config; const shouldRetry = !error.response || (error.response.status >= 500 && error.response.status < 600); if (!shouldRetry) return Promise.reject(error); config._retryCount = config._retryCount ?? 0; if (config._retryCount >= 3) return Promise.reject(error); config._retryCount += 1; // Wait 200ms, 400ms, 800ms, ... before each retry const backoff = 100 * 2 ** config._retryCount; await delay(backoff); return api(config); } ); ``` ## Retrying on 429 (rate limit) with Retry-After When the server responds with `429 Too Many Requests`, it often includes a `Retry-After` header telling you exactly how long to wait: ```js api.interceptors.response.use( (response) => response, async (error) => { const config = error.config; if (error.response?.status !== 429) return Promise.reject(error); config._retryCount = config._retryCount ?? 0; if (config._retryCount >= 3) return Promise.reject(error); config._retryCount += 1; const retryAfterHeader = error.response.headers["retry-after"]; const waitMs = retryAfterHeader ? parseFloat(retryAfterHeader) * 1000 // header is in seconds : 1000; // default to 1 second await new Promise((resolve) => setTimeout(resolve, waitMs)); return api(config); } ); ``` ## Opting out of retries per request If some requests should never be retried (e.g. non-idempotent mutations you don't want to duplicate), add a flag to the request config: ```js // Add this to your interceptor before the retry logic: if (config._noRetry) return Promise.reject(error); // Then opt out on specific calls: await api.post("/payments/charge", body, { _noRetry: true }); ``` ## Combining retry with cancellation Use an `AbortController` to cancel a request that is waiting for a backoff delay: ```js const controller = new AbortController(); try { await api.get("/api/data", { signal: controller.signal }); } catch (error) { if (axios.isCancel(error)) { console.log("Request aborted by user"); } } // Cancel the request (and any pending retry delay) from elsewhere: controller.abort(); ``` axios-axios-aa54a5e/docs/pages/advanced/testing.md000066400000000000000000000077101526660636600222650ustar00rootroot00000000000000# Testing Testing code that makes HTTP requests with axios is straightforward. The recommended approach is to mock axios itself so that your tests run without hitting a real network, giving you full control over what responses your code receives. ## Mocking with Vitest or Jest Both Vitest and Jest support module mocking with `vi.mock` / `jest.mock`. You can mock the entire axios module and control what each method returns: ```js // user-service.js import axios from "axios"; export async function getUser(id) { const { data } = await axios.get(`/api/users/${id}`); return data; } ``` ```js // user-service.test.js import { describe, it, expect, vi } from "vitest"; import axios from "axios"; import { getUser } from "./user-service"; vi.mock("axios"); describe("getUser", () => { it("returns user data on success", async () => { const mockUser = { id: 1, name: "Jay" }; // Make axios.get resolve with our fake response axios.get.mockResolvedValueOnce({ data: mockUser }); const result = await getUser(1); expect(result).toEqual(mockUser); expect(axios.get).toHaveBeenCalledWith("/api/users/1"); }); it("throws when the request fails", async () => { axios.get.mockRejectedValueOnce(new Error("Network error")); await expect(getUser(1)).rejects.toThrow("Network error"); }); }); ``` ## Mocking an AxiosError To test error-handling paths that inspect `error.response`, create an `AxiosError` instance directly: ```js import axios, { AxiosError } from "axios"; import { vi } from "vitest"; const mockError = new AxiosError( "Not Found", "ERR_BAD_REQUEST", {}, // config {}, // request { // response status: 404, statusText: "Not Found", data: { message: "User not found" }, headers: {}, config: {}, } ); axios.get.mockRejectedValueOnce(mockError); ``` ## Using axios-mock-adapter [axios-mock-adapter](https://github.com/ctimmerm/axios-mock-adapter) is a library that installs a custom adapter on your axios instance, intercepting requests at the adapter level. This means your interceptors still run, making it better for integration tests. ```bash npm install --save-dev axios-mock-adapter ``` ```js import axios from "axios"; import MockAdapter from "axios-mock-adapter"; const mock = new MockAdapter(axios); // Mock a GET request mock.onGet("/api/users/1").reply(200, { id: 1, name: "Jay" }); // Mock a POST request mock.onPost("/api/users").reply(201, { id: 2, name: "New User" }); // Mock a network error mock.onGet("/api/failing").networkError(); // Mock a timeout mock.onGet("/api/slow").timeout(); ``` Reset mocks between tests: ```js afterEach(() => { mock.reset(); // clear all registered handlers }); ``` ## Testing interceptors To test interceptors in isolation, create a fresh axios instance in your test: ```js import axios from "axios"; import MockAdapter from "axios-mock-adapter"; describe("auth interceptor", () => { it("attaches a Bearer token to every request", async () => { const instance = axios.create(); const mock = new MockAdapter(instance); // Add your interceptor instance.interceptors.request.use((config) => { config.headers.set("Authorization", "Bearer test-token"); return config; }); // Capture the request config by inspecting what mock received let capturedConfig; mock.onGet("/api/data").reply((config) => { capturedConfig = config; return [200, {}]; }); await instance.get("/api/data"); expect(capturedConfig.headers["Authorization"]).toBe("Bearer test-token"); }); }); ``` ## Tips - Always mock at the module level (or use `MockAdapter`) — avoid mocking individual methods on a shared instance, as state can leak between tests. - Use `mockResolvedValueOnce` / `mockRejectedValueOnce` in preference to `mockResolvedValue` so that tests are isolated and don't affect one another. - When testing retry logic, use `MockAdapter` so that the interceptor under test actually runs on each attempt. axios-axios-aa54a5e/docs/pages/advanced/type-script.md000066400000000000000000000127521526660636600230750ustar00rootroot00000000000000# TypeScript `axios` ships TypeScript definitions in the npm package via `index.d.ts` (ESM) and `index.d.cts` (CJS), so type checking and editor support work out of the box for both module formats. ## Module resolution caveats Because axios dual-publishes with an ESM default export and a CJS `module.exports`, there are a few configuration caveats: - The recommended setting is `"moduleResolution": "node16"` (implied by `"module": "node16"`). This requires TypeScript 4.7 or greater. - If you use ESM, your settings should be fine. - If you compile TypeScript to CJS and you can't use `"moduleResolution": "node16"`, you must enable `esModuleInterop`. - If you use TypeScript to type-check CJS JavaScript code, your only option is `"moduleResolution": "node16"`. ## Type guards for axios errors Use the `axios.isAxiosError` type guard to safely narrow `unknown` errors in `catch` blocks. After narrowing, you can access axios-specific properties like `error.response`, `error.config`, and `error.code` with full type safety. ```ts import axios from "axios"; let user: User | null = null; try { const { data } = await axios.get("/user?ID=12345"); user = data.userDetails; } catch (error) { if (axios.isAxiosError(error)) { handleAxiosError(error); } else { handleUnexpectedError(error); } } ``` Use `axios.isCancel()` to narrow cancellation errors to `CanceledError`: ```ts const controller = new AbortController(); try { await axios.get("/user?ID=12345", { signal: controller.signal }); } catch (error) { if (axios.isCancel(error)) { handleCancellation(error); } } ``` ## Typing request data and query params `AxiosRequestConfig` uses `D` for request data and `P` for query params. A custom params serializer receives the same `P`: ```ts import axios, { type AxiosPromise, type AxiosRequestConfig, type InternalAxiosRequestConfig, } from "axios"; interface RequestBody { includeArchived: boolean; } interface SearchParams { query: string; page?: number; } interface SearchResponse { results: string[]; } const searchConfig: AxiosRequestConfig = { data: { includeArchived: false }, params: { query: "axios", page: 1 }, paramsSerializer: (params) => `${params.query}:${params.page ?? 1}`, }; const response = await axios.get("/search", searchConfig); response.config.data; // RequestBody | undefined response.config.params; // SearchParams | undefined const invalidConfig: AxiosRequestConfig = { // @ts-expect-error `query` must be a string params: { query: 123 }, }; ``` Default request results preserve `D` and `P` on `response.config`, including when request aliases infer those types from a typed request config. `RawAxiosRequestConfig`, `InternalAxiosRequestConfig`, `AxiosDefaults`, `CreateAxiosDefaults`, `AxiosResponse`, `AxiosPromise`, `AxiosError`, `CanceledError`, callable instances, adapters, and `mergeConfig()` carry the params type as well. Request methods add `P` as the final generic—``—so the existing response data (`T`), custom response (`R`), and request data (`D`) positions remain unchanged. An explicitly supplied custom response type still controls the resolved value. `P` defaults to `any` for backward compatibility. An adapter or another explicitly typed promise can preserve both request types: ```ts const searchAdapter = ( config: InternalAxiosRequestConfig ): AxiosPromise => Promise.resolve({ data: { results: [] }, status: 200, statusText: "OK", headers: {}, config, }); declare const error: unknown; if (axios.isCancel(error)) { error.config?.data; // RequestBody | undefined error.config?.params; // SearchParams | undefined } ``` ## Typed instances and interceptors Annotate the result of `axios.create` with `AxiosInstance`, and annotate request interceptors with `InternalAxiosRequestConfig` to get end-to-end type checking on a custom client: ```ts import axios, { AxiosInstance, InternalAxiosRequestConfig } from "axios"; const apiClient: AxiosInstance = axios.create({ baseURL: "https://api.example.com", timeout: 10000, }); apiClient.interceptors.request.use((config: InternalAxiosRequestConfig) => { // Add auth token, log, etc. return config; }); ``` ## Symbol-keyed custom request config axios preserves own enumerable symbol properties when it merges defaults and per-request config. Applications can module-augment `AxiosRequestConfig` with a specific symbol key and read the option from `InternalAxiosRequestConfig` in an interceptor or adapter: ```ts import axios from "axios"; export const someFlag: unique symbol = Symbol( "some flag used in request interceptor" ); declare module "axios" { interface AxiosRequestConfig { [someFlag]?: boolean; } } axios.interceptors.request.use((config) => { if (config[someFlag]) { config.headers.set("X-Some-Flag", "enabled"); } return config; }); await axios.get("/users", { [someFlag]: true }); ``` Only own enumerable symbol properties are copied. Non-enumerable and inherited symbol properties are not. ## Typing response data Axios request methods are generic over the response data type. Pass a type parameter to `axios.get` (and the other aliases) to type `response.data`: ```ts interface User { id: number; name: string; } const { data } = await apiClient.get("/users/1"); // `data` is typed as `User` ``` axios-axios-aa54a5e/docs/pages/advanced/x-www-form-urlencoded-format.md000066400000000000000000000106041526660636600262460ustar00rootroot00000000000000# x-www-form-urlencoded format ## URLSearchParams By default, axios serializes JavaScript objects to `JSON`. To send data in the [`application/x-www-form-urlencoded` format](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) instead, you can use the [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) API, which is [supported](http://www.caniuse.com/#feat=urlsearchparams) in the vast majority of browsers, and [Node](https://nodejs.org/api/url.html#url_class_urlsearchparams) starting with v10 (released in 2018). ```js const params = new URLSearchParams({ foo: 'bar' }); params.append('extraparam', 'value'); axios.post('/foo', params); ``` ## Query string For older browsers or environments without `URLSearchParams`, you can use the [`qs`](https://github.com/ljharb/qs) library to serialize objects to the `application/x-www-form-urlencoded` format. ```js const qs = require('qs'); axios.post('/foo', qs.stringify({ bar: 123 })); ``` For full control over headers and method, pass `qs.stringify` output as the request `data` and set `Content-Type` explicitly: ```js import qs from 'qs'; const data = { bar: 123 }; const options = { method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, data: qs.stringify(data), url: '/foo', }; axios(options); ``` In very old versions of Node.js, you can use the built-in `querystring` module that ships with Node.js. Note that this module has been deprecated in Node.js v16 — prefer `URLSearchParams` or `qs` for new code. ```js const querystring = require('querystring'); axios.post('https://something.com/', querystring.stringify({ foo: 'bar' })); ``` ::: tip Prefer `qs` for nested objects The `qs` library is preferable if you need to stringify nested objects, as the `querystring` method has [known issues](https://github.com/nodejs/node-v0.x-archive/issues/1665) with that use case. ::: ## Automatic serialization to URLSearchParams Starting from v0.21.0, axios automatically serializes JavaScript objects to `URLSearchParams` if the `Content-Type` header is set to `application/x-www-form-urlencoded`. This means that you can pass a JavaScript object directly to the `data` property of the axios request config. For example when passing data to a `POST` request: ```js const data = { x: 1, arr: [1, 2, 3], arr2: [1, [2], 3], users: [ { name: 'Peter', surname: 'Griffin' }, { name: 'Thomas', surname: 'Anderson' }, ], }; await axios.postForm('https://postman-echo.com/post', data, { headers: { 'content-type': 'application/x-www-form-urlencoded' }, }); ``` The `data` object will be automatically serialized to `URLSearchParams` and sent in the `application/x-www-form-urlencoded` format. The server will receive the following data: ```json { "x": "1", "arr[]": ["1", "2", "3"], "arr2[0]": "1", "arr2[1][0]": "2", "arr2[2]": "3", "users[0][name]": "Peter", "users[0][surname]": "Griffin", "users[1][name]": "Thomas", "users[1][surname]": "Anderson" } ``` If your backend body-parser (like `body-parser` of `express.js`) supports nested objects decoding, you will get the same object on the server-side automatically ## Depth limit for params serialization When axios serializes a `params` object via `AxiosURLSearchParams`, the same recursive walker used by the FormData serializer is called. A `maxDepth` option (default `100`) limits how deeply it will recurse. Payloads exceeding the limit throw an `AxiosError` with `code: 'ERR_FORM_DATA_DEPTH_EXCEEDED'` instead of overflowing the call stack. The shared `SerializerOptions.Blob` type member only affects serialization into spec-compliant `FormData`; it has no effect when serializing to `URLSearchParams`. ```js // Raise the limit if your params object legitimately nests deeper than 100 levels: axios.get('/api', { params: deepObject, paramsSerializer: { maxDepth: 200 } }); ``` ::: warning Security note Only raise `maxDepth` if your schema genuinely requires it. The default of 100 protects server-side code that forwards client-controlled data to axios as `params` from DoS attacks via deeply nested objects. ::: ```js var app = express(); app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies app.post('/', function (req, res, next) { // echo body as JSON res.send(JSON.stringify(req.body)); }); server = app.listen(3000); ``` axios-axios-aa54a5e/docs/pages/getting-started/000077500000000000000000000000001526660636600216215ustar00rootroot00000000000000axios-axios-aa54a5e/docs/pages/getting-started/examples/000077500000000000000000000000001526660636600234375ustar00rootroot00000000000000axios-axios-aa54a5e/docs/pages/getting-started/examples/commonjs.md000066400000000000000000000102771526660636600256150ustar00rootroot00000000000000# JavaScript examples ## Importing the library To import the library in a CommonJS environment, you can use the `require` function, or the `import` statement if you are using a bundler like Webpack or Rollup. #### No bundler ```js const axios = require("axios"); ``` #### With bundler (webpack, rollup, vite, etc) ```js import axios from "axios"; ``` ## Using then/catch/finally Since axios returns a promise at it's core you can choose to use callbacks with `then`, `catch`, and `finally` to handle your response data, errors, and completion. ### Get request ```js axios .get("https://jsonplaceholder.typicode.com/posts", { params: { postId: 5, }, }) .then((response) => { console.log(response.data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Request completed"); }); ``` ### Post request ```js axios .post("https://jsonplaceholder.typicode.com/posts", { title: "foo", body: "bar", userId: 1, }) .then((response) => { console.log(response.data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Request completed"); }); ``` ### Put request ```js axios .put("https://jsonplaceholder.typicode.com/posts/1", { title: "foo", body: "bar", userId: 1, }) .then((response) => { console.log(response.data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Request completed"); }); ``` ### Patch request ```js axios .patch("https://jsonplaceholder.typicode.com/posts/1", { title: "foo", }) .then((response) => { console.log(response.data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Request completed"); }); ``` ### Delete request ```js axios .delete("https://jsonplaceholder.typicode.com/posts/1") .then((response) => { console.log(response.data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Request completed"); }); ``` ## Using async/await Another way to handle promises is by using `async` and `await`. This allows you to use try/catch/finally blocks to handle errors and completion. This can make your code more readable and easier to understand, this also helps prevents so called callback hell. ::: tip Note: async/await is part of ECMAScript 2017 and is not supported in Internet Explorer and older browsers, so use with caution. ::: ### Get request ```js const getPosts = async () => { try { const response = await axios.get( "https://jsonplaceholder.typicode.com/posts", { params: { postId: 5, }, } ); console.log(response.data); } catch (error) { console.error(error); } finally { console.log("Request completed"); } }; ``` ### Post request ```js const createPost = async () => { try { const response = await axios.post( "https://jsonplaceholder.typicode.com/posts", { title: "foo", body: "bar", userId: 1, } ); console.log(response.data); } catch (error) { console.error(error); } finally { console.log("Request completed"); } }; ``` ### Put request ```js const updatePost = async () => { try { const response = await axios.put( "https://jsonplaceholder.typicode.com/posts/1", { title: "foo", body: "bar", userId: 1, } ); console.log(response.data); } catch (error) { console.error(error); } finally { console.log("Request completed"); } }; ``` ### Patch request ```js const updatePost = async () => { try { const response = await axios.patch( "https://jsonplaceholder.typicode.com/posts/1", { title: "foo", } ); console.log(response.data); } catch (error) { console.error(error); } finally { console.log("Request completed"); } }; ``` ### Delete request ```js const deletePost = async () => { try { const response = await axios.delete( "https://jsonplaceholder.typicode.com/posts/1" ); console.log(response.data); } catch (error) { console.error(error); } finally { console.log("Request completed"); } }; ``` axios-axios-aa54a5e/docs/pages/getting-started/examples/typescript.md000066400000000000000000000063641526660636600262000ustar00rootroot00000000000000# TypeScript example ## Importing types axios ships with TypeScript definitions out of the box. You can import the types you need directly from `"axios"`: ```ts import axios from "axios"; import type { AxiosRequestConfig, AxiosResponse, AxiosError } from "axios"; ``` ## Typing a request Use a generic type parameter on the response to tell TypeScript what shape your data will have: ```ts import axios from "axios"; type Post = { userId: number; id: number; title: string; body: string; }; const response = await axios.get("https://jsonplaceholder.typicode.com/posts/1"); console.log(response.data.title); // TypeScript knows this is a string ``` ## Typing a function Wrap requests in functions with explicit return types for maximum type safety: ```ts import axios, { AxiosResponse } from "axios"; type Post = { userId: number; id: number; title: string; body: string; }; const getPost = async (id: number): Promise => { const response = await axios.get( `https://jsonplaceholder.typicode.com/posts/${id}` ); return response.data; }; ``` ## Typing a POST request You can type both the request body and the expected response: ```ts type CreatePostBody = { title: string; body: string; userId: number; }; type CreatePostResponse = CreatePostBody & { id: number }; const createPost = async (data: CreatePostBody): Promise => { const response = await axios.post( "https://jsonplaceholder.typicode.com/posts", data ); return response.data; }; ``` ## Typed axios instance Create a typed instance so your base URL and headers are baked in: ```ts import axios from "axios"; import type { AxiosInstance } from "axios"; const api: AxiosInstance = axios.create({ baseURL: "https://api.example.com", timeout: 5000, }); ``` ## Typed interceptors Use `InternalAxiosRequestConfig` (not `AxiosRequestConfig`) for request interceptors in v1.x: ```ts import axios from "axios"; import type { InternalAxiosRequestConfig, AxiosResponse } from "axios"; api.interceptors.request.use((config: InternalAxiosRequestConfig) => { config.headers.set("Authorization", `Bearer ${getToken()}`); return config; }); api.interceptors.response.use( (response: AxiosResponse) => response, (error) => Promise.reject(error) ); ``` ## Typing errors Use `axios.isAxiosError()` to narrow the type of a caught error: ```ts import axios, { AxiosError } from "axios"; type ApiError = { message: string; code: number; }; try { await axios.get("/api/protected-resource"); } catch (error) { if (axios.isAxiosError(error)) { // error.response?.data is typed as ApiError console.error(error.response?.data.message); console.error(error.response?.status); } else { throw error; } } ``` ## TypeScript configuration notes Because axios dual-publishes ESM and CJS, there are a few caveats depending on your setup: - The recommended setting is `"moduleResolution": "node16"` (implied by `"module": "node16"`). This requires TypeScript 4.7 or greater. - If you compile TypeScript to CJS and cannot use `"moduleResolution": "node16"`, enable `"esModuleInterop": true`. - If you use TypeScript to type-check CJS JavaScript code, your only option is `"moduleResolution": "node16"`. axios-axios-aa54a5e/docs/pages/getting-started/features.md000066400000000000000000000050021526660636600237560ustar00rootroot00000000000000# Features axios is a powerful HTTP client that provides a simple and easy-to-use API for making HTTP requests. It supports all modern browsers and is widely used in the JavaScript community. Here are some of the features that make axios a great choice for your next project. ## Isomorphic axios is a universal HTTP client that can be used in both the browser and Node.js. This means you can use axios to make API requests from your frontend code as well as your backend code. This makes axios a great choice for building progressive web apps, single-page applications, and server-side rendered applications. axios is also a great choice for teams that work on both frontend and backend code. By using axios for both frontend and backend code, you can have a consistent API for making HTTP requests, which can help reduce the complexity of your codebase. ## Fetch support axios provides first class support for the Fetch API, which is a modern replacement for the XHR API. The adapter is optional and can be used through configuration. The same API is maintained for both the XHR and Fetch adapters, which makes it easy to adopt the Fetch API in your codebase without changing your existing code. ## Browser support axios supports all modern and select older browsers, including Chrome, Firefox, Safari, and Edge. axios is a great choice for building web applications that need to support a wide range of browsers. ## Node.js support axios also supports a wide range Node.js versions with tested compatibility as far back as v12.x, making it a good choice in environments where upgrading to the latest Node.js version might not be possible or practical. In addition to Node.js, axios has Bun and Deno smoke tests that validate key runtime behavior and improve confidence in cross-runtime compatibility. ## Additional features - Supports the Promise API - Intercept request and response - Transform request and response data - Abort controller - Timeouts - Query parameters serialization with support for nested entries - Automatic request body serialization to: - JSON (application/json) - Multipart / FormData (multipart/form-data) - URL encoded form (application/x-www-form-urlencoded) - Posting HTML forms as JSON - Automatic JSON data handling in response - Progress capturing for browsers and node.js with extra info (speed rate, remaining time) - Setting bandwidth limits for node.js - Compatible with spec-compliant FormData and Blob (including node.js) - Client side support for protecting against XSRF axios-axios-aa54a5e/docs/pages/getting-started/first-steps.md000066400000000000000000000073751526660636600244420ustar00rootroot00000000000000# First steps Welcome to the axios documentation! This guide will help you get started with axios and make your first API request. If you're new to axios, we recommend starting here. ## Installing You can use axios in your project in a few different ways. The most common way is to install it from npm and include it in your project. But we also support jsDelivr, unpkg, and more. #### Using npm ```bash npm install axios ``` #### Using pnpm ```bash pnpm install axios ``` #### Using yarn ```bash yarn add axios ``` #### Using bun ```bash bun add axios ``` #### Using deno ```bash deno install npm:axios ``` #### Using jsDelivr When using jsDelivr we recommend using the minified version as well as pinning the version number to avoid unexpected changes. If you would like to use the latest version you can do so by dropping the version number. This is strongly discouraged for production use as it can lead to unexpected changes in your application. ```html ``` #### Using unpkg When using unpkg we recommend using the minified version as well as pinning the version number to avoid unexpected changes. If you would like to use the latest version you can do so by dropping the version number. This is strongly discouraged for production use as it can lead to unexpected changes in your application. ```html ``` ## Importing axios Once installed, you can import the library using either `import` or `require`: ```js import axios, { isCancel, AxiosError } from "axios"; ``` You can also use the default export, since the named export is just a re-export from the axios factory: ```js import axios from "axios"; console.log(axios.isCancel("something")); ``` If you use `require` for importing, **only the default export is available**: ```js const axios = require("axios"); console.log(axios.isCancel("something")); ``` For some bundlers and ES6 linters you may need: ```js import { default as axios } from "axios"; ``` For custom or legacy environments where module resolution misbehaves, you can import the prebuilt bundle directly: ```js const axios = require("axios/dist/browser/axios.cjs"); // browser CommonJS bundle (ES2017) // const axios = require("axios/dist/node/axios.cjs"); // node CommonJS bundle (ES2017) ``` ## Making your first request An axios request can be made in as few as two lines of code. Making your first request with axios is very simple. You can make a request to any API by providing the URL and method. For example, to make a GET request to the JSONPlaceholder API, you can use the following code: ```js import axios from "axios"; const response = await axios.get( "https://jsonplaceholder.typicode.com/posts/1" ); console.log(response.data); ``` axios provides a simple API for making requests. You can use the `axios.get` method to make a GET request, the `axios.post` method to make a POST request, and so on. You can also use the `axios.request` method to make a request with any method. ::: tip Set a timeout in production Without a `timeout`, a stalled request can hang indefinitely. Pass one via the request config: ```js const response = await axios.get("https://example.com/data", { timeout: 5000, // 5 seconds }); ``` See [`timeout` in the request config](/pages/advanced/request-config#timeout) and [Error handling](/pages/advanced/error-handling) for the matching `ECONNABORTED` / `ETIMEDOUT` codes. ::: ## Next steps Now that you've made your first request with axios, you're ready to start exploring the rest of the axios documentation. You can learn more about making requests, handling responses, and using axios in your projects. Check out the rest of the documentation to learn more. axios-axios-aa54a5e/docs/pages/getting-started/upgrade-guide.md000066400000000000000000000116641526660636600246750ustar00rootroot00000000000000# Upgrade guide This guide is intended to help you upgrade your project from one version of the framework to another. It is recommended to read the release notes for each major version you are upgrading from/to, as they may contain important information about breaking changes. ## Upgrading to v1.19.0 ### Malformed HTTP(S) URLs are rejected Requests now reject an `http:` or `https:` `url` or `baseURL` that omits `//` after the protocol. Replace values such as `https:example.com` or `https:/example.com` with a well-formed URL such as `https://example.com`. The resulting `AxiosError` uses code `ERR_INVALID_URL` and identifies the safely redacted offending URL. This intentional security change prevents malformed-URL normalization from bypassing `baseURL` or URL allowlists. ### Synchronous request interceptor errors When a synchronous request interceptor throws, axios invokes its paired rejection handler and stops the remaining request interceptors. A rejection handler that returns normally handles the error and dispatches with the last valid config; its return value does not become the request config. If validation must block dispatch, omit the rejection handler or throw/return a rejected Promise from it. Terminal errors continue through response rejection interceptors. ## Upgrading from v0.x to v1.x ### Changes to the import statement In v1.x, the import statement has been changed to use the `default` export. This means that you will need to update your import statements to use the `default` export. ```diff - import { axios } from "axios"; + import axios from "axios"; ``` ### Changes to the interceptor system In v1.x you need to leverage the type `InternalAxiosRequestConfig` to type the `config` parameter in the `request` interceptor. This is because the `config` parameter is now typed as `InternalAxiosRequestConfig` instead of the public `AxiosRequestConfig` type. ```diff - axios.interceptors.request.use((config: AxiosRequestConfig) => { + axios.interceptors.request.use((config: InternalAxiosRequestConfig) => { return config; }); ``` ### Changes to request headers shape In v1.x, the shape of the request headers has been changed to drop the `common` property. This means that you will need to update your code to use the new shape of the request headers as follows: ```diff - if (request.headers?.common?.Authorization) { - request.headers.common.Authorization = ... + if (request.headers?.Authorization) { + request.headers.Authorization = ... ``` Default headers that were previously under `common`, `get`, `post`, etc. are now set directly on `axios.defaults.headers`: ```diff - axios.defaults.headers.common["Accept"] = "application/json"; + axios.defaults.headers["Accept"] = "application/json"; ``` ### Multipart form data If a request includes a `FormData` payload, the `Content-Type: multipart/form-data` header is now set automatically. Remove any manual header to avoid duplicates: ```diff - axios.post("/upload", formData, { - headers: { "Content-Type": "multipart/form-data" }, - }); + axios.post("/upload", formData); ``` If you explicitly set `Content-Type: application/json`, axios will now automatically serialize the data to JSON. ### Parameter serialization v1.x introduced several breaking changes to how URL parameters are serialized. The most important ones: **`params` are now percent-encoded by default.** If your backend expected raw brackets from qs-style encoding, you may need to configure a custom serializer: ```js import qs from 'qs'; axios.create({ paramsSerializer: { serialize: (params) => qs.stringify(params, { arrayFormat: 'brackets' }), }, }); ``` **Nested objects in `params` are now serialized with bracket notation** (`foo[bar]=1`) rather than dot notation. If your backend expected dot notation, use a custom serializer. **`null` and `undefined` params** are now handled consistently: `null` values are serialized as empty strings, while `undefined` values are omitted entirely. For the full parameter serialization config options, see the [Request config](/pages/advanced/request-config) page. ### Internals no longer exported We have elected to no longer export the internals of axios. This means that you will need to update your code to only use the public API of axios. This change was made to simplify the API and reduce the surface area of axios, allowing us to make changes to the internals without declaring them as breaking changes. Please review the [API reference](/pages/advanced/api-reference) on this site for the latest information on the public API of axios. ### Request config We have made changes to the request config object. Please review the [config reference](/pages/advanced/request-config) on this site for the latest information. ### Missed breaking changes This guide is not exhaustive and may not cover all breaking changes. Should you encounter any issue, please open an issue on the [docs GitHub repository](https://github.com/axios/docs) with the label `breaking change`. axios-axios-aa54a5e/docs/pages/misc/000077500000000000000000000000001526660636600174475ustar00rootroot00000000000000axios-axios-aa54a5e/docs/pages/misc/security.md000066400000000000000000000254421526660636600216470ustar00rootroot00000000000000# Security policy ## ⚠� Decompression bomb / unbounded response buffering By default, `maxContentLength` and `maxBodyLength` are set to `-1` (unlimited). A malicious or compromised server can return a small gzip/deflate/brotli/zstd-compressed body that expands to gigabytes, exhausting memory in the Node.js process. **If you make requests to servers you do not fully trust, you MUST set a `maxContentLength` (and `maxBodyLength`) suitable for your workload.** The limit is enforced chunk-by-chunk during streaming decompression, so setting it is sufficient to neutralize decompression-bomb attacks. ```js axios.get('https://example.com/data', { maxContentLength: 10 * 1024 * 1024, // 10 MB maxBodyLength: 10 * 1024 * 1024, }); // Or globally: axios.defaults.maxContentLength = 10 * 1024 * 1024; axios.defaults.maxBodyLength = 10 * 1024 * 1024; ``` The default was not tightened because doing so would silently break every legitimate download larger than the chosen cap. The responsibility to pick a safe ceiling for untrusted sources rests with the application. ## Other security-sensitive options The following request-config options have direct security implications. They are documented in full alongside the rest of the [request config](/pages/advanced/request-config), and summarised here so they show up in one place. | Option | Risk | Mitigation | | --- | --- | --- | | [`baseURL`](/pages/advanced/request-config#baseurl) | Applications sometimes treat a `baseURL` path prefix, such as `https://api.example.com/v1/`, as a request boundary. User-controlled relative `url` values can contain `..` segments that are normalized by the final URL parser and resolve outside that path prefix. | Do not rely on `baseURL` for path isolation. Validate untrusted request paths before passing them to axios; reject absolute or protocol-relative URLs and `..` segments, or check the resolved URL's origin and pathname against an allowlist. | | [`socketPath`](/pages/advanced/request-config#socketpath) | If derived from untrusted input, an attacker can redirect traffic to privileged local sockets like `/var/run/docker.sock`, bypassing hostname-based SSRF protections (CWE-918). | Strip or allowlist config keys from untrusted input. Use [`allowedSocketPaths`](/pages/advanced/request-config#allowedsocketpaths) to restrict accepted socket paths. | | [`beforeRedirect`](/pages/advanced/request-config#beforeredirect) | Runs after `follow-redirects` strips credentials on protocol downgrade. Re-injecting credentials without checking the destination protocol can leak them over plain HTTP. | Only re-add credentials for trusted HTTPS destinations. Check `options.protocol === "https:"` before assigning `auth`. | | [`sensitiveHeaders`](/pages/advanced/request-config#sensitiveheaders) | Custom secret headers such as `X-API-Key` can be forwarded by the Node.js HTTP adapter when following redirects to a different origin. | List custom secret-bearing header names in `sensitiveHeaders`; axios removes matching headers case-insensitively on cross-origin redirects. Same-origin redirects keep them. | | [`withXSRFToken`](/pages/advanced/request-config#withxsrftoken) | Setting `true` forces the XSRF header on cross-origin requests. Older axios versions implicitly enabled this with `withCredentials: true`; newer versions require both flags. | Leave at `undefined` (same-origin only) unless your backend explicitly validates XSRF on cross-origin requests. | | [`redact`](/pages/advanced/request-config#redact) | `AxiosError#toJSON()` includes the request config by default, which can leak `Authorization` headers or `auth` credentials into error logs and telemetry. | Pass a `redact` array with sensitive config key names. Matching is case-insensitive and recursive. | | [`formDataHeaderPolicy`](/pages/advanced/request-config#formdataheaderpolicy) | A custom `FormData` whose `getHeaders()` returns attacker-controlled values can overwrite headers like `Authorization` or inject arbitrary ones in Node.js. | Set `'content-only'` to copy only `Content-Type` and `Content-Length`, then set other headers explicitly via the request `headers` config. | ## Supply-chain hardening: `ignore-scripts` and lifecycle scripts The repository ships a project-level `.npmrc` that sets `ignore-scripts=true`. This blocks npm lifecycle scripts (`preinstall`, `install`, `postinstall`, `prepare`) from any direct or transitive dependency when running `npm install` or `npm ci` inside the repo. See [THREATMODEL.md](https://github.com/axios/axios/blob/v1.x/THREATMODEL.md) (threat T-S2) for the rationale. One consequence: the repository's own `prepare` hook (which installs Husky's git hooks) does **not** run automatically. After your first install, enable the git hooks manually: ```bash npm ci npm rebuild husky && npx husky ``` Run those two commands once per fresh checkout. You do **not** need to re-run them after every subsequent `npm install`. ::: danger Do not remove `ignore-scripts=true` Removing `ignore-scripts=true` from `.npmrc` to "fix" the husky setup re-opens the lifecycle-script attack surface for every other package in the tree. All CI workflows already invoke npm with `--ignore-scripts`, so local behaviour matches CI. ::: We recommend the same `ignore-scripts=true` setting in any consumer project that pulls axios (or any other dependency) into a build environment that handles secrets. ## Verifying a Release Every `axios` tarball on npm is published from GitHub Actions with an [npm provenance attestation](https://docs.npmjs.com/generating-provenance-statements) that cryptographically binds the package to the workflow and commit SHA that produced it. Consumers can verify provenance locally: ```bash # Verify every package in your lockfile, including axios npm audit signatures ``` A successful verification proves the tarball was built in `axios/axios`' GitHub Actions environment on a known commit — it was not tampered with between build and registry. It does **not** prove the code in that commit is free of bugs. If `npm audit signatures` reports a missing or invalid attestation for a recent `axios` version, treat it as a potential supply-chain incident and report via the private channel below. ## Reporting a Vulnerability If you believe you have found a security vulnerability in the project, please report it to us as described below. We take all security vulnerabilities seriously. If you have found a vulnerability in a third-party library, please report it to the maintainers of that library. ## Reporting Process Please do not report security vulnerabilities through public GitHub issues. Please use the official security channel on GitHub by logging a [security advisory](https://github.com/axios/axios/security/advisories/new). ## Disclosure Policy When we receive a security vulnerability report, we assign it a primary handler. The handler confirms the problem, determines affected versions, evaluates severity, develops and ships a fix, and coordinates public disclosure with the reporter. ### 60-day resolution and disclosure commitment We commit to **resolving and publicly disclosing every valid security advisory within 60 calendar days of the initial report**, measured from the moment a report is received via the [GitHub security advisory channel](https://github.com/axios/axios/security/advisories/new). The 60-day clock is a commitment to reporters and downstream consumers — a backstop, not an aspiration. If we cannot ship a fix in time, we still publish the advisory at day 60 with the best available mitigation guidance so consumers can act. **Milestones inside the 60-day window:** | Day | Milestone | | ---- | ---------------------------------------------------------------------------------------------------------------------------------------- | | 0 | Report received. Private advisory opened on GitHub. | | ≤ 3 | Acknowledgement sent to reporter. Triage decision: in scope / out of scope / duplicate / needs-info. | | ≤ 10 | Severity assessed (CVSS v4 where applicable). Affected versions confirmed. CVE requested via GitHub if a public identifier is warranted. | | ≤ 45 | Fix developed, reviewed, tested. Release candidate prepared on a private branch. Reporter offered a preview for validation. | | ≤ 60 | Patched release published to npm. Public advisory + CVE published. Reporter credited unless they request otherwise. CHANGELOG updated. | **Exceptions and extensions.** - If a reporter requests a shorter embargo (e.g. they plan to present findings at a conference), we accommodate where possible. - If a fix requires a breaking change, coordinating with major downstream consumers, or a `follow-redirects` / `form-data` / `proxy-from-env` upstream release, we may extend beyond 60 days. Any extension is disclosed publicly at day 60 via the advisory, with a revised ETA and the reason. - If a report is **out of scope** (e.g. falls under an explicit non-goal documented in the project's [threat model](https://github.com/axios/axios/blob/v1.x/THREATMODEL.md)), we close it with an explanation to the reporter within the triage window (≤ 3 days). Out-of-scope reports do not enter the 60-day queue. - **Actively exploited vulnerabilities** are treated as incidents: fix and advisory ship as soon as a patch is validated, not on the 60-day schedule. **Reporter expectations.** While a report is under embargo, we ask reporters to refrain from public disclosure until the earlier of: (a) the coordinated advisory publication, or (b) day 60. If the 60-day deadline passes without action from us, reporters are free to disclose independently — we will treat that as a failure on our part, not on theirs. ## Security Updates Security updates are released as soon as possible after the patch has been developed and tested. We notify users of the release via the project's GitHub repository and publish release notes and security advisories on the GitHub releases page. We also deprecate all versions that contain the vulnerability. ## Maintainer-side incident response For compromise scenarios affecting maintainer accounts, workstations, or release infrastructure (phishing, stolen hardware key, unexpected tag/publish), the project maintains an internal incident-response runbook in [THREATMODEL.md §3.7](https://github.com/axios/axios/blob/v1.x/THREATMODEL.md#37-incident-response-runbook). It covers session revocation, key rotation, downstream notification, and unpublish/deprecate procedures. ## Security Partners and Acknowledgements We would like to thank the following security researchers for working with us to help make the project safe for everyone: - [Socket Dev](https://socket.dev/) - [GitHub Security Lab](https://securitylab.github.com/) axios-axios-aa54a5e/docs/pages/misc/semver.md000066400000000000000000000045051526660636600212760ustar00rootroot00000000000000# Semantic versioning Semantic versioning is a versioning scheme that is used to communicate the nature of changes in a software package. It is a simple set of rules and requirements that dictate how version numbers are assigned and incremented. ## axios versioning axios follows the semantic versioning scheme. This means that each version of axios is assigned a version number that consists of three parts: major, minor, and patch. The version number is incremented based on the nature of the changes in the release. In the past axios may have at times not strictly followed semantic versioning, however going forward there will be a much stricter adherence to the semantic versioning scheme to ensure that users can rely on the version numbers to communicate the nature of changes in the library. A brief overview of the versioning scheme is provided below. ## Version format A semantic version number consists of three parts: 1. Major version 2. Minor version 3. Patch version The version number is written as `MAJOR.MINOR.PATCH`. Each part of the version number has a specific meaning: - **Major version**: Incremented when you make incompatible API changes. - **Minor version**: Incremented when you add functionality in a backwards-compatible manner. - **Patch version**: Incremented when you make backwards-compatible bug fixes. ## Pre-release versions In addition to the three parts of the version number, you can append a pre-release version. This is done by adding a hyphen and a series of dot-separated identifiers immediately following the patch version. For example, `1.0.0-alpha.1`. Pre-release versions are used to indicate that a version is unstable and might not satisfy the intended compatibility requirements as denoted by the version number. Pre-release versions are ordered based on the order of the identifiers. For example, `1.0.0-alpha.1` comes before `1.0.0-alpha.2`. ## Version ranges When you specify a version range for a package, you can use a variety of operators to specify the range of versions that are acceptable. The following operators are available: - `>`: Greater than - `<`: Less than - `>=`: Greater than or equal to - `<=`: Less than or equal to - `~`: Approximately equal to - `^`: Compatible with For example, `^1.0.0` means that any version greater than or equal to `1.0.0` and less than `2.0.0` is acceptable. axios-axios-aa54a5e/docs/pages/misc/sponsors.md000066400000000000000000000115501526660636600216610ustar00rootroot00000000000000--- layout: page search: false ---

Sponsors

Axios is supported by the following organizations. If you'd like to sponsor Axios, please see our open collective page for more information.

{{ capitalizeFirstLetter(sponsor.tier) }}
{{ sponsor.name }}
axios-axios-aa54a5e/docs/patches/000077500000000000000000000000001526660636600170445ustar00rootroot00000000000000axios-axios-aa54a5e/docs/patches/@splidejs+splide+4.1.4.patch000066400000000000000000000010631526660636600237160ustar00rootroot00000000000000diff --git a/node_modules/@splidejs/splide/package.json b/node_modules/@splidejs/splide/package.json index 677e6d0..d274e39 100644 --- a/node_modules/@splidejs/splide/package.json +++ b/node_modules/@splidejs/splide/package.json @@ -4,6 +4,7 @@ "description": "Splide is a lightweight, flexible and accessible slider/carousel. No dependencies, no Lighthouse errors.", "author": "Naotoshi Fujita", "license": "MIT", + "type": "module", "main": "dist/js/splide.cjs.js", "module": "dist/js/splide.esm.js", "types": "dist/types/index.d.ts", axios-axios-aa54a5e/docs/public/000077500000000000000000000000001526660636600166735ustar00rootroot00000000000000axios-axios-aa54a5e/docs/public/android-chrome-192x192.png000066400000000000000000000057721526660636600232440ustar00rootroot00000000000000‰PNG  IHDRÀÀRÜlsRGB®Îé ´IDATx^í�ml•gÇÿ×sN{`°L§¼”J\[&QãŒ5Ù?˜™%òÒm"Î,º™™9Pã†cdÎ,ñƒè„˜±”·9¢&Æ/ÆèH|Ù² £ŒPHKW%¾Ì­ÚsžKŽ ä´ç鹞sžëôþóµ÷ÿ~ùýïmOÏÓ ø�& Ÿ�G'P^‚  P€ ëçá)ï@Ð(@Ðõóð€w h èúyx À;4 tý<<àšº~žðM€]?Ox‚&@‚®Ÿ‡§¼A A×ÏÃSÞ�  P€ ëçá)ï@Ð(@Ðõóð€w h èúyx À;4 tý<<àšº~žðM€]?Ox‚&@‚®Ÿ‡§¼A MX÷Š\Õ:zfU mÙÛ¿èÛMx7[¦nªH¶‘µ�§Þ¥ˆï��›Ñ{·?’,ÉQ•P€&¹ÝÐ\¾køVÑâC€Ì²‘Ø ¤6~ H«¬î<¾XÑòU�Ü õõE)€µ `%XÇ|÷ 3[þ¥7 ¢»¸øÿ?iE¬ø)€•`�òë®;}åÙèÜfܦŠ7T^†XñS+Á”óÝ]ÚÚ‚S]lWÅ{&ŸžXñS+Áów¬x¾å•Ѷµ¢ñ@ÚªOMª3ªò_ˆuæÓ#гü³JcoÚ ÁíÉf¥É8M<ŠŸ¬SÌ÷,ž‹[U)@ŠX'�Š4Št‚u(@H)¡)µLG,ôjËR€Ú¸Õ%Eê‚•_5km+R€Ú¸YRü `¡—r–¤ 4Át $ë�»–þ©ð÷ÂÌ÷ç¤8¼óе‡'š�XIO=O¦ÎlJ‰žåÃs‹ãÅ»º:ÊaÝ®C‹öS€)!¬ë` PG¼åË_/np“/çrz ¨#ð¦¦5@«éîÖ\¾ïdG„ÜŽ ïçQà4¨F®ñ§)3?ÿf6ºЇ¼õÂô eÐ)MGRyašUEäK®¾xj �2蔦£©€TYuýñ¹·> ŧ*MIR�ú$ ¤«»ßùßgt„âcò ¨ š‚A¯íq.7¶EU˯̶!L–¦É85zTpܺlhq©TzX!+!È5 8hé©­Œ •5�D_WàCi½¾Ÿ7HJª±ã‚ Œµ{éKsò­csSC,Q‡¨”`VuN �õT' J€TÉ(? ,ŠŸ*Ð^mn P�P6§îÀÏI”Š xN¢ÀP0Às¥†"(€ž“(0A ðœD)€¡ `€ç$J EP<'Q `(‚à9‰RCÀÏI”Š xN¢ÀP0Às¥†"(€ž“(0A ðœD)€¡ `€ç$J EP<'Q `(‚à9‰RCÀÏI”Š xN¢ÀP0Às¥†"(€ž“(0A ðœD)€¡ `€ç$J EP<'Q `(‚à9‰RCÀÏI”Š xN¢ÀP0Às¥†"(€ž“(0A ðœD)€¡ `€ç$J EP<'Q `(‚à9‰RCÀÏI”Š xN¢ÀP0Às¥†"(€ž“(0A ðœD)€¡ `€ç$J EP<'Q `("{Ð!þ±á—EEä¥Þþ…Ï DÓœ×ë\ÀÐŒF!8g8Â¥Q•cyÕ5½Gþ1µ9�OD 9À°ûK¢EˆüBÆtýîc Óš´框–¦‡:.‚ïå$÷hoß‚“M¥†Ú¦�/«Ä÷äó­Oí<8Ä€¢i£ÀP]“ p‚ûÞÖßþÃM�Ø€¡©£ÀP_“ Cð´J´io_ÛoÈ«=ÕLB@Qôq>Úøä¡öÃѧM”ªl²Ï¯ Ð[ˆ ?:çñ§U”êl"N¨àÅþöï GžvQ `¨´9Ðã‘bÝàüc¿Þ¿ÿ#EÃq§e”ju.À ¿ËG¹•!¾¾Ÿ´V �”T…qŽ`G!Ž·<~tñ�áˆÓ>J ; ¤o˜©3·ó›ÝêåR€êŒ&áL€ò»7O ôžÝ‡¥úQ"÷Q `¨È™ÏFª_>wä™_íÃ-|¥'a¯ !¨Jà ðd„Âw~ópè?Ù�j�`ªÄ.ï@€òض�Këýûúç¾j8J°Q `¨>K8¥}sF©e¿Ù­½D P;;d&€à4þìмE¿Ý¿_øÃ-C‡À/b<§À�{úþÞ°uF_#@ W¡ÁŒ*dWÔ¢î>Öc‹†ŠªF)@UDh #"ØR(¶òë}Ca¢ÀÀ³(§5.Ý·÷Èâ�øÉ-CM“F)€�l�(?¹õ $÷µ=}mO¶Éè$(€ázÔWùyK.ç’C�ß„MÁ>³k¨'Q”$ÂTyP=(��? 0.ÿþܾþec†í1š€Hi¢!i pÇ mì˜5+÷çíœ1l�Ñ„(@BP•†¥-€a+ŒÖH€Ô®£xN¢ÀP0Às¥†"(€ž“(0A ðœD)€¡ `€ç$J EP<'Q `("©œôGš�ïÞóÂ5ÿ4,ÉhÊ(€h"P|«u´ý±'NÈYÃrŒÖ�0@­*€`­Ÿ=cè—Û¼{ܰ£u"@ `'@Ë—ýÀx„ž}}‹Ž–`´Î(€ðŒ dG!.ñר6*J ¤+0¦¢÷Ž”FžøÉÑþ~ÛFE)€�ôE´Að"$úüž¾?3LÉhƒ Pð‹8åäþ¥‡<òœ3 Ì,J è×t ¼]E>‘kÉoÛypÞiþZBÌŒ¢ #ð\Ö à£î"# #ð\Ö à£î"# #ð\Ö à£î"# #ð\Ö à£î"# #ð\Ö à£î"# #ð\Ö à£î"# #ð\Ö à£î"# #ð\Ö à£î"# #ð\Ö à£î"# #ð\Ö à£î"# #ð\Ö à£î"# #ð\Ö à£î"# #ð\Ö à£î"# #ð\Ö à£î"# #ð\Ö à£î"# #ð\Ö à£î"# #ð\Ö�ÿ £ HÃj6äIEND®B`‚axios-axios-aa54a5e/docs/public/android-chrome-512x512.png000066400000000000000000000225421526660636600232260ustar00rootroot00000000000000‰PNG  IHDRôxÔúsRGB®Îé IDATx^íÝÁ®TÇ™ÀñÓ°+ÏÒXxcA6É#8oKÇ– OüÆO`òבì¼Aüž��²ñ•ÃdÍ&fÀNŸQ“xDpëtÕ]Ußo¶>]]õû*£¿}áÜÕäÿ @€�t«t'v` @`.H( Ý‘  @€€p @€@B�pèŽL€€;@€ €„Cwd Ü �P@$º# @€à @€�„ áЙw€$ ‡îÈ @@¸ @ ¡€H8tG&@€À @€ @¡;2îH( Ý‘  @€€p @€@B�pèŽL€€;@€ €„Cwd Ü �P@$º# @€à @€�„ áЙw€$ ‡îÈ @@¸ @ ¡€H8tG&@€À @€ @¡;2îH( Ý‘  @€€p @€@B�pèŽL€€;@€ €„Cwd Ü �P@$º# @€à @€�„ áЙw€$ ‡îÈ @@¸ @ ¡€H8tG&@€À @€ @¡;2îH( Ý‘  @€€p @€@B�pèŽL€€;@€ €„Cwd Ü �P@$º# @€à @€�„ áЙw€$ ‡îÈ @@¸ @ ¡€H8tG&@€À @€ @¡;2îH( Ý‘  @€€p @€@B�pèŽL€€;@€ €„Cwd Ü �P@$º# @€à @€�„ áЙw€$ ‡îÈ @@¸ @ ¡€H8tG&@€À @€ @¡;2îH( Ý‘  @€€p @€@B�pèŽL€€;@€ €„Cwd Üš¸qåûËOVçŽçùÌͯ¼uÒä&mŠ@Ç ãáÙ:�Q>¸òð£éhº3ÏÓ›ëùÌ;`ÔI;×!À!õ}7ÿ&pãò÷o>½ðÆñ4Ï×~ùÀ%!PG@Ôqµ* >¼ò×÷Ö«Õñ4M—Ÿÿ¨Xéq… ÊcÔØü[ÿOçÏ}2OÓ­—}ƒ¨ãnUÀ @à`7~õð7OçéÞ‹ÿÖï¿l$¾8‘€H4lG%Ð’À‡Wÿöûõ´¾sÚžü€Ó„üsÛ €íÜ|Š�-~ùë}«iz¯d P¢äËÀr3Ÿ @`K�Í¿õÏ«õíÍ_ï+]B”JyŽÀ2°ÌËÓl!°ùƒ~OΟ»WúoýÏ…ØÜG€$� °½Àæ¯÷ÍG«{Kþ­_lïí“J@©”çX$pÚ_ï+]Ì(•ò�e`™—§ (xÕK} >ú�€mÔ|†Àéàt#O °@àƒ«?{ÕK},óÿ� €mÔ|†Àéàt#O P ðì¯÷Mgï¯V«_<^üˆ(¦ò �E`—‡ x™À6½¯TR”JyŽÀ2°ÌËÓ<'°ô¥>Ûà €mÔ|†Àéàt#O ð�÷ßýáÚÑÙ£ãmÿz_)ª(•ò�e`™—§ ¤xö×û.œûlž§ûÀûPö@Æ©;3�-"ÿz_é@©”ç,˼!û#Ј€hd¶A H@AZ†Àè`ô ;_6�mâÎK`K°%œ�hT@4:Û"К€hm"öC`7°›ŸOH# ÒŒÚA“€$ƒvL» €]}ž@[ ­yØ �f@³£±1[ €­Ø|ˆ@>�oæN<¶€{¾NG L@„QZˆ@ ‰1Ø�ö@û3²CKÀ-ÏH, ßчCŽÕ¡² ÜøÕÃß|þíÅojž[ÔÔµ6�ý €ý›ûF¡^ýÛïçÕúöŸ¾½ø_¡ ¿°˜¨©kmûû7÷�Â>¸úð³yšnm¼ûÝŪÿ{ac³�&ªþ?Œ&Nh¸qùû7Ÿœ?wo5Mïýr<0à ‰@EP×Òjܸòýå§«sž¦éòóë €ÚÖ$0®€w¶N6 ÀûïþpíèìÑñ™VóíW­)"µ­E`|0þŒ�°s�ÍÏûºpî³yžn¼î( óAÛ>�= €=ƒû:K6?ï2�½¿Z­~}ÚçÀiBþ9Ï ÷�@£^ùë{ëÕêøÅ?ìçG�̶t& :˜íæؼÜg=­ï,9­ÿ°D˳w€@cÏ¿ÜgÉÖÀ-Ï Ü�<ûyÿêÜñó/÷Y²5°D˳w€@›_æótžî•þ¼ÿe[ Òt$ :–­Ž)ðÁ•‡MGÓ�—½ÜgɉÀ-Ï Ü8íå>K¶&–hy–à8€Àæå>O/¼q<Í󵨯Q’Ö!�C@䘳S6$ðª_æ³ëÀ®‚>O —€È5o§=°Àæå>óÑêÞ®?ïÙ1À�‡ëë t& :˜íö+°ÍË}–œV,Ñò,À PYàÙ/ó9î“yšnÕü*PS×ÚÆãÍÔ‰XòË|vݶØUÐç 乿í´{ˆx¹Ï’í €%Zž%@@¸*l~Þ?¯Ö·küa¿WmWT¤% , ®£F`Û_æ³ënÀ®‚>O —€È5o§­(°ùÃ~OΟ»·í/óÙuk`WAŸ'�K@äš·ÓV¨õrŸ%ÛK´O —€È5o§-øàÊÃ�¦£éΡ_îS¸Ýg� €%Zž%@@¸^héå>K†#–hy–àø—Àæå>O/¼q<ÍóµQ@�S³g‡‡³÷Í ´ðË|vå» ú<�\ ×¼�ö%›—ûÌG«{=ý¼ÿeƒ®7KÀ-Ï'ÐúË}–€ €%Zž%@@¸)Züe>»Bì*èór €\óvÚišZýe>»Gì*èór €\óNÚÞ^î³d``‰–g î@�‘~Þÿ²¡ €4WÙA „€F‹´.ÐÃ/óÙÕPì*èór €\óNwÚÍö{rþܽ~™Ï®Ã» ú<�\ ×¼S�v„—û,˜X¢åY€;0¤ÀûïþpíèìÑqï/÷Y2°D˳w`8�^™Ï®ƒ» ú<�\ ×¼‡>íˆ/÷Y20°D˳w`�Q_î³d8`‰–g î@÷£ü2Ÿ]!vôy¹@®ywÚÑ_î³d``‰–g î@·^î³d8`‰–g î@w~Þÿò‘ €î®² 8¨€8(¿/_*0ò/óYjñâó`WAŸ'�K@äšw×§ýàÊÃ�¦£éN¦—û,˜X¢åY€;Ð…@Ö—û,ŽX¢åY€;дÀæå>O/¼q<Íóµ¦7ÚÀæ@C° €Ž†•m«Ù~™Ï®ó» ú<�\ ×¼»9­—û,•Xnæ2 €ÌÓoôì~Þ¿Ý`Àvn>E «€È:ùÏ�ý—ùì:°« ÏÈ% rÍ»ÙÓz¹ÏZ�@&�iÚ�žÕË}b#b­B ‹€È2éFÏé—ùÄ FÄYZ‰@�aÊ�žÑ/󉌈õ´�ÑÀènð|›?ì÷äü¹{«iz¯Áíu»%ÐíèlœÀAÀAØ}éÈï_}øçCÄ�ùV9�xojÅä ö¬ç3ï|õà­“ØU­F€€p €XPëi5¿w�@°€ˆ±žV# Ü•@,¬ˆõ´à¨$ ba@¬§Õw€@% +b=­F@¸* €XXëi5À PI@Ä €XO«î�J VÄzZ�€pT±° ÖÓj€;@ ’€ˆ…±žV# Ü•@,¬ˆõ´à¨$ ba@¬§Õw€@% +b=­F@¸* €XXëi5À PI@Ä €XO«î�J VÄzZ�€pT±° ÖÓj€;@ ’€ˆ…±žV# Ü•@,¬ˆõ´à¨$ ba@¬§Õw€@% +b=­F@¸* €XXëi5À PI@Ä €XO«î�J VÄzZ�€pT±° ÖÓj€;@ ’€ˆ…±žV# Ü•@,¬ˆõ´à¨$ ba@¬§Õw€@% +b=­F@¸* €XXëi5À PI@Ä €XO«î�J VÄzZ�€pT±° ÖÓj€;@ ’€ˆ…±žV# Ü•@,¬ˆõ´à¨$ ba@¬§Õw€@% +b=­F@¸* €XXëi5À PI@Ä €XO«î�J VÄzZ�€pT±° ÖÓj€;@ ’€ˆ…±žV# Ü•@,¬ˆõ´à¨$ ba@¬§Õw€@% +b=­F@¸* €XXëi5À PI@Ä €XO«î�J VÄzZ�€pT±° ÖÓj€;@ ’€ˆ…±žV# Ü•@,¬ˆõ´à¨$ ba@¬§Õw€@% +b=­F@¸* €XXëi5À PI@Ä €XO«î�J VÄzZ�€pT±° ÖÓj€;@ ’€ˆ…±žV# Ü•@,¬ˆõ´à¨$ ba@¬§Õw€@% +b=­F@¸* €XXëi5À PI@Ä €XO«î�J VÄzZ�€pT±° ÖÓj€;@ ’€ˆ…±žV# Ü•@,¬ˆõ´à¨$ ba@¬§Õw€@% +b=­F@¸* €XXëi5À PI@Ä €XO«î�J VÄzZ�€pT±° ÖÓj€;@ ’€ˆ…±žV# Ü•@,¬ˆõ´à¨$ ba@¬§Õw€@% +b=­F@¸* €XXëi5À PI@Ä €XO«î�J VÄzZ�€pT±° ÖÓj€;@ ’€ˆ…±žV# Ü•@,¬ˆõ´à¨$ ba@¬§Õw€@% +b=­F@¸* €XXëi5À PI@Ä €XO«î�J VÄzZ�€pT±° ÖÓj€;@ ’€ˆ…±žV# Ü•@,¬ˆõ´à¨$ ba@¬§Õw€@% +b=­F@¸* €XXëi5À PI@Ä €XO«î�J VÄzZ�€pT±° ÖÓj€;@ ’€ˆ…±žV# Ü•@,¬ˆõ´à¨$ ba@¬§Õw€@% +b=­F@¸* €XXëi5À PI@Ä €XO«î�J VÄzZ�€pT±° ÖÓj€;@ ’€ˆ…±žV# Ü•@,¬ˆõ´à¨$ ba@¬§Õw€@% {4­¯OóêQìªm­vöþæó“w†>c[âv³Xa @ V@ÄzŽ¿Úü‡»ß]º5þ9�°5ÐÚDì§{Ðý÷v€yš>þò»‹wöö…¾ˆÀsÀu , ‚A\nµš­Öóõ/\úzÀã9R' “AÙf? ŸYh§'ëùÌo¿zðÖÉ�¾ß×x& \Á t åVÓú�ü�[÷ý�¿�¦ÚïQ@¿³³óF@£ƒ9ô¶Vó§w¿½tûÐÛðý~î�` Úùr›Ÿ÷ÏëõÇw¼ýyçG±ýÁÀ`uœÃ €ÃÏ ¡œL«éúÝo/~ÓОl…À3à"Á �.7ÏóÏÓÙkþ°_§L°m�`ÈŽ¸_°_ï6¿ÍË}Úœ‹]=/ ÜÁ ´³å¼Ü§³�%Þ®H<|G¯# 긶¾ª—û´>!û{Q@¸‚@0hËy¹Os²Ëç€ë@ X@ƒ6¾Üó|ææWÞ:is‡vE`¿`¿Þ¾-�€hqÈ^îÓâTìé°à°þ¾}@ÐÖP½Ü§­yØM; �YØÉ  �Anþ°ßj=_ÿâÁ¥¯ÛØ‘]hK@´5»@@41Dد‰1ØDË åéØ[—àÐc[ÝÿññÓ›÷OÞytè�ø~- €–§co] €ŽÍË}ˆï«{½MÌ~›û‘—ûìßÜ7ö/ úŸ¡4& ö>¿Éoïä¾p0Â�¡)°¿qøM~û³öMã €ñfêDû€—ûìKÚ÷Œ) Æœ«SP@ÔÇ÷rŸúƾa|0þŒ�pÏ ¸—ûÔ³µr>�oæN\Y@TörŸj´Î( 2NÝ™« €xÞÍoò{üø§ë^îokż ïì�¼’€ˆ†õ‡ý¢E­G`# ÜÁ tóóþõ<}úåwïĬhžî�`êå>!Œ!ðjàv»�z¹Ïn~>M T@”JyŽ@¡€(„zÉc«iýÇ¿?þÇ-Øo{CŸ$P* J¥‹¸%Jž!PS@ÔÔµvJðú±ûM~)ÿgáÐ €‡bK} €WÎïähžo~ñàÒ×}OØî Œ! Ƙ£S4$ þs^îÓеÿ®�`ðï ^î|Á,G H@AZ†À/๻àå>þ‡A YÐìhl¬W0M›—ûÌëõÇw¼ýy¯s´o£ €Ñ'ì|{“ßä·÷[ç ,ËÍ|‚Àk2Àæå>ó|ææWÞ:qMh[@´=»ëP ox¹O‡×Õ– €ÄÃwô:ÀË}êÜ%«¨) jêZ;¥@¦Øüa¿Õz¾îå>)¯ºCw. : í·'�(ü&¿ö®Ÿ(ÅT$P&�#V÷|üôæý“w•©xŠ�Ö@k±Ÿî†/÷éþŽ:��€p Œ\}xkž×�¼Ü'øÂXŽÀ�À�à}í¸£À¸s29@ι;uEP×Ò„ €0J ø§€pèA@ô0%{ìJ@t5.›%�V@¤½ƒ×µd­K€@¤€ˆÔ´?pèD@t2(ÛìGÀègVvJ ³€È<}g¯" ª°Z”�` j9À @ ÐÔì±+ÐÕ¸l–@Z�vô^K@Ô’µ.‘ RÓZü-w€�N@'ƒ²Í~ü€~fe§2 €ÌÓwö*  «E Á –# Üz=LÉ»]�Ëf ¤iGïàµ@-Yë ) "5­EÀßpèD@t2(ÛìGÀègVvJ ³€È<}g¯" ª°Z”�` j9‡€Õ´þ㟾{û}” €R)Ï(Ø{¬æOï~{éváö~û#Ð�€èfT6Ú‹@�Øü¼^¯?¾ûàíÏ{q°OÚmÏÇî:¨~Þßá=°e­ €Ö'dÝ DÀÿyúôËï.މ߉  ðjÿÀí ,° ü¼?ØÞr” €r+O(( €ÍÏûçùÌM¿Ì§ˆÔCTP-™[àôðóþÜ7Äé ´! Ú˜ƒ] $𺘧éc?ïh¶ÄÖIDATØŽB cÐñðl½M�W€Ÿ÷·9.»"�V@¤½ƒ×x1æyþïy:{ÍÏûk‰[—�mÀ6j>Cà5ÿó~|üóm¿ÌÇ•!@ 5ÐÚDì§{�_ÀÏû»¥Z@ =^‡;„ÀûWøü̼úü‹—¾>Ä÷ûN”€%Ï @€�ÁÀ`u P" J”Ô¤ÇlºcQ覫DdSßì�©>lbŸJ,ùÁÄ�µÛšð˜My$ ÝDµÜyÕÌÊâÊÎ[RM!¢—”�¦¦÷Sh¼$ �³rššéKŠ÷‰êÍdÕ™Í(tmØ)tm¼œ¤Çúf?�ªN™È‡/Ü€B׆œBׯ+Óôm�õ&iºEL·ˆHçRÃ)tmÈ)tm¼2K�ôÍ]—¨îû奖ó„ê F¡¨åç—%/2‘ @œ?¶C 9ÊPè `?·þЊ®üE·ŠÈ£"rÚ_¡QRÙç(t¦›ç·[šÞ%"¨=…®…V¶Y ½Ï÷?¼Z²òûåÛêÁM¡ë¡–Í }DZþ£SIÊ^ýd¥¯VÂO¡³‘³ž)ú4µò/K ÌÝ(¦Ï˜ÈÅõÀ<³†B7B¯±µZD¾~íüª“ÿ-m6Õo‹É�!åÃI�òkd}Û =<°ÿƒy»äç"º©ÚÏ—QÐ|…FIeŸkc¡MGÞP+~_$¹YÄÎ>\Ô(f Ý(Áú×·­Ðcƒs7”RÙ)"W×�oé•:k¢ø¼¶úækçW]|²t“¨>!"]8*É}ë©ýÝGÁk¶|ŒBƒ¶œÐª?Ëuä¾»ëå5'Á+F£Ð`�-$ôqÑdûd¡û§àÕ¢ŠQh°Î–Úd.MäÎ’œx~º0¸^-ª…ël¡_(™ÜñÌ�žWÀ+E£Ð`­Í+´MäÉŽtqÇ®ƒẀ׉6F¡Áj›Sh[T• ƒ=Û¦§µ^%ê…ëmB¡_SM·ÿkáÔÄïo8^#ú…+n&¡Ul¦¤2²»Ðó¢ˆx…¶ˆQh°ææÚEô/IÒùͧ÷_q»íb¬<¬ÐöŽ©=0UX÷cð¸m£Ð`õÁ„V™“Ô¶Nèù ß/W/‹BWgô~"„Ð*²/×i7=õ�Þcà1Û>F¡A< m*2]²Å{wøèkà jàQè½OWîœ8|ù;àñ;M€Bƒ*xú¸J:~ìÊ#�ïÙó™"x4ÆÎ!@¡A<ý’&ºya ûþ,e‰…Ù9:±½ù\çW~½ïÊ#àq[†…Õp$ô*}2ÉçØõòš·À£0V�…õp!´‰Ï¢~ÓÐ/j±ÇQÏ”çý-*º„jÑÑ• äR/›RÒ=�)È&šû’D9D�˜R#iÏLcáŒ=Vñ¢2�Œ^� ¶˜yÃÝk•¹Ds�Á· Ä¿š*Ã0Ca{}KÎ|PZi�þë•M6,flA� À� g€m÷}"ª´S'–µÓô9�õþv û@Wó±[¨,¿‚=¦cŒ?$Ç%G;ˆ‡]Ö–d’ Ü¥­Ý îÍó,0%X¶»,¯ŸâµQá¾lAÉY©Žö\ ×¹ÙÖQ¡’þP/�j%‹ö’ãì©€à@ (Ð+ð 2å!há€IEND®B`‚axios-axios-aa54a5e/docs/public/favicon.ico000066400000000000000000000360561526660636600210260ustar00rootroot00000000000000 h6  (ž00 h&Æ(  è.] ä*\Vä+Zqä*\Vä*[—ã+[eæ&Yä*\Vä*[—ä*[£ä*ZÝã([-ä*\Vä*[—ä*[£å)\ˆå+]Mÿä*\Vä*[—ä*[£ã*[Iä*\Vä*[—ä*[£ã*[Iÿã,Y.ã)[vä*[—ä*[£ã*[Iä)\Kã)Zðä*[—ä*[£ã*[Iæ-Y(ä)Zqä*[£ã*[Iå)Zˆã*[IÿÔ*U ( @ ç*[*ä*[«å*[‘Ì3fä*[«ä)Zÿä*Z0ä*[«ä)Zÿä*Z0ã,^.â)W,ä*[«ä)Zÿä*Z0ã*\Hä)[òå*\Nä*[«ä)Zÿä*Z0ã*\Hä)Zÿä)Zýä*Zzÿä*[«ä)Zÿä*Z0ã*\Hä)Zÿä)Zÿä)Zÿä)Z§æ3f ä*[«ä)Zÿä*Z0ã*\Hä)Zÿå)[Þä)[³ä)[³å*\€ªUUä*[«ä)Zÿä*Z0ã*\Hä)Zÿå*[‘ä*[«ä)Zÿä*Z0ã*\Hä)Zÿå*[‘ä*[«ä)Zÿä*Z0ã*\Hä)Zÿå*[‘ä*[«ä)Zÿä*Z0ã*\Hä)Zÿå*[‘ä*[«ä)Zÿä*Z0ã*\Hä)Zÿå*[‘ä*[«ä)Zÿä*Z0ã*\Hä)Zÿå*[‘ªUUä*\Vä*Z`ä*Z`ã)ZËä)Zÿä*Z0ã*\Hä)Zÿå*[‘å)\2ä)[áä)Zÿä)Zÿä)Zÿä*Z0ã*\Hä)Zÿå*[‘Þ,Yå*[Âä)Zÿä)Zÿä*Z0ã*\Hä)Zÿå*[‘Ô*Uä*Z™ä)Zÿä*Z0ã*\Hä)Zÿå*[‘å)[jç*Y+ã*\Hä)Zÿå*[‘ã*\Hä)Zÿå*[‘é,Yå)[Áå*[‘ÿ€€ã,^.(0` ç(` ã*[Iè.],ä*Zâå)[‰ñ*Uè.],ä*Zãä)Zùå*[Šæ3M è.],ä*Zãä)Zÿå*Z¯ß0`è.],ä*Zãä)Zÿå*Z¯ß0`æ3f â)W,Ô*Uè.],ä*Zãä)Zÿå*Z¯ß0`ê*`å*[°ä)[{è.] è.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çã)Zøä*Zªé,Yè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)Zþã)Z¹æ&Y(è.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)Zÿä)Zÿä*ZÏå*\Nÿ@€è.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)Zÿä)Zÿä)Zÿå)Zèâ)[kÿ3fè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿå)Zûå)[çã)Zæã)Zæä)Zßå,Zcÿÿè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)Zâä*]Bæ,]4æ,]4æ,]4å.\2ÿ@€è.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cè.],ä*Zãä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cÛ$mä*[„ä*[Ÿä*[Ÿä*[Ÿå*Z¯ä)Zôä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cã*\$å)ZÁå)Zûä)Zÿä)Zÿä)Zÿä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*câ,W#ã)Z›ä*Zûä)Zÿä)Zÿä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cØ'b å*Zlä)Zóä)Zÿä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cÿUUã*Y\ä)Zâä)Zÿå*Z¯ß0`ê*`ä)[Çä)Zÿä)ZÚã*cÿ€ä+ZAå)ZÁå)[®ß0`ê*`ä)[Çä)Zÿä)ZÚã*cæ)Zâ+ZXë'b ê*`ä)[Çä)Zÿä)ZÚã*cê*`ä)[Çä)Zÿä)ZÚã*cæ&Yã)[¹ä)Zÿä)ZÚã*cªUå)\2ä)ZÇä)ZÚã*cÿ*Uå)]kÝ3Uaxios-axios-aa54a5e/docs/public/logo-light.svg000066400000000000000000000305441526660636600214670ustar00rootroot00000000000000 axios-axios-aa54a5e/docs/public/logo.svg000066400000000000000000000305441526660636600203620ustar00rootroot00000000000000 axios-axios-aa54a5e/docs/public/site.webmanifest000066400000000000000000000006151526660636600220670ustar00rootroot00000000000000{ "name": "axios | promise based HTTP client", "short_name": "axios", "icons": [ { "src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" } ], "theme_color": "#552cdb", "background_color": "#552cdb", "display": "standalone" }axios-axios-aa54a5e/docs/public/sponsors/000077500000000000000000000000001526660636600205615ustar00rootroot00000000000000axios-axios-aa54a5e/docs/public/sponsors/code-for-japan.jpg000066400000000000000000032007331526660636600240600ustar00rootroot00000000000000ÿØÿí,Photoshop 3.08BIMíHHÿáRºhttp://ns.adobe.com/xap/1.0/ image/jpeg Web Adobe Illustrator 25.2 (Macintosh) 2021-06-14T09:33:10+09:00 2021-06-14T00:33:24Z 2021-06-14T09:33:10+09:00 256 256 JPEG /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7 FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX Yq7FXYq7FXYq7FXYq7FXYq7FWJ6/+a35faE7RX+tQG4WtYLflcSAj9lhCH4n/WpgJDstP2Pqs28Y Gu87few+7/5ya8hxFlgs9RuGH2WEUKId/FpeX/C4OMO0h7K6k8zAfE/qU7b/AJye8kOaT6fqMO+x CQOPp/eqfwx40y9lNQOUoH5/qZRov51flrqzrHFrEdrM2wjvFa3/AOHkAj/4bDxB1+fsLV49zCx5 b/t+xm0ckcsayROHjcBkdSCpB6EEdcLqSCDRXYodirsVdirsVdirsVdirsVdirsVdirsVdirsVdi rsVdirsVdirsVdirsVdirsVYj+YH5neXPJVmGv39fUZVLWumxEeq/bk3Xglf2j9FTgJp2fZ3ZWXV S9O0Rzl0/aXzR52/N7zn5skeO4ujZaa1QunWpaOIr/xYQeUh/wBY08AMrMre90PY2DTCwOKf848/ h3MJwO1dirsVdirI/KX5hebfKk4fR7944K1kspP3lu++/KM7b+K0PvhBpwtZ2dh1ArJHfv6/N9Jf lp+dWhecOFhdKNN12n+8jNWOag3MDmlfHid/n1yYlbwnanYWTTeqPqx9/d7/ANb0bJOidirsVdir sVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVYR+a35lWfkjQ/UQLNrN5yTTrU7io+ 1LIKg8E/E7eJAJp2/ZHZctXko7Y4/Uf0DzfI2ratqOr6hPqOpXD3V7cMXmmkNSSfwAHYDYZU+lYc MccRGAqIQmLY7FXYq7FXYq7FV0UssUqSxO0csbBo5FJVlZTUEEbgg4qQCKL6h/JL83T5ot/0HrTq NetkrFMdvrUS9Wp/vxf2h36+OWRk+fdu9jflz4mP+7P+xP6nrGSebdirsVdirsVdirsVdirsVdir sVdirsVdirsVdirsVdirsVdirsVUL++tbCxuL66cR2trG808h6KkalmP0AYs8eMzkIx5k0+K/Pnm +982+Z7zWbkkJK3C0hJ2igU0jQfRufE1OVE2+rdn6KOmwjGOnPzPVj+BzH1T+XvlTyz5C/LtPM1/ apJqX1Mahe3TKGmHJPUWCIt9ilQtBSrdcsAoPnnaWrzazVeDE+ji4QOnvPf3+55h5q/5yJ1/XdK1 DSV0q0trG/ieAktJJKqOKV51RSf9jkTJ6DSezWLDOM+KRlE30r8fF5LkXo3Yq7FXuv8Azix/x0fM P/GG2/4lJk4PI+1v0Y/ef0MQ/wCcgf8AyaWp/wDGO2/6h0wS5uz9nf8AE4/53+6LA9K1S+0rUrbU rCUw3lpIssEo7MpqPmPEZF2+XFHJEwkLiX2r5I81WvmnyvYa3bjh9ZT9/F/vuZDxkT5BgaeIoctB fKtfpDp80sZ6cvd0TzC4bsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVeUf85H+Z G0zyRHpcL8Z9ZnETAdfQhpJJ/wANwHyORkdnpPZjS+JqDM8oD7TsP0vlvK30F2Kvrr82FVPya1NU AVVtLYKBsAPViFMsPJ817H318f60vuL5Fyt9KdirsVdir3X/AJxY/wCOj5h/4w23/EpMnB5H2t+j H7z+hiH/ADkD/wCTS1P/AIx23/UOmCXN2fs7/icf87/dF5zkXdveP+cXvMjrdat5clf93Ii39qpO wZSIpqe7Bk+7JwLx/tXpfTDKP6p+8fp+b6DybxTsVdirsVdirsVdirsVdirsVdirsVdirsVdirsV dirsVdirsVfNv/OUV/I/mnSLAn93b2JnUduU8zI3/JgZXN7v2TxgYZy75V8gP1vFsi9Um3lXy5f+ Y9fstGsULTXUiqzAEiOOvxyN4Ki7nEBx9XqY4MZyS5AfgPqv86I0i/KnXI0FESGFVHWgE8YHXLZc nzrsM3rYE95+4vj7Kn012KuxV2Kvdf8AnFj/AI6PmH/jDbf8SkycHkfa36MfvP6GIf8AOQP/AJNL U/8AjHbf9Q6YJc3Z+zv+Jx/zv90XnORd2zz8jb82X5oaMSaJO0tu48fVhcL/AMPxwx5uo7ex8ejn 5UfkQ+v8tfMXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq+Xf8AnJeJk/MKBjSk unQuvyEsq/rXK5c30L2XlelPlM/cHk2RejfUH5Meffy5uLDTtC063j03XxaQxXXK3jga6miiAkYS x19QllLfEeR8MsiQ+f8AbnZ2qEpZJHjx8RI3J4QTtseXw2ZH+dv/AJK3Xv8AjHF/1ER4ZcnB7B/x zH8f9yXx5lT6c7FXYq7FXuv/ADix/wAdHzD/AMYbb/iUmTg8j7W/Rj95/QxD/nIH/wAmlqf/ABjt v+odMEubs/Z3/E4/53+6LznIu7Zf+Uas35leXgoJP1tTQb7BSSfoGGPN1vbB/wAEyf1X2Zlr5Y7F XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXz1/wA5S6S63+hauq1SSKW0kbwMbCRA fn6jfdkJvbeyeb0zh3EH57foeE5B69Xsb25sb23vbZzHc20iTQyDqrxsGU/QRixyQE4mJ5HZ9cfn JMJvyl1mYDiJYIH49acp4zTLZcnzXsSNa2A7ifuL5Ayp9MdirsVdir3X/nFj/jo+Yf8AjDbf8Sky cHkfa36MfvP6GIf85A/+TS1P/jHbf9Q6YJc3Z+zv+Jx/zv8AdF5zkXdvS/8AnHnSWvvzKtbjjWPT YJ7p69N09Ff+GmByUebofaTNwaQj+cQP0/ofWGWPnDsVdirsVdirsVdirsVdirsVdirsVdirsVdi rsVdirsVdirsVYN+c/lN/MvkK+t4EMl9ZUvbNRuS8IPJR4lo2YD3pgkNnb9h6zwNTEn6Zek/H9tP j3Kn052Kvrr8zK3/AOS+oS2wLrJYW8603/dq0chO1eiiuWHk+a9l+jXxB/nyH3h8i5W+lOxV2Kux V7z/AM4rwSm68xT0/dBLWPl/lEymg+gZODx/tbIcOMdfV+hhH5+TRy/mnq4Q19NbZGO1OQtoyenh WmRlzdt7PRI0cPj/ALovPcDun0v/AM40+VH0/wAtXev3CcZtXkCW1Rv9XgJHIf68hb7hlkQ8H7Ua zjyjEOUOfvP7PveyZJ5Z2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV8ofnj+ XEnlfzE2pWMVNC1V2kg4j4YZj8TwnwH7Se23bK5B9H7B7TGoxcMj+8hz8x3/AK3meRd893/LH88/ L9r5Zi8seb43EFvEbWK7CGaJ7YjiI5kFW+FTx+FTVfxmJPIdq9gZZZjmwHcm65b94/HNJ/NVn/zj udH1KfQtQnGrGKRtPtgt4I/Wp8C/vYulf5mwGnK0mTtTjiMkRwXufTdfA/oeQZF6V2Ks2/LW1/K+ f9I/46vJrTj6P6O9ETHlX1PWr6McvT4OtMIrq6rtSesjw/lwJc+K68q5keb1qz/Nv8nvI+gSWXlB Jb6RqyCJI5kMkxFA00twqGm37INOwyXEA83PsfXavJxZ6j8Ry8gHz5rWr3msavearesGur2Z55iN hyc1oo7AdAPDIPaYMMcUBCPKIpO/y58i33nLzLBpkAZLRCJNQugNooAfiNf5m+yo8fauEC3F7T18 dLiMzz6DvP45vsywsbSwsrextIxDa2sawwRL0VEAVQPkBlr5ZkySnIyluTur4sHYq7FXYq7FXYq7 FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FUv1/QdK1/SbjSdUgFxZXK8ZEOxBG4ZT2ZTuDi36b UTwzE4GpB8l/mX+Vet+Sr9mdWutFlYiz1FRtv0SWn2H/AAPb2qIp9J7L7Wx6qO22Qc4/q8mEYHau xV2KuxV2KuxVP/JvkjX/ADfqq6fpEHOlDcXL1EMKE/akbt7Dqe2EC3D1uvxaaHFM+4dT7n1x5B8h 6R5M0NNNsB6kz0e9vGADzS0pyPgo6KvYe9SbAKfNe0e0J6rJxy5dB3D8c2SYXAdirsVdirsVdirs VdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVUbyytL21ltLyFLi1mXjLBKodGU9mU1BxZwy SgRKJoh4n52/5xpsbl5LzyndiykO5026LNDX/iuUcnT5MG+YyBi9XoPamUfTnHF/SHP4j+x45r/5 aee9BZ/0jo1ysSdbmJDNDTx9SLmo+k5EgvUaftTT5vomL7uR+RY0QQSCKEbEHA57WKp3onknzdrr quk6RdXavsJVjYRD5ytSNfpbDTi59dhw/XOI+O/y5vWfJv8AzjNfyvHdebLxbaEUJ0+0IeVv8l5f sL/seXzGSEXm9d7UxG2EWe88vlz+57voXl/RtA06PTtHtI7Ozj6RxjqehZmNWZjTdmNcnTx+o1OT NLjyHikmGLQ7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq hbrStLu25XVnBcN4yxo58P2gcWyGacfpJHxWQaJotvJ6kFhbRSDo6QxqeteoGLKWoySFGUj8SjcW l2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV 2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2 KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxVK/M3mjQfLGi3Gta7e JY6bbCss8nidgqqKszMeiqKnFXzR5q/5zckF08XlTy8rWyn4bvU5DycV/wB8Qkcf+RhxVLNJ/wCc 3fM6XSnV/LdlcWhIDLaSywSAdyDIZ1PyoMVfSf5cfmb5V/MHQ/0t5fnLCMhLyzmAW4t5DWiyoCw3 p8JBIPY9cVZXirsVdirsVeXf85H+dPMvk78tZdZ8uXn1HUlvLeET+nFN8EhbkOMySJvTwxV3/OOH nTzL5x/LWLWfMd59e1Jry4hM/pxQ/BGV4jjCkabV8MVeo4q7FXYq7FXyH+av5+fmf5j/ADIuPJP5 dSyW0NtdPY262aobm7nhJErmVweEalGpxIHEcmPgq91/IjT/AM0LHyfdw/mRJLJrh1CVrczTQzsL QwxBBzhZ0p6gkNK1xV6PirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVfEX/OWnnvUPMP5kHy tayM+maCEhjt03El5MoaVyO7LzEY8KGnU4q+ivyX/Ifyv5C0K1murKG981Sosl/qUyrI0UhFTFbk 14Ih2qu7dT2AVZl51/L/AMpedNKl03zDp0V3HIhSK4KqLiEno8MtOSMDvtt41G2Kqvk/yX5Z8m6H Fo/l+ySysohVyN3kem8krnd3Pcn9WKvMfOP/ADlr+Vvl7UX0+0N1rs8TcZpdPRDbqRsQJZHjD/NA V98VVPJP/OV35X+ZtRj024a50K7mYLA2orGsDsTQL60buqk/5fEe+KvZWZUUsxCqoqzHYADqScVe IebP+cvPyv0PU5NPso7zXGhYrLdWSxi35DYhJJXTnv3UcfAnFWAfnf8Anr5D/Mb8n7u10aaa21SG 9tZJdMvFWObgCQXTgzo6gns1fEDFXof/ADiB/wCSch/5j7r9a4qm/wCYv/OSn5beR9Rk0m5ln1XV 4TxuLPT1ST0WH7MsjvGit4qCSO4xVJfKX/OXf5X67qcWnXqXmhvOwSK6vlj+rVOwDyxu/Dfuy8R3 IxV7eCCKjcHocVYf+Zn5p+Wvy60q11PX0uXt7yf6tELSNZG58GfcM8e1F8cVfFX5R/mL5a8qfm7J 5v1eO4k03letCIEV5g1yGVDxLIPsua74q+1fLv5reTtb8iHzyLltP8vJ6vqXF8FiZPRcxmqq0m5Y UUAkntviry/U/wDnNH8tba7eGy03VL+FdhcrHDEje6rJIHp/rAfLFWf/AJa/nr+X35hSNa6LdSW+ qIpdtLvVEVwUHVkAZ0kA78GNO9MVRP5n/nB5U/LeHT5vMEd1ImptKlv9UjSQgwhS3Lk8dP7wUxVh PmX/AJy5/LLRo7H6vDfalcXlvFdPBAkamBJkDokzPIF9TiwPFeVO5xV7Ta3C3NrDcKCFmRZFB6gM Ad/vxVVxV2KuxV2KuxV2KuxV2KuxV2KuxV+fv5rk6H/zkRq11qSl4bfW4r6VXH2oGdLhR329NhT2 xV+gEUscsaSxOJIpAGR1IKspFQQR1BxVdirx3/nK3zZqHl78o7lLGQwza1dRaW8qkhlilSSWUAj+ dISh9jirzj/nFL8lvJus+VpvN/mOwh1a4nuZLextbpRJBHHDx5OYm+B2d6j4gaAe5xVC/wDOWP5N eUPL+g2Xm7y3YxaU7XS2d/ZWy+nA4lR2SRIx8KMpjoQoANfbFVt1+amvTf8AOIayGd21J7weXJr3 kfUMArJu3+VbgRHxGKoj/nFD8mPKGueW7nzf5ksIdVme5e1sLO5USQIkIUtI0bVV2Zmp8Q2A98VU /wDnK78lfKWg+X7bzh5asotKZblLXULK3ASB1lDFJUjHwoysvEhRQg9NsVTn8mPM115Y/wCcVNc1 20PG8s3vjauf2ZpCkcTb9eLuDTvirxD8mfOH5V+XdYvtZ/MHSbzX71ip06JIoLmFXYs0ssy3E0Qd yePGoPc9aYqnH55fmJ+S/nPTrSfyh5eu9F8wQTfvpza2lrBNbsrcxILeaTlIH4lWK9KiuKvpr/nG LzHea7+TmjSXrtLcWBmsDK5qWS3ciL/gYiq/RirOvNXkryr5ss4bPzHpsWp2tvJ60MU1aLJxK8hx I7MRir4q/Inyh5a1788bnQ9X0+K80lP0hxs5K8B6RPp9CD8OKsj/AOct5bTy7eeXvy+0C3XTfLlr atqpsYaiNp7meWPk1Sa8fSan+scVe4/lj/zj3+W+jeS7CHVdCs9X1a7to5dRvL6JLhvVlUM6xcww jVCeK8KH3rir5s/PPygn5Sfm5Z3vlKVrOB44tW0xORb6u/qOjw1Jqycozsf2W4muKvRP+czb8aj5 Y8h6gE9MXgubgIdyolit3p9FcVT/AP5x3/IL8vrr8u7DzF5h02LWdT1pHm/0mrRwRcmREjSoFeI5 FiK1O3TFX0VFHHFGkUa8Y41Coo6AAUAxVdirsVdirsVdirsVdirsVdirsVdir5x/5yq/I/U/Moi8 6eWbY3Wq2kQg1SwiWss8KElJY1G7vHWhHUrSn2aYq8//ACi/5yw1Lyjo9v5c816fLqunWCiGzu4W C3cMabLE6SUWRUGy/EpA237Kso89f85qWU2lzWvkrSLmG/mQomoakIl9EkU5pDE8wdh25NTxB6Yq irO0/M/86vyCv7fzDpyx6pZSxXfl3Um/cNqLwBg9YaBVJRmRZBRWZug4k4qwT8hv+cgU/LC2vvKf mzTbt9PFw0sZhVRc2s5oksbwymKqnjX7VVNdjXZVR/Pb89pfzXfTfKvlTTLtdOS5EyRyKGurq54t HGoiiMlFUO1ByNa70pir1y//ACD1Rf8AnGhPJkaiTzPbkav6SkcWveZd4AehPosYga0Lb9MVeR/k D/zkDF+WUF95Z8zafcy6TJcNOpgVfrNtcECORWjkMdVPAVHIFSDsa4q78/8A8/v+Vl6dHo/lzTrm 38uaa63t9cXKoJZJK+jGWWMyCONWloPj+IsKgUxV6l/zj/5XXzX/AM4z6p5dMgibU5L+CKU14pKQ pjZqb8VcAn2xV4j+WHm3Tvyr826ron5g+VItStZGWK7huLeGa5tZIi3GSATjiyOG3ow5DiQdt1Xo PnL/AJyM/KKEWcfkzyFp2oTtKhvG1DT7aFPR/ajiEYdvUbpyIovg2Kvp3yUdOk8safdWGi/4fgvY luTpJhjt3haUAkPHF8Ib8fGh2xVO8VfE/wDzjb/60Zd/9vP/AIk2Ks//AOcxvyw1fV4NP87aTA10 NLt2tNXijHJ0tw5limCjcqjSPz8Kg9KkKoT8tv8AnMPy5p3lCy0vzbp96+q6bAlutzYpFIlwkShU ZhJJEUcqBy6iu+3TFXmOrXPmv/nIX83Uexs2tbMLHbr+0llp8bkmSV6AFiXZvdjxGKvTP+c17SCz 0LyTaW68YLdruKJfBEjgVR9wxV7L/wA4/f8AkmfKf/MEP+TjYq9BxV2KuxV2KuxV2KuxV2KuxV2K uxV2KuxViPmj8pPy1803Butd8u2d3dtu90EMUzf68sRjdvpOKoDR/wAhfye0i7W7svKtl667q1wH uQD4hbhpVB96Yqz1VVVCqAFAoANgAMVYt5r/ACu/L3zZN6/mHQLS/uqAG6ZOE5AFADNGUkIAGwLY qqeVPy08heUmaTy7oVpp07Di1zGnKcr/ACmZ+UlPblirJcVYj5p/KP8ALXzVdm917y9aXl61Od3x MUz0FBzkiMbvQDbkcVV7X8r/AMvLXQZfL9v5esU0e4ZHuLP0VKSvGeSNJWpcqehYnFU20Dy5oPl7 Txp2h2EGnWIZpBbWyCOPm32m4jucVQPmryB5K82RonmPRrXUzGOMUs8YMqLUmiSikijfoGxVK/Lf 5M/lb5bvFvdG8t2dveRmsVy6tPIh8Y3mMhQ+60xVmeKuxVjukflz5E0bVm1jStBsrLVH58r2CFUl Pq/b+ICvxd8VZEQCKHcHqMVYJq/5E/lDq96b2+8rWRuWPJ2hVrcM3WrLA0asT3qMVZR5e8r+XPLl iLDQdNttMtK1MNrGsYZunJuIqze7b4qo+ZPJflPzOkCeYdJtdVS1LNbLdRrIIy9AxXl0rxFcVR+l aVpuk6fBpumW0dnYWy8Le2hUJGi1rRVHTriqKxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KviD/n J/zd5r0785NXtNP1q/s7VIbQpb29zNFGC1tGTREYKKk1xV9leU5JJfK2jSysXkextmd2JLMxhUkk nqTiqa4q7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX Yq7FXwT/AM5Xf+Tu1r/jDZf9QseKvt/yf/yiWif8wFr/AMmVxVEa35g0HQrM3utajbaZaA8fXu5U hQtSvEM5UE+wxVg7f85F/kos/onzVbc6gVEc5Tf/ACxHw/HFWb6H5i0HX7Fb/RNRt9Ss229e1lSZ AaV4koTRt9wd8VTDFUgsvzA8hX2pLpdl5k0q61N3aNLGG9t5J2dKllESuXJHE1FMVUfNf5leQvKb BPMWu2mnTsOS20kgacr/ADCFOUlPfjiqWaF+eH5S67cra6b5osnuHPGOKZmtmY+CicRcj8sVZxir sVYx5n/M/wDL3yvI0WveYLKxuFFWtXlDTgUrX0U5Sf8AC4qlGlfn3+Tuq3K21p5qshMxAUXBe2BJ 6ANcLEtfpxVnqOjorowZGAZWU1BB3BBGKoPWNc0XRbM32s6hbaZZBghuryaOCLk32V5yFVqewriq jpHmnyxrNnNe6Pq9lqVnbErcXNpcRTxRkLyId42ZVou+56YqxG//AOcgPybsL5rG481Wf1hG4t6X qTRg1p/exI8fz+LFWZaJr+ia7p6ajot/b6jYybLc2siypUAEqShNGFdwdxiqPxV2KuxV2KuxV2Ku xV2KuxV2KuxV2KuxV2Kvgn/nK7/yd2tf8YbL/qFjxV9v+T/+US0T/mAtf+TK4q+YvOn/ADjZ+bPn T81NRbWNZ9fQQfVtNduiH428jErbw2yFaSR7gqAqd6/EMVZBJ/zhD5UNmUj8y363tNpmhhaLl4+k OLU9vUxV4jY3vnT8h/zXa1lmL/U5I/r0MTMLe+spKMDQ+KGq1FVb5Yq+/re4hubeK4gcSQTIskTj oyuKqR8wcVfmynmbVPLH5iXuuaUwTUbS8vfqsjDlweX1IuYHcrzqPfFXuHkX/nEfzF5ptP8AEXn7 WbjT73Uj9YazVfVvT6hqXuZZTRHbrx4sR3odsVQX5n/84f6r5f0a41nypqT6zDaK0txp00YS59Nd y0TISshA6rxU+FemKpj/AM4pfnfqo1mDyB5hunurO7UjQrmUlnhlRS31csesbqp4V+y2w67KvpD8 ytE8za35J1TTfLGpvpGuTRf6HeRkKeSkEx8+qeoBx5ruta4q+ZPIH/OHPmPWYf0n541NtIM5L/UI As92SxqWmlYmNCeu3M+NMVR35hf84aR6Z5futU8qazPeXVlE876deRoWmWMFmEUkQWj0HwqV3PcY q3/zhz+aGqvqlz5B1O5e4sWga60b1GLGF4iPVgQnojIeYXoCpp9o4q9M/wCcuIBL+S98/KnoXdpI B41lCU/4fFXyz+V1j5/862h/LLy1L9W06+uW1LWZ+TKnpKkcVZyu5jQqKIPtMw8BRV7Rqf8AzhBa Jo0v6M8zyy6yiExfWLdY7aRwNlIVneMH+arU8DiryD8n/O3mL8rvzTisb13trQ3v6M8xaezVj4iT 0ncgHiXhb4lYfKtCcVfoHirsVdirsVdirsVdirsVdirsVdirsVdirsVfBP8Azld/5O7Wv+MNl/1C x4q+3fKckcfk7RpJGCRpp1szuxAAAgUkknoBir5+/MX/AJzMsrC/l0/yNpseqekSh1W9LiB2Bp+6 hQpI6/5Rdfl3xVi9v+cH/OWuuql1pWiXMdrLvG8Gkn0WDdOLzo9R78sVeQ/mzqH5kX/msXH5hRyR a+beMLHLDFAwtwzenRIlVaV5b9cVfoT5P/5RLRP+YC1/5Mrir4U/K3R7LWP+chtNsb0K1sdZuJ2R xVWNs0k6qRQ1DNGBTFX6BYq7FX546/bx+Wv+cgLqPSSsaab5kD2arXigW7DqnbZfs0xV91ef/wAw PLfkTy7LruvzNHaowjhhjHOaaVgSsUS1FWND1IA6kgYq+ZNX/wCcwfzG13UWsPJXl6CH1CRBGY5r +8YDuFjKJ9HBvniraefv+czb1HeDSr23DbgHSreMry3FBPFX764qwf8A5xR/8ndov/GG9/6hZMVf Sv8Azll/5JPVv+Yiz/6iExV51/zg5aweh5vu+ANxzsYhJ3CUnYgfM9cVfU2Kvz0/5yJ/c/nd5oaL 92Vuo3BXajGCNidu9d8VfoXirsVdirsVdirsVdirsVdirsVdirTcgp4gFqfCCaAntU0OKvlKx/5y +80R/mNbab5m0q20Ty9b3Ulpq9uiyS3UJ+KPk8hO4hkozBIwSAfbFX0pL528nxaQ2sPrdiNLWMzG 8FxGY+AFahg2/wBGKvgP8ydfk/Mr839QvtHjeQa1ew2elRsCGZFVLWA8eq8wgYj3xV9sfm3ZXdh+ SfmOx0vkXtdGkgTjXl6McXCTvX+6DYq+T/8AnE9/JyfmmjeZPQEotJDor3RAjW9EiFacvh9T0+fC vfp8VMVfcuo6lp2mWUt9qNzFZ2UC8prmd1jjRfFmYgDFX57/AJ9+f7Lz1+Zeo6zpxLaXEqWenyEE F4YBT1KEAgSOWYV3oRXFX315P/5RLRP+YC1/5Mrir86W8wX/AJd/MeXXdPIF5pmqyXMNfslo5y3F v8luh9sVffn5cfmv5O8/6RDe6Lexi8ZAbrSpHUXUD0+JXj6kA9HA4nFVH8z/AM3vKH5faNcXWqXk UmqCMmx0eNwbmeQj4BwFSiV+07CgHiaAqvjf8kvLesfmL+dFpqF4pmWO9Oua3cUPCiS+sQf+MspC Ae/tir1P/nOM33q+UB8X1DjfEU+z61YK19+PSvvTvirNf+cP5vJx/LNYtN+rr5jE8510Ar9ZYiVv QZq/H6YhZAv7NeXeuKs7/OH81NB8geUr28urqMazLC6aRp4YetLOwKo3DqI0b4nboB70BVfIv/OK P/k7tF/4w3v/AFCyYq+lf+csv/JJ6t/zEWf/AFEJirAP+cHP+Ob5v/4zWP8AxGfFX1Bir89P+cjv /J2eav8AmIi/6h4sVfoXirsVdirsVdirsVdirsVdirsVdirsVeF/nd/zjDpnnrUH8waBdR6R5ilA +trKpNrckbB34AtHJTqyg17iu+KvDD/ziB+cYn9MQ6eUrT1hdDhTxoV50/2OKvdPyO/5xk0/yFqC eYdeuo9V8xxqy2qwhha23IFWZC4V5HKmnIgUr074q9xliimieGZFkikUpJG4DKysKFWB2IIxV8t+ f/8AnC57rU5r3yRq0FpazuXGl6j6gSGu5WOeNZWK+AZKjxOKpDon/OGn5h3txEvmPXrKz09W+NYH mu5gB/KjpFGK+PP6MVZf5/8A+cO7TUn0iLyhf2+l2mn2Ytbk3avJNcSiV5DPI6AAs3qU6ClABtQY q+gvK2lT6R5Y0jSbiRZbjTrK3tJpUrxZ4IljZlrvQldq4q+Bvy/02w1T8+bLTtQgS6sbvWJ4bm3l HJHjd5AysPcYq9b88f8AOFWoG+mufJWsQGzcs6afqfqI8Y68EnjWXn4Dkq+5PXFUl0D/AJwq8+XN yn6c1jTtPs6/vDbmW6mp/koUhT/h8VfTv5a/lf5U/LzQzpegwHlMVe+vpSGuLiRRQNIwAFFqeKqA o+ZJKqr+ZH5ceXfzA8tSaFraMI+QltbuKgmgmUUEkZII6Egg7EYq+XtV/wCcMPzGsbxn0LW9Pu7f kVjlkee0n4nuyKkqj3o5xVmH5c/84dCy1eLVvPuqQ6r6Dq6aZaeo0MjLTj600ojdl2+wEFfGm2Kp n+Wv/OMWveS/zStfNy6zaz6bbTXZFkiSrIYbiKWJFqfhqvqA/Rir1D85fIF75+8hXnlmyuo7K4uZ YJFuJgzIBDKshBC778cVY3/zj9+S2q/lha63DqGowagdUkt3jNurqEEAkB5c/H1MVet4q+aPzR/5 xR8yecfPuseZrXXLO1t9SlSSO3ljlLqFiSOhK7fsYq+l8VdirsVdirsVdirsVdirsVdirsVdirsV dirsVdirsVdirsVdir8/Pyt/9aJ0n/tuS/8AJx8VfoHirsVdirsVdirsVdirsVdirsVdirsVdirs VdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirxXy7/wA4s+UdD88W3m+31fUJb22vGvkt 5PR9IuzFuJoganxeOKvasVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirs VdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsV dirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVdirsVf/2Q== proof:pdf uuid:65E6390686CF11DBA6E2D887CEACB407 xmp.did:0f3e4130-c54b-4966-bcf1-8259c7d48c7a xmp.iid:0f3e4130-c54b-4966-bcf1-8259c7d48c7a xmp.iid:f18bc732-9318-41f7-9e35-877f644e0276 xmp.did:f18bc732-9318-41f7-9e35-877f644e0276 uuid:65E6390686CF11DBA6E2D887CEACB407 proof:pdf saved xmp.iid:18890d28-b6d8-493c-bb14-933ded1508c7 2021-06-14T09:31:28+09:00 Adobe Illustrator 25.2 (Macintosh) / saved xmp.iid:0f3e4130-c54b-4966-bcf1-8259c7d48c7a 2021-06-14T09:33:10+09:00 Adobe Illustrator 25.2 (Macintosh) / Web Adobe Illustrator Adobe PDF library 15.00 21.0.0 ÿîAdobed€ÿÛCÿÀ ¸ ¸ÿĤ   5 ! 1"·9Au8xQ2#´µv¶7w—×XaB$56–ÖWˆ¸qÓ%Õ‡ÇH˜¨È�R3“³4T„”ÔU•Vg§çhØèIY‘b’C&i¡rÒSst¤E¥'±‚²cDdÄGÁÑ¢äefƒ£(ÿÚ?ŸÂ ü'ðþSO=¾ùæŠ >ßÏšK#·ì¥k_ξ¶Óì¥=è<×*æœ;‚ë/Ýó~YƸnš?«Y6ü«{«ãÚË>†<¹sûóöùXx–ýLy%¿ÖÿÍŽË®¯ÙJÖŸŸ'/:Í™“�‰ =kYrfŽéí¶·Ýë|·[m=¶ÛZ×íû)Açr9· ľèò¹oÆ’ßOtsïµqIO[i})[$ʶïZÛZVŸgÛJµ’üœ|mðÜÌ�w.ùð‹Œl±}ŸxÖïü¬è�FÆÉ�dVß�ŸÏ ˤ“bMd–[ì÷_eöÝJV—S× ó�(|gëÒÞÉò#¢úöêäE‡Ky¿mð)uræÅûô8¶Û¾äW&l*}k#§çÝçÒ•·í~yòñqm¥ùY8øÖÖê[K§š8m­Õ¥n¥´¬—[JÝZRµôþe +¶ºß•¬ÜÃO}-¶—Wî²É�_J×ÛJR˜QdVë½-)ëZSíôôj‡6üAŸ |ë×{ò Ò9ÿwŃ2OàM9Ÿeû¢Èž¸ñÙ:ç‰òªåe[%=o‚/|ñYù÷ÙmŸœÂ[Ÿ’ÿ��<Ù9¾fxåÛgÀ:º]~º9.¥·åæG¦î�¶Öüx)_uÔÇÆžZÒŸ›eÕûî>u¦¾ûmº,ø­º´¥d’ke”ÿÊ®¤Y2IZSý kWõÿ„¯ÿÏQÿ¨íwýx}oóо)?Þ›ÎoðKÒßûQŒ—þpÿÇþ¬]½þ ö?õÅû†ÿ?nÿÑ\¿úCêGä7ZÉ}¶]›µ†ÚúúÉ&§"¶[éJÖžêCYdûkOJz[_¶¿ÌûYwIø¾~¶»Ž$Þ’ImoöÙî¾ß}ÏwÅôÒÙ�ñÈ1l»Ýë<ý5ÜWE¥µºžúbð|™ëî­=´öÙwÛ_·Òžµ§úþqïý8õ&wþ“>Œ]ñÕ’SÖîI|5÷zRÙt»ïZþOÎõ‹Y-”·íýÚÒ¿c7ñïÅEðq»Å¾}‡—û®'‘L»±¬×òü�›*héÛ›dÜg§ù¶�/ÕÙÓï_yÀûm§º¾¿~ÇÇöÛéù+_JV¿e>Öuá_ˆ�ágŸK��Ïî¤Á¿+*lH«Ít}›ÖÑ[,}âûògì^ Å Ãź?²ÉæºÈd“ó,ºëÿ5°/“¿�NE³`y“ãæ=—Ë$4¦÷²8ÿ–—Åe/º·AÉrµ3Ùi_Í’¶Ò;ëö[ZÖž�¡MÖž¿oé]oþNn5?ù–•}Ü~wÂ2«e¸üËÍ|•¶–GfûWYnºúûm·é}ëêR뮯¥)éëêÚ®-ò­ñ‰Í©­·‹üˆøC·ÊÛ_�¿W”Ý%òyó&·í_6ÇÝA™“‘u,²1ì–ëî¥)o­hÌ\+Ëß»'&ì½òwÇÎsŸd˜qI�Ä{—®¹|Rìe¾ |3`êy^\3gOÖCmÖRéo¶´¶•­£; *·S3&¶{}ôÇÈŠjÛîõöû©÷{}Þ•ôõü¾�G�—‰—o¿+&ÊÒ—Rüy£šÚÒµ­)_tw]OJÖ�ÀàÝŸÖ���úW­{ƒv¯éY?é. Ët³èÉ,ðG7ß4 †?Ò’|Yl¶ïw¥oŽêSí¶¾™ó e¯ÙGIµÙøYðÖ”º’áeA•m­n¶—Rø/’ÚÒµ¶´õõü´«õ?CÜ¿hó[^gÄ´—IÛ’èõóGoºül�ž™~ß·ùÜO«\›ë_JúR–Vµiyü–|{xÏ•¼Õ÷Çšþ0u�#ã˜ódíøO$î®bÁß¶cõ¾&ó/žlrm–;¬ú8Ú馭ô­´¶µû3´|žñ»¤e‡¸û÷¦º·3*)fÃ×sþËáÜOg�ôšì fïq…°Ïúw}•¤1ßZWìü¯Ï‘™‰‰Kk••��KëZY\‰â†—VŸ––ÖK­÷VŸè1ÆÏÈ.´×ÛZãìv‹éöV-f«*Û½iZÓÒ—ìé­‚ïæúÒúÓÓ÷\vîŸÅ»ðÍÕQÒ¼;´;£Èœª['×Âén‹åÚé1æ‹"ø.Ç®g{]ÒZŒ««K>¥²cdÏ�|u¥m’µûéìÏž?�¹‹yunû7s¤³#þ õŸ\s}¬»|¨-­Öáhù"Óñ~°¿"¾”ŽÒöaÖµûf¶”­iñ29f‡²Û\êM$T¯æcÅ4´’êSÖ–Å5¶}ÚêÝù)_}-õüµ£Àl|¢×GuÖê8–nUŸéeØìàÀºŸgåº l]•+öþçÔ§ý);wñÂu–¿;a‡Ð¿Üï–ëko·UÈ{w¼øÿ]çYuc²¿[aÃxg_ö„[l¾ë~”{Û=Öú]õ)_Íhe~'î�ÖÁöžñwµy®MùÙ‘NÊæ\ÜË,•¾Ì˜±í¯§¤PãAttôýÚW"Éåõ¯ú7Uä2¹¯2ζ¶fòÎI•ø^Fóg,–Û¾ÈïÉ­”üë)_²Ÿ–”s÷°þEþA;oº®Òó›ÌÅÔV뮦Ÿ›y)ܼŸOoÈÃË»èêw<Ï3] ·ek±å­,ŠÚ}H#»ùë-­4ÿ”|�ùÑ̳²óù˜^Ke_›}—͇‰Ý]…§ÓYXì‚Ëi‰ Òïõú< +÷{.ºØ1ã¶é)ï­+}ku~d»ÝÔÒ]%ûLê]w§­"É–ééJSób†èâ³ì§îRžµû+Í_}ò][ä¾ëî¯åºû«uÕÿ£[«ZÕ§¹y™yùægedfåÏw¾|¬¹åÉÈšïJ[î–y®¾Y.ô¥)ëZÖ¾”k$å\Ÿ™m%ÞrþG¾å[©£²)·“q°Þm%Š/wÓŽ]†Ï#+.Hã÷WÛJßZSÖ¾�Ÿ6Dù7ý\‰¦È“Ò”ú“I|·úS×ÒžûîºïJz¿Ëó¾ ø€?í+[kK­­m­+ëJÒµ¥i_æÒ´ûiWûŽY!’9¡’ø¥ŠûdŠXïº9#’Ë©u’G}µ¥Ö_eÔõ¥iZV•}Nßm Ùbn4[MŽ—o¯–“àmu9¹:í– ô¥m¤Ø™ØrÕ�--º´÷Yuµô«ýÇ,�ßl°É|RY_[$Žû¬¾ÚúzzÛ}µ¥Ö×Ò¿¸ô|»•ë­¥šþMÈ0l·òY‰¹ØãYJz[o§²›-ôöÙJzz~JR�§ë?:üÝél{1:wÌ_)ú£;}–aõ¿�]³Â0éaÂÆ¬_uã\·Y�X®ÆÖãÇ[+mm¬pGmií²ÚSeøï�jñ)0oã~\ù/¨�].4¸˜x½åÙtÖ[÷JÃô!›S'%¿Y—‡Kqì²è&Šø$ŽÚY}—Yù¯£nïqeÖßM®Âµ¶ê]J]™=öÖ¶×Öžë/’ë/·ÖŸm+JÒ¿ºõØÍÙºëm²Y›5–ýž™øúý•Õ§¥´üév™3V¿™O·Ýëù›_]þêŸÄOóKÓ˜‘àñ_>»[{‹e¿Në{[EÖ½á—,U¦/²M·rð~w¹¥×Û®²ŸRÜ‹e·Ý-m¾Û¦š·í×ùÁù<âëi“9œ‹_$“Y˺çª9 [8!úT®.Ëi—ÁéÉ$Ž{"¥/’<è²+ëui%.º·WéÇÌ9—ÛuÙ–Kmµ¥k˜¸´²úSý-ÕŽäô¯úR¿è½®’|÷–[—‡Ç¶VÒ¶ûï›/{éJÖµô¿>,ºêWÓ×éV”ô§Ùù}zAÕ?Œ›åƒ‚bâàsÎ'âwvÅd˜ß~ÛóN©åÜc“äA ùd[‰•ÕÝ—Áx¾N]“[mo¿O=–R++lt­o÷î�üLhi¶XÃÎ¥ñßœha­)²ÆÕèùï äyÖÒÛ½+‹½³žr .¾[®­+unÔOmiOJ[oÛWÔ‡Ÿlm’•È–/·ÖÈ~¼V¿¹é%òä[OJÿï•{�w”pV±Ù¶âÇOKi.F»mdÕ­~ÏucÄÉ‚”§îÒ•ž¿Ìõý×P:‡ñÃà_ú_ߘþŸt³�òΡïØs=þ¿NÜì­7^ó>´Àú^ßKïŠ ù=þïZYtÔô¬•Þ>´üP}gŸ‘<=Åâ‡:âx±Ù‹÷m�Zv6ƒ°²2ä»êÓ7ëé¹Nƒ¬c×Y -²±{sò«'¾ê]ìöR·ýl~‰w¿ïzü˜}=¾Ï»ËW»òû½ÿR˜žÏO³ÓÓÝëëûžŸo¿Öy×ÿùÕ‘·Ò×òÕ-\’ÒµôýÊê$Ú}•¯ÙO_Oô}aéoÅûðõÙóÙ�ÏwþDxå_¥%ÒæöïIfò-e&Š©ô±ïè-ïuí%³"_Xâ¾üH½kéY-Šžµ¦úuŸâø×çÚü,žEØÜÿ§ö9™7ã~‚ì®­åy9¸·S"è!›/eÖXÝ�Æ`ÅÊô¤–É\ÿH㺕›éV—Rß±�Ì43ÛenÊ“ûîöý,Œy©u•÷VÚVûá²h-¶ïËëïô¥+öú}¬‹ªçü#uô©¬åZ,™gû"Æ®Ç Ûëö×Ò˜97ÙK½)ù+e*ë×G|ºü`ù޽Aç—‹¼‹iÉ%º 'Üvçà�‹°žÚzÖú˰sø·aÇ/§ÛKoÖ[ZÓò:7Õþdx™ÝY¸Ú®¦òW£{y•�\Èxçìþ²åu¥)[¦›‹Å·þcY¯ç}Lk+m~ÊúU÷1óðr®­˜¹¸™7ÛOuÖãäC5ÖÛù=klwÝZSÕëéZV”­+ëJý´­>ÚV•ü•¥]†hr!‹#Xç‚xìš á¾Ùašm¥ñË–Vë$ŽK.¥mº•­+JúѲT­.¥.¶´­+JV•¥}iZWí¥iZ}•¥hýcú?èÿ—]m¶Öë«Km¶•ºë®­)m¶Òžµ­k_²”¥›77[‡—±Øåã`kð1§ÍÎÎÍž,\<,ü¼X"�Ýo¾ë}Ôõù™›­V¾™YøÑßÖÛ|4¿êä[[©JÓÝ�ÔžŸe}�ôôbÏ“Ú(?3CÆö{úÝmeÙäãjâ§§ÙmñÙ�M¤²ÛZþåßJ¾ŸÌGçÈÆç㯖Lü+íþÓ­ÐæA^GÞ\ûˆô®m–Ë~á8]۟ȵwËK/¬Sçir/о•úWý”äWt~&�xµs°z3¢ûW·v™õÅ‹iÌ6z¦â[;}i]®³2+yÿ*–:×Ò¶A™§À–úzû«/šËçzؽöâcäæ_mÔ¥·]íÆ‚Kvëo»êOOOÜ¥b§¯ú O·ò+±6>¶àË©ÑYî»Û]~ºÌ‰«e}iK$“k~ÂÊÝJWùë,�íþgäpoÈ/ÅáòýÜ^ì^·åâö®—åÇKzo¨5\‹w™�‘õ,Š ¦ï½³»‚–fÁ ô§Þµ˜ú»ýô÷ÙHëéJr“¹?×�üûô¶Xê:ƒ¢µ99T¿OŸÇ¸”Üß›k0©K}qsw]ƒŸ½â;9ï»ÝîšÎ=‰ùµ§¶ÛkOu|îW:ÚKõ-Ň˽>�ÞÛ§È�짯ôI.¤Öµõü±~Oýën9Ÿ,ßýJnyça·Vû±§Ødו¯þQ…l–âEOô-²”pç¿þA<æò¦,Ü?#<»ò/¹4¹ù“gËÅ9ßos}×ÇÉŸÝõnÕð9·4áºh«K«JE‡ƒVÛöRÚSìs¶þ@¼ÛïIö2v‡”}ÕÈ0¶ØßsØqÜ.s¸â¼/'ú'º+ø/ŸCÃm¤´–´¾¶àR²[éK«ZR”§ÀÉßî²®­Ól²þÛ=•¶k�mû}itXßJ*Ö¾¿mkOZÓò¼ËOÚ|ù¹ªäÜ�Eþ³o¶ú«}Ôº¶`lrñb¾´¯­>¤PËdRSý ©ZU±Ýæ'–~0Ë4¾8ù7ß½L©c›;¨ûwžõö¿g|RVkmÛkx¶ûY¯ÛCY+ZÝLRÇw­}Ö×Ö­–êO3<²èÑõ‘ÝÍÀuš9>®·�èû ’ÙÃc¯¶û>–O ÉØdq†7¥õ¯ÑÈ–/w¥}¾´¥iôñ·;\?¥÷}†\vÃOHâ¬×É´ô­=¿w’·Á[iëöR¶Ö”¯ÚÊ:� »#YéLŒý~î:[m¶Ç¶ÖÃù´·÷~¶¶ºÜ‰/­?-o¾÷o|züX2]v&/(î´òSCƒƒ¿CäRqÌ롊/Z}æ~[Ô÷õ/anv7Ò¿�>Ëq�}Õô÷zº}Ô_ˆä?®¬�šmú«¼°¾ù�$Òö/^âéwQkìºÊd`ëö][�׸–M44º–dfbg_eõ¥×RúSÙ_¿�Î70ÒÛg¦.e=ô­÷K b–¶zÓÖËnǺ(­¯§ä­l»Ò¿—Õ•ôÞP`_[lä<_/”²žì�>d9¾ù?wÓ6Ü¥gÿç¾®ïøåøÞ¸dºí_— òî9 Xv~–ç9öN›šI›ŸK}/ýÕ�™ƒÀ­ÕáÝ}=iõ9Žeô¥ÞŸo·Öî­tÿâyé�Å×cw¿�=�À¯ú¸�ãí:¿•qÞÍšÙ=-ÊÌØk¹,a›ª‡úû©ldºÏ_O[©KnôX¼÷O³/ 'µ¾”¶°ßU”¶¿–ûëwݯ·Û_Ü¥·}ŸûÆ\Òw/[ï=¶ÃÉq0&­–ÝtšI¨¬u»ý%r3m‹Ké_ËHå½ /ß”4ÀÂÒùƒÄ:o”åa[™—ļ–ÀÚtDºšßïôÃËçߨ³ö±ÖŸG]Èó®õô§úk}zåÒÿ-_Ýïu1¸w”}w£Û}ãš>ÐÉÎê¬ùÛû10uqö^Åä9RKZGé­›2ÚIZ[ëëu´¯¤ÅälÚúA±Æ­Õ¾Øí²[«�%÷ÝéKmŽ<›b¾JÖµôüÚWíû.áÉŠ9ñæŠx%¶—Å42Y,RY_É|rY[¬¾ÚþåiZÑÚÞ+Ë8·:ãº~_Â9/æ>DKKá×G}e¶—Öµ¾žç•ÍÜívºÜ¼ì‰c¾–ÒøiÒǺ–]K­÷cÃHà­iu)__o¯­uªMY|ÀØÔò î†K¥Ònvz›ï­µ’ºììœJKí¯­)5°Ie“[ö~K©ZU�ú/Ê?%¼`ÝäòOü�Þg}v[.ŸìÞg×Yx`’9lÄÝW‰îuVnp+|6ûñò©4[OmÖVß±žzWÊO#¼rË·/¢ûóú²Êí,Ýejøw1ÝêxæÛgPãÓ+Åb˯äU»8î³?"ËãŽÛn¶´¶”§ïÃÚluõ§Üó21í¥õ“éÙ%ßBëëm-­×Áwº+[m¥?:ÚþJ1™4>EóÍ_Ó�kMo"Ç¥ô­÷fcS;éÓÿ…Å•¯ú[_ýúH%ªE>+þ/•ŽŠ»Y¨î|žžòë‰cäàÇ�ý´¸6? ìK4Ø–]˜.wÔ’pÍd[›}¾ìýÞ“�MZÛÝZÖ½™è?Ä}æ¯[ÓW¬îŽ1Ö^Dh±nÌ®Çe±ÕW¬ûclÒK&Qò>¼',_lt­Ü^yeŠÊ{ﬕº[½F:ÙCKm̃6Ûiw­ôõÅȾêÝZÛ[¯Ž—ÁJYJúzR*zÒŸ—×ÖµÍüɲ¥±îñ¶\ro­÷Ë ÛL ]ëéK#ÈÀŽìËëéöúÝ�e¿è¤Ñâwã'øâîõú¯'zû¹|>äóÇ,›¦^ª^øê|+©}¶Ã��˺ßQ�Ùy¹3R¾µ¬œ# )O¶Wnzñx%Ú¶bkûVÞÂñÏ’I�©³'øeDz9— Ÿk°¾èrptÜ·¯âÝìîÁÖMm+&nßQ¥‡é_mÿg¤”�ÖasM6W¥³_6 •úvúdÇZÇu÷úÒêY,5–ÛlŽê}·IôééZWù¾™¿Q¾Òoà®N“m®ÛAmimòkó ʤWVÚ]Hæ¤7Ýt2{n¥}·Ò—S×ò$Ï㯗>.ùsÆ%æ>0ùÔ=ñÇñlÄ»i—Õü÷Žrì®?.tdã`ò­N§?#qÄö×Á%·]‡²ÇÄ˳×Òèí¯ØíGYvÿTwO³—tÿeð>Ñâ÷Ï./ðƒ¯ùf‹—ê,Ë‚´¤øsgès³ñàÍÇ­Ô¤�ßu²GZú]m*õÏLt—h§ŠêÖ–Ë –KÕ¶µ¥Ô¥öVëkZVž•û+ë6%‘_Ôü2r±° —+3" LX,ºI²rfŽ!ŽÚzÝ|³Ku‘Çe´§­kZÒ”y.uÏø'Wq=ß=ìÎkĺë‚ñœ;ö<�šs®G§âhön¶»Lµ=­·[Õ˜›Ì¡\h7½Ù̬ÖqÜýÒ’Ýô¶|G˜CJÙZ]exòWçwãûÇÚåêxÿ`ì|†æ×Yè.‹ÃÄäÚ(®Ÿìœl¬®ÇØæê:û'][élsWY°ÚeÁ}Þ—cÖ´º”óyܳI…ö}çïr~oô<[?Ùw¯ç}ou˜ß›éöÓßî§óùÉ<ŠçzßšÜ3‰[¿7î±YŸ°­•³ÛtrçgGt?ÏWÖ—E7Óìû›¯2¿‡É÷‘9]'�÷uß…½{—6\X˜½m¥Ãì.ÖŸO™‹\{µûþÕìmfÃ ÜØ$¾²CŸÇxÿÌŽê[éÙZÖ>^F~#¿2»CïΊâüÆ®?-¸—G�ƒ��Ú½‰‘V_¾Ã(æZl^÷ ën²”¶.3T>ÚÖÜ�Zýž/7�l§¥m‚ km¿Ÿ_õTôº—V·VÛä²È)eÖúSÒ±Ö´û~ßæa –×i¸È®fÛcŸ´Ë­´³ï; ¼ŒÙý–Öµ¶Ï­“$’{-­Õô§¯¥=Q¥í¾ëîNþæ]‡Þ½³Ù]ÑϳqáÄËæÝ¯Î¹?aòÌœLjß\lI¹-ÚmöòbcVK¾œU›Ùº¾ÚSÕþ×îþäïmý¼§º;O°;W�EL«16¼ÿ–ï9VN»3&ì¹ðu¦srãÓënÈ»Ýn.-°ãÇéJ[e´¥)O#•�™�ÔÌÊŸ&êVúÛõ¤ºûl÷×ÖêEekìŠÚÖŸ’ÚRŸgä~2bçå~œ<ÜÍ~LY˜y89pV·C•‡<¸Ù0ÝZVÚÝðßd±Ö¶Ö´õ¥iöUë87=ç=aÊô¼ó­y§,ëÎqÆòéŸÇyŸä{Ž%ÊôÖÙtvæéyƒ3_·ÕeÒ;î¶’A4wúV´õû^Ë�ö/`õg"ÆæcÎù—\òÜ8gÇÄå“îø‡"ÅÇʶ–dÁ�»ãùÚýœä[m)}¶ËKo¥>ÚUý ÉÈÅ¿êcO6<•¶¶ÖH%¾ýµ­+[}ñÝmÞÚÖ”û?ÐfN5ßÜÿCô¡ÍËÇäxQÒ++â/v]"²¿ŸôöXÕ‡.ùä·ìú“×#Ò´õô¯Ûë!ÿ ÿ_Ë7ŠÕÓhyßgq¿/:ëYxÏ’;·œÆ-n>G¿:lãão³v<�;µŽÜÞG�È⊴¶êã_éu.íG�ÿˆKÎΙ›_«í,Þ#ä§Ç—<œNªƒŽs¨µx˜wcSOØ;]ZæäKK%—7w­ßdIu.õ¯­Þ´õ¸<ßm�ZS.Øsã÷V·{í¦<þßg¥¶Ù,6Ò:R—SÖµº;ë_¶ž¿ÌØ>3ä_ ÜR8w‘fqŒË©J]\‹.Økk}×ÒËl�;/¯o­+ë[¥ÇŠ;iO¶äµÇ�áå÷7KÉ•.f6»±:÷E6ÅËˤõ–i¶¼?UªÂŠÊÖLêÒž©øÅøƒüï jt}«“ɼfçÖë1§Åì,Zïºî]¾w­™º®ËãXócãjð%·ú&~ÿ_Ç ö]mÕôüê[ìµüÇO›ì²i/Àšïe+fM=!÷Öß[ý™6zÇH캞žé>Ÿ¯Ùö~ã9ëöZí¶5™º¼ü=–ž¿O+&¼{ëmkmÔ¶h/’:ÖÛ©éZzý•J#§ûæ<„áX]‘ÐݳÖÝÑ×ûeÇÂæ½W͸ß>âÙXþÚdáÙ½âÛ-¦¶™Ø—]Kf‚²RXnüÛí¶ï±ÛN͸obqÍ_1ëþ[ƹÇÞcG›¦åC{¬äœ{m‰-=Ñåkw:|¬Ívv=ô¯Ù|r]mšõ6_d¶Y$wÙ$r[Kì’Ë©}—Ùu=mºË­­mºÛ©_ZVŸe_µ”^�þ€?“óî#ì­wû¼LIý¶Ýf—W'e%·ÒêÙušüjK•Hï­µ¥$ºÛcõü·QÎ/8~Zþ=þ;02­ò£ÉN ù¤X‘eàôÿž~yÝ;Zf`Í�©¬_áÜr­N»ud>Ì}¦Ò ~›ê_oÕÌŽÚûš»ä‡š~-x�¨¦ÛÈ>éá�{4ØÕÊ×q¬¬Ù6üçw�Ib‚³hx‚§1ÜãG4ö[$ØøRA¾—K}–úÜüY»uŸS7*{kJÖÚ_wôI)mm¥ßJ+}ÒË[ku=}¶×ÓÕ­\«É¬Ù½øü;KfU¥iúKwKr2ý.·Ò—C¯Æš¸¸òYwÛJß,öÝû¶Ñ :‡mòßÒ\CãÇÇmORèç³7>åò>ÌmÙWÁ“�¸yüsª¸¦âN»á{½V]/»ß´Ûs,,«.¥.ÅŽ´­kŸ(¿Fl·äh<;èø±`¥,¶½‘ß7}|«îú™�åG©ëN»¦<Ò+`›73}/º·ßdºû}´­ÞaÏm¶µ³Y‡ïôôþ�™ZÛmkK®¥Ô·+éuÖ]m)Z]Y-¯ÛöÛö}ºíÈ9o%åSýã�n³ö—RúßYÖ˜�]u¶Ûuqpbúxx”º–SÖ‘Ge+ùkö¢å_œÞ`yÁ˫ͼ²ò+´ûÏs~nËSƒÍ96\Ü?Šd죂<û8^ë¿Gð½ÁÌ·Ï©‹£Ökñ®­¾µ�Öµ­c—ä7™ÞTy]°®�}çÏ{& dãæãqÝ–ÒÍWÖçbãW,í7^qÈ4ÜG�\{«móaë –ZÝuoºë®­ká³¶û=—Ù››4Öþm~—­#ƒÖß_mßB*Y¾ž¿—Ûêó­Rkæ€>¾Ÿ}»ãÙTÍÑísõY?›î—*\«m—Û}#žÈÖëiëd”ºË¿v•fþƒò_Èoù¼=“ãovö�Eó˜ìÇ‚nKÕ¼ß�p½†Ï2 …4ÛÚè³ðáä\{#+˲5Ùödàe[OlÐßekmsGù)ß¾5r;yWCv÷=ê½ÅÙX9yßÁC�¯ÕooÖÉ|¸Xü£ŽÖI8÷,×C|—©6x¹x·RêÒèëJÖ•ý¸[,ý}þü,©±ëî¥×[eõúWÝJV”¬°Ýë¾”¯úkjØ.+ä¯ Á¬Xü³]�¼Æ§¶Û³ðm³_´·íüédŠÚ~ŽÊ­)ù,¶Ì·òÜ–‚¿ŒĮ̈ÉÖqO;z›‡ySÂiô1ò»'€bêzo»õô“&½m3µº|íEÍ¢ÄÁŽûqõÐj8¼²Í¾]•m¥-HWÅ_ĵۓ{‰|7avcIŸ�×\ÍvçÜ=ž'ßó¹Ò{©©›Ù}´æG˜?.žøe]†�›v5;³°'“^¢êѼϚàæC. ™ri¿Iëø·—-ŒsÝãc…™6=·Ý�EöVÇÄÙò^§Ö܉þ¦E?ü—Û.Gÿ ¯çÛî¶È2J]O©užë}}¾¿‘«<»¿y·#¬¸ú¹¬âúÛé[i®û®Øßeie£mï¶Ì‹o¥ö×Ò¸öãþm}.÷~TüÿüU&žd͹â½Aʱ|!é¬êß7è-®ÂÎÖÍÁ­ø¹ÿ <�˃_Í¿IãdC%´›‹ÁÄq§Æ“édcMé[î‹—ˆ/ÌŽÿ‹mÅúfÝo‹}{°†l;­àÙÒo{W3 +ìL¨ó;Kaƒ¯›K-f¾³ãOÇõÚLüK½¶Ó*Omo»ìù¶Ë.—Å…mºøn¥m­ÖWêe]mmºÛ©õ®¥-‹×ÝëJÙm/¶´û.a $’i/–Y/–Y/ºù$’믒Kï­nºûﺵºëµ­këZ£Q±Øì7 í¶Û;3iµÚfdìv{=ŽTù» ŽÃ6kòs3³³2o—'/3/"K¤–Y.ºù/º·]ZÖµ«†\ƒ�ïùfïiÉyVóqɹï2mŽë�r žnçw·ØdÝïÈÏÚmv3äççæO}}o–Y/¾êýµ­^BYešK¥šI%–úúß$·Ý$—×òzÝ}Õ­×WÒŸºÿÆøïæí�‘‘‰‘V,ócecMF6N<·Ã‘�‘ öÉ ðMÖÉÑIm.¶ëkK­º”­+êÿ¶Ýu·Rëk[n¶´ºÛ­­iu·R¾´­+O¶•¥Ywˆwo9╇í‡éÝTu¶Úë·U¿&ëbõ²•·aî¦~5mŽÏlvÖûá³××é×ò;åàâIùAð&ýÆî <•é-Epðîé¿$æÛsüM~“Ì +uü±oØcvw¦³M…÷}^$\Ž?�}Ô¾ýVE-úwvOÄ�/8üb»W å\ºÏ$:Óx­ŸŠw.nÃoÊñp.Í—+:.;Ú¶ß/3ÃÍš9¾Ž=v·ï00b²Ë!Ã¥–ÒÇ«Öó¶¶9ï¦Â WùÜ›®úô§­÷]ìʧ¬•­Õ»òÉI=)JR”¥IÄ;ï„òZE�±žî1³¿Ûm`ÚÉe5òIZV·}ßmm-Æ¥–úzzÏL{«Zú[J§'ññø¬þ6<É·Gû³{›átì-ÄÆ›Ž÷~Û#§¶›Yé›$ÑqNþÃÆ×q|}~8Ñûæå˜|Fù'ȶxò+JÜ”‡Ÿ8ÞùW]Oßò™¼xílêbcWƒ÷^¯CµÚMºÉqøweE-¼Gw»M‡Ý0¡Ï¿O¸Î¾;®³_K~ÖAÕò�VζÇIk‰“uiJcåVØë}ÕúvÒ�ËëX¥­ÒIí¶ßZIw¥kíôfën¶ëiuµ¥ÖÝJ]mÖÖ•¶ëkOZV•§ÙZV‰5afáì°ñ6:ì¼lý~~4¸9ØSÅ•‡›‡•“ãeâdÁ|�dãdÁ%·Ç%—VËì­+JÖ•v:Ëí’Ûo²ëo²ûi}—ÙZ]möÝJVÛ­º•­.¶êWÖ•§ÙZ=þ¿Ký 1ÍûLJñ §ÂÅ–¼‹s n²ì d¶Wk~ÊÇ�²ô“­º•¶ëc¤ÒÙu=.²ˆÙ|•þ(o�29\õ¶êO2üˆÒ_‘¯Èë®”ßëi×<[w�ô¾¦¿±»Î¸Û®'¨ŸO­�“‡¡ÆäÛ}~tcgaâ]ëu¼�ókæ‹Ã¯ dÝq r9;¿ºuväãW«z»/a›m™ñljÏyÍÕ›Œðêcìp©n-—gï0þ­’W[}•õyý¯&Öj}ÑÉ-r2­ÿò\Kï¶ïJúRký~œ?m)ëJ×ßé_Z[V¦s^äæ|ÓëcM›úM'¾ÏÑ›¤ÇŠXnú–û3ò½ßzÏ÷Å})}·]H.­)ZEmPù"üB#ß$×o¸5í ôw�›jåb[ã¿BOµá|3o¦š³ÃL.ËätÏÈç¯\Ì+¡ûî&ßa¿. dbj°®¯¶‘4ó;æ»Í.äÚñÍ,¿ ú‹6üˆcë~¤Øì59»=l—eGf76ìnÆå|®ù°²«^\‘R±×£¾ü«ù™á/Ü´½eØ¿ÂγÅõ·ûNö”yÜîáŽï¼ÝíÐâþ‘×ïø_¦F]óÝn“a®‹"K²lž”ö×Ðjù.×UKcŠo¯�oÙLlŸt‘ÛOÉJEwºÙ!ô§ä¥·Rß_ËJ¶Ã…ùÅyÒÃäÿöw{lú™2}]<÷×ìõ�?Ûmq=ßmkIí²Ëiö}Kªž‡Æßâãðoʯàÿ\ù‡¯ÿ�'tç}Ó]ü#ä»[ù�Ü£g'ݱ¾ñ…ÙßuÄÏë/¾Oõ²d‡”âbê5¸ô¶;·™r×Ö²ÉðËçÏÄ%?FñNßÊÿ‚çhåzC÷ÁÜceõvß'ý]'ýCí_»jõzïL\K.¾Íþ6—ú>E˜ø×åßùÕÈÚ¾]«Øûcšï¸d×ìúyÓèÝ_·ì�'ÒØëöRŸÏRÊÖµô¥*Ï‘ÉÑÇ,RY,RÙl‘KÖß‘ßm.²Hï¶µ¶û/¶´­+JúV‰`i7z^M¥ÔrN7·Õò;È5zýÞ‡}¤Øbm´»½.Û,ý^ßQ´À—#e«Ù`äG6>D2_Ñ_mö][kJ×¹°OL0äãMF>DQÏðId°Ï ¶RH¦†Xëu’E%—Rën¶µ¥i_Z=Sý¾£ú€ WÎ{ƒˆðzM‹>OémÝ–ÝKtºÛì’h奵­–ì2~Ø5ÖVêÛî÷{¦¥µ÷[î|Ÿ~"/�ÏŒÛ9Üóù äΦ-Ž>:ô¾×UµÛñþC�‹•\]póŠß—Æ:‹›¡‹2 šgrLx2-ɃM—*æ—›Ÿ+þ"x3�°ÑóÎgü;íøqï» ¤úÞL=÷6�&±ë¥‚Þ_?Þ#Òuæ˜ûh2ivã#+'êK…�™tuŽ¿kÈ5šŠ{rf­ùž¶âÁJI‘ZzÙöÝO[l†žÛýÔ÷Ýoº”¯·Ö´ôiß7îNeÍk6,™uÓid÷ÙúU|�Ç4W}K}» ¿Zdì+|WÒÛíº¶Áum¥iµWyòeø…þCþK¦ßpÞQØ7ôŽK35öxåÑ»¿â»í&DÙ–Ó¶y_Þmåý»>^¾x"ÌÄÙdGÆäŸÌŒ]>µ¾·CÓÍ¿šß0üÄ“mÅõ\†^€é�„Ra׬:¿o›�Ÿ¹Àš,ˆr ç�‡d:ÞKËlÍÇ˾Œ8©­ÒäCm”¿²[Y.Æ{^]³Øû£†êàbÖ”§ÑǾµ–êzS×êäûl’êV¾¿e´²ÚÒ¾•¥+¸ZãûÊ€2 ìþaÁï²ÍFÊéµ¶Ýî¿K°÷eêï¥k}×}8n¾Ù0®¾ù+u×cß×ÝJ{«u>ÇUþ;~h¾A¾1öúü»«cŸÔ±ìnÏÝøéÙôËç}¾¦FLÙ»+q¸†nv>oØíó&ú¹›>+›¢ÛeÝ–Ï“,T¬ué/…¿+^`øE—©Ôp“Íz— &/¾ô¯cM•ȸ5ØVy2q¸½óMúg¯òd».Yi~›# 2«l™Pe[oÓ»Ðêy6ÏU[,²Zäâ[ZR¸“Ö·YK)éël2WÖø+íõôôüÊV¾µ¶­ÁàÝíĹgÒÃÙßoÜßZYn&Â{nÀɺµ­)÷=¥l†ÝwÙýZE}n¯¶ßåXWñ‹ø¦ü ó«ô/\÷Æv„ÞCçÉ?íNU‡“ÓÜÏ:êOHÿ€ÝÛ—�Çô8YÙŸFÊ[¬äpi2äÊÉ�ýœž²V`¾ üÚø�æ-Ún¿ÛÙãïwl®‡>¶ì�¶-š^C²–ývS¼%øÈà6óO+»ƒYÆ÷›]~Vwê1l<§º»*ìhókd|/¯qr Î¿[>f Ø—nvRkxî]ñÅ—°ÇºK=ÚÁå™>8xk©ÍüƒìÍ/ ÅË�"¼{ŽÒëö¼ß™dc_� ¸|C‡kmÈÞïo‡#22'ŽaáR[dÊš½d§âÎØákbúù¹Áekéo»Öé$¯­´­"ŠÊ]$µ§ºž¾ÚWÒŸm})ö´Û�÷ç'äõ›EYxΚ뮷ýI5ÔÛåÅîüÚäçÇí®5·ÛOZÇ·ÓÖ¶Ý|”W?ò�ø¨|äó–þAÖ>9elü,ñÃ2\¼êºï’e×¼ùö¢é-ŠË¹ïnë¬Õçè0v0Cî“MƬÖÁlY3aæåí¡öÉXyùÙø�|Šòô·ñš §�]O‘tø’r „R÷g*À»ï°{³yN÷buö6^<ÐÉ\m÷gãÏ iM¬±_tli·æ¹y~èu¶Ýƒ}iY«í®d–ÖÛ­­)u=ÖcR¾ïô•­ô­)Z_OÈÀȺ£ñ‘‘‘—‘>VTódådÍ.FNND·Í‘‘‘5÷I4óÍ%×I,ÒÉunºë«[®ºµ­kêñ]u×Vë«[®ºµºë®­ku×V¾µ­k_¶µ­Aü_ð—x'sòÞXpîž»Ýt¥Ÿ¢62ßZãÇm-¥¶ës½$Ÿ–[g¥¶zI)ZÿCõ¯­;éñoø‹|ùøË»�p ~K_$ü^ÔY |víýæÂl^3¨Žü_Lnžì_»íy7U_���txØQDzãQW"ioÓË‘}³YÙ¿þo|³ðöÝ' å™÷yÑÚÈcÁ‹¯»o“'ãzØqò£ÅÇà=“\m¦ãI�‹5ðRÜ-Œ}lX˜ÿwÅÇÄ­ÿZÏ[©åû-w²ŠýÿßJ{&º´ÈŽÚ{þȲ>Û«ù×Sì¾—ÓÛm-·Ûù[¡Â;?‰óÈ©n§6¸Û:[uÓivOe,÷Vë⎒_d¶ßw¾¯¥¶Öžú[_±c÷ÆGÎ?�)ºŒWJvü¾,×͗ȾY>…³2ðÇäoÅ:ôÌé^wf?4ÃÄ“+‘u2¦í9ÅûÎTüwï¹qnô°]›µÙê§Ï×Rù)Ó[5/ŠÌŸ¬Üëöñûðç¥×ÛOY1äü̈¾Ëk_|U­k[i[éOu¾ë+_²•¯£!» Þ·Ô~lÌÌM~4Ù¹ùXøXxÖVLŒ¬©£Çdž:V”÷Ë4·YvúÖ”õ­iöÕáû+³z禸/%ìþÛç|C¬zã†ë«¶å¼óžò-Oâo[I¡Æ¦nï�ïrðuZÜy2²#ŠË¥–ß|²Ye¾·Ým+ñy#ãÜ?C·å<³{§ãg�ëò¶ÛîCÈ6Xzm—W…Óæl¶»]„Øø:üHl­òK,–Ge´­kZQÿ.ºÛ-ºûî¶Û-¶·]uÕ¥¶Ûm´õºë®¯¥)m)O¶­Tì#m³êë8vÉ_JÙ'"ÎÇ»ÙO[köêõùÙu×Ûuiý"Ïo­+O¥u+KÐ?ùcüaÔŠý÷JüPꢛҗëöÞ`öo¾¶R·Gu×MÑýK˰ã÷_ —GJn9޾¶V¶Ïº;­®>}bëòøŠ¸×®÷«|×bóCsµ[/ ¹n®úðíLßw‚:eõ�϶<ž]›‹“<Ô³a¸‡Wdø¶ÝÏke§‚Ýsh`¬˜Ú›mÈšžë.Ì¿ÿ9㺕¥=`³òäWùïί¶ÏZR´÷Ò­PØlsöÙ“ì6y™9ùÙWûò2òæ¾y应¥´­òI[®­-¶”¥´ü–ÛJRž”¢Ý¥ÚÝ›Þÿ“v¯rv1í.ÊæYÿ¤ù_<çÜ‹kÊùg ζ±bŸk½Ýefl2ë�‰�Ût•²"²(élv[m"_Ú½·Ù½ãÎw}•ÛÜç’ö';äY7äí¹/*Údm6{¥’X°ñ«5Õ‡[©Áúµ³ Èpðá¥"‚(ã¶Û)�²²òs¦»#.y2&¾µõ¾K½})[®»Ùe¿Îǵº¾–ÛJ[oîR�Äð vüÀëócMF4ÒãäA%“A<_ÐÍÔ¾9b–:Û|rG})[n¥iZVž´}~?È7ÜO}¤å<[w·ã\Ÿ�mõ»þ9ÈøþË3M¾Ðotù�ìu­&ß]66ÃU·Õl1£Ÿ& #š £¶û.¶ëiZ}¾7Éy ßê9Wßîø¯(ãùøÛ]$ã{\íÿI´Ã’’âlµ�dø»n~,¶Òèæ†K$²êzÛZUý"–Xd¶Xd’)l¯­’E}ÑÉe'­·ÛZ]m}+û�™ëß"6:ÚÁªçɵÀ§ÓŠ=äR»L[iOg»:*z[²ŠÚzV·ÓÛ‘JRê×êÝZQ4‰_ÅáÝ}é’ì÷�ýG uÚ]G‘Üwo 8xÿq†nÂ×I>¯»tXµ‹éó®» •Çerò§ÉÞäß-²ƒðñò¾+.›¬|î×äó>1JbkµÝûÄ4ñÿ tÞü»aûÇdqu Â嚬LIýÒgéáƒkX¿�‡³Èž²Y�´üâêVØ76úÓòS6ý.¥}ÔÿΈ,¥)[iJ×ó£§­=)Om}kVßi·zžC¯ƒk¥ÏÆÙ`dSÖ<œi)}¾ïJ]tRÛ_I ž?u)|wÒÙ,¯Ùu)_±aW�ÞOxÿåÿSqîóñ›¶8rõW'·Û¬å¼;a\¨ γ+/E¿ÖeE‰¼âœ«Wl_}Ômqpö˜ÉK21â¾¾Ô¶ú¿µzÛºø>�²º“œqžÄà|ŽfÒò®%¶ÄÜéó~ï4˜¹˜ßyÄ’úcçë³!¾ ¬iifF.DwÅ5–IeÖÓ"Ã<9Û6<±Ï þ¾Éb¾Ù#»Ûum»Û}•­µöÝmi_æVž�¨Îï~þ  [صÆ8 WÁ‘/éMíÖ×èèðe޳Ùuc¤‘ɱ›ó­×cßî·Ò·Ré.¥Þ¶G})ZÓ…ß-?¾|Sêv<;‘m­ï�*eäšzã{¯³¨—/Uf×UµîNWô6ºîŸã9Ðeb_Þ±ów¹�fE‘ƒªÌƶy¡æÏžÿ)>3ø ¡ÈÄç;¿áÏrçê¦Ïâ]Ãóq¦å›+¯²Ûuùü³6¶äáu÷ÊÈ–ßõv}µžxlšì\é ’|m¾÷M®Džü‹¬÷ÉÛ4´­ÞÚ]_ÜŠ/u+ù×zSókéëZz4s›ö7'ç¹[s—ìÁŠO~Ÿß·´¶¶Rûa­×]>G¶µõ–ZßçV”­¶ú[JÅ~J~^|×ùOçôäÞJv%qºûK±ûÿ^ôûÿénººÌipãÌÔqysó²wÜ¢H2gúÛÍÖNÇou¹AñaÒ,X ùçWÉ¿“¾{r)ëÙ|šî-Õx;)ó8ŸIpÜœ¼ ¤‹êE÷�å¿RܾsÉq¢Ç²êìvu“èÍ|Õ‡ ®Ç¦&Ür-†âúÛ-õƒ—WÙ‡ ×R?oº—[YëöW"K}´ûn§¥+O[io­^Ì'<ê8§2ä\/aM�ØI‰%Õ�ïX×EÁÏŽ;«ZC›‹u~œÖz]u)_²K=Õ­—[_µº^ ü„ymñÍÛp÷‰�¹»ë�öMúØy‡»Ù¸ëÎÌÒk'È–.5Ù<aõ4|«SHó²l‚I#³a­»&Iõù8™U¶{v×Ä8<’ð‹œ]ͺŸåh#ØËƒü,á;h«»ëÞw…ƒ•M¸³ŠäK6E÷GðYŸ‰~ß æ·3²ßZýMfç?Q-dÚ¶Ûu¢A'­øòý¶×óãõ§¥ß›éî¶¶ßJ}”­)Z·o®{·�sJA­ÙÖ-$ºØìû¤òÒÜ Œ÷]ôý5YR]ët’]ZV˜òzKOw¥•—Û[–]|=~&Ÿ¾G,â�)Þ·h¼Vó>->¢Èw6ÃÔ�ÍÉs'·WKzC›n'¥ðïw+¡º#º’ÍÍ·çG‹®ÈÞV œ«&ññßó5ã§œ‘iø!—¤|Œž)"“«ù.Ö9t¼Ë#ëi~OWr܈°qy™ßlµÔOf>ælßN¬|{ó.ËZ^K�¸¥"¥~ë›_w®$·{«}-§º·A/¶Ûf·ÛëZÓÒ—ÓÛZÖßJz×6¤Øì[Ñ?>^^.4Ù™Ù0aâcGt¹YRÇ<[üô’Í-ÖÇ”þmkJå‡O\M�1æóšÒ:ß‹¦­.¾Û½·çÝm·ÇöSó©�Ô­²~wÙﺞ߲¾”­+Kš³4ÓdÍ.FD²Ï<ò_4ÓÍ%ÒÍ4²][ä–Y/­×É$—Öµ­Õ­kZ×Ö¨8ï7›®OºÜrNI¸ÚrEÈv› æÿ¼Øeí·[ÍÖÛ.lý®ãqµÏ›#;g´ÙçdI6FDÒ_,Òßu÷Ý[«ZÖ(\ƒ�oùfóoÉùVïoÉy/ Ùfn7܇�l³w;ÍÞßc=ù[ ®ßm±›'?e²ÎÊ–é&ži/–Y.­×]ZÖµci$’kî–Y/–Kî­×É%×_}÷WòÝu÷V·]u›Wò|·È€¸ä’,–+ïŠX¯¶HäŽë¬’9,º—Y}—ÛZ]eö]JV•¥}iWý¥kJÒ´­iZV•¥i_JÒ´ûiZVŸm+J¶/®;÷oÇ«§–×'{¥¥}‘ì+}fÝkì¯ó¾²K}?IcYwúY.¤¶Û_;´¶ØÒîøtüUþCøqè_;%åþRøÇ�_Ñz~Û:›Ÿ#:w_}Ô¿Ì-îï2 vÿ ×K[㦯u—Û [i…³û®>ªI(üpþ .Èé™4}Eæ–G íþ­®F³QÜVV»NÖëìÇf»”ÒÿõOhñÌK­²if–úò,¬÷Ò]•~ï‡g¾ÑóI`öcmë|ðÿC²ÌËiîž+iéek‘m>Ü‹)OJÖêDû+ëï­iéº:mÖ«�ë±öÚ\ü}Ž¿*Ê]N=ÞëköSÝ–]Ke‚xë_Kã¾Ûd²ï²êR´ôY)ãw“}åÿQqŽøñ£µxŸqu?/ƒêi¹Ì–Xlɲ(&ËÒïuYÐao¸§)ÕS&Ësõ\\-¦¾[¾žNùùVò/ÏîA6·‘l&ënŒ×e]'èþ+¶Ì»AÒ˳+u϶4³^Àå0Ö¾œù0Å……t~ì,L[åȾlC»ä™»‹î�Öìl²–âY}}$öÝK©~EÔ¥¿Zÿu)ZSùÛ})éO_Z×9æÎ=‡ ç\‹‚ìk°Ðæ};eöS7"—K®ØGk[cËÇ¥ö{«gº¾Ù,ºÉl¥ÕöÝOZúïÿÇ—É·—¿]¿NÜñ[±eÑG¶—Wbuw%·7yÔ=»¨ÔO<¸znÆáPì5Öl«‰ffLx{,9ðwš»2ò>ᛋYæ­û¹á'È‘^v¼»¦y=Ùcg‘eyÏTr9³3ºëžbû,Š·m´ÑdC]~ÿ8íû¦Û è6Þß§õ/Æ’|y¾ÎŸy�¦š—ãß[ñî»ÖlKî¯Ñ—Ö”¶êú}¿N_JSÒû~ß²ž¾´õ¥w˯;WŽö?ÓÄ¿ôvò­¿3I•%Ÿ^”ôüù°eô²ÜüKn¥i[­¥/³ì÷Ùgºß[Lþ!>y¥‡MÛþN_ƒ-³h©—vGê‰§Ž¶áfvV~·",ŒÝånº“GÇpæ‹6èm÷å̓¸×dy}ÿ'ÅÓÒüx©L�…l¥m‡Ö¿J ßO[/ɺ•¥iöWÝK)_uÔôþv—Ræ�n7;MþÇ'k¹ÍŸa°Ë’²O“‘ºêúÖµ¥‘ÛOK!‚:WÒÈ쥶Yo¥-¥)JQY_}wÿtùCÛü¼ƒì®UÛ]³ÏvWí9O6æöLù«Of6,vÛ›E©Å¶Üm~·mv·8ñ±`†ãŽØ4÷Ÿ}v÷’��ºížïç{ÎÂç›ÚÛfVçu5žÌ<¥žl]6“[�>¯�è0/É’¸øPÁ‰oº¶GJÝukˆó³òö9dæMtÒÝJ[J×Ò–Ùe+ZÛvSÒØã¥kZúRŸ–µ¯å­jùŒ>Ä/ÆþøÙ98YåáäM‹•�%“cäãK|Á4u¥ÖK ÑÝl‘ÉeÔõ¥iZV•z>Ìyw^ò®;θ)ä|›ñ ο‘q>cÄ7{>5ʸ¿ ÔeG›ªÞñÞC¦ÊÂÛiw:ÌÈl›+h§†[iu—RêR¯SÂ9Ç2ë^Y¡ç�}Ê7Ü+šq|øöœ{”ñ�¦f—{¦ÏŽÛì·'_²À– ¬i.ŠK¬¿Ûw¥ñßu—R¶ÝZWûcäO‹5™òß ÑWÖÉ#º¶ßmkJÛ_JÓ÷.¶µ¥iù+JúWìnOXwþ6Ò°èùÔ¸øHâÄßz[ußÎ{6VÒ”‡_“ZúWêÓÛ�w­}i¥=ö"|"~+î1Û’ñ¾Q¹ëÎÊ’Ýn‹€ys$X|k®»*K ÀƒUÞX˜ñchzÓ•Í5Ô’œ’ bqŒ«.¿ï‘j®‚“gMâÿç—‡w¼œg¢<ÆÍÑõ×sdG�¨ã=»é�¤ëžÐØÛ[aÅÃävB×õ÷7ÙÙé[kZǤØäÒû`®²âàÉ•xÿ.‡aô°öÜ|ë«ì²Z~n>Ußé)O·Òï§Ùí¯æÝwóµõ­-¦ÐR¾¿m>ÚWí¥iû©ÇÙ}’Yl‘Ýmö_m·Ù}—Rë/²êRën¶ëkZ]mÔ¯­+O²´I>•õûiöÒ¿m+OÝ{Qþ€üæšx¤Ÿ"Xà‚ï–i¦¾Ø¢Š(í­×É$—ÖÛ,ŽËiZÖµ­)J>'%ä¼s†qíç.æý'âœcQ±ßò^OÉv¸:.=Ç´:ŒIsöÛ½æëi>.·S¨Ö`Á|Ù9Ç Yu÷Ým´­_Ÿ//';;'  r²ó2æ�î–|Œœ‰®²( †+ku÷ÝZ[m´­k_A§}©ßsæ_?àyWãáR—Å�È£¥Ñåe]_[o‡Q[½·âã[OË‘éIo»ÿ-û-¶—I]wηâ¦å»?+ñ/ã–n8GRÒÜÝfym«®ÇAÏû.·Û~.gèúË߯¸$VVúdr;­‡}·–ëmÁ¦· §ÛDcåGç»':NQãÏ‚<Šìm}µÊÐóo%µ’_f^möß~>ÓSÒ²ÖÛ~í…émÐW”}²Mî¾MU,¶˜Û)1×!æ4¶’aiïõ»Ö¶KŸo¥m·Òµ¥öâzÒ´ºµôôúŸ“Óí·×Ö—SUÐsDó###/"|¬©æÉÊÉš\Œœœ‰o›##"kî’içšK®’Y¥’êÝu×V·]ukZ×Õ�nºë®­×V·]uku×]ZÖë®­}kZÖ¿mkZƒø¿à`ú¯¼6Rì]&’m—¶ÛqàÈô¬Û %–ÓÛЯ¯»/]?6°×ÖøìôúUô·éß+ïƒÄËÛßyCÆO/òy?xøQx\{Œíí’]×mxÕ… ,ÆÖÉÂfÌš’ó^¨ÖÁJA‘Åò%¶}n-±Ë¦š+q®Õldeñeó£Ì|n·�t'–™Û¾Âè,{±´ü_±½™{¾Áéím,³ *(íŸaÎz÷Wí§·ÒM¶³·[‡vL1cëí÷œ{˜_‰Kpö·_65+JE—é[æÇ·ÓÓÙ-)Jß<4ü´¯Û}¿m?:ž”¦ïëvZýÆ6ËW™�Ÿ�—%ÇÊÆ’Ùa–ÊÖ´¯¥Ö×ìºË©[n¶¾—[u+JÒ•¥h³«¥»¯©|‹êîÝ}Ø\[´ú§°uï8:á»H6ú-ÖÒI�=±Ï ~¦&Ç[�¸¹ØyE™�›¸Ù1Ev·g�,ø™xòÛþšË«éZV•ô­+FR²û$²É#¾Ù#’Ûo²û.¥Ö_eÔ¥ÖßeÖÖ¶ÝmÖ×Ö•§ÙZ?s(=3ý>nãq¬ÐkröûŒÈ°5Ø1Vlœ™«_m–úÒÛm¶Ûit’Í-õ¥¶Ye.¾KëKm¥kZQ„<ŽòG£üGéŽsä'‘��¡ê¾ ë�_énWÌyùWÁ‹“G‰�­ÖëuØùÛ®CÈ·{ âÄ×jõøÙ[ŽlÑããC,ÒYe|ivŸ^tŸ_òŽÓí~]¦à½}Ã5·í¹'(ßäýÛ]®Ä¤‘ãÃgæÛ&Ff~~dñãâbÁd¹Y™RÇÉ4–Ywòšh±â¾yä²(c·Ý$—ÝKm¶ßæÖµþm~Ê6­ í.äÚsËäÕk­›UŬ–ۭº¶Û™³º+½ÑMµ¾+ï³Ùmô¥öcÛuÑÙu)uk}ÖÛuµYüà~!îøùQÞîzcªéÉú/Á�fç#OÕæcbs^â—I�ff�–÷Ön“736°gãÅŸ…űr²´zœÈ šù69¸˜Û ­ò§ó5Ø>lgìº{¥$äeâÞE¶dëäšíg4î,œzÉJl¹ýÚü©#Ââ1ßu.ÂãÖI,7Ie¹Y·Í=1¡ÁÄÜ‹•K²ºüL¯ƒ_Oͺÿβl¿²´º²zWó1î¥})gå­>Û¿/¶Ü(Ž…ïɽqÚμͭ1ë\ý\¶ß²ÓM}i—R”²¹XRV—}Ó:‘Ò”÷R•¶JR”¾Û½¶VÞÍ|?üØù?ñÙòäð<™»3Æîm½ÂÙw�Ü‹k.7ä’ÇZùùÙ_}Ýyٰꢲv˜°ÉÂ,|xvXù�ãbÛ�Õ�?•.åøûçk+>Ó±¶É̺èxÇ4öêÝ,VÖ=~ê*} Ûi%¸™¸^“AÈò´ÒÙõ¾}}Õ­%Æõ¥k.¯­eÆ­Õ¥,’ÚÖµ­¾´¶ÿZÒ¾•ôºÝÿ✯KÌôØû½O×Å›ó%ŠúR̬«m¶é°³a¥×}˜}Ôõ§­mºÚÒë+u—[um�ð3Ï¿þH<|ã~Gø½Í­åCm[5|ŸŽm!‹WÏz¿›A…‰™ºë®Éãväe] åº*fYîúräëóñï‹3_•™�‘�•4ûD¾Ÿ“Ö–Ûm+uõ¶ËnºšSçÏÈŒÿ>>r#|ŸæuãÜW[%5|[Ši¬ÃÙvh󈤗YÁ:׌äçk«Èy6ÂØî’ï|ØøX‘Ë™�‘‹‡Ùà&|žé�:›yÜýé˱¸§Óß,vÙ\Ý÷'ßf[%uœ_‰é!º™›ÞC²ú7Ýd1SÛ Ë‘=ðãC4Ñþ\ÜÜ]~=ùYrÛ 6zR·WÖµ­ÕþvË-¶•ºûîýÊR•«žý…Ù[îÂØRm…ÿtÕcIuÚÝ,Öì\JVžÊK-þÛ+™›uŸeÒÝJzzÖ–[eµöªHùgùŒò�å·¸¿†»Ÿüén#´Î—¥|rã;9òø?Zk²"¦6;Û±uÓóÎÄÙaYÿTwù°G$—Ë$XXø5� *ý¾H~N{›ä7žãɾ¬ü£ø†Ï#/­:s_�\�v·&ø$ÂþòìÈãÇ·”sœ¬dŠÜ™,¤Üy¥ƒ8­›*L¬5¿ä¹©JRèpa»××Öµ»Ò´úóÖŸeó]JÖ”ýË-¯¥?-×]ŽÜ”s=瀳‡s]÷ÛYµÑäÖ:Ö¶[™…-n¿c�ek_»æAK­÷ÛOu}·R´’:Öµ¶êU½?"ÞPügwÞŸ¿|bæ×é6v_……Îøçï[·íÎ%�•\ŒžØüj¬Kvúœ‹d’˜ù0É�³ÖM%r02q²)Ii¹žyÏÞöäŸÓû¯«Ú}Ç_ØÝq¶žzðîÊã˜sË4zÍî$u»î»]wÞf¿Y´†ÚfkešOen‚|¬|�¯¦Üåir©‘�_|wûmÉÆºêÒ<ˆéZúRµô¯²[=k[/¥+[k_Ý¥n¶½ àƒ¤ìE» môÇ΂–YµÔK%·eë².§ä¯¥-¬ørÖ•¬3RÚ[%)ZV–ßmö[mÏÄï˯Œÿ-}geôæoð?µ8Ž6«¼¼~ä[L<¾qÕ›7Û«$RÅÜ¿®wYqÍú’A��Æ(¯Žx0³áËÀŰgÁß;º7Ï^§‹±ú‹q÷mÞ¢Ý~cõ¾Úk,åÝsȳq)‘MvÛÛvlË£—ôvÛÊál-†JYu³Ã‘£YµÃÛcÓ#Ow§¶’ÅwæËõ§¯²K›üÊÓÖÚú}•«Ýº¬Ý7ÑxŽwÏ´\SúKo-dÈŸêG¬ÖAu¿|ÙdYm.­‘R¾´‹/u¿Vk©ìŽ—RŸm×YmÜÊùLùYñ—âw oî.ùÚɽæ|§ô¶£¤z;ŽfâÇÏû—˜ëpâÈ›UdÖd[Ǹn†ìÌk÷ü�*ðtÐdÃe,ÉÏË×ëóuÌÏ5º7ÁŽ¥Ëí^êß_2nÊ×ð®©úY\ð¹·dÇ¡ãZÛäŽÚûiì®Vl÷E…ƒöÝ<–û¬¶ÿñÙbjñ®ÊÌ“Ùe¿e–ÛOt³IéZÛVzÓÝ%ÞŸÍ¥)ùkZR•«ž¼Ï›ïyÖÚí®ï#Ýì÷ǃƒ­˜zìkï÷ý X¿›_³ß%Þ²IéOukéJR¢ÿ‘‘ÿ(>N{÷gßžMrûv™ÐÛ›ªëÞ¿ÑG.·®ú“†ägÉ��ø†ù§»+®·ï9ÙRdí6rGl¹™3ßmµ¶½9|ëîß=û~~ÏíÍ�0ôÚšgk:×­u9Wˆõ§Ëɲ{õšxo¶*ì7[:ãÃ~×m=Ÿ|ÙM t»éãA‰‹�…7;œ­ÖUr2+ìŽÏu¸ØÖÝZÇ�kOZR¾”÷Ë¥+}õ¥+uiû”¥¶ÓÈ41¥Ï�ûœw‘î8¦Ûu£Ì¿;µ¥.¥)tSC§ÕÆÉ†ï['Æ–”§ºÛ©ùiJÓÒêR´Øÿ¼´ïï{Ó†yãG`í:ãµ8>L—köØ4‹+[¸ÔåÒÈöü[•hòí—YɸžûÊE™�—�ËJ[})l±Ç%™ûÆ_&û�ÄNàã}ÝÑÜ¢^5̸õ×ãdÁ5—åñîWÇrå‚ýÇæZjM[Þ-»·?¯×Y$SEN4�fcãäEûµû ­^Tyx’{$³ìºÚúÖ9£­i[ášÊVžø¯ôûiöV•¥+JÒ´¥iÐ>´í?aà{l¬x<‡ $Új.º¾´§»éÝ™�uÿnN òzzúV·C[©mÿ–Û¯¶{áKç¡>]:ššè®Ôu‡—ýuÆpv=ãÐWåOlwcÓ&-Lý�Ô™)dÌå][³ÚK ³Yõ26g/2 •×R}~vÊÂŽÏ’n–ùëhöüK&)ÜgUƒ7jtþ~UÒn8¶dÒ]‰~ÛG“4PS“p¼ü¸ýq³à¥n†’Ç]�d]ôëšôÛ¬MÖ5&‚¾É¬¥´Éƺ¾²A}}~Ï_J}H®­?6úSÒ´ü´¥}m¦Qw-Ñ—×x>Àì 7_i®Øìn¦FnE$�S©ŽJ[“±É¶”­iJÖ—} H=Ô¬ÓV•¶;kJR—_u–]Éÿ—?—?¾$|zŸ´;Bx9Ÿos86š¾‚è-^Ò.WÚœ¯ ¾¶VTßK.^1×b\¸%ßo¥‚X°¢–8 �'a“‡‡‘¤yùçÓ^tÖWfönU7ŸqLÝoWuv·6 nQÙ<£ä®d�"ºž9©®DRíöòÅ&>»K)K'ÊŸ'æm¶Øº|[²rnõ­}m‚ kO©<žŸe–S÷-§úk¿%´ÿÈ¥yÛÊyVç˜îr7{ÌšäeÏù‘Ço­˜ØXÖÝua†·]H1a÷WÒžµºë«[®­×ÝuÕ¨sͯ7ü�ù ò•ù)å<ÉæÝ…É}ºýfK�Ãúû‡aåæåhzë®8Ýr2qø·ã—l&®>-—Ë>FDóææM•°ÊËËž¼O-|¾ï5;kiÛÝåÉîÜî'ûÆãØ݃ĸ¿2l¼>)ÃôÿRZ`jp~·¥d–ù³rï§ÖÊžyîºK°®Óm™·ÈûÆ\ž¾ßu!†ß²(,º¾¾Èíþoäõº¾·]éOZý”yÖ¥5…óKQ·ÙhvX›}F\¸;i66L5¥/²úR¶ÝmÖÝK¬–)lº¶ßeÔºÉ,º¶ÝJÛZј:¿»�žãëþÿèÀßuoõ~úG¹¯ž(óõyñÅ..V6N.TY:ÍÖ‹u¬ÉŸg¬Îƒ']µ×dχ™øÓËù{¢{Û´ükí>)ܽ5Êó¸=áùÖæk6xw{ñò±ïô³?M¹À¾µÅÜh7µº Ì9íºẴ­=}+OÙ�Ÿ“­É�/JÇ,uÿ£d–WÓݶúÓßþŸm?òiéZR®‚õgië»]ôfú8<—k³ÖRêÒɬ¥m³ôŽ»ßuo“ KëJ]mk[ྴ¶êÖ•²ûí‘ø'ùØê–®§§ æ•ãýeæ×Yqü|žÜê½ÃÓÑgäO›±áÙã·e­ºIóøÆ|öcdß64ØùÖü_ü u¯È_Z×"š¾äWÕãIÚ][Mô‡&_{¯é™4¹»N ´Í–ËdŽëåËÒeËn&]Ò[&fvfÐï±÷Xþë}±eÅm>ó�ëöÛ_Éõbõûn†êÿäÛ_²¿¹Zå”�ÝO}àƒìÀÓuöšíŽÆêdfäRHõ:˜ä¥¹;›iJÖ”­iwÐăÝJÍ5i[c¶´¥)u÷YeÜŸùsùsñëâGÇ©ûC´'ƒ™ö÷3ƒi«è.‚Õí!Âå}©Êð¡‹ëeeMô²åãqÆ%Ë‚]öúX%‹ )c‚òv8xyA矞}5àMevofåSqÉ÷ÍÖõwWks`Æå“Ê1 ŽJáaVHò+©ãššäE.ßo,Rcë±ä²”²|©ñ12~fÛm‹§Å»'&ïZ×ÖØ ¶´ú“ÉéöYe?rÚ¦»ò[OüŠW�¼§”îyŽç'y¼É®F^E}±Ço­¸ØxÖÖêÃ…… n»èbÁ=ku×V·][¯ºë«P—š^iy çÿ�¼Ûɯ&¹´üϱùœöÁ[6àÜS l™8ÿ^õï“'..1Á¸ÄYr[‰‰l’Ë,²Í——6NvNVTõÛù]åwrù™Ü¼‡»{·�×qÉ7û–ŸO…õñ¸¿âøÓÏ.§†ðÝL³äSSǵ4Ⱦ¶Û[äÈÉÈ’\œ™gÊži¤Â{=žVÛ*ü¬«ýk_ÍŽ;}~œ1úúÛv×ò[Oݯå­~Ú¼ëUÜùÀ£©Ûlt[M¶§.lŽ Ô›*Ò—Ç})[kJÒêVÉ"’ÊÖÛ캕²K+[n¥m­hÊ]%ݽ±ã‡lp>óèÎyÈ:˶ºË�bò~Î8ÆU¸»mÛÙ!º´¶hçÂØëv8SˉŸ�—ø, æÅʆli¥Šü—ÓÝÃÙ]Ù\K·º‡–í8Gað�¤{n;ȵ2YI±æ¥—Ó‰—�5’ám4ûL)dÆÍÂÉŽ\LÜId‚xäŠKì¯éÄËÈÁÈ�+K¢š+½mºŸ’´ýÛn§äºË©öV•û+GAº³´õ݇®ú3}Kƒ µÙë)uidÖR¶ÙúG]ﺷɅ%õ¥.¶µ­ð_Z[ukJÙ}öÏüüìu?ËWSÓ…óJñþ²ók¬¸þ>Onu6Eغžo©Å»^áéè³ò'ÍØð�ŽlñÛ²ÖÝ$ùüc>{1²o›l üë¾/þP:×ä/­k�‘M_ò+„jñ¤í.­�&úC“ /‡½�×ôÌš\ݧÚfËe²Guòåé2å·.é-“3;4èwØû¬u¾Ø²â¶ŸyÆõûm¯äú±zý·Cuòm¯Ù_Ü­rÊ@î§¾ðÄsîw©ëý›}—¬ùÝt½dWÛfFË3Ûî¤vÝZ]ôq¢§çM-iZGoä¥×Ýe—s#åoå; >&üfÙw¿qßw)朂lÞ5Ñ�#¦ÚbëùrvXtÉ·U‡‘49µã¼/�Y4Y<��Ë�‘�§Â’Ël‡/a•®×gj/›gõ'‚Ý»îžÖÊ“'éËú„ð­tðGÈ»™dãÍ6»�i-›Ýl6ÖÈ.›3.ûn‹9%º—Ö–GàÙlqµX’fe]í²ÏKl²ž•’in¥kdQ[ZÓÝ}Þ•ÿB”¥k_JRµs£•r�Ï1Üäï7y_/"¾Øã³Ýn65•º°áaC[®ú8°RêúSÖ·]ukuÕºû®º´ùù¿æ×�?!>HsÏ(|”åÂ^ÂæÙ6A…¬×Û—‡Ãºû‡àK‘^7×u¡ÊÍØ]Ç8?ÇɾÌ\zÍ>FDòÍ™›>VÃ+/.zçü¾ò×¶¼ÔïOÞ]½´úû�ÍÑàqî7…‘™à|Ké?Cðþ)‡—4ßqÔàR[å’´ô¿/6iò¦÷O<—]„vÛL�¾d™y›ëù°ÃK«u�EOçc³×÷vêúSÝuk_J~G�jSX_0ôu;mŽ‹e‡·Ôå˃±ÀšÙñra­)|wÓÖ•¥ÖÝK¬–),­m¾Ë©u’Yumº•¶µ£,tWzv׌ÝÁ×�ùÑ<çuÖý¹Õ\›—p^g¡’gi÷ºÚÛ6.d9ZÍÆ›i‡,¸{nt:í¦¿"|L¸'ÆžX¯Ê�%Ý]“ã·jp¾çê>I—Å;�n"Ühvøµ÷Ç[©eøùºÍ–-Õ¤;-&ç_4¸™Ø’Ò°åbM$WÒ¶ßZ?^nF¿*,¼ký“Cwºž¾µ¶ê~K£¾”­=ÑßoÙZ1ÐέììÄÔÝ[í‹ �ëì²Ý¶¶Û¿2ï_KmØ`Rû®’ü îû=+Zß ÿ™ukOe÷Û—ðkómÕ?/“fÓGÖ~^õ>«]|ô¾mÔ×ì ’°àÁÛýMÃ3+q³ê¾G°’ØæÇžIóøÆÎZk³eÈŽMnÏkbÆŸÈß\|†tíÜ›Wîf··:Ëï¶M.“g‘ú›’ñÚK-Ù¹ü‘É•ÞJ}H%ŽLikt‘}I3n—s�ºÄúðþdÑÖ–dãÖêVød­=iüÊÝž•ö]éJWÒ´ü´­)”ÝÕt‰ök–ò½? Òeo7SÒ,x)ì‚kJäçåÝm×Aƒ‡kë&Dõ²¿ûí–Ò·ÝZYm×SI~Aüüñûã_ÆNiäÿ‘\ƒôwÐÔ^Äõ÷Å.í^ÇØàls8¿Yp\ +þ®äœ‹ôd÷Öû½1uøù9ùwÇ‹‘4zù哽GâLò®òî�ÿèN#Æa¶\<[,ÉßòÎE—d¿¡xÖ]$Wmyöx«d1Öë!†;dÈÈ’X'ž?É�›�¯Æ—/*ÿd1SÖ¿»u×Wì¶Ë-ÿM}õû)Oÿcœ\Û™í¹Öû'w´»Ùïþ……ƒ—ß�®Ã³ÿ-bãÒúÿ÷Ò]éoÔ’·]éO_JS¥ò;òß&þPó?';ïgô³·OIÀz÷Y±ÙeðΣëÍm×ÓCÀ¸^.Âi>íƒ�õ/ÊÎȶȯÙí²r³¥²Ùr.¥+™ó«Î>ÞóÛ»¶½»ÙÓþ‰Óâý}W[u®»a—›Æ:ÓˆýK+ŸUvE°~�Üì>…“íö—C »<ß[éöcbcáÎç+u•\ŒŠû#³Ýn65·V±ãÇZÓÖ”¯¥=òßéJß}iJÝZ~å)m´ò-iƒä>Ïßí8Æã y§È®>v ÖËÞ·}9lü’ãdYmÖV\lˆýl’ÏZ{­­%~ÖÁx±åuxcß½kä¯�œÃ3„ö¯Vr}÷Úcß=Ø)K±÷c’àC‘�nóˆr­LÓköºù/¤Y¸Åw¥.õ¦xñ§É>Ûñ/¸ø—xô·"»AÌø¦]+X2)>Fƒ“é'’*î8‡.ÕÁ“‰]ÏßãÅH²±þ¬RÛZY4Á“Åûµû ­^Tyx’{$³ìºÚúÖ9£­i[ášÊVžø¯ôûiöV•¥+JÒ´¥iÑž¼ì O`é,Øá]d zGãU[ýÓë²®¥ÞŸ––Ö\L�en†ZSÒúR´¯¥ößm·|=|µô‡ËwŒø�¯ÀïÀâ]ÏÁbÓh<‹èûó¯ÈÜu5ØÁ›\ì²,�#q×|âÝNV_ÚÛK¬ž(gÅš¶gáfÁ ‹þyßÔþ~ô~»µ:úHôœ«UL-Gku–^m2÷�oÌeŬÒ릞¸øwn8æÓéÉ6£k1ß�mÔºÈr¡ÊÅÇÍÚ�®>ą̃?6ï²Ùà­}×Á7¥+tu¯¥¾ë~ßͻҔºŸÌ¯­)ï�ho ê>fçs­ãú¼ÝÎß*<=~7O“<•ô¥-¥im‘ÙoóÒÏ4—RÈì·ÖëﺖÛJÖ´£ù1ä—MøƒÑ=—ä—�Ë�uSqɹ'.äYõ÷_H¾¼­.Ÿ Û©‘¹äüŸw›�­Ôë ¥Ù;–\ÐÛt²Ùmqçlv¿^to\òþÚí~U«á]{Átóo97$ÛËXñ0pâ¾8a†(ã¶LœýžË6h±p°ñì—+72h±àŽI¤²Ë¿”óÃ� ™[1[[ä’úú[m´ÿäÖµ¯ÙJSí­~Ê9ÉÙƒ±ì-õû �©�«Ä¬°iuµ¯æáâ]¯Ô–Ûnº;³²©m·M}+_ZÒ–Ò¾Û-¥)ßù…ùcî�–¿(ö]¿Î%Øqn›áîxÇŽ=-LºÝ©ë.¿Êϲk³¶8øù`l{/›[‡�•É6Ô­÷åO ±]n¿ í¾M¾G¹ÿÈ_sßÈg‹eÄúK„OŸ­éεÉÈ·êë5SM[få¼²,Lœ�vO=äÑGeù•†ù`Á†Øðà’["»## ò üÛ¼ŠzRèpaº¿vÇ­:¿¹Yçô­m¬÷Ó÷)ëK)öS×íºìzäÛšo<ôü?–í¸V÷}§—Û<±äcß[¾íŸ‡%múøYV[Z{á––Ò´ýÛ/¶ÛíôºÚV›§ñÿç§üny9Á¼£ñÛ�S]Êø½òê¹?ÙI•'í¾ÚO‹(ë^}«ÆšÒÜ_�G‡ôûmÈ×ì1ñv—Ã�‡�<[{á'š³à¯yè{§«r-ÍŠ*[©ç\?3#Žö/ ÈšËö\kuX-–¸Óþm&Àͤrɯ͎9©d–ÒøeúÚ}ÆN›.™0~|wz[‘�uÕ¤yzÿ;ZÒ•öIo¯­—úV¶×÷+JÖ•é嚎i£ÅÞé¦÷Á?¬sãß[~ó�—e-¬øYv[ußNx½Ô¯ó/²ëo·ÖÛ­­nCøïùèO’ï¸W”^>î.›C¿º]4á›,Œ[ùoTöF«#“õ¿6ÄÆ¾ëq7º[vO ”¥°ìu™x¹øþìl¨oºÇ¼¤ê?1ºWŠ÷§Lo?Jñ~GÝöZ¬Ë±bäœ'”bÁ�.ë„ó-n.N]šžO¢¿*Ï«$’à’,œi&Äž åÍØØÛ,X²ño÷E-´¯¥}=ñßéOtRÛJÝKe²µô­=kOÝ¥kOJ½3x›ý€å<ðbÁ6VLÑcãcE$ù_lPÁ6VIf–Këm‘ÅvÖë®­iJRžµ|WʸÇâü“›ónC¤â<7†è7«–ò¾K´ÂÒqÎ1Æ8ö»#o¾ä;ýÖÊlmv£I¥ÕaË“—•‘$pãÁÒ_u¶ÛZÓñìv:ýF¿;m¶ÎÄÖjõxy[–Ëa“¿_…ù9™Ù¹yÇ�‹‰‰�ÒI%÷[e–[[®­)JÔsó¸ûBN{µ·Y$±ñ}T·ýÆ;½Ñ×e•OtwígŠëm¾Ú]eÕ¶ /üèã­kZ[u÷[J•¯Íï ùXïœ~¹êžûEáFo³©ÔÜo2Ì�Lݯ̢�/S±ï~c¥’ز¢ËØàdM‰ÆðsiYôúY¤ºè±s6;Pù–ùQÙùµØóô×Qì2uþ.u†þzj&Š[㟸ùv¾³aËØ{x­öý1‰î’.?ƒwºÿ»ÝvvG¶|›1pq*äw즿úS]ž•ºÊú×2[+ÿ—.ºŸgвïç-§ÙwóÕõ¯¶–átt8xàEë>ÁÎëÝý™ñý\�Fg³w®²ëi÷¬ZVïdÑRÿ̦f×Öø«ëo¯çY[©m÷U×?†Ÿ–nØø“ò¯UÛ|ríß+èÎw~¯‹ù'Ó8xÖcv7Æ›.¸[}L¬×bö?_dl§ØqüÚÉ�Ô¿#\ˆðv´¿¦¿#ÜçãÛ»"ÝÝfË”tW<Ê×k;›®ñfŽ“æk"¾øq9§·&ëqqù¿³&ù±ì¾è¡ÙãýL)¤†’Ç•‹è¸öþm.O¥Þ²`Ï}´É‡÷mýϼCÿ”Í?-?%öý•û}·[Ñ­FÛ]½Öáíõ9Qæë³á¶|\˜«_l–Vµ¶´ºÛ©Kã–+í­—Ùu)}—Û[n¥.¥h¸‡Ç¾ÿê/*:S­¼†ènk©ì.£íž3‡ÊøW,ÓË[±óµùË�—ƒ��%-ËÔr ×#_µÖåYn¯g‹>&TQdA,vØ£×�…»cƒq^ÊëžG¬åÜšép¹äzy鑯Úê¶RX2!¿Ò—Ç%¿m’Å%¶K¶Ý–Û}·[Lב͒Å}²G%–ßöV—[}—R—[u·Sì­·R¿cè³#Ù?ØM;ëµ®ØO•Áxüöþ�Æ’Û9|7û«›•Þê꡺ßÍ·[iõî¥kt’ÛìüÛl»êWø¨~vdîîQË~1|Nä¶i®È,Àò§´´[*ÉoköÎŽoí1Ç%Ä­°Ù×�u½Ä¥ûÜšß-û®A�f4vÁ‡­¾M´?þ{>T¦ÝçïüñÓ–Ånƒ]4úÏ%¹¿Ëú’îv¸ÒV)úKYµÇ»éëÔÏe…?wºé22í·U$‘ÇÏ'óCüö£oüªÝ„±×ÿ"¸T¾Ÿÿ·Óÿ”­Ÿµ«HB"ŽÆà ñÒ�§ Ù[¡ÜKJñ}¶Uµ¾i.ôý �%)3ì»òWoKmȶ¿ÎÒ”’ÚÒ¶Ýl’�ü79�>ë³ÆÞÿÜ×#ÂþæRò=žlÓ]'�]—³‡K‰ÛÚ«i[ã—…m¡ÆÄÃåØ^ÚIn<,[¾¶ ¸{)ü"|§dx›Ø8¾7w§*Š;+sZéw»ì�fIsý¤”²Íí›)nöë:û•äÖØ÷QM_¹àeV=�·c[úJì¿mĹÜ%¦¿:on Õþ�|•üÌI}n¯þ[Çšµüïô¶Ýùße+}[ïmÖÝm.¶´ºÛ©K­ºÚÒ¶ÝmiëJÒ´û+JÑkvnËc®ËÆÏ×çãA›ƒ�…6^&LÉN6L[|rYul¾ÊÒ´­iTíl¾Ù-¶û.¶û/¶—Ù}•¥ÖßmÔ¥mºÛ©ZÒën¥}iZ}•£-?ëô¿Ð×þòí q-eÜoK‘}œ—o�Zß“ÞÛ´úÙktwäÒZWß~U-ºÈ=¾—GOY=m­#÷D›ñ>|áÉàwQMá_Œ<Ï3[æ7|q9'å\Ó‹g}×kã—Mî¾¶G ÅÜcßL�'lö,O‰Ç«‹[vlr7— "šir¸[óaòug…ýSoJõÞÛ|˜î=_è­†M”Èê^•,Ú¼þÀžØ¥·*Iµ’,ŒN;o¥¶Ù• ù—]ZaÛO–åêj1i=Ö×a•mi >Ë«�¯¶ì›¬õõõõõ¤~¿en¥köÒÚѡ긒I&’ùe¾ùe–û¤’I.ºù$’û«u÷ß}Õ­×ß}Õ­kZ×ÖµašÖµ­kZÖµ­kZÖµõ­k_¶µ­köÖµ¨?Ãþ7 ;JÙ£Çà[ü›ë‘+oÍžúßl°Yen®–Y.¯ºËà¶Ú×Öµ¶ë=b§¶¶Çmö þÿœH¶˜\[â¯Êîg—vë )pü-ìnQ�vDúŒ<[²$ñ£q¸Ê¾³beéñqäÈáɺè%Ķ]RÃt<™•üü¡W°ôz_{ÓqJóž#§¾/9†Ç2ûææ\;K‡6NOXlë‘ëZr©Å¬º‰-¾ës4Ñ_�["“Ëór¯äËŽÝVeÖýâé÷I+_K²!²•õŠêWìºh,§ÙZ}·YöÖž¶ÖµÛøR�{°…ì>u¯àw#q—í›2_v6£_ë_~vÂë.º;.öÖ—Y‹ )ïšÿ³Ûe=)ë}Ö[w,~_¾Qº§âƒÄ.UßœÊío"í.C\¾ã·RÏ4µÍìþÖËÁ’ls ÄŸ3�q?êŸ#ØýX,Ç×ÅLhd»c›¯ÆÉÓ?<¼ÑëïÇä;Þ%¿Ñò®/¸ÙqîMÆwÎCÇwúlÜ�vãG½ÒæÁ²Ôn5[ I"ÊÀÙk6ÑÍÑÝl‘Ke·[ZV”«ýÇ%ðɱ]u’E}²G}µôºË캗YuµýË­ºž´t7§û".} ¤Y²ÙNK¨Ž(vñ{m�ïvV•² ¬YKcúy^Ïè¶ÙJR9½ií¶Ë£õ·/ðò|Ìê¾U¼\þ v®ëW�æ—�ºÍ.‡½´Ñâàinì�,¶W�w×Ó`Y�¬¦¿—Ý‹XwøÚø`ÆÓòd¶Ü\<ÝU’Ø£ñ7ò%¦óÿǸv{ûðõ�÷ÕÑê¸ÏtqènÄŠ=†ÂlY-Óö6�‘R9΢š_£ô£¦Æ¬Ki|0Ã<ùÃ�î¬Ý`ÒjÒÛ2¡­"ËŠÛ©_mþŸ›-”þz‘OJV¶úþJÒ¶ú×Ûë\¼�³©ïºü¹ÙØšÌ,½Ž|ñâá`ãÍ•—‘-kHáǂˤ–K½)[«K,¶µô¥+Zþå=^µ»O¯z;¬ùïqöÏ*Öp~²ë%¾ç\ó—î+?èÞ;ÅxÖ»#k¹ÚdÇ‹NnOÝp±¯­�cÅ.D÷ûcŠ;äºÛ+ð9W*ã|Œr.iÌwzÎ3ĸ–“iÉ97"ÜåÅ�¨Ñh4˜Sì¶û}žl÷Y&»Ie’êÒÛ,²µ«üß}‘Ù|’_lqÇm×ß}÷RÛ,²ÚV믾ë«Km¶ÛiëZ×ì¥ÝìÎ}™ØŽm•ÕžN/»G¯–êzbaúÛï–øì­b¦fuöRI«JÝ_çl÷]m–©­ù–ùQ쟖?0y/wo$ßñΔáôÌá^6u&Ó2±úï­!ɲêì¶ü eÕÓ°»/ݯ"˲L©k=`À³*ln±W#ò•òȾ@|�ÏæX×l5}1×ÿ¤¸§GñËä¶ü5.DÚrí®%kH`å\ÿ+¼Ú[o»L+¯š˜vÍ~䛫·9Þë>Ì�®øw!æ{\lK¤¶¾e`ñÝ~Ç' Ù.¥.šKmŠÚþ[¨ý8Øyy—VÌLiòn·Ò·R¯—ÙK«éJßì¶´²Ú×÷kéF[Òôdmémókpô�ße·Ù.ç:8«u.û}µÅÁ·?6))OËI"³Ñß/? ¯Ì‡�ñkv{®�á5q�¾¶Ý–#òO²´üVZÛ}·]fË�pnËí¾=²¯¶”¬.;‡u•ºžïO·Ó¯ý;ø{~E;:¾rÞ7Ö��õ±½ŸÛK°136¹˜S[“eaj:DZ'‚X,º¶ý …Úùk%¾ÚÒÛ=éñ¸FêzzÍ÷\:Rú[[f›êI[>ÊÖû)�lñ×Ó×ì¥n¶µ­?rŸk'ë|\ÿËWî9ÿFÆÖê½?sòE�•™_OÎýÚãþJ~O·ìí¯Lþ­¼ØÚ}—�ß zÝ~_Ô“øAÃzg¢2·Õ‹èÙô©§ì¾oØš9m“ëÝwº³ñ:ÓÙm=>Û«ìêZ~®‡—fOqyiʹÐÒ’ézÓ¬õ7.)ý—{«g'å<›�Ñ $­¾”®¢+«mµû}nüßA�À1m­ßzØäMo¥=´Ç†o%Í­)ùÖMŸƒWWÒ´õ¥1µ�Ëo¥këOÏ«¤œ'ð_üYqêâdr¾ÚóG°2ã‚¶fâì»3©ô©îŽ[>¶>èývóËk%·ÛeÛ9¿>ÊzÝ[kuµÜÞøs¾<8…d¿’S¼»Ré/­ô�›ö^&®)XkÓÇ·­x¿_ÏH©}~¥>¬²ßï§óÞÏÍ}L~¢†—RH²2ëZúÒìŒ›í­´ôü–ý×îÖúÑ¥jô0ô'WEüÿŸ#óiOY·[»~Ú~[¿Ôû)î»÷sýÜñ_“ð‰Ç©m6þ0ó>w[paĭܫȿ 0ë~D_Kß´»øØÜ6”ÎÈúu÷Ûm)�O}ÞÈmüßnmÆø"ø³‚”¤¾4åæÖ–[o®Ou÷õ•­Ôôõ’¿síJ{îôû~ÏoÛöR�Ñüãßúoÿê¼ïý)íÕ_ú«ös‘ÿ×w³ÿ5Çà£ü†ð™óÿj ú¿æ*ø­ÿ%¯ý}ÞF�Óø#Ç¿ôßÿÕyßúRhžªÿÕ[ÿ³œ�þ»Ÿæ¸ü�Ïþ>aÿíAó|Vÿ’×þ¾ï#?Æéüãßúoÿê¼ïý)?´OUê­ÿÙÎGÿ]Ïó\~ ?Ègÿ Ÿ0ÿö �ùо+Ékÿ_w‘Ÿãtþñïý7ÿõ^wþ”ŸÚ'ª¿õVÿìç#ÿ®çù®?ä3ÿ„ϘûPGüÅ_¿äµÿ¯»ÈÏñºx÷þ›ÿú¯;ÿJOíÕ_ú«ös‘ÿ×süׂ�òÿÂgÌ?ý¨#þb¯ŠßòZÿ×ÝägøÝ?‚<{ÿMÿýW�ÿ¥'ö‰ê¯ýU¿û9Èÿë¹þk�ÁGù ÿá3æþÔÿ1WÅoù-ëîò3ünŸÁ=ÿ¦ÿþ«ÎÿÒ“ûDõWþªßýœäõÜÿ5Çà£ü†ð™óÿjÿ˜«â·ü–¿õ÷yþ7Oà�ÿÓÿUçéIý¢z«ÿUoþÎr?úîšãðQþC?øLù‡ÿµÌUñ[þK_úû¼Œÿ§ðG�é¿ÿªó¿ô¤þÑ=Uÿª·ÿg9ýw?Íqø(ÿ!Ÿü&|ÃÿÚ‚?æ*ø­ÿ%¯ý}ÞF�Óø#Ç¿ôßÿÕyßúRçZ Uv¬&Ü g�:\ín¿6NO´²üÌL«ì³ ÙeùñÍu¶û¯­}¶ÖÿJzý¾‹zðaôgptgLvÖÃÎnØÓgö�Sõ×bçiðú{‡äáê³9·Óò\�n&DüÂÙ§ÆÁ›gtQß})uÖÛJÖžµMO�þÿö¼wA´Íïo"©™²Òêóòé‹—Ö�ãS'3 ‰é�¼&X ú²WÙmÒIu¶úR·]_¶¹kø¨ÿÏ�—þfÅÿÒ7îÿ‚ö‹ÿV�·þ‰aÿól±þc÷@—Çpÿ�ŽþìŸküÙ¿ßßÉýëñtôÿùó²ÿÌØ¿úFÁ{Eÿ«NÛÿD°ÿù³üÇî€ÿ/Žáÿ+ýÙæÈx…þþþHÿèÿXÿ‹£ø§ÿÏ�—þfÅÿÒ3þ Ú/ýZvßú%‡ÿÍŸæ?tù|wøá_îÈÿ6CÄ/÷÷òGÿGúÇü]À=?þ|ì¿ó6/þ‘Ÿð^ÑêÓ¶ÿÑ,?þlÿ1û ?Ëã¸ÀÇ ÿvGù²!¿¿’?ú?Ö?âèþéÿóçeÿ™±ôŒÿ‚ö‹ÿV�·þ‰aÿógù�Ýþ_Ãþ8Wû²?Í�ñ ýýü‘ÿÑþ±ÿGðOÿŸ;/üÍ‹ÿ¤gü´_ú´í¿ôKÿ›?Ì~èòøîð1¿ݑþl‡ˆ_ïïä�þ�õ�øº?€züùÙæl_ý#?རÿÕ§mÿ¢XüÙþc÷@—Çpÿ�Žþì�ódv_ù›ÿHÚ—É5qèùûK·ÏŸu´ÕÇ<–ÒÙ&�_�>%’ßm¿›mò[­iO²•ªÞat~¿Æ?-¼¤ñ³S¿Ìåz¯üŒîî�Ör�Ž݇$×õ?frn‡¿Î×cM“��™¸ÆÐ[‘,1É%‘_%m¶ë©JV±òc¬tý'ä�=3dzö[]Q÷wkuŽ�g¹»ýÆÇOÀ¹ÞûŠë3ö·`ãaá]²ÌÂÕY$õ†¢¬·]ì²Û}-¦0ØcÙ‹Ÿ›‹nº³æXñ[-™œo–ê­¬uº±ß6¿2Ì}†-bÎÃÅž-ªð»Ë~Åð�È^ßÝqîÏÊãòͬåœFm–V³SØ<mt6r^ºÈÆ�"–clb‚9ñf’ «0vx¸¹”†Y1¬¶¿OQ´›Q�l6Òÿm+d°ÖêÛlÐßé[öÒ¾´¥Ö×Ò´¥Ô¥kJúzW¥¼k‘ky^“_¿ÔÉY0¶Ò[-¿ÛI ’ÚÖÉñr-²ëí³#kn²úRµ§ºŸekOJÖè <ÄéŸ=¼cê�*ºq&˯ûS�E³�]Ÿ\k9äX—ß�ʸ.ÃÅŸ& WÃ7ØóàfÙ’Á$�ýly&Æ–¤²Ç¡»Ç®¼’êÝýQ»‡Á;E�¼ÓfY|ÊÄ¾ë¯ÆÙèöðA6E˜ƒŽíqçÁØbÖúß‹›�,W}¶U�±r`ÌÇ‹+Ke‚k)|wÛ_²´¯ÙZVŸ–Û캕¥Ô¯ÛmÔ­+öÑ÷[R˯îÓO!{"› ›¸&š{nÂÁšÉyL2Ö´ÈØCuÕ�S_eiÑkï¥/š•­ßêŠRÚÒÛ¢¯­p‹oæR�ÉÏó~.|wäödu_Qòl-—•ü«Eµž°s®åãY²M¬éKë�5¸9Ü_§öpÅ™º†k²)/0Šn‹#AïȈâù!ý5±—À¾™äI§Ôd`m|�ßj¥¾ï¿o1e�?�õY‘ÉH.ÅÒKd[-å¶[%k›LL_©øÙØ÷ãžk¼­´ý‹}i[©mÙ÷ÛJ;_K£Æ¥ß–•»ìºÿOO³Òž¾•º�ZB F5zÎ)Â974̦'ÖM—Ko¶ÌœÛéôuØT»í÷eæÉé u¥ž·RÊVéo¥?2Û«ö7—Á¿�ß4>F»�{âGFò®É¿ 7—s«á³�õW]C—d™æven®Ãâ|rêàcÍ‘�ƒ~Eû]•°_&\þØnÜ�|ò�ÍîI]'Aõ¶Ãq£ÂϦ¿’vNú²è:ÈMJ`É‘û˜äÁ&%û,\MŒ9Ö`Ù›¸“ÿ©$–Òµ}m^“c·¿ÓèVÝ[dÉ–µ�;©Kk[n“ÒµºúRêWÛm.»Ò¾¾ž�®â7qýu�årìÙwÙ´¥.¿ùpµÝ[-õ޲YXö¿NJV´¿Ý·SÒ—Gù}g…ñóø4ü^ê|=8ù ìý¿“½‡¶eìz«ö[þ¸ñÿU‘&-#“W�Éñé¦îÉ·6ß¼cçÅ?†[nú9Él¶êÉ,/‡Œ½K›•yAÈ6~Fsì»fÏÅqeÏáý9ªÏŠü Èà³U�‘,æ”ÀÌÆ–*˱ÍÇ×lqeöϪ·í£#ë8V·Ù&mnØOmm»Û¬x¶ÝmÔºž�ÛZÖZSÓÒ´¾ë­ºŸ–ÖÂë5­&-¸Z}n¯Úû©��‹$5¾´¥+%ÖAe–ß%Ô§Ûu}n¯îÕ.žŠñÇÇÿ8T=qã—JõwFðXd�"î/Õ\ŽpmFntpG�v×k�ǵøÜîòbŠŸ_;.³fd_ë|²ß}ku{õ×ýo×�OÆ08WWp^×<;WõGqn ÆôüS�aVyoŸ"üm>�r2$ºI.¤tºI.­×V·Vµ¯¯Š( ŽØ¡Ž8b²ž–G–Ç”õ­}-²ÊRÛië_Ü£è³CÚ? ‘êÕ#µ¼ÕŽ=þ°hÿyõŸÒP/Zð“ø˜ø‹ü˜zõSÄÖÉp¯öÄ¿­�õ+²°Ù禽ìöyÍ¿®îIýYÍR òÅûS~K?—ÿ™òŠìe_Þÿ5ÿ•¿’®>dÀ;Ÿõãkûå�ý5+È´©šÎ�ÙÀÝïèm¬þÎ9¾ž(ç’i«d:­�iô±ö^—ÿBŽ ¿6,š×Ùý–ßuÞ‘RÚÉËðÌüÈIñÁå'ö‚îþS.†~Rr-N§™äí÷?uã½#Û³Ç�¥â=ß[ ¿Ejô[£ÇÒs ©~»OLMŽDÓSE�‹/~> ¾G­ñS¹ÿàéÚÛ»1:¾¹[=„³S­;k6<}>�’]%´’,>=Ìé.«q$–Û5ÑáæI,ø¹U“ÛpíçÜrFäÝ_ºæIm »Ó×èåßée¾µ§çR9þËkùimÔ¥~Ê{ªßŰÉà2ÐöÿaYÀ¸Í÷âIOáâ“aé#öÙ}`¾–[÷�œ–Iëeb×Ù%µ¶•¶ú]5ñÛ[kmn­8ø†~\p>,|1ϧ^nqmòßȘ7ý{ã®¶‘bæäq ǃ|×¼68yžì;µ½_�´Ç®¾9£Ê�/‘çk¢—|*g]0þW<ÿÓø ãFÓ“éòµ¹}åÙ4Øpî�ãYYÕš»êãEMÏ=Ì×ML™œs®0³¢ÌÈ¥aº Œù°pe¾fÒ[>ÿs›ù©XîÊ—ÖnÃc°ÍžüœÌììÌ›åÉËÌËÉ–é%–K®¾Kî­×Vµ­j®Ws¹Ür=Æ×�ò®Ë}¿ßl³·;Íæç;+i¸Üî6™Rçlö»]žt³æìv[Ùïšyæ¾ùf–û¯¾êÝZÕƒo¾ù/¾I/ºI$ºëï¾û«u÷ßuku×ßuÕ­×]uÕõ­köÖ¯ðüošÿ ÿqÇ$ÒYV_,²ßlqÇ·_$’_u-²Ë,¶•ºûﺴ¥)JzÖ¯ûJVµ¥)JÖµ­)JRžµ­köR”¥>ÚÖµlßXô^æØ7œÚÌ�n®·G..Žžè6;ÿŸ÷g_ëIu¸—úRžÊR™Rµûbô¶ë¦‰ð›øR» ÉÌN'äïÉ,éž�ËŸ[½á>9c]™Æ»›¹´ô¥ríÏçùµún›ë½­Ô†; ²ÛyVÛì‹áý gÜvR�øËü?Û®Àƒ�w—�X;n)³11¶ü_ÇxfÎÑónEO²|LÞÔØãÉ‹³àúªâú]ú±î¥ºZS*m}Ð_�‘‘42·Ò,ÍÅ+K.·ß¿ó­¿íþr컩[n³ÒŸoÓ§ÛëZ{«OK¬n>·Y®ÓáA®Õabë°1­­°baÃdGK®­÷ÖØã¥¶ûä¾êÝuÕüë®­kZÖµ­V/t—Eôç�½gÅúk úÏ…õVpÌ;°¸×à: 9Çõ¶Í%ù¹s×Ã36ÛlÙ/ÉÎÎȬ¹™ùrÉ‘“,³I|—Kï„pnÖœWKÁºó‰ñÎ Ã8æ%04Ë¢Øl"¾ŸnÓòV(«Oõ7ó×Fô¤67þ_Ãi‹ÒxÜ'ÏÏ‘½ÇÌį̂°9O޾3smLyÝ' ”�7MÚýÁǶPß½×}µ³#G¡ÊŽ´átöffYü"ú0ñé°ü9|0êú Žù?åÀÛ÷®lx;¾·ë=æ&>Ã¥â¥Ñfëyï"ÉqçíZ_e’ÃëJÓAZSÙ_¿R·ãå®5Åã×Yfn}–IŸu)u‘ÝJ_f>ÊÒ–þ[k‘üÛ©üïä·÷k]¢N!$ç´r=@:¤v·€:±Ç¿Ö ï>³úJë^“AþªxšÙ.þÃx—õ³¡þ¥b¶Aö<ôÀ—½�þÏ9·õÝÉ?«9ªA¾X¿joÉgòÿó#þQ]Œ«ûÏïãßæ¿ò·òCõÇ̘sþ¼m|³¿¦¥y€5!ó@kŽï¶<_w®ßꤶ<ífE³Ãï¥ÕŠ[kmÑÏ�=¶Ýe×cåA}ÑÉJ]mkeÕ¥+Jý­†ñ;ÊÜð·Èî¡ò—¢·qè{O¦9v/+ãY96äË«ÙE\|­W!⼇+/?‰ó^1²ÍÓíñ£ž òµ™ÓÅI,­þêg¿|‹ì/»ß®{û¬3,Çå}{½‹cL,�mÚÞG£ÊŠMw&â[›.Ž_]?)ãùy89ÙJO“ýh/Žxâ’ÏÝ­ØM¬Íƒ6 ×Ýô­özÒ”š*ý’Ãw­·R–Ég­=}+[köÓí¥5â¼—_Ë´ÞA¬º¿vØAKîŠêÖ²bäÙubÊ×ÖÛ=eÅȲë+_OK½=ÖúÛZV·Wøæ·S|…ø›Ó¾ZtÖM-âý¥ÆãËÛñ©òþ÷¸ëÞu¬’í_:ëŽE}Øšù/Üð¾O‹‘…vGЊ „Å›�ïÄÉ‚KìÐñÇ¿ºûÊ>‘ë®úêüû³¸ocqø781Ït5Øióì’\ïÜÇ�$ÐÁ½ãÌ\Œ Øì¾û-ÉÇ¿ÙuÖ{n®zÄʇ;¼{ýðÏ$²¿g­=~ˬ»Òµ¥$ŽêVÛ©ëö]JÑè[†Í¯ÐÔ#{êÉÕÍù�Ýo#’Ïu=ÒúY>¿Yî÷RÛ¬ŽÛ©‘-=.§¾±R•¥m¾ÕyÿŒ/å’¼—‘éþ*zG‘Òþ?IJ8ßcyµÖK›e›^]ôð¹/Uô¤³Rìl<ÍwÄ›•n¬¥™�I³ÈÓGd°ejó`¬M?oÈp±uþõnóÛ•�›˜ùŸ�&=÷C¯»îÛ®ÕÒÍKåŸlë©ÿie,†O»ÓYe²ßF\5ǼÛuôã·Q�'¤’ÒÙ3n²ëk[aü±ã]éJÝmÒ×Òû©ùµöRŸ–ÛêÔÔ ÑcÕøûÖVfß<ÞãÇ&,ÝoÄšß}%Ë‚K£›o%—SÙìÄ–ÊÙ�ùkõ©uþ–ÖË+tç? ·Ân³·7:¯”�(8¦Ï®ø?%ËÄñ€ïõß|Âæ=…Åv3ào;ãm‡›uóèzÛ�áß�ÆiO¯|¼›+6´Å¿Q…~d´¾>0õû<}_ž=õÆ#ÍŽ<ËëãOÞãE6-2u¹RA›Üû fL7ûòp¶ÕÇã7K[~”±M²²:ÝMnU™3†ñûm²ÍÆdu­÷ý¸1_m=,³ÿ>ëmië[ïôþ‡ù)K;íõ¶´ÜU‹‰s2 ‘êÕ#µ¼ÕŽ=þ°hÿyõŸÒP/Zð“ø˜ø‹ü˜zõSÄÖÉp¯öÄ¿­�õ+²°Ù禽ìöyÍ¿®îIýYÍR òÅûS~K?—ÿ™òŠìe_Þÿ5ÿ•¿’®>dÀ;Ÿõãkûå�ý5+È´©šØ‚ìñžAüÙM[tœ’xâ†ëýÕ³ww¶I©éëK"Ï¥)µôþ{éÝZÛm—zÊãð©ü³ßá—ñ¸y-Ø~0ù�Étú=~FË'7ôWWy#—L.?×üÒ;,®F»MÙ0Ç�Å÷ÒÖéY+§ÍÊʃS5/‘?áúù» »¾Oû#oôz�È]þ=x>VU1­Æá½å“�‰«ÔVL©gÇ–-WeàáA§–Ê[“wéhµ•ŽÈc¿2Zû®ºû®Oè©îÿSæ_îÆºµ²”‹+Ûéí­×VÚûrm¶–Òžµþ‰KiJ~ujÞõ¨ Ȳ°Äö1Æà¼Wc½—Ù&U–ÓU‹§¦^Ó"—[‹m÷Ç[¢�Ò²ËéZ]ôc¾´û}(æ/Ë÷ÈÏøºðS¶<žÜS]³ì `‹¯z †lmú°óžóæ{øV«+™ºÉ2¸ÿ‡/�o,�& ë¡ÓfÓë²k wê�ž\ñ¼dì^ýåâì6,4ÜŠÏ™$ÜÛ±÷´—‰ñœzÉ%“É�~U·fl.‚ÙgÆÔaåäÛŸF¶×çí60ê°gÍ›ó©¿Ðã÷{k4×}‘Åm}.­+}ß–´¥}¶ú×ÓÒŽgffdì32³óf¿#37&|¼¬‰+ë$ù9]4òß_ݾI/­kþ�T¶óÞwÌ;G�sNÌì>E´æÿ±yg"ç\ç–ï2+™ºå×n37ü›‘mòî¥.ÊÚn÷[ òr$¯Û|²Ý_ÝVqØœÿ—v·<æ]›Ï·9‡›sþMºåü¯w“lqͳßò †FÓi—X`²<|håËÉ»ÙVY6zYe¶Ùm¶ÓdäK—‘6L÷Vù§’éd»ù·__ZúSò[m?%)O²”û(üÏ&ñ¯àö½}Ãò9Ï*Öèb¬‘ãI}rv™1þ\M^5m».j]ì’Û%¾•¤QVêVß­%”¯ÙWFþ(>?9É·œý/â—“gªâÜ‹k/+î^g¬†Kåà'Ä.ƒcØ<šÌŸÑÛ\<¾^Ñj4—åÃ\9yÓ_Õ¶É«u7¿ãkÃ�—œ¾[ußIzecðˆ/Ÿ�vîãHãÈÓuoÉÁ»�Ý�}ÙÍ"ÎÎÃÒaͳ]�›µ†{ãº(äôû|Wú_g5Þ¿BÏ\ŒªÒ´õû¼U·Ým?>;½f¾ëcõ·Ö¶û½Þž”«¦x˜˜Ø¸Ø8pÇ�‰‡X¸¸ñ[íŠ x#¶(bŽßܲ8í¥)þ…Tu·\ð~ŸëÞÕeÆuœ/®º×‰ñþ ÁxŽ–+¡Ôñ®%ÅuXºN?£×Ç%òËLMf¯ (l­÷ß%Ô³Ö뮺µ­l°ã¼{EÄxþ�Šq}>»�ñ®5¨×h8ö‡O‰§K¤ÓáïÕjµ˜8ÖG�‡�¯ÁDz(b²ÚYvRÚR”£:[m¶[m–[m¶[m-¶ÛiKm¶Ûiém¶ÛOJRÚRŸe¡í_eÿ@‹?e®ÕcÝ—³ÏÂ×bÛZRìœü¨0ñí­%.›"Hã¥kÿE‹ûk»ú_ ¸µüç½»{«úW„Å=¸²sÚçüS®8´yWÛuöc_È9ŽÛM©²{ì¶µ¥•›ÝZRµôyÞUÌ8—Ñçò~oÊ8ï ãZ¬y2¶œ‡•nõœ{G­Å†ÊÉ6N~Ûo“‡�‡�–Ö믒Km¶”õ­_òë­²Û¯¾ëm²Ûku×]Z[m¶ÛO[®ºêúR–Ò”ûjÇ[êë-d—Å7)ÅÉ–ÏOÍ×bç죻־Ÿ™•…‰>ÞŸ—ÿ.8çÛ‰wáG§óvš�§›w›ïu”¶´Öu'\vÿhál®¬ñÃ}š¾gÃøË®rk·ÖJÖíÍ–Ý•öÖë½¶ÝÏ~ÊùƒøÖ꬈19–}w»ÉÉ¥õ†ÞµÆå½�_§[i}'Øõgæ¬þw­¿xž/})_o¯£ãdr=5-¬›<[½Õ­)÷{«—ZzåTŶjÙÿ“éêòù'×ÐÝu±âò\ªR”­/ƒ[…m·úÒž´¶™;L{é[wÖ”ÿA ¼‡ñ›üNér²1õ½sæÇ-Š`º<î=Ô=Q�‹—Ym޲Y�g+ïÎ3›mØÕ¾´¿êÃ+[+ì­ôöÖíZ忈‡ã‹ŽA�6Ÿ}Ü<úLH¡“‰ufnûKåº;oƒîuµáXÑË�I+uõÉ“ÊÒÊû.º¾Ú]øeæZìºë2¥žêSÖ‘Å‹‘Kﯯ§¥µž8c¥èÝJ?7ü&¸þšywþˆi¿ëûÌž­ñeþð~ÿ‚Ïöª˜§üæ_?Þ›Ëö‡ÓŸãéø‡šüöÙæ_ý,?á5ÀÿôÓË¿ôCMÿ_ÏóÕ¾,¿ÞÏÿðYã¯þÕQþs/‚ïMå¿ûCéÏñô4ÿùí²ÿÌ8¿úXÂk�ÿé§—膛þ¿Ÿç«|Y¼Ÿÿà³Ç_ýª£üæ_?Þ›Ëö‡ÓŸãèþiÿóÛeÿ˜qô°ÿ„×ÿÓO.ÿÑ 7ý?ÏVø²ÿx??ÿÁgŽ¿ûUGù̾½7–ÿí§?ÇÑü<Óÿç¶Ëÿ0âÿéaÿ ®ÿ¦ž]ÿ¢oúþž­ñeþð~ÿ‚Ïöª�ó™|ÿzo-ÿÚN�£øy§ÿÏm—þaÅÿÒÆŒ«A±‰@Xãßë�÷ŸYý%õ¯ ?‰�ˆ¿É‡ ÿUaj`®M÷Y���fÚ †>=ŸKÚ×8ñݵ6úئ¾êW*H2éù¾µ–ÛiýÛm¶R–ä[ùÔô¥-¥}m§ó¬¼�ÛªO¼ççyóÏá*¿]ƒ7¿EÇ.›¶]mÑfgVê[±Ø[u”üû/’:E}×[Xâ¥öú}K¨©;ñ8|¦Yòç†Ï­ºÃ�ǵñÃüŽIÕ[“­ÍÂÍÒsî~v4ÃÛ¸Yzÿ¼cìu|‡¥ƒQ¥ž<¼¬<�“?èß±É�Ož�;¯òƒÉéz?‚î$Èé6~-pò²i®æ±uöasîQ4�cfAÇ21?Ak/ºÉ©e¸Ù™óV…Ôb.g·®nwÜ!º¿vÀºëoô­Ô¤¹ÎËuÖúûk÷Oe¿g­+ïô¯¥XI‡ž0¼Þ9ñ iø¶G&˃۰ä“Wî×_o¤‘i°ïº8)J_e·Å÷̺I-}+[dŽ‘]û”YÏø;¾>±:#®cç8ã–ãv§˜ü½OÎÏÇ‹ôŽ—Ç^·ÛdéµVaÇ•®ÇÚé.ìNÈÃÛl²í¶i16º­n‹*ÊzYeÕ�ßáèñÎŒñ/¿9>¦L>Äòe#Å¿2¼|ÍwQñ›óuÝ{ƒLl™o‚Èù^NÇ{fLG÷Ü ž¿ßH"­3 ÖýËWL›í­'ØÖÙîõõ¥i�gº˜¶ú{î²´ºÛ®’—R”­i'¥%˜‹¿¯\󼟕èx~²ý¯ ØEƒ�m}‘Y_Y2ræÿK�…�g¬Ù3]ëöÒÚzYo­×VÛi[©¦Þpyûâ�Ç_Mçw–=­§ë®-mr±8ÆŽ–ß·ç½�È`Š9#â}o°>¦ï–o¦¾x©/ÑŽÜ=|2}ç?#ÉrcÁžCy+ÑÞ*uÞoi÷ïbhºë‡bÏL\½´²Í²ßmïÇÈ˃AÅ´81enù>ÿ#YlÃÁ‚yþŒRKu¶Å—Ûù²óqp!®FdñãÃJú{䯧ºïm×{#¶ž·É%m¶µ¥¶Ò·WÓì£PùŸ‘¼‡k|˜œGÞ=�ΞØs7zÝJWÒKdÂÀ¶û+O[m¶Y-­=m—÷ñüŒ~0�1ûÿ/uÀüâø¾u=òæaEØ[ˆ4÷ÈžW¬¾»,Kr%ÏÙaløVE³ÖåC%øšŒMžß]™/ÆßÖÚú"Aæ_â@±â^ñ(:Cˆ[7Ò�³ù¶¿GË»[s—ëæ¶mo͇oÀx<3V̬i¢šÎA‘,G49sR¶ÛŽ6¼ç"Jݦ*cÇëé÷©í¶IîôúwzÇ }ÐÅOZ]m}ßSÝmi_Í«^ö=–ß&ìÝ®Ã7e™})Kò³ò§ÌȺÛiém·M‘|’VÛiù)ëéDL;O·»c¼ù¦Ï±û¯³»·û uHmÛó®Ïæ\‹žó ¥˜öÖÌhóù')ØíwqãG_lvß5ÖÇoÙm)Dy{S¹ûs¼¹%üù{7�ö�'º“Gfëžr�Ï(ÎÄÇž{òoÁ×I·Ìʦ³YdÒV±âãÒ,x©öYe¶Ò”§‰ÊÍËΓêædÍ“­Õ¶²Éuô³ß_uÖÇmkíŽÏ_ô¶Ò”§ó…ŽØÍù@Xãßë�÷ŸYý%õ¯ ?‰�ˆ¿É‡ ÿUËKÈxæÿ_�µÒo4ûKåÅØjöÚ̸²1çŽë£–)-ºÚÖ•¥VGáåâì11sðr`ÌÂÍǃ//[&ÆÊÅÉŠÙ±òq掷G4Ã}.²ëkZ]miZ}Œàþ�ºýw_:þq ¢Âž±owÿW[«¬w]l¸ÑÖÊ~�ØÙu—Ç|waãÉKc¾Úû­žHëéZR¨ê~&”9>9üÝqN¶äsé|žòÞœ‡§zc'W™›�½á|bšÜjwpks°25ùšìî ÆwxÚýV^>Ly˜\“}¬ËŽÉbÆÈ¥¼¢ù‰ó’? |Eä™ü[sfww 3úÛ§bƒ"+6šœüü/NSØXðÖJOl<E“õ¡žØåŠ=ÆNº)möOZ¾#ÛSQ­–[.¥2¦õƒŸ›ëIo¥}föÝK©[`·ó¾ÚV•¯¥+ù\ðTd®‘ƒ@SG©ÈÞîuZ\OJdmvšøn­+[l¿.{!¤—úzÖ‘Åï÷]_ܶ•«2øéÑü¿Éžþé?xÝíæÝëÚÜ©8´Ù´—ô~ó°¹N¯Šëö;Ká¶ù Ôkr6”ÈË—ÓÒhï¾µ¥-­Y«Ç”ßyß]CÑ%s®Òëöû(!Ýò9±),5Ÿ�i~ñ°È§¾Ïè××ÖŸ•ûu¸Wì3ñp¬÷zäMe—]m)[¬‹×Ý4´¥kJWéEK®ÿÈuC]�‹ª×àë0£úXzìLlH½k_§�‰ �Cgº¿mÕ¶8éOZýµ^—Ó}OÂú¨º·£úß_.§¯zs®øWVð]dó×+#_Ä8Öñ^9‰‘—u¶]—“�§ÕCl’ÝJ]-ô­Õûj´¯‡q.;À8�àœCU‰¡â|+Ži8ŸÑëá³MǸæ·O¥ÕaA¶Ç&¿[‡QÛm)Kl²”£`l²Èì²8ì¶8ã¶Û,²Ëim–Ym)m¶Ym´¥¶Ûm´ô¥)öR�ØÉFÿ@Ø]�©ëí%Û,êÛ‘Ÿ‘ï‹Qª¶Odû›io»í¥·ÖLz_K¦–´­,¥iJzßu–ÝÉÏ—ß–ÞŠø�ñ¶^ÙìZcs>Þç5Úè¼|è¬]§èí÷jòílx5ÙådeG‹�.ƒ€p¸¶¸Ù[íÅðÝ,sA�%ÎÍÂÇŸGüùó¿©¼é ‡jv%öï9F×«ë<ûp·½“ËâÆ¤Ñëqò~ëŸ]/ÖRHæÛm¤‚Xuø×[KlŸ*l\\�—·ÚãéðïÊŸó®ûm‚ WÛ|ózV¶ÇJú]í·ìüë½+KiüÚúR¼ïå<¯w̶ù­æ]Ù3V¶Å¾ëq0qéZÖ<<+uÔƒ*Wì§­nº¾·_[¯­×V¢6<âòOä¾ùO‘žPö8ç|†iqõ:Û/ÊÄá�uÅ-ÌÉËÔõ÷Zq™ròñ¸—ÐW.úcâÇ|“O5òåæM•�‘“•5y>[y‰ÞÞkvžÃµ»Ó–Ï»Ø].t<_‹àý\>×ú¼¯¼EƸfгM·[�e‘Ù$Ò]6vmÑ[.\óÍë%p¶Óm›·ÈúùrV´¶·Rmû!‚Ë«ë[c·ùµô§­ÕõºïJz×ì£Î5%«¯˜êÇÿX4¼úÏé(­xIüL|EþL=ú©âkd¸Wû â_Ö·ú•ŠÙØlóÓ^öû<æß×w$þ¬æ©ùbý©¿%ŸËÿÌ�ùEv2¯ï?¿�šÿÊßÉ×2`ÏúñµýòÎþš•äZÔ‡Ínÿ޼çôÆŠn!Ÿ5nØñë>®ºëî­oÈÒK%-¶?[®ºë®ÖäÉôÿÒÛl2Em)ùµªÌÁ÷òy7øÙË><;W{vojø£¬»–ôÆfÃ&yö§ÆÍÞæ <�ÔÈ—*|‰ú_œîbÀ¥÷_ 1h÷º|™‹o~¯a§Õeûy7=ŶüÜ «û’A.^>dvÁ,ÚHµ±Me/ÇaNS¶ý)³’‘ßK±0ëv>7¶´­·úVŸ^zVÙ$²úM%¿›u==ÑÛoÙëêÆn69\ó@ ýã–Š›>w&ÖX®º=«É˲OJV;s³}ºühïõû=×cO=öÿ2±úþâU_„Ũ{ÇåNîéÞjò2¸¿ˆý-Î;76M|yÚ_í“έÆêN¨Øß5~Ž6}ÚcÈ7:ûëm×Ù•¡¤‘ÒÛã¥öHoðÝô5�‰æw2î�¦®Í/�½_±ÊÕç_<¶I¬ì>ΞN#Ǥ³ÊÛM™"Qmk'ºØï¶Ë©o¿Û}žç‚aÒmŽFeÖÙu¸xþÛ+Z×ßdùU­–_m)öVŸB9-¯¯ä÷Sÿ#|–§':Êàã“‘<ùySG��‹ ¹Ëu,Š ²éfšKëéK#Ž;kZÖ¿e)G™æ¼ÏŠuÏ å½…Î÷ú¾)Áøßs>eÊwyV`é¸×âú¬­ß!ßíóe­"ÄÕéµ3ddKwæÇw]_²�óÙëôºÝ†ão›‹­Õjprö{=ŽtñãakõøI•››™“-ÖÅ�‹‹�ÒI}Õ¥¶YmkZúPs;±9¦_;åÛ©«}˜tº¸šŒ[¾Ïºjá’ÿ»Gu´ºúRy}Õ’ZÒµ¥e¾ïOÍô¥)[ùlùçÿ(^nv¯“<¦}ž—>nÐÜ>êÙo]tØg[Â8ýØ–ægããï¶‘åM¸ÞÝ·Ã>ûe™|^È+QÖ¹òeæÿ$ó»ÊNeÙÒl3íêþ;—›ÃºG�d]4Xº>»ÖfËfÖí|�ã]�¿æÓY]®Ê²Û~DräY‹Y/‡–`ÞE¸¿q°’[n»î�Vè°ìõ»ÛH©_JÍíºÛ+mù5·Ý_Z{©OKkZûhðÎh¹ðø ¬qïõƒGûϬþ’�zׄŸÄÇÄ_äÃЪž&¶K…°Þ%ýlè©X­�}†Ï=0åï`³ÎmýwrOêÎj�o–/Ú›òYü¿üÈÿ”Wc*þóûø÷ù¯ü­ü�ýqó&Üÿ¯_ß,ïé©^E  H|ЦáÜŸ3‡rMW"Â¥d“_‘K¦Ç÷û-ËÖۡÌĺêÛ}-ûÆ4—[K«mÞË«K©OZQ¸ßþföÇט]å×YÇ\ý÷Oó(6»n5~U˜X¼ãƒmñ2¸÷að,ì¹pöacó>¶Î×Û•÷y¤Àš{2¢·ëAi¶>ùWÊ<.òw«¼‚ãVåæâñ-×Ý9§ÆÉž 9‡^o#®¯™ñ©£³3''+M‘|úúäÖLlm¶>&UÑßX-£êi¶Wêv8ù–û«e—{2#¶µþ‹�'æËg¶—ÇKî¥?:Ê]_m/¶Ú×ò:{­Øâmõø;L i>Ç7jR¶ýL|˜­š+«mÔ¥Ö][/§­µô­+öWí]ÇÒ}Å×þBôÿW÷·To,ä�iÜ< ŠöGÞ[ø÷ìx¿1ÒáïtóeaÍ鑯Ϸ6Û2qf¥³âä[|R[l–]m,áá\ËŒö'â¼ÿ…íñ9æüsKË8¶÷O«…¸ã܇]�¶ÓìñdÿMn¿.9-ýßK¾ßµž¬¾Él²Hï¶Hä²Ûã¾ÊÒë/²úRëo¶êzÒën¶¾´­?-µ“Þ™þ€+¿y§ðk‡ß§Ä—Ùµå?[]¶¿Ÿ®Ëmý-?ÛeÖÿEŠ[qéJÖÛ¿£ÖëkëeQiüWÿ$ð7øûÉñ¿€îþãÞ>r:³î’úgñ·Öâáo.Gw®&^=¿ÂMVç ‰Åµ‚Yaäy8²}]}þÞ-üçù¥ÿ/7\/ŠmþáÛÞHþ•ë÷\�£³Óq0âþÙüËéÏ�—èÎ;Ÿ³"é6.Ës‹5´­#»ÓÍr­¯èÍ\”Žÿn^g®6?¥Þ—ÙK©ý{}/²K~Œuû.·×Û%Öúý�U:¯­…¹ã.ž˜¼Swº¾Ë¬—o¹·Ë«ù%ÃÕc[Ho·÷=)•�=¿ômYkø(ºá”¾Gecäãn{ÛÈ�W^ã}hï¶ îм3I³Ä’²VÉl¿–÷!Å¿ÒÊVÛðëJÖï²–ÎSðØtÕ8O†û·sµ88ÛŽîî-µÚýÄ4÷gíx'[j𸾓:ZÙei¯›dòo£­ÖÙIõ’´¦^àøŸCOvEÖÇîÍÉ–Ko¶ŸÑ+ >�Yd•ô¥2hä­)ëZR—zþí[(šD�dÀÞBòŠè¸Gèœy}™ÜŸ+ô})mÕ¶Ku˜ô·#g-¾–Ö—Yu>”ÛZÓÖÜŠÿ1ïÅáæþGŒÿÿ8–çôocy·Î«ÖòG�•“…³‹¤øZþ_Ü[,  Šøç‡;?+�ñ¼ìi.²ÙõÜ—#í¯¶¶ÝÄ?Ÿ�'òzÁ÷ãÛas_%7ÐõÑÈÁ·;‚M‡‘¹í �¸9¸Ù~Öçq¼;t5Ž–I�^A¶IeöÙZù~_Ÿ\-4ÖY_Is®¦µ§²µ¤r[uÙ­·Ò¾¶Ý—YëJzÒ·Ò´ô«B\ Âà¬qïõƒGûϬþ’�zׄŸÄÇÄ_äÃЪž&¶K…°Þ%ýlè©X­�}†Ï=0åï`³ÎmýwrOêÎj�o–/Ú›òYü¿üÈÿ”Wc*þóûø÷ù¯ü­ü�ýqó&Üÿ¯_ß,ïé©^E  H|ÐåxÙÍk—¯ÎáÒúͬ¤›=/»òݯžj}ÿ•¥–ÓýK›5%·Ö믺“Ýééle‰ŸƒKä¶ng×�™ñ‹ÚØïÝu>.ß»ëml[ý ·ßþêuÿ.ÚÅX«|Õº¸;¨ †;a¯¦Wá;jåb_­šïY°©î‚·]ZÝ~%×zRŸ�ZÖ¿w’¾ßÜ¥-ºÚRŸci“¬I±î@ Ö”¥kZúRŸmk_²”¥?-kWóšhqá—#"Xà‚ïšyæ¾Ø¡†­­òK,—ÖÛ#Ž;-­nºµ¥)JzÕÿ+Z[JÝuiJR•­kZúR”§ÛZÖµû)JPsWµy…Üךm6qËI5¸·×W¦ö×ÖÊë0¤’Øç³ÖÈîôΚëò=.§ºß«íõô¶Š`þr¾B³>J~G;üõ[�Ò�;ÄvRt¿ŽqÃtßq³¤ºçiµÃã܃ <½~·aöÊÝæìy\ÐåG\ŒI··bÖêÇt¥pÿ.þdÉæwš…Êt[{6}OÖsäu?OýË6ÌÍFâÛ È¶<Ë_..Lø–sîI&^Î ¨éd’k%†O_»Û鄹F×ô¦ÒZÇ»×ÛwºËéeÕú³Ûí’øîúÒzú]o§º:[ëùåÈw0pÒŽ¢ÕÓQÖüKý4ú»vwWÒ”­k¸š]­)w§å­–fRÚzý¾–Ñrïáéé : áŸÀŽ#“53ùoNW»ó2`†åÍ�ÈNUÈ»¿M.l°[Jådáñ¾}…‰Kä­Ò[4qÖ´¥”¥,¹ø­ë=oS|vø…ŵ�Í{.–âý‰›MÒ]‘MÏnG7jïi-e­d·Û¸æSÛe•ÿËQÛm”¥)m)Lõ¡‚Ìm6²+-­”ûœÝmÕ­kIr,¤ózû¾ÚzÍ-ÕôýÏÈÉÍ·ýõ€'äŽòí‡8ÆÓÙ%Õƒ�ê±â¾*ú{lÏÙ«ò$¶¾”¯ô\+ñi_¶´üÏú*´ÿÏ“·ò�¡è<ìÏàï‰ÝÁx®fži)~�Ûp׸y6óß»Ãt7mø&â8²Ûï–žºÊ]K©[«e°vüJ]ßw8ó ­úSi®ÍÒôWSbåìp1©KönÚÛáûg-/­,û×Òñl˜a­´ºË&¬žë©-)n)ç™Wa‹‰mÑÝn&5d»Û_Y,›*ÿϲOέ)ýî¥=)_K½~ÚV�{DÍ…uc�¬?Þ}gô” Ö¼$þ&>"ÿ&ƒýTñ5²\+ý†ñ/ëgCýJÅlƒì6yé€/{ýžsoë»’VsTƒ|±~Ôß’ÏåÿæGü¢»W÷ŸßÇ¿Íåoä‡ë�™0çýxÚþùgMJò-jCæ€=äY|K‘ê9=Ójòíšè½im21¯¶è3q+uÖßK)—‡-ñÖïJÖßw­>ÚQ¶~ ùuÏ< òû <ºëhþ÷ÉúC°09<ºZÏ$\³‰æãfqÎÂà¹9²âg~�ÃçÝ»Ùégɲ+æÆ‡>ébô’ËkM¡ð»É�ÿ‡ÞOu�úfΧ_òˆæäZhkoÔä<#u‹“ÇùÆ‚*K4ÔÍÚñM¦\x’KZÇ�™Xf­+Xèúz}�Ú­Ž6m)[¬ŠÿI¬§Û[à’•²kio¾Ê]²êÖßZúRúRµüŽ ëv8›}~ÓZO…±ÄÇÍÄš”­¿S&+fŠêÛu)u—VËéëm}+Jý•ûW{tŸquÿ�½?ÕýíÕË9'Zw⽑Áw–Å~=û/Ìt¸{Ý<ÙXszdkóíÃͶ̜Y©lø¹ß–Û%—[K%´¾”–Ïôf¦{캶ädúâãVÛ«mö_-·{æ¶ën¶û+ T­mºŸ’ÿoó\÷T´®ý„ö”­Õ¥¶Òµ­Õ¥)J~ZÖµô¥)þ�jÿqG$ÒG 1ß,²ßdqE·_$’Iu-²8ì¶•ºûﺴ¥)O¶µ«èê5;-þÛW¢ÓâK°Ûî¶8Z�V¥gÎÙlrbÃÁĆ—VÛk.NTÖÙo­iO[Ÿî(ïšHáŠÚß$·ÙvSÓÖëﺖÙm=})ëuÕôu‹Yƒ³]¯ÖÃÿ–uøX˜1zR”þ‡‰pYöR–ÒŸ›?%)EóÝÕºÞ�èî™éM5Ö]§éî¨ë¾­Õ]Óúwkzûˆiø– Ö},\½•ÅÔ[éí‚}?%–SóiloÒañž=¡ãz裃_Çôº½& 1YdqC‡©Áƒ(£ŽÈã²8àǶ–ÒÛm¥)OJR”ûý¬®û@˜=‰³»qιfÂëþ¥²ï¶1Au=>Ü\L‹ððÿ%kOZbÁe>ÊÖŠB~W;®ÿ">K¼ìîvSmµ|·Ê^æ�ŠçdRËf“�ñ¾m¶â}y–Ç6DV݉Á´Zø},’û)ôý-­iéUfŸ'��—Ûß ¾]s<È"‚±÷o/á8VÅ%e¶]7VåYÕú »®­¶úK�¤áØó_m=ieòVÚVêR—Wr<Šäï6rVßg·*ìJWÝëLJ[‹K½})ößH}ÞŸ¹ëèñ®~´IñXãßë�÷ŸYý%õ¯ ?‰�ˆ¿É‡ ÿUfÏ3;cwå—eàßì† M~¯3S�+éöeγ®^ºü)o­ÓkVêÝ[®Ä“Ö°úÝu÷VêÅ}.³Ò”¥¶ÙK(Ù”Ø7{@<»Ë—W”ó¬Ø`’·kxõ.ÒaR—_ì¾ly.®Ë'ÙZÖÊ_.mn³ÝoóñEU ~&� ùÛò�ÚxWsvÇ¥¼V¦W�}Yf>D÷jöYü+qŸwksX!¦VF·&~OÙ“ì1ñöԶ݆‡W«º¾êGmUùüðùsw’þnò. Çö5Êë_aÏê.9dSMvw7ÇͲ~Ùäãäàa͉™w*ƳG¶ùñò1øü_[faÞe³ûöÖ¸ÖWÖ u.Ç·Ò´­.Ⱥ¶×*ÿç-ºÚÒûiiëu?¡úÓò°â>N)¼ˆÒðÌKsùÁ¾ž¶eò=&4”ô­¡Í³ÆŽJ×Óíô¥—VµÿA·Ÿ}q‹Ü^yøMÔ¹ô¯èþÍòÛÇ.²º–É}"ÖòîàáÚŒ÷Ò+n’‘A…Ÿ%÷Ö”üÛ-­q¶>ñüžSæÿ‡ú\­…vMôe2±ðñ¦Ë–Í^?eñ¬½Æd‘Ad’[‡­ÔÁ6FD•§²x¯’úÒËn­>¦’ËïÜê­²Û¯­6—Ö–Û[«K#žÉ$º´¥+_m–[[«_ÉJRµ«©kσ³àòl3#×àggÍZR,<œÉkZÒ”¤xÐß5õ­kéJR–ÙV2î¾ËÖt¿Mvßqn©Ú~§ë.yÙ{ke’Èb®³‚q]¯)Ϥ’Ë,ÅquWúÝuö[m>ÚÝJ}¯•½Ûbè4›�îl‘Ç¥Õl6ÙrË}±Å.»lÉä–Kî²Èã²(k[®­iJRžµ­&î­n­nº¾·]ZÖµ¯îÖµõ­òj¡fi¥Èš\‰ïºY§–I¦’ï¶é%–úß%÷ï×ßukUN½ÖÏ‘î¶ü‡w—~~ç}´Øn¶ù×Ùwæìö™rçgåßÅwäeO}õ¶Ëm²•¯¥)J}�s–Y'–I¥»ß,Ò_,—zRžé$º·ßw¥´¥´õº¾¿e=ñüŸ-üÀXãßë�÷ŸYý%õ¯ ?‰�ˆ¿É‡ ÿU�·ÇIo² ë$tº¾ÚMe•¯ät?â›Î]×Ç?Ÿ^9ù]…6Æþ+Áù¶6Ÿ¶ôÚØ¦ÌŸ’ô¯4Žþ+Úšx5Vì5˜ÛmĤ¤ÕÖ`äßo­l|ݾ}5šÜ¼ßJVèb¯Ò¶êVënžJÒ8-ºÚ]eÕ²²ßOw¥i_o«™Ê\Õ’äddeäO••<Ù9Y3K‘““‘-ódddM}ÒM<óIuÒK4²][®ºêÖë®­kZú°×]uÕºêÖë®­nºë«ZÝuÕ¯­kZ×í­kPüdž Ä·7²ø„7R•¥›:åúVÚßùعö×Ò•§¥iv5+ëûŸ—÷~øà1vOÌ—Ç·š;d³]䟟RÛ±eÌ¥%ê�û´`“éC$WÙtSpën¤µ­l‚´ú—Òël­µêW¦›{òâ†TQÍ“žnm²Hë-´Éã½EØ<ƒZ[JÛé$zË$²ïô—[K¾ßOG¤â?ì‡_ÿ¡_ÒY.“®zXÜÍ€Øù4ÅàÎjÖ–ÿñ3º†ÚÖêYé&NøÑúV´¯ç}IiéOݯÙû®hüÌsŒN½ø›ùä9’E™>ùÄ1¥—>šß§³ì>¸ßuþžHr«Õ®]›nO `Š”¥ÙRØ­ºÊßKíÔï<ùwðƒÌG«Ì¿]µÔxÉÞyzœø¤¶)°¶ÖõŸ%³Y—÷Û}¿xÇϾ;ã¥i_uô¥?uø6’_³c4WVÉ"ÀÌ’;ééëmöcÉu—S×Öž¶ÝOW0Ôšªøkð«{ý`Ñþóë?¤ ^µá'ñ1ñù0ô꧉­’á_ì7‰[:êV+da³ÏL9{Øìó›]Ü“ú³š¤å‹ö¦ü–/ÿ2?åØÊ¿¼þþ=þkÿ+$?\|É€w?ëÆ×÷Ë;újW‘hR4ЃåŸÂ> �®È–—ìxÅöêg¥×ÛY.ÀöÖýDÕ²ÚSÙ1m®=¾¾µº¸õ¯ª×ßÂ}ç­<¹øËÑô—,ÞÓgÛžn0z;u�•�\½¾WNçáåmú4‚(õÚl>1‡ŸÄp"÷IuÑðë亴÷Ò‹ ¾ |°·É¯xWÞìãÌì処9t2Ms²t<(¥ë=ýøõËÊÍ»?„Ý��vTÞÊåìµY·[OKké›x®ÇôŽž ßw¬øŸêIÿ/­k)ô¯¯ºë®º²A[k[«ùo÷3zOnÉ=J|•å_~ßëxž4ž¸ú—ªõTí^ÌÄÃÌ×åYwaó)p8V£k�lì5;n)ÀäŸ:;+5–dâr¨ïº:û"½Œ¹æÇß..²;é[a§Þ²m¥c»Ò[é[1ì»ÓÖHäŽ*Ýw§­)[d¥}+ö5• ´^Xð—úÏwiñªúú};7wÿÑõÐm,ôÿøÒ ü-ŸyùÐð®o©ìý…ä¶_·Ùîú¾ÿ;Ïéúû­öz}÷ßëé_ç}=>ßZvƒà Œcï¾Múƒk6TØòpžÜŸ(¬²ë3r2úבp˱r+wçG ˜¼ºI©[;êCm?%jõ¼* eÞY%n­+‹�‘=´§§¥×]m¸ÞÛ½sÛ‘ZÿÑ£¢+{V 3wd÷cõ,’˽•»_kí¥Þ¶ån5Ø×ÛéZ]OϲjÛëûž¾¿gåp›ñ1oòø×Áçž,¿¹M“ƺgA|ßB<Ÿ~'+òO¦¸¶~'Ó–í·ïø;‰ ú”¶—Eõ=öÝeÖÒûyÅòí˱øGÆÏ—{œ�‡èÈózºî#nOÝ/Íú™�Èô< _ôcÆËºÏÒù\–ÌO«ì¥¸ÿ[êÖøéed·äoæ² .Òù.öÛvðÒ¾•»Öü‹+�¾–Òµüù%¥=%=}kéOµÎ%< ÙÕŽ=þ°hÿyõŸÒP/Zð“ø˜ø‹ü˜zõSÄÖÉp¯öÄ¿­�õ+²°Ù禽ìöyÍ¿®îIýYÍR òÅûS~K?—ÿ™òŠìe_Þÿ5ÿ•¿’®>dÀ;Ÿõãkûå�ý5+È´©šÍ=Ê¿ƒœóykf¿’Yú&•ºïe¹rßKõS{)ö]/ßm¤4­~Ëlžê¤iø\¼è»Ão•.¯áü“mvSù‡_9¼SM“\,N[Ë6™½+È-ÂŽK1dÙÃÚxX_¼Ïù˜Z½þ|ž¿–•îGÀ•6øý篭9ÞÍ_ù9§³¬6Veåâaë`ì<9åÛuNÒkç†éò3ò·åñü8#’:I7"õ­/ºÛ)Oc¶?tÚ×Kél;þ—çV;mûÌ^ëñ«[®üï[©[ã¶ÚWó®’Ÿekèè:ÜDü|øv{ ]F·?k›}cÃÖáegåIKnº¶câA~D×RÛi[®º‘Ç_JR•­köQ‹;˸¸W�=-Û}÷Ù9³ëºû¥zלv·7ÍÅ‚ì¼Ì~+×ükeÊ·×`áÇý;>ºÝT”‚ =džjÛe´­×R�-Îy§ë~Ì;™làÒqÅ÷üË”îr}ßwÕqÞ3ªËÝn¶3û-ºúÅ…­Â’K©JVµ¥¿e+Wø’K"Žùd¾Û#ŽË¤’û«J[e–R·_}Õ¯ÙKm¶žµ«–{ÝÆW Üí7y·V¹[\ìœéiîºûc®D·_lÖêÖ´‡ÊÒË)ù-²ÚRŸecù)ß\çÊ_ û«ÉËÉûÏ;ï.Ïæ½£É©ù9x.g¿ÎÞ]¥Ô×.IfÇÐè!˳_�ëìÅÁÇŠ)m–[m*Ûò?¼y?’½óÛ]õÌ>ñ÷µ9Îû—K¯ÈØÉ¶ý®Øf_MÅØÍ�‰&V³‰h#ÅÖb]X¢¥1q#¥,²”¥´×ýŽl›ì¬Ù)Z]‘5ÒRÚÝï¬qÿ;^ÿmžêE)m+éOZQòXI…ˆ�|v²ûû" ­¶µ¤Zm­òVŸ’Ë+dQÒêÿ¡Y$¶Ÿôj“gáÖçç|ÍõžVFN>›¤{ïeµšî¾=~¼3ôDy™w[OHqïÚmq±éu~ÊË=–þ[¨�ᵊI>@¹UöYuÖÁãWbË5Ô§­#Ž¼Û«`¥÷ÿ2ÚÍ5–ÿѺ�mÀÿ׌ŸÞÙ¿¦°Ûö¶1;VZaÞú’¶uo"¶”¥~´ÚXë_ü¦”Þk¦õ§ú>±z䣫ø¬9N›áÊ�tÁ,\³–xåÇó/—ê}Lllo"ºÃ”Û67²ûmú÷fq¨£¯¾—[ôä¿ì÷zVœˆùÜš±|Yy3-¥i““ÒpÝZúúÛK|�êÌ�u¿èû±éOúy¾]þǶú ý=ŒçŠ£wÌ&êÇÿX4¼úÏé(­xIüL|EþL=ú©âkd¸Wû â_Ö·ú•ŠÙØlóÓ^öû<æß×w$þ¬æ©ùbý©¿%ŸËÿÌ�ùEv2¯ï?¿�šÿÊßÉ×2`ÏúñµýòÎþš•äZԇ͸ä’#–+ïŽX¯¶Hä²êÙ|rYu.²û.¶´ºÛíº”­+O¶•~ÍvÇa§Ø`íµ9Ùš½®¯3c¬Ùë²fÂØk¶SÙ“‡�ƒ™�|y™˜™1[$RÇu·Ç}´ºÚÒ´¥_c�r ßßèùWÚfi9Ük9Ýk¦»a¨Ýé³`Øê¶˜z_f~4rÅ}>Ûo²•§äH¥’cš+«d±Id±ßOËd‘ÝK¬ºž¾´õ¶êzº…Á¹%œ·‰h¹µ·êl0#®]¶[[,�a�uØ»ì¶ïΤqçA%-þm´¥iöUwoÅ—™˜ ?¾.yaDrÍë-l=•6 5xúþááy9|·ð0õžûïÂÔCÙkgv»Ýéõµ·ãÍm=’Ûëh_‡~Céü¯ñ‡¤üƒÓ}Ò*v_Õí·šü'›GÍ0~®“žñ¸gÉÆÃ›&>5Íuyø–±YI©�õ-§¶ê6_™fÃ6?J[‘ ’VÚ]ï¤wúzK¿Ûoº°ËJÛZúSí£Õ·ý²¯Ø×¿#y556 2{rù>e!ºÚ{éu5šÛ¡Ë̾Ûí§¶ÚÝ“v,�[½sÛiÿF¨É~.NK‹á“³5scK<œÏ»ú�bËö[fø¼ÏøavLô»ó¤Šün)$4¥¿oÔ–ÚþJUÇ�žœ«qþ.¼‚Šënº¹Ûž˜Å²´­=,ºÎìëüÚÝw¯å¶¶aÖß³÷kG›åßì{aÿ ŸÓØÎ}ªtWœÂ`¬qïõƒGûϬþ’�zׄŸÄÇÄ_äÃЪž&¶K…°Þ%ýlè©X­�}†Ï=0åï`³ÎmýwrOêÎj�o–/Ú›òYü¿üÈÿ”Wc*þóûø÷ù¯ü­ü�ýqó&Üÿ¯_ß,ïé©^E  H|ÐÞxÇɽÑoø„÷ý±ÖÝþºÚÒêÖ¶_ôpvvûëù¶ÛeÿvºÛ-k}õþjÿ‚wÍ?Xü¬øûäÛ:~eØ^Vu±SÝZ_N9Ö=ÕƒLé?;ÛmláÙXxVÝéJݰȶÊzÍuf%øf<œý3Á{¿ÄŽA²¥ùü+i�Üýs‹•±Ÿ#*^/ÉnÃã}�¬ÖàIÑÀÓñ¾K�«Ì­,–´—/�ÍÓ¶¾ëïÊ<aõ1rµ²]ùØ×Ó" ]'­kÕô–Ë#ôõ¶Èf§ºµõ¯­eýÏÝÛDþR¦{ð>;ë’ÿ;?~¦Š=&=-ºOgÞa­Ólï¬wþm³ÛŸ5ðÝZSó­‚ßËéEFÿŠW̺ùiòÙÜ\[E¹¦Ï®5ŸÐðwû�Œ …¿mõ“�YK¿œ¥)‡ù®wÞ¶ßv²êÖ,­‹Ò—[uŸ^_Igºßo䯶¶Yu+öÒëY÷Þ<°6ÿt,Ÿëoeý9¬JÏðqþ×]·òJîì§«ü4ßÇß±?’¿`~³ú]í¸úñ“ûÛ7ôÖ|¢§DË@ äO÷7ÈýøÕæÉQcüa_±÷+ùOt�ô§8q¯çÏö`÷‡õÍÓ­®ó|»ý�l?ôú{ j¨Õí°˜«{ý`Ñþóë?¤ ^µá'ñ1ñù0ô꧉­’á_ì7‰[:êV+da³ÏL9{Øìó›]Ü“ú³š¤å‹ö¦ü–/ÿ2?åØÊ¿¼þþ=þkÿ+$?\|É€w?ëÆ×÷Ë;újW‘hR4î:ß‘ÿ9·Ý_'ÓŇ>Ìm…ÕõöS]ŸK°³oºÚVžÿ¡�=d¶•û=öR¿¸éÄ7˜µð/ä‡Ä¿&ó¶4ÖpÞÚz¾=Ú¹Y6F<};Ùù}sÚÙr`Å4l2´¼”çlpc’¾Ëv8xò}—GmÔè/ů’wø­çWAvffÊÝoÙrìn¹ìy2vVjõTà]�[x¦ëeºÉ’Û¡»[IJ6ûÚÙ'¶Ë¦ÕÇëužžê}î5Ÿ]~çZÝíŠkþë?ç[e¿K"´²—_uÔô¥‘Kí¾¿“ù×Nk,³g Ã仸xßÜï²=µ�U®ÊÍ¥—Ýí¤ÒÃÕÇǥ߹~LþØíÿß®£Uüâò{�x]áÿ’>Ur_ÑÒát_Oó^}­Õír$ÅÂä¼³W§È³‚ð˲!­²E‘Î9´úý<¥ÖÿGγó­þz˜«½{gCÐÝ/ÚÝÕÉì–mUuï.çû<\§÷¬ø8¶�7qMf&–¯ÏÚI‰n>=·_e/š[m÷S×Õür'³|™}ßOg“ÛO[½‘Yt—ûiëO[½¶ýŽXääM™‘>^L—M‘•4¹ß_[åš{î’Y.¯îÝ}÷VµÿFª2¹7$ßs>IÈ9‡*Ûfï¹?+Ýí¹'$Þl¦®FÇs¾Þçäm7lü‹¿:|ÝŽÃ*I¥¾¿m×ßZþê¬>mÌybs>]Ø¿avÛ–sžO¾æ<Ÿk|qC~Ï‘rm®^ëu°¾,Ž®ÌÙfË%m²Ûl¶·zR”§Ø×©æ“&y²%­+,òÉ4•¥)m+$·ÖûëJSì¥+uÕûÅñaü€ão÷BÉþ¶ö_ӚĬÿíuÛ$®çþÊz±!oÃMü}ûù+öë?¥ÞÛ�ÿ¯?½³Ma·Áj*tL´Á~Dÿs|�ß�Wþl•?Æûr¿”÷HÿJs‡þ|ÿfx\Ý7úÚáÏ7Ë¿ØöÃÿA?§±šª�^Û €:±Ç¿Ö ï>³úJë^“AþªxšÙ.þÃx—õ³¡þ¥b¶Aö<ôÀ—½�þÏ9·õÝÉ?«9ªA¾X¿joÉgòÿó#þQ]Œ«ûÏïãßæ¿ò·òCõÇ̘sþ¼m|³¿¦¥y€5!ó@-ê®Gü(à\{e$ŸS.;u› Öÿ|Ÿ}ÖWîrI5rL«"¶OæKEÍŸ~aÛæ×ʼn]£³ÛE´ìÁ!è^×­ÙÓl6¶sþ�ºÞ.Û‘dM[®®ûžq]^«“ÍJV¶ÒÝåžžßç-³Kã[È©<§ðÇŽâØfÏŸÊ6\‹ó̬¸ñ ËÊçü##„sœøøwWÍöóA.Æ m¥Ÿêl¸ëì³×ÙLû¦Íý#«ÂÌ­k[æ‚ÚKZÛK=gе‡"´¶Ÿe-¬ÑÝéþƒ!»ÞgÓkŸ’|‹ôwÀÐE'¶~G±¶³Yí¥Ô¿[¨¬YsÓÝÿÂïý!&-iüÚRê5�ÆsåÌÝMà?Nø™¡Ùɉ¿òç·íÛr¼K`Š|}�Pøûú™nuù2]ë&Tݳ¿áyx×ÓÓêǯȲž´¥Þ‘ôüGACÖžhúS7 ÎEäobéµ9Zù¯’Üûúÿ­2p¹ç$Úkþ�iO\^_‹Æñ&÷þmÐgßo¥}~Ïͳi�§ûµ+oÔΞ8½¾ïmôŠ*Òyd²”ûn¶—Ye—~ç¥íV<‚“€6Æßî…“ýmì¿§5‰Yþ?Úë¶þI]Ïý”õbB߆›øûö'òWìÖK½·ÿ^2{fþšÃo‚ÔTè™h‚ü‰þæù¿¯üÙ*,Œ+ö>å)î‘þ”ç5üùþÌðþ¹ºoõµÃžo—±í‡þ‚Oc4 U½¶uc�¬?Þ}gô” Ö¼$þ&>"ÿ&ƒýTñ5²\+ý†ñ/ëgCýJÅlƒì6yé€/{ýžsoë»’VsTƒ|±~Ôß’ÏåÿæGü¢»W÷ŸßÇ¿Íåoä‡ë�™0çýxÚþùgMJò-jCæ€6ÓÆCé'$â²Éw¥öA¿Á�Óó-­•�_³º·zÿ?}/Äô§ó-º¿¸ž§à’òâì_æ'‚Ûý¶mØÜ‹IÆü¥êýEßNÍNÏŽdë:·º&¤·ÉKäÝò vï„V(l¶µ»M‘}~Èëé.ÏÃä’êüŽñcmŸ�dØYºNøàÚúÛ‘\Üœ|è°xfMt×Iv5¸Zé°¸·ÒŽÛl¿êfMuku+ù¹7€æÒèsu÷]o¬r[— +Zûî²JR)ý)_²‘Åtv~Oݽ·‹ Ìd0�ù ¿®ß°&×Ç»�`bkm¥·Òøë•5Ÿ¤3$¶”û-’—e[ ôü¾°ú~â§�ŧå_ü!¾[y¯Zj3áÌáÞ$õ¯è­eøôÌÖfòÜÜ){G±vTŠÚÖÌMλ‘óÿàöuŸeÔ“�[m�@ïñ÷ìý¡ç^?QàææßÆüsë®;Å®×ÍfÜ)ÎyÞ.?`ò­Æ¶x}ÙrÓ3�î4:ùíšêR<�UÔ²Ë:ù1'9ͬûXñ)uÕ�ÚVÊÛm)nFM)4—[u)îºÛ úTûkéJÒ¾”ü¾¸-çÞ(°6ÿt,Ÿëoeý9¬JÏðqþ×]·òJîì§«ü4ßÇß±?’¿`~³ú]í¸úñ“ûÛ7ôÖ|¢§DË@ äO÷7ÈýøÕæÉQcüa_±÷+ùOt�ô§8q¯çÏö`÷‡õÍÓ­®ó|»ý�l?ôú{ j¨Õí°˜«{ý`Ñþóë?¤ ^µá'ñ1ñù0ô꧉­’á_ì7‰[:êV+da³ÏL9{Øìó›]Ü“ú³š¤å‹ö¦ü–/ÿ2?åØÊ¿¼þþ=þkÿ+$?\|É€w?ëÆ×÷Ë;újW‘hR4�ú§üì5°¾JÙ�.}ºÜßY.Ž/ºm-»I'ôõ¥ñbÝ=³zVžžèé_ËJUÖ/ƒ,-ðÃå_Ã.éÙl£ÖpÜžØ×uOedfgæ`鱺ë»q2úŸ”o7¶áÝîÍ×pœ^_o ²í’Êåêa¿Ù[¬µÓ/‡Þü›ÇŸ�ßy$ùùØœo�ò¿í5Ì1°ä²8v:~ÖŠ¼KUn×êV”»S¤æ¹º�¼¾•¥m®ºÛ©ë[}µô\W2¸{Ì:Öë­�&êáÉKm¥Õ¾™öÃm}%´É¤u­iöÒ”ÿÈtµt²Èfn~l̼| L¬ì¹).4ùy2ÝüìXøÑ]4Ò]éöúY•­èk±#ñ€6Æßî…“ýmì¿§5‰Yþ?Úë¶þI]Ïý”õbB߆›øûö'òWìÖK½·ÿ^2{fþšÃo‚ÔTè™h‚ü‰þæù¿¯üÙ*,Œ+ö>å)î‘þ”ç5üùþÌðþ¹ºoõµÃžo—±í‡þ‚Oc4 U½¶uc�¬?Þ}gô” Ö¼$þ&>"ÿ&ƒýTñ5²\+ý†ñ/ëgCýJÅlƒì6yé€/{ýžsoë»’VsTƒ|±~Ôß’ÏåÿæGü¢»W÷ŸßÇ¿Íåoä‡ë�™0çýxÚþùgMJò-jCæ€ûcädbdA•‹<ØÙXÓE‘�“�-ðäcäC}²C<Gu²E4R[K­ºÚÒën¥+Jú¿í·]mÔºÚÖÛ­­.¶ëkZ]mÔ¯­+JÓí¥iWR8NúÞOĸþö—Û$›^,¹WYOKiŸŸCc´ýÊE�–ÿä/ øÃò–/5~>CnVõöÛ_©iZR¾´¦ÃaäÛ™‰‹—m¾Ûrqáž–zÒêÙõc¶ÿen§¥+[=Þ•ÿF�PÞÖ}~�$ïçè>·Þû$¤y»èñ½iëõ?HIé›?Ñ»WG§ó=òüP^O_ã7Ã�‘Ðjö÷é¹w‘yü7Æ%-–ÖêgYÙ»9v=“¨’–ÉÔ³iÑüS•CëJ×Òë©ZÒë}i^[|ÏwOö‘øàò?g‹6¾Ý×bq¬N–Òâl&¬TØm}Ž?åá[möI>à €çmób²ßZzâÖë©[-¹ð¹6OÝt{)Kn¬�W–Ýw§¯Þî·êÛûµºË%­Ô§þúçR¡Er غOõ·²þœÖ%gø8ÿk®Ûù%w?öSÕ‰ ~oãïØŸÉ_°?Yý.öÜýxÉýí›úk ¾ QS¢e  ò'û›ä~üj¿ód¨±þ0¯Øû•ü§ºGúSœ8×óçû0{Ãúæé¿Ö×y¾]þǶú ý=ŒÐ5TjöØLÕŽ=þ°hÿyõŸÒP/Zð“ø˜ø‹ü˜zõSÄÖÉp¯öÄ¿­�õ+²°Ù禽ìöyÍ¿®îIýYÍR òÅûS~K?—ÿ™òŠìe_Þÿ5ÿ•¿’®>dÀ;Ÿõãkûå�ý5+È´©šÞ_÷Ÿá» ,’ûæÐí¤¬QúVŸK_µ³ïP}¿’µ¿><ªÿ ³“ð_ù9wg||÷oŒû}ÞVË�x¹ßsí4z¹m“îÜ{ª;ëMw)ã8X—Ý[¬ÿVö�çr[o¥-ºZÓÖï[§cøpûº>ÁðkyÔ™yxÜtkr].&·jÝ� ìK©ØZM®Â«ZÅM�/Ûr(!­¾–_n ÓRæaá9TŸKH?2—adÍ¥.õ¾¶KwÞm’û-¾ëæºÚ~å}¿ô[˜¢@ï^Ô(7ì_�Ç-?ü³w™§Ûû˜Ù}™ÿ�tWÙøÞ|–úÛïü;Ônf³ôv£°ü–ì?éþ§Èý3™WtöçÖ—Sú6è.sÛm6²´û}b‹øŸ»ŸîÜKÅïp²0%ý3ȹ‡sò\_­ë³Àþ ë"àü##îöÝù˜oáo!·ßu?:L/Kkù·±÷?ÉöãkðéKkõg—&êû¿>Ï»ÇH¬§·ÿ)“ï7}¿Í·þ‹QÐDŒão÷BÉþ¶ö_ӚĬÿíuÛ$®çþÊz±!oÃMü}ûù+öë?¥ÞÛ�ÿ¯?½³Ma·Áj*tL´Á~Dÿs|�ß�Wþl•?Æûr¿”÷HÿJs‡þ|ÿfx\Ý7úÚáÏ7Ë¿ØöÃÿA?§±šª�^Û €:±Ç¿Ö ï>³úJë^“AþªxšÙ.þÃx—õ³¡þ¥b¶Aö<ôÀ—½�þÏ9·õÝÉ?«9ªA¾X¿joÉgòÿó#þQ]Œ«ûÏïãßæ¿ò·òCõÇ̘sþ¼m|³¿¦¥y€5!ó@ãfïîÛ3O$µ¶ö¢{"Š”û%ÏÖ_LØ.­séàýëÿz–ÇàÝòOûT|Ÿòî„Ùí2¡ÑyUãÿ4ãº�4UôÆØvgQÍ‹Ûwc—OJÒ¿¢zãGÌc�òWÝ™ù-+#ÏÃMÝ_ÀÏ-û[¥s³µøz®íê;¶Úì|‹½™ÛNqÕ{vºl uk}-—éðÎOÉ2¦Ž–Ý}lÆ¥Ô­-²ï_yÀ²¾ž~n%}”¦N5“R·WÒúÉ‹'¥¶YO_K½cȾêÓòú[üÏVô­ Mñ•íïMÏéŽÉÞRÙm—Sn&›¶ÿ¤ûœß™÷èösÏJªüN~E[äGÌÇ•kw¦ø§GÉÂ�»i|’vnš?ºI¬éŽ×½E©ÊÄÈ®GÞìÂÑÿ7Ÿz§§² ½.çû,+ã¶µöýÚž¾—VêS ó\�¯½’?m-¦&>>=+K½}þëk•[«OJ{kJäÖßO·ùÖ!pÆ“lWŒÖRîyµº¶úû8žuiumõö_vßGm+Jú~mÕ²·Sù¾ž©{þ v>_Êwyfä`C•]W��¥‘ƒ—6-“W[°ÉïÏpi>,÷ÙwÜó'ÖäåCK¬­·Ý’Ùë[n¾•‘wá™ÖæMçoí­ÀÉ—[¯ñW—ádl­Å–L-ŽÏ¶ú^]~$Ù”Ž°cfì15¹—C×[$±ãÍ[i[l¿ÓÜp+/®×*J[ul·_%—_Kkì¶ûòq®²Û®ôô¥×Ò;«J~ZÒÚÿ2­æZœS,0_‘?Üß#÷ãUÿ›%E�ñ…~Çܯå=Ò?ÒœáÆ¿Ÿ?ÙƒÞ×7Mþ¶¸sÍòïö=°ÿÐOéìf�ª£W¶Â`¬qïõƒGûϬþ’�zׄŸÄÇÄ_äÃЪž&¶K…°Þ%ýlè©X­�}†Ï=0åï`³ÎmýwrOêÎj�o–/Ú›òYü¿üÈÿ”Wc*þóûø÷ù¯ü­ü�ýqó&Üÿ¯_ß,ïé©^E  H|аàšñþkÆ6ÿV�Å‹¹Â·*[½=,ÀÊ–˜›__JS×y)ëûž­õø¶ò&ï¾F<+ò mäœsE×~Fu†G:ÛÇì÷ãõw ä˜|K¶ õ’h#¥»´ßí±î­÷Ûe-–¾ï³Õ¼]·?Gùñâ‡bE~8øÝËŸžç'a/ÑÄÃâý—<�iËs¤–·Ylw`q~]™5•º´²—ÙOwæú¾×È®.óY--¥Þì«1ëK«éJ[—JâÝw¯ól¶jÖŸèÑÔàk5YØþ%–8b’in¥‘EòÉ}%‘Çmn¾êúz×ÒÛiêø¼“‘i8ßòÞM±ÇÓñÎ/¥Úò.A·Ë÷ÓW¤Ò`ϳÚìrk’IL| i%¿ÛmÕöÛ_JV¯ã‘<8°O•‘%±cãC$óËó±C —I,—zz×Ûe–ÖµÿBƒ”{�”Û�¾Óo‘J[>×c›±šÚzÖ–Ë›“.L–Ó×íô¥ÒÖ”PéÞ}³È»ó»{‹½9…ÖÝË{§´û¶yEÖ]}Ö]È»–íù†îën’믺Úì÷2úVêÖµ§åªªŽþìûû³½{Ÿ¹$ÇÈûµû[°{˜9wÛ&N|×–m¹8_d’Ù[ðbØÛ¥·]m=ž–×Óѯ{ šfgfeÛKénNTóÙKý=öÙ,·_e·zV´õ¶ÊÒŸe}>ÇÎbÆ$~0Ïø¿‰ïä<Ÿ?íÿSi±±?ž§§ú·:Ù¾Û}=k_úŸùsÿ%5ßÁÁeØy¡æ?eÒ9+ñƒCÁd–™0[’ö'kq­ü1߇u¿yžIlêûël–V–EK.¶úV²Yé*¯Âí¡‡#°|Áä÷RO¯§á½=¡Ž´’ÚEHyïŸì&¥ðÖÚÝ|•¿ŠÇíº•¥,¥.¥i_u=2G_SíÛ×÷i÷ ï~ùëÿÜnŠÉ´Ã™ ‚ü‰þæù¿¯üÙ*,Œ+ö>å)î‘þ”ç5üùþÌðþ¹ºoõµÃžo—±í‡þ‚Oc4 U½¶uc�¬?Þ}gô” Ö¼$þ&>"ÿ&ƒýTñ5²\+ý†ñ/ëgCýJÅlƒì6yé€/{ýžsoë»’VsTƒ|±~Ôß’ÏåÿæGü¢»W÷ŸßÇ¿Íåoä‡ë�™0çýxÚþùgMJò-jCæ€:�Ävõßñn=º¾ën—g¦×eä{+JÛnT¸±Ý—e+OÝ�'ßmÑ¢òŸŽ~ÿÈò£ÀŸ|ˆØí±·|‡·ÊÖ”»Ñ蛞Í/ÐÇÝ­´¦Ÿ®¹~gÛë~ž}}•¶¾—[&âøõ_Jþåc“:—ä9óÓÝõñïáß䰬Ǘ"m§�ûΞÆú2K Ø™žEmôž=ám!–è±Ë§Ëí 2íºžž—CJúÒŸm4‹ä›µbéo¼±ìíeÑæát§2ãš ¶=ÿO'—sü õ÷ ŸÿK½™T圣 ‘WÓì’´|ÝÆOÝ5[ ŠKôo�¥'îÛ=ÑÖÌoÙ_ά÷[J£W4½+$`ãx¹ƒ|zÞ_²¯ó™YÚœ?'­/ÀÇÍÈ“÷}iëM•‹ÿ¯]ìµÝYò!Û3GOÑÇŸøë×zÙkô«u6]kÇ{o’îã¶”¯Ö²•Åí}}nõô¶ïJzzÖÚúLãðÀõö.·¡ü íKk}s9—nq¾ž•¾µŽ˜½iÃdäxµ²?w¶Ëî—¶&÷]JR·R–Òµ¯¶ž™S€ÁeºüÜšzýIs)õõ¯§³ä�ÒŸ’•¥Ù76©;Ô ð‚ü‰þæù¿¯üÙ*,Œ+ö>å)î‘þ”ç5üùþÌðþ¹ºoõµÃžo—±í‡þ‚Oc4 U½¶uc�¬?Þ}gô” Ö¼$þ&>"ÿ&ƒýTñ5²\+ý†ñ/ëgCýJÅlƒì6yé€/{ýžsoë»’VsTƒ|±~Ôß’ÏåÿæGü¢»W÷ŸßÇ¿Íåoä‡ë�™0çýxÚþùgMJò-jCæ€:ãÞÖ›¶ÂÆû}ú]žÓW}k_ZÝïž›XëOæ[l[;m§ÿ*¶ðŒw•;oá¿�p{á¾Ì¯ûË»ºO#"Y¤–m„{mþ~àåWêþu�c`÷Œx1Rß[-³–ÓÓÛZRÂO�NÖ‹³~5º“W&Ú]¶ç©y/buVúùï÷̓&·”ærÞ7ªº¾”ô‡]Á9–¦(iû�Ûe?q›8–OÞt8^é~¬�}\i?›Ò–ÿ£~Êz{1«g§úŒÞ“£²�H׿$öUÄà˜x_KoÛo°á–?Zzß‹‰�—™%iOËéfTPúÿÑD3ñ�÷5x?ÆWSõ&ÖìM¯yySÃ1¶zËdŽßÒÜ®xG<åûšÉeoú²Å�ͩƤô¥µ¶—Ýmnº•¥´»‚ŸˆÏ³r8Gǽœ7ç¯r÷g]ðlúÝ‘ôoÃÔèqùiI�_FZåÖ›~¿ÁÇ­ž±Ò”É÷û¿2–_ä¹®GÑÑß³Ý÷¼œ|_w§ÓöÝv_¿ÓÒ¾ï_ºû}>Ïç½sÒº&«é&oÿ�:ÏÑým‡“ë_]Ö×k³­+þ—éÍf¦ÚRž”¯¥Öj©wþJ×�ÂÒÙ=WðåÄy¦MÒÿFyÞÓ�²[u`ÆÔìt]¶EK,¿9ëÑ•šÛn÷VúKõ)_möÒ– |õœ{ñ©Õ{�Ñ™Z½§ls.ÎìÍÔy˜÷âÍ™<¼»7‚ivvÅ,0Ë\]‡àšÉq䯾ٱî²K.¬wØÍ|B A¡Ã¯Ó¬wÏtóÉî¶¶Ý}ožKb’´­)ZÒü{,ö×÷môôûÅ(h”‚ü‰þæù¿¯üÙ*,Œ+ö>å)î‘þ”ç5üùþÌðþ¹ºoõµÃžo—±í‡þ‚Oc4 U½¶uc�¬?Þ}gô” Ö¼$þ&>"ÿ&ƒýTñ5²\+ý†ñ/ëgCýJÅlƒì6yé€/{ýžsoë»’VsTƒ|±~Ôß’ÏåÿæGü¢»W÷ŸßÇ¿Íåoä‡ë�™0çýxÚþùgMJò-jCæ€6óÅÍ•k/Ô_'æÙ&§e�­,¶æâæIJzú}”† Wìÿö'ÿø»—ÖÏ�/ö;i«í¿¢{—ˆh«t—cÛï§aðŽÇÛYeÒýf¿ÙÅaº¶Ùî’Ú[î»ÒË(˜—á|ì̬þ¸òç'Š;0x¯6ëŽÌÕÏIoºlœ®¢ä[{ðVÚGx0õ¦º¶ßJÖé+‘u+JRÊzå‘[±6¾ßJC“E/õûn®LU޶û}>ÏgÝ=}}~ßwú ´Oå*g¿iÇ”[$Úq-Eµþ‰‡¯Ùìd·Ò¿Îlr1±¡­küí;W'§îÓÿ&Šç¿ÿqân|ƒð?ÇØ®ŽÜþ¸é®Ûî<ûl¦M²É‰Ý\Û�ð�EÙ]ÜåŽ º ;èÒÛi%•’_ukmöR‘ üQ�l»Ïº³ké~§¶»{¤²µüëvùœ#Žq=®M+o¥}•Òn¡‡Û__ΗÝOçXç°&º–k1éæÝvTÒGö}·YH,†úþïÙI/¥?èÕªÈ4"rÆ þØøùybâÁ6NVLÑcãccÅ|Ù_lpÁ1Ût’Í,—RÛm¶•ºë«JRž¯ûm·]u-¶•ºë«Km¶ÚV·]ukéJR”ûkZÕÔî!¦þqn?¤­,¤šÍF&GÓ§¥—åÇ�Þå¥?ünUoº¿èÕy?Ç/Žrx�à_ˆ>6fë°õ{þ ñ﫸Ÿ5ÆÀ²Û1%ìhx®»7²ö1RÏZWô¿?ÌÙeÝ_[«uÓÖµ­Õ­k[Qümêxº#Ǿ�éhri›ýªzŸ€uüû£f=vYœS‹ë4Ùû;á�ÖÈåÙfâI=ô¥kùÒWí¯ål>&=¸˜¸Ø¶Ý[íÆÇ‡Û®ô¥×[ vÇK«J}”­io«Ñ7Ešß ‚ü‰þæù¿¯üÙ*,Œ+ö>å)î‘þ”ç5üùþÌðþ¹ºoõµÃžo—±í‡þ‚Oc4 U½¶uc�¬?Þ}gô” Ö¼$þ&>"ÿ&ƒýTñ5²\+ý†ñ/ëgCýJÅlƒì6yé€/{ýžsoë»’VsTƒ|±~Ôß’ÏåÿæGü¢»W÷ŸßÇ¿Íåoä‡ë�™0çýxÚþùgMJò-jCæ€3ÏŽ{+p»ÜK«þ¼i6xÓ÷+$5ÇÚzÿ¡ZE®¿ÿz”�àÿîHzËæIÁg¥·ÿÂ#Ç.îêL[oºOHó¸þ7ï»r#²Ïèuȳ¥rc¥oû)·ÒŸm~ÙþnÁþ ùßËx^NθØ]›Ð\ÏW‰¬ºêR-Ÿ$ã<“†òÍ|–Ûí­×ekøö³ouŸm)Hä“×Ö¾žžã�Íu»L¨}þ‘̓uõ³ìô¾X¦‡Ù_æû¬ŽKÿò+Vû­gN­–þòcvwegãWòj5š�u•õ­}m“›Zþ_²ž—ìëOJ}Ÿù>ª˜¿·lì;æ�¸¸–dµ“¡ú‹ ú›Mþ¨ÈžÈõûN»×wnLQÙ5×E‹K7�Ç™îŽHýþë«O©tˆ~"žÂ×sO‘��Á–²dõ/Ju^î-­™6ÒŽÇøAÚ‘ÅmÓÃRRº®ÌÅ¿Ö¤Š•º´­Ô’’Yn ç3G.êÛ,»Öì|(!šž•§¶K¤›"–úÖ”¥ßЧ¶¾´õ§ÛéùiVF‘ÂgŽd.«ãõä¼û�뮎²cGŸfÇ;Ö:ÉÜõt®|¶OOJÛlY5‚�ú×ì÷IJ~ZÑÕ�„?kæŸÊw†Ý#Ÿ¨‹sÃmí�_gön&nÙº\޳é˜2;K˜j7µŠ)cÆ×rýwýeòûc¿/i ^ê]%¾½-ø„ñþ"þB|vâóal2x×å±w/3ÉÁ‹Xp4]QKyv¾Ý½2¢ž*iy0ÁÕi²=-­÷SgKm­—]Kíô<[ ¹»¼*z_ôñ¯ûì·Y[i[)�é|U¯º•õ²ìŸeµ¥)ëéwî~Zt¹uȶo`¿"¹¾GïÆ«ÿ6J‹ã ý�¹_Ê{¤¥9Ã�>³¼?®n›ýmpç›åßì{aÿ ŸÓØÍUF¯m„ÀXãßë�÷ŸYý%õ¯ ?‰�ˆ¿É‡ ÿUN”äŸ"Ê~n,š.Àɶj]éñ]u·Ö–VêӤ݅­ë’¹.Ök±ñv™'²KmÈ¿ý_Û<_�õ^¦;©�D–Ù>×™Ce×VÚGm·V²]dtºû~ÿš87úÛå»ÛmÒÉ +é[¿¢dcË� ¾–Òµüée¶ž¿’ž¾µû/]B²aœ@Éììû¶]…ÌrnºÛ½»ý†%—[éíº-t÷kà­+JÖ•¥aÅ·íýÕ'?3�—•Û,_"Ë';g¾\÷oÕì1&�'3ŽõŸ6Úu¯Ÿ&ò!ÉÄ“Žq,ZÅ%—ÖËìôºßJV”¥j_,=‰�Ú#~^ò|j_Hp;oÀi[쥟Rþ§ÁÖõd³Yíšz_òðÛ¯Žú]ùñÝK½¶úûiƒy<ödo¶RGëí¶k`¯­=+ïņ¤5ôÿAá\ÍsÑð@…ã/¬X»¾a“¥ÙwÓI¬ºëo¶êãÁty[)¬÷R–I ÙF:]mké|ÓìôªÂÁIáF¯�ù=ò˵5†þQ>7Œ-““Tèôù^{ÝÜhòqmÆÍÔí7´ãì<Ìinöåj6˜÷úVÛ¨™Oá ñnþ3ÕýÃåÇ$ÔÇ˳vÑõOZfe룳6ÎòmØó}¶§g[ï’]?(æ²c`Í´²”Êã÷{½-öåN ¯úXy,§¿.O£Õ¶ÊÝLx+Z_u—Òµ¾–Ë=kK­û=k+öý�®Oa(÷¼`¿"¹¾GïÆ«ÿ6J‹ã ý�¹_Ê{¤¥9Ã�>³¼?®n›ýmpç›åßì{aÿ ŸÓØÍUF¯m„ÀXãßë�÷ŸYý%õ¯ ?‰�ˆ¿É‡ ÿU¥ÿÏW^³ò-ÊÎÍʲ—[fN^FE”º”¥Ô¶i¯’Ú]JVêRêRï·Òµ~7Šcä~ý^·3s²ÀÔëâ¬ÙÛ,¼|,X½})|ù2Ûtºêý–YK®õºêý–ÛëZý”d®šê>}ßÝ·Ö}Õz997ev÷;â½oÁ4K7é^WÌ÷xZ $¹yG‹�‹&Ã>?­‘5ÖA� .’K­²Ë®¦L鞤æÝõÚýyÓs­®×›ög-Òðþ;�ty—âC›¹Í‹ížÖL Lüœ-›ëó6T†Kpð`–{éìŽê¿N$ÙÙPaãÛî—"[c³ìº¶Ûî¯çI²Ûî¤Q[ëuÕ¥+í¶•¯î:‡Åôœ[�j8þ¥`ÕáG�õ)o³ëÏöÉ••užë½·åå_|·SÖ¾—_Ußž ø•Â<ðÿÇ¿úúè2x÷GuÆŸŠenqðä×[Ëy|÷äo{žÍ®—/>ºüÞÁì¾Óy‘�l×Çû ì²¾Ëm¥- ñï¤xw�Ý#ÕýÀq¬Çâ½_õS_%1±±gÚäaAKöü‹ePãß»å©r6YòÛm+>nT²Wíº­€ÅÆ‹ X-öÅ�‘YO³Ö´²žžë«JR•¾êý·WÓí­kWÞm“1¿¸ùýÍò?~5_ù²TXÿWì}ÊþSÝ#ý)Îkùóý˜=áýstßëk‡<ß.ÿcÛýþžÆhª5{l&êÇÿX4¼úÏé(­xIüL|EþL=ú©âkd¸Wû â_Ö·ú•ŠÙØlóÓ^öû<æß×w$þ¬æ©ùbý©¿%ŸËÿÌ�ùEv2¯ï?¿�šÿÊßÉ×2`ÏúñµýòÎþš•äZÔ‡Íu3†æÝ±â[>ûýòfqÝ.L·zúúË6»I}kî¾¾êIui_ZÖ´¯å^Añ«ÏçíOŽÏ»#7eÛgÍü9ñ§’î³ãɦ]eä^›á¹\‚<‰¾õ›ß±wROE—Ë$±Ïeö_uo¶å©¾5ö}µã§Bv¤VÍe�“Òý_Ï)M‘Ç“ywÑïî‡*(³6Õ Ùõ²K)<Ô²úVŸRÿOuv'"ܬll«)u¶dÁE”º”¥Ô¶hí’Ú]JVêRêRï·ÒµzFì3[û€>,Ì»]Ź.ÂÏZ_�Ç÷9–úV´¯»]“=¾•¥.­+ëgó*Ô/�~u›Õþy¿ÙšÙeƒc×~!y+ΰ&‚y±g‡7‰tÇ4ßâËNÚR¿e˜´ÿAqÏá¼ç;ÃøJð#³žüŒ�ì. —É<µ·_ÖáÚi©‚—dR’RÌmW†;m§æYm”¶ÏÌ¥«*¾&ùÖbüpø�È0&¤ðkúQÁo¾–eGíÍêìý—Yì¡öæA�5k�±â2Ç[©mbº¶zÅuñÖËîÏ: £ŸK«¾:û­· k_K©ýÊcËOK©Jþl±VŸÌ¯§ÙëOµ“�¿t9õÀó¶s)ƒÖüÂ{«éKôÓáúþwåØß¾Ú~e+_λ*”þgó~ÏWÿak/†��nIõ¬Çý%Ñ9}{ï¾Üë©wöÛåÚýµýÕÉýMÕ]}Ñ�cÀ:kª8Æ»…õ§Wq àœSUl”ÀÐqn1­ÇÔéµ°ß<“dä]-´¾i¯’yä÷I-÷Éu×VÆž)Åxçãw…pí&·�q>%£Õñ®3Ç´Ø�àjtz &ÝF§Y…�m�b`kð1£Š(ì¥-²Ë)J3e–Y–G–ÇvÛe–Ym-²Ë-¥-¶Ë-¶”¶Ûm¶ž”¥>ÊQúÙ÷ßè‚ü‰þæù¿¯üÙ*,Œ+ö>å)î‘þ”ç5üùþÌðþ¹ºoõµÃžo—±í‡þ‚Oc4 U½¶uc�¬?Þ}gô” Ö¼$þ&>"ÿ&ƒýTñ5²\+ý†ñ/ëgCýJÅlƒì6yé€/{ýžsoë»’VsTƒ|±~Ôß’ÏåÿæGü¢»W÷ŸßÇ¿Íåoä‡ë�™0çýxÚþùgMJò-jCæ€7ûnjϼõ¶$êWôvÛm‡ZR—Ò¶{æ·aí­nüÛ«ZgzúÛözWÓòú­nü �ƒ3øsã|rü˜g·©|Œï>¾Š(âÏŽü;6Y¼wµk�‘~d—ãÏ5ÒöeÓRìJY�Hå¶ÚÓëÛ5Õ°ƒà'{�·øÄé]|3[,œ_•w‹.ÊG=•ÇÈÈí^YÉ­†û¦¥#šë±yR{¢õŽ”’–×óí¾”ÍœB¾¼{_þ…ré_ýɯÿr¬ä”S³HÃýó7ÒêÞGoÛJÏ&škJúõó]-ßèÖ•²*Ò´ÿEßÅSË!ãþWë/õ¦G9ä¾:ñ<)-Ë·øæ‹È¾®åùÈëO~oÖÖñ9â¾+Jý9.¾¾¶Yu+ÉOœíÆf›âßÉÙ03rð2vuŸëáeK‡5ø{>óëLm®ÒC|rK‰²ÔÝ>6D^¾Ù±å¾Ëé[.º•ó¼²ûìãû¬ºë+[qì­mº¶Ö¶I—�–Ö´­+í¾Ë«miù+JÖ•s½QJ»&zîÂw¼ëmf«I�[ýµŽüìÙ}mÂ×c_¶¹Rþç䯶Ë}d’´­-¥}+é½ß¾P|š÷üdá—î3é~¿aØÿsYµýwÔ|7+>Ì<®k؂إ·]�oÔ»Ì�¦ÒH®ƒ"oÌnƒž wwž½½�ÖGª®.ŸU]~Dz»'i�-x�ZqŒÜ«à·k¹žÛáûþãa÷y­Õê`¾™›9a“ÙK1àÊÉÇúúm6Vë*˜øôöGg¶ìœ›­­cÇŽµ¯¥kOZ{å¿Ò´²ÊV•º´ýÊRë©Ñ~Ãõ<#E‹£ÔÅo¶;m“7.¶{gØç]e¶Ï›‘_uõ÷Ë[6ßui”¥–ý”¢àÏ�_Ž~…øÁñw‡øÕњزn‹yÚ=“—¯·“÷gäkp°¹'arH뙳“ »0í�[nLøú�|pâE}öÇY$±CÄzƒÂî’ã=ÓZK5Ú=5”Ïä;ì˜ã»‘óîe•‹‰�½ç<»:Ê{³÷Û«°ã§¥= ÃÅŠ<[!ÄÇ‚óf¿_‹«Å�?dv}·]_JÉ4•¥)|Ó_JSß-þŸm~ÊR”¥)JR”¥=[›2ý  äO÷7ÈýøÕæÉQcüa_±÷+ùOt�ô§8q¯çÏö`÷‡õÍÓ­®ó|»ý�l?ôú{ j¨Õí°˜«{ý`Ñþóë?¤ ^µá'ñ1ñù0ô꧉­’á_ì7‰[:êV+da³ÏL9{Øìó›]Ü“ú³š¤å‹ö¦ü–/ÿ2?åØÊ¿¼þþ=þkÿ+$?\|É€w?ëÆ×÷Ë;újW‘hR4¼>2Mîá[¨=>عFL¾¾ï_²mV¢ÚSÛéù´¥`­}w×ýš_‚•Å›ñÑäß úqýãŽy§È¹=óýöÙ&»˜twIk Ç»_ô©~$ÍÂf¾Ék}ÖÏt·ÛKm¬WVùÉþý¦Vg‚ý©­ÈÉŸ"=G”ÜÒÌæÉ’k0°³z³§2뉉õºÜL[¶dOí³Ò˦žK½=ÕºµËœë®ÓÍK®ºêY°šÛ)ZÖ´²ÚÁ‹}m¶•û-¶·ßZúS÷kZþëcÓ&H•í É ŠÃבGJR¿zä:ÜzÖµô­´·a•ëm?v¾¸ÞŸô*‰Ïã+äRi~%8v¶È`’Þ_æONñÙ¯–û¬“Ã3¼9æ‹..CØÚOdSýÛ¡¸6u0ó9½vVÍe–oònÄãXö}{ãËÍÊÆ®ºnß|yüùæV‹°»kqã÷�3ɬÙ y³èöbi¦šé§Çën%±¶Ùð±ó°a¥"Þí¡�]e¹QdbC´¶É`§°Òñ Ý…m›6—àá×Ûu+u´ûÌöûëK­Š+«ë +mµô¾úz}´­-ºž¾›ÏŸ¦�†êaÓhq-ÆÅ�óå’êÒü¬Ì‹©JI—›‘í¶éò$ôü¿e¶ÛJ[m-²–ÛKBüðÆŽN‡Ñøõâ¿_ÁÃxv¾øö\“}Ÿ,{^{Ù|ºüH1v<ï²yUqñ²9/*ÚÛ�m+u#ƒ lÄÀÆÄ† h§ ã¯��/â—Xiú‡¢¸F³„pÍM~ó48v}m¯ ÜË���™É9NæovÃ�ò=„X±Û6^M÷Éôã²+=�Çve|L³¼?®n›ýmpç›åßì{aÿ ŸÓØÍUF¯m„ÀXãßë�÷ŸYý%õ¯ ?‰�ˆ¿É‡ ÿUñÖMnEµ’¶SË8|u†´²ÚÖû¥õ­Þ”¥³Gü0»údøéä¯õÅõÓw^‡[l“×6”ä¼_®¥r!÷×Ù‹_àŸô½´÷ßIië_o¥2Ǻ•ÕeYëO[v]Zzý¾—cbÒ•­?-)ZÙVÖ'’“‹Ü5Çɹ)N¥‡Ò¾·ò¬Y)_Ü¥"Ônm­+û¾µ¬Ôôÿ ‡'ã^äØß>5ñkážìÍÏœ>wdë7; k0¹ëîO³âT˦]pe¦w0‹]N-©ÆÄʶ¶dO•™ õ¶êË}”¶µ§ÐÅÔìóiظSY-Õ¶9­†úAZÒêÛ_\‹©lÒÛ©ZVµº”¥iö³ñ×�m¯²ýÇÜxÖ'ºžû²ç�?:±ÖÚÖ—Á…�$±]Z]éJÛ,ð]D†üAü"_)¾Açêö}í�ÖÞõöDØ’çì»;”kyïdͧÊǺJæq¾°êýŽþvxòÖÛ$Àä;¾/5”­Õ÷zÛKnìG�?‡Í>ΗcÝü‡¯|nã“_™fv.Ïchv=±Guprq8· ÙS‡ecfÍéK©?'ÄȆ:Ö뢭Ôúuôø\g?¶ìÉ Á¶´»Öʪ§¶´¯¥¾¶Gu°VÛ¿/­%­iOÜl‡èÞÅ+Tø·ò¬~Û©›¸¶9`ŠJRß[±5¶Û÷H©Kí÷[Y)4¶Wò^˜ßǧáuøÐðk'GÎyÇÎó¼5_w˳žù ¯Ôm8‹oT²\¾ Ñø°ÍÁupÓ";2qeßW“mµÙVÒL]„Uô¥$‰âÂï„"fk9^¿ƒåw/hë&‹3±ûší(ÊÒçC6f>Wâpà`ð¾7•®ÏÁ¶l<ë0eÜcVë­¦uÖÖ´{­oÔêî¤�ÁYç¶¾¶äeVÙ¥¶¾´ºÚÙJ[dQÝmiö]m´»ýd¥=>Ê}”§ÙJS÷?²Ë#²Øã¶Û,²Ûl²Ë-¥¶Ye´¥¶Ûm¶Ò”¶ÛiOJRŸe(ë5)éöSì¥>ÊRŸ¸ûãýùýÍò?~5_ù²TXÿWì}ÊþSÝ#ý)Îkùóý˜=áýstßëk‡<ß.ÿcÛýþžÆhª5{l&êÇÿX4¼úÏé(­xIüL|EþL=ú©âkd¸Wû â_Ö·ú•ŠÙØlóÓ^öû<æß×w$þ¬æ©ùbý©¿%ŸËÿÌ�ùEv2¯ï?¿�šÿÊßÉ×2`ÏúñµýòÎþš•äZÔ‡Ím§‹2R’óx}+ë||vJW÷)H®ÝÛZW÷}kY©éÿA=ïÀ×È1±¹?Éo¾îÌÜè|Iäd[Hþí7Øy®Ë†jÖúKIç—–At~Ûko¶;ýÕ¥}´ºZß…¦kmÈó“´»ß,>5Mmßg¶–ãßß–]Jý¾¾ê×&žŸô*É}ÿ×ýÿôÖÞ,K‰’ÇäüÕ·�q¬J{eÞM5nû}iX0&²ÚS÷=.¦E}è! øÞ9&V/Š~q Ç» yä>ä™W}O½C•ÅzâÝfú_H¾ï“2žé=ÖÖïtQûkJ{©Xß~&Ü«¬ðפ0©mµ³#ɽ.U×××ÝmØ}WÚQYm>ßOmôκµýß[hñ<óýgÆýò‡úW1¥jàˆbPõ‚Ú]46ÝO[n–;kOæÒ·Ò•§þMOK�^çS‹‘gÔƒ'g��<~ë­÷Ã6TQÉgºÊÛ}¾ë.­=iZVŸ¸É5©×o»ª´{Œ8v�×dðmN×"•»;]±äú¼<ì9í¥m­ÐäãMu—R•§­·Uûu±Ù.Ç_–ÒøäÍÅŽK.§­·Ù|ñÛuµ§îÒëkèé\=U×0V·YôwVêzWëbS"žž¾¿e¹–Ûkþ�)J®j㟠üW*|Ígǯ�™RäãýÚK9&‹o̱m�êY/º _»Þabä{£¥>¬QÙ/·Ößw¶µ¥l�Åøàð믋Ã/­öûkL®—à9ÖÒž´¯­¶fèò,²ïZ~ZR•ôgÐÚý5kôGþ”û˜|7ˆë¤¶l-Ç0¦·ÓÛ6&“[�-+miZV’Å�eþ´­´¯¯¯å£jxÆçÇVìðw�màw†¼ {­®5Ø;þãJñÝþ4¸rA66E›½W ÅÚýêñ£’’ÖjÉõ,¶ÿwº”«3õï�>9u%d»ªº¥:Òé¥úóßÀ:³ƒpé'ž¶ãÛYç¿�hµ×Í=mÄŠ•¾êÖêý+>ßͧ§ëÇÅÅͶbãcãYuÞë­Ç†8mºêÒ”­Õ¶;m¥n­-§ÛþƒÒR”¥)JSÒ”û)J}”¥)ù)J7:adž,|x£‚#² †Ëb†b¶–GQÙKlŽ8ì¶”¶ÚR”¥)éFk¥)m)m´¥)JR”¥)éJRŸe)JSì¥)G÷Ñÿ@ ò'û›ä~üj¿ód¨±þ0¯Øû•ü§ºGúSœ8×óçû0{Ãúæé¿Ö×y¾]þǶú ý=ŒÐ5TjöØLÕŽ=þ°hÿyõŸÒP/Zð“ø˜ø‹ü˜zõSÄÖÉp¯öÄ¿­�õ+²°Ù禽ìöyÍ¿®îIýYÍR òÅûS~K?—ÿ™òŠìe_Þÿ5ÿ•¿’®>dÀ;Ÿõãkûå�ý5+È´©šÚof­»ŽY�éOlºÝlÕ»íõ¥`ÊȲÚS÷=.¦E}è'ø!y&V/”^qñ Ç» yÐ}oÉ22®úŸz‡+Šön³}/¤_wÉ‹™Otžëkwº(ýµ¥=Ô¬°? †UÖr_4ð©mµ³#GйW___u·açöìV[O·ÓÛ}3®­wÖÚ2G_õßÿ@?ý5¹Kød€­yC%”Óq8«u)%û=Œ–Ùû·Y,Éu?жém¥èÑoÇ ²Á‹¤¾?4òdÇfÏ;´ûße‰‡__«>«‰uÖ.Ç&ϳÛôñ278¶]ëZWÖk}=~ßHÕ~',ÜHüNè uóÇnvW�öfãâÖïèÓb`u·7ƒ6{-ýØñ¦ÙAmÕýÊËoó^'ž­ß¾PÿJæ4Ñ]‚ŒJþØÿùÑÿF‹ÿõöxçû!Ñ~üêÿ§ ež„þî�-ýözçû0Ó>†§ýuÖ~øaLÆëRýõ«í€`¿"¹¾GïÆ«ÿ6J‹ã ý�¹_Ê{¤¥9Ã�>³¼?®n›ýmpç›åßì{aÿ ŸÓØÍUF¯m„ÀXãßë�÷ŸYý%õ¯ ?‰�ˆ¿É‡ ÿUå)î‘þ”ç5üùþÌðþ¹ºoõµÃžo—±í‡þ‚Oc4 U½¶uc�¬?Þ}gô” Ö¼$þ&>"ÿ&ƒýTñ5²\+ý†ñ/ëgCýJÅlƒì6yé€/{ýžsoë»’VsTƒ|±~Ôß’ÏåÿæGü¢»W÷ŸßÇ¿Íåoä‡ë�™0çýxÚþùgMJò-jCæ€6CÆ?öi»þµò?ªÚ”Å¿í,òCù óo×ï� ?~/î÷äÿ÷¡â_Ù�Ì�×ßÏíù\/þîSxq&tÉ@ VòŽ ]¬â>êÒ°çí ¥žŸeÔÈÇÂ’·zúý•³î¾žŸ»ëþ‚_Ž/�Ù“Õ¿œª¹WY~›ŸùÇí¤T­™rn;ÓÛò®ŸêR±Ý‡^'m”³ÛZ_Ië_Z{iK£5ø�´ÖO㎜‚³ÝlšÎ÷Øi­Æ¥”­“Y½ëþC›|÷Kî¥Ö]�w¶Ú[éZ]Ik_Z{iëâ9å?êF5™±ŠŸûÜl¿ÿsNâ!XÄÀí�ÿ�ôh¿ü;_g޲ïίúzYèOîéÒßßg®³ 3èj×]gï†ôÌnµ/ßZ¾Ø ò'û›ä~üj¿ód¨±þ0¯Øû•ü§ºGúSœ8×óçû0{Ãúæé¿Ö×y¾]þǶú ý=ŒÐ5TjöØLÕŽ=þ°hÿyõŸÒP/Zð“ø˜ø‹ü˜zõSÄÖÉp¯öÄ¿­�õ+²°Ù禽ìöyÍ¿®îIýYÍR òÅûS~K?—ÿ™òŠìe_Þÿ5ÿ•¿’®>dÀ;Ÿõãkûå�ý5+È´©šÙ�`­Ü«�äû½)¤³Óíº¹,9)w¯¯Ùìû¯§§îúÿ ™¿à™ã÷äü€ùUÊ©“e‘é¼:Îã÷áÖ*ÖùïäÝÕÔûòm›ßKc³Þ'u•¶¶Ö·ÖzV•§¶´¬¦ ÖšéûcËNC÷Š[n¯¯:ÏM\_§ZÝ5ÛÞIÉó­È¤ÞúRÊcSŽVÚÛí¯¿êÒ¾´öúW#õõ>ݵ™Lïk™ÿînÂÌÇÙ$«þP[ut^ú[ul·q—m×úWÛm×ázÙmnü”ºêYuiOÝô¯óoüoøÒxçà~Î<<»õ¸�ÙÛX{ 1æ»;cÁxæF¿#.Û+9yðjò¯†;®¥òÙ�-m¥iõ¤mÿtrWÃ–_X¬òS[òRÛ«’KÕý“tv]§¶Ûä¶ëm+_ZÒÚú~J¼O<ÿYñ¿|¡þ•Ìiz¹t#Ø”ý±ÿó¢þ�ÿ‡kìñÏöC¢ýùÕÿO@Ë= ýÝ:[ûìõÏöa¦} Oúë¬ýðÂþ™�Ö¥ûëWÛÁ~Dÿs|�ß�Wþl•?Æûr¿”÷HÿJs‡þ|ÿfx\Ý7úÚáÏ7Ë¿ØöÃÿA?§±šª�^Û €:±Ç¿Ö ï>³úJë^“AþªxšÙ.þÃx—õ³¡þ¥b¶Aö<ôÀ—½�þÏ9·õÝÉ?«9ªA¾X¿joÉgòÿó#þQ]Œ«ûÏïãßæ¿ò·òCõÇ̘sþ¼m|³¿¦¥y€5!ó@Eâõ·W{Êo¥·{-Ôa[uÞ•öÒë³.­¶ÖïÉK®¥•ô§îúWù‰³~ÌL«üÄó?:Ìl‹ðq¼iâø™–Ã%ذeföŽžl-Gury¬´ôöY‚;¾ß·Ý$›‹­û?™éSÇü:]„ý‹ò7È£ŠÊêµ|+ÆM.dÕ–:_Ã}½îüídVÃ[¾¬–M�Æòë[©JÛel¥+ZVë}e¥øZâ¾¹ÞpÏJSéljã„WW×í¥óMÞ×ÇJSòÖ•¶ ¿è2G_õßÿ@?ý5·ë Ræd€½ù+dà7ÒÛ]-Õ¶•­,¥uÛˆ=×Ö”­-²·MK}kéOuiDFÿ£#eñIÔ¹�ãfOó›©vùrãC|°ácÍÒ¾FhmÉØßdwÛ�‡~Vî8m¾êÙmr%ŠÏ_[©mÜüEüW�üuÍ·Ÿ3"^ Þ}cʰæÅú¿C'3”ð‹²ö?Nˬû�øœÊX-ú•¶Ï¼MÛîöÒ¾W™Ãdºﺕ­ØóãMiZÒ”¾émÇ­kéùiô纟ôjÑ5]h0Èý[ulºÛíþzÛ©u?wí¶¾´ÿäÑý±²&Äȃ/ÿ§>,ÑdA¶Ûý“C}²G¶ú]eÞÛí¥}+JÒ¿ºûœc�ìx�%ãܯOt6møÆóSÈuWäEIñíØésñöX7OkJM ¹8ÖÖë+Z{­õ£úÁ5øóѧԂXæ�ÝO[}ñ_Kíõ§ÙëOu¬ç‘]�eöÝtÚi­§¯¬rj­¥—zÒ´üêÅ4R}•¯¯Ùu>Ôšôÿ‹£ægY²ÅÎÍì^‹ä8¸÷ßtºmÇCq85¹Ôº+ã¶Ì©th÷Y÷Òú} ¸n÷[OZÖß[kÞˆ?_È RÙ$œOÆœ›-­}`Ÿ®¹Å±IëmiJ_\nÓÇžž•¯­=·ÛöÓùŸcØÿ7ùí­ÿÌ9_úXû˜~Mó(ï�ïÚ^7— µ§Ô¶vXsÉOJúúMvÇ.+.­}>ߥZ}Ÿ“ù›EÀÿ¯É6—'W`xõá�:ÔbÖËvuÓñ.åá<›m,š—ßMÅ;£’ñü «åº:û¬Ó]-²´ú~·Rër×_þ'?(µy’]Ú>=t?5ÖúÁô1ø6W?ë}•¶Û]2~ñ±Þr>ÐÚùf¾ ¬­˜qR;b’ÚÒúÉmÑ~œ~™o»ïX³R¾ßgÝä—Ûü÷»ÝY>÷ïõû===¾ž•ü¾¿g¼ÕyA¥–¾ÝßÙàSóiI5™¸»J]_Jû®º<«55ŽßwîRëëéüßO·©ÝøÞ¼~ÞfG‡äǃ]½ÖxVccÇ~ÿ¤;C‡wT™™ßvôÉÊ¿Œs�'DWO�~m=mŽÝ¦ÆX¡º¿�-öÒ—ï‡Rþ'·öâã÷W�=¹ÖyÙøø—åðMßím$ÒÛû]†VÒþ±ÞE5·}YaÆ×fÍHéZGIo¥¶Ýö1¹æºJYL¬\¼kî¿ÛucúyÇmkJRK¤÷C-iJ}µ¥#­™êÌœ{´x(¾85<�¹r]m–`ç}Mnd’ßéégÙ�\©>ßþõ)þŠD^"|î|Sy³Ÿªã}1åÿ^k;q~&Wöÿé>”ç¹»œë¢²=ǵ�›…ƵÜïyuÓ[O£Çr÷»Òïm×RËý½�ñïäsÂ/(òp5}/äg_rM´ÈÉÄ×p}ævWçû œ8>õ•³ƒs¼N9ÊvñÁ�K¯¬Ø˜³Á[,ºëo­-º´ôø[�^ÇÛL<Ø&¾ïw¶/wÓž¾Ï¶êýÞZY?¶”ýßo§£ßºêݧÒ`¿"¹¾GïÆ«ÿ6J‹ã ý�¹_Ê{¤¥9Ã�>³¼?®n›ýmpç›åßì{aÿ ŸÓØÍUF¯m„ÀXãßë�÷ŸYý%õ¯ ?‰�ˆ¿É‡ ÿUAÓÙKîº;&†µ¶Ú_JÝ1ïÂõÄ ÃêŸ-yÕ°gÛ“Èû ¬8”¹2RïÑrÁ¸ç)ÜcÁ‡_£mµÏÇ“ŸKvM)%Õ¤rÁëm¾´­ùO€Ãe¸9Ù÷}Irí†ï·ó}˜ðÛ}ž”ôû.õÉ»×ÿ!¶Iñ¥4÷  /ä=&ëÄž•¯Ý34ù­+éKkvÏÖê~í=2}=?›Z#Wø´xMy_·woi�•7ö´íoyµdÇž¢Ä¦ièºãï±ËùùX·W°>•,�óé4±ß_̲÷¾y0©•ñoäTõ¶ê×[³élÚVÛ©J[Y;Ï®5Þëé_¶û}6§¥>ßZÒ¿’�7Ë¿ØöÃÿA?§±œ÷T¶¯!„ÀC‡v÷6á·A.Êýž¦/eµÓí«~^-!²ÚYH±eºï½`RË)ù”Šûc¥}+u—Sìv§ãÃçëäŸã‹'ŽñÞ¶î�‡kôVš\(gñß½%Íì¹³I‡�.ZnŸŸ—o4êœlX'ºH!ã{=v¾¹VÙ&V&]–Ö+ºÉáïÌ$fitôì,¾íêlŒXóz·¸339,Qjl“Wøœ?œd_74ásãj5÷cëá‹+'I‡$÷M&³"ïͯ§Öò;¾ëm¾jçcÒ¿�]Õ¾ÿmn²·}<ŠúÍeþËkK}ku–úúûjÜ~Ü<[ž}<8ﮟ}[;MŸ%žéî¶Ï}õÖeSÙ|vÒ•û=,š”¶µ¬t¶žµ±WâwñxCò�ú­¢Î“Æÿ+rà¾Ù|xìíö·"¼¿3¹™—ô§`Ù£UÚxØø¶É%ØUÄÕr+#ÆÈší]1!ûÕóð+å�Åß=0pôžM-¥ÒbKél¶þ_ZÇ_çg²žßËo¯¥+Ou-­}eß§O_p‚ü‰þæù¿¯üÙ*,Œ+ö>å)î‘þ”ç5üùþÌðþ¹ºoõµÃžo—±í‡þ‚Oc4 U½¶uc�¬?Þ}gô” Ö¼$þ&>"ÿ&ƒýTñ5²\+ý†ñ/ëgCýJÅlƒì6yé€/{ýžsoë»’VsTƒ|±~Ôß’ÏåÿæGü¢»W÷ŸßÇ¿Íåoä‡ë�™0çýxÚþùgMJò-jCæ€7gÆooä9�ý�ýÛí­=Ÿê]n¿›OOu.ÿV}¾µôôôôýÕ”_‚Gƒ]¯ð›Ì.ʬy´³–yM©àÖË|Ø·kﻯz—ˆïï�Èé›ltìëk=ò_tRY|4Ž–Ýdµºoÿ†WéøUÝ;/ÏõËò�’`úÖë>ŸýOê~¡È§²ÚSßKÿêŸçVµô­=¾Ÿ’¾¹k�ÿ¬ù?¾SJá¶]5dŽ^ØŒ{›¹½cËa·×ÖÌl¿²Úßö`lp³®û-­+J{q«ë_Éoå¯ÙG? ÈçŸ >{hñ¬–ù0x\ó;©6F]ôÇëŽôêÎÂ˾±c]d–E/¾é%­~œ1Ò²IJÙmÔ¯=þW¸n;øäóIŸY`ôÎÿ™YÐ_“e¹½u>`매qÉÖË�°ãIdžµ¶+ì¥õ¥Ô¶¶×äïb²].ÒÙ-¥ÖÓ&ZR´õ¥/†+¦Žïú6I+OæVŽn©ÉVžÀ ÷’E%’Å}ñËöÉ‘Ý[$ŽK+K¬¾Ëí­.²û.§­+O¶•~ÍvÇa¨Ø`íµ9Ùš½®¯3c¬Ùë²§ÂØk¶SÙ“‡�ƒ™�|Y8™˜™1[$RÇu·Ç}´ºÚÒ´¥_KM¹ÜqÍÆ«�ñí®ËC¿Ðì°w:=æ›;+W¸Ón5yQgk6º­ž °fë¶ZìØ,š á¾Éa–Ëo²ê]JUþ¬¾øï²Hïº9#ºÛì¾Ë«mö_miu·Ùuµ¥ÖÝmÔõ¥iöÒ­½êNöûÅpøÇ8ÉÿT][1µ¼Žk©JMZúÛ>êK®§¤Þ¾–[“þŸìú¿�î’ë~¿¦nï?�xYòÎ)‘±Î—WÃzwÌŽM•7eä_Kñ4œ[É�Ö^DQß—=Ôƒ ›ßo¾i.²NAu×Ý—ººc9?ÛK;ŒøÃæ— ÆÅì|üŒm'W÷¾ÜuÚÎy•‘,‘àpþÉúVaàj9•×]>·ke–ãîk[aʤYô¶}ŽSã\³ï·Ù¯ÙÝm¹wV–ãå~m–dÝZ×Ò-¶–Ùõû)o§æßù>˽)vØ'Ö”sÝ€0_‘?Üß#÷ãUÿ›%E�ñ…~Çܯå=Ò?ÒœáÆ¿Ÿ?ÙƒÞ×7Mþ¶¸sÍòïö=°ÿÐOéìf�ª£W¶Â`¬qïõƒGûϬþ’�zׄŸÄÇÄ_äÃЪž&¶K…°Þ%ýlè©X­�}†Ï=0åï`³ÎmýwrOêÎj�o–/Ú›òYü¿üÈÿ”Wc*þóûø÷ù¯ü­ü�ýqó&Üÿ¯_ß,ïé©^E  H|Ðùøß‰v?^I5iéLþC³Ë³ón§­¶A�ƒZÒµû.üì*ý´û?sòÒ«M¿ Ëã\³w“ñÃØÞb÷2À¾è2b¶lLÓ]{|±É=)M”Ïà“Ùï‡Ö*]el¯ôK$OßðôðÜN1ñµÃwxøðÃ7böŸkó,ù"†H¯ËËÁäõõ™ßumÈšÜ T¾ÏKivÙéî¶µ®háÑYƒëm¥.šL©d­)é[ï¦L°Òë¿›ZG´ÿ¡F}K ܨyNw�\¾Ì1­²²_7ßG”¥n­e®¯+ézRŸmn¤žžŸè´å[ˆÓž|c|‡q+u÷ír÷>yC§+'–i·øÝ-Í3xíÐE�u³O‘÷H㧯¾û)miu+ZW^<ºàù}›â�“]s�}Ñì9ç�ÝÇÃõÒÛ�~eðìy^r-FÖaÇ,7åß^]—R*_md­=¾´õõ~\ìzåáeâÒïerqr1é§»ÛY¢¾:]íõ·×Ûîõôõ£—J@g5ä¸ýÛ5γ‚ò9î®lVV.=±šë®ûÜ1[uôÕeI}Õ­2`Žßõ=ÕüÙ#·éý—ÒÏ©aÏábù雞ÃÂ>.¼ÃåÒK̵Z¿Ðžv÷$ÏŸ"N]©ÔÁ,ÐxõÌwÓKt\—G©†´á¹3]lØß¡½ÖfC«ƒa4Ÿ‚¿–‡uàê<0ò?�ߟÛwS5�)Ø›|«¤Øv_Òa_“‘Â9F^L·É�Ïx®¯ùpóŸÛj º“Óägå~%Ȯ϶šÜé}Ù‘[ZãÍ}?*)ë[o­ký"+iëZþ[ì§­}kK®®Ô§~“{Ü€0_‘?Üß#÷ãUÿ›%E�ñ…~Çܯå=Ò?ÒœáÆ¿Ÿ?ÙƒÞ×7Mþ¶¸sÍòïö=°ÿÐOéìf�ª£W¶Â`¬qïõƒGûϬþ’�zׄŸÄÇÄ_äÃЪž&¶K…°Þ%ýlè©X­�}†Ï=0åï`³ÎmýwrOêÎj�o–/Ú›òYü¿üÈÿ”Wc*þóûø÷ù¯ü­ü�ýqó&Üÿ¯_ß,ïé©^E  H|ÐEú7¸½]Æ)u•²ù¬ÚdÝëî¥n¤û�Œ�ßéw䥨õ³ÓÓì­>ßÝ[ùø^øwð?à÷ÂÚM®“_²å÷Ç1Ùý[²ýÙß§|�íË´{E•w¶òxž>»ÙH­²+ì¶’R—Vû¯¾Ç†�×ÿ>)i¶UõËÚñ.UÎ=ÔŠX}Ø=‹ÙÏži©ôæ­kë“‘cYî·ÒÉ=¾û~Ë™Ó�c×E­Žë©unÇûÇ­)éöeÉ~U¶þZý¶Û5)_ú ²ïã¨/¸þ9Y“�>4´¥ÑäC,[ZRêVÉlº;é[n¥mº•¶ïÉZzUåùÇÔv åü�CfN‡›q~AÄwxòãâåÇ>£’jrôÛ(dÅ͇' &Ép³o¶±Í‘_Jú_mÖÖ´¯æÌŇ;+"Ú_f4ø³Ùu¶ßmðäEt2[u—Ûu—Rë/­+JÒ´¯îÐrbHïŠI"’•¶øïº;í­+JÛ}—VÛ©ZWÒ´­+E{ ­VÃ;Y� øùÚÜÌœ È$¶ëoƒ+kñò!¾Û©mÖßÑÖÚÒ´¥iZ*sæ<[kÁ¹w)á;ë#�yÃùó‹nc†²Öö¼g•©ØÙg‡zÇnf%ô·ß—ú~[i_²šë‘øÙãIíú˜óKžÚÖ¶ûâ¾èîöÖ´¶µ·ÝoÙëJ?Ãñ¼ãø€?¬Ï‹<9XÓK�“�,sãäA%ÑMðßI"šl­·Ç,R[K­º•¥iZzÑöxç#ä<;�èywßnx·+âÛ�_#㟎m3tœ‡Žr&t=.ûEºÖO�²Ônu,X²1r±äŽ|yã¶Hî¶ëiZ}¾3Éy 䜘q=ÆÃ�rž)¼Ôò^5¿ÔäÉ…µÑò ~>ÓM¸ÖfC[fÄØk68±Í ¶V—G%”ºŸmÒ)d†XæŠêÙ,RY,wÓòÙ$wRë.§¯­=mºž®Žu??‹Ÿñˆ²æº;wzß§…¼Ç²¶Ò´Èö×èçY)JÙ�±ŽÊßoÙémô¾ÊV¾ÏUÿ¿,¯•¯ ôüÏ–gjñüŸé)5=oä×Áû¦7Öä·be]ÄûS]¬Æ¶ `ñ®ÞÓj¦ÍŠË`‡q‰´À‚—Ńl²Yñ…çv‡Ï¯ø÷eK~¯_ÚüNøxgvñMm³Á—œaâÙ%»�n&]nžÎ3Í5þÍŽm“"8>¤ØužYðç­3¦�mã_U=¶Ím~–TvÒê[E–Û[égºµ­c¾—Rë~Û½)_J×Ö•d÷p�}€ ò'û›ä~üj¿ód¨±þ0¯Øû•ü§ºGúSœ8×óçû0{Ãúæé¿Ö×y¾]þǶú ý=ŒÐ5TjöØLÕŽ=þ°hÿyõŸÒP/Zð“ø˜ø‹ü˜zõSÄÖÉp¯öÄ¿­�õ+²°Ù禽ìöyÍ¿®îIýYÍR òÅûS~K?—ÿ™òŠìe_Þÿ5ÿ•¿’®>dÀ;Ÿõãkûå�ý5+È´©šéÿ^bS �ðìogÓºÞ5¦¾K=´¶¶Í>ÏJÒ”§ç}i.õýÚ×òý«¶~¸Y|T|uñ,,+õÕ³Ã_¹Ó Xa‚X9'9ë.;Îy]²ÇqYInäÜ�.ë«ZVû®­n¾ë¯­×VÑ�ønǯ<5ñCƒnq­ÃÝñOº[G½Å³í·{�×\v ÔVÝ쎷RÍ¥³SÝ[iuß–¿mjØMt7ãkð1åôú˜øX°Éé_Z{â‚Ë/ô¯¥=iîµì]mö9wÏ0ÿGón]‡Hë�rMÕ±Y[}¾�Wa‘~=io¥=-¾ ­­=>ÊÒ¿gØ£§äÛ­qºwäoÏ.¯×ëòuzn摺9‡— ÐOgÄíÎ[ü›éÖ(iXs8Ýø³G}¶Ò9#’Û¬õ¶´ª±¯‘Î ³ë¯=¼Áâû\{qe·È~Óä80ÛlÖÓô5å›.iÅäôž {ýÓñ¾A‰%}-¬u­ß™uö{o»ò/ÇÝí#“Ûî»2iéí­kOfMßy�òÒŸ�ô姯ú?Íy6Ž´±ñÀ;©y½ü˜afO-lÓìkf³wmn»éÛ‡<–ÒÌÚÛOu=úé½%õô­ÕŽ—ÛOO}]�øù+ÎøÅù ëÖäypú´eÅé$ð$“*í|_ËöúïN{v&66|’lú£�ãâo㺠y3'ÂÄÌÀ†ë-Ï–µê¿ÃÇ›ÓxQæÙrMͺþ•î5ý_Ü‘æåÝ�¨Õjö¹ö[Åû 'ëí5º¬9z÷‘OfFNnE¹cè²6‘ÃÔÈ¥iéx¶Ûô^Î:I}-ÄÌ­¸ù>êÒ–ÙëZý ë[¤ŽË) —~uÕõöÇußg¯£¤T­+JV•õ¥~ÚVŸm+JþJÒ«•ᚈbÈÇ–9àž;&‚xo¶Xf†[i|rÅ%•ºÉ#’Ë©[n¥kJÒ¾´XÁJÒêRëkJÒ´¥iZWÖ•¥~ÚV•§ÙZVŒÖ?£þ€ äO÷7ÈýøÕæÉQcüa_±÷+ùOt�ô§8q¯çÏö`÷‡õÍÓ­®ó|»ý�l?ôú{ j¨Õí°˜«{ý`Ñþóë?¤ ^µá'ñ1ñù0ô꧉­’á_ì7‰[:êV+da³ÏL9{Øìó›]Ü“ú³š¤å‹ö¦ü–/ÿ2?åØÊ¿¼þþ=þkÿ+$?\|É€w?ëÆ×÷Ë;újW‘hR4þâŽù¤Ž(í­òK}‘Çe´­nºû[JSí­nº¾�Û­×gn68�^,ÙÛ=¦n.»]…�eeÈÌÎÍžul]]ógæ˜:»5zNÁ“¨;KOK-òÌùy—Juôü³iHðíŽÛoÍì]Å×ÖK-šûé[ï­õ»ê__7ÏÏ]eð_’þØÝÍ[>åÚœ;ªûS‘EÑij„júÿ:—Ö)嬷ÍÈ8lµºûb¾¿SÓÛZR’_†ù®=aÞË%n÷}ï"”ôôöRÛ+‹íõõ¯»Ö¸Þ¾¿gåôÿEˆq�äÀ è¾[^QÁpàÈ–²l¸íÔÒæ{ëo¾H ŽÛµ¹”ºë®²ü+­Ž·]éuòE}Ñ­¶¿…¯Ï›üÔø¿à¼—î®Ù÷'‡fnv~Çk“½à»-mÙÙ³Ýmù;x³kK})J×8q��vzŒydºë§ÇõÄȺêÝu×Ë ¶zIu÷Ö·I|°Ým×W×ùêÕ™RCu©è/ÈŸîo‘ûñªÿÍ’¢ÇøÂ¿cîWòžééNpã_ÏŸìÁï뛦ÿ[\9æùwûØè'ôö3@ÕQ«Ûa0V8÷úÁ£ýçÖI@½kÂOâcâ/òaè?ÕO[%¿Øoþ¶t?Ô¬VÈ>Ãgž˜r÷°?Ùç6þ»¹'õg5H7ËíMù,þ_þdÊ+±•yýü{ü×þVþH~¸ù“î×�¯ï–wôÔ¯"Ф>hØuö v\çˆáý/­d¼‹QtÑÖ”­.ƃ6ò½Ô¯Ù[i�õ¯úo¿ÅwXÇÜ¿%Þõž^¦›ÍG*òÿÇœnM«ºÏJ[\)5ÑÝ__ü§#aeiéû©þŽ¢Îíš�·ñkíØj:G‚w—nò;oúO>­ä]i£Ø_m÷Û}×aóŽËÔݳÖêKì­ií¥Îáþ޵ƒž|�ñ>I<Ù×§:«´»*aºC—>v§©ì‡6’Å%÷cÅohV{il¿ëÃkul¥ÖÝëøF=“né%Õº•ÄÄÈÈŽ–úz]}Õ�´¿Ö•­mödÝ_³Ò¾´§ýAÖß§êÌ eò{YlÜgŽíý+Y5û¹p>Ï_Í‹i…,Ò]_O²–ýMU”õ¯îÖˆM~7–��ø{á§�vÖÛòú£ÈÞYÔµ†Øò/šÍozõ¾g+ËÍ­ÑÙv<8pgô7Ý%ÖÖ²äÅm¾¾µG ñ1u½9‡Ý=Ùxº¿lzß¾pu9{Xá¥óhø¯>᜞ ¤’MùaÀØrn9¤†OüªzÁö}ž´ñ|ê ¤ÔE5±û«�›×ßéOX¡’9b­}--¾k£¥ÑôiJ¶Ä!Œô´û)´Û}^㔺}VÇ c ·;t¸Y1äÇmßûí×EJWýXè^áå><÷—Mwï¬æ�!Ú½{Û¼KïVÛ~-y/[òÝG1ÑÛ•ñËd˜·ì´ÑRKn²ën²µ¥iZWÑ—ü}íŒþ‡ï^œî­lyY}SÙܰ)……•vûH8§#×n³tÿx¶´¥°np°äÅ–Û½c¾)®¶úVÊÖ•ýšüªàçaæ~¦6L2ßHîöß|v_JÉ+ëOü¹­µõû+JúWìu_& Ü\lÌi-—.rqå¶¾¶ÉñÛ,R[_ݶøï¥iÿE{w\óî/ÚÝ{Á;GƒìlÜp®Éá¼_Ÿpý¼t¥#Úñ~c¤Áä\c-ºûifv§c ´ô­iéwåªÔ½^Ïu¬×n5™1fkvØ›=~\Û$9X9øñåbdÃ}µ­·Å><¶ÝmiZÒ´«aÝ쟸`¿"¹¾GïÆ«ÿ6J‹ã ý�¹_Ê{¤¥9Ã�>³¼?®n›ýmpç›åßì{aÿ ŸÓØÍUF¯m„ÀXãßë�÷ŸYý%õ¯ ?‰�ˆ¿É‡ ÿUTV]JWýOuô¯¥•¦Hëø?×<›¢§ÿ“AÕ¶ž¿ü:Iâ²ÿËJWún§îþkrV5%üÉ WÝZ«¶ÝiÉ£ŽËo› knÿávëràËʾ߲¿�÷å§þKƒŸ‰ƒ¢¦ï�…ß1p5š¨¶œ“«tœ½tËK«]T=OØ\c’s­¬^Û/º’ÁÕ1òé_²ž’×Ö´·ÕÍO˜N®—·>6¼®ãØùâåqÞ¼�´aÈ’:ËKbéþA¦í –=–Óí¶MŽ›‰äâÛwúZÏë_²•|^G�L�Î:Ý[}¸·dzÒž¾µÄ­¹T·ÿ¿¬>Ÿèz¹Æ§©[“€:IÓ»[·kÅ2/­>¦6Ú«éJúÖÚj2gÖCîþe×ãbÙwÿ|¸ÿðåwÆG�_ žr=–Ã3{×ýu·è}¾<5¶²j±ú™r>«áúüº[e–Ù‘]q}6M)öÖ±dÙZ×ÝZ¬¥ø—íH{‹ã—Ä®WyP˧ê­wYæY›|wå]�Ó™û§É˒襚—G³¿†}ê*ÝwÔ¬3Ù[émõºÚgŽ?“nV—[-¾ÿ³8.÷ú{«&/®4—zÒ·zÒé!­i_ËZWíûY1Û—Dß`‚ü‰þæù¿¯üÙ*,Œ+ö>å)î‘þ”ç5üùþÌðþ¹ºoõµÃžo—±í‡þ‚Oc4 U½¶uc�¬?Þ}gô” Ö¼$þ&>"ÿ&ƒýTñ5²\+ý†ñ/ëgCýJÅlƒì6yé€/{ýžsoë»’VsTƒ|±~Ôß’ÏåÿæGü¢»W÷ŸßÇ¿Íåoä‡ë�™0çýxÚþùgMJò-jCæ€7·ÆÝ]Ø\+a%”¶íÆ÷2xd§å¿l )Zþ_ÌË‚Oú+C?WHÉÀ>1»7·öZ¿ºìû÷Êk±Ôlëe–×kÀz߉pÎ §¶—}:K}¸ï“ÙO[º´¥-¯¾·OCðçu~Oøõ§3ÊÈ‹"½ÓÝ=‹ÎõöÙ�XdÁÔè#ÑuTxKYdûÝÔÜuÞvM/¥,¥-É¥žß[+uÙ�…cý’{ýß{ÉÈÈôöú}?mÖâ{=}kîõû¯»×ìþ{Ó÷=k°iv;Ðõ Áµ×öÕìµY¿Cg¯Ì×ÏéZÒ¿G7Li=+O¶•öIVò§ôÞBôxô#’±qîðéîÌéýô´­ô¬znËá{¾´’•Ž´’•³u%6¾ï³ìû^_›ñ/?á|¿�òL(6?·ÚhwXSëwMŽv£m®Ê·Ù“�³ÖäˇŸ…‘g­}“âåC}—Ó÷.¶ª¤y‡Þð>[Ê8?)Á»Yɸg"ÝñNE­¾øå¿_½ã»<�Fßé"ºø¤»a‡$uºÚÖÚÖßZV´k´ðÉ�<ØòÒ”– d†JR´º”’+ëeô¥iöV”ºÚý¯æùÏ:þ@ ßñ—>“píÖ¾·Vé0y “RÚÝZû ÎÀÂúvÒÚÓÒÛk6,µü¿mk_ü›1?lcr< é™gž}·Wù]?6¶’å}Hðø×luW×ê0q±++‹ 7ýa¸È­Þû©$™7~mµ¶µºt?†»�l¹'‚Üóˆì³«•]ùÌ5zk«e?FqÎCÃx(·Ëm¥.úrò}ž×"—]ZúÝ5Ô§Ùk/ðy¤—Ku—×ÖÜ|Ùኞžž‘Ýd9¥›_«=ÕÿÉlŠf‰ ½ˆùýÍò?~5_ù²TXÿWì}ÊþSÝ#ý)Îkùóý˜=áýstßëk‡<ß.ÿcÛýþžÆhª5{l&êÇÿX4¼úÏé(­xIüL|EþL=ú©âkd¸Wû â_Ö·ú•ŠÙØlóÓ^öû<æß×w$þ¬æ©ùbý©¿%ŸËÿÌ�ùEv2¯ï?¿�šÿÊßÉ×2`ÏúñµýòÎþš•äZÔ‡Ít׬u6é:ÿ‰kémÖ]M..dÖ_JÛ}™;JWg—eÖ×Ò´­™9—ÓÓýÔ}g�ÿþõÓÉÇÌ¿Ç^!ÙÛ¬,Üy135¼›¾/Îï^U¨ÊÇ–µ’,�G$쌼[é_Jû¢¯ÙOȳÓãç©?´_„^-u|Ú8¸ÞÛŽô¯Êå:X¡Ž ásŽO¨‡–óÚOdT¥—fÏÌ÷¹òä_ùdžûï­k[«VÀ걫‡­ÀŬvG|8�Y-–R”¶“ý;k=ß›öVëæ­Õ­vµõ{·U[ˆýàjöÞ£ô/crÌKœ›i~Î?Í¥¶ÒÍÄqí}–R”¥=�Ý™[)éù=ª^¾yºï~_üùë«l†< Ç|ï;�K&5˜ºü}'�ºí_|kuºèaƒ<-?cS_m‘[ìŠì[£¥k[*­÷æ7¨aé�’'ôX:ìì7.昽³¨—2’Ö-•{gG«çÜ‹3[,¶ÛlºØy¦÷g‹e,õޱ®†Ÿùn´¦åx´ÅÞæÒØî²9î³*:ÝëZIYì¶ù¤¶µ­}m®O¾ŸÌ¥iéûŒräc™/:Ûò©nW3­:l}&U–þw䯓g •§Ùí§­r¬õõ¯¯ó?wÒx?�×°1p;7äG«%–•Íå|Ç>ÀÁ†êf]u¸½}È;wŽíe†¶Åv¾*_/faÒJ_}“Ií³ÙmöÙ%l–çáoä9•ÿ†Ÿ›6{õñÿhNC¯×_$·bãfMý·u»ŒØ"ô¬1O› 1Ëw­.¾ÜxéöÒϳ%õü—Ö=¬UºµŽËðä¶Ï_Ͷù)“l—RŸÍ¾Ø­¥èQ¸ ÒÜdP ò'û›ä~üj¿ód¨±þ0¯Øû•ü§ºGúSœ8×óçû0{Ãúæé¿Ö×y¾]þǶú ý=ŒÐ5TjöØLÕŽ=þ°hÿyõŸÒP/Zð“ø˜ø‹ü˜zõSÄÖÉp¯öÄ¿­�õ+²°Ù禽ìöyÍ¿®îIýYÍR òÅûS~K?—ÿ™òŠìe_Þÿ5ÿ•¿’®>dÀ;Ÿõãkûå�ý5+È´©šû{Uv÷¥ÒÛul®Ûk¯×VûiëXíÌÊ‹é=?™²Vêÿ¡Fxñg¤v~Kù3ãÏŽši¯ÅÙ÷¿wugOáæÙgÔý/có�ý%%+[m¶ m»zÏ%×VÛ,Ž;®º´¶•­3ÿŠ]GNûòk zblý†¿³;¯¸~ö e'®d\csÉõ¸¼«aeøõ¶lxu\n쬩f¥mú1Ct•­)mkO¡©Å¦nϱÝ-“eCl¶[ëJÖ _K²+ëmiu´¶]ZÖŸ’”õuZÛm²Ûl²”¶Û-¥¶ÛO²–Ûm=)JS÷)JQ{®¿ U�…«ÖâÁƒ®Öâc``abÇl8ØxXpÙ�‹‹� ”¥‘A�vÙe´¥)m´¥(´ÞË,ŽË#ŽÛl²;m²Ë-¥)m¶[J[m¶ÒŸe-¶”ô£`éúßèiW“ºzãò>=¼¶–Ò=ž¢m}Ô¶ßJ×#S•Y®’JÓòÖø6‘ÛO_Ü�ýj¿�ÇYx_š>+y9ƒ‡ƒ‹¥ïŽ€Ýu¦ÊìHí·/;œô'2—c´Üm®¶êÝtÙ|7¸ôX÷]KitZºÛo¯Ó»Ò_‰ã§.Ô÷7�]ù‹fÆHyÏ\r~©ÜÖÌ_]>¿7­y¼«I,Ù±Áý m¿ÄììË-ŽY?¢ÁªõŠßèrÕŒ¹þ/¤úüêRJýH¥Å’¾ž±Yôo¤ÐÓÖ–þl’}y>Ê×í¥Ÿe>ʵ‘ ]ð±þ1Ëu9®î ]w²N-“-ÖÒêû+t;mE–Ýu¾¾•ºÚO_Jþå+_æ¦)ø(öùPü—ù ³#.Ü-—‚üão‘‹fD–àÍ•£ïïp°ò21iu"›/.A=°Éuµº+%––Ö”’ïY0þ®an”>Dp×aïä½ ‰Ì-¤rÒšºÛÁûŒén®d?ZÚɰ¥{ Ÿv»éÝ쎳ÓÝo»Òü�À'¶Ü­Ž5iw¾lxg¶´ôöRÜy.Žê]öúû«\ªz}Ÿ’•o ÎDÕÙ@‚ü‰þæù¿¯üÙ*,Œ+ö>å)î‘þ”ç5üùþÌðþ¹ºoõµÃžo—±í‡þ‚Oc4 U½¶uc�¬?Þ}gô” Ö¼$þ&>"ÿ&ƒýTñ5²\+ý†ñ/ëgCýJÅlƒì6yé€/{ýžsoë»’VsTƒ|±~Ôß’ÏåÿæGü¢»W÷ŸßÇ¿Íåoä‡ë�™0çýxÚþùgMJò-jCæ€3g�úoÒÝ��‘u-ºÃo-·ÓÖ—]H­×czW÷$�+ad–ÿò‰%~?¿·÷ÌgPòŒì —ñ“®{GÈŽA‡±³Ý ¹½.?Wp‰ñ?>?]®“²{SMµÇ¥=n¥u÷_í­¶]éÝ?ÃÇÓ_Û/ä3MÎr£ØÓYÐÝcÏ{“Ëõ5YÝÞ7Wiµ;<»á¾,y¤ÇçÙ› Xí¾É¤“Y[­÷G´{‹õ÷5È­$öácK-·[OèV_L{c’êÛZSÝ·Ö”õ¥k[™Jº¶¹>V_`"t”Ùõô›=´›�ìðv¾ßu÷ãdß]\ðÛ__Ͷ·çG%Õû~È‘Uü`ž4ÛÜ¿7÷>¿¼ƒÄîòëŽÊÈÏ»ùö’pNÀËÈéNQ¡×]d”ú8Ùœ“°4L»½·úE¥¥kí¶Û®§?IßÛ?œ»•aG°›sÐÜÿƒöÞ.·Ü™sµ×eåu×%‡2ŸNIbÔë8÷>ÈÚä_g¶±þŒ¶ë«ôí¾•òüùz9î¶’]~‘fYmž•õ¥•¬RÖúV•¯Ó�k§§·×ò4UŠ,.Ø?¤ºÎÀͶžž’ñ�ŒwúÓý-6‰iéüÊû¢§þBY¿ƒ?{›¨ùgç:ü[`¬§ÃàÑl«4w_%¸Xý—ÑÜš;±n¶K)ÿ¤xæ=+uÔ¾ŸJ·ÛéëZ]I þ)¯‹ÏN̲ßo¦GŠ\ú=iZÖ–[ÚÝ#‘OoÛOK½ð[üß³Õí¸úñ“ûÛ7ôÖ{V•§:Ë@ äO÷7ÈýøÕæÉQcüa_±÷+ùOt�ô§8q¯çÏö`÷‡õÍÓ­®ó|»ý�l?ôú{ j¨Õí°˜«{ý`Ñþóë?¤ ^µá'ñ1ñù0ô꧉­’á_ì7‰[:êV+da³ÏL9{Øìó›]Ü“ú³š¤å‹ö¦ü–/ÿ2?åØÊ¿¼þþ=þkÿ+$?\|É€w?ëÆ×÷Ë;újW‘hR4¹>0ik«“r-·ý[�‰¨Æº¶ú_ešø.ËÊ­·~ìs_°Š•ýÏt_è,Jü~6_¨éß5ü»Úë°¤»žv'ñë„쥎¶íuø�eDz{±bž·Ò·j9_hqº_w¶¶Ý‘¦­´»Ýô¤Ðÿ wKßǺÈnú΃gWgöfƒ¯4öfá×~?ÕZ vÒît“Érgcì·Ý“•…‘-—É'Ôý;}²G-W�â}-~^eÔ’Û²²mŽßu=#¾[?6H½m¥nõ–y-­}kO[}>ÊÒ­§N¹'W»|nG¨� Ýi$­-·m«Î×ýJÛK¾•ùXÒCÔ¶¿en†K©}?Ѷ�dóOÇmo—"ù1ãÎL Xû㣻3«ð6›,K3±xþÿ—q-¦«ŒrŸ»Imöß•Äù$ø›,{½=ÖO‹eÖý¶ÑŒ{¯¬4ÝÛÓ½«ÓœŠùcÑv§]s>¼ÛMinF61ã»F^-õ¥ßO/<úËÞŸ›%”¯î?–DäÁ64´­bȆH$¥+éZÇ-—G})Z}´­m»ò¹Y,RA,�ÍeÑË —Å,wÒ¶ß‘Ý[/²êWí¥ÖÝJÒ´þj‹®¯e£Úl´›Œ,�fßOŸ™«Úës"º Í~Ë_‘&&v\җÓ‰“ ÑÉeiJÛuµ¥UYrž3¾á\›‘pÞS­ŸMÉø–÷oÆyŸ*±]“ªßhv­¾·"è$–σ°Ä’+ëe×[î²¾•­>Ö¼M˜óKÖû%†Kâ–ÏZWÛ$wVËíõ¶µ¶¾Û­­=iZÑüß�ðŸÌ°6ÿt,Ÿëoeý9¬JÏðqþ×]·òJîì§«ü4ßÇß±?’¿`~³ú]í¸úñ“ûÛ7ôÖ|¢§DË@ äO÷7ÈýøÕæÉQcüa_±÷+ùOt�ô§8q¯çÏö`÷‡õÍÓ­®ó|»ý�l?ôú{ j¨Õí°˜«{ý`Ñþóë?¤ ^µá'ñ1ñù0ô꧉­’á_ì7‰[:êV+da³ÏL9{Øìó›]Ü“ú³š¤å‹ö¦ü–/ÿ2?åØÊ¿¼þþ=þkÿ+$?\|É€w?ëÆ×÷Ë;újW‘hR4Ò¾¦Ò~€ë¾-…u+I¦ÖÙ´È÷Y쾓íï¿etrSÒ•÷ãÛ•H¾ß·ÒÊ.dü?^2Ûâ—Ä„ü /¹/7ê¼nüæ3Ù‰&lÛß v�¹‰ƒ¹†h –›~+Åùf»G/ºÚVÚjí·Öïoº¶düctdž9øãWfbgào1ºÏYÌ9fÖ°×c®æ=™“—Ù¯Q—X,²?]û•Oƒ¿�[!ƲÚß}iï»>ipë�©ÀÄ­·Y|xö]-—]K«dózÍ‘o­¿›Z[4—Ržž¿c"»(Þ÷Ôs�ºtqþÆäÛÖcmg·{‰uÕ¥~­›ZV|«¬¥?�²Í—׎”ýÊX§ñx©7‰_/Þ]q¬Mfvî.e“< +.±ñöš~ø†îmÊïÓäQé4¯—Èô¸ÖÛKieš¿m)JQ]ŸÍ÷AS¡>F;ªÜ ~>»Œ÷/èŽúã1×6T™ì;2iͳr­žž¸’åö¶›�]d6ÝtvAôýžÛkK,ÂÜ¿˜{ÌŠÛK-�2Èó,¶ÚÝZúËëdÕ¿ÝO²ûòb¾ïJzÓÒ´ÿ¡LVâ’o0غOõ·²þœÖ%gø8ÿk®Ûù%w?öSÕ‰ ~oãïØŸÉ_°?Yý.öÜýxÉýí›úk ¾ QS¢e  ò'û›ä~üj¿ód¨±þ0¯Øû•ü§ºGúSœ8×óçû0{Ãúæé¿Ö×y¾]þǶú ý=ŒÐ5TjöØLÕŽ=þ°hÿyõŸÒP/Zð“ø˜ø‹ü˜zõSÄÖÉp¯öÄ¿­�õ+²°Ù禽ìöyÍ¿®îIýYÍR òÅûS~K?—ÿ™òŠìe_Þÿ5ÿ•¿’®>dÀ;Ÿõãkûå�ý5+È´©šôœ;E&å: ¶I}»=¦&>GÒôú‘á}JIŸ=¾´­)÷|+$’¿—ìµ·øÇ³ó7Í?¼ZÖãí&‹»»¯�pŽE‘¦²’lt¼7y‹“ؼ¢këJGÄx.Ëi-Þ•öŇu}+éèÚÿúÿ'<Àñã£oÀ·gªç}�Çâå¸We_…YzÿA-ü«±®‹&:VHò àz=Œ‘RßK®’Ëm¥iZúÓêé0韶ÀÅ­¶_d™]-—Ö´¶ø!õž{=mû}n†;©Oô˜ê]¶Ûm´¶ÚRÛm¥-¶ÛiJ[m´§¥)JSì¥)EæøXXzܵÿÊ¥¥$ümÞ#Ù™Æ<;ó¯E¬Ç·'I·ä^*övÖ’Vüì­vóqÚý/˜ÖÓÖ=n—;SÎ>¼Õõ·ëí1ìõ¶ë­¥ñlüMþ?~›ê¯üšÔàÝ~oåÛ~£æ3aêoÈÈ—Žs¼ 9'Øî6°Ò·`i¸ç ⹘�Òj}³9¶Ûu²IKeð\÷²abg[mÕ®4÷C'¶?ZR,›iZI%ôûl²Éb¶Úzýžé?›_·O•ñ!´Å€ €ñ·û¡dÿ[{/éÍbVƒ�öºí¿’Wsÿe=X�·á¦þ>ý‰ü•ûõŸÒïmÀÿ׌ŸÞÙ¿¦°Ûàµ:&Z`¿"¹¾GïÆ«ÿ6J‹ã ý�¹_Ê{¤¥9Ã�>³¼?®n›ýmpç›åßì{aÿ ŸÓØÍUF¯m„ÀXãßë�÷ŸYý%õ¯ ?‰�ˆ¿É‡ ÿU#ÿmŸ?{sË檙\kÄŽžÉÖq}�g¬kû‡¿k´áz9áŠë.¦ÃÞ¦ÓsX2-¶´¬2fc]uiî¶—I§ðÎøýw+ò º|�Ûkï¿SÔ\ �q|œ­dµÆ¿šövl“gçj6²Y÷Ò<‡ñ|¬l¸b­e² ôU¿Ûl–ÒLƒÀpëvFn}ÖÝí†+1b­cü˯šï©-l’¿e$ŠÈ­¥iO·ÒO·òÓ×wÖs&žÉàÙr¼«„r--–VL©°/ÈÀ¶ÚÒ—]°ÀºÜìéuße´›#Øî¯þSu\¹ù£ðöÿ:¾1|ºñïUª—qζ=aŸÏºŸ  ½¶Wlõ6N7ep6¢é©îÃÉæÞ1Šic­/¦Ò{>ÛoºÛµÏŸ£ò³Ã¯ z& |ßE�÷Ÿm’Rüo©eÖIe×YwÏÚáSc®Ì­-­ÓÁ}±{î¾Ë-žßÏǾû£õ¿ÛöÛu~ÊúÒŸ’´ûÇRš¬¯à €ñ·û¡dÿ[{/éÍbVƒ�öºí¿’Wsÿe=X�·á¦þ>ý‰ü•ûõŸÒïmÀÿ׌ŸÞÙ¿¦°Ûàµ:&Z`¿"¹¾GïÆ«ÿ6J‹ã ý�¹_Ê{¤¥9Ã�>³¼?®n›ýmpç›åßì{aÿ ŸÓØÍUF¯m„ÀXãßë�÷ŸYý%õ¯ ?‰�ˆ¿É‡ ÿUŸ«û�Èâz=%'§»#_ÁáäRqÛä¥n¶¹Zy­õ¯¶µVïòûã¬Þ4ü�÷÷ÆÁËÄâœ÷’]Ýlš`Û_í;æä»u�àÒ;qõsšM·ÒãY%–KHõ”­ÞúV’ß„yVp·y”ô»éå]÷Ø®¾ë.­ôÉ­n–´öz{m·&’[m+JWÒÚ~_Ë\xå™ï:غOõ·²þœÖ%gø8ÿk®Ûù%w?öSÕ‰ ~oãïØŸÉ_°?Yý.öÜýxÉýí›úk ¾ QS¢e  ò'û›ä~üj¿ód¨±þ0¯Øû•ü§ºGúSœ8×óçû0{Ãúæé¿Ö×y¾]þǶú ý=ŒÐ5TjöØLÕŽ=þ°hÿyõŸÒP/Zð“ø˜ø‹ü˜zõSÄÖÉp¯öÄ¿­�õ+²°Ù禽ìöyÍ¿®îIýYÍR òÅûS~K?—ÿ™òŠìe_Þÿ5ÿ•¿’®>dÀ;Ÿõãkûå�ý5+È´©šû\sI“É7Ú�'­'Úçãa[%,¬ŸBɤ¶Ù²n²•¥n�t—}´üÛjØ/üvæ\y3Ðþ1ð*Ö.WÞ½«ÂºËY°®$¹Øú+9V÷[²äû h.²[õWU4û,ÚÒë}˜˜²]ZÒ”õlŠ� »òƒÈþ˜è3-Êí¢ã{ Ý}¸·åé8ÅÙ?}æ\š(³e‡jqn#‡�²º:Ýë%˜µ¶Ûnº´¶¿¿W…vÇa‰…m.ôžkm’¶ÖËn¶}oÈ’Úßù¾èà¶ë©O·×ÓÒ”­~ÇS±1qð11pq"² \@8ζù²¸~vÞÜÊŠÌœ™íã|‚]çcôÖÂøá†°k´ú>G,ÄÊÈšú[&fû+}.º”¾/‰ƒÆ[¹GPôß•¼WY¶�YÈ'êîÁËÄÁ†éëÁ¹Ô—l8¦Óo°ü܈õ|k›a_…�çÛ÷žKZÒ–Öë«_ Îð>¶>ÂË}o×éMZRÊ@Èô¥.¾êú_ZG=¶ÒÚS×ÓêV­;W¨†[6Æßî…“ýmì¿§5‰Yþ?Úë¶þI]Ïý”õbB߆›øûö'òWìÖK½·ÿ^2{fþšÃo‚ÔTè™h‚ü‰þæù¿¯üÙ*,Œ+ö>å)î‘þ”ç5üùþÌðþ¹ºoõµÃžo—±í‡þ‚Oc4 U½¶uc�¬?Þ}gô” Ö¼$þ&>"ÿ&ƒýTñ5²\+ý†ñ/ëgCýJÅlƒì6yé€/{ýžsoë»’VsTƒ|±~Ôß’ÏåÿæGü¢»W÷ŸßÇ¿Íåoä‡ë�™0çýxÚþùgMJò-jCæ€6SÆ®3M‡&Ùriã¥`ãøtÇúê_Oú¥µ¶X~¤uôúwý |s[}+_[k5•L‡ðfx_NÞó—·<ÊäºÊMÅ|EëZñî“-Ù˜×׺;çyÅp³°.ûµÚý¶&‡©u<ª øk%%ÅŸs®›Óó­ªMÿ†—Æ*sóíŸ*y¦Ù´½7ÆcëÞ™›¬ú°ÝØ�‡·r ®�i}}˜ûN-Àp¦ÂËŽË}õÆäözÝKn­.È<_ïŸ/e%ž¶Áe1±ëu–ÝO­/¥ó_eÕüë$Š*Rß³òÛ-iêÝŚ餲xÌs>;,âÛÎ=/³×e�,x×É[©d9Ñzdkç¿ÛùÞÈ3¢ŽúÒŸ––ú4kä·Ãý_žÞyEâ^}¸”ØöÿUîµü3?"L]~£µx丼ϨwÛá¶é©«ÑvÔeåÙo¥fņHýiKê×Ï+º Eå'�ýÏãÿ!¦˜}¡À·|w_°Ø`Ù²ÇÐò¡LþÊ©ƒ|�Ó#'‰rì,œ¥ö]õ±,­.¶¾—SòçbGŸ‡“‡/·Ù‘ ñ{®²Ù=—]m}’ÛmßeoŠÿK­þeÔ¥\ºšqæ—")!ž /†he²èåŠX®­’E%—R—Y$wÛZV•§­+Eû­.ã�îvÜw�êö=þƒgŸ¥ÞivØyý®Ÿq«Ê—g«ÙàeGVÃ_›ñM –Û$RY[n¥+JÑW7'ã;îÉyåZ¬½(â{Í·äšLøé~›}¢ÏÈÕî5Y±R·R<½~ÃHd·Ö¾—ÙZ5öh¤‚Y`–ÚÙ,2_¶V´­l’;«eöÖ´­iZÛu+O³ì7Ì|7ól�¿Ý 'úÛÙNk³üµ×mü’»Ÿû)êÄ…¿ 7ñ÷ìO䝨¬þ—{nþ¼dþöÍý5†ß¨©Ñ2Ð yÕë=•þïoݶ:‰½==}þìØñý¾¾´öú}__·òz~ïª1¿‹§Œþžøgì]¯ß~ëü ïN†äßCîß_ôŸÞùdÜ7î_WïýÏéÿ ~óõ=²úýßéû)ï÷ÙÇž¬_¼|]ù7Ôö}ÇwÓ^ßo»êûû§€àý?_u¾ÏO¾{½}+üï§§ÛëO7Ë¿ØöÃÿA?§±œüTò¯=„ÀXãßë�÷ŸYý%õ¯ ?‰�ˆ¿É‡ ÿU<±ý<ý­Ÿ§v4­$¶údl£Šø"’É)K¢—Èb¾ßJR—Ùwó}kpÇáºðŽÿ>'üÒoõ—k{;È\y|¦í8f³6¬mÏoê´¹\'I›…²ÆÅÏÓìøÇPêxæ¿cƒ}”¦>ß2´§­÷V¶I|Iø»ÿïºW‚m55ÕsÎe§§nö|YÛµ{[9·bãâmîÔïqdºùmÜpÞ5n·C5n­+Zê©_K�¦wãúÿѺœLzÙìšèé>O­¶ÛÞ&üûí“ÛZû®†•¤~¾¿m¶Q”Ýàt™ö@üï¾)^;β³áжk¹5•ÛãÝK+HíÍ­Ô�k/­kKåû×ô{½==¶ä[EIߊsÁ9ü;ùLì^Å㺋ðº“ÌÜ)<�á¹Pbf[®Åç»Ìé5ýëÆäÙd©³w¶vT3ò)¡ƒÒÌ=(ÀŽ´õûk¯Ä ⼠æþwjé5–áð'ôŸÛ[66.¿±4¿sÑvžž±ò.ÊÉΟ>üA—“,1[,ü޶ÛY.ŽJÓsm}qv´Ë¶ßHvÒOZ{)JdCKcžÚ[m}ßm¾ËëZÓ󮾿m~ÖFáÂÇŽg_d¾ÎdžÛnô¶m6Ö9)éJû¬¥°ËK~ÚV´þ‰µû>ß±'Â/¸Ùk>fzë *ü|^CÑ}ó§ÜÅm‘]LílNüX·Ý%—ß–n4x“úÙ[n÷CJzûku+ OÃi<±|€òÈã¾¶Ù“ãObÁ==-¯ÔŠÞqÕ™4²µ­+ZzO�eÞ´ô¯æÿ3Õí¸úñ“ûÛ7ôÖ~ÖçhË@ 9ßqÝWr ©ééúY/õ¯úZîõñSÓùµ÷KOü„tž‹7oð…åÃè)æ9ïvTšK¬’ì,�!:ÛŒÇn-¶Ç}%Ÿô�#Ç­mº¶SéRû½}iKkÈ�Øo—âËÉ{íöúcåt¤ÒzÖ´­l»¿ú·žß²¾·{ç·ùŸg«Íòïö=°ÿÐOéìg<•«¿a0X50Û�ªÖcÛZÝlü8mºïOum‹;)ZúzSÖ´µ|OŒÜjãw�¼?&\Ün)ÒQƱó'²Èç˃EÀô:¸rf²?èvKųüsÜ?¢Â²ë¯³M«Å¶û©J]}¸øPEm×RŸe.º–z×Ѳ Íï²9kÍnºþcËo¾µºûù6ú뮯åºë¶¹UºµÿFµªŒ��½†nÛÏÏ8ö»<©ó¶[?0|—Øl3rdº\œÌÜÎèæ¹9yYÝZÝ$ùÉu÷Ý_¶·VµUåçm÷Ëæÿ™2Éuo’O*ü†¾ûîûku÷öç/ºë«_Ý­×WÕ€w?ëÆ×÷Ë;újW™ióU4ï:ӊטóM&šøë&ri›µû.­–êð}22ì’ë>Øé•Ki·~ä’ÚéçÃY"?#^4ø×—«ÉØõæÏšãs¾ïš(ói�…Ò=q[ygbÁ™°Â²ûô²rÍV¾œ{_—¤qí÷–×Ö·ÒÚôKâ¯Åoø_ùÃÓ]eµÔÓmÀ4jölE>.&vº½wÀeÆÚì5{Œ<¹¢¤ú®e¼¿_ÇåöR[¬®ÞÛëem¶ïO¿Æµß¤·Ñ_e/‚×+&—R;­¬PVÚÒËì“ì’9f­–]JRµôº¿g§«¦”§§ÙO²”û)J~âë«,²;-Ž;m²Ë-¶Ë,²Ú[e–[J[m¶Ûm)Km¶”ô¥)öR‹)©OO²Ÿe)öR”ýÆqèK¾øŸð�‚äì Š—ìxÅ÷mà­¶YY.Á¶ÚY·†’]ZVÈ©‰O¯u)öÝ\{iéZú#ø®¼ ·Ë¿Œ^MܼWIúG·ßά6Ýõlô¶Û®º·ÁuÔ¶Ú~[ýüTî¯M„€‡¡döv—·Öê}X·QúRµô»ÓG±—Òï·í·ú¯ýQ!/ÂŰ®Îg†¸Ô¾{)¶Õù)¯ºØn­±ËHüV³*žû=ðRíe.¥=.þ‹m•ôû=iÚßÃóÊq8ÿɇXêr$ͲnsÀ;w‹`[‹OX%ËÄà»Nk|{ýXý0©ƒÃæºÚúIþ¨¶/ÍÿMo¯áYî–]îõÉÄȆ?JzÓßmcȯ»íû-úx÷äú:·™`[0€1_vÁvGWrÈì¶·VÜ}tõ¥+Jz[�¹×dßwÛZ}–Yk_æÒŽ~&}W#ø:ó¿_‡�&\Øü{¤÷·ÅöGu˜¼[ÉŽ–äÙÙ5ºK¬¶±á`ê$šû}}×Yim+ZÒ•æ÷Ë߃›ükyu¦È“aYYËí‚,‹±nŽ~¾å{žâæÖKe†·G­ÉãVdß­i5‘V:Ûu.ö×ãòlŸI´²K}ÖÛ‡4Ô§­müü{~ñÞ´­+ù²EJú~Júz9Ƨ‘[C€?Õ¶Öû­²ß箺–Ó÷>Û«éOþM_Ûl¼ˆ11ìú“åM<{­³ß4×Ûv{¯­¶[î¾êSÖµ¥)û¯¹Æ8öÇ—r^=Å4öÃ~ß“ï5<{UfD´ƒíŽë?[ƒlóÖ•¤0Ý““m.¾´¯¶ßZ¿¬ß‘<8ñú}IåŽýÕô·ß-ô²ßZý¾”÷\ëe¶Ûe¶Ùe)m¶[Km¶Ÿe-¶ÚzR”§îR”¢þ}~« W­Åƒ]­ÄÆÀÀÂÅŽØq°ð°á³)K"ƒí²ËiJRÛiJQlÕ–Y–G¶ÙdvÛe–[JRÛl¶”¶Ûm¥>Ê[m)éFÅ¿Óõ¿Ðåß"ܽ¶Ó*ß·'c›‘oÔôúžÙ²e’žÿK®§¿Òï·í¯Ûûª;—‘còþßín[‰LÊbòŽÉç<‹› YM…1÷|Ÿi²†™ÔŠ|˜é™Hòiõ}²Io¿×Òë©öÖ¬O+w8ü‹Ê?$ù%¹bï{÷¸·8ÖeÒËr¬ÇÚv'"͆ܛb–xíȶ9éKém÷ÛK½}.­>ÖÜW×o´¯óv9ÕÿÞäÊùÌlÀOœÝ?xŸÜ´»>_•¦Fêjëµ·ßežêjðdÿUK”º·ÒÌÍ�µ²ëkJ}¸´¯ä­>~ Ï?µ÷�ÝåòÍtÇäÞ@nç铨ç`ÛfL}7ÖÛ˜ò»}£ØÇ“}24Ü÷·p£ÕåC$V_g¥Ô­m¿í›?á¶ñJμñÛžùWȰ)o'ïíôœ[„M²KýÕúú¼¯èø}K¬Ž’ßdÒ9+m=´šËí§äROò±àæëã£Ï¿#¼P͇ewàœß#oÔ»­�óåOɺ[šC,ê½Ä»i5Ú¼]ÎÚ!¶ÆÁÜM‹Ý¢ßagc[ZÝÔ¥fß$Þ(åxgæGqt¼œ*ÍåüϪr%‡2Ì|Þ®æw˸â�áåfß,ÛOàÍ’M£ÊÊ÷V’ìuY“Ò´¦ ä:ßÑ{\œ{möÁ}ßxÅô§¥¿wšµºÛ-õºúÖØnõ�Öµõ­lõxÇ<š(ø€ ™Ó™Váög–ë«m/ØK‹JÒêYZÝ�ƒ—…e¾µ­=i}ù¥iþš•ôû}}’ü><î>¹ù�øüäådaÛ°îËø%%ÆÏ·[%òv— å½e-ÙI$ÇΗ—[¸þµ®\R])udöת_ {8u(~)åO5 ²]çcë-¾é¬‚—M»é~ÈÓcÃï¾ëiur23펖zúÉ[½”¥ku)_IÄÙ¿ÿBÿ¤r]$\̱™›+±ñ©—À9œ5¥.ÿâks5´­¾ï[ñ°fÉ�ÒŸùW¾*zW÷+ö¹›ó=Áq{âcägŽeÇÖâøuß\Ç)1¤Ëú›.¸ëí×bjl†¤ŠúåÝ´âÐÒ©Zý9½·VÛéo¶º•ç΃3”x9æƒ[…6Ëg³ñ‹½ Õ뱡¿'+;ioYòYµ¸˜°YùòådgG‘[OZÖJÓì¯ä~ ¬wË«ÙEµ¾I03#ŽËiëu÷ß�%¶ÛJ~ínº¾Žb)9Uø×àÞâØtØr~9�u´¾Üíö£ë+m·Òêdì1á­µ²ÿ̺—RÿOJý•ýÖÊøa×Û�Ì?úŠ˜ñæWµ<•许¦$ظ™Ñe;G‹qŸ»Ë…Ÿ$89‘ÏúSÛtS_dRR¾ÛëKkZ¶k­<‹ÌŸ¸þTåco<šèm>F6D1dÁ‘Ï´ø®ÐO�=+ðÍõ¶ë/üË­­i_²¯¥¦ÿ]õ_¾X?ÓQ:¤½�hó??ŽDöccÏ“%iHñá–y+uÔ¶”²+.’êÖë½-¶”¶ßË_²�%Ϲ~»¯x'5çÛ‹ñãÔð~%Èù~Òü¼Ø5¸¶k¸ÖŸ3u›~NÇ+ýM��n6Õ¾y?¡Åo­×}”«òæåÅ�…—�=ÖÛ.F\×_}±ÙlXñ_4—]%þ–Ym,²¾µ¯ÙJ}µ–­kZÖµ­kZÖµ­k_ZÖµûkZÖ¿mkZ¨7’I&’I¦’ùe–û¤–Y.ºù$’û«uòI}Õ­×ß}Õ­kZÖµ­j©s???kŸ›´Úfåì¶{,¼œýŽÇ?&lÌüüüɯÈËÍÍËȾLŒ¬¼¬‰.’I$ºëï¾êÝukZÖ­q¾ûä¾é$ºëï¾ë¯¾ûî­×ß}Õ­×]u×Vµºë«_ZÖ¿mjÿ�ðü�òúšMFfÿo­Òëì÷æm3qð ¥ium¶ùä¶ÊË-m¥Õ¶m­o’ïOKl¶µ¯ÙFcñ㢻Éîö꺧W]¿cw_bñ.´áøwRZbYºåû¬M6>Ãk‘rþ�Ñj)•\½†]ô¤8x0K<µ¶8—Ç~�å¾Iw—VtGŠ[ù/isM/ÃÉ�\øôø™Ù6Ýºä™Ø°_²jx¶’,�–miu¾ÌLY.­Ô¥+Z~Í~›Ül(½ivD¶Ù[©o»éÙüô²Ößu¾êE+uiëOZQÔ�&£A§Öipmöâjð±ð õ¥)uÖcÇlVOO²éfº•¾úÿ¦ºµ¯î¯ñoÇ~âOŽ]'ã7Wã}ß‚twZñN¸ãòß���™µ‹�ê±ðsyÚÌ[m†Mÿ)ÙÙ6Ëc->Ùó²¥’êÖëëZÚ-Õ�mÅzs­8Splµ¼7­xoàÜ_ùçÊ—Eŵ8šme™yRÏ—™•\L;k,Òß|²É[¯¾ë®ºµ®ÀC xðÅ� ¾È Š8b³Öë½±Åm,²ßuպ뽶ÛJzÖµ­_QžžõýÇäŸûþ—˜âÙO¼èï·_²­-üé5y³R˜Ò]w¯¯¦ÂOJR”ûi“uk_± ÆkñóNÍñ慨E86¢Ù9ŽY˜}7Ý“ãbß&VǤ» ‘ÖN¿Ýçf]—샮;ku.0E�[ç¯5šY$¥˜ÖÒ±ºüFþÿmOø—•üG[d¼ÓÇœºèùÏÝ1-»;wÔËg‰�÷‰äÅÖeìs®àÊh2±ã¾x0ðõû]¶Mõ­Ô¶•ñ\ÛW\¼ 3â¶µ›µ¬”¶žµ¿ZÛI+élw_u`¾”ºž·[m¶{ëV•«uB1‰§ƒäÛ‡Í8Ž]õ¥±ãòm²Vµ¥)H¬ÙâÖ_ZÝöRŸN•ûqº|÷ªþCüì­¬–E¨à^cøÏËw7É%�ÇM.ƒ¹ø^ËqmóßE¸ö_­Ç–ÚÉ캱ҾêSÖ”nÇ·)Ïá¾vøw¿×eÓ H|–é}~dõ‹jWK¾ì ƒ�ãVܨgŠÊfèvy0ýJ[õ"úžøî¶KmºŸ[C,�îµwÇw¶êçcEZú[_èsËlÛéu+OΊJÓ×òÓ×ÖŸk¨ëÉV{3Ðñlð¬ÙkvéimcÏÁËÂ’—R•¶¶eA$Òê]d–ÖÚÛ'Ûëmiþ…X£¾:à »z;¹ºcgLzë»sª;¬6ˤ5Å®>âŽ)—LšdköÐW°m®÷ûñrlöúúÅ%=l¯Æäz\^IÇ·¼{:r0wÚm¦—3"8åÇŸiƒ>D3Å4SÃ,2Ã=mºÛì¾Û­­i[kO°rv´­µ­·R´­µ­+JþZV•ô­+þ�*¡†Hä†I!š;â–+îŽX¤¶ë$ŽK.­·Ç%—R—Y}—R´­+JV•¢¦íž³a¥Ùl4Ûl<�v×S�—¬Ùëò⺠¼†D˜¹¸yPßJ_F.LWY}µ§­·[ZU®RG|R_–ÖÉ#¾èä²êz]eöV¶Ýmiû•¶êz?ãü? ü27QëÿIvOÇû¡í¬Ø}ŸÿI†m§îÒ¿ùæë§ÀŸTdw?ÌoǧÆŽ²É§ò+Œö½ÖÒ´§¦?C`m»Ë.O¶éé/]_󴝿ý—Y_Ï·§? C_ÍþM¼LÓl±£ËÇÂæ¼——DZÅ-¶ì:û®9§=ÔäÒÙ¢šÊI‡´ãPÍeÔ¥.²øéu·[u)u=¶Ûùº—[K©K².¥+OZ{¬ÄȾ˾ßݶûiZ2´t©t"Çönyns•\.˲í»Ù~?ÞKßg­%·Y“X½=iZzÖOJSÖžž­ ùNæŸÚïã;äšY�ú;/Aág“™zœºSÖë7òtÏ2Åã±Çë�•e%ÉÞäcÅeoŽèíºúVú{iVò»žKÕ¾.ù!ÙxöI.O_t?nsLHb’(¥Ÿ/ŒðA¹Ä‚)gƒ*åŸ'Û-ºøä²Û«JÝmÔõ¥6fE112²«mn¦64ù¶•¥+u!Šé=´­iZRµö¹p£éV+^IãW¦^ÏgÌòâõ‡UKµZšÝKkmÛ¨i~|öú]î¶üL m³í§¶êd×Óí·ì›çàÇø÷þØÝñÜ#<óIY¸¯�¸¹Ý+ÑyY6Yv>_tsÞ;d½“È0d‹2ÙâÌàM½‡]|sÁtsO|w}\Zû%gøj|?·oÉû?Í^_ª÷áñHòzƒ§o˃øîä{\L]‡er¬;2p«•V£A“ƒ¨ÄËÆž‘Ë×g�%µº?ÍÉYÿ�im¯îââzÓòÿ;vDÔ¥Ñÿ¡m–Ýmßù]+Fæ,|L’‡g®ÄÜk³õYñýl-–F\^µ¶·ãåE|2ÒÛéùÖ_ì¾¾—Sí¶¿m>Ö)ï^—à>Gt·ltjjäÝu¿tuç/ëq­‚jbædqžm¢ÎãÛ{µ¹µŽ[µÛl|Lû¥ÄʲÚÉ‹•dsYé}–ÖžW�pž3Ù\'—õß5ÕA¼áüïŒïx*ÒåR¿wÚñîI¬ÊÓîuó{kK©f¿2HëZV—SÝëJÒ¯ñ$vKñIe·Ç%—G%—R•¶û/¥m¾Ë©_²¶Ým}+G/yWÌâ|‡mdz¾Ü�^]ðR_m,¦N=Ô¶\<»,¥÷û,ÌÄ’Ém¶µ­m¥þ•ûiU køŸØÞ y_ß%vµžîiÑÝ�´â[1.ÀÄå|~ë1÷Ÿj°dÈÊŸEØœi­ÞàE-õžÌ-ŒT–”’—[JÀ¼¾ñ¿“x�ä§nøñÊ¥¿3;­¹^F»W·¾Èbþq-–>6÷„ò£�6D8·r>#´Â; I}q¤šèo¯¾;©L¶×ß«Ødá_ZÝHdþ‡},�ßJ_ õô¥)îº;©ëéù.õ§î<ûWÜùÀ÷—Å%’Çum’;í’Ë©_JÛ}—Rën¥iù+JÑú°3²õ™Ø{, äÅÎ×åãçaeC_l¸Ùx“Y>4ñ]þ–HfŽÛ­¯îV�AĹ>ß„ò®3Ìøüöâï¸� Ór}&MÖÖëqöú Ž6×[=ÖÛu—]l9˜–]ZR´­}?-Ûkñ§‡"/O©�4sGëOZ{â¾Ûìõ§­=iîµÖ\<¨³q1s ¯¬9xðeC_·íŠx­–:ý´¥~Û/§î/´êþ¥í޳ë¾ÓãwýN;Ù\ˆóý þ²×ߥæZ "Õßë>>$õ÷`ìc¯çÅÿͲÚý”¶+Y°ÇÛkuû\K©~&Ïa‹}=}/ÇÌÇ�&©ëKkétrR¿m)VÅ?CÝ?på÷?Öݨæü¯]Xë �oöu‚:úSÓ|©20î¥)öz_‰-—Sý ¨÷ùEéŠøóò=ç?MC¨ŸE¨àÞT÷†ÖäVÛ¥�€ì;{»ëÌšÖÚRÚÙ±àÛ=~MŸe?2Z+&ù+ë]—Rùùå× ÚE7Ó½¹ï-×E�wº;8çcî&ìn'mkéJRZq~W‡õ-ü–ÉëOÜ`ŽC�v6ïgÕ¥Õ».LŠVŸ“Û•é•e>ßݶɩJÿ£G�h{H°7ë)™ÏæÎ¾Ë«n£E�“”§­¶äåK�¯²Ë®ô­)uøùRÖŸm+_mÑJ×ðtô¬]“òѰìœÜl¿ºxñã7lö»>8«\(¹G0ØqšÁÖåOXd¶9óøÇdnf†Ïtw_L;ëJÖ–]mÒ*ü4ÝsŸÈ¼ÔìÞÅ¿Ytú·è=î$›ZÒÚÇ®å<ã—ñ~‹í¥n¶m—Ôîëmiéù�_O_·Ò¾ï�C}ÛÌŠR•Š,/£uÞ´õ¤“ÏñÒ”ü¿m¸÷ÿïൠ8æUbžîλ¬yEöI[$ȇÏOË}¹»L,yãü•§çbß'¯úþ'NʧZ|$ùŸ>>ÚÝ^ç›àõZé#üúK¶¯4ï>¶×òmL7[ Ö[tü ݼ—ûý”º¯¶—RêÛëÌߘÎÄ—¬¾4ü°ßcÙYr7\[×qÅl–ÇuñvŸ0ã}oŸ}.¾)­­1u|¦y®·Ò•¾Øëm·[uiu>/#Ȧ6�g%m­Þì[±ý)_OJåÖÜZ]ÿÞVo_ô}åSî­å‚@§'a—‹ƒ‡ ùy¹âbÁ=o›#"Kb†+)û·I%ô¥?è½Wàü»³y¿ënÇö\³žv*ãÜ„ñ]4yÛò^]Ë6øz7ÇõXÞ¶ýãe¹ÜçÃ�ž´÷Ë%´ý×®ëþ Ê{G�pÞµàÚ©7œÏŸò� âšh¤† 6œ‹“m1tú|O“$8ØôÉÏÌŽÊÉ-öE+[¯ºÛiZÓûcÁ.TðãCoºYå²(íõô¥o’ê[o­kö[o­~ÚþJQÓîÆqøÓqìzYZàbYL©l÷V™; «Yö4­þ·ûgÌ’û­¥�³ÒÚ}”¢íߌŸ¸§Çgƒ^<ø•ÆcÖϱë^ …'dref“šw&ºþKÚüÂ,¬¸ Ùdkw<çi›]e™4º\-E˜˜t­#ÆŽÛmñ3Ç^)âoŽ}Iãç¤rjúÛ‰aê³öqÅ|7r.Q™|»neʧŠIg¾ùG+Øfgݾë ûÇÓ³ÒË-¥3þZü<|(òÞ¯ÚgÍý¥»7k÷lh¡³ôG#Ødq]†vL²dd~•ÐâÇJE�_H»~$� ¿†mÁ¼Öáºß~ÿªþáÖ½½ô)ùù�sÈwÿyÿW:8©üæûy0/¶ yr2#ßÙ|—Û=<9Õý|X¶‘[ë.¤YŸ–ìi/üË¿/ÿ šïÉJ~KëZý”i⽤4¬ÓªÙÛ¶ë®—g­}š\m}Õ­=+YuíLÕÿGÖ\+¾ßÝ]ð5ÝwßÃÇÇ¿<†+¢ýã·ê ýõ¾²O�ãÆv×Çý†d—Iu×ß&vYI5×V¿�Y+_ÝY¹ñ³Ù8½µà?ˆÜÛqvþyú'€q½ÖÚIë“>_*àZh81®\÷]uÒçÃÊøÎdy­}ßZ˽~Ö~ÓÏ÷�Vº«õ®¿ êIî÷Vé­ŠÛ'÷VŸéíšÛ©wó+J²­íÜ} ‡y¤®»žYµ²Ëé Ôâe]%œ»7•ÖÏŸèÇ‹��uÑ‘U¯ãñ·#§þWëÝX˜¹×h<¯è¾´ì9vrÇíÖÓ›õÆ�#Èøþ¾ïu}Óëx·_qí†E)m)îÛÛ_¶ë®A�ñ$t}Ü Í>ܘ˜°ô�õÔú‰¶K&¥ÒmùÿYdÝÄ7ô—·Vøk¯àòñ{)w¥,’—}Ÿ�mÕbŽw‰ô¶XÙvÙe¶eã{nº•üé'Æ»Û}×ÓýdŽ”¯îú À¨£ÆðÀ Ðñ‹K\}#ßÉJÒ»=–6¶ ]eiýUÓ_,wVŸ�d³l«e}>Ït^Ÿ–‹?—�ù\cÆß2|®ÚcÃoößíÞÒœOï8G°‡OÒ\_?–r=ž»6h-¶m'"Ü÷&>%ÿFK¬»/C}·Ò—CDÙ? çHËÄ|bî®ôÙjó0vÍÚx\cK—“[¾ï·á}M§’=–¶Ï}ÑÛ�ü2æ{Ü9o¥-ºù°kmÞ´ŽÊ²ÏÄú:Éòî²¶ß™“Z[}nõ¤˜øÖû,­-¥kK}³Ý-?%+_ú� N%G·kw“[+qø†›WlžÙv[ë'­”º´¬˜ºì,Ÿ­JÛééu¶äeÃ_ËöWÓÿ"_�sº0ø·�>1tT9rco{ƒÊ/áÅa�&èéŸÃúk­¹fû|ZGZdÁ'í=þêßJG$6~mÕº•²=‰3²³8—ƒ/�ë³1á—µûã‰ê·8’]wÞ3x·㼯˜eW;$�֘ܫY¦ºûî¥ö[JÒžßuÖÝo�æóß“éÛíôÊÌ‚ =ië_§m$É¥lûiéwÔÇ·íû~ÏV�+®³’ù#Ì4×{3qþñ�“�Ÿý¨:ÿe~Êhî¬Y{}æ�2 ½ÐÝé'�Ãyá—ðïµù¯™üË[õ8ÏN}÷¯º£ëÓúhòM5¿ÂÍä_K:Ëÿø‹à»‹q©døòA4Ü‚Ùb¾Ù°«é�¸.¯ß4ÛimüØ=Øø¾¿»5öÿG’Ÿm+ù‘]KiöV•÷×÷hݵši 2pçmõX[Íf~Ÿe 'ÁÙb͇•¯¥nŠk+eÕ²ïËd¶zû¬ºŸm·R•§ÛFò' ºËÊ~ŠíŸû—GNEÕýÏÁ9_s=e—CgèŽC�.Û6dÐdÓUÈô™Ç›¬Î¶Ë¥ÀØcѤ‘[Zx¾Çëî%Û=Íz¿žêcÞðžÂâ»îË4òM>=6\’k2u;lKr±d‡+Yp²ï¥“E}’Ã¥ö]möÒ´þrÅñI ¶Òø¦Žø¤²¾¾—Ç%µ²ûkéZWÒëkZ9�ʸîgä;n=�öϫ˾ Kí¥”ÉǺ”—.Ë)}þË31$²[m­k[i¥~ÚUH�›&vG‚ÞWw¯‰}¯Õæ}#϶¼J]­¸wëðùwº�í¸??ÔaK>LøºÃá;-~ó9o¬Ñáì#¶Z[%.¶•‰y…ã?-ñÉÕñó˜S&|®Érq´;¹ñìÆ³–p½…-Ùð¾]�RO�eœ‡�åãdI’I÷\‹¤Ç¾¿RéL ¶×IªØdaIî­#¾µ†û©éõ`¿íŠJWÒ”­koÙw§ÙK©Z~ãϵm­šÞ�÷?~á9Ú™%¶ét{™íŠ*=Æ(òá­Г7ï5¥ÐYçø0|„�±>9;“ v;¿¿r¼�Ýäë4µ¥)]Yw'Òò®3ééöÖ=§ciy”´õÿMmÉá~ŽÛ¿žx ›×™wbÇ—ÑÝÇÎ8žÛœxWÙ‘ò½¥ø¶eeæEÒ]·n«ƒö}¸Zìv› |uÉæ[X2>¤šn{Þ¼Ï:ýî·/”ôÇÉ—ƒõ�Ì¥}}o‡.:úR”¥k ÿÄñÚ˜ÛÎþñ§¦!Ç“ëõ¿TòÎÂËÎú‘]’öß)ÄÑE®¶;kY£ÉÁÇê+&’·R–Ýfd~ßÉssü‹nÉ×bÒ—{á‚|‹®û=µ·&K#²”û}}Ö×ï_³÷hÖ„2чcàêÁÂÊÙfáë°a»#7?+  km.›'*[ ‚+kum¶•’[éOZÖ”§¯Úö=u×Ü϶»‚õW\qìþ[Ø}™ÌxÇ_p>)«¤5ÙòngÌ÷x³‘S"y"ÃÓÖ´�·â'ðr^àé-—¼Mv_`tÚnȇé3w½'±Í—*ý�öcáäåeËÖœ“6ìßJß ú�ŽÏ&[ëH,µâ¹®§ï˜Vì!³×#•¬¾”§ºL:ý²zúYu××ÿϧ­Ô¶Û+}~ÚÖ�,W„ƒ63Æ­ïÜ9ŽÇG$–ÙÿUuÑYZWÝ.ÃS}r`¶ÚþJRÜ ²®¯ý¾ÿw””ê�‘®Ðñ§qº®òË¢¶ÖèôöÅ}ÿ§;o¢rr;‹{¤¤”Žõ�U�ÎåõºÛ«[®¶”­=kë$ßÃ=Þü7Ê>ç轌úÜLNïêÜG§“++èì6ǧ¶¹™zý«êÒÌËò88ßçÍJzÉdZÚÝJ{i}iïxU#ÍÍí-§ÞqãšÛ«})Zß‹}Öý;m­?>·Y“uÕýÚRÏýæò,ýM­”À›7a‡•�› ™y¸ÓbeA}=l›";¡š+©ÿ”ßõ¥è¼?fõ¿ î>¸çýGÙ\•õßhp¾O×¼ó‹ì>­0yæZ\Þ;Ét™u‚H§³i§ØÍ ÷G}’[Kýmº—R•§Æä|wEËøö÷‰ò�Nÿ�r}6Ï�r×,Ý^çGºÂŸ[¶Õlpç¶ø2ð62C4wÒ¶ßõ¶´­*ÿ—[möÝeöÛu—[[n¶êRën¶êz]mÖ×Ö•¶´¯ÛG2yçÌàüŸc Ê¥÷Å Ÿ_[•uµ¶™ÚÉyVÖ¶Ûmo­”­’Rß[mšËí¥kíõR…ò�ñýÙ_~höçŠ=‡fÇa®ã[ùRó¬Ý|¸ý§ÒÜ‹3:^¼ì V+0¤Ÿa¯Å“ m%ùÚîC�°À¶inľêÖòá§'ð[Ê>{Ò;{3rø—Þ.å}Kɳ.²[ùgVïs3?ƒ;)çŠhäÜk+‹6³gJE”ÙàäV;k b¾ü¾Ôß§ØË�éZÁôlKëZWß�}ÕöÒµ¥ŸŽ´­·zÒ•­iëééZÊÒ||Ko¯§ÙZÝU'¿3QÇæ_Ê/šþ@`l5ûn3È{·}ø×SZW[·ë. ÅÀéþ´Ýâ{k[?ê÷àšüén·ì’|™/ûkukZÖ>X;¾ß >B|žç8³åM¤ÔöGZq»&Îûö%š^¦ÁÂëˆòõ²i±ñõ\ƒ;Œämc²/mµ¿>ù.§Ô¾ú×r|¯½ï3ï¥döC/Ýl¶Júû>ëm!’–SÖ´¶;æ²ëéJå^µûkW„s%ÎçÀm޹Û<®oŸ®&¦²àé©­)6Îh½¹yVÒ—ÓÝf¾Ê{­­µ’oZV—GöM§ðuüb]Û}ë̾J;_�Ù“×^Y¡ÝñnO¨×r5Éu-!Ðî0àØjwzMÆÚý®§i�•d˜ÙºíŽD�Í –Ýd‘ß[n¥iZÑÿ.¶ÛíºËí¶ë.¶¶ÝmÔ¥ÖÝmÔôºÛ­¯­+mi_¶ŽfvÉàܧc¡›ß~5—ýçU“Û\½VE××k®úq[tÖ[mc—Ûm-¤ÑßJzÒ”­iSù^øõæÿžqwм¦í–Ó‹ivó”æÛ++Y{£yfVvG^ò¹rmÕipò÷xø¸³j7µÄdz GªØcÁ[â†Ûî­7äƒÃß‚žVsÞ–žÜÜ® •}¼×§¹t°O?!êîE—›úL¹¡º•“oÇòðò´û ¯‹³gk¥š8­‚Xn»ïõWj6RãÒ—}ÞÿèØ—×Óó ¾µô¶µ¥×WÝ ô­•õô­}¾ïJR´x§8Ú ø€CÅ7ÓqŽK¤ßÃõ+]VÇ*["»Ù|ø¶ÉKsqiuiZR™x—_о­¥ð‡ÉÞEá—¾7ùUÆ)²ŸaÑ=ÁÂ{?S©Îý™Ê8®£s�ü8á5έ+H0¹ß ›?M•Zý•ÆÎ’•û*Ùß ¼€Ëñgʾ†ïÜy²bÃëŽÅÒlù-¸Xñåffp]•òh; W‡•¶Ë²÷o±Å޾´­·ÍJÒ´­(úZ|ßÑÛ<,Ê×ÒÈg·ê×Ûï­ ’•‹#ÛoîÝô/»ÓýR ž˜!ÉÇ’É ÈŠ9àš:û¬–l¤‘IeÔû.²û.¥i_Ý¥W˜ñ[ƹ÷âüï…î°9/æ¼wIËxŸ"ÕOL�^ÿ�r=n6ãEºÖäÛénF×W™ðßO²øä¥uh–n&Ça�‘f~4¸Yx÷Û,X™QY>>D[ZÛ$3Ã}.¶ê}•¶´«??«Ñ?HÆý™×:þÄÒ}ÎKìÃÛàÖùôû:ÇK«×[é~.Eime»+ÒŸRÛ~Ú]m·Ò•­¾ÚñæÃá˪þ^|p‡…æìu}wäwV×kÈ3‹“fË"´’ypµ°æì°ú;ñ×ñ­Ý_ ½‘·‹àçq>˜ã›||nÐî\ì¯ÑqÈmŠì�¤×C$çYx3GX0!ºêcÛ‘ùwCöß Ðñü�ÔÞ¿� WÒ™>”ü¾”»èÃëö_5m¯Ûùie+J×òÒ•èŸãZ®#¤ÂÐ顬XXQú{ä­·ddÍ}}ÓååIm¶R\œ‰+[®­)Kiù-¥¶Ò–Ò߯ü,èo�¿úëÅŸøÜÚ»ëí}ô“c´“7—sžS°­¹›°¹öë_ó™ò½�+>\ÖA4R<\81°qñ±a°ç z­¨³W©ÄõŽ\ìéï’L­¦÷u™Pþ’äýœòægdÖÛk6L×Ýém+Kišqq ÃÇ‹;b‚)dv[O²”§ÛZÖ¿–ëﺵ­Õ¯ÛuÕ­köÕ÷Û`ÌOîñ]‹É)Äø_ ÝÛ}cÉÇÀ’ ­ûn¦Ë6´ÃÀº–ÿ¦¤YSÛ}ßûíµ«˜ÿ2^dSÀß�/-<Œ×î%Òó­'XløWQfaäÃÒãíáëδÚk"—óódâ¼£’AºÉŠ:}O¸kr/õ¶Û.¾ÝKó·È¼_üDï¾ú—+kÁøΜ7ïX¹™¸Ù]‰ÈõÎV.+—&g8ÝàG‘uµ²Ø±ë|—ßv]%¿ƒi�n·_—›w·ýO ×YK©}m¾k½,‚;©e+u-’k­¶µýÏ_ZÖ”û\ÄR~«í¯À±Çôyü—u®Ñjãú¹Û<›1¡¥}ÞÈé_[¥Èš¶Òë­ƒn’K½+í²ÚÕŸÃa=,¶¹Ù2ËZzßV{ÆÆ‡X-öCvÇ¿ûí´ôõº¿gºû«öÖ¿–µ¯«î6�–ßÜ„;Ó€×—ñzìµðVMï¶lÌKc²ëåÍÀºÛnØkí²ÊúÉ%lŽ’ÅOm×{ãö[éõ.ª0ÿŠ;⦾}xI7}õG¿iå†úîEÏx¦6Ÿ|½÷dôÜðC�Û_6¶O·Ûaájâä:«vWßõ³ëðb²MÌ÷×�ß5þ_æ�‹œ‹‚éîØwÏ@Ǹç]i7ÞnÍå:) Ä“°zê,|\\és³y.ŸUN®bú²nµø�RXaÈȺ¾o”iÿKk®úVû³1=fÅû}+äúÐ;ukõ£·óiözÉm¾µ¥=\þTþ¯a„ÀýÐ<¶œ‹„ëȗ߲â÷Ù¬š—z{î×]K¯ÔÍém´¶–[�mØôÿM_»Öµûkë[[ ·Ÿqùkñ¯¬è[¼»eÛþm5Ý;µ‡*JɰÌé}Ü[-¯BnîöA,:ý^‡[±âx±Y[¤¶)l’þtÖÝ}‚ß¾Zcù+à×á›�•™=‘ã]ø�9Êqd»<¹ø¦³’õVþ~C>/ü8ù>ê»:ÃÊδ�}•§ÇÙÓ¯»W‹M‹Ç{‹ª6Xl�+g×ÜÚý~ƸvO,Í‘¬Øclt[ ± »7'èÅKu“ʯ¼ó;­òºË¿x.)Õ{g—�ò jÛ­æœk5"öï8W'†;ó´{d‚:Ée>¦m–}¸21~¿g�v6d6ÍkK©ëëmñßOÉ|R[étwÓù´¯ÛOZWÖ•­u̼{åÚ ¦ÊãþÞQ«²—Im¸¶ÒÄQÛmo­²k®º´ÊºžžÛ~ït—É_þoäWmò+øKþAüKÊä<ÛÅÜhüâé 2v8÷uήš^ÿÑkaÇû̘۾”Ÿa°Íæ�Ëwݱ¿‚9»Üí…lú×kðéѲÞeþÿ'º_+wË|cØãù%Öxÿ_7ŽÙvî­& Ÿ¥2åÆÉãyÁÇùźì\x­ŸO•fÏe—=m‡O¶ý¸×iÂ3qîºMmô̓òÒ+î¶<»>Û«Z}´¶©m´§ÛJÛuÕ¯¥,û>Ü•‰•ƒ‘.&n6FTÖ9ñ²¡“"éùl–m²Hï§ó+JUž]Ãùw_òmß çœ[‘ðžeƳæÕr>%Ë´›>7É´Ljûr5»½ç kªÏǺ¾—ÃÏv.%¾´§­!¬×]mÕ§¾;©ê™OÆ—àÚî.w�Ç»?äÓ°1úk„Ù~6Êﺇy¨å¹¾²Ë¶»UÏ{7Ý·_õæóA‰-ñèîäÙ™¸9Ã÷�F]–Él•ó§˜ãê5vÛ…²þÐ�e¶¦fë.멃—v«°{+þ§j£Šûr13pøïßnšË­—qÖúWÞjx7§²}Ä´¯ÙK¾å�uv‘ÝKgɧ¥}i_u·[ú¤Ÿ¸ÛmFŸW ×ãj´øPkõø–R81qíöÙm?-×ßukt’Í%ß�|—ÖëﺵºêÖµ­Sûñ߯þŠñ3¨ø—CøáÕüW¨z—ƒáSŽðÎ#…v67_Kk™µÚgeK•¸ä|“o5¿_a¶ÙdeìöY7]>TóM}×ÖWÝkÖ={Ó|#�õ·UðÞ=ÀxÄ®ƒŠñ}n>«O­‚ùdÉȺ,l{-¤¹Y¹sI>LòVùòr$¾Yo¾K(¢‚;!‚8áŠÊzYV[e–S××ÒÛm¥-§ÛWÒf׺@�ù1ËlŸ/QÃ1d¥ÔÁ­7[j[캖eÍ�k`ºê]Y,–,Ie’ëkJR¶O~ßÜ®Çñ¤ùõ‰Ì;7 þ8ø>â,½oQÇ‘]ñ5pr"ƒ²yn�cÇú{‹dÉJþ’Ön¸·[îvû|¨.¥ ÉÂåÚé)[¯Š´²#Ÿ‰‡Êø¦—§|2ã9¾ë±$‹¼{Sè]u>œ×÷ã=iǤŸ6–Ý_¡>ße›‡‘})v²{.c¾y±öÙ‹«Žµ¥d­32=+u=c¶·Ç•ô¯¶ûn¾—]ZWòVËjÕ4Q*c0èxéÀ+®×ÍÍöpVÜݬwbé#–ÊÛv>®—SëçR—Wù팶ÒÛ+í¥i ~ëk[eXÛø>>).ë²ä'ýÓÇ/ƒ�÷6«m×Þ/êw_O'�õ;»sŽÓŠ ›ï—gÙûÝ]5º©«>DZ vDðË>òž“fü;þ_Ô=A´ó'±´òbözê®Óuvt9PeñÞ—‹6 «·wcecb]_fîµñfC}>¼Ri0ð'‚Z[™5ŒµÂõsûc5¾™ÖÓèÒ¿–w>?¿à§ä]ýã×Z?¹ôO‘›}¦÷ 2ÿGp^Ø’·ìù§¾ûîÈÆÁ×òI$“u¨†—űə‰�xúë}q2Ó}Ç3ô„ÿ©säºëéëu~–e}o–•­Ô­)lÿmöÓ×òÒêR”¥(ÂHÛ¸Dñ€ ™ÔÜßø Ëðóò/ºÝF})­Ý[O}ifòY[s=–[}×_�5¶Ëö[[î²—YOç�–ø!ù.Ÿâóä­û““geÙн�Ý3䎺¼Ïd}[ËöZé†ð`âám22v½[ɰ0wñÙ��~nn^ºãûü—º«ðùçD–O,Z\�”pYYg¥—‹méªÙ[Inöâf{qò=n¥,޵»úEÞ묲”†úþuk_Ͳë«öÕÒ+n¶ûm¾Ë­¾Ëí¥Ö_miu·[u=mºÛ©ëK­º•õ¥iùW*jöšÍæ³]ºÒìpvúm¾&ÓS¶Õåãì5›MfÃ<¼ Ž»?Iqspsqe²XfŠû£’;©uµ­+J¬`ŽHæŽ9¡’Éb–ËdŠXî¶øäŽûiu’G}µ­·Ù}µ¥iZV´­*ÍoôýÏö>NÛA£ßEl½>³mu­b³cƒ�™H®ºž•ºÏõ†úÓ÷mô«^|€ñ#ů+tøº&|uéNûÖk¡Ë‡OmuŸç™:¿E|93ñ­�#ÔçìxÖt–I_LŒ qç²µõ¶úWíc®Ëê§î} x·oõ—í.5õ£É¦ƒ°øæZ{2¡¯º ¨µÜ‡_±Å‹* ¾Û%¶ÚIe~ÛkJ¿”ÐA“bȆ)⺴­ÑM’Çum­+mkeôºÚÖ•§­>ÏÊÆ;„ë=…$¬z\�d²{«YuÛ<û+e×zþtpeM—‰g¶µû)Hý´þg§Øâ?q~_…¾Õ�>MAöGmv7åÍ>ç§{»² ’,Œ«f§ÕÀÐö^ç²øf¶Ìi%¥ñA�¬‹ÊÇm¿J¶{­»›ý…ð}ñ—Ø{ ®ß#Çn#¶Û['ÔÉëÞwØœ;_…-ñ]“긮¯”Ûµ·A[©}±Å­¶ Ým=Ö]OZWâÍÅô3ßY/×EmÕ¥)é “ãÙOJzSÒ(%Ž*Wÿ½û^ZoxEßl;ŽSÛZÖ—åj¥·Ó÷)m)¨Žêz£Z´‡’þ /�ÜÊÃwòOÍž?wÖžü»7¼¯£9DŶ¸ðáY‡Ñ<^|JA_u+t²äÖûkOÉZV·j6çðÑø#±ÏÊÌÖv_”Z2rež=N7ë,Ýv¾).÷G‡ƒ~×§óvÕÇ‚•ô¶ìœ¬‰kO篺¿kåÉÁt×ßuÖËŸ·Vµ¤qÏ l²ŸùMµ—I+J£ujþðbâú~äŸü³þ·<§ù’¾ ÿ•·–¿ù�§Åãäÿ›-á'ûòùQþÚº“üM?ÇðOÿŸ;/üÍ‹ÿ¤gü¸‡þŸ¹'ÿ¬ÿ­Çù’¾ ÿ•·–¿ù�§Åáþl·„ŸïËåGûjêOñ4ôÿùó²ÿÌØ¿úFÁ‹ˆéû’ðzÏúÜ™+àŸù[ykÿ˜úwü^æËxIþü¾T¶®¤ÿGðOÿŸ;/üÍ‹ÿ¤gü¸‡þŸ¹'ÿ¬ÿ­Çù’¾ ÿ•·–¿ù�§Åáþl·„ŸïËåGûjêOñ4ôÿùó²ÿÌØ¿úFÁ‹ˆéû’ðzÏúÜ™+àŸù[ykÿ˜úwü^æËxIþü¾T¶®¤ÿGðOÿŸ;/üÍ‹ÿ¤m#VœƒÛ€=�㘼·˜ixîtùØ»9²#š|ZÇLˆéNMµŽ³G,~µ¾ R¾¶×ì­]ø³ñ‡yççï�ž#v(äÜ/‡w_-Þh7¼Ÿ‡[«¿“jqu<•r¸æÔÛ»ÂØêþñ6V‚8®¬ÐIm#’êÒž¾�Øøêñ¯…ù}æ_KøéØ{ŽQ áÝ�“ÍáÜíø^V§ “aÛÆz×™s<*ë2wš}þ®+¦ÙqØc—êâMJÁ}ô·Ûum¾ß¯¢À‡gµÅÁȺ["ŸëûÛ%>–4Ó[í­öIm=nŽž¾´¯ØÚ¿ø1qý?rOþYÿ[“±ÿ2WÁ?ò¶ò×ÿ1ôïø¼J³üÙo ?ß—Ê�öÕÔŸâi�ÿ€züùÙæl_ý#?àÅÄ?ôýÉ?ø=gýn?Ì•ðOü­¼µÿÌ};þ/óe¼$ÿ~_*?ÛWR‰£ø§ÿÏ�—þfÅÿÒ3þ \CÿOÜ“ÿƒÖÖãüÉ_ÿÊÛË_üÇÓ¿âðÿ6[ÂO÷åò£ýµu'øš?€züùÙæl_ý#?àÅÄ?ôýÉ?ø=gýn?Ì•ðOü­¼µÿÌ};þ/óe¼$ÿ~_*?ÛWR‰£ø§ÿÏ�—þfÅÿÒ3þ \CÿOÜ“ÿƒÖÖãüÉ_ÿÊÛË_üÇÓ¿âðÿ6[ÂO÷åò£ýµu'øš?€züùÙæl_ý#~¼ø$u²éö|£&´¯­Ö×7YWú]ëéí�QIm¥m§¥}/þo§§Ù鑸§à°øÁÔŃ/*ï_7yv ë~tqöJqí}¶æVh¢·]…ÐY[¬K+‡m°ËíÙÝuõ­÷ÙXë[)²á¿†ãÀ5²³a¿äÞFv‡éߣå…ÄuºkïûÞ>E2=Ü®8ŽúÙ¾„AZ}ÿéÖ9¯­-¤ŸNøÿ´#I õºO¾e[[}>œùÛe+ëJû©\h±ä÷R”ôþ{ÓÒ¿“×ÓÓÚjúS¬õWÇ,|f ɬ¥)Y6™9»+$ô§óÒbedIƒZÖ¿oÙ(èÇH~Ÿ…®ŽÉÔmp¼;Ôö"Ô}Þ¿§;¿ŸöOhãläÇŠ ~¦ß‚ïù]ÝY›õ憲Égèbºé.·ÛHý±Ûº]_ðíñ¯Ô¹9¼{Å.Ès2âÆ‹"^ÐËäý½�u1~¥l¿OÙûîY¢×K%ÒÝYnÅÅ‚²ý”¿ÖÛ,¥¿W�è±}ÿOY�w¿Ûë÷Š]—éíõôö}î龟óßo·Ó×÷%/ [�f&»/²<\,xqqã§ÙOK!‚Èã³ò~å¦ë¤êž‘â}Òýe×½EÀuW_~³„u‡ ã|ˆko’È¢¾ü5Å5º�.×Å–Ö±Ámkm–ÓòRŽ‹qî7Ç¸ŽŸŽñM—ŒqýV48z½ÕàétúÜLxì† \ f¶ l,A²º¶âk1®žûm­´’y+ZG�‹º´¶³åd_lvzÖ”÷]O_±ªþmùuÕþx«ÝžY÷Eôá1Âóy$ºœlˆqvœ¿‘O,žÀ´Sd[~=œƒžrýŽ£ éiôcÈ̲ùklVß}¸ƒ¿{ÀøÛÓ]‹Þ}Ÿ´‹SÂ:×�fò-Ä×O‹N|‘{1õ<QLÙñ±ò¹'Üäc뵸վ۲³ò¡†ßξ�Ï••<ÙYÒ8`²·ßukJ}”û)m¾µ§ºûî­-¶Ÿ–ë«JSí«˜;ÝÎw"Ül·›)>¦nÓ.l¹ëJßì²²Ýëd0Òû¯ºÌ|xéHã¶µ¯¶;iOÜRCäÇ�ý™å§�=Áä¯qí­Üö_vsîCØ<³&³Gaæï³¤ÈÇÐè`Øfl2µÜ[Œk¾Ž·S‡Y¤·[‰=•öEj±#»ãšù;Þ�¡ß�‡5’rÎÏåyü�;î“Oƒu#ÂÐq­uò[l·jx¯ÃÅÖáÖOY>뉾·]ëZàMŽl»,ܜپËò$­þß²´²ÊR–E+Km÷R(­¥¾¾žµôõ¯Úù,Â�ÄÈ}cÁç缫Uí’Ý^?¦nï*ϳèkẞ譾·[íÈÍ’´†?O[­­õ¿Ò¶Ùw§X>>3ùgÊwœýoÐA¶Àéî?4]�ä‡6×zA^'Ó\o?»¼-…þ¶bò¾}›4 -m�"ø3¶4̾ 1pò«gJ>+¼Úùçå7à»,ëzo‚]…ÎûÇ{;(±#á˜ñRÛl*ëùcfG]v³&,¨q~ù� ²ÓKƒ�éë¸Ø[–×î˜þÙ²îô¾”ºÊ]J[�KìôöÉ‘_²Ÿ�m}´º´ûmtŸ H Åņ<|lhcÇÇ‚-Ž †ËcŠ£²”¶8ãŽÚ[m´¥)JSÑs¯áü[¯x�à<�êxŸ àüoGÃøÐaA­Ñqž-Æu˜º^?Çôºìk#Æ×êtÚœ(q± ŽÛlŠí¶ÚR”¢È=n·_§×`j588šÍV« [¬Öàcʼnƒ¯×àÁ.,Ù6&&4VÇv[Kl²ÚR”¥(ÎìôoÚ<�9â<ã�çè3}±Ý=¿[.¶Rû°6PÛwÝ3,§­.üË®­·Ò•¥oŠûíõ§¹ÏO”��nµù;ð¿µ|Sì+°µ;Mþ¼£¨ùîN ¹óõotqÌ=‡ð žâÃJ}æüL\Œùõûh`¾ó´;ü;%йRÝ`ó#ÅŽ æo޽�ã÷>²|Na¨¾N5ÈîÅ“3+„s�m·åqk¯† ÍvDóñýŶI.=¹Û�‡Y±%»èdKmÇ–FÿÎOg¥.§¯¬r[Z]¶ú]mkXä¶—zzúWÓÒ¿ejænÛW�¤Ùgj6x÷bçë²eÅÊ‚ÿJÖÉb»ÛZÛu=m’;ééu—[ZÛ}µ¥Ô­iZUK'wt¿eøçÛý•ÐýÇÅó8giõ4ä�ñœÛàžMW#ã[õ»ñóq%Ÿk­žH>¶n,³açbIF<’C$wÝY¿uôç=ñ÷¶9ÿJö†žý<ë~K°ã‹´Ÿî÷äaßK±¶z¹ò Æ“;E¼×Ëv¿*‘Ûf^DSÙù’[VÍÛ*|<ŠR“cÉXïôõöÝû¶Éen¶Û«–V—[ZÒž¶Ö�žÅ¬\ü  Ðñû²íÙaEÁw3Ò›t7WA‘,•­ÙÚøéu÷ë«[ë_\�ltõŠ”¯çcÓÒ–ÓéV·XÝøLþj5Ý¡×Ú‹_%9M!í>°ÔeSÄ~eÈ7K'bõf§|ü¾�š]œ—Í^WÔÚø$—�E×Ç•Äãû”Pc[£¶ìÙ²|ü–`w\ê¼+îNE�ou^“îý3³ÙIX'ì~ªÒbzÅÅãžK«�™ËºÏ¥d6Ö9²¸üqKdRÝ�Ÿ‘\µÃ÷ÖæãÛ­Ê“ýY‹g¤º”§Þ1l¥)o¥Ôû.š }•§Ù[­ô»í¯º´Úâ’T{`#ÔªGkx)t¯÷Pâý5�ýIØ;iør?m€¿ß+™þ§{!ÕÿƒÏÚ•â·ÿOöÏê´ž“ˆÿ²þ…ÿHäº<¹bË6¯§Û_²”ûkZþãüß}‘Ùt’]m–Ym×ß}÷RÛ,²ÚV뮺ë«J[m´§­k_²”+_O¶¿e)öÖµýÁ¡½éÙ–rý­œ{K‘d¼sK=o»"*û£Úím¶H¤Ê²J][dÃÅŽúÇ m§¥õ­÷úÝmÖ{j±üO?4úÿ‘úÖxµãŸ*‡máÇ�\�;"ÎI§È–mW{÷Ž<; ã²1rl›î[.ÂõyyŽ-,Qûr­ÊÙlmŸ#a‡f,¾x~L±|˜ìk|Wé>E~oEuúYyÎÿ]u¶k»Cµõwå`Í\¸å¾ý¯ àvÉ&6þ‘ãçìïÈʶÙà‡]“\UÌwß{–º¬Kîû¾<Ÿê«é_Kr2,¯þ[§§ÛtX÷~__²ëþßOͶµÀ(«£¸ð€÷rM$qEeòK-öÇv[[ï’KYe¶Ò·]}×V”¥)öÖ¯Ù®×l6û N§3iµÚfb뵚Ív,ù» ŽÃ6{1°ðppñ¬—'/3/&[cŠ(íºù/º–ÛJÖ´£êè´[žQ»Óq®9ªÏÞrEµ×è´:MV,ÙÛMÆçm—¯U­ÂDzLŒÌý†vE�ÃvÝ|’_Km¥kZ?Üqß4‘Å·_$·Ûv[O[¯¾û©m–ÛOݺë«éGFú“€GÀ¸¼8ùÛúwiô³·’úE[ãž±Óèël–:ÝïÇÖÙum§ç]mÒÝ%öúRÿJ\-ø~¾'õßþèøÿ5Ôa[åO}SKÙÞLn©n§'3K½¿_%8�LámõvÍngéýFÆlk½2ó±gä9»|ÜYi�™QØûñeà~ŸÀ�øÿÏÇ×ew8®/7îþI‰dr]™Ì31,·Šag}IäÈãœ_}5ØžÛíƒ#"™YÖEù²ÙLé¢ÔÙ§×Ç�OmÓßý.[}Õ¤“ÝJzÒÚÝöý8­¥-·ì§­)ëéJÖ¬¤î³¤Ï²Õÿ!úëôŽ 9Σ×?[±oâ†/Y2õ–R–ñº–}·Ë¬¥=²]ZV¿w¯­n¥° éø»þ «Ú�}Ê/@q¯ØÝO¥×qÏ-4|Yl™üÏ©5Öãëx§rÏ��m¹{ ×RY[5Û¼›¢Éš¼Rü|‰eÇÁÐ]î�gâøó·¸z²ß3ú¯Eý›ÒÚK î <:KfO/é¬ gÉ»’W&‹/wÖÉvD²]X+v†\«¥–ÿ¸âBñ<ÏK÷ÌJl ¶ŸyÁ²êÍùÞß©…m.¾ÿ²´­.¿êû©öÛùµ»ò×ÛF—«’Bi‰@Û##" ¼YåÆÊÆš<Œ|ˆ$¾)àžé$SC,u¶øåŽûi[n¥iZVž´}þ'ÊùGå/�dPdÙeûm,TÁûö»G>‰Ÿ—^çÖõOlìt\/ÊÎ=¯ú9Ú:Ë�«Õöþ» \œ®_Àqd¾Ë+·‹ IöÚx½×âRËò`¶¸žúcæ^=ÈàÜCH¥­�ì,§¤°zúRoJVµ›•¯­ÖV”õ­¿mlýß³ÒµÏIU»lôÀG¨TŽÖðRé_î¡Äÿúk;ú“°vÓðä~Úÿ¾W3ýNöC«ÿŸµ+ÅoþŸíŸÔ?i='ÿd:ÿý þ‘Étyr"Å–l+_O¶¿e)öÖµýÇù¾û#²é$ºÛ,²Û¯¾ûYe´­×]u×V”¶ÛiOZÖ¿e(V¾Ÿm~ÊSí­kûƒNûŸº£Ï�+ˆðì»d–Ù1÷{Ì{½l̲êVÉuÚÙiö]‰}µ­%šß²Z~m•özÖúìïâLÀíýw9øüøïçÐçõ>ËeÄ|“òc‰g{ñ»O &9p7½?Ó»¬k½³õ~T7I�¿ä8×VÎO×á`_]5gÈÜD׿kæŸ_¿”ø�á×.ÆØæl±³¸÷t÷‡̳# _…“d˜{N¼ëm¶=×C•³Ë†ëáÚî±îºªÉ¿Ä®ÿÊä<ïÆ�åÙy[ ñý~.N ÜǦwYYòäl®ÞuNÃw�4ùe_±Ðåkò¤È¿2LØqëÛù™øêÊðwÈiùOÒdEãuçìùZäÁ—àpžAî·3“õ^MðҕóG.M24Ô–Ë>ñ§–Èì¿"l,ËíÃ|¯G]^gÞqì­03.­Ñûl¥#Çž¾·_�ù¿›mµûnŽž”üßZS×ÛZ±#ƒn8<˜úÃ4ØÓE‘�,°O–M ðÉtSC,wRøåŠK+mñÉô¥iu+JÒ´õ£êh÷›®1ºÓòN7¸ÚqîEǶšý惣ØeêwZ=Ö§.ýVãOµÀ›;Y´ÖgcÇ6>D2Y,2Ùmö]K©JÓëñþA¿â{ÍG'⻽¿ä¼e‡¸Ðò?²ÍÓo4›}töeköº�¶ºllýnË*+d†xd²X¤¶—[u+JUþã’Ho¶X¤¾),º—Y$w]eö]OÉu·ÛZ]mÔþmuÖ^AÇ-¸ú>}/²zßHqy-¶YlÛZC³st·èßK©íûÅ–ÖÊÒ´¬–Ûét•Ÿ¿Â·â×À›ˆxÇò³¹—>ËãÐpŸ4°õ”“ *¥[u¿"øî“ëáåY-”÷—êñ¤Š_«ûŒHi~îI†ük~ Ž-Í1øÿLyݲÂáü×݇©Ðù &.¿ƒòy$º˜Ð[Úãâp]½Ò]g¿m�4’RëäÉ·]dU’\£¢æpdÒÌmµÖcäRÛm·3ì·zÒ´·Ö_OKq¤ºŸmkÿ–ÿ/ó¿eY�‘TäâÍN6DVM�‘�%“A<2[Kã–cºèåŠK+JÛuµ­+JúÑ<Î%˸§>âü{›ðNOǹ¯ åº}w"✿‰nµ¼�‹òn?·Å�;S½ãÛý>Nf§u§ÙáMdØùXÓIÑ_KìºëkJ¥®Ùk÷ü¶£? iªÙâcçëvzì¨3µû ¸¬Ÿ73ùq²ñ2`’Ûã’;®²û+JÒµ¥^ñý^…û@#ÔªGkx)t¯÷Pâý5�ýIØ;iør?m€¿ß+™þ§{!ÕÿƒÏÚ•â·ÿOöÏê´ž“ˆÿ²þ…ÿHäº<¹bË6>Nï}§ãzéö»Í†>»ÚÖùò/ô÷]í­ÔŠí¥ÒädIKkíŽ;n¾úý”¥j×Ï'|¬ñßÃ.¢ä=íäÿlñ.�êÞ3ÿ}䜫6û%ØçÓ'3�qm 9œ‡™òÝ”r×Q©ÄÍÙæV;©V•£ö×oõ�DpAÚ=ÃÎ8÷^p/‹÷­×&ä¹ñààcÒûí‹Úû²v[]ŽMöÁ‰…�Ùy™Ù É-öÙ_å<ðâÅ|ùÇ 1Ò•¾Y.¥–[JÖ–ÓÖµû=nº´¥)ùkZúSíi7hwžË–Ó'GÆþ¾§�ÉmaÈ–ïè{=ÄwR´–Ü‹¬¾êb`KJû~�•­Ò[ëõ.­.¬vÖ…ó{øœ{›ä+–xÑâN?'ñûà –4Ú.W›™^²¸¿Ì$Ì¥øzºß7ºëdÊûmŸ&Ïç}#§¥.‚¿/þWu==}¿mµ×äQÑÔx@µëþ�Ï9.�µŠ ÿª¶™Ÿ¹ƒ¬†HíÉž”öÝK¦»êRÈ­­=.–ûiZÒß[©Ñ¯Š¿Ž^ÐùGó/®<[ë¹fÑès¯»˜÷7býÖL¬N¬é~;�¯�šs)!¶£ÊÜIM†>³I‹'Óƒ;}±ÂÇš\x$—&òøçðo˜ùóä—ê-=v:Ž«ú<§¸¹¾°Ù^ט9�E±¿#//¼«�K-¸ˆo†z]—7Ö’+±qòn³íètÒn³­ƒóìÆ�ÒL¹­¥?¡ÅëöYm×R¶Òi«OK}}v¾•¥µt³W¬ÁÓk°µ:Ì{1p5øÑbbcÙëZG 6ÒËi[®­×É}}=n¾êÖëî­nºµ­kUнÑÝ_ãOMuŸ@ô¯ÀáUÔ\;IÁx7×ûï�]¢ÑaÙ‹ù™s]&fÛs±’Ûò¶ù7Ë™±Îšl¬™$žY$ºÉ¾ Áø§Yð®'×|I‡Æø_㺎)Åtûn·O ÐàA­Õëñþ¥×Ë}˜Øxö[ï¾ë¤¾´­×]uÕ­kœ£ŽÈ£²(ì¶Èã²Øã²ÚR–Ùe”¥¶Ym)öRÛm§¥(ýì²õoöò\߇ë¹ÇÎÐl=#¬Öýlϧl’k¶[wÝsb¶µ¶µö]um¾Ú]mdŠë¬õ§»ÕÏŸ“ï�¦ù>ðó²¼Wí:cês7xôäÝOØ”Öãìö�KÜZ<ëxO`jà›Ù,ÐbM�6Û±¦Ùhsó°mž äýk5“ÌzÛÌî€ç]ÙØ–þŠå?yãü†-›mÁù¦ºÙ&ã×EwÔ‚K6<û©Y"¤–Y›‡$ø“Vì|‰l»ñl01öx“aäÛZÅ5¾ž¶×Òøï¶¾è岿¹|wR•§îWòV•¥kG4·ºM‡Ûì4{Xk~·"üy쯯¶ïo¥ÑÍ×RÚÉ�‘ÖÉÞž—Ùu+OÊ¥ÿÉo»wÄnúí?ûãŠåpÎÙéîY›Äy†‹"’ÝÞqì‡/[ºÓåÉ Ûq~S¤ÌÆÚj6Ûô6:¼Ì|¨ktSYukLò+ »Åîêì.‡í]lzÞo×[Ù4û/»ItÚÝ®°Ã°Ñò-.EöE&N�’èó1óðï¾Èå®6EŸRÈä¥Ö[‚vës'ÂÈ¥)$7z{©ZVÙ,º”º9l­=6K+Jÿ6ž¾•ô­+GÉ`Ö~ @á}›Ëx$žÝ6}%×]un›M°¥ùZÉ+_uku�ýHåÄ’ë®õ­ð_×V”÷{©OGUþ8>g¼÷ø¹ÞÒ¾8vµÛ^¬ÍÍ“;’xóÚqìy¯Gò ŒŠß~VtTõ›/.¶Ò× ÓòMŽŸÛwS#•ûq'­ke´ºú_}`¾•÷A}ßoäõ³ÖêÖ¶Ö­µâAðÝý"ÆÝýN-²¾¶ÙZfßõõR_un¥+Ò;,¶ iKi[«‘6Ûî¥)uÞ•ª|ÿ‹“ãÓÊ[tœ+Ê–Û,¿pŸÛOtyw[d^ê[î¿Ù“ëH«e+öSßìºïü¥�1r±s±âËÂÉÇÌÅžËeƒ'hò1æŽúRëdŠh®¾9,º•õ¥iZÒ´J/�víN#¢ì°æüC±ø(Á�gÆy¿äºnaÄy¶o_£°ÑrN=›±ÓmðeôüÙqæ’;¿r®Ùi÷:ŽC«ÀÞh6ºÝæ—k‰~¯q§ÎÅÙêöX9QÛ66nà Yñ312"¾—Y$wÝeöÖ•¥kJ½M+JÒ•¥}i_¶•§ÛJÒ¿’´«û½{éG¨TŽÖðRé_î¡Äÿúk;ú“°vÓðä~Úÿ¾W3ýNöC«ÿŸµ+ÅoþŸíŸÔ?i='ÿd:ÿý þ‘Étyr"Å–l|­Æ÷MÇñk›»Ú`ê±iî¥&ÎÉ‹Ù.¶ßuc†’]K§–´ü–YK®¯îQ¯ÞEù]ãOˆ¼&îÅòw½z»¢øulϮ۲¹Ž›Œ_½ÈÖãÙ•™¬âºÌü¨öü»yd2Y[uú¼|¼Ùk}¶Ù×]m+�û/¶:æx¾W6íÎÃá]eÄ0¯Š<žKÎù6ŸŠécšycÇÇÆ¦Çu™‡�&VNDÖGVÝY%’ûm¶ÚÝu)_ç4ÐãÇtÙÅVz{åšKbŽßuÔ¶ßu÷ÖÛm÷]u)OZýµ«]9’šŒ:M‰Ãu×íòiù¶mvvK‰¬¶¿e~¤X~±l2íü´ô¿îÞ•û~Ú~Xzü‚þ3Αà°ïø'ÇNl;ÕGt¸X=ñÞZÝçêkìšÛ¶ã]_FŸµ9¾V'Ô²Ë6ópÙ±§ößt9ÛXï�?˜‰¡zëmżDáùýíÍiØØ��ËðöÜ7¨õ97ÁŸyÐër¬×óîmvÂ~¦%°i1²`–·Ã±õ¶”»Æm9¾5.�]es§ô­)-Ôº,Hîô’žµ­Ô¤³ÖËí¶¾Ûim·[_²úUª�•ò[›÷þCµÊÙÏOwÒ¶k©f65·RÊ]fVÇ‹‰eÿN•º‘Ùoº´õ¯­~Ô¼¼ó‡Ë<û2NÜòÛ¼y·tó;#ŸOw#ÌÇÄã6<],ÓÏ5ôŽ(bŽÊVù$’K©KiJVµ­^‡ˆñ.SÏù_àœŽîù‡5漇KĸãZÌÍß#å§‘ì±´üŽè4Úèr6}Þïm™6&,ß6DòÛe–ÝuÔ¥}' áüŸ°¹oà|'I�É9‡3ßêx¿ãúÈ©6ÃwÈ7¹Ðk5¼(ëu–Ý“��“dvû«m´­Þµ­)ëWõ‚ rf‹ +$ÓIlqÙOJVëï­-¶žµ­-§Û_ËZÒ”ý×G:«¯ ëþ7$´ŽMæÇéeï2¬¶ß·#Ù_¥ƒ”ºêß‹®¶úÙe}}/¾·ÉJ[ïöÒá���‰.;ñ;áÆ³ˆr<]VÏÊ>ê³GÏ|œæ86aÍHù,yWq¾¨Òì±23lÏáýA…¸ÉÁÇžÉä‡c´ÉØì¢¤1çY��žñÚ[uÔ¥~”tüÛ)éO²ž¾žµ¯®NvýÑ—Ù`>òë/á~£øC§‚ëù&“úýl÷É·ÖYZË&,¶ŸRL¼_[¤Ç¥¾µ¾µº?JÖûklO¿¤ž|ôGü0|sây0|mây÷mxÏÀûÞãÈ‘Õ×+sµá8úü{k›¶ì^|¹[>35$ËÙG6nªØ2²2µ´Åáß͇ƌ~iôͽ·Õ:L;¼—é]6nVŠÌl/ú§Ú}‹÷�žÛ¬fÈÆ·ï9;ŒIå›?ŽÒKf²Ý„“â[H­ØË‘—åmñ>®=–~�ƶµ†êþmÓÅO[¯ÅºïÉöÖ¾¶{¾Ê_özÛK®«BÕd $rC%ñKeñK÷G$r[u’G%—VÛì¾Ë©K¬¾Ë©ZV•§­*Ã¥iZÒ´­+JÖ•¥iéZVŸeiZWí¥iP‡ü}½/$ßñÉ«>‹s±ÕIuÖÝ'ܲæ‚9«gó´È‚Û¾ŽM´õþvKn·ýÄxñåÏ”~$òI¹oŒ~A÷Cïs/Ç»k“Õ¼ÿ’ðümüx’[,œŸU§ØcjyFºÉ-¥k‹±ƒ'ïJzÙVÃt?–~Kxó³iÐ]ÝØ½_ÿT/Úåiøß#ͳ‰n62aS_v_#á·fpîM-0í¶ËHàåRß§Ô¥.ŽÊÛô0¶»ui\,Éà¶—]Ò¶ÿt_}ŸNëïǿݗ{)O¶ëkZzS÷iFdÒùÏuÞÈö–jwñRÿY$ËÃû–mÑÿó¸æÖß�‹e÷ë ¾¿ÍõH�ÇÆòÃÓ°b껂�å^¢›fÎÙöoYÙÁ9庨ìö_©Òò‘ÙõÇÄ’úR•¦VÇ�í§÷SÖêßZÕÙ^‘üIjp+u:Þàá�Iß|;%·g´ÊÓæu·`mî­+ô/ý;Ã练àÖ*×ó½œ^ï}´ýÊúÝ_S‰ÎöQ{-ËÆÆË²Û+K®·Ý�<—~å÷_oÔ†ŸèÒ‘Ó×ýMÖùC¥–¾›Ž+´Â§§óÚÜìM�k_³ý&Uš�m+öÿ¦¯§ú.ÙôÇã~ñÿo‘Cx%Ü]{‰n5”ÉÚôÇkp®ãÈŸ.ÚÅl’A ç‹‹j{ï¶Ë¶S]¥¶Vëýk%:]׉ãÇ –ª{cÆîí᛺Öjd`õÞã‚vfªÊ[=öÁ|;^K·ê\¹+65-¾ûnóéßZÙK¯¥)%ßr}®º;k‘‡›¿oºÈ~„ñÓí¯·Û-òãÝw­¾•¯æSÒ¿gÛù^ÓÈ^µË¶Úägm5u¯å³;S“%Öý·Só«¬ý#oÙKi_²µû+OÝõ¥:=Ö?‹Ç᳟[�w*æÝýÒUžøm–ÎÎè�öÚì:I“‘òdצ6]¹eÖcÅ ³_ôk-ÕŠ[)m.’—ÇfìqÏŸ_Œ=æ>VÏ»9Ožk}dÖò>œí|¬ÜJýI,ödIÄx—)×]w¶Ê_ý yií¾Ÿo»Ým¿Zœ¿�V”­sëOô+‰›ëOú>˜õ£íGÝý[-m¥¼®{úšÍÜ4¥k_oçV]e”²ß_ݻҔ§Ûù/®üNÛKñlÆó³Ms'³ìz'Ê-=–I$ßBÛ²¤ÛtŽxRÿ¶ég¬qÛgçÖêYùÌãÆþ`>59^»a«òç­ñ`ÚMtÑòL^[Ãsb¾ÌÉ0kvÇ[ËøÖ‹c¨†³E[©&\PGt5¤´­bºÛëûbäI¬¶K6˜vÛw¯¥%šÜ{ééum¯º)þœ–}´û=iOZ}¿‘ô?¶ç[êß©ÿàæÿ¤²Gùà Ÿû°žÿÔgb¸–Iÿœ¯ãïü³|nÿ |;þº¿·é�?þ�u¿ú=‹ÿM?¶ç[êß©ÿàæÿ¤Ÿç ü.îÀ:{ÿQ�‰þâOùÊþ>ÿË7Æïð·Ã¿ë©úgOÿ§]oþ�bÿÓOí¹Ößú·êø9¿é'ùà Ÿû°žÿÔgb¸“þr¿�¿òÍñ»ü-ðïúê~™Óÿé×[ÿ£Ø¿ôÓûnu·þ­úŸþoúIþpßÂçþì§¿õØŸî$ÿœ¯ãïü³|nÿ |;þºŸ¦tÿúuÖÿèö/ý4þÛ�mÿ«~§ÿƒ›þ’œ7ð¹ÿ»éïýFv'û‰?ç+øûÿ,ß¿Âßÿ®§é�?þ�u¿ú=‹ÿM?¶ç[êß©ÿàæÿ¤Ÿç ü.îÀ:{ÿQ�‰þâOùÊþ>ÿË7Æïð·Ã¿ë©úgOÿ§]oþ�bÿÓOí¹Ößú·êø9¿é'ùà Ÿû°žÿÔgb¸“þr¿�¿òÍñ»ü-ðïúê~™Óÿé×[ÿ£Ø¿ôÓûnu·þ­úŸþoúIþpßÂçþì§¿õØŸî$ÿœ¯ãïü³|nÿ |;þºŸ¦tÿúuÖÿèö/ý4þÛ�mÿ«~§ÿƒ›þ’œ7ð¹ÿ»éïýFv'û‰?ç+øûÿ,ß¿Âßÿ®§é�?þ�u¿ú=‹ÿMsUM"²Fd.©Úëô��Æö›\¸°uø™wdåMîúPÛ&·2+¶Û®ôºY-§äý×V¾{ש|gùWðß¼û×›k:㩺÷žr}—3æÛ˜vêô;²ç,Þ¿ýµ»/­6Gd¹Åuk�î¥ðI[mºÊÅtšÿϧÆÏÖM�Çy¯hv®TwEmšnÕ�]²È¤’ãGuñMÙõë�E¶Ad÷_¿*ÊÖÈoöÒ뫲~¹–‚(î¾Ì©r.§§¤0âä[%Þµ¥+í®DpEù´¯­}n§ÙO³Ö¿cÃm<¢‚ŸRÍ/–O[kô²v›;!öÝû•“ }¿ô2-r÷»°íõÞ8ø“.EöÝM1îÞíŠ`¾’Ö–I·ë^ Á²o˶H}+[!å�VË«éõ®Žv�â‚ë< `‡¥¼Tç\®aÈûÖË´{A×Òàä[tTĤ>'§ìË6°Ëmo¬•»a‡tu¶Ú[KýÕ­¿'#Ÿâ[ìû¦¿&o_w¿ïÅ‹íüžßgÓ¦_¿×íõõöúz~ï¯ÙŠ·}ùØûš_;,MYtwť²«K¾Ïu¹y—ggÃ%)ù.Ž[+G ü›üTŸ1žGAµÔi»¿‡øÏÅ7ZûµÙÜsÆŽ½ÖðüºGZÙ_½ë{›eö'pqÝŸ¬ùWȰn§º´¥)m})ÊîêüB!Ý©Ø?‘õÏCê¯Ȩ̀·«x6>^ó']=$�;�v>g<ɇ++í­rµ±ë%º[}Öû-¯²ž+›îgõ¤v¶’Vëk4–_gÛíŽK²k4wzR´õ­¶[ZÖŸ¹O±ˆóö; ®Mù›<ìÍŽ\Ÿùs+;&l¼‹ý?'ºiï’K½?Ñ«�=•Ú]›ÜÜÇmØ}ÁØÜïµû}-&ÞóžÊåüƒ�sÔÔõ­%ÛrnQ°Ún¶2Ò·Wó¦žúý®=vOlö—rròÞÜìŽwÚ¢°Sœ‡°yfû˜ncäÒä[…Ç�gì2àÂŽiïºÈl¾Ø¬­Õô¶ž¯-“—•™}$ËÉŸ&úR´¶éå¾ZÛmk[«mžú×Ùo­%=(üoÇïÎÜOzËî±EÏw˜÷Û•=—ÛÇ1'³Ûôqe·Ù&âû.§¾²eGulÇü”úUºÿÎ÷Ùu¶~¿…Jñ­f£åWÉÞ!“!ß`fâøcÁù'Ýé©ã{,yuû¿#6¼ˆþõ~'Á–]w¬ßN+uRåímŠ{stù˜ó9øøË¯Yq�wœ�Ý¡ŠÞÀçZIlè.5´Â¥Ù\+�î±n‡/±ç·*/~'%çÚÉîƒ]X©mØÚ ¯¾²_úJè±ò·Ð}Ò*m2ã·ïYzâÙu=nÇǾßü¹__²É²-¯î}¶ÙözþuÔ¦Ö' “ûÝ4£¿:¿ô6dÜÛGߢ¶Y»¼Xì¥lÖìgº”¦l~ÚzÙ…±šïÎõû#Ȼҕô’Ûm­'ñTü SÆNÅÝ|�ø»Äó,ñ븹M&ò;„ép­›UÒÇÊ3ìŽ.w­‹:M©ëáÝåE²knÆÓò¹î‚9ìÇÛk01!³óåñu“Á¹÷Ξ†ãñ]Áy^Âܯ!xvŸ ‘]Äy~Ë"Øÿ¶¶»*C'æ9²Ò›ßͶ\MĔ̺³G�=p±¯1ã×Rë÷1[쯭Û¬§¥m»×ÿ:í²ŸeÖÝëýÓí¥>´¯­Õ¦³!z‹k€3gKõ•Üçs]–Ò))Æ4³Åvekmm·i™Ol±jc¾´ô¬u·ÒìšÛùÖÅZ[ùµ’Û©$oÕð«²ùDò:½³Üú-œøëÉu½§™|Y88�ÏÏqìÆÞi:E¶öÇõ1³q/ÇÍåÒáß\½vƒ"-“+m¯Ë³ºÿ ßyev¼}ãÛºXîñ—§y%û nÓéñ»ƒžáRŽ‚â®WÔÛt9\Šûë}%‚H0mŽï¾M6'³â\~»韗µÀÇ¿ól’ž´ÊžßJÒÏe~Ë Š¿mþ¿eÕô·Ò´÷zt Ûm²Ûl²Ûl²Ëim–[J[m¶ÛOKm¶ÚzRÛm¥=)J~E´š½^³G¬×itºìF›Qƒ‰«Ôêux˜úýf¯Y¯Ç�]®ÀÄŽ,\,,X¬Šb²ØãŽÚ[m)JR‰ïGpÇ0ÇdQEe±ÅvÛdqÇe´¶Èã²ÚRÛ,²ÚR”¥)JR”e×ú~çûŸ/;# 2òq2à—'ki|SÁ5—G,RY_²ë$²êÒ´þe^+²:ç‚÷_ó^ªìþ-§çuؼ_wÂùÇä¶æé9/äzùõ[½.ÏêÓêâgàdßÞ•¶û}}m­·R•§Çä<{EËt¾+Ê4úÞCƹ.§c¡äÎFëK·Ä—iªÚ`eY.6n¿a…=ñM–Ýd‘ß[kJÒ¯ùu¶ßmÖ_m·Yuµ¶ën¥.¶ën§¥ÖÝm}i[kJý´s¯µºÛ/¯w•²/~GÙß4Ú\ÊúÖû,¶êVMv]}=)™‡KéOwä–ÊÒúzV·Ye?_:Ü÷âGÊ\ž;ª®Û–ø«Ü[®Mã_fçG|¹•ÒcäE.ߪ¹ÎTqY‹NÆëzçCóÇí‡s­“e˜÷äd``סòáñ¥¿ðº®Úðü –ÏÆžÑÙgæõG$’ü½�ð[6”»3?«9>Ê«5›Í5ºýtÙ_~ÛWe&¤’dA�lc“è+§É¤¸öß]~MÕúWÝГÓÝv5÷×Ö¾´§­l­ÕõºÚ~ZÖÛªÅn ¹òàiÀøFÓŸoàÒë«H"¥¿xÙl$²ëá×`Ùu¶É=öR¶ÖY®­Ô¶(ýmú’V”­m·Ýu½ø½øÙîï”ß+øwŒý;íÐk&¶œ›·;Wa¬ÈÚqΞêÍvf,‡šíp Ÿ »}¥k“›ULœZí¶ù8ø÷dbÁtù˜ûÑñõà—fùûßZžªáVM¦áÚzáo»o±¦Æ’MWá|¶œŠ_ô¯ƒ7–ní²L}.¶µ¥Ù¹VÝ}õ�/"µ£ÓMºÌ¶ =ÖcÇí¿.zSìŠ*Ýéí¶êÛu¿Z_JÒÊWòúV¿’•t—C£×q­>�SÐÀ×cÛ~mo¿ÓÖé'šëm²’ddKu×Éw¥=×ÝZþêå�|Sé TZþGǶÙ“‰5ÑÉÔ³Ù-’C|‘ßù³1 ÎÆ›"ß|3Ù[/§ÙëOÉ[o¶µ¥iK㺔ºÚþåÔ¥\Ûæ\KiÂwùº¥•ú�]õ12©el‡cƒ%×}Û;Ö·Ò±Ímµ¥iK®¬rRë+_uµS[òàOz|lùMØ^,÷Þš|m÷ˮφs0¦Æã±Ö›<¼Èøhp¬‹åɇ/�ò\\9-’+f–mfË+]•ìÍÃÉŠ:Þüßð»µ|ï~EÒ½› ÛXn“kÀ9î&ø:Èá3Ï%š®O©ŠY²é…•u¶ý†מývu’AYe²Øç—n4ù:lºãOùñÝëv>E¶Ö‘äEëüõ)Z×Ù%¾¾—ÙëZÛ_Ý­+J×Ë4‘§ï’úš]6Ë�íptÚœkòö íƒ)ùn¯­o’K¿�Šc¥o’û½-²ËkukJR¬Óã¯�Åå‡wu¿ŽÝÂ6½‰Û½¯Éqx¿ ⺋-ú™Y“Ù.NnÇe›5Ñái8ßÕcO°Úìòä‡W­ÅŸ/&X±á’Ks?��Ú>Pvï é�ãÒò.sͶqààC_«³U…g¤›>EÈsã‡#ôWÐáRüœÜšÙwÓ†Êûm¾JÙeß·_�“³Ê�Ï|²Wí­}iVSÓß,·R•öÇe+ö×Òµ¯ä¥+ZÒ•é^pW_h£Ö`ÒÙó²>œÛ�¥löͱ̶ÚÒ•ûkZʼn�î­°Åëéeµ­kë}×ÝuÆ¿}/ñ+âÖ·¥ø 0¹_ls*ê¹O‘Ó~xû®Òì l)aŠ i$ŽÜÝw]pªgdâq½M×{0±çŸ&J_Ÿ�Ÿ‘‘cç�~u—�]¡éÎô÷{é©ç³;#]¿sؼÞhin~ë.äÈ¿M…ë\mN¾³OL ,²égžéò&Î:�dœ(°àüïoçM7¶–Ý<×z{应­}=}=)OZûm¥)ë_G¼uiº¯¤Ç½‘×ÚîÁÐɯȤxûLZI>—g[?.¶ÿå¹/¶Û¤®Um¶Ù¬§¯­)K©Ou–Öœ}ùžøŒê_—³zÏ{ú‡÷÷]Źä¾7w.^ô~Í2ðm³#‹ò\ì<íÄýWÏdÃÅÇßaÁd×Û\|lø¡—+Úè‡ÈG�=[òÑ›²æÑãè¹ÎŽÌíÇQv|0änºÿ—Kt¥ÞêÙõóø‡ »7zÊ_e™¸ÖY}—E—�‰“ÊÜj1·8—cOù—ÓÖì|‹m¥ÒcËû—[Jú{¬»ÓÒû}iî·÷i_JÓœ{�>ÇA³ÍÓí±dÃØ`Mt8òÓÒ¶ÝOK­¾Êÿ;$3Gu/Žû}m’Ë©uµ­+J©ëò  ;ÅŽèìoûïƒnzß·z§‘äñ~mÃ÷°R<½~|1C—‡›‡‘Òaî8þÿS—�±Õl±$›k¬ËÇÌÄ–liâ–úå{ë¢{3ÆžÚæ�'ÛÜz~5Ï8.ÚMf×úI~l¥³kwºLÉ"†›>=¾×É^U¶ÒÙñ¥²ïKkZÛL!Ÿƒ‘­Ê—*Ïl±]éëo­l’Êÿ9,WV”­ÑßO¶•ô¥r´¥}hù¬:ÄÆþØøùybbÁ.NVLÑãããÁòÏ<ó_H↣¥×É,—ÝJ[m)ZÖµô£ïñ>)Ê9ç)ã\„qÍï0æœÏ§âœC‰q}Vvû’òžQȶ8Ú�ãÚ=\[=ÎówµÌ‹)'ÈÈ–Èã¶ëî¥+éxoå]‡Ë8çàú §)æ<»s¯ãÜgŽiqdÍÚî·{\˜ðõúì X©[åÈÊÉ–ÛiO²”õõ­iJV¯ëdÍ<Ý,Ó_K#ŽßËu×WÒ”û})J6µû)O¶®ƒtÿXAÀµÍØEœ§i •ØÏù’þ�‚êÒK58²ÛJÒ–GZ[Yî¶µ¶Ymü·[e•¥³?‡—àë�|Xte;_¹ôú]ÇœÝáÆp)Ú;ˆäÖîàéŽ!“4[|N�á{Ü[&‚ë±ç�~W›ƒ<¸{mÖ,qÃ6N¿"K ~'>1¸�ÇÿQÙ¹äøÚÎEäÇdj±&í.ge˜Ùqñœ)kl=[³iÔÇâúlˆ£¿>{/­Û�œ_x’¿B,|\×Çô0éq½+[eÍš”®Lô§ÙOÉZA kJ]H#­?-~Ûîûkéö[ndIÖç aÞÝêìnªûÞqAÊu�Ýú7&µ²+s¡¥ßRý^l•·ó¢¾¾êÃuÕ¥!–ê×֖ݬr?_Á�ùUéZö�Nê´‹a5Ù0zÁ>~.ožäwxôô­°æÁmßvž¾¾Úúýµ‚zR•­ÐßZ~ZR·Y_¶ž¿�mÜùÊÅÉÁÊÈÂ̆Ll¼IåÆÉÇ–ÚÙ,_tSE%µûm¾9-­+OæÑS/5á|·­ù�+ëÞ}Ç7?œð^I»áüˉò �^ûŒòž7²ÉÓïô­nU‘äàmtû\9qò!’Ú_±ÝmiëE{üÿ�s­æÜ«®; �ì8§7áÝ—åa~%µpñû�¦qu\Ê^ §šÌ[/³ ]¬î®=… ·ÃÂù6Êë"¦*×ÇmÐí'’�YuõÂÌ­¸ÒE•�å¹/·s &ƒÛ|Ö‘ÝZR–äYù~„·zzÓÒ¿ÎV¿eµ­r¾´Ðù¡—Y`ž) ž /†hf²èå†Xî­’E,wÒÛã’;í­.¶´¥iZzUVÞãO·ã»}¯ä­–‹}¢Ùgi÷zMÆN³o§Ûë2eÂÙj¶ºÜØ Ì×ìµù�_ ðMe’Å-—[u´º•¢Û½&ëŒî¶üo’j6œ‘qý¦ÃI¾Ðîõùz�Ö“u©Ë—i¨ÛêóâÇÎÖí5¹ØòC‘�4vK ¶]eöÒêV”ÃWÙ|wß–]‘Ýu—Ù}µ¶û/¶µ¶ë/¶êRën¶êzV•ûiWó|çË�åôŸM[®·™òÌj]±º–dèôùýšÛkét;,ø¯§Û±­=.†:ÓýOOKîþ‹éH¬Jü3Ÿ‡j�g]ü’ùÛÃqæì|¼Mw3ñ[ÇÞQ¬²{zÖšEŸÇ{Ó³5YñV?í—,³/‹j%²¿Á«/�g‘Jn¾éši_ ¿Rt®7óÊ>=m;so¬ƒiÓýO¾ÖÙõz£]°²,Œ^mÌpvÿxÄí,ÜZÓîX¶Ëøö<—W"•ÙIôµ¹cŠq¯¸ÛfË:Ïõd‘ÿ@‚û~ÜK/¥+î¾—SÝnMÖý•§Ù[)ZÒ¿mké´©×$Ü÷ v‹”cdrŽ9�ìäØÑÒüÌ8,¶–ï Ž”¥2•·þ©Á?¡ÝOZÍm¿N´­}•¶‰ðòéüÇâÜËÎ� xMpüÀâÚ»·=§Õ\S_ ±y;ǵqGLÝž«WÐú÷†‹UÒc]·ÏÉà‚˜UŽ\úâ]w~cþ tž_qíÏ‘^?ipô¾Sñ�U’î´˜vc`ë{÷C©Æ²tÛ‹®º lNÉÔk ¤z}­õ¥3!ŠÍnmk 0²uÞC“q›6¶]™‡m±ì£·í§Ùm™¶[OJG%kém³Ûm=,¾¿g§æÝözV;ˣºë/¶ë/²êÙ}—Ò¶ÝeÖÖ´ºÛ­­)[n¶´ô­+öҪȲ±rprrp³q§ÃÌÞl\¼L¨dÇÉÅÉÇ’è§ÆÉ‚[l– à–ÊÛ}—R—[u+JÒ•¢ ›]VÓC´Ùh÷šÜý6ëMŸ™ªÜiö¸yí¦«i®È“a­Ùkó#‡/?.âšl¶H¤¶¶ÝJ]Jш/²øï¾9,º9#ºë/²ûkmö_mkmÖ_mÔ¥ÖÝmÔô­+öÒ¯òþÀÿ Áén–û·Ý9‡0ÄÿTþfN“I“gþs~KâÙl¢¾ŸùÓù.†©ýì¾ú{ý)e�?†ëðÝ}ËøòòÀ¿ÕŸõ7˜xÑãG0Öÿçþ[ÏÐ÷qhsãÿÎßü·“ǸöLÐ3;:Ï©ô Žcÿ ß ßÚóø'åÿ—üOÿ¶ú‹‘tŸIò,/î{üæ^§±»S—g÷Cþr}F¢{?øžüܬ«K}(µ9G‹qo»}=žÎ?õOÙ~&%ôÿÎoݶyí¯ÿ”þí¶×ÿ-þZþ¥,ÚôûÒ™{ð¹vÿJCÉé•ɸ¼vcò*YY³uÖÒÈñ·•³íºK+ZÛn>ÒëÓWó&­)K½·V·¡³ø…ÿ ~‹Ì;yïœ h0¸Ï–aerNØé=dzþ/ä¦N-.ÈÙr~9m+��ÆûÏ7—]‘w¯ÜyLö[tôƒe,ù¹‘úù|øjÑyw­ßy㦻]Æü¢Õk©•¼ãv݇ªã½ó…­ƒÛn»e“5Øøz^Ë‹+bÖí¦¾Ìlß§fÂë#º,ì!ɸ͛[.ÌöØöQÛöÓì¶ÌÛ-§¥#’µô¶Ùí¶ž–__³Óónû=+f�M ¸òËñIðI|3C5—G,2Çul’)c¾–ß‘ßmiuµ¥+JÓҪϷ}¿Ûíxÿ Õl´[íË;O»Òn0ru›}>ßY“.ËUµÖæÅf¿e¯Ì‚øg‚k,–)lºÛ­¥Ô­`Ýé7\gu·ã|“Q´ãü‹�í6Mö‡w¯ËÔî´›­N\¸MFßWŸ>v·i­ÎÇ’Œy£²Xe²ë/¶—R´¦ ¾Ëã¾øä²èäŽë¬¾Ëí­·Ù}µ­·Y}·R—[u·SÒ´¯ÛJ¿›ç>[ü€7¥º[îßtæÃýSù™:M&MŸùÍù/‹e²ŠúçOäºn§ô?²ûéïô¥–þ¯Ãu÷/àÈWÈWÿVÔÞaãG�Ã[ÿœùo?CÜ]Å¡Ï�ÿ;òÞOãÙ1ÿAüÌìë>§Ð‚9�ü/|/kÏàŸ—þ_ñ?þØ_ê.EÒ}'Ȱ¿¹ïó™zžÆìmN]ŸÝùÉõ‰ìÿâ{ór²­ý-ô¢Ôå-žíôö{8ÿÕ?eø˜—Óÿ9¿vÙç¶¿þSû¶Û_ü·ùkùþ”³kÓïJeïÀ·ºk˜E‘È8ìQâò¨£¥ÒÃëd8Ûèâ·ÓèÏ[½¶C²¥”¥"šµ¥·zRÉ>Ïmñćñ~>)çÆ£–ù}á–‡QüÞÖaSiËø49í ò�[‹[%ÃØÏ—v«ŒwM˜�Ùn³w4°`mk0ö·YI"ÙàpwåÓáÛŠù¡¥Ýw·Dëu\SË 6²ò¢ú¸úž=Þš½>'ÐÅ㼦io‡_­çxØ1Y£{-l¶Hâ‹cÝ-ÆÉÖù>GÆaÛY~V5¶Å²¶Úz]üí™VÙOKc›ý-$¥¿e·þ_JR•û)OMÈÇÈÃÈŸ. q²±¥’ Œyã¾)àž+ë°Í”¶øåŽûkK­­)ZVž•V Ëx—*à<«’p^uÆ÷Ü7šðÝöß‹rî#ÊuúMÅù6ƒ>}V÷�rÖ ]ž›w¦ÙâË�•‹‘sãÏÖ_m·[ZR <×…r޸弋�óÎ;·â\ωmó4\—�opæ×íô»}|×A—ƒ�‰=¶É±IoÙ_¶Û­­.¶µ¶´­qF<سI�‘ÑMÕ²Hï§¥ÖÝOþEiZ}´­=iZWÖŸcø¼óË¿ˆJzý”ûk_²”§î¿í¶Ý}ÖÙe·]}×RÛm¶•ºë®º¾–Ûm´õ­n­köQúpðòö9xºý~.Nv~vLxXXpK“—™—“-°ãbâãCmóddäM}¶Ye–Öëî­)JVµ«mºû­²Ën¾ûYm+u×]u}-¶Ûië[®ºµô¥)ù[•Ó}%n¾Ü^YÌðýÛý<�>�"Úúk½+I"ÏÙÅ_²í…~ÊÇÔôǧÛ}>¯¥±X�øw? Ì]{gó«ä«®-“±n»OÊüzñ_˜czÇ×T²ü]¾—´;ËAuþÉ» Olwêø–uµ��ÙZË·‚íµlÃÓÌßáßá7§ëÆü¦ó‰ãfvô8›î£é½½aÍÀêÉ ¾“ë9Ç8ĆY°6}�[élúÝ|ŸV=éfD´®Ú‘۪ʜkŠ[…ìÏÙÇmÙ–ÝKññëZ_f-iüì—ûk[$Ÿ×í§å¥•ô­?;Ò´ÚdíÒx{ íNžÖó¸$Úki³”Ã~ž],¶Ì}µ#Ž–Å�´öSÝ[í¶ÊYÿmñÛéJÒëim)ÿžÃËÔ¿(|koß½ê/:øÖŠúaòË0±5œKÈ|=>¶]' îY1a²Zr<=|8^Uý/‘áæ[—ƒ¸ùGø˜ë>ø~g1âqé:çÊ9¬–¼G±©…f6¯›Û‰�˜|+µnÀÅ“;g£šl¶Xe¾;©upÞfNEøÙq_ ¶Wò]O²ë}kJIß’øî­+éu>ʾs±sò€?¶><ùsË‹ ¹992Ç><ß,óÍ-Ô²(aŠ:]|’É}Ô¥¶Ò•­k_J>ßã\�™ò-áú ß+圣q­ãÜg‹ñ­Vv÷‘r-þã2~£G¢ÒêàÊÙm÷\üˆàÆÆÇŠI§šûl²Û®º”¯¡â|G”óÞK¤á¼#Žo9w-ä›5<Œñ½^në{ºÙå]ìÇÀÕêµÐäfçeKwä²;.»Ò•¯ä£úà ÙÙß4ÒWÛdqÛ[ﺾ•­}-¥+_²”õ¯ó(ÝÞ¤é ~3÷nEË!‡3�Җ͇®º‘daé/¥Ô¾9«u+$y[Hý´­/§æCwóžë©Kée¿áüü3|sÃÛ¸g™ž}ñ�4òº bò­èý•š^OÁünÙC•f£–î§Ç—i¦æ=߬ºçÁɆI5¼_"¿W¹±³ðç!ñ%ð¹ÆWÃw¶AwÓžËcÉÃÉŽlø168¹xpWéå·‡]ïáGjl:§½8”Ú]…²fÏŹFdÍá�� ÆÉúr^½úQE²×dY|wÉ –Ã�ƒt¶Å—<þ±Ó í59ºŒ�¡—iK«uašß¶ì¶¾•º;¿›OZzÛ_K­õ§­>Ú<ãQ¸ù€¥§Óì÷û]NŸ lý†d”‹ }nºµü·ßuÕ¶8aŽßξKëm‘ÛJÝuiJV¬ÅÐ>÷G”Ý¿ÁºǾ¹ä�­Û½‘¹‡EÃøOÆŠlý†\”ºLŒÌÜ̹±5:?§ÃŽL½–×c‘‰¬Õ`C.^fDÐË-™w£z·<“ì�Rt�Üö>äR]L.¢8­³+ã³/o¹ÙæK�ªÐhuÿZÚäçfÍ$ºžù)ëO_Ùƒ�—±È·¦–êVêÒž”¶Ë)ZRé$¾¾–Ç+ZSÖµüµ¥?-iFûuwNêxQm3þ–Ó•_i&w§»[õ-ºÙ`ÔÙ}–ßenŽÿeóÝO©%=iJYm×Y[P~ ¿ßJ|[éô½ñÜ·h{¯Î�¾†ü|þyl?à}ã3yÆ:7c¯Ãγg±ÕçݯÚòŒ¸¬ÙgâÒL|HõøYY˜ÙsàøÂø’êo¸Æ'1äuÔöo“›Ýmcå}›&¿SÄ"ÌŽK2¸�UcgAff£G<ßC/e-¶lw7Ré$¦6=ñàcæ=ÆÒÃ[«íŸ:ZFÊ­¾žÛ-!‚•õ¬pÓ÷kùo¯Û_²–ÛnhIU×·¡yn[ô<ÛU&§}‡lñÖ—×*?m™Úùî¶”¦NG¶ë¡–ÚÛJÖ•¥c’”ößmÖúÑ¢!*üšôfÇ£<¢àpï°á‡i“×ý‡¥û®³³ºƒ”lqcǧ.ë~U&.\š�…²bãß“‡ž Á‰e}o­.šy+K¦žëiZ[[î¥)J[e+_m´¥-§­kéëZÖ¹Ööò¾ ð쵺ýÆN·i‡�Ÿ�—bÈÅÉŽÙa–ÊÖ•§­·SìºË©K­ºž—[u)ZV•¥*ÅýÓÒ�Kä_Ws>”ïN½âݧÕ=ƒ¨“GÌ8/2ÕÁ·Ñn°.’<ˆ.’ ©õ16:Üè"ÊÁÌǾ,Ì Ø"ÉÆ–)âŽK|Ï2áœG±8¶÷ƒóÎ5£æ<;“ër5‹Œr]fçEºÖeÙìÈÁÙk3âŸ/[Òßm}+JVž•¥*ÿ7Ùd–_–[$r[u—Ù}´ºË캕¶ë/¶êVÛ­ºÚúV•û+F“v�El8ÇÞ÷ÜVÙ¶|vÊÖið?>mžž*ÓÖK«OJÝŸ¯†¿oÔ§ôX¬¯ôJVÛn–µœüá~þßðf¼ãÊ qy7zxy…÷ŽIÊ8%,ÉäÓã¶¢”º}´ûŒ|Ljäö?Sh>Ù©¾Æ²»=>»Ý]¼A…‘ºÈ…ÇÊÀß3èkw�ã᮳‘vWMÛ“•°äýGer·ý�Õ˜ÒüŠæñëïºm¯aðm}Ô¬WåíîºÇ|ôΆܼè1~ÿ†É�þªÓÙ$ðÖµú¸t­dšËZ]kZß<¹íûd¥}?ž¥kí×dGQ´xgë.›ÜóËâÙæÖMG¶ol™÷YO½liþ“A©ŠúV—zV•²¹Ò±Yw­)I.¶ë)"…¯Ã½ä¯Êž×IÛÜâýÏ�þasÜû-]´å]§f—7îÛÎ3К ¤G“LȤÀÈä¹Qßǵ9qä[OÒ9¸Sêîí÷ÆÂ÷ly»“�Ú�·ü éß —lnI& ˜œÓ¶iî²Y°ºËi‘A ³¾“r<˜&À¤·Ò,8ó¥�.˜žÃ�ñIöu·'6’âà{mºÊú{fË÷SÖߣK©_d>ß¶²Vž•õ¥-õõ­mÞî?ÇtÜ[Y£E�¿·éÅJÖù¥­)Kò2fº·K“‘%-§ºûë[«JRž¾”¥)i¯ˆžøãà§Iq¿ü\ëWõ§­ùwájã»#uÊ9LØû>cÎ9&]eÝsc·�+r69óM=a†(,­˜ðA s¤éN�ê�zߎu'Kp�/ëþ+‰f.«C¥†úRù=¶Ó'gµÏÉ’}ž÷}²’ß«™°Íš|ÜÉ«Y&–ûë[™g 8"ÆÆŠØ`†ßdqÙëém=kZÖµ­ku×]uk[®­k[«ZÖµ­jûmœeg÷kwgô. ®Nó‡Û¯wu+.F«ó Õí$§­nºJRÍvt´ýÚ@’êSÝK+[ä¬3>l¿ �XyK.òoã›]ÄúGÈÉñ²·|¿ÇÛ®Ãã#Ý»Xk~NFg¾Û"Õtßfna­Ñ×ÛH¸®Û2È.ʳU4Ùûi£ÕòqðSÖÞPßÈ»«ÅØø÷Pù °Î—yÉ8æGÔÖuolædV·ír¶¸8Ùáça%~ñúO °¶9Sïøÿ_*M”>?}ı¶~ìœ/§‰�uõ¾JÖ—S&·=Ym²—V9}ßo¾ÚVµ¯¯º•­}i¥û=^ÇKŸ“¬Úáä`l0äúy8™1ÝÑ]éK­­m¯Ùu’Yu.²êzÛ}µ¥ÖÖ´­*®¸:sµ|ì®]Ó�Û×ܳ«{K�í.Órþ Í´ÙšG¢Ï¤QdÃn^¿68ä»; ",œLˆýøù˜“G<É –_t);o§ûC¡ùöÿ«»‹ƒr.»çÜc2\=ÇäØ`æGXæ–³°füü-Æ“aH«&Ã\Œ ìzÛ6<ÒÅ}·×ebdàÍv>\cÍekëd–úzÒ—]o¾Ë¿�’;«m}.¶µ¶ïÜ­_��˜Ýù€Ö'ÊžlXeÉÉÈ–81ñàŽù§žin¥‘C QÒé%–Kî¥-¶ÚVµ­}(ûDò[vÝuÔ¥~ßã<“™ò ?áüyÊùO!Øcjtk�js÷¼ƒyµÍ’�áë4úm^>VÇg°ËšêY0Ç|’]_KiZ¿¤QK4–Å rK-õô²8¬ºI/¯åô¶Ëi[®¯¥?q¶ýaãõ±ýÛ}Ïa¥ÒRêO‰Æ}mº;iO¶)7WÙ[­’µ¯ç}ÚÊû~ÊRZ×Öø©>¿„ÂgdÀŸ*~U´Ë=²cònáMr­“ÊYí›G¶òWg�5Öd]uþ™ŸÀœ«¶˜ño2/÷lt6Ë÷ã/ðþixwè~ìóÓQ©å\ªÉuû^%ã´Ypm¸�¬>Ì»3;s3ùuÜËm\š[oè,y&ÒÇwS2]…2.ÆÅÉÚ?¦VÞØçš´¶±áúûà†¿eÕ¬õ§ædIOÉíûc§Ûü÷­+M±Ž8áŽ8¢ŽÈ¢ŠËcŠ(í¶ÈãŽËim‘Çe´¥¶Ye´¥)JSÒ”OƒI¤Òñ�.£�ñ½F¯�ñÞ?«×é4:&¿S¥Òiu8‘`jõ�^Xø:Ý^·8qñáŽÈ¡ŠËl²Ú[JR’›‚q¡‡±ññ⎠‚;"†b²‘Å 1GKlŽ(ì¶–Ûm´¥)JzQïŸíõÔx>s×\oŸ`×o�ô³¢³Ós‹lvlp®¥ku¶RZÛZdbÝuÕ÷í•õ­ií¿Òêr—åáËß•ŽwÇ·Œvî‡U~WyÁ°µØ=³×³C&V^¶mŒÐV.m×÷çfÍvgÚV\)-Èš\K°së|:_æ‡�9yÝÀãáÝáľ®çQ]xOeqÚãjû�æeÅ[%—A¾»"“ë2/öß“¬Í�+Y•|qß$–(d�çlõ8;h~Žl^ÿo»éKm}³Auô¥+tRzWÒµô¥kJúÛuiOZWÑ¢\û¬9'_åWôŒ?|ÔM5ñàï1,º¸™mky6zßveÑý¿JúÖ•­.ö]%-­Ê·>U>üËø�ç7AܼcøwÐüƒ�eéúÇÉŽ�™“Öܾ¾Ùrõš~KÕÉÌë.ÁÊÕÇ|—è¶×Ò³_�—]nNÏL´þA>*|�ðyvÇ“àßÙ}#°ŸéqÞòâ:lØxçÔ“*Ì\}G:ÕV}”ýyɲ/ž/¥�—‘>eeöáfe_E°â-×ÎÓ]Y+Jåaz[ZfEm¶ÊÖ´³Ù‘gºúÁw¾´¥+ZÖÛ½ié__ZS9æ;΀=‡à¼��g×A…Ym‹Û\ÌüŠÝ»ËëémÙy>Ëý·]é_lvRùo¥+[m­)_Mþø÷øÈóäë¶.êŸúÎ~C¦ü ùÿhr[ót?Õ:Ý”’Ù‡³ìNsf¿a²üË1§“[‰vïe4õÁÂÉúÒÍÚð§ã÷É?>+üqøÿãñgq-öÃîÍž²>[Þ³[‰g"Ê­ðYMޝ…k)~dIJò½×ýËyòç³éÛ›™™XbºÌ¹¦Ðà颥 ·êdÝe-Ÿ.úSêËöÒë­¶žµ¤0ÖêR´²•ýÊzÖêÓÕ•]çtÉöÀÃ+;l<Üx2ñ2c¾Œl˜¬Ÿx¯§¶ø¦†KnŽHï¥}+JÒ´«ÈóÞÁ{S†r^¹ìÞÅ{¯ù–§+C˸?7ãú®UĹF“:ϧ™¨äwy‹�¨Ükr¬û$ƒ"#»÷hùý‹•èöüc”iu<“�ò nn›}Ç÷Úì=¾“w¨Ùcɉ°Õmµ[r0v:ìüYnŠh&Žø¥ŽêÛuµ¥kGüºÛo¶ë/¶Û¬ºÚÛu·R—[u·SÒën¶¾´­µ¥~Ú53±|vº•ŸoÀ>Û}-¾^5‘7çR¿m$¿WŸ“/çR´ô¯ÐšïZz]í’¾¶ÇH |Àþ�–²^KßÿU»g§¶ v|‹Ã>aÈ¥—w…%“_nÃ'¡»•l/¦ëî²G?ðo’æ[™e`É»m—$Øz˜¢»ò-øxp7Óï»{À«°t{YïÈÚn¼qßìíÃÐfÍt2K‘^§å{YþŽƒ#+*ÊVÍ.ÞkuÖÝ=ÿw΂(pëà7œ*9ë&V£Ù µ­÷ß…uilWÓÝé�wä‚ë®õ¥-¯ô?¶ž•²”j†N6NDØ™˜óbåcI|9Ù1_ðMkmñM –Û$rYu=+JÒ•¥PCåü;—uï*ä\ŸqnGÁù¿Üì8ï,áÜ¿I³ã\«‹ò FL˜[]"ãÛ¬\-¾—s¬Ì†ør1raŠxe¶¶ßm·R´DŸ™ð®a×<£uÂ9ÿä<'™q¼ËµüƒŠò½>ÃAÈt¹ÖÇd¿uÙéö˜ø¹øSVl¾–ɵºËíºž¶Ö•®4ž ±¥¾ ˆ¤†hëJ_¶ÖËíõ¥.§­·R•ôºÚÒ´¯îÒ¾´œy‡òÛmºë©m´­×]Z[m¶Òµºë«_JR”§ÛZÖ¯ëåO642ädäKøðG|ÓÏ<×Ò8¡†(ét’Ë,—RÛm¶•­Õ¯¥>×íÖëv;�Ž¿O§×æí¶ûlÜ]n¯W­ÅŸ;c²ØçO.¿_ƒ‹¹9™¹™2ÛQGmÒI%Ô¶ÚVµ¥êË/’û#ŽË¤’K­²Ë,¶·_}÷V–Ûe–ÛJÝu×]_JRŸmjÙN¹ñói¹¬niIôÚ¿u²G§¥>žß>?m.§Þ+ZÖº¨.º´¥iu+‘ZRê{cõ¶ôÈ> ? ‡}y5/ï_‘hùŒ] tñìµ]n7è_#ûC(ì’9ÓoíÅrò¤¥’þ’Æ—“äG�4VkõöÏ‹´I»ã§ðösnÉýÛžrSoÖÜZ`mu­È¿_Ù<«ë©•l}…³‚êM×:œ˜)e’ëà­w÷[,–Ivªx­­ùI®’–dî=ÑÙZRë0lº¶ËwÛö}æK~Øí­´þvÚû¾ß¶¶Ö•£ru}^ƒ_�ªÓáA¯×âYHàÅÇ·Ûe´ü·_}Õ­ÒK4—~uò_[¯¾êÖë«ZÖµXÍã¿�ýâgQñ.‡ñëø¯Põ/¦áœG ìl(n¾–×3k´ÎÊ—+qÈù&Þk~¾Ãm²ÈËÙì²nº|©æšû¯¬Àú׬z÷¦øGën«á¼{€ð>-‰\-âúÜ}VŸ[òÉ“‘tXØö[Ir³ræ’|™ä­óääI|²ß|—ÝurLQEvCqÔô²8¬¶Ë,§¯¯¥¶ÛJ[O¶¯¤Í¯tþ€ {κˋóìzÓk‹\m¤qû17xT²-„¶µºÈ亶ÖÌÌZ]ZúÅ-.¥)u}•²êû©È”O„þUø¶LÝÓ«Á;ã]§®·„y3Ö˜šÍGjè>ëe×ê5\žiqo×ögÃÉû.Óîl›ècÍ‘MvF·&zå[£>iüvøÃç·QÝ\.Øy~·îüS¶x•qt�—Å)e2¾Ž6&þ¸™1î4VK›-÷j¶pæën’ÿ«HmžØæ³ælôøx©d>ë­õúSÙ_dðÖ´­=l’”¯­¿�ëíº—YZÒ•­+éF‘sî¥å< ûò2¡¦ÏGïöüÀ²úãÛmÒVȬØC_tšì‹é[~Ë«tUºê[d—×ÕY·ÊŸÀ¿œ[}�%ì>3pøÓ.ÒüN5äçWkö|*Ø2¶uÁÒkû;C/Þ7}CËsãŸ•ÆØÖ}Dù™?vÖí6WÅ-ÖÂ?χŸ)¼“cËrµ¿Û›¡ñ÷7ÔäÙ�§Šo½{lì^÷­žç€Ë1=oʾ\Ý'¬ðEnÆü‰+¸£qŶŸt¶Ò¹¸t¥+\˜l­.ŽžÚÝug‚—_tVÛí¯çzÝg§§­iZú1s‰ÎN¼ÈÜð®ºåó*°é0½0â¾¶emó=ðjñ.¥´º¶I‘HäºYý.§ô(­’OK©ZÛK}kN˜üqüGy½ò‘ίã>0u|²p�>˜ó½ùåù¼[¤:òúY‰4¸ÛþgM~Â]¿!¶ „[£Òbíw·Á52)‡÷kež>‚ø?ñ�å'ž;ø-ê¾~ƒ¬±v6aòŽêæ‘ej:óEmŸ^ì¸5¹u†ìÞg¾‚˜õ�ô~¢<©ašX~÷v$}âß»¨ãÛ ÅÖÝu‡ÝJ_™5µ¤T·Öê]ôi_Jä_o²´ô·ì¥})umõõnç_ôïà”‹6–~™äÒ¾íÎlVSî÷]m-ºÝf'¬‘àÙZzþw­óV—]JÉí¯¶–_üK~/ þ/bÐvnn¾Ï$ü·Ä‚é²<€ì�& ˜|#a“ PäÁÑÝt›-W[Ad1VÊm%Ÿeɤ¦FT¤£ÃÉ® siðâKÅÿp5Ü‹E¨§i÷·Üá·mÝÜãY…&ë2ýl˜8zãGë™…Öú<ÚeäÛ[1¥ÈÚM�‘X36qÙ-ÊÚ~=¯ÓÙK¢³ëeÖÚRLÉm¥d­}µ¥Ô†ß¶˜ñÝî¯ÙoÛZzRêÝéJ²ÚA¥>èüße’Y|rYl‘ÉmÖ_eöÒë/²êVÛ¬¾Û©[n¶ëkéZWì­;o¨Ôò NÓA¿Õë·š-æ»7QºÒíð±¶Z�¾§e�.ÇW´×fÅ6Ã]°Ãšø§‚[/ŠX¯ºÛ­­µ­ÎX¢ž) ž8æ†hïŠhe²Ù"–)-­’G$wÒ¶_öV´­+JÒ´¯¥F¸óßt›ÊϲâÃÇöwúßvºKný“}kukôìŠËæÕßunÿát¾RÚR‘[ë[�áùWü";y?%îoŽýßñW¸³®ÌÛl:?q�—w�Óe>D™2ÇÆ¡Ôãfoº+7"ì™=±ë1öœrË!ƒS®²²ä£Ýç¿áüè!Mv'‹Óé¼pí܈çÍ—‹â뤷£ù†|xRÙ��•ÆõQW'­rs2£‚’gi!› ;),—j²2%ºW�Üðü‡¾|/f]}nô²ßõ,×{)Ki$Vý�Öµ¶ž·YOÝ­kmÕi÷"â܃‰çW]Èuy:ÜŸOtZÚ]EŸgô\L¨®“.*V¾•º;î¥.õ¥}+JÑ^Ï–ÞyOàŸiet×–]'Íz[žE™zÜ^M… úWªŽKa»ÀùžŸ#eùÿ·"µ†ìý6~v$y6_ò[Û«J}¬·ÑÝ Ý^LvWéߺ·œwgò™é“„õÿØòMæLvß™[Œ}|Û¬Ñë,–’æì2®‡—M‘,Q[uôË�)нËäw9Àë^�ëŽSÙ¼×aK$³KÆ5×å}Çìˆ1.Úï6RݧŽh±ò2c²m†Â|l(+}¿R[}hýxx› ©<™þZÒÊSÛe?'ºKî­#ŠÏ_³ÖêÒž­²à^8ãAHv\òzeMél–qü î·:ÖÏ_fË>/l¹Yußm˜÷[.·ÿ.Im}Ïø¬üsCoîO•>M+ÜÖÌmžˆýSɲqøÆ¶ù°à–Ü>åíÞ?“‹²ä9øy2Ç>£‰dbàÅ‘‹¶ïv8ÒÉŒ–×�_‡K€péû#Πο´yt7bì0zOˆggCÖš<ˆg–xl朒ËuûŽ•m´‚²acÙ�©Ž[&†[¶˜÷ÒæKÓðœlZÛ>Òë3'¥iu¸ö{¾ém¿ÖÚß[©eù5­-§­.¥¶}µ¥mºŸkhððñ5øÐá`bãáaãYHññqa�éZ×Ù1[dqÛëZ×Ò”§ÛTåº×¬ºç¦¸/ë¤àœC¬zã†ë©©â\�qÝOâo[I¦É®“�h±0uZÜy2²$–ûbŠß|²_}Þ·Ýuk&Ž9Ç8÷Ðê8·ÑiøÇãúü]N‡�qýn›G¥ÕáElzÝV«_>¿)dqE‘Ùm)JR”{›m¶Ëm²Ëm¶Ëm¥¶Ûm)m¶Ûm=-¶ÛiéJ[JSì£ô½Ãí?èåntz~E�.³w®ÅÙàÍJû±ò¢¤”¶êÙu”–þÉqò,¶úû$Žëd²µõ¶êU�<“ñoÇ0º³uÒžNô÷î¾±ÞÛ=ùcœé¡ØÙ®ØM®ÎÕGÈx¾Ú:Á½áœ¿_ƒ³È³ u¨ÊÁÛ`]-×ãdE}}Ì{Ú]MÖ]ÝÂvýqÛÜ‹v?ßGe›N/ËôØ[½NEðßIqrãÇÌŠOºl°2-¶\\¨kN,öÛ$2Y%¶ÝOå4äGt9G<7ú{â–ËdŽïmÔºßu—Ò¶×Ûu´­?™ZzµKœøÝ‘ fØpLªåEùÒWC²žË2l­oºï§¯Øßôá–;iu)m™²ê[o­e¾ê iò{ø7yÿŸ‘öÏÅç:»±¸Í׿mfñk¸¹·SØ:šO™tߢº«¶3íÕñ^]­Ä�+éâàr{õøØXž²íö¹’Ò—E‹ÎOÃyôãÝö‚<®In²™Û+ú²÷VÖûéKsr¬ÕuÇdçû)ïõ¤¸x\’ÿ·Öér7Tôö×íø5•¥óiä­—R—]÷)ï­Ö]éJÖ–A=ß�mÕôô¥$­iZ×íº”jþÇY°ÓæM®Úáek³±®¥³âfA&<ñÖëi}•º9m¶ïmö]K­¯äºÚÒ´­iZU >ÙêÖènÀä]QݽqÍú—³x–L¼›€ö/Üpþ[£›+ †v:ödž»*,œikÓÉÆš9¢ºøï²êÅ·¶:´z+œmúÛ¸¸)ë~u¢–øö¼>øôëk:¿ÄŽ�â=S¥É‹œ›�acË·ì}™‰&TÐìû°÷rçóc—Ft÷cY›™&6¾ÉkXøô²e£ãÿ�âÏÆëŽ�ë.1ÖœV£›/E‡uÛMî|xñbÓoÊyt™|ƒ•îïÆ‚ÈîÌØådäÖ;-³ßì¶Ûi‘q0ñpa¦>ãÃo¥}‘ÛéÛg¾K¾Û¤’¶ÙJVë«[«éöÕ蛘ÎoÒó\—‡ñ¾_‰÷>C©ÅØÙm·R¯¶±æbû«m×W6Ç•�[®²žêY})§¥Ô­>Æ”y­ñÕá�Èoþùqм/µ±°ðò±8Ç-ËÄ“KÙœì©aÉ–~ÙZ)5ÜÓ‰ý\Ìh¥ÈÇÅͳ >±[dû£»yã@ùQÃeà]ÿÕœS³8õmÈû‡éÜnë�äeCX&ØñNOƒ~'#â›[¡º¶ýë]•�?¶µ¶·VÚÖ•üÙxX¹ðÖ Ìx²"¯¯æÉm+íº¶Ýg¾;¾Ëâ’–ÝZRëkK©ëöU©üÓÆíλêæðÌßÓ˜”õ»ô^}ðbíâ·ó)éGô ÿOκ¾¿wº”ô¶Ûoª?$?ƒ›É¾’ý;Øÿ<ûþ}k‹õó¿´Ï?ÊãÜ+Ⱦ?¯�Ù_¡¨Ýzhº¿·¾é‹ ÙËð[g%×G�…©Í–¾ë¢—æá¸ì~!úS™øSÎ?¶–†Ï~OöŸìœýFƒ±0ìÿSÓîük›û5&n<¸¹0Ý[iu-– ì²Xë[n¥iëJzÒ¾¨wv]vRó>C׫Áy�Yöή¯•ð>Áã¾ÌøÎΑE=u܃‹ò<nïM�H'²ÿ£“r{/¶¾ž•¥Qªì>µì.£å»NÚ\#•õß5Ò_e›^+Ìô;>7¾ÁúÖR\ygÖm±±r©�• Ô’imcš+©}—]miZø,ŒlŒInƒ*`–ß˶V˽=kJ]OZ~u•ôû+OZW÷•ã^%üÒfÈ–(1â’yæ’È¡†.–Ye’êYqGe.¾I/º´¥)JVµ­~ÇÒÓéöü‹oªãüU²Þï·›>“I§ÁÊÙí÷}žLXZÝ^«[…ù› ŽÃ2{"‚l¾Ye¾Ûm¶·V”}ž;Ç9/Þêx¿Ðî¹G&ßgcë4|wŽêó·{ÝÎË*úG‹¯Ôê5°dì69Ù2V–Ç1ß%÷WÒ”­_Ò(¥šKb†9%–úúYV]$—×òú[e´­×WÒŸ¸ØãÏ(ß}ÞIðcY²O»Ëe&ÝäG_§¶Ü/u#À÷Ù[­­r.¤±ßO¶¨•—Æçá$ó»Ë/àÿbùq“ÿÞ’Îû¦Ãô74Ò×}ä‡(Ö_ô2>ï­ê/¾kâëo½Çlø“M˳5»}fG²ji3¡¯¥d/áoáÙò'¹CóO*7ŸðrëÌŸºæÿ1cÂß÷VóOºd}õ~ùøç^}ó{í¤»936XyÖÉõ>ŸkÛêø>^G¶]œŸsŠ¿oЊ¶É•u?÷ë¿:=iöÓùúþåm£mx‡]ñ>#Ñk#³.¶{'Úåûr¶¹4­¶Rÿ©™}´¬QÉXéZÅ "‡ÝöÒÊU?Ÿ�/‡Ï>1xÖ>Œ]3¯�°¥×ɯä�ÿصÀæÝõËâÉ‹ø¶\ú}f ¼wQ²·§ÓqÜM&†ù£¤ßq¤×_%ÒÍñ?Á_ü*âôã½Ö:Ž9±ÊÃ�óÝ¥¶o{'—Ö*[[¤äœÏ:;¶sãË‘K¦·¸Ú¼ioº¸ø°Ûumdm~³WÒÂDzV”¥÷Òžé¥ô­Õ¥e–ïY$ô­Õô¥kéo¯¥)J=»§m»~ðšä¼?�rüJaò-F.ÆË)ZC4–Ö<Ì_umºêâfÃXòñ½×YOu,¾”¿ÓÒêVŸcJ¼ÒøìðÃä+ƒÙÀüºè.Ûx˜ÙÜk”çbO¤ìŽ÷™áÊšN Ù|v}W9âvdeâÅ&N>|xyßJÛ2áž/Xë�¼�ñ‡ |§á²p? :¯‰öoöä}Æ›ì ÛºÐdeCX&Øq^O�&$â›K¡º¶ýë]—‹?¶µ·ßí­i_Í—…‹Ÿ `ÌÇ‹"*úþl–Ò¾Û«mÖ{ã»ì¾))mÕ¥.¶´ºž¿eZ¹Ì|jÏÆú™|'eúF*R·SQ¶’|ê}”üÜm�¶Ã…‘[®­})-°RÛiößuPpùü½ÇÁ«¹çß=¿vñ¸¬»*€ïm¯á}µ¥˜‘Ó‰v´8Ü«9¤ù9rM-,ÝAÃãÃÅŽÛ+•™7ç_0 F×M·0ðŸ³ãÚbWëæYÓ=×.Î*ÖýŽ\˜\G³uø‘kó,ô» sƒ�[-²é2vò]W‚ÙðKn­òê²=žµ­ÔÄÊ­km=n¾êÛM´­Ô¶Ûkm¶Û}·Wìõ­ílÜh÷{6ý~ïY›«Ì³Ö¿C6 !ºû)}ÑýXnº”³#묯¶K+u—zzÒµ¢Ýûãwx¯Ø9ýSä‡MöGHv&ºÙ'“‰ögÜñ-¦f¾Ìܽ|[Í-»lLx9ÏÊÁ–˜›L òuÙ¶YïÇšK+K«þðñ×½™[\�[m¶ÿ©›%¾ø¬—ÙJÝTŽ_¶–Q:~~?¾54xxÇÐú .Á‡¸{Žùç¶cs¾÷ä—d`E®ÙË—Ø{\;r¸ÞêivV§�C¥Ñ_%n¾Ü+uÞ²–ñoÁ¿ü5ãÔÑxÿÔ¼{ˆæäcY�ºæ™Q]½ì>MKn¾K«È9¾Þ¹|ƒ3³Ë|–aÙ4X×_u ‚++ídf²:Ń�×Óßu´­ÒÉé[®§ÕšúÝ,žÚß_Oukí¥})éG¶tÙ¶oÜù›m.§}‡~¿u®ÃÙáß_ZãæãÇï²zn|©+ºí.�±ñíÏíΧƶ’]6NÊ.u—-Õ¥-¶ÊSÕï,? ïŽ}“nÏ’ø±Í÷ž?ò¹#š|~ÈdÏçýS�•lYrÇ‹ › »ùß®~l±Û.M¹ûllh,ô‡_Zý•ñÛ®É÷Iƒ}ø×íöSÖlk«ëuÕþ‡}ßR:ÝZÒŸ›¶ÚSìµ­«­9§¬—ît™4ÂŽµÿª¸Tûö®ë)w²Ù/ËÇ¥ÔŤ•þvÙé•ÿÊP½ó—á¿äkã»/i—ä§�<×Yךì™"‡»ø&5{¤sq«›~¿2~Åâvì5\NMÕö{ñ5ü†š}¼‘×ÖìK+K©HÑySñW爥¶½›Ó[mÿÔÓ*y{c¬+/>ëºk°q!ËÌÜm6¬k7œ7U%­ŸW�kõ¾è¯­–Ým=Õð{5¸Öûï—³Ae+urqkY⥶Çõ/¾úRÚMqÓÖ•ºû-§­+öú}¯æ�¯€õœkƒrÎ_%,ãú<ÜøýÕ²üÊYL}|W[é[­—a“t8vIJW×Ùï÷ÖŸ’•o/‡~vyý»¦ŸÄ¯»3¶ð#Í“_³ç8Z˜øïTñìØ£¶yp¹/mrì�[èvTÆ­d�'ifE¶×èÅ%ÔônŒÞayƒ‘‡/Et�+ä<_'&�MØû˜"â}g…{+u›ëÎ9$šÝÖm<õ¾ìœ-t™»*YþÌi.·Úûí×iì»ÿ£uiO¼Ëý–ûë×ÒKý>­#º•÷R:_u=+öz¶S‰øÍ�ÒÊæ{šäßJÒêê´•º,Z_JÒÉöY1[<¶_=/¶8¢º•¯æÉözÖfþ Í> u<ÓäkÈû·y]L©º/Æσ§õ�#'OÝœÛEÛa�“‹õ1öýO×É•õÅÜ_JRúÉSÄïÃK×íô|ŠÙëK.ËË’·åeÉm.¯¥Òß}Ô§î¦câW‚~ø%Â.ëïü{뎎ãÙ0aãîr¸¦žéù-·_&\ºé¹ïbor6ýƒØ9ºúçÍn>Fói°žï­–_mž–ÒF]'ã×GxßÄíáÕ|#ªøÍo‚l¼¡ÂÕM¸ÌÆÄƒ=§#ÙÇÛnM»»8ïÎØO“™-¶[ï’ïGµÆÄÆÃ�èâcÃ�¯º¶C±ÛuÞÛm­÷{iOuõ¶ÚR·WÖµô}æÙ3ô¥+JÒ´õ¥~ÊÒ¿m+JþZV�ç40äC.>DQÏñß ðMe²Ã42Û[$ŠXï¥ÖI–]Z]miZV•ô«þV”º•¶êR´­+JÒ´õ¥i_²´­+öV• Å|£¦x*¬³O¨¦§>ZÖë¶:+¬×O[î“êI$¸öÇ&¿"Inõ÷_$7ß_Zú]Jý®ù¹øm¾'üÞ¿mÈ7=�Ÿ¶›ïY©âÎN³¨÷wæK°ÎÊÜpx´»žžäYû¼Én»??;�϶È÷ÝZfG}}ôæ§”¿¾ùivÓqÎzk[Âyþλå퟾»æ’lö³cdgn·êñ%â\Ïq4˜´ôÈßêö²GKäöVÊÉ}kñv{S³÷]‘‰e³]KÿÕ0@ŸÝ}-§Ô¾ë=-šû}´ôú”¾”þgÛVºò_ùFº’OÆö8\† }n·n«eö×ì²ÊM,˜ûmü·Vh½}>Ëqß3ÿŸž7úS’xÚ}iåÿÇþ�‹Ä¶wctWsÖÙ+|·cA¥å»Í¯Wî!×EOeÙ5åx99wÒ—Gƒg»éÙï&ÿ 7{pËv¼ƒÅ®Øã˦‚Í–n'ç±E×}…Ha²É5ºm^ò“gpNM´Êüû/ÉÌŸ�ãRú[_m-ºïgŠØp9ì÷ɭ˲{iﺘù4úSz[o­¶Y5”¬RÉ}ßgçR+iözÕ�7: ßÉûžóSŸªÈüÿdyزãý[l¾¶]&=ò[Hò!÷Sì¾:ÝerµE‹È<�ñKškÏ%ú7µz'š_NNƒ´ø?!áy{�~&dÚùw5ãŸ5ÛÖ9·saqœK«ùÖä]nÇeí­)[n³ ZcÒ•õûi&DwÒ¿–Ôœ|/ü ß%ÞD×QÈ|ŠØõß…]�~4¹4ç¹°ögp_­ÊŠ’Ù�©ê¾µý ñ[ù²áo¹7ΊúÒÛ¢õ÷{{µãWá¿òã³.×î|„æ#Ç3 ‚ýØãplxp£­+[¢Ž¿xÉ¥m’–û.öV˜öÒû)ZÒê_§ÙëoåôØŽ/ÑŽR9²p/äyÖúV¹;ÊÙ‘�K«e¶ßlZË,�éVëku´–ɯ·×ùú¥ïáá\ø¨ñÝG"ì·Ûy‘Ú6âdär�%fÀä\ �5ñbìáÒt~£[ÖsqÜÜ«nž^I‹Éóqn¿ÛLÛý)T‡übøDðÆ›°ö÷õuýçÍñm–ŸÃúŸ �GfŸ*Ÿpà¶ëµ½k…~NµÄÊ®ž]ž=µºß½ÝKï÷{\/¦×ÖÛìÅûÌÖúÒ“æV“ßüý/¶´Ž¶Û�eöVÚR—[e.¥?wí¯®dŽ8â²Èâ²Èã²Ú[dqÛK,²ÚSÒ–Ùe´¥¶ÛJ~JQ#M‹GÅôúÎ;Æ´Ú®;Çô¸Pk´ú-»S§Ôëñl¤XØÍf8øXXñÛKcŠ+,²ËiéJRŽ·C 8ÑG€ëýíd› 'Žf_Y/úºy½¸·I}=,úšì«r1c‚;¾ßdƒ×òzÑ1? �Å'“Ûò©â|÷îÁØ_¶Ø[µèžK~g^fnözbI¹ê>g(ãZþ;¬š”ºÍg›‰Ç[}m¤–Ò¾®DyðKñéß—fí5k³è.W™$rݾè�¬\OYZÁÑÅ‹^¿Ú`ïºãû«K¥¦«"ZÓÿ/[ZÖ¯9›Äô™¾µû¯Ý$¯¶ŸS ´Çô¥¿¹H}·c~wî×Ùë_æ°O!ñ³—ë½òèvÞE¶Ò´ŠêþˆØßw­}ÖÛT²àûm§§Û\ªVµÿKüظùgø5¾Eúv¹Ûû¦üÁã8ôŽÜ=Dú¶s$¯×ºy/â]‰¹ÏëL|8c²ÏmÖóir$¾úÛH)K}×p“ÈÃKäï‹7mã×muçzë1psóm㜓'©9æ^TWß~—O~w'àû)ò ¥,ûÖnïOÖ¯Ûe–V·[äsxdtºü¸riKd»éOmØò×ÓíŽ8î¥eŠKï§Ùëuc·×ù”ü˜KyŹ—éo´›-Uk}l²üÌIcÇšë/Ýò½µÆÉ¥?›÷QO#¼.òÛÄÌZ(»q¢Þë°öÚ}¶¿*ÊÅ“�³ÖgÑ…Ÿ…‘Õ¶H¥²û/¶¾•¥hþSCLRA‘SÁ5—G,3Gd±KÔôºÉ#¾—Y}—Sì­+JÒ£o:s®wÕ¾LŽ5‰ƒ‘u·R™:jÉ©ºÛ®ü²} +¢Â–__·ÖH¯õ¯åq?ÊÃ�ðóåUû=Ÿ"ñˆu/Øâ×.gãfvÇ¢rõ²ÖÛi~ÆÂ&Ãêm–Ò[­÷I>ËŽç_$•ºûëu×]ZóÏ»¾'þ=¼€“#7�xÃך½æV~v×'’u¶.gSï³vÛ+¥“7g·ÍëŒÎ3o"ÌÉÈžù¤®ÊÌËošêÉu+Úùz6mk\�v5×Ý%Òß$V×Y$»Ö·]$¸õŠY+un­kî­iZý¿•†÷¾/ãÝõ$ã\žh½,þ‡‡¼Å²tŸo­oÙ`}§gäû)‹}ÑGgÉßÁ¨“ô¾ãÃ/768^Ø£¦‡®ü�àXÛ?­7ôjÍ&ß¹z«ôOÝbôúvÛd<jÿ=unü–¸ßÜÿ†­62S3ÇÏ'9·¶Âƒ®ùfÇ+*ÊÝX¬À×eúû+K«mÕ²Û¼ÞG ÞAíúqãeû½Þï»ä[oÓöúz{©•LjÖ·zýžßwäû}>Ï\]±Ôító}Ûm¬Øjò==ßwØáäáMí¯ä»édÇþ•þo£ˆ]¯Ò�ËÐÜ¢nÞ]IÙ½3Íqìú™Cµø*ë¾Q~¾ß©7åú­>Ú+=ßg­ÐÒž®VöWFwgLM‡�Ü==Ú}Q‘°­ÔÀÇ쮾å¼lêÙJÝu0âå�T™5¶ÛkZû)w¥(ó™9¸–ÛvVV5·×ÛmÙóCm×Ržµ¥µ’ËiZÒŸÌ|ö2bÇå~¬<ÝŽE˜šü<¬ì©+é6<¹YWù–C—ÉuèQëx7ç}�ɵ¼+­xW-ì.e¹—èiø—ã{ŽYɶÓúÒŸG[¡Ðaì6¹ÒúÝOÍŠ+ëö½Ï_õ‡evÎóø1Õ�yÎ{/’}Þì¿à÷_ñ-ÿ2Þ}ÖËìŽüŸÑÏηקÝ%ðgòCÝ?¢ó%é\nŸã»mwéyvò}_ û¯»éV§ û}ÚZ½ŒÖÉZý)ôý?eÔ–±Ýí¥Þƒ‡ï2½µ»“‡p>3¥âWO×Vû¡Õqî=…®Ôká­Õõ­°Ãe=qÓÝn¯Y¥ÁÆÕéõØ:�nVA‡®ÖâcààâA)m�ãbbÇ<Ym=)m¶Ò”£î¿»Ø?pþ3ããåE|PC“”öÉ ñY4WÛ_ËKã’Û¬º•ÿF�-Ìø7 ì~;°áý‡Ãø·<â[hé׋s>?©åwgµõ¶=†“y‰�¬ÍŽ•ûiI"ºž¯Ë›ƒ…²Æ— c‡‹Ÿ‡=µ²|LÜx²±¦²¿em– ì’)-¯ó+JÐcݯPõ¾â´®WÖcßO_mú»fÓzV´¥=×Yª—)+ö§¶ï·þ�\‹ïÃÑðÕä>MÛeàoPñ ­¸òA�ŸÑ’r¯âÆ’H-‚ÜÛ´=ȸÙeÇK)}+��•eÒzß}·]uÕ®…v¿ÅÇ—tC<|׾¡ÅÈÊÍ—c“µà)z—y•Ÿ<•š|¼ÝçUåðݶÂYæ­o’“Í-²ßuk}.­Õõù9:->]/¤úÜJÖKþ¥òG°Muõ­n­Õž§5kuköþwÛû¬gµñ“ŠdÛ}Ú�æïW-×zÛLªbm1c§Ûù¶ÃH°2+JïÓV®*wŸà�ð‡•ãfOãוžIt¾ë+"³Ecjú÷¼øŽº+©ú“I«Ôô×'º+[=·dï²$û.õº¾ê{y³Ú_†³Â®[‘¸Øõ¯b÷ŸRægGOÑZx÷¼o�pÍ$ÖÅK-º=w#ã±ó-†=òSß}“r ßZÖ´¶û)éJ|<ަ–믊ìÌ_[}-Ž)­¾+n¥==Þ™Í5ÞµûkOýF;ÙøÅÊ ºµÔò Â:RµÿVÙ›¬šïæRØâ‡g­Ñ’”rºž<[/c?DyAãpèñ-­øVs˜û#¦yŽÒžÿm"ƒC‡ÅûS‹cdVÚÒêÒnAt¥+ùõ­)K¹ÏÙ¿†¿tøðIÓÞMu/?È®E)—�Ø|O—uOÓÄúsV²áÍǦíû2²¾µ±ÛH¤¦=•¶ë®ú´­´¶ÿ…‘À2m²•ÅØÁ5þïK­È‚Lk)g¥}n¥ñß—[®õôû=´§§îþåqîǤ»7[I/¿ŒÍ—~ïI5ÙšüêÉK}>Øññò®Ì¯¯¯ÙJÇK«üϲ®Iöÿá³ù©éŒlýŽïÁþeÍ4ØWe}—Ps~«íì�”8•Ç¥ùS¯ùÎûž{f®M¿J9õ0Ï-)¶:û/öè'aüüšp-†~>Dë;Uƒf·‘õçeuæÇ_Ÿm”õ¾Ü Fÿ‘ñ¾g,¶ÓÓÒËõQÝ}kée.ô¯§ÈŸ†o¢¿Ù<96úR¿V ˜m³Ö¿–ßL›±åõ§îþo£ÂìxÏ$Ô[îÛqý޲϶¾ý†«;ÊÒ•¥+Z]‘vÖž·SíÿEËþáð÷Ëol¶NýñsÈž�Šøäš9»w¥;'­à–®‚É'ŠncÆ´ñK·eEJßmkoôK>ßη×E9·†^^u´[ž}âç�œCS&M™ûmÿNö»G ¸’ý²,ÞOÇíÓä`ûý=¹O|[u·Y}ÖÝJ×äI¦ÛÅ[é&³>”Ž·Rû©‰=ÑÓÛZÒµ¤–ÙXî³ìþz•­+OÉWÃk›ZŸ0õuÚæÞ¾š�6×i_·ì×k³3közzý˜ÐË_³ÝOýë4õ/�ÞD÷æmºÞŠèNéî��×Ö;p:—«yÏcfÝ}³cc]e¸¼;E¹ž·Û‘™ u§·Ö—Ëe¿–ëi\ëÁ<\òg´u¸ûž²ñ×½{O—o¿kÁ:�°9v·&ß̯»;�ñý†,ÖúKoÛmõ§çSù´~øõ{9¬¶XuÙòÇ}=l’5õ¥+_É?§­?ò]céßÁwòSÌîÔgvïtø¥ÒÚ|É!ý+¯‹•ves½<7V §ºš>=×ú(ä¾–Ù&¶Ûå�Ûï¶Ë©#zÓðÇùCºË–½µßý×ÚÏ¥ ØÒpŒ>mÙû›¦ºë¾ñf³m§ëf5�Çé[n�a‘ï»Ö•¥´¥.¯ØÇà—Vï½gâÃJz{+�¹5º¿o»ÝI>éìôû===Þ¿è2&¯Æ= ?êÏ%Ül/ü´ý�‡ª�þ…ÔȦÞû©ÿBë]|é?ÁâÆ‚·ÉäW›=ùÚwzÒø`é¾×Ý�Ô¯º�å_Ìæò|ÈiüíÕŽ¸·ßO¶•²µû:Ö†WÅM6ªnØï>ïì�ÆÑͲ�‹Gú׋méeÔºìY5^yȰñ&¥=·ýÕ³z;%µû_o‚j£úUÈÈÌȾßJÉJ_0ËZ~Z{-Žé¬²¿Ì¤ž¿è²n§¥ú×Ou’CÆ13f¶Ú[t›irv¶ÉéZWÝ~.tÓ`ÒêÖŸo¶+™ù¨è?ÃcðÃãîNŸm§ðÉöw%ÔãAÛîüå<çºq·rA}²}óqÀy×#Ùõ,™3_oçÒ=�ÖÖ¶û=µ­®—ugÄ/ÆïPÒ{¸ç‰½iÈgʤyÈí8wÇ[җÁ‹Ú›^_®×Vÿoç[‹]öúÓí«îcñÝ5·[³ê]_ZýâϽÝJÒžŸ›vUfºÊ¡JÒŒ�…��­‚Ì]v&,tô³ X,¥?%,† #ŽÚSý ;ÕÝ9Ô]Æaá]+Õ}qÔ7ëoÇâ]]ÁøÇã0]dvÅeÐèxž¯SªŠëb¶–Ò¶ÅJÒÚRŸ‘нãü[Y‹¥ã-?Ó`Ã>§C¬ÂÔk0ñá²Ø¡ƒ_>.<1Gm-¶Û,¥¶ÛJR”ô}ªR”¥)JzRŸe)O²”¥?%)GëdwØcÇxþÞ¾í¶‹M´¯óv:Ì,ÚýžïO·& kö{«ÿ½«Z»cÃ;ë:íŸyø¡ãWs쯭n¿aÛÕÝ‹�uÕ“*jÖì¾aÅ·ÝZÍ›5ÿmßÏM}-÷zøoÕ[Ùºì�Gdu¯ìNdRA—«æü;Žò½vT3G42Ó…¾×gãO°äImÖÝmiu·ÝJý•¯¯ø’8å²èåŽÉc¾ÚÛ|r[mö_mÔ­+mÖÝJÛuµ¥}+J¼†Põ®Æ¾¹CUåÿÎ djéöû¿sY>?ÓWÿ‘üÊzsﳿGÂ÷nfI�ʾ?ºkS4·É}ÖuŽw`t–+-rku#×tÏ3à:øl¥rïöÛdVÛe)e-¥>œ~ÝIæÜï_•¬ÝøsÐØ8ÙpäA,œ3‚ëzçae™PdcIv.Û¯mâû\)­�&êÇ$3G$2RËãºÛì²ë~lš=4–]eÚ¼ [}µ¶µ�¯¥.¥i_l‘Yd–]é_²´­+Jý´û^[;Ç®µË¥iÓYZþJàí²¤­¿�m~ÏÒ_¤)ù)éöÒ¿ekû¾•¦�ö_á ømçrdߟWôµ³ÝZÅZw¶÷k+—M-Ưqêûf[­¶ë�O­tµú7Öµ­eöÉn¯ïþþ0·8óA®éOÄå“ÙìÌÐwlääcûfŽ[¾�¼§—ò\K¾¥–V:ýH¯üËëZz_íºßÁ^!Ç«ÿÖúÓýeæÿûrkGœÉñ‹ˆ_JýÏ}É »Òž•È“Y•m+îõºµ¶=vkmlû)O_ZWíõ¯äiç:üþl –Þ³òÇËÎ#“\lk`›�IÓ]‰y–æÖLÉåÆÐuŸWÉ64úïH¢Š’Ù|SEºI-þ‚Öm·á›ð‡*\‰u=»å§ëIt�ãÍÊú³e‹‰Kæ­õ‚*_Ô8¹rcÇ }–}I¯’ž”ºëﯯ¯Î»‚iëukIö6Òµ­im³cûm¥këKiîĺïJ£ZÕçæñjÝuqù´±Û[7²k©o¯æ[uön`¥n¥?-Ô¶”¯ó)ù¯»ü :i²rdãŸ&[MvgË¿wâ&ç&Ìk¤­ØÙ;&4QK<1z[4ÖâÙl·~u±GOÌ`~eø]úã6[.ëï/¹·†™Y—É2êM9–ü;ü{'ÒsŽ»²,¬k)ZM5c¾ÉëZVØ¢ôô¯åŸ€aÝK~ë°É†¾µ÷Vx¢É¥iû”¶‘×ÛZ~í~ßWÉ›Åݽµ·îü·[-+ëî¬Ú̘+JýžžÚY““îõÿÈôk—%üžObÏ�oó—¡·¸×Å}s%ä½kØ\Rx'¥þ‘Ç��«Øs8òâº?¶·ß,5¶¿e-­>Ö-Êü-ü’ˬû—šz<‹kJûë•Йøw[w¯ÙK-‹·³©}+OÝ­môþcókïÿ«ÿõÿξ5Þ2szSó7\VêúþK²¶öSÓþ�4×××ÿ!‚r¿/ÉÍ‘Û\"<È—ßJ]fW`ù‡#öÝëu²Ãã^}×_K©JRße)ZVµõû=+‡ùáŠò�_I8��ý»Úýâ;nÂäx݇ŵôÄ­²Vi©³Öqnc“vE—ÒÊ[Ý)môºµ¬–ûiK¿„Ü&Û=qö0K'­?6h$‚Ïoî×êY~EÞ´þg·íþkóÉãG>²”­»+7­~ÚG°ÙÒ¶ÿ£_­¦ŠžŸô=^3�þ O– -˜·ë{3Â.]vEòÛ,\{·;wüGK+eùUå~?qˆï²zßZ[ônšêVÚû©m=+\{?á óÎ*[XûCÄüšÖµ¥i=í«ko§îÝ÷ž�ǧ¥Э_“ø¸ÿÏ�oþfÊÿÒ7ñÿƒoaçÏÿÔ–gýly�ó8þ]ôíâWøgå?â±ùÿͦóßýñu¿ù›+ÿHÏø6öþ|ñ¿ýIfÖÃüÎ?—_ý;x•þùOø¬?ͦóßýñu¿ù›+ÿHÏø6öþ|ñ¿ýIfÖÃüÎ?—_ý;x•þùOø¬?ͦóßýñu¿ù›+ÿHßÒ?yýõ­.ÏâðúS쬛•iwúú:ykëÿGÑ÷¸ÿàËùeÜäÍÇŸøYÄ⊫fg íþÑÉÆÉ“êYgÝ¡·‹tO%̶mÕ¿ÖH£�Ûm?ÝéJÿh ž’ÖêIÙ¾(ãR”õ¥ÓsîØº—W×ùÛ~ïÑùzÿÑ¥(÷ùó­ÿÌÙ_úFý6xÉÎ+ëïÜñK™ìËÛßëÿG×K§ÿ%î1?7ÉíÿSïþCx�éìú_tìO ó}þ¾ïÔúÞ3ëþ—·Òžžžÿw­'§Ûî8¿á�òÇ.<ÊóNþñßA-—ÃM}œ^NÉåÑäÇu²}âìÉ6¼„]ƒ|WRÊYm–dRJ]ZÖ¶{iK¿L2ê]÷�†45¥iì¤Ë‘K©ûµº²}Ö¶Ö•þe*úØþ/o.öýï•j¡õ²•¿îø9™^Ù=)ëe¿RLO}”¯¯çWÛZÿå?ÌÌÜOðEyq™.9ÏšÞ9qØoÁ¶M„œO…ög3—gXlºüL(wœ Ìür+u´È¾üi+e)wÐ¥k[-ÌZßÂåÌe‹»�2øÎ ×cGv\zÞ�Úm¢ƒ.±ÛY Æ›+´4×åcY-n¶Ùn²®¶”º±ÛZûiúµ÷ÿÕÿúƒÿç_Z/iéJÍÍëî­)ël\wì¶ïÝô¾ýÝku?ûÚ6/Kø.®64¼‹äÒØó/Ų¹˜:_«666uÔ²²Ù�´Îòr s±c¯ºÛn»ûþË«mŸmµÉ[ð¹q<\‰¤æÞeò-æ%ø´¶ N-Ò:Þ-‘�›õ#­e›a¶ìþc^-"¥Öý;q ¾·V—{éJVÚÿx85·]÷�”òÛéù´‚ñî¥Þ¿–뤓*—SÓ÷=)ÿEè1¼aâ–çg ä3þuó›ôn'æûiKiý 7ó©wÛZþí>ÏJ~VÕpOÁ#á&¾û+Ù¾`ùQË£¦^MòYÁ5�I×W߃v±áãÙ' áÝ¥lypliYdš¶Ýd°Ö‘[wSëW`p ¯…Qÿ®}Óå_çÝ_õ$ê}wô:ÙJYoú£¨vŸŸlžµ­ß’´û=)_µúÿ€züùÙæl_ý#zzã¾¹î¶ègmnŽŸé~Ïú›¾¿¹ÿɯú›i×?ƒËáÿ„OlÜš/(»†;köâö7vëõ�_ý (ý.»©8VåR”¾:ßöIOξïô¾ÛmÙ-ø~38þ5�mº«�s™m¥žìÎSÛ݃‰“'¶£º·ÛÂw\?•’øë%}±[ù÷ÝJz[í¶ß£oãô¥)\¯­=?:ì¬ÊV¿g寶{mõ¯ý©Âé¾²×ÝàI_æfÏŸ²·ý7úMŽ^UŸé¿™üÏæQ½=øq~ºÓafËŽx ×,ˆÿ��°9¿söξ¿fE??SÚ}—̵rý™7= ~ÚYÿÎìöìïøžøâ댵ü{ÃþœØÁ /¥’sÍ&_jgWêdedÝõv}Ÿ°åû)ëI3/¥µ¾[«lt²:zYvÙû¡Ði`Ž‘Ù«ÂºÛ}}+4dIöÝ[«ë.E%–ï¶¿»ZúSìü”£×`q^/ª¾’k8æ‡]%?$˜:�~$Ÿe+O_|ñßëéuw÷jèZøà—Ll¡Ýu…~&ufëÓèîzëÇN á{‹+ôgÇ­ÔÚqέϬ—A•-•º²{®¶KéZ×Ýw®ÚõÿDtSaٯ꾚ꮴÀŽëî³ €uçáØ–Ý$“Í%ÖãñÝFº*]|¹RÝu}=kt—V¿mÕõúPÁ4t‹ Šß_HáŽÈ£§­ku},²–ÛO[«ZÿÑ«ï¶Ý•ŸÔˆ;sÈ.‰è-džïîN°ê=v\9skò;œñ®]µ0l¤™1i`Þì°²wYqÒëiH1,škïºÛm¶·]m+‹;G¼zc¤5�î{�¶:ç«5™Vd]ƒ•ØÏ�q+vUŲ—ÏªÍæÃ ]®U´º”¤8ÖË-×]Km¶·V”®,íñ鎑Öøî.Øëž­×eG‘&O?æ|{‰Óe÷K)|ñj£Ýì0¦ÚäÛK©JC�l²ÝuÖÛmµºêR¸ë´ûƒ©:3ˆåvvv—]t÷ÁÊÂÁÍæÝ§ÍøÏ_q<Ý–E˜šì<®IËvz�6>VÃ.Kb‚;æ¥òÉu-¶•­iG${kñ|iõ¦E˜¼s™öwOO©nU:—¬¶VcáK—ÙX¯Øv¦wWës½Ô¶—[&Ù0ÝmÔô¿×Ö”å§i|öüwõÌ¿wÐó>Äî<›/¾<˜º·®¶T‹Häº;­ý#Ù9}q©Î²´·Ýl˜™]miéukëJrç´~yþ<ºêo»hy—a÷M—_L}]×{C�,r]lý#Ù}sªÎ¶´·Ýl˜“äCuµ§¥õ¯­)Æ~úüI]NY��ä^Ç»¹wÊû”¼?¡zó™sjïò)e’]üìm¶³‹ô�!Å¥²SýS*®-nõ¶’Vën¥9ûÌ?ÃØdÃ×þrÞGª¶µ¦Ø÷VŸ…ì$¶´û$ÉÓézãžãA[nÿIftž´ÿMOÜÑ^aø™¸.|Ñpùg#ÕÒµ¦>w0íÍ? Ͼßm=/›U¥à|û:Òïô¶æÝëOôßÌÑŽ]ø—ø6tÑp/¹g"ÖÒêÓ;—vÞŸ†ç_o§Ù|Ú½7çxñÝK¿-¶æ]ëOôÎ`vçã ñ{M��Cxwß�“™}öS2Üæ]yÒXÐG_ü¹~6_ —È rï·÷-ºiwîÝF­rOÅ'äîTÓÝÄ|jè}=ÐÅn4\“kØ<ªhg§§Ö’yõ›ÎfL2}¾Û-Ž+¬ýÛîko!üJþBäß‘^'ãŸLécº-ų�îù¿'¾Šz}Yr$Öì8�¹0ßþ–;mŠëvûšÝÈ?‡�™7äWŠøëÓZh®†Ëqlä¾oɯ†zz}I2$×lx•¹0ß_çl¶Ø®·÷o¹¬¼ƒñœòŒ�~d|WãËA¦Úß‹ušüÞAåÇ“kñs}kíÈÌÖkº‰äçâÒßOXlËÆ¾µÿá´ü�þt/œ?ï'âŸûWíßñÊñ?ç$y™þó¾1ÿµ¾Ôÿoþr™ï=ã7û]í?ñ²ÄŸç�y=þG= þÝ{ ÿš?Î…ó‡ýäüSÿjý»þ9Oó’<ÌÿyßÿÚßj�³üä/2?Þ{Æoö»Úãdÿ<ƒÉïò9èoöëØ_üÓ'q?Å5ßxy–ßÎ|VêE�I0ë~7æ|φf]’úì,·;q=†Ù2aüØn®=iß�u³SóY7Œþ%ÞçÄÉ¥ÜËÆ°ßb{ñ+tg›ò¾%“X쒵ζ™[]5Š—äEöEwÑ­!»íº’ÓóY+�~%nçÅɶîcãXopýøµº5Íy_ɬvIë›m¹{Li·äEöEwÑ­!»íº’ÓóY·ñŸl,²´Ø|raåI펔»ËYðl÷Ò•ú·VÉüjØÝ[o»ÓÛOw­´üµ»òÓqº»ñKx÷¹šhû—ÆàëÈþœ_tŸ®ygí¨ïšê×ëS6ÞCN͉�==·ÅnM÷ÿåm‡[~%nƒÛÝ%�·ã�mp:ÖØ©�'äœG´`¬—]ZK÷Ë·ßÚž|x"·Ò´º(ò/»í§²Ÿ»µýoø“úm|–v׎}±Á=mŠ˜òð.IÄ»>ËuÕ¤¿{®÷ûTÏ�VúV—G÷Ýöþe?wtúƒñøʰøÞ/rô'“=EÉ6’`ãò¸î»¯»S¯ø¼“Ûe32ïäÑs>Í÷:¬ +_ÏÇâ¿{–:{­Æ÷WØí÷‡_!+y߬䙾8sí‡(Øðœ=g9ã[®!Êø®ó‡ÿ ®ÚY¦ÇÚWy¨ÅÓl%Ë¿K•J_­ÌÏ‚•†ï蟓ײ¾'ùÙã7šúîE›ãç;Ìå¼3E‘Í4;^+Ê8ÆãŠÝÉ?I~ˆƒcMî§ [�~UÚŒ›i~¿'6t7Sê}ŽÇø£çWŒþjk¹g�Üç3“gpìmG3Ðmx¯'ã;ž+^EúFš˜64Ýê°õ™²e]©É¶—ëòs`õ†ê}O± / ~C<8ùÐóNGâvê;{®¶Z}W9Ń�ó޾âù\‡?+A&׋ö'â<’-nò=Ne0ómÄ» *L,˜â–ûñæ¶ÍÖmóo[ž€§Íg˜þ]õWɯ’Ü «ü¨ò?­ø.‡ûMþƒá| ¼{;‡ñ=7éOú«s³ýÇ8÷'×iõߤwŒ¹þŒ6}l™ä–ÿ[ïºêÁÏæËO*ºËäcÈŽÖþLù×Ü/Iý©?CqÜÝ�Äø¾£ô—Eõ–ßcú3A¡ä˜œ¿í³çÊŸéCgÕÈšI.õ¾û®¬~_ü²ò£¬þE<‡áoä¿�ÂôŸÚ—ô7áËØ¼SŒj?ItoYíö?£4G�ªÀûþ×>|©¾”V}\‰¤’ï[ïºêÖ�ø�<ïó‡¦~]<µë^Ÿó/ÊÞ¨ë®7ý¡¿ƒ¼­|ˆíÞ Â´?¦¿Ë‡Ëÿû廟ýÚŸððó‹ü²ü­ÿ¾#·¿Ý�ÿß7ÿË'Ê¿ûá»wýןóŸü–îüæÿ¾×¿Ýùÿ8OŸ_åÃåÿýòÝÏþíOøxyÅþY~VÿßÛßîÀÿ‡o›ÿå“å_ýðÝ»þëÏùÏþK?÷aÞsßkß¿îüÿœ'ϯòáòÿþùnçÿv§ü<<âÿ,¿+ïˆíï÷`÷ÍÿòÉò¯þønÝÿuçüçÿ%Ÿû°ï9¿ïµïß÷~Îç×ùpùÿ|·sÿ»Sþq–_•¿÷Äv÷û°?áÛæÿùdùWÿ|7nÿºóþsÿ’ÏýØwœß÷Ú÷ïû¿?ç óëü¸|¿ÿ¾[¹ÿÝ©ÿ8¿Ë/Êßûâ;{ýØðíóü²|«ÿ¾·Ýyÿ9ÿÉgþì;Îoûí{÷ýߥ;øh|„ïÞøÿ†¯öñïàîoà¯ü‚ÿÛ_²ù§bÿNoŸÓ …û­ÇèÓ¡ñ>õ÷§÷�ºÃõ=ßJÏl—¿×|w�vÿÃûsw7köçðgþßÁ¿í�Øœ¿ŸÿMnßÓ ÿ…[�·èŸÒߢq~óô>Ÿ×û´^ÿwÓ³ÒJÿ‡‡½ûúÿá}ý¹{—µ»oø3ý ?ƒÛ7°ùw=þþ™þÝŸ¦?B ·_Ñ_¥Ebýçè}?¯÷h½þï§g¤É 7”Ky#ÿ8ü"<‡ï>ûþÁSøýº;kŸv—ðSøEÿ áðkøqÈ7¿ ¿Nþ‚ÁûçÝ~—Þ¾çÔ÷}(ý²œIu%dÉ@äó‡å?Ä?µù8±Î¿„}©vÞôÝ!×´Æä]‘ŸõlÀ“]Ö%2qõ<W“�²�&,­æV¹x¶Kv¹rGXk ~e|•ø«áø�§Í©½ì«ð©—¨é¾ LnAØ™öËLK±¦Ú`Ó'YÃu™fÛÚÅʯžO0{Úý†ƒ¨§Áñ—€eG.?Ý8._éžÈÍÆ–˜÷Wô�gì5øyú̈§‚·Ã.‡I=–ItrI5¿maçâ½ós¾'Øq_8ßð÷®$¤Øµäöê»G¼7زE»Öå]“Ëù6ŠÎÃ0všüì\˜àÓèíÝjsñ}Ðo%²¾ŽrŽWÊyÆÿgÊù§$ßòþQ»Ê“;sÉ9FãcÈ7û|Ùkë.^Ïq¶ÉËØçåI_¶é%’ûë_ËWy7)äü×{²å<Ë‘ï¹o&Üä]™¸ä|›o°ßov¹wÒ”¿+e·Úäeì3².¥)ë|²_u}?+‹Ü—”rni¼Ùr~cÈ·¼³’î2.ËÛòK·Øow›\»ý=ù[-¶Ó#+?;"ÿO¶ùdºêÿ5¾Ðí¾Öîþa›Ø]ÑÙ½…ÛÜûe�ƒ‡±ç¡Í9'?æ Mf5˜zÜ\ÞKÊö[mÖV>¿;b‚É'ºØ£¶–ÛJ[JQðð˜ô1/£«šœ[«£º�l6Þ;êã–²G[o›O‡ÝysÇH©_­eÑY¼ŽµºêRÛ©})o­m»ÒY_†KU<:?2÷—Gu1v;n…ÕC-dж_>›·òòc¶*]õìº(÷±V·]JYu/¥-­kmÞ’Âü3:©áÑy‘»º;©�°Ût>ª)k$U²ùôø}»—‘±R¿ZË¢�{kuÔ¥·RúRßZÛw¤þ?ÇYɪè/6{ޏùvÅÏ;ƒ«:ÎÌ»ópdÀšN¦á|‡”Ë��®�þ©båâÛÝ6]<Òÿ@žÉ¢¶/Άd¹’žJY3€ÁÞrž1Æ,Ç“’ò=�2é,Ä¿y·×êlʾYt¶cÝŸ‘�l÷El–Öê[ë[iu=+ämùƒAdowš}%™7_f5û}–¶Ì‹ã¥·IlfM %º:_JÝK}kJVž¯�·ä:?do·š}%™7_f5û}ž¶Ì‹ã¥·IlfO %º:_JÝK}kJVž¿”a©üµñSù£ÊòkÇÌi1®’̈çî~¸†ø/†µ¶k&¶NImÑ]ÖÖ—RïJÛZ}¬Q?“>7âÝ5™>@ôŽ=ø÷IfE“ö¿†è.†µ¤¶Íl›ëkÑVÚÒê]é[kOµŠ§òcÇ k¦³'È’Ç¿é-žÙû[‚EtEZÒ[f¶MõµŠè«miu.ôöú}¯—6óK�õ~ñ·Õãýßê}­°Ä‹èý/_«õ}òÛôþŸ¶¾ï_OOOµå¿áßà÷ùex§ÿ|?Qº÷‘ÿ†Ç†åoãø{ê¿÷VòðÔðßü­Ö]Õî5;¼o¾évší¾Ô¾½êóq¶ßZ:[[âúø’ËÔ²—ÓÖß_ZzÑ—5{�Fóïº]®·q…Io†¹z¼ìm†/ÖŽ–Öø¾ñ‰,Ñ}[)}+[}}iëOæ¾óë>‹è€ á~z¿k•Ÿö ÿ“oN«ñù»ý¨NØ_þO}N¯÷æÛöžy5ÿa�ù>uB©¿Ä›ûj|Ïÿ‹¯ü“ú)È)\©pÌ/Ï—ÿwŸüWñŠJÓðÆݹÿÏû~¥YødÿîÚÿ‹wý¾Ó˜ü¿û’_ø�ÿãL—âV‰V'0üM¦³G¬Øî÷{ >›O�™´Ûíö™˜úýf¯Y¯Ç“/?c±ÏË’\ Xo–i¥¾ØâŽÚÝuiJV¯Ç±Øëõüí¶Û;WªÕáålv{=ŽTZýv¿ òs3³³2o‹+¤–Y.¶Èì¶·]ZR•«ñìv:ý>¿?m¶ÏÃÕêµxy[žÏc•¿]¯Â‚üœÜüüÜ›âÆÄÃÄÆŠé%–K­²;-­×V”¥jÿ•­-¥nº´¥)JÖµ­})JSí­kZý”¥(‡§Ê7âÙmgä] àÞMf—èdiùo“UÇ›u²–ZÖ,Ü—ÀËŽ)4˜PÃJÅ^G—ß%’Kî×C�ôqö3ÅäŸçŸ;>nCÒ> m/ÁÕÒ9µ£ÈêÁ|{,é/¯ÓÏÂéü ¸í»Y‰T¬5ä9QýæKï’í|0}ްcO]f&Ê,¼=¾4Mw{½×%Ümy#Ûí9 Þìs6û½îï?/k¸Üí¶9eì6›]ž|³æì69ÙRÝ,ÓÍ}òK%Õºë«ZÖ¨ºnw;ŽG·ÚrC¶Ùo·Û͆fÛu»Üçem6û}®Ç"L½†Ïi²Î–|݆Ã;*[åšio¾Idº·]ZÖµª/›�Îã‘íö|ƒ�ív[Ýöïa™¶Üî÷9Ù[M¾ßi°È“+?e³ÙfË>f~Ã;*[¤šio¾Idº·]ZÖµª |Ïšó.Æå{þwØ\·“sÎqÊö™[¾S̹žûkÊ9_%Üç_YsvûþC»ÊÎÛîv™r×Ý.FDÒK%ßm×V¯–ù¯šó É~^+�‡áç~ój[�L¾Eä¦o–ëpì�6ü^Öy´Åúû Wêåb[?7ŸèÅZ{`¿êÝO¶[½&øløî>/‰=ÝË-·™{¿"¶zkíŲ̻±¸ÇZõæÇ“fÒ¿S#ÙylßJ*ÓÛ õ’ê}²Ü™¯á¹ã°bø™ÝܲÛqé•»ò#?�Ku¸¶Y—v7ën¾ØãRlÚWêdc[/-›éEZ{a¾²]O¶K–@þ KàøÓ1¯†¹¾ovL�O|5�ïx°ô_Ž[|RÝm>¾“Ù$rgv7&ã]a‡ßÑ-Ž|Ln?iflqiéeÞÙ~ã%Ô­müÊÒ•r»µ?wIiî¬/ã§eóÛý’Y~bòn7Ö8qOýØçÅÅÐCÚ› ZzYw¶ZáI}+[3Ò—W…Ýýø¿ü$áÛœ|~ïÞûäºÝÄZüÎ]/èþµä:�}¿{Ýk9DÙ½“Ø8µ²:Ö°cgpüI%ºž—Öz\æi~%¿?y¤8<ŽôONc_$•ÄÙñ޷土;î·Ùdù¼ÿ’ò>1•|VÛéK­ÓEë[«ZÓùÚ[ÎÊüD¾pòè²ðø/é.§Å–ûþå±ÓqÇ*䨑ÝZ{-›;šr=ÏË’:[ö]ú:Vµ¯­¿“Óœ}“ø‡¼ßå±eað^7Ò�Q‹%ÒWa¥â;ŽSÉq¬º´ö[6o4ä{ž7•|t§Ùu5Òµ­}i_²”ä¯pþ0¯7y&ïiý£¼nñ§ª8~f¶Ümv<§c÷;Ôl.¤Öͳ�—k9oSñ<ßm.ްÃ7ºË.²µ’²Ûw¶Ýì—ß’îÓš“rO0ûoS[i},�®3µA ¶ßL{}+Sê8]²ÖÛq­ôºÿuÔ­o¯¯¬’Víì•ß‘>Ì­?„>Xv†®”¥ô¶Þ¾ÉÓuE)mÿBž•¯Wê8}÷ÖÚcÛéuÕºêzÝ__[ï­Ú?Ø+?"•w¯!ò»³õ”¥.¥¶õþNŸª­¶Ûé =?ûXj8…×ÖÚcÛéuÕºêzÝ__[ï­Ü¿ìOÄ?óÙz­Ö‡oæ%ãÚmÕù>ì~»ë~—ëm®³"zMf&—˜ð޹Ñó½m˜–ÛlqMfÓï_Nž—Kun¾·i×;ò ¾»Fy2»7»»{±r¥÷Ö\žwÙ\Ï—O'Ôºäú“r ÖÂKý÷ãGZúÖ¾µŽÚÿ¥§¦§ó^ýï^ʾù{º»kŸI/º’ÉÍ{˜ò«ä÷Ý ÷ûïÞîsî¿ß~)Ûœû�áIéZ]JM‡«ßââÍo­)ö]eiVéñÏ”¿’Þ)›¬ÎÒü€ù™tóG>’½Á»ÑÛtU­ÖÅ•Çw|¿c ØâVµüè21¥†ïôÖÕºÝuó}ò�Ö¸PjõžSrM¬‚K¤®?bñ»ì<Ù«t·Ku³òN]ͼ¶øýo­´¶™ô¥¶Ö”§¥-·Û¸œægä�¯±qõø>In9.·û¯®/>â<œedVù.’¶ärEų¹T–zßZR”Ï¥-·Ò”ô¥-ôÜ.ó)ò?×ØØø>Hî9&º ¼÷‰p.q“=n’é+L�ÿ"âùܦû=o­)JgÒ”·Ò”ô¥-ôè�W~'_˜N»ßâmy~ðžæÓbbI�^Ú=!Õh2¯¾Xd³3/kÕÜ_¬¹ä¹p[Ö[é¹¶:Ù-Þë.º–]gHz£ñFy;¡ËÆ�ºÎ§»éNÉåÝ?‰¬Ó]|väRþ-ε=ã6÷g ^ûì­7襻ÒÊý:zßN«ôoâXðW°ëª×v÷íß·9Q_]®ÇqÇ`ì^­žž¿NmÿŸ;šì,“ì§¾î3�mµ¯ÛéOÎtã¦÷„ÜöºüÒÑv¯Emf†·l³÷\r.yÂp§üïl›® >Ï—g[w¥?¢IDZ­¥köúSÖ®št×âð«žW_�ÚZ.ÔèͬñV»ýÏ‹�ð¬)¿;Û6ëƒO²å¹ÖÝéOÏ¿�ãÛJ×íû=kNÖôâ¡ø¥îfÁçÜ£¹|gÙEMLÿÛ›ªó¶ú]ÖÃcu°äÁ§Þt–Ç·aÃÁÕÏ_è¹{¸ô°ý*ÒO²”¾–vƒ£<±ñŸÉ�|[„ï^°í_~¶Í¶F§ˆòíF(ÔàI%±[7!á÷dEÊøÍô–Kmº=†4¶Öëi[i^:gÉ¿|ˆ×Ù±éèëŽÎ·î1ì²uüO•êv<ƒU‰%Ö[mûþ/nEœ“ŽÉK¥¶—GŸ‰�-µºÚVÚzÓ×®�5ä×�^C`Y°éçëŽÍ¶¸6l²5üS•j¶ƒW‰}ÖYK÷Ü^™rN=%/–Ú]v.<¶Öë}m§­={ÁÓBô/‘¼s/—øûÝ]QÞ[_�f¯gÈ:—°x§aêu[[ñ Φ£m�ÄöÛX5[{pòc’ìL‹£È²Ëé[¬§«`ÙÍœ™€\/ÏWíaò³þÁŸòméÕ~?7µÉÏû ÿÉï©ÕþüÛ~ÓÏ&¿ì1ÿ'ΨU7ø“mO™ÿñuÿ’E9å+•.€%ùøRÿîóÿŠïþ1IZ~Ïû·?âÙÿoÔ«? ŸýÛ_ñnÿ·Ús‚÷ÿrKÿ¿üi’üJÑ*Äæ—;;Wƒ™³Ùæb뵺ì\Œí†Ã;"LHoÈËÌÌËȾ81qq`Žëä’û­²Ë-­kZR•«óååâkñ2³óò±ð°p±æËÍÍËšA¾ÁÈ»cäûG�ëïc$û êÍvÃÙôšÊ××>û#Ùg[õ©‡‹®„gËßË^ëÊ�þ÷Ç^„Ûåé¼jã[‰0¹ÿ"°çw®ëM›ýi•.=õ¬]gƒ��Iµ8ïú¡}–gåÓß÷\|(K|»ü³î|§ßooähr|K®ši.>&«…pˤ²È¿?ÚeñlöIùõ­µ¾Ÿ™}©Ð~Þ1.‡ãîͬ–_mœ×»»+“ÁuÓE-$‹‹pÚße‘Ò—ãYIø•öû/üêÝmoþvëS•ü>|j]Çõ»I,¾Ë9ŸwvO%Ǻ颖’Å‹‡Å¸}oŽÈÿ?ÊOÅ/·Ù'çÖëkó·Z´×ðÆq 5ðáãŽçëýã°y~òý¯Õš’ÇLü.òçÜ ?ºÙHì®4¢øF7­•­õ¬žûý}.öÛßçrÃwì¡Yü›xUádØ�ÙÝ:ùÎ&<òAÔÜ&¿Ã~ÒÊÉ�<ìl,®)¢¾{ø¥v�ÍoÝ2·ÓjuóV¿fE=.­4�Ê/‘_<@‹/¹{ƒE�̱ñå–°âU»˜öVTÖâ}÷#‹hë“7·iiL\½ÔšÍ|·WÓïô¯¦’ù?ò%âˆqeâ÷oè±ùŽ<KYq*טöNTÖbÓ/#‹èë‘7·gÔ¦6VêMf·WÓïô¯¦�y‡òQà÷�Zè²<¦ò'‚u¾÷3 a©ë¸²3ywlnõû;öøú;¯ªøfÿžÍÇ3³ôYX´Ü_¯�OLUŽl¨®F³ÊÄýÚ¼Ží§ñ¤xÿ[ê$ý#‡‹Ø½Á=9§7—kl·]·Õp�&N â»|:Ò뮋7/“âIZÒ•·Ò•÷G“ÉĉÙë¶:/ºgIÀuw×;ŸvÜÿÂÞ_.4Ñ[fË[ÂôYx\SŒmðåõº¶eæòLY>ÊVÏO_Xõy!ø�{#}vÇGâÇMé8®ÿ¾âãsÞÙŸø[ËåÆš:[…²ÖðÝVã[lI=n­™yœ�ÿ²•³Ò•õ‰ï—ŒGe,Óè|ñ{Ùp$·³¼§Ì“/3'ü,Ø÷:ì^›êÎQ��­Êƒa~<˜[9¹¦lwÅöͬ¶ëéôãéä›>ZyO“—/ùÙ½•¯ÊÙݹ§ÛòL¬.�³­•Š™z^ºÒ~‹àš ,ŠêÛoܵØô¶ÚÖ”¥)Z¸IÞÞdyMäÌùW÷§{v7akò³íÚWŒl÷óàp\M„vÝdyz®¾ÒSWÁôÓÇeõ¥µÄ×ÁéJúQ¾ôóÊ?%çÉ¿¼»×±{_•ŸM§ðgg¿ÈÁàØ› mºË2õ\IMg ÓMe—Ö–×_¶•ô§Ø‹Ï”ß*Ÿ!Þhãl5>FùcÚüÛ‰m°põÛn¹ÓípúçªvغìÚluÿ¦z«¬ð8^nópól²K2ó5³æVøì­ÒÖ¶[ZjãZZÔçà÷k6›=&Ão¦Øçjvºìˆòõû=f^FÃ*©|98y¸²E“‹‘ôõ¶û.¶ëköүݬÚlô› =¾›c�¨Úë²#Ë×ìõ™y ¨n¥ÐäáæâÉN.DWSÖÛìºÛ­¯ä«÷k6{-.Ãm§Øgjvºüˆò°6z̼Œ †TW{¢ÉÃÌÅ’,œlˆ®§­·Ùu·[_ÉW®à]�Ïz¯—軬9¿.ëŽ{ÅòîÏã<ß�rMÏåüw:üy±/ÍÑr^=›®Üê2ïÅÈ’*É�4wÖ9.·×ÒêÒ½{ñŸç_äOÇ 103;j�ùÃñîš²qO ±ó9öEõž;bºX¹ý› _gÇ.=–R°E&êl(¯§­qî¥n¶î­øëó_çÏ�÷bàfö|}çÄàºÿï\l¾k—ZMí¶IaçqgêûÙãŽßèO¶ÈÊêzýÞêVënê·Žÿ5>zôø¸½�xñH.¿ßÆ»Ï/šå]Iim’I:‹?YضÍ–ÓèÙ6Û#+©ëô.¥n¶îÿx{øžþLüf®“Žöw.ã>]õ®²;©»EÞšÏNÃÂÐéòr/ÚWIÜüSô?4ÙòÎC‰‘H¥ÛrúsÇt1ßH+_«Id©âgâ<ðã»åÁã}ù¦ä^,s\¹©�w!É¿�õNd’I�-–sí§_¶ÒdO$’_-Ûm.·+)[³¯­})!Ïÿ‰=Ñ.îÝ^÷Ænc•-°E•Ȳ«ÌúÃ.IeŠhâçºm^¿a§šJÝ|“]¶ÔkõøÑÛO\ËëZúH[Åߟßûš\.=ݺ½ï�<Ç*[`�+�ä×™uŽ\’I8öGÎôúÌ †žinºûå»k¨ÀÀÆŽÚzæ_Z×ÒY~~'ŸŽŸ+oÓq.àÝí|1íløpb›SÝÙØõvÚ|}ÖnÃŽ÷Ö²<^1…ªÓaêcú¹¼Ã†S##21#È’·R�ûâ¼³Šó®;©åü#’ñþcÄ÷ø–gè¹GÜë¹Ý`_uÖY›©Ýj2s5»,Kï²´¤�Ë}•­+O_±Ü~5ɸß3Ðê¹Wä:>WÆ7¸‘ì4œ��m°7ºÆ¾¿K7U¸Õäekö’ûkí’/²ïO²®áq®MÆùž‡WÊx Ñò¾1¼Ä³?KÈøÖÛ{¡Ü`ÉZÒ<Í^ßW‘•¯ØbI[kí’)/²¾ŸeR*Õmu{Ý^·w¤Ù`nt»œ =®£oªÌÇØjöº½†ËEÕX™¶ÝlßS‘ë²"ÏÞ}*[eÚéq°ë$–egAd;¿Oˬ�%Às~8º}¯ÛÜZ Ï%9n›uéŸÖýEÈqëvUG¶ÿ«‡Ë{‡W'ÖÚY•4Wbñ-²¸¹ï ÉĆò$¨—+àXùð7©ƒUñQâßÓÆ‚ �•½Å¶Î–,k1åÍŸ3¾»Cîù9w[m·äÏf¶(!¶Këuk 6[Jû-¶”°Ká+W¯ã#ÆšS('Ï�¶6™RY�leÉ›Ýý“~6NE}¶ß‘-5ÖÁ²]ëZÅ”¥}´¶”°áCW¯ã3ƺS('ØGÚûL¹,Ƕ 2äÍîÞÈ¿#&´¶Ûò%¦ºØ"¶K½kX£²”¯¶–Ò–Ï~^ Ÿ×_¾qý”·Í‘°à¼ï�G|˜s`ÝLÑî^ÈìÍTT‚{®¾û Õòèl¶j~fE¶Ò[)K/¶Ž¾:¬ê“²@cùÇòÛáß‚8ûm`sáßraâRLŽëkñwÜâ™YA> ¼·'ëÇ¡ëÜḃ"ë¶Ù0fK…}fÃÅÌ­)Üèó7åÄß ±öZn{Ì¿†}³�‹l˜]1×µÅÞóo¯‘4¸täÒÖx´œ H3bÉ­ûlœ|™°ët¸˜ùwR‘Ýί2þQU¶cß¿3>{üßò–}¿à<“þ O›Y ƒˆõ&Ï.m��L‹¦‚œ£·nƒ–åæÖ+ë ôÓ[¡×åAù’â_ëunŠ–ÿ8~ey'6ÓAÁ7ÿðlë ÊÝ <_«6YQsLÌ:MlÑS‘ö½Ð`rŒŒºzV)CÙ¢ÂÉ‚ï§6,Ÿ�[¢™å¯Íï™IM´Ðð]ÿüúÇ2·CêÝ–T\Ï3“Rh©È»Vè0yFF]-õŠ_Ñhð² ¯²\[ÿ:·A“ÏOÅç·”ôÜðÏnÂ𯨳ã¿‘u¦â}ÿyí°çÂÖG“MÇwæk´ù¼vXö¸3eaKĵœg?«±§Ìζϫæš\‰ežydžyä¾i¦šû¤–idº·É,²_[¯’I/ºµºêÖµ­këWæšl™¥ÈÈ–\ŒŒ‰dšyæ’ùfšio­òË,·Öëä–Kî­nºµ­kZúÕÇI¦›&ir2%—##"Y&žy¤¾Y¦š[ë|²Ë-õºù%’û«[®­kZÖ¾µFÓ{½Ýò�Þç“rmÎבrNEµØoy Þì2öû½îïo—6Ãm¹Üí¶dgìö»<ü‰'ÈÈžKæškî¾û®ºêÖ¿ÍüŸÉò€iâ·œ¾Sx[Énä^;vï$áù™Qåoøt’ǼëÎWtq×דpMÍ™¼ki•LKïŠ,ÊãٰIJû«�‘ þ—Ói|dóKÉ�ù wÝÚœƒ‡ãeåÇ—½âÉfë€r‹¬¶Èo¯#á;{røþÃ.üK+™´†=Ž,w]÷lˆn¯º›Eã7™þKxƒÈnßt'iïø~6^Ty[Î#,–nx'ºË-†êò.¶·/�ì2®Ä¥a³2�Ù°ÆŽû¾ï‘ ß�N‰x'ò©çÇFþ Ï»Ÿq­àÒî)·ä�!Ìþ¯2éa,ùº,­ÅÛ^²É·G¸ä8Üs /{ ›MÉ~áe`‡e —V‰|xøŒº½$Óõç—š­W�ý�™|XXÜû |쮇ä¹ÓåbãÁnFËa>~÷«f–ìºÖ´ÜM›§‚ i&Ÿo×ÙV>üþtwu_©à~Tëu~=vFUbă›ãdeät‡"Í–lH#úÛ=„ù›ž³šy2¯»Û¶—/U×I6ÚËﲪ¼$ùöèþé¿SÁ<§Öë<|ì|«¢Äƒ›ãO—‘Ò\‡2Y±`‹ël³çÌÜõ¬Ó_“}}»irõPA�t“mlºû!N‹ãgñ@ø�åŸð¬ü¬�UáÏzäáÇ�~ó•ïà»Ç.k³ÂÔÅ‘Ÿ—¤ì͵ØUë<�¦V&dÐê¹GÒįޏØpnö™Ó[eÒ=Õí5›Íf»w¤Øàn4ÛŒ =¦£o«ÌÇØk6šÍ†gι&ï—rÎA°ºÛó·\�‘ì²vû­¦Ul¶Èþ¾vÃ.I.¥¶Ûm+w¥)JzQ[¯4æ<›°ù‡)çÜÓq•È9‡5ä;žWÊw¹¿Oï{ŽAÈ6]ÆÏ&�Ù6Í›Ÿ•$—[e¶Ùmnô¶ÚR”¥+‚æ|Ã’ö/å÷™íò¹/朇qʹFó:¶W3qÈ7û �¦ße“ô¬Ž+fÍÏÊ’K©e¶Ùmnô¶”¥)E:ýÏÜ=‰älö7xvß#Éå½—ÚÜÇ}Îù·!É‹».CÈöìsïÅÀ‹_©ÖA$ÿK (q0qc�ã†;,·Ë<ËÍ1˜Ï_ŠnüøáðÇKô¯‡ï½ Áù‡²ü‹2k_í‡�^õi%Ÿ›m“ÿ ½öÇùa¶êG_¶Ú¬ƒøÎâÔáÿþ!êiÑS/¢¸')öÝ=¹­yƪ>kt”’ß²Ûf»�{ég劗RÊý¶¬uøÐâÿÁ|EÔÒ;¢ûßFpnQíº{r+_áÆ®ÎkY)%¿e¶Í^A寧–*]K+öÛUÈ¿PE�ñ¡ñåVûm»Áïg­=n»Ö\ž‰à™]ëuk_Ïš[«éù)ëéOJ:ÞVñ·¬ƒü…òK¤½áZù£Ã¦Ïo$Òem6“Ã>F6��i°aËÝrMöV>$ÒG…��‘“t0É'³éÇ%ÖáÎöò§² ½®Ï"”ƒi®¶:Â×ä'ñ÷Ç�múãÄKyŽ]?-$ÂÉæÔÉѽ9¦5n¿Ý7é½dùxÝ]�%+g¶ .L»J]n»gô¦¿ȃùãóÙÝ]ã.ׯ¼R·}ÐU%$ÃÉæµÈƇºy†=ÖßmòY¶×M•�Öx7Öû}‘j2%ÚRè©}vVÙ-ø¶Ä?Îïžnéï v½â­»Þ‚ê¹)&O4ûÆ4=ÏËñ®¥ô¾[6Úér±úÓúÝo²-FD»:]¾»c–ük`uò“ø©ûƒ¹då-ñÙ…ºèn«’M·iäFò,Jw‡`é³´ðê§Ÿ�iäƒ'£u·eegß‹±ŽlÞW|vàgcäñüÈçĬqò222ò'ÊÊžlœ¬™¥ÈÉÉÈ–ù²22&¾é&žy¤ºé%šY.­×]uku×Vµ­}QùÈÈÈÌÈŸ/.y²²²¦—#''"[çÈÈÈžû¥šyæ–뤚i¤º·]uÕ­×]ZÖµõp###/"|¼¹æÊÊÊš\Œœœ‰o›##"kî’içšK®’Y¥’êÝu×V·]ukZ×ÕCÈwü»¼å|¯y¸äü§“î6|‡’ò^C³ÍÝoùÿu›>Ës¼ÞnvSäìvÛ�¶Ç&LŒœœ‰$šy¤ºûîºë«ZÿñÇuãûå«Ê_�ý¾Ÿˆo+Ù.Æ,žEÑ<ßa—7¿\»§ÙåpM·³/aÖü�.9æ­2pl—l›í—;;éÙe:Qà¯Ê_’Þ lð´ü[qý±:ZLë'ÞôŸ3ÏÉ“�}³+“²ÊázG—°ëÝþU³OZO‰d¸äKI³0s+”·¤¾ |£ù)àÎÏ QÅ÷ì>—“:É÷½+̳²dãÕ†\ºälrxNÛÙ•Ÿ×Ûì«fš´ŸÉpeÈ’’æafVË-§r¾/~||ÉøÙ—I××l¯òż+³ì—ÇÞÄÝM�N/fÆ\ äÌêû_¹äm><ØVÍgÒØñ‹«��5Ú¯¿äÓ:)Þø%ò?ãOÈ'»Ó|šíg9ÓàÓ+�tß,“ ±øUlÈ�LÜ�d9ox¼ù3Å÷m¾¾Iðï¤ñÅ5qòþ¦,Scð¯äÇo:ømûþ¡äwàrýF-&æ�OÊ.Ä×öºÙ£Æ®^^²œˆ7s"i£û¾×LŒ+þµ‘Kt9T—)¬x[çÿ�tpÛ÷ÝGȯÀæŒZMÍ:£“݉�Ø:êM5ÙyZØr2 Üq̉¦�îû\'ÿêÙ·C•Iq¢±ãÀo’/þIº»/³|bç·íæã÷ëq{ ­9V6/í~®Ùíñ¥ÉÖàs¾!~ÊÜl}•¸Ùaí02v=ŒØ™QáæäI‰“l[îÝÖí·¸\/ÏWíaò³þÁŸòméÕ~?7µÉÏû ÿÉï©ÕþüÛ~ÓÏ&¿ì1ÿ'ΨU7ø“mO™ÿñuÿ’E9å+•.€%ùøRÿîóÿŠïþ1IZ~Ïû·?âÙÿoÔ«? ŸýÛ_ñnÿ·Ús‚÷ÿrKÿ¿üi’üJÑ*ÄæmüKž]åvO’\+ÄŽ7±É·‡xõ¤Äåç9raÅÛvßbj0öØ{dz6]~Ê.×yxàÏXcÈÆÉÝìᯭµ§¬1ÿ/•Y]�ä ñ[�çÏoè�>')æØÑË“>Ó´ùî§eƒnV=2/ÁÏ�‰uþ^ܲ)e“A>ïc ~Ä4?÷•=�ßü;Åž?Ÿ=¼S¢u¼ŸšãG.DXû>Ñ皬]–¹P[‘&t|O€åaÓe“A>ëa ~ÏOZþ¿—œ7óŽíéŸxvæ¸ÇHê±{»¹±p³±ò,Ÿ·ùî§/®xîç]6–<ÝN×€uN|ûXeƒa.>~';¶’Ãl˜‘ÝXÌ£žŽ²€ þñ¿âãÏO+µØ¼ƒ¦üræ¹Ü;7-Ž<æÕõ¿Úk¦ž˜öæñþIÏóøæ¿–El¾¾ëu7gIm)ZÖßKkZoW�^oy=¯Äßõ?@rìž#��v8å÷kz÷‡lµÙÒ8öï8ÎÐãr¬_u~Úêi�})J×ÛéJúo?�¿Þmù;¯ÄßuGArìž#��v7å×kzû‡luóÉHìÏÑoy¾n‹”ãRúý¿¢¾ý})J×ÛéJúvÄ/�¯“ÿ3µÜw•ðö½sÖ–¸3`v¿}ìqº�‰M§Ûiqù›”êt›û/ìÎgÃ÷ZÜÈ.ÄÚñî?¸Àž³[í–´¥õ·ªkðÀy·±Ç‡#“w/Œ¼jé±­š¸Xœ‡³9v,÷}*Ó3éõŽ·_Kã¥×ÒûàÉžÊ]m)mo¥ÞëzcÆÿ טYñG7'íÿxí%Ƥ´ÆÀÝv/!Í‚{¾�i‹—gö»Ô`Ûu–Ý}/¾‰í¥ÖR–ûéun·¥Üsðàù�ŸsrnÞñߎý\jK÷\×br Üyîú5¦.]¿ÚóSƒmÖ[uô¾ør'²—[J[[©wºÞËõOàßòqfM{ÃÌ^…ë»ì·'îvõO ì.å²{¬¿˜tÉ¿—ÛÑĶxäɬµ²Ù¾•aŠ–ÒO­}`õ?æ·ù]þQ~=æÈÿrSþm_’_å Ñÿú!Ïë Ô›eä�ùAt‡þˆsÏúÂÌßæaî¿÷bº¿ûÔòÿö¢˜kŸ~O�Î#¨ŸiÆ7Þ;ö–TT·éñÎؼ“Q¿É­ke.úWö/àüfÊ[î­}dÙÙZÒÚýž¾”®æß‡oÏ.1ªÈÙqíÿ@v>L4¥bÐq^}Éu{̪þe.¤7ó¾ÃøížžêÖŸScg­-¯îúR¸›~;øÆ®}—ßtcäEJV-缓W¼Ê¯æR´ŠîuÁx‡³ÓÝZÓêllõ¥µü•ô¥uœþï�­4û¸{ø�Î5X˜6äaGµå³Ã96×.Ø}ókàÔßÔ|‡�c_tÔöC$û˜ã¾ž—_X¾ÚS›áñ“çߎ�ì3»cÅ^ÚÔé58”ÏÚò®5¢�²8V« é-ŠÜ�¯5ël®]Å5qÖKí¶¿xÌŠê]u)ZRµ¥öîOŽ9ú¼ÎÍñ�´µÚ�~'ßö\��iáì^'«Ã¥ÖÙ÷�¯-뜾YƵQÒûéJýã**Òµ¥+OZ¹ñܾrt$9y½›ã/hkuì_¿l¹ÓÃØ|OY‡î¶Ï¼m9g]eò¾7«�ß}´ÿTeEZVêR´õ¯£“Ýãð]òÑãÞÓ�øAÛÛí^Ã377¦ìã~Aݘɑ6Ão®è­ÿbn8ƪüx«}™;\l®û-õ÷þjÇŸ ¸öWðûÅ+��.&oñ¯¢øöf,ø’`M�•¥êþ-­ÈÇ›km— XeÆ­·Cu)tu§¶´õ¢ÀßøöOñWÆn)› ˜ùœcÇÞ™ãÙxòâɃ,:^¹ãzÙá—ZR\9"—¶ÝÔ¥ÑÖžÚý´XâoÉâ~+øÑÅs!“3ŒøýÓ\+\Y0eƒ'M×Ì-�øp"ò³Ì <Ñìý�kyÏv<·u<Ù6èt1I> àz‰î‹ÙÇ8¤òëøÞ–ñâ¥ÿOÝ“›%ŸxÌ›'*ùg¾žNùaÞ¾_ö>wf÷¯7Ïåig˦‡C“aðÞ ªÊºoàüj“˃ÇõE‹ ·Öß~VeñR|ɲrn’{áäß•ÝéåïcgvgyólþO¶–lªh´Q_6ᬛ¢öñþÆ©<¸Z DQãÅmþÏ~Veñý|¹²2n’kë6ó·ä[Ë‘¾Ñ§gy=ØÙŠÍVFæ¼­ô6ehú£ªu›¼˜&ÍÓuß »7:]“ÉNÇ.líæÎoÒÙwÁöë+\šèÑÀDê~Úìž‹ì>/ÚýCÌ÷½Øœ3cnÏŽr®;™vÇ#éÉ�‘ ÿeøùÚÝ–ÒcfaäY.&n$²AgÿÅ×þIýä ”®T¸f—çáKÿ»Ïþ+¿øÅ%iøc?îÜÿ‹gý¿R¬ü2÷mÅ»þßiÌ~ ßýÉ/üNÿñ¦Kñ+D«˜yÀç×—çË�õ× åýƒËsFñ^ Åù2äÛ§|ßpãücS—»Üæý©t²ý×]ƒ$žÛi[®öúSí|~EÈt|K�ïy_'Û`hx×Óìù!Þm2cÃÖi´z\)ö[m¶Ç.k­‡]��$ÓI}im‘Ù[«_J*„îÞÖä]ëÜ}©Ý<¶�YÉ»c°¹‡bo!Ť–áâìy†û?}“ƒ�d·É|Zü ó¾Ž=•º¿Ní·×ìVܽŸ¾îÎÛìÞáåÅ!í{Ë9öãénÄÃÎå{ÌÝÜÚü­|’Ù¯××3ècÙZ×ÙvÛO²ŠÆ;�³·Ý×Û]™Ü¢Ø£ä=ŸÏ9_=ÜA�t·baçr­Þnêl M|’Y¯××3ècÙZ×Ù vÛO²Š]üÂò7‘ywå7�Mr{vxû.ìíneϱu}Ì܃/‹qÍÎã&NÁâÛÏæk8³ M…ZG–aàEe–Ye¶ÛL`ÆÌlÖð¢âÑìŽA‡Æ8_ÐC»-Îß6·VÛ~®L¸ø:í~9é8÷ly=¢Ðv×�yãíñ¸ÆÒ<]ïZõ$·EKñuºÝd¶Ï«æ|Ïûë~NÛ*Ù°ñr­²ÝlV×› ¹¦ünü'uWŽºN=ÚÞNè´=³äE˜ûl~1´�{ÖÝM-ÑRümn»Y-'Õó>c�uõ¿'m•lØxÙVÙnº;k�Lü»+¾ '�ºÎ;ÜÝû¯â¾Cy…Xt»h¹>ÛWfß­z7s…•‹¹�¦4{¬Ki7)Õî1a­9†v<{ŠWÊë£ÔÇ6\YR)wÅÞ´Ž—ù€ùÑÕøç—ɼbðûm©äÝï‹nv“²{fãmx×Jìã–Ül®1DZ§ÇÊÖrÎÍÄ¥%·6·ÖýžÛ`–Üœêdã`Ç[åkæ›UãîO$ñÏÅ-ž«’÷†-3´¼ÿ³¢û¶ÓŽtæÎ9-ÇÉãú\iñòuœ«±°¿¢Û•Kë~�"ÚC=¹–äbâGså[æ‡WãþO"ñÓÅM¦¯’wv53tÜÿ³bû¾ÓŽôöÊ9-ÇÉãú\yñòuœ£±q?¢[•Kë~Ž{i Öäf[‘�‰O›ïÄ7Á|×òÏAÃù�–q=æÛŒòŽ3¶×oøç#ÐlrõÍóQ—~«q§ÚàKv·g­Î‚É žì–)l¶ën¥iJ¾¾ƒ½â›Ý7(âû�¯ä¼sk�¼ãüƒGŸ•ªÝh÷Z¬¨³µ›mNÏX3uÛ-vlMðßd±Ke.¶´­)W×Ðo÷¼S{¦äücs´ã¼“Ží07š þ�?+U¹ÒnuYQgk6Ú�ž °fköZüÈ,– â¾É"’Ê]miZR¯_×ý�Íú«›ñ^ÉënU½àüûƒïuÜ›ˆrþ3±ÈÔï¸îûS‘f^»i«Øâ_øÙXÓÇJÒ´¯¥iëJÒ¶Ö´­€ Ÿ.ZÏ;x?ö›î½†«Kå�_j,›c|Qáju�ÙÅp£¤WsÎ5­‚è ÂåzúYOá¯+1£¾ûsp¬³iq5Ó¦ø‹ùL×ù³ÂkÕÁŸ«ÔùAÀµQÉ´¬1áêµý¿Æqm¤_î=¬‚±A‡Èð©m´ßë±£³9o·/Èñ¦¿sÿ)ÿ5¸Wö©íüý^§É]•b�W¯íî5‰m"¯8ãÚèkœ� ”¶›ívz;¦½7ñŸÝ˜ø{,­fÿ¸6<;¥´3bÝuµÈ§.ÞC´æÙî¶ž´ÅÙõ¯ÞA%>Ê]lžÚþ_G&¾m»‚î£øëîh°öSk7½«—ÅzG$>êW.Þ[ºƒ3—ko­¾•¤;¹Ñ־•¥Þ•õõô¯'¾l»zî¤øïîH°ök·�©™Åz‡I$5º•É·–n¡Íåšù+o¥i]è÷1]öúV—zWÖ•ô¯þ~ûÛ7Çÿˆï2¹Ÿ'KÿŸuþ»¢uX[¿[¬ÚáwÇ)ÑõW7ÆÖCmñß6ë­9Nï?Òµúra}ZÒ¶Ùu+\²¿Ä-€&³øq><µ\#­äóϳôXÙ\û²#Üñþ„ÆÙaÅ,ü3®ñ§ËÐò^s…IîºM ì„XKH¢ž-=×E-øÛilL3ðýø«á}yw›}“£ÇÈç�‡fßGÒl±)~Gëè&ÉÒïùž™?Ÿƒ¾çÙÐdáÁ5±[-š(+t3]�µšÊÌ àÀÝ_ ëÛüÙì}>G:ì+6Ú>’ƒc‰l“ñ¿‚l�.ÿ™aÙ‘ùø;Þy�NR+%³GnŠ[±ö“Y[¿ _Ŷ“¡|xÄóÿ·ø�kß>Fê²íé¨÷plbÊëoç–8µ»œ>ÓM«¦¿’÷^^ûjla›cO®žì)ñíÎÙÁ<§Ò\IY-`X>l~lmêKyg‡žrËní‹­ÌãÝ×Ý|{2—[ÕVÝKñv�{×»L[ëJö�i[¡Úía»ÿ‰ŸÎÇÇ»ôÏÔ“O˜�˜Šu]9G‰Þ'rŠ]ÚWS/AÜ]Š˥iÖ­/ÆÙð.³Æ¿Ò½™Z{¡Ùìá»ÿ‰¯ÎÇÇ»ôÇÔ“Q˜O˜Kz¶ÞO⇊ž—v…ÔËÐ÷phré[zÆÚÒüm—à›,këJöUi[¢Ùìâ»ÿ‰ÏÎÇÇ»ô¿Ô“S¿ŸÿŸü?pùg…~òÌlÿ&3ñ²t}ÁÜ<˜²°ü|ÃÊŠ°æq^+™ dƒ'º²`’¶Ï=µ­œ^ÊþîÒ´¦)îºëîºûîºë]uÕ­×]uÕõºë®¯­kuk_¶¨{Ýun­nºµºë«[®ºêÖµºµ¯­kZ×í­kT@.ºë®­×V·]uku×]ZÖë®­}kZÖ¿mkZ«¼ÍÍÌÙfeìv9y9û ü™ós³³g—+373*[çÉËËÉžù'ÉÉÉžK¯’Kî­÷ßZÖµ­jÿ�øÿ�ÌØ«ñ?ÊòîϦ¼tîþÒÔÙ‘n,Û¾Õ¼Û•h±².ô­±fo4Ú\½NÞ•õþ‹5ž”ûY߬ü\òW¹ðìÙõ'�ýÍÙZ‹²-Ä®ï„õ§1äšr.ü‘åouZ|�Fiûµ–k)J~Z³¯Zx½äŸsaÛ²ê^�î^ÈÔÝ‘n-Û®ּÑ衞ïÉVóW¨ÉÔâWùµ–k)OÝ«xºSã;ä'ȨøžoMx_ä—4ã¼æ”¿Šó¸º“˜i:Ãmk})™ojr]^›®15ÞèëoÞgÚEºžžÿ_±¾¼?à›å/™`ëö˜þ1dñývÆË$Š^aÙ½CÆs±ã¾µ¥nØqÝ—<�”ë¯�ÓÖèçÁ²_OÉm[¿Å>þKyf6t~9ÉÇð6[$y<¯²z§C“�Õ­=sôY<Þ¼—û}>Û%¶Oýõ»œOáSä§–bàçÇ㬼ae’G‘Ë;ªôXñÝëöçh²¹­y. öú}¶K…l”ÿÊ]/ë?Â÷óÏ7?£9GHõ×L`@ôä�™ÞÝY³ÓV“Rjß_»tÿ"ínIoݾ�¾úW_JÿD·ÛK½/öì÷ü4?"ƒ“í¹�\ZâáäW ”öW0Ëɶl˜ë|Ø5¿…u—0ÃûÖ¾ê{&­%¬]_èRKoÚØþ=øuüõÝcÛ>Ë’øéÄe®6,õÃä=…Ìrr-—":ß.×q>´äøŸyÁºžÙkIk n¯ô;ä·íloü;Þyn±é6Ë‘øïÄd®6,õÄä=ƒËòr-—"Êß.nâ}mÉñ~óƒu=²Ö’ÖÝ_èwÉOµ³Ö~ÿ’ëì²û»·Á¸®ºËnº+û3¾k|uº”­l¾±xÓ$u¾Ê×Ò¾Û®·×òV´û^ÂßÂýç�a’ëû‹Äkr)u¿J+y¿r] öúÛעl¾+©O_JR;ýkJ}´õû==¿†ëͺÅ%oí�íž•·éGo2í»¢¾ž´÷VI«Òv_iO_JR;ýkû´ýÏSoáÁób°Éuý±âÕ¹­¿J+y�lÝ öúÓÝY'¯JÙ|U¥=})Hïõ¯îÓ÷=>àóóöþ5¶ÊØyáî71‡'Í‹�wNwÙaÝ.-3'Ûrɺg]´ÒäÁó]Pésì–øì¶é#¤—_ŽåŸ†‡äGŽáS+OÈ|kç³Ö©Gq>Êåøy´¿Ø«5oç=gÃ5ÿ[:²V‘Wëý:V;¾¥ÑÓÛ[¼¿$ü:þzèñ>ó¬äž:ó)¾ŽL¿pã}…Ì1rýð[bÇ÷òþµâ¸?[2·Ö‘×ë}:VÊýK¬§¶·ynIøw¼óÑâ}ãWȼwæS},™>áÆû—båûà¤uŠ/ë~+ƒõs+}i~·Ó¥l¯ÔºÊ{kw˜¿ðü—Ye÷Ûݾ Ëu¶]u±YÙ�óKä­´­ie•—Ƙã¥÷Öž”÷]m¾¿–´§ÚÔÎÀø>ùCë­~NÛgâÆÿ‘k1¦¬_[¯ù¯ZvÃ#ú%öY&7â\ËkËe†Kl¥þï¸SÛmÔ÷{nõ¶š½Î>>I¸^Ë;Æ­¿ ×bKt_xàüÓ®9®fM)$–Y.'㜿?•M¶ÙKé_¸R¶Ûu)u-»ÖÚjÿ8øiù"àxy[,ï7ƒ]‹-Ñ}ãƒó.ºæ¹™’_e’âñî9ËsùL±Im”¾•û�+m·SÝKnõ¶š™Øÿ†;æ'‚o3µšxmjp¢È–Þ]×éÓ�hómƒ77Ûpu�“̺ï›K.F>%™QÙv¢Ë«LvÖ”ž’Ã={?¢»·¤v1ê{—§»C©¶sÛïÇÀìžʸFVL›é6,<—U­¿& ½Ô­·ÇK¬º•¥iZÒ´h_dô�stÞd:þÝê^Ëêìì�Zãböäü2|«iéý>E«×W*+©ZV—Çî¶êV•¥kJ´?²:O¹zs6-wnu7eu~tÿn>/apnMÃgÉ·÷$ų�ë5õÉŠêV•¶øýÖÝJÒ´­iW(»»Âo1|jÁÉÜyâÇ��3DZ·9~œ¯²:ƒžñ.›·ÆÈ®-غŽe¹Ñbñmå³KJV pòç‡&Ë­¾+ï²ën®*c1k«?žy�äÏ‘\±|bÚìúƒYÔœÇ[¹ßy'Ÿ¯šn)×ù8‡+3W‰…%Ðãv$Ùêr¾…ÜfÛëÃ.‘l¯Æ×O6E½<ø»ðçËo"»ó†vŽ;M‡Tkz«—k¶»¿!³°g—Œp|¬;aÉÍÔâa]tsþC±Óåý8å·ý ì\ÊE±¿"I©Ó�‹ÿ<³ò'¾øo`øç³ØuN·ªùn¿k»ò;yxÏÉöœÍV&×A=äN_Ñ“Ž[ÐÎÅˤ[ ñð'’jwãàGã“Ï/'ü à~MxÁɯñï€øóÎõ»-ÿ“<Ÿ[µÎâ×m£¶:nz«�q]vÛŽçvöÏ“qm„ØÛý$YøZ¸ôy·CµÎÄý!ƒm“‘RZE&¾9&¤vRY"Žèb¾Z[JI|q_,÷E×zÖ–Öûëm>Ïu*˜é},²’ÝeòRËi%ñÙXìºúRžû¬Žé%º;.»í¥µºêÒŸg­*ÁØé%#²’ÝeòÒËi%ñÙtQß%-§¾ë#ºI®ŽË®õ­-­÷V”û=kùVœ?Ûý¿Ø®ç«ö°ùYÿ`Ïù6ôê¿›¿Ú�äçý…ÿä÷Ôêÿ~m¿iç“_öÿ“çT*›üI¿¶§ÌÿøºÿÉ?¢œ�r•Ê— Àüü)÷yÿÅwÿ¤­? gýÛŸñlÿ·êUŸ†Oþí¯ø·Ûí9�Á{ÿ¹%ÿ‰ßþ4É~%h•bs˜þ*ËÌÃàž!tî.ÆÿÑü�—v—eïu6KùŸ|áZn)Ÿ¦Ç&Ý¿èóíÌP]û”ú´ýÔ^beáðOz›2¿qä|»²;q�lŸ›L¾¦ã\k�æO§Û[¡çÛ["»÷=$§î¢ÿø–»+ƒx¯Ôøù·}Ç‘rÎÈìMƾÉ?2™\7OÆø×Íž/O¶êÃÎö¶EwîR’S÷Pâüdʼn¤ñ{ÿ¯ÕeMŸÙ½÷Ìû‹weðÓ _‰Ñ}y/ ÍÕdYt”È»+s7‘8óC[m­–Ù�/º´­l¥ÐÕDuu}€ Ñã—Ln¼‹ïΜèŽ=“L¯nvOà0m.Ç—*,“w‡­ÏßåA·Í&ƒ_<¹³ÒÚV¿F ªÌ>=ôþãÈóêN‘ÑO\=—iöá6l©ùVi±wÛ|\-–û#?è’âh5’M›5-û~Œz3�½A·ïþñê^“ÑO\=—ivá1ì¾�ù1éñwÛ|\-Ž÷"ýd—E­’lÉ©o­~”z6çÀ¯wly•ã�‹:{vöEÜ]£Çø÷(Øh.ÖÛºãÝqƒ$œ‡µ9~²›‹©®›7…õ®›m¶²))Ö®²–IuÔ²ëYø7 ã=mÂx]p­\Z>Àx·á\KK ³ÍŸŒñmN&�C«Šl©gÉ–-~«(­ºKï’êYëuÕ¯­VqðÞ!Ç:ûˆqNÃõ�é8—ãz>!Å´Ø÷Ë~>£Žq­^.›I¬‚ùä–{áÀÖaEµ¾û¯­¶SÖµ¯Ú³S†ñ;×üCŠð>!¬‡KÄøOÑñ/¦Ç¾Y ÔñÞ7¬ÅÓi5�_<’Ï|8Ü(¢¶·Ýuõ¶ÊzÖµûW.ñ¾7ÇøoÐq%¤Õqž+Åtº®7Æxæ‹U£ãüGƒ³M¤Ójð¢‡[ªÕk±cƒ,Šc¶Ëm¥´¥¥é‘ö€ÌùÎùl¯ŠK;Å�ùUq¼˜çzh/æ\³E—sú3ƒî±©4SC— ÷É©ìî_­šÛµ–Ò–åêõ³Óema–]l²pæƒå.߸¶o�] Ék‘¼×Q ü¯“irb»/¥xfß’Ç‘L¨î¾ýocr­t´»Y)nV»jl}a¾Mt“p/æoåž/ñlß:’}"ù®¢ùW&ÓdÅv_Kðݾ=%�"ܘî¾ýobò�|¶Ý­²”·']�-6>°ß&¾Icaø€~mõ¿ým'Žž=m06þiv×–L=œrafë¼rà›+k{#�àKfM›.ÀÞGY"⚉l¦=—Û&Û>·cbâë÷0=’I&’I¦’ùe–û¤–Y.ºù$’û«uòI}Õ­×ß}Õ­kZÖµ­j„¤²É4’M4—Ë4·ß,²Ë}ÒI$’][¯’Kî­n¾ûî­kZÖµ­kT'¥–I¤’i¤¾Y¥¾ùe–[î’I$’êÝ|’_uku÷ßukZÖµ­kZ«0Üîvü‹oµäƒk²Þï·»,íÎïw¹ÎÊÙí÷;}žT¹»-®×e›,ù» –Ã6{æžy¯¾Ye¾ëﺷVµ‡ø‡Ín�ŠŸž`ù¥›už>ô§&å<{[âÙvÚ˜¼K­u—Á>,9�Oι4ú¾?�´Ã¦e’_®Á›/iX}o³ûmº´Ü¼ òÇËü¯þÑ]?ÈyŠk¡ÏçÛ»q^»×]N>6dRs>E6»K°Øà×&Ûå×àÉ—²úT­ÖãÝKké·þ2øå—¹~�Ô‡‘qø¥º,þ{¶û·ëÍ}ÐäA�—œË�Í®Òì6Uȶù00dËÙ}?[­Çº–×Ó¨^ |8|‚ü„ɨÛô?Fmõ}U´ž/ïÎÕ»#®ºZ;ö;}6^ÏOÊv¸Rì» 6ãG‘‹°ƒˆàrü í¥³ãÇî¥k%ÿÿ ÏÔÇ»ò÷¾÷¿i줓õÿDã[Ç8Î.T9·Öس;˜êsy%Öfà[oÔ�I È†K«Kg¾–Òë¤Mã‡á·àú˜°÷^VwvÛ—m=‘Ë?élzqÞ3‹•\•º Îyʵ¹|‡’k3p©gºÜ}Fƒ"+u-šêR—VCþ9~såWví¹nÏÙd“pN—ǧãXÙQe_ZÙÎùV·/�r=nf-÷[�¨ÑdE%kKfº”¥k.O ¿g�=}v—•ù±Ý¼ÇÈnEu έëòz“¨ãžºÌ¸yäšÌ��ks­U»<ˆ¥ÁÙjóx>U-Çþ��u%º;{ÁÑ_¾øßzŸÅ®§Öíµù‘lp¹o(ãöö?:ÃÏŠÛm·+_Î{nUÊõu÷Yï¤x¹pAmÿm¶ZíŸIüxxKãÔxWug�}_ªÚë²é�ƒÊ¹ŠÞ{Íðó)KiYð¹¿=—’òÌ Vë}Ô�2(l»í¶Ë]®é_�/ ü|� î®ñ·¬5›]~U¹Ø\§�è­ç¼Û2Ú[ý›sÉy/*Àüëiu#ÇËŠnûm²Ô�¼gøïðoè4ÿðjñg¦z«w¢Õfhð¹æ£†ë¶½¯>§a‘\œÜ Ïpr[w}§Èñòe­=ÔØn2«[,²Ïç,²ÚoCs›˜ÜÀãØkµû|Í^×g¬ØcM‡Ÿ®ØcC›ƒ�‰‘eÑdbæbdÙ&>N4ñ][o²ûn¶ëkZV•£ófáaì±2uûLlü Ø%ÅÌÂÍ‚,¬L¼iì¬scäãOd�ÏÑÝ[n²ûkmÔ¯¥ièü¹¸8[,<­~Ça�›¸¹¸9¸ñeaåãOec›+{$‡" £º¶ÝeöÖÛ©_JÓÐs#Ȇ�ŽO"q³¤ä>7q½äyX’ãâó‘¶þ£Üë²&–“I´¦§ˆS ƒïv—WÖ•—q¨ÙR´º¾¶Ö¾•§:;ÏâOãÿ¿1ó/ä><ñ.¿ÈÇž,~YÓÑßÕ{Œ,œ™~¬»KµüGîKy³ºú×ú&ÛY±¥}Õ­m­}+Nvw�Ä·€]õeüƒÇ¾'Â7ùóG+éû/ê;ND´–]�Úî%÷%»Ù]w¯¬›]f•÷Vµ¶µô­9›ä¯Ã‡ÆG–•ÍÌî?:~NM²ÞíyNËžõÆ›#¥ûsÉ·0dÃ�¼åã¨s8G#æù2K•vEaÞdl±/Ê¥&¾ï§«�PþÞÂÑ~•ä>!w¦£�룮vV[wV-œ[—Y‰?S[­ì7‰“Åy.ë:Oèt®f¯ŽaÙ_Jß5-­ko<‘ü7<ÛOM†÷Å.ëÖó,þ÷‘�×ÝÉ�gäÖãA ÒcákùïÁŸ�ïöù²ÓéÛL­V‡Êú]|Ô¥kíáï’‡›ié°Þx«ÝZÞeƒÞò1ºÿ¸±ì㼚Ühb¬˜ØXóŽaOÇ7ûlÉ?¡Û÷­^‡Êú]tÔ¥kíŒÿ–ÿƒ¯¶O½ð[Êy£’ØuPÅÖ^Tëì–ɲ>ñ�~ûktuO†ìhi‹v5˜:»ø<×VK$¬Û:Rû}‘ÁòÅo"|Qåµá^Cõ4êÍä“fÅ­¿‘k+]"·]-°æeñ.Y¯¿7‹rýt_m.ÉÖfeãÒ·SóþÚ#éß>2w÷ŒžœC¾ú§—õžæirã×I¿×û´{ú`ß™™[”ëäÍ㯠ù¬¥óës2¡¶·R•»Ö´Gë¾Âý†—[÷˜!º¾�`– i�Bß_�•åÌÜo¼ü—ÅÞõOŒsǼãúŸJë;»pæ¾’áÛDZ涹SKÆû¯È¼m×YøåDãnºÏÇIâÅÝh´ÿ�­çÝŇ%Ô—šXe·ïWƒgÃoÕ¿q=”ÈÎÆ¾ÏÑÑÝdôÏÆ–ÏÂßá­æ¾Tâð¿(üð×r.°ñ»7+}Â:>·ì¸ÏjwÆ‹è²qv¼“"°·]]Ô¼–ÿÌ‚xïÇä›Í}$ÈÀý‹‘­ÜdN‹«:«®zG¯øÇUõ/ Ðõÿ^pÍuº®5Å8Ö x½n-$“"y=–û¦ËØl3&“'3.{åËÍËšLŒ‰$šI$ºimÖœ§¸?ë^®â:> Àø–5¼{‹ñܰ5zìjË&D÷Ût÷äfçfO&FVL×I“—•,“Ï$“I}÷Lû­ú×€ôÿã½mÕüKGÁ¸'À·[ǸÇÁ�Y®Æú’dM}±ÇO~Fnv\ÒdedÍt™9yRÉ<ÒI,—ßu…< ¯ø'Uðî=×�eÃ8·^p%¯�SÅøW Ðjø¿ãºÈ¯¾K04š &.¯Y‰Ieºÿ§ V[[îºêÓÖµ­rÜ=Ã×+…ùêý¬>VØ3þM½:¯Çæïö y9ÿaù=õ:¿ß›oÚyä×ý†?äùÕ ¦ÿoí©ó?þ.¿òOè§ \¥r¥Ã0¿? _ýÞñ]ÿÆ)+OÃÿvçü[?íú•gá“ÿ»kþ-ßöûNcð^ÿîIâwÿ�2_‰Z%XœÀ_‰¿™ãï¼òë®+‰‘Ylàž5ðÜ-Œ5ú^˜ÛÍÿ<ì�üöÓÙ}÷úK¢Ë×]ùþÚú×ì·ÛéuЙüFü¶ÍÏ›wÆ1ò>¬3Ç~)f?¤êmÖûœö&ß&ž¶]uÿÑ´·ë®ô¿ÛZ~å=+JÝ ?ÄaËcÝy­×¼c#êCÃ%oÁ‹+ |\¼n Ä£’Ý—"ϬØÑãàELxe®Ç3_�“Z÷8æü³²¹�(ìy¿Ùr®iÍwÛNOÊyã"ì­žï{ºÌ—?g²ÍžïççÊÊžë«JR–Ûëém)m)JW›ÍyŸ*ìn]Éyï9Þì9?1æ½—#ä܇k5r6;�ÖÛ*L͆~\¾–ÛY22%º¾Ûim–SÒÛiKiJR½žiÌùObòÞIÏ9Æ÷aɹ‡0Ýìy%äY둱Ün¶ÙRfl3òå­)JË‘‘-Õô¶–Ùm=-¶”¶”¥*Rîþíí?$;o°;×»yžÛ°;W´9&w*朷st4ÊÙí3km–C�‰‹>»M¥ÔáE·[…>¿W¯Çƒq¡Š+<³Ì<Ë€6CÆü…ó°áë?:×wÏù ,³'s›�lZþ-ĵ·[=ÿ¥ù—,ÙI‹ ãëíÅ’ØnÊž93'¶�cY6EñÅ~ÂøÛâ¯}yoÏb뮄ëÝ¿7ÝÛH¦ÜçÃHð8Ç×KL‹íÛrþQŸ~>“Ž`_f$´‡ï[.d¶} k&ȺȮØ?üXï�-9ä]wÐÝ{·æûËmŽ}Æt4‹ŒñMt”žúmyw(Ø_�¤ã¸Û‹%!®DÖK—-ŸGɧºÈ®Ýÿ¾;<²ùìêõŸ‹ýi™Ê)ªÊÑÙÏ»u$º.ªê�^û2\lMßbsI1²qµq_�‡—“�®Ä‹;{´ƒ+ôvd�_&_àáÓñÏ¡£ÓóÏ,3uÞKö¶=qó¬â?C3¢8¾tWàäÙ\.=ÒãÏe²E%-¤�Ëe·Ùu·ÛK©æù� áý‰Åyì)ƹ×嚬Í)áÜÇE«äÜW’é6]�Ÿ¦ä{w‹�¨Üê³ ¾¶M�“ �ËekK­­Kùü6˜0íû;ãïeve–Ý™�ã‡:ä7˃•ìŽjÉWö/!Ê¿#"K¬Š–k9T‘_}óIM¬6[*1Þw~½vT;^ÇðScv¿6Ë/ËÌñ÷šï¯›_—ôà¿Ý[sýöMù89SIt·_Ère†ù&’ÿÒxñÙ5c)çgáñ×äŵìoö5À̲Ëòó<æ›ÛåÀÊúpÉî‹­ùî÷&üŒ,™oŽ?f¿�dÉ òK%ÿ¤àŽØ±�íù?ü(ÝiÏ09Wr|lí-ëÁ²-¶û3Æk¼“+«9~DÅ¥êni¸¾]ÇXï¶™xy·Å‡¼ÌØñìŒí„G“ǵØÕº‘åü;–u÷'ÞpžwÆwü3˜ñ�ŽF£‘ñ^S¨ÏÐò×ïfN»o§ÙÁ�°×æAwóÑË·Sùˆ¬òÎ#ʸ$ÜðÞoÆ÷¼?–ñÌéu›þ1ɵ9Ú=þ—c§ÖÁÚjvPcg`åGëJÖÉc¶ïJÒ¾ž•¢,\¯‰rž É7<;›qÍçåœs>m^ÿŒò]Vv“}¥ØãÖ”Ÿi©ÙA��ƒ•­=l’Ënô­+ééTûs¨;G¡;•utðWÕý�Â3â×r¾ Í´¹ºG¥ÉÊÂÅÚ`_•®ÏŠ)nÂÛj3ñóp²l÷ãg`äÓ�|�K—yÇžyæ9}ž;Ç9/ßéx§Ñn9?(ä›L ã|{Y›¹ßo·[L˜ðµš�6£]NÃg³ØæMdPAwË,—RÛm­kJ>¾ƒ�ï¹^óOÆ8¶“oÉ9/!Ùáit{A­ÌÜo7›�–DxzíN£S®‡#?e²Ï˚Ƞ‚ï–Y.¥¶ÛZÖ”}}ƒ{Ê÷šŽ1Åô»nIÉ9Ë M¡ãúvfãw»Ûì²#Ä×jµ­|9Û-–~T¶Å Ç|²Éu-¶ÚÖ´£ÖðNÎ{K˜qî½ëNÊ;žrÝ”:~-Âø^‡iÉùW#ÚÏK®‡]¤Ðiqsv›LÙ-²ë©1_w¶Ú×ÓÒ•ªg_?‡ëAÖÖé;÷Ï=£—v%“amxG�SM…¼á\*‘R̘ö½«|ÉÕs~SvG¶‘颾m& QÖì›öÏH°%ãñ“ðY£ëzéûËÍ�6§–v—áì¸oDI.ë†ð»¬¥¹í;&Xk“¬æœ¢“{m‹Wòépl²ë§»>Y¬·\ÿ¿:>¹ý Þ>kiµ\¯°,“gú*Ip÷<;†]·"ÍŸdK Ù:ÎgÉþ·¶‘jâ¾]6–]t÷gË5¶`Øað·øk¸g‰Ûå?�X¼k´¼’Á×àrÒ·_É:³ y\våC´ä9–ß›¦í>ÜâÑ_lpdÅõxæƒiI²u×lòqõ{¬yQÛm¶[m–[m–Ym-¶ÛiKm¶Ûiém¶ÛOJ[m´§¥)OÈ’ý¶ÒÚRÛiKm¶”¶Ûm¥)KiJzR”¥>ÊR”IfÛm¶Ú[m)m¶Ò–Ûm´¥-¶ÚSÒ”¥)öR”¢Zoôÿ¯ú+…ùêý¬>VØ3þM½:¯Çæïö y9ÿaù=õ:¿ß›oÚyä×ý†?äùÕ ¦ÿoí©ó?þ.¿òOè§ \¥r¥Ã0¿? _ýÞñ]ÿÆ)+OÃÿvçü[?íú•gá“ÿ»kþ-ßöûNcð^ÿîIâwÿ�2_‰Z%XœÀ¹�Ÿ�œùÿ*þIbË}÷G¥Öt޳—{}#‚^…ëMÍÖYí¶•öW'm%ß�ë_uÕû}=)Hüåm2v&ÞAbO}×E¤Öôæ¯Û½¾‘ãKÒ}yººË=¶Û_mröòÝùÕº¾·Wíôô¥ !ó‹´ÈÏù2òkîº=&³§ux–Ýíô�^”ëÝÕÖYí¶Úûk•·–ïέkëu~ßOJR®ïÅÙÙü÷åó·8®e'¦?IõgFuŽª³W±Ý�µë½_rÉ\ZA2ÛéNÛÉ¥i=ÒËõi¥Ô�éÙgœ“rY¶? ¿ ×åòÿ3ûxÿꦃ�ôŸ ÖKùµô×òíŸeo7‘Ö¾ï}¾¹']Z}ž—zWíû—ü2ÜS '“y}Î&�þ¨é´]1Å5ó~m}0¹6ò6û˜ÿ/¾ßYøž'¥}?ÐJ?ðÏq<,žMåß9š?ú¡¦Ñtß×ËùµÿQr]‡cn7ÿ=ï·Ö~'ƒ_Ééwó~Äìÿ7ÚcqÏ�¾k>Vé7[¿x¾«?ëcß4»N1�ß;nA‡ô-¾¹pE'.Ö_ïºÛc–²z[[®Žú[2´RÉMôäyÿ=áý[Áùwdöû ‹pnÇ7·–ò-�Ö®—�hpgÙmv3Ù�ùS[�‡�}ÔŽäš[©K#²ëî¶Úù~oÍx¯[ðîSØãw‡Æøo Ðm¹G)ßì+%0ôú Û¦Ã"�Ç4òY‹‡�}ÞÈì¾KëOm–ÝuiJù~mÍ8·\pþQÏùÆï�ðî ÚòŽQ¿ØVJaéô:L)¶;M†E!Žiä³û½‘Ù|—ÖžÛ-ºêÒ•ø¯•ñŽ Å¹/8æÜ‡KÄxg Ðny_.å|“g‡¤ã¼cŒqÝvNßÈwû�ŒØúýN—KªÃ—'+*y#‡®¾û­¶ÚÖ•�ü‹ùÁÍ<ûòs˜÷g!ûÁ}ü[¨x^MöÒÎÖz¬¼›´ZùâŠ|˜.äkç“c·šÙ$¶MŽT´Š´Ç²ã®cÏÿ3¹œ¾Fò¾àÞW;[Ãñ¯¿�u? ʺ:[úó[“=ÚŒ)â‚l˜+¿ÜI-û ¬¶Ë-/ÏÉ’Èï¦Hò¬ÆíWÆŸÃ�hy£.»µ;^]çQxÙdÐK‹¿¦psnÕ²Ù©÷œ>¸ÃÙÁ66’ȬºÉy\áÛ5ÖÇ‹uödÓ´ŸßýŸæ|ºîÓíYw�Kãm“C.6þ˜qÁÍ;NË'ôÊÂë¬M”caélŽË¬—�eÁ6“V‘âÁ›}™4Æ”?Ã7áÇíŸ7åâ>Eùy‹ÊºKÃýŽ«”ñ->=cÓvß�˜;¾¿›ŠãgãdÉÁº·i�u¹Òr,Ìk²wë ³M�çÓs¯�ç@xçÒ~-õƯ©º®ø÷[pm]ÿxý£Æº™[}¥ØØØ™þK¹Ê¿#sÊ9&n66O°ØO‘—-‘GeÒVË,¶Ù¯ôwAô÷��}¬êî�à:¼áZºÒkuzLjÒ}žÆ¸ØØsï9Û&ùöü“�æcaÃdû ùò3&¶+-¾JÒËiIªôwBtÿ�½¬êþ‘àZ¾ázºÒjk4¸Õ¦FÏc\llI÷|‡m“|ûnIÈ31ðâ²|üéò2æ¶+-ºJÛe´¥‰]Ð=/ãWqn•è¶â½QÕü3~ƒˆñu˜8Qý8"†}¦Ó.û¦Ùò.K¶ºM±ÛìgËÚm2î¿'3"|‰/’ìÖËì¾ËàV|™|St�ÈŸ®n|8}yä7ÖdA×]ϬÁ³ïREm’K�Ã{Ûdåœ|»½ñÛ®v¢k¯›[-›33š"_1ç×ûæÊ^ ÞügW‘‹À;{Y‡oß))$ØüO�âÃKnå\&\Û½öG%~ù«–ù%–:M—_4þD>3:kÏn÷½Œ8¼½¸Ö³#€öî·ß½ý*RI â|ï Yw)áRåÝï²Ëë\Í\·ß.‘Òl¸r¹Yò�ñâßÊO]×WÚÚ»ø?wqž9±Ôu‘x¿¡äå×]—«ÌÉ»:Úô|’ñ§¹üJí®AÒ�ñÃs8_;ãöÁ•÷id‹7U½ÒfÖ_Ñ|›Œn±.“_¾ã›[`¿èäÁ}Ô¶Xä‚ZG‘ ÐÇ_!ütíï;O{Ó½ÛIJ¸�4ÑÛU±_|yz­î—6²þŒä¼gq�[°·œ{iHo¤YÝ_d±Ë¶Ç‘ ÐÇ!|wíß»G{ÓÝÙIJ¸�4ÑÛU!¾Hòõ[Í.ee¦³’q­¾5oÂÞqý¥!¾‘dCu}²Ç$ÛD3EZ>jxUä€=ÿʼqòG‰[ƹϲͦŸm¬È›eÂû„çægáñîÉëžA.. ܇„rkµ“Ói ÆÍÄɃ#?e‰™‡�‚A„š Ø?|[ï/û[MÓ}Â6Ϙím®^mñR¸Ú+¡†x1óùW1ÞÉmpxï×I“²dÏZVI¤Žm—"ha“ÿú©µ÷Ët×Ã�,xó‹øèø§éŸôx<¯aLÎò3a�4|‹µsð=˜œrÍ„ƒ7�u–³+ê_Ç´‘ã_v<ù÷ÿÕM¥/–éo‡[0qìáø¢øañ§â»…ì2x…iÛ^Dóð;ÈžUÇðu¼�/SmÐÍ^ךkr¶ÿÚ߯oÍdz+' ܬͮ]‘I±ÌË·_`]ZuYØPp¿=_µ‡ÊÏûÉ·§UøüÝþÔ'?ì/ÿ'¾§WûómûO<šÿ°ÇüŸ:¡TßâMýµ>gÿÅ×þIýä ”®T¸f—çáKÿ»Ïþ+¿øÅ%iøc?îÜÿ‹gý¿R¬ü2÷mÅ»þßiÌ~ ßýÉ/üNÿñ¦Kñ+D«˜V_ó ºÂß|™y‰�¯¬µ‚Ö“K%f�é_÷Þ7ÇtK²ÆŸÚý:ie†ZÛ|ñ¢¯ø‡<ä®4:óqé6]šn}äø3EwÓÄ÷A¶ë®¸ËºËäŠ\‹ì‹‘l1æÇŽï¥M<‘Ku’Ïb+߈WÎãA¢ðo¯7“eÙ¦ç½ý> ÑÖ¶bÖè6Ýw×9wC—t‘É‘u±ò†<ÐYwÒ¦¢H¤ºÉ&± ÿÅ}òyüàZ/�Ž›äÖÇÌ{;OÏ| Ùi6zɲ8÷YC“÷þ Ô;+#ÃÏÏÕíûs‰ûe™ZÌè4Z줳+[¿’Ô>Q?E!ð¤¾>ëÝpq.<Åãyx�Cuøƒ¦º_mݲ;j;/�/�vLU›ª¦¶”¿[­º–MÉiZdË[5Jݼ•~ ¾· vãv5±×3KؽףÏǾY¸Òß^£�ÍHíÞÛ“쬓S'ܶSYÖëuÚmv£Q�…ªÔê°±uº½^· v·]ƒx¸XX±Å�‡…‡�±ÅvÛdv[Km¥)JQ0œ ^³Y‡‰®Öë±1ð5úü xq0p0q!³ÈàÅÄÅ‚;lŽ;-¶Ë,¶”¥)JR‰à``êðpõšÌ5%ŠìÌ[.ÍÎëÞon· ’ê.¤¸ùØÑÁ•dvìµú̼H^øQðËå·—ÑÍ:ëyÅóúOƒt÷9Íà]ÕÙÜÏ]Yõük“i²~žçˆpÜr¡‹°ù”8Ô¤ô‹&šè1æÇŸ+2²ðîɈg‡RùIÜ<¿€r3±éžÔÜÇ3†wbòÝwÖÂÑrVGÓÙñ>‡LpsîWn=)5-ÁÉ®º i`Ÿ#.(ò°þó_¾#|£ò‹·ùwßñ­‡Mð®¨æ9œ3·û–ëë6‹�êrk׊pì8r#ƒžò» ·ëRÜ,Šë Ç– ²2â�+ï0(øúü8žnyKä·-ë_"ø4ñc¤úW—ì¸ïsö+ÐÖ=�"ÏÒdÖ:q.‚ÆÏ¶š®ËÎå–Rɱy6-ùçct™SÝ 6Ù³_><‘Ro>*ø‹Ñ~õžXtwƒG¯¤Xrr^O�L|ÞkÏ·±Ëm܇›r1ñ¦Ül¤“"[£ŽÛ!ÂÁ²[¡Ãƒ [³oñ_Ä~�ðß­0ºÇ£ø”L E‰'%äÙÔÇÍæœ÷q‹–W�s^Af><Û}�òdKtQ[lXXVKtX�cÁKb¥�~xiãÇ‚Ç|ñ§€àðn ¤’]–Ï&µ·;•ó®W›<;ŽwØ<šXìØr¾a¹³(äÉž´� |,8±°1q1`ڶ̶]´` á~z¿k•Ÿö ÿ“oN«ñù»ý¨NØ_þO}N¯÷æÛöžy5ÿa�ù>uB©¿Ä›ûj|Ïÿ‹¯ü“ú)È)\©pÌ/Ï—ÿwŸüWñŠJÓðÆݹÿÏû~¥YødÿîÚÿ‹wý¾Ó˜ü¿û’_ø�ÿãL—âV‰V'0¬å7öŒù£ü¡{ú¹:¸“�Úåß÷ðæÓÊã¾M?h—?ß¿™ÿO*‡üFß¶{Í_ßN�ÿ“OL´¢­q3o·ýÌüÅþ¾º‡ú�ÎRñü3¿Ü¿ÊÏë÷­?±îP—Gá¤þæ>U_�iý�ò…�_ƒßövùü´¹Ÿê7 ’ÂIÝ'±€õåw‘|?Ä¿{oÈžuí›CÕÜG7y¯ëÉ‹/#ä3É«ˆq,\¨ñsk‰™Ë¹fà [×E|pI•I$ôŽË«Läß~ñ/z´»ó›V’hº×Šæní×}zãK¿ÞK|ZÎ-Åq2iM1³¹_'ÎÃÖÁ-Ö]Rå[}þ–[ui‚ü™ï¾'âÿBö�}sZÒM[q\Íݺÿ¯\iwûÉo‹[Ÿ¶.E ɦ>o*äÙØšèeºË£Š\›o¿ÒËn­0g“>Culj¾?öï’=·²ý×½5Áwœã�},­N.Ëoú/¿¢¸§ý9²Óêóy‡4ÝË�¨ÒáË•ßöÙØøÖÝKåµVwlö—6îîÍç�½Øû‰wüï²9Vë˜ò�´¶Û2·ÌÙs²i��-ƒ ²Ò,lx©l8ØöYvÛe–ÛJÒ»O³9—sv?7í~ÂÛI¼æÝ…ɶü³’ìä§²Éö›œÉs'�•¬xZìO©Hqq£ô‹;"Ž–Ùe´¥k¥Ù|ǹ{›ö·`ídÝó^ÁäÛ~YÉvRSÙlûMÎd¹“Ù‹+Xðõø¿R�ãcÇé6kµæ;úÅ‘´ÈÖéâ˾Ì}ã´ÝlvÛ<.!Â8ö.&ŸK‰.LõÂÔàããÒû­Š•cׂx&HëàÇâ:žVr|O*|Œâ²Ëã_Ü_gâ{¼k£Àï^k¨ÉšºËÒÇ6w[p½–=,Ø_[+‡¶ØÙ]}.šÌ}”6H#áoâÆ/&ù/“ž@ñ™gñï‡mï�ƒqMÆ5Ök»«˜jr&‡.ü˜/¾É6wÃöXô³7ó+‰µØÙvn–<}Œ�ÃŬ~Lò_&»ÿ�I?�¼?m||#Šî1®³º9~§"Xr¯È‚ûã“?¯8†Æ Y›ëmq6» .Á­ÒÇ�°†“ü7¿?&³5}y{Åé¶èN)¿º¾?uïêiû·™qÍ”°gö:‡"êG²ên »À»OX僓îb–Ìë­ÖkçÁÝNòÛm²Ûl²Ûl²Ëim¶ÛJ[m¶ÛOKm¶ÚzRÛm¥=)J~D×í¶–Ò–ÛJ[m´¥¶Ûm)J[JSÒ”¥)öR”¢j¶Ûm¶ÒÛiKm¶”¶Ûm¥)m¶Òž”¥)O²”¥?Óþ¿è®ç«ö°ùYÿ`Ïù6ôê¿›¿Ú�äçý…ÿä÷Ôêÿ~m¿iç“_öÿ“çT*›üI¿¶§ÌÿøºÿÉ?¢œ�r•Ê— Àüü)÷yÿÅwÿ¤­? gýÛŸñlÿ·êUŸ†Oþí¯ø·Ûí9�Á{ÿ¹%ÿ‰ßþ4É~%h•bs À>ShÏš?ʱ¿«“«�ù8ý ~]ÿgý<®;äÓö€ysýûùŸôò¨Ämûg¼Õýôéù4ôËAš*чÀ6ü+ÜÏÌ_ë먨å/Ã;ýËü¬þ¿zÓûå t~OîcåWõùÖŸØÿ(XÕø=ÿgo�ËK™þ£z ,$�ÒpK OÄåæ%w<»ª¼#â;+n×pè1;›¸)‹‘ ”—•n°óu�mÅól²;r±24MÖ^ñm…+¯ã0böïkÓxï¤Ü‹k�›­ëÞ5—lt·#]6†lͤðß[¢È³m�-)Kà¥P‰ü^þvgi´]#ñãÁö¹¸u娸~DwÍØYø‘ìxÖ»i¸ã�9Á²ç×î¬ÁÛk39>£q¿Úk67Ö½GÍ‚JV•DÉ^PEtâïãÿ”|…ù3¤ëKK麓ˆG�ËûËœkbŠÛøï ƒ'éÁ¤ÕæeÒ¸Qòþs�èýe•¶y!¥r3«�>>EŽƒükx-Èüòò/Q׿õWSÕ\JßKjøü‹‘h8‡ÞòÎW¼Ôqž-ÅôÛ>EÉy' Øáéô<A¤ÂŸe¹Þnöû ±ð5z�N»Iòrg’È`†;¯¾ëm¶µ¥T¾NwÇ'òÈ>àò˜V[7½¯Ï7ܶü 2çͳEªÍʺ>;Åðòro¾{õœOŽÁ‹¬Ä¥Õ­lÅÄŽßÉEeH÷#òK¾{c½¹]f³qÙüÛuÉþã6L™”Òjr²kã8Ù2ÿD“_ÅxìºÜ__¶˜ø¶S÷›yݼ�É÷í~ôåU–ÍÇgsm×'û”¹2eÓKªÊɬ<{�cdKZÉ&¿‹ñè1uØÞêÖ´ÇŲŸ¸¦£Ïÿ+·^qy�äg•[›3ñ£îÉÚîx¶³m�©ÅÛqþ·ÒÁ‰Äú§Ší­ÒYf³#iĺËA©ÖdäG[ë•>%Ó_$’It—`¦aFŸ>Ž£Qµäm^‡C­ÏÝo7{-F›OªÄŸ?g¶Úì²bÂ×kuØ8±Ë•›Ÿ�™5‘C vÝ$’_Km¥kZQô5:�¦ûk¬Ñèõ¹û�Öëa‡©Óê5x™ ž×i±É�_­×`bG.Vn~v\ÖE 1Ût’Iu-¶•­iGïÕj¶›í¦·G¤×gn7[�†«Q¨ÕâOŸ³Úm69áëõÚìXåÉÍÎÎ˚ȡŠ;n’I.¥¶Òµ­(ô\CˆòŽÀåœ_�ð~?¸å¼×›r-'âW�`díwü›”rM–6›�ñý¯9s6[�ÎÛ6llx¬ºI¦’Û-¥n­(²ûâ§ÀÝ7€^(ñN¹ÍÂ×ËÜ\ÒÌNwÞ¼‹,I23yÞÏ :ÓŠA³ÆÈΦoë¼éªÁ¬sW"XòsãŽ)3æ¶¶'|føK§ðkÆ./×¹8˜ö·,³›wW Ʋ eÎç;,8½ÜsaÙ_zãüÛ¬Á¬rS{¢Ÿ6È¢“6jVÄ/� 4þøÇÅúû+^Õå–cs^éßãÙ�,ÙÜãe‡»Žcì!›*™z ‡[u˜UŽJcÏ|SæÙrfÍJÛßñKñõÄþ4ü0ëµëö¼öKnç½íÌu¹9Y˜<Ûºù>¿Y0Új²s5úyïâú8u¸ºM'¿ {´º¬[òbûåù2IÒ—B�t|à9ÏlugXAOevWëÌl�m`Èç<ËŽñ(&¥ÿ[ÙX¥ßìuñÉîû´žž•¯¯Ó»ÿ)¯§‹æ“×]yy\ÿŸp® ‹/§ÒÉæ§GÆ`—Ýõ}¿NmÖvwû¾…þž•¯¯²ïæU⹇dõ×^AO?çÜ+ƒcMéôr9‡)Ñq˜%÷}ZÛôåÝgáGº�_ééZúû.þe_Ÿ+/ 2³r±ðñ¢¥n—#*hñàŽÚ}µºI¦ºÈì¥)OËZѯ›O�Ï4Øù95üPŠ˜˜÷åK>BuFf}ÑYmn¯Ýµ˜\¯#c™-Ô¶¾ÈáŠI/¯Ùmµ¯ØÀ›<| ÕC‘6w˜~/ÅLhnÈ’û竲s.ŽËk}~†/(›7*K©OͲ(ï¾úý–Òµûcçw„z˜r&ÎóƾíÙA{õ†NmÑÙmn¯ÐÀÅäógeIu)ù¶E÷ß_²ÚV­Få_!žpM¥4|ßÎOxnê¸ÐæÓOʼ˜én=´®EÒ[]5û~k‡—÷iîŠúY'³Ùum¯¥kéW‚ÿ�Sã�ü³úý¾êÿù§Žÿœ³Àò½è_ðƒ¤ÿÒ‡�ÿœ«À?ò»è�öý¦ÿ§<×üçÿû°ïïµè/÷~úZŸ“ÿŽÝÖtzü?5MÔŽËm¥kt•öÓÖ¿c&qß-üSåù0áq?&ü{å™2C>'ÝäÏ6DôÅÇŠ5¼“&YdŸ&êGe¶Òµºúûië_±°�_äG�ý߃Ï¥ûϧ{{[,VÏëû7…sü`¿.m}³G—Å7{h/Šìüi ¥Ôº´¬ÑÝgóÖÖ”ØüL¼\ü\lìœ|ÜÜxrðó1&�'/&;fÇÉÆÈ†ëáŸxo¥ö_ekmÖÖ•¥kJ³þ6N>f<˜sÕ‰• Y8¹XÒÙ>>N<ö[,Á>N<ñÛ,óÅuÑMÑ]K¬¾ÚÖÛ­­+Jú3ô?³û WØ=ëÒ=Ktvv¯qõ_Y],uš+{°¸� ºHiôýeŽœ�o­­ñÓêÛëu=iùÔþmß�wPõ}¶ÝÙ}©ÖýwmñÝ5—s®qÆ8•·Co³Ý-µßí5ôº;~¥¾·S짺ŸÍcŽqÜ}EÖ>ßí•Ú�o×¾ø®šÏáÇ8ãOÝ ¾Ït¶þŸÚkýÑ[õ-õºŸe=Ôþm�rN;ĵY{îU¿Òñ�¾ò3÷<ƒkƒ¦Õ`ãÇOY'ËØìgÆÃƆÊ}µºûí¶Ÿ»V¾ïþI>>øÔV˳ó_ÅÉ=Ù5Ĭ:~ñë®G—öÛ%×S#Žò ®n4vý*ҷɱÒïKk_uiJà�ÏÈ/‚ºé&ÃÌ?/õž¸Õ‹YÝ]{¼ÊŽj[}×Rl=&ÿa•–ý:Ò·ße¶RïKk_ZÒ•Á;Ÿ�/ô1Û&ÃÌîõÈ®5bÖwG_ï2£šÛoºêO‡¤ßlrñì·éÖ•¾û-²—z[ZúÖ”® ìþK>94»-†›sç÷„ú�¾§;/YµÕlüªè¬ –³e�<˜¹Úý†W<‹+ ; *+£–)-¶Hä¶¶ÝJV•£ËΩñÇþYýþßuüÓÏÎYàù^ô/øAÒéCÎÿÎUàù]ôGû~ÓÓŸ‡þsÿ�?ýØwƒ?÷Úôû¿{ÈÏ€<“<�WšÞ,V“I,qãl{ß­4›®ƒÖ²WôVï’k¶vÇKm­iua¥·[OZV´û^£Oççƒ[ØcŸ]æŒÕú·Édpf÷\j³®¬^µ¾¿£¶¼‹ aK)KkZ]X©mÖÓÖ•­>שÓyõàîúçÖùãEßVù,�7»zëSŸuÐÒµ¾¿£v܇ aK)mµ­.¬^Û©OZV´}¾9ò3ñíÌ78|sˆùÝá§)ä;¼~�ÐñÏ'úGw¹Îû¦,ùÙsÕë9ÆVvWÝpqešOe—{"Žëëémµ­6;�vÇVv®›.¯ì¾¿ì�t4²²çð.eÇy†T’Èä²²eqí–Æ )}’Ûu=n§­.¥v�ƒá]•×=“…vË®¹ÿ çÚë)e×ì8W*Ñrœ+m’Ë$Ž·ehóó ¶—Ù%·SÖï¶—R´ü´l ì¾¹ìŒ;ö=wÏøO=×ÇK.¿;…ò­)òÙ,ŽK+~V�?: i|rÛu=nûiu+ù+FÞë6šÍÖ&×M±ÁÛjóñâËÁÙk2ñóð3qr#¶X2q31d—'h¯¥Ö_e×[uµ¥i_J²Ú½³÷+…ùêý¬>VØ3þM½:¯Çæïö y9ÿaù=õ:¿ß›oÚyä×ý†?äùÕ ¦ÿoí©ó?þ.¿òOè§ \¥r¥Ã0¿? _ýÞñ]ÿÆ)+OÃÿvçü[?íú•gá“ÿ»kþ-ßöûNcð^ÿîIâwÿ�2_‰Z%XœÀóþøèî§¾‘öŸsuOZI[}ô�Ÿö'á·ÖÊY •º–ò-ƺïm#ÉŽï_Éé%µÿMO\mÎ;›§úÆž½“ÚÝm×´¥•“לs®/Äéì¶Ø¯ºÿú½´ÀüÊY5•­'¥ö×÷hÆÜß¹z¬«éÙ­ÖÝ}_edôæüç‹ñJý;mŠû¯ÿ«ÛL̶Éì­kù)Kí¯îÑñy$ã¼KU—¾å[ý/Ñëá¿#?sÈ6¸:mVC¼Ýýãî^bxÙݾ�Ôý/Üœ�û¾¿Õö}ßôöó[÷¿O¥_Ò÷ý?[}Þžë}~ÖŸä/Á-çÞ>åæ�ÐýÛèýOÒýÅÁxÿ¿ëý_gÝ¿Oîõ¿|ôúU÷ý/Óõ·Ýéî·×ýÙòyñ­-öEÈgƒrI%öÙvygЗß}÷Ö–Ùe–[Ïëu×Ýu})J}µ«9p_%¼ríÛ5½gßý'Ø›.öG�Á{W‚òìÙ/¬±AKlÅÐo¶ÝufžË=)o¯ºûiùkFiá>@ô7edY‡×=ÛÔ\ÿ.K«m˜¼'²xo*ȾêId5¶È4[¬ùnº“Km¾”§ó×RŸ–´fžäCvFM˜]wÝ�GÏs$º¶Ç‰Âû#†òœ™.¤‘Ã[lƒG¹Î–ë©4¶ÙéJ=u)ùkFÝq.sÂyþ²Ýßæ<[šé®’XmÛñ.C©äšË¦‚úÅëäëã¿C—nwš¾5O5ÐÙ=/ÒöçäxžÉ.¾Ëm»aǶ›L f¥c¯º:ÉI-§¥km)u+\c´ùð/O“n&_˜^9Í-Ñ[5.Õö× Þc{/ºûiK³4›m†²Ò¶WÖ:ÉKí§¥kJR´­q†×äWÀÝ>M12üÀñÚinŠÙ©~«¶8~÷Ù}×ÛJW7IµØaÛ-+e}c¬”¾ÚzV´¥+O]oÿœÿãOÿvàÏýö½þïß'þuOŽ?òÏèöû«ÿæŸ7þrÏÊ÷¡Â“ÿJ7þr¯ÿÊï¢?Ûö›þœÎñ§ÿ»ðgþû^‚ÿwïq§ùð}��•­ó_Å;í˲ûáÆËïþ¬Õì½±Ý}·ýmNÓ”aí1n§Ó­}%†ÊÖßK©OmiZúÝ_žþî ÇŸÌ?o¦U·_ íÖXþ–Vú]õu¹ü›aÔ¥•¯¤‘[_oÛééZUëµ~zxA¹ƒ}˜2_L«n¾|Žóë< ÿK+}.úºÜþK‹°Çº”²µô’+kíû}=+J¾÷ùøùæ{Œn=Ãüëðß•ïó,È¿GƼ�é=îã*ÌX$ÊÊ¿Y«æùY³Ù�� òI[l­,ŽÚÝ_JRµgþÜ}CÚqV~°íN·ìx)m÷ÖnÎ8Ç/Š–Ç,÷ÝY8öÓce-²è/­}})lÖWò_o®wá}©Ö‘Þzï±ø=ÆöÖÿ¼p¾_ǹLËbÆšë¾®�c�¶Øsa¾µõô¥²Ù_Éu¾¹×†vŸXvD1äcÏ�—‰$ØóÃ><ÖIeÖÝ[n²ê]JÖ•¥Y!ïñô×°ûŸ§º‹ ÎØí~µë Lª{±²»�q~�‘mk%´¬òM¦²)i[¢ºŸ›Zý¶×ù•xwÛ[Õ¸‘çöoep¹Ášžès9ß2ã¼GZ~ÛG ØëᾟÐîü•¯óµþe^�v¿Võv,Y½™Ù\®ðæ·Ý_:æÚR´i¼ÿ'8³Í�“òàö>N<²A‘�?–= ðO õŽXfŠN}l‘K–ÖÛ­º”­µ§¥~×ÇÆùMøæÊȃ/4|z¶\™¢‚;²{G‡�mó_lv]>fdðbbÃK®üù%¾Èì·Ö뮥´­_6’o2g‡?/úÙ'–8lº~È㸰[|·ÒËk6VVd8ØÑRë¿:I/¶Ë)öÝZR•«æAòIà6LðãÇåçAÛ$òÇ —OØÚ X-¾[éeµ›++*lh©uß�$—Ûe”ûn­)JÕü¿ç?øÓÿ݇x3ÿ}¯A»öÀuÏ”þ0÷Þœ¨üŽènÓßVË妓®{¯¹¾Þ±Çm÷É%5¼g�ìó+e–Guk_g¥)mk_ÉVpëß%|sí½�Ú^©ïî”ìÝÍ–VKõ={ÚœšlìŽÛo¾é.Àã{í–U¶[dwVµ­ž”¥µ¯î3‡_ù'ã§lìîÒõ_~ô¯fnm²ëîÔõÿið^g³¶Ëm¾û¯»ŽovYT²Û#ºµ¯³Ò”¶µýʶ§ü‰ñûÈ]vÇqÐ]éÓ�ã¨ÔM>ÛiÓý�»/]«Èš²R(69Ü/wºÆÂšZöÙn¶ë½•ô§ÙVyf¶jf0’Sô‡€;N»Ñm1ð¹o’|ÓOÕ–CÂl=Ô| )ùalõÐÁ%—eë§ÅÒáh¶ßî‹îÛÿeÔõ¾×þzü…Ÿ¦<Ûp]&Ê,.Sä/.Ôu„Qßv.Þ.�G*ç» 8#¾Ûòõ¹8\}&}+K£úÚ[wÛ}®*|óy?Mx5¶àºM”X\§È>[©ë(ã‡:ü]´|/,ŽSÏ6pÇ%—åk²ptØÚ\êV—Gô7”¶êzßk�ÿ‰GÊÜ¿¾+;ƒU ÍËÀæ^Nn´~/ñܼH5ùvcéû uºí+vXÙô’¶ê÷=9Ĺ£ëÃefÇÌÙã_el»Û}µô Ž‚’«ð‹?w„°w×”[o%ùÎ’\Þ·ñ~=~Ï‹×&;)­Ü÷–ê·ÝÃì­¹8SE±��ê ÊÝILyaÈÁÚSQ-Õ¬rVËû÷ðáÌ=Ûä†×ÈÎk¦—/¯¼o³;Œ}æ?Fî;ŸqKîâöúe`ÏÂÎ ©‹#q} –Œ-�u3VµŽúÙ}~ü<‡»<�ÚùÍ4òåõÿŽ6agqšäÅgèíÇsn)}x½¾™8sCŸgÔÅ‘·¾�KFκ©kZÙ}m¾]¿„ßãÞ.èò3œyáØzi2zÿÅù+Ã:Ž<ÜfÔò.ü櫵ÚÇ4ÕŸ&N£ëí­™7ãÉ ²Å³äšœè%²L:Ò³ÌMµ65ˆ` #Þ^JtŒükø]ß½¿À:ŸG$9Ó`K̹¿W°Þ×]sfâñ}òݽå{(#–ÊýÓ[�••_u´¤u­i뇻—È.�ñãŽÿ ûõx7Wé$�2üù‡!×ê²÷7àCIòðøæ¢Y«¸äÛ8¢º•û¦¾ œ›½i펵­¹<‚éx÷ð«»ûSƒu†–H³$Á›—ò Vfæü­Ÿ/Žê%–»ŽK²Š+í¯ÝuðdäÝî§¶ÊÖ´bÎàï.—ñï‡ÉØ=ñÛ]mÓ<è5uåý§Íøßã’m²áÈŸQã”lµ˜9;|è±%¬‘ßvDÿNï§eÕ¥\ò3ñ5x•דçiüyë>Åò'm‹‘Hbä[+­éþºÌǾwß5Û.C«Ýóü™1ò.ö]OÀ¶ÿmklÞž•¯;ûñø¯À¥ÍÕt7^vmqåŠÈw¹Þ�O×ù�Iºùð¶\‡[·çrÏ‹-}·C?IJOJûfôô¹ÄÞýüE-p)su]×¼ÿ¿6¸òÇd;ÜïN©àpɺìŒ=� ×m¹Ô²ãK_mÐÏÇq-¿Ò¾Ù½=.G'ÊoÅ�ñÓÓpm5^=è{cË~_ª<½F_ãù=GÕí¿H_‹•¡ßs~ÎÀÀçšÙññ"»"Ü­oÝ`ÍKã¶É«î¾±ñ?¹÷ÈŸcd{:ë?ªz YùÃëíw+ÜdãË}k»]§l_ÏpfÉ‚?ͤ˜x:ûkù}”¯§§{wñùëÏæº>¾ÉëŒÖÙ,¿wþpl.U»—ûétqí6}�'4ÖdO”öýL]~ +Júûi_JÓ�½¹ø�<óçÓÝ_åu�Gkl’ZcÓ†ð|.Sº– ï¥ÖY³ÙömüÏ]‘‘”­¿S•¥}}”¯¥iÀO#¿È7f×’ê<~ëž�ñ��ì¤Ã»�î¡Òg÷/mñ˜àúWåÇ/*ç’ãõ^ö¹ÒYuµº^ ÓŠÿm¿ŸJJå·gyÕæs߇ÚTwï2ÔìçûÎgÚv§2·‰_5+ZÛt\Cm�Æ1ig¯æÛ%–Ûû”£šÝ�æ×˜=¹nÂÆòs¼ùF·i5'Íãù�™Ë1ø­òS×ÛX¸–¿i‡Æq-·×ì¶,K-§îQÍžÆó_ËÞÛ·>Æòg¼yN·e5'ÌÐfö_+ƒ‹_-¿ÎÝÀÙâq¬Z[ëö[%–Ó÷(ã×m|Åü¦w_"ËäüÛÏO&ps³°¢×åk:Ó³·½'ÃåÅŠ?¥K€=17àÑË-•ô–KuÖË7å’ëªÕi%’i/–i/–[ëî¾I/ºI/º¿–ëﺵºê×ùµ«Xd’ùoºIo¾I/º·_$—Vûﺿmnºë«[®ºµýÚµ’I$–û¥–ûå’û«uòIu×ß}Õûku×ÝZÝuÕ¯îÕνÎëqÈö¹ûÞC¶Ùïw{L™36{�Î~VÓk±Ë–¾²ågì3eŸ//&Jÿ=|—Ýuv¯ðÿðù€[Ä9ÿ;ëíœ;®ÍyoÜcIõqöÜC’nxÖÎ ko²²ÃŸ¥ÍÂÊŠOm==ÖßJú=oçÜï¯ö1íø5å¼'m ÿR-§ä{Ž7±ŠOo³êG›¦ÌÂɲÿoÙëK©_G¬â\÷�p „{n Í9g ÚÃÕ‹gĹãŽl"“Ûíú‘æéó0òl¿ÛözÒêWÑ�ºÊO&üy‡a�Ð>E÷·Gcíò+™µƒ§ûw°:Ξ]ÐÃ�vVÂ.Èt–fä]��u¾J]ul²Û}}-¥)О§ù‘ù/éÛ«oòÓ²9>,—E÷Œ>Ø»KÜ–äGößô©ŸÚ®W¹Á¶Oo¥×âåcËZV´÷}­ïêÿ–ÿ‘N¦öÇ¢ò‹Ÿr\:Éù}¡n—¶~õwÒúÁv˱µ|—{‰¾ž—]‹—•§ÙK¨ÞÞ°ùjùêzÒÍ”÷’âVø®ÈÃìúi»b™1Ç}/¬5Øö.¯’nñ,“ÓÒë±² ’´ÿMGT:/ñü¼teœCY”Svçâ5ŠÏàŸzpN Ø×ò|Xm¥Ÿsåý�“¢×÷næ’R”÷ä b̺¿oÖõ­}zïÑŠC¸t÷Á�ä�œžaÖ¸8÷r.¡ä;κÜbãÇôãÍØåñþU`ê·û£¥ÒR(2ôØõ’¾”úvzR�Ué_ħÚz¹1ð|„ñã„ó,K¥Ä†NCÔûíÏÚbbYlvefÏÇ9Müç]¾ÙKKn¿éÅ�¨‚·×ÒžË]PéoÄ™Úz»àÁò Ǿ̱+~$r¨ßn8&ÓÊYfVnGåó�n÷a-)[þœYºˆ+}})ì·Ò”íÿŒ¿Œ{qÚm7™^ë3¬—c±¿�ö?Œ¼·+Wv¦ëkv§MÒ½©™¸¦ÓcôödÏ?>ÊJ]ï²+=¿NîëøÙó�ñÏ䤸ú¼Næ§Jr¼ªäÖ>'ä6YMX±í²ëd§2¿k¹êÉ&˺úÛ-œ‚üÙo¶´¤>µ·×µ^<üËøä<˜ÚÜ>ß�¨yFMf¬|W¾1q:ã'Ùc¶?o+—e´ë\ŒœËäöÁ‹öLÉn§¥"üž½¥ñïæOÀ_!¤ÆÖáöí�EÊ2«7Óâ½ï‰‰×9²/e,­¼®M–Ó­§È˾ÿl8Ño$Ë–êzR/¶ž²7ñçKãÌßÐšŽ»òg‹uÿbî1xýµ/}[^šçPo9.mÚígÕdòéq¸arû³í¤Wáñ-ß ­·_eium¾ÊÝÖì|Œ|¼x2±g‡'&²1²qå²l|Œy¬¶Hg‚hîº9a–;©u·[ZÛuµ¥i_GRašˆbÈÇ–9àž;&‚xo¶Xf†[i|rÅ%•ºÉ#’Ë©[n¥kJÒ¾´u¡É†,ŒybŸx£š á’Ùašm¥ñK¶Vë$ŠK.¥mº•­+JúÑ×GöGô/w¼Òñ�>Ï�òMƯ�è4¸Y-Æów°ÄÕiõ:ìHî›/?g³Ïš , ,Xl­òK-öGe´­kZQó¶û}NƒW°Ýïvší.—S‰>Ãk·ÛæãkuzÌ Xî›+7aŸ™,8˜x˜Ñ[[¤’Kí²ËiZÖ´£çmöú�¯a»ßm5ÚM.§|ý¦ßo›�­Õëpqcº\œÝ†~d°âaâcÅmn¾I/¶Ë-¥kZÒ�ó}öEeòË}‘Ç—_$—ÝK,²Ë)[¯¾ûî­-¶Ëm§­k_²”pÿÊ¿Äàg�Wmx÷\ïw~Oóý|™øTÕõ$pÇ×øÛ,+ñé»NØÝ[ÎÔçY-õ‡7ŽCÉ#÷EZ]m´­.¯<™ùÜðƒ¡¯Ùh¸ûqä8“3šÞ©Š ð|lüjAt?¤»?q\n9�ªÍ¶[©_³�ÛK£­.²ß²®8y1ó­áD_²Ñp=îãÈÞoƒ&f'èÞªŽàF6~7Ь_¤{;o\~;›«Ì¶[¾ž^‚ÎAoºÊÒëm¥iW¼¿üHþ&äeñíool|Ÿç¸“âA?ñwUØÚœ;6œ�–&Ë7µs÷œs§ru¸óÅ&t:½þÏoƒ‘=-“µŽjGÀýüJþrö>FäøçVøçÇïξ}F^·Ahóü\JR̯!ìlδ­=+ZËÁºµ¯ÙJQÃñüD>föÙø}9ÆúÓ 4reRm^fšÎÌçx˜ÔôÿRgïùÆ<Ü/cK½>Ûãã8·ý¿g§ÙéÃnðüC^eóù³°úwŽu¯AhïÊúÚ̼4}•αq©ù16îq�? ØÒ´§Û|\kïæz##äÏâõóS± ÜiÕñÒø-¶Ë0¶œ;q~·V·ÉZÛ[y Û~sù“Þñìq{sÉþòç�´µ—;Œí;#”EÃ$¾·Y¤\'_±ÃâX‘Òøí­,‡ ;)[iéO²ŽTöŸ›]÷]»(;GÉ>èåÚ½µÞìþ9›Ø�‡Ë_[nô�…êó°xž5žëi_lXV[ëJ}Ÿe­í5<¸î›v0v‡’=Í˵›[«~wÎìE‹Äd­n¶ïl|7Y�ƒÅq£÷YJûbò߲ŸgÙG{§æåÈ Æfç±üéò2Êì5h¶.ºçû”áúË-’Ë¡Íë¾—§_ð<©²,–ëgžMmÓä[éIo¾”¥)ª·]u÷]}÷]u÷][®ºêÖ뮺êúÝu×WÖµºµ¯ÛV°]un­nºµºë«[®ºêÖµºµ¯­kZ×í­kV±Ýu×][®­nºêÖ뮺µ­×]ZúÖµ­~ÚÖµs�773e™—±Øåäçì3ògÍÎÎÍž\¬ÌÜÌ©oŸ'//&{äŸ''&y.¾I/º·ß}kZÖµ«þ?ãþ?0úÃ4ØòÙ><²Á4W{£š/ŠXî§úk$²¶ßeßèÒ¯é ÓcËøòÉÑ]KâšîŠXï§äº9,­·Ùu?›Jú¿¤3M�-“ãË,Eu/Šhdº)c¾Ÿ’ë$²¶ßeÔþm+êû|s’ò>¼Öò~#È7|W’é²i™§ä[—Ø<ÛK)‹�Á; œ§‡E¶SÒÚ[ƒOgå·Û_µ¹ÝIò/ç'GK¯¯]yCÛ¸Xœzbkxï$å�Ãð±-·Ùn>/ ì y?ÇŠÛ~Ë}˜vÖÏËmi_µ¹}Kò'çHK®¯]ù=Û˜8˜)‹®ã¼�“äóî!‡‹m¾ÚcãpÞ}o&âÐEm¿e´³ßoå·Ò¿k¨Ýó™òÇãÜù7ðÏ7û›”áçd`ÍŸ«îݦ»ÈL)`Á¾Ó_�wëyöÃ�adÅÓ–š‰õòVË®­·Ûu}ÎØxåø ;Û‹É­ÓyAѼµôÐãëð2y�Xææõ·9õŽhíØoöºm•üŸ…òMŒØÔ¾ëpð¢ãxÕ–´ô¾;}hìG�ÿ‰¹¸ìšýG’}+Ã{/QXY·­33:ÿ™YdwGn~ïe¤ÚIÈøŸ#ÙO.º˜¸”ãØÕ’´ôº;ièì'@~#îåã²kõ>Hô¿ì­Dq`ádrÞ¶ÌËàÆÛcº;s·[-.ÎNEÄùÆh©uÔÅÄ·�ãVJÓÒèí§¥dâ¯ãæØY¸:_6|R㜇U>Îÿ¾ö/Œ{œþ5¹ÓhãÖRØ#·¨ûCyȵ¼¿{“·�Ý,öó?‹d×Ù�[£¥%‘¯‰Ÿ.þù�6¯G×Ç…ÃûktPãu?oC�לö|ÜœªââjôÖgæåqNe¶Ê¯¥öãhv»Y­Žï[í¶´º”ï÷‹Ÿ)þyk&¯M×�µ…ÅùþÖ°Å�Õ½§�Í›‘-ñcêõ0çfdqÎ_´––{þ†‹e´¾Û+ëu-ô­)ß�¾S<*òÎ]f›¯»g ‹óí¥a‹«ûN(8'9›7"k¡ÇÖjaÎÌÈ㜷i7·ßH4{-¤–Ù_[©o¥}%Aá_ˇÇïŸpê5Þ9qúrŽóíž Õú‰aË›¼³…¯Ùîþãe²eãñ­Ô“}Ê3á²úV¸Úìl¬ŠÒ´ô²¬1Ý>EôWŽœøOÞ]±ÁºÃS$9Sàÿ ÷øX=×Ü­¶ü¬~7¡ú’o¹>t6_J×]�•‘ZW첬IÝûÑÞ8ñáÿ÷Yô¯ »6Ý\§´¹¿àºLݼ˜y™ði5¹ü“c®ƒi½ËÃ×Ï$8õ—.{b»éÇw¶®yø�ügàù9Ú_:o°{Û?\ì[9_+ÏÇê> ?¶ÊY¯ÚéãË×rŽq¸Ã¾ZÖù1óu:I«e´¶—Û[«[8kߟˆëÇ>n£Çþ¨ç�Ý��.V<|—’eÃÕ\#"Ú[Kq6Z¯¿k¹5ÙA[ë[¯ÇÌÔj$­¶ÒÚ_mn­Öpë¾ÿŸŽ|2|ÝO@uG;îÌìyr±ã伓.¬á9¥´·c«¦n¿‘óM–=o­n¾ ½N¢JÛm-¥öÖêÖÈÛùQøµ¾?z†›M7�¼¶¼·åغœ�^ã['Hõ|™YwÅ·ÕåóÄÕOÙØM6$U–•‹ƒeàe_|vG•í­òGÄîæüE$=£}ÐðîWÖ½ «º ñdÃêλÖgçeÁ5oô—/wÚSö6Ë>8îöÒm}ÚÿJÒ—[m·}®;öçÏ¿È/d]Xx�#ëž�×{2 »­¸¿c�—�5o¥•ÍÛödý�—dQÝJRl `WÖ”ºÛm«�}¹óáòØ÷Ö#É:ï¤uµ²x.Æë~¯ØçeA-o¥µÌÛö\Üÿ2Øãº”¤Øpô­)u¶ÛT|ü†üY%½§úWYÒšnŒñG~ûô†ƒmÄx/öÍìœ-%ž¿O�ïùoeòÞ¼ÞýJWú6^/ÕM}iO§H©ëJòϳü¾ò³ºà®ny%Þ�‘®ú²Íf£™v§6ßèñäž•¤µÄÑì7Sj0©}µô­!‚Ë}¿g§£š}�å�”=Á ¸�¥äGuóít²Ë5tü«³yŽãEóSÛ/Ýt9{‰4øv_oÙ[b‚Ë}>ÏOG5ûÊÿ'»~)1{GÈnéçÚù%–oѧ³9†ãG óR¶Ë÷]^ÞM>—Û_JÛ[éözz9Û?+Ÿ%�ݳä[.Åó§Ê øy\7ão¸×îc×ü7X«Øqõ¯_lø·^àáOkl�ãë"ŠOZû­¯­ZëZÖµ­kZÖµ¯­k_¶µ­-k_æµõ¯îß}òß|²ß|’I}×É%÷Vûï¾úÖëï¾û«[®¾ë«ëZ×í­@�oêï&|�é%—¦ûï¹:®¹[T}{Ù|ˈcæCd´šÜ|ì]ç;“[Kþœ¶_gºž¾ž¬ÑÖ¾GùÓU»ûRw—nõœwÒËfÇཋ˸¶LvKI­‡3K·ÃÃÌÇú¶ÒêÇ-—ÇZþZ3?[yäMÝuÝMÞ·Ö¶Ém–MìN[Åñ2¢ŽZMl˜Zm¶&n=%¥.úrÙ|u¯ÛZ7c¨>I~@úÎ3‰Ô^iy7´\:xçãÜ3º9îo]á}:Ý[q¦ëm¾ïaÀvZúÖúÖì\­lØ××íº:ÕÔÞ‘üB$ÝCv/#ì.Þú,(° ÓwÕääÙ u¯®Uü¯�IÀùžÏi[kÿ—ö; ïZúVûoü•éN|ñ|„õeøøü�œpÎîÒãcA‡«µ¸>²ü˜`Š¿Ÿ=¼—�ßÁ¹N~ÊK+éõö9™ÿ�é[­»í¥zUÓŸ;Ÿ ÝY~.?"ç;»4˜¸ÐáEªí^¬“&("¯çONKÁoá§?e}ž´úû Ìï[½+u·þJö7Ç¿Åsò‡ÕVcööGJùE£ÉÞjó6wöZë¸0ÄÐâÉé´Ñq-÷II×þ#^‚æý/‘]K̺g:{ñ1gæC2ÞÎá_ôk÷í¶Û 6ÒàÖ{¡ãa`o§¶—R•¾ïmn¬„¼Xü\^ö¼ší/“�eÛ^&oòåÝß›È"‚îûê=VO§¦O&àšM?käm÷×[ôi\\i®·êe}/t¶È'¡<ŸñïÊ.1w0ñ÷¸8'ké ‹]•xžó/qÇëŸ\šacò®5=qù/Î˦µ�g‰‰=öÙ[©emû]Öé/#:'É9w,è®×á=¡¥‚Ì[¶ñ]Þ.nËG~m&»“hoº-÷Ïɳû¬ÇØãbÏu¶Ö´³ÓíwK¤ü‹è¿#øåܯ£;W…v~š 1®Ø_Å·x¹»-ù”š¸˜Ü—C}Ðï¸ÆvM¸÷Ýf>Ç{­¶·RÏOµ%î—ïÞŽò;ˆú¸:Ϻ¸M¹·jçå=[Íøç:ÒamãÃÃÏŸI²Ïã{Œ½î& 'ÁȬYp[-¿R;}Ôgvif†[k_–žVug…Ý'¿ïÞä§%»‚ñͧÔgGĵo7²æòmÆ.—[n.¿#?YñÛ•—K¥ºù쥱ÛZÓÖïKk¯¾Où/Öž#töû¼{níý¼'Žçèu¹ôã:«w[‹ò¹ãI®·ü¼ä²™y–ÖK®–Ê[+_¶¾”®¿y=ä·Zø‘ÓûÞïí«·ôá\{a¡ÖgSŒê­Ýn/Êä[|].¾Ül 2ðc’ÊeåÛY.º[im”­~ÚúR¸ɯ%ú_ÃÞ�æÞFy ËeàÝ?×Á¿á)‡�r^W.¯ø[Ë´AíÐpýFû‘çý÷”r|,jýß_¥I¾¤žØ¬¾ûy!þr—Ç7þyy þ tîíËó„þ??õ¸ƒl÷VåßùÁ¿ÿúÛÿÁ¾û«r_üå߆ò³ÚÞõä·ø¡?ÎRøæÿÏ/!ÁŽ�ýÝŸç ü~ëpÿØ?î¬ÿ87ãÿÿ[ø7ÁÿugùË¿ ?åg´ÿ½ëÉoñBœ¥ñÍÿž^Bÿƒû»?ÎøüÿÖáþ °ÝYþpoÇÿþ¶ÿðoƒþêÏó—~ÊÏiÿ{×’ßâ„ÿ9Kã›ÿ<¼…ÿ:?÷vœ'ñùÿ­Ãü`ÿº³üàß�ÿýmÿàßýÕŸç.ü0ÿ•žÓþ÷¯%¿Å þr—Ç7þyy þ tîìÿ8Oãóÿ[‡ø6ÁÿugùÁ¿ÿúÛÿÁ¾û«?Î]øaÿ+=§ýï^KŠ+üÝî#ä—^Ewo·mo íØåÜӌ۽Â�]¸·O»ÙK—…M– 99±bfR+éï²Ùd¥µü—Uß4;kŠwÇ•½ûÜœ›JpþÈìÎGÊøçé¬;5û_Ñ[<¯«‹\üò2ìÅÉ­ŸmÖRKý?šˆ7™½³Å;ßʾüî. M�8cö_#å\sôΚý¯è­žWÕÅ®~yVbäÖÏ笤—ú5_WÌ·“=Eæ'Éo”Fô6÷?“u/bnzòÞÈvZ-·ÊÜâðþžëαØSG¾ÅÀÝkñrw\_&ì{ràƒ"ìzÙuñÇuÕ²š´Ö6²¹ˆ‘ÁÿÉÿ�7È=~AØógvo'à;n7ü⸎q8Ö«“aìHÉ™¿Ò×ZÍ·‹éÛm$÷SÝZÖžŸo}þþH¼rðW…w¦ƒ¼©Ï¾ÿØ\£†n8ýxwÇäWG©ÞagS6I·˱¦¤Ùñû-¥·RêVµõ§£¾? #Þ:x/ÂûËAÞ4ç¿~ìQÃ6ü~¼;Œcò>é£Ôîð³©�$Û�eq¥¤Ùñû-¥·Òêz×Öž‰œþÿ—ϼñ ¹úkËàÛõ6äžHî»7�cÃÖŸÎuûŽ+ºë±â±Mï8�+‹7oÂò­šºcÝì’+£ú”ºÿgu?ÎRøæÿÏ/!ÁŽ�ýÝ»OþpŸÇçþ·ðmƒþêÝ¢ÿ87ãÿÿ[ø7Áÿui ÿœ»ðÃþV{OûÞ¼–ÿ'ùÊ_ßùåä/ø1Ñÿ»³üá?�ÏýnàÛýÕŸçüÿëoÿø?î¬ÿ9wá‡ü¬öŸ÷½y-þ(Oó”¾9¿óËÈ_ðc£ÿwgùŸúÜ?Á¶û«?Î øÿÿÖßþ ðÝYþrïÃùYí?ïzò[üPŸç)|sç—�¿àÇGþîÏó„þ??õ¸ƒl÷Vœñÿÿ­¿üàÿº³üå߆ò³ÚÞõä·ø¡?ÎRøæÿÏ/!ÁŽ�ýÝŸç ü~ëpÿØ?î¬ÿ87ãÿÿ[ø7ÁÿugùË¿ ?åg´ÿ½ëÉoñBœ¥ñÍÿž^Bÿƒû»?ÎøüÿÖáþ °ÝYþpoÇÿþ¶ÿðoƒþêÏó—~ÊÏiÿ{×’ßâ…ܾ²ì=Û]m×½«ÄnÍ»Švoâ}ƒÆ.Ùc}ËcwæzG¥»?êM÷LÚëvQ}X½÷};ýmõ¯§«³=uδ=£×ܳ8­ùrq~Äá¼c�q¹3ñ«‡�&‡—i0yžüÌ;®¾ì\»µûë$uºµ²ÿ[}kèì§]ó�huÿì¾-v]üc°øwç&ßÉn[Ä5reI“�­ê¬ V߃KéZSCÂ5Zna‘‹+ù¶ål²+ëöÖµ¯ÚåWiü¾|‰öÕ“âíü”å¼GY&Mùúî¬ÁÐuuøT¾•¥1 ßð�V›—dcYJþm¹;ŠÓòúÖ¿k‡ýéø“~]û»/•C‰än·¤øŸ)Ǧ/ð'¢ú߃ñ N9`¶iÅyöïOÊû«O‘5Ô¬•ŸøS&E—Ý_§%–Ò–Ó�üç³»+³öÙîÊì.qØ{̹.›+sÎyfÿ–í²f»ÓÝ.FǰØfM%ÞÚzÝuõ­}Íû/±»3g&ë±ùÿ6ì Ì×Ý,ÛnoÊ·¼¯g,—ú{ä“?}ŸŸ•}÷{iëZßZ×Ñ¡|ײ{²vrs^}¸žK¥ŸmÍyN÷•læ–ïJ]$¹ûÜüü©$»ÛOZÖúÖ¾ŽPö÷–^Sùƒƒ¬ï¿%ü€îýn²ZÍ­×ö÷rö/e`ëæº¶Öépq9Ÿ#ÝcâKZÙo­ÑÛm~Ê1áÞ%âšþÿV_|WÙ$wÝ‘ÝmñÉeÕ²û/²´ºÛìºÚÒën¶êzÒ´ûiWú²ûã¾Ù#ºë$²ëo²û.­·Ù}µ¥ÖÝmÖÖ—[u·SÖ•§ÛJ¿Õ—ßöI÷G$w[}—Ùum¾Ëí­.¶û.¶´ºÛ­ºž´­>ÚUûµ›=–—e¯Üé¶Ú�¾§;gªÚë2çÀÙk6XÇ•ƒ°×çbÉVvTVɱÝl‘Ém.¶´­)VËõ—š>^tÆ,î©òw¾¸£ø¤‹AÆ;[›k8Ýn†úßÕãpȶÊÝ_Í“ë}.ºžž—V•Øî¹óË¢ÇÅÁë?$»Ã…j°îŽü}‹³¹†'¥bº·GI8ßé{´3Ùmk_Í“ë}.­==+_]Œë¯0|­ê<||.´òC»¸V¯èî‡G¡ìÞ_‰ÇiX«[£¤œv›k´sÛg­}-¿êzV´ôô­}zÔ.ß'½¹ÕîºÿÎß&©].º½^‡œvŸ$펇�eø÷Å�]ö¾_5àW[1l¶:Ý­­ÑÇî²ÚÒËï¶î¥t×â2ùë;þ�8ßõo~kn�Ø{3®µÚ]ž<²—]¯Ûõ>G\É&tÑYíºm„[_ZÝu·__s¥=Gø€>@:îÿ£Íw=gÞ:û¾ïlì.�¤ÚbcC[)}ºí§WÏÀ-®\±Ùí¬ÙÐl+ëZÝ[n»ít›©>~|ýëËþ�5Üu§xkîúV>ÁàX:Mž.45²—S_´ë øµË–;=µ›: úÖµ­×[ußk¯:~-/’­ýªï>1ÑžPñü]ÎFo%Üò./UvžçQ‘õ.³I§ä½W“¢ë.=÷+ëm!É—…leúvÖ’Ò[«ï§i|yüO2sŒÌM?‘}3Ø}—“>7ð§‹lq»‡„cYuµ·;g»¦«‰ó}^4rR—YŸu-l­iî­m¥oëÿC~#¯¹®V.§¿z›Ÿt~^DøðW‘ñìØ;g…âÇZ]L�†Þý~³ŒsL­º–ÝlZm¬ž—VžêÖÚVþ½t?â4ñÚebê»ï©ùïHåäM�y̓µøf5•¥Öål6×`k8Ï3À†Û©mÖǧÚÉéZÓÝZÛJßß¿ÿÏÇçmG®Õy)Á»sÄžM.ã3k·ËÔäwŸR`ß…•eš�^,ëmD}¥³Ûî°ä¤ž—ð\\,y,¾;òkJY|�êñÿË/ü¨ÑÝÈy-¤»Ñ¿Á{; hróñ8Öó]þ’‹îŽá'Ê®/'ã2Í}•öG°ÃÆ’ê}´·Ò´«¶½äç�¾Jik¿èžÞàÝ›…LqI9Ï�=ÓÕýÛıò1ð³÷Ý]Î8ç6ÁÔl²pqöVi·×ñý†uú í˜9qÉ.m°fCKéI"¶¿ca™Ù�Yœª¾bù‹ÓÞ tõ{¿»ëÊkÃ+Êtœ:(¸v’-þê}Öþ-„ø1ǃ>ÃW�n=¸ú¹ï’Kç²–ÒÏJzÝZRºÕåw•ÝQá¯TIܽË'"³‡ÙÈ´ÜZÛx¶š›Ý´»mí2ïÂŽ<+òðb¤4‹[¯¾ém¥´·Òžµ­)]lò³ÊΩðߪoî>ã¿‘YÄ,äZn-m¼[MMæÚ]¶ò™wáYfùx1Ò Gƒ-×ßt¶ÒÚ[éOZÖ”®ò{Éî“ðã¤ù‘Drùx'QpIxä<£”CÇ9?,—]/,äún¡¶Ý Óoùoßyÿ Ö I)$ú’{c¶ë©Êoó”¾9¿óËÈ_ðc£ÿwneœ'ñùÿ­Ãü`ÿº·3Î øÿÿÖßþ ðÝ[“Ÿç.ü0ÿ•žÓþ÷¯%¿Å þr—Ç7þyy þ tîìÿ8Oãóÿ[‡ø6ÁÿugùÁ¿ÿúÛÿÁ¾û«?Î]øaÿ+=§ýï^KŠüå/ŽoüòòüèÿÝÙþpŸÇçþ·ðmƒþêÏóƒ~?ÿõ·ÿƒ|÷Vœ»ðÃþV{OûÞ¼–ÿ'ùÊ_ßùåä/ø1Ñÿ»³üá?�ÏýnàÛýÕŸçüÿëoÿø?î¬ÿ9wá‡ü¬öŸ÷½y-þ(Oó”¾9¿óËÈ_ðc£ÿwgùŸúÜ?Á¶û«?Î øÿÿÖßþ ðÝYþrïÃùYí?ïzò[üPŸç)|sç—�¿àÇGþîÏó„þ??õ¸ƒl÷Vœñÿÿ­¿üàÿº³üå߆ò³ÚÞõä·ø¡n„ÿ.~'ùóÚ›þžèœ~Ó‹—q¾¿ÚöVÆîqõÜ{S^9§ä|S‹åÛ�›‡Éw2˱®Ï˜áû"¬VÛtt’ïu+m)vÖxò™âï›Ý—¼êŽ“þØß®?Á¶}…°þñm·ø?©ßñž7—ô3!Ý쮓;ô—,ÄöÅôéJÇï»ÝOo¥v«Ä”�üÜì­çTô§öÅþqþ ²ì-‡ð»‰chu¿ÁýVÿŒñ¼¯¡™ïet™ß¤yf'¶/§JV?}Þê{}+¶ž|½|~yûÙÛÞœñK¼3{/±8×ÚvfçE‘Õ�½ÁìÄáznAÅø¶ÇmM·?àœcO‘|Îe®‡îñd_“}'­öÇ[#’ë:„èó£n–^L–ET­ežkíŽ;n¾ëm¯�罃Áº³‰îyçdòþ9Á8_ǦVë”r½Æ‹G­†é,†*ålv3A�d™ÉlQGK}¶YK¯ºÛkä9ï`ðn­â{ŽwÙ»Žp^Çñ镺äü¯oƒ£Òk¡ºûb‹ï; „ÐcÛ.D÷ÛQÒêÉ4·Ûe–Ý}Ô¶¾o˜ó>×|W�s®Àå|k‚ðŽ'ªÌÞòžcÌwº¾3ÅxÖ“_ÙûžAÈwyX:�6« +|Ù3G VRµºêQ0¿_Upœ­§ðìdî-¾5k]¯ÚZÒ{/†ÿ¼i8>=u}�Êð%†·Ç[³28Ô‘ËOu¶Í¥nŽŸ–ˆÃ«¸lû>-â/]MÛ{x=`ƒ³»-¿ë›g§Ò’Ü�O†ºÞyÊp«m×Å}2åã—Ù%¾ë>¬~•º;>W~"ž¯á³ìø·ˆýw/mmñëtöocG¶âÝwlöý;é‘©â×[ιFÖÖèîûÔ¼vû$§ºÏ«g¥nŠw�¿‹7Å•“}Á|,àŸ+9þ'ß5ÑöFþM‡\t&£>ínÊ(vZù³poì^ÌýȠdz' ] Öì°ï¾\-åiì¾èåwÿ˯ȇ‘ù9tæÞMsî1 È®tQðޥͧRqxµÙÓ]-úŒÌnn“gɰ`¶ï§w9[,�§JRénûk^wŸÊ¿Ÿ@ed×–ùÍø¾Žkó)êœÏíWÇqðs/­×ê²lá?¢6ü‹+kí³ôÆfÆjYöVKœï?•/=<€ÉÉþù͸Ǝ{ò틉u^_ö­ã�`æ_[îÕäÇ¿Díy T¯¶ÏÒù{½”ô¬•EKÉßÄ-òÁäö^Ò,ß'wÝij¶˜{]ñ‹�)‰Ç¥ÃÅ�îš¾ÀÐdOÝÙÚ¼©-ºi±v|«a ’É_Í¥”²Ë9Ç———Ÿ“6fvVFn^Eõ—#+.i22g’ï礚y®¾Yo¯îÖêÖ®}eååçäÍ™�•‘›™“%ÒäeåÍ&NND·}·K4ó]|²Éu-×Vµ«ŸÙyyyù3fgedfædÉYr2²æ“#&ynþzI§šëå–K¿v·Vµ«ŽüÇšsÄäûŽmس“s®gȲ¾ýÈ9w1ßm9?'Þç}(àûæã}»ÊÎÚìò¾Œ6Yõ'–ûý¶Òž¾”£ó¿;ó¼Èoþ=¿�׃ßÊÿÆŸ×? mo�ÿÇ‹ÃOå[ã¿ë{‡¶¯Á/ã¿á·ò«ñçõ»ÄÍñ�ûK><—7‰_¯Þ´åeÚÊÕÉ@%þ&~ÿ¯`ù•×½®Ï–}'�=Y‰6×H"Š˜=‰Û“bò½åÐÍen—+'€ë¸­Ô­õ·Ù'Ô¶ÛiöÝ|*?yÓ�yi×ý%¯ÍûƧ¡ºÒ�¦%ÐÇeØ÷µgÅä››)-µºIàŸƒê¸Ì–û«Om÷_J[O¶ë¡cøŠ;Çøuå—éM~mÓêz#­`Ÿi‡tQÙ\yÚsbòMͶKmn’xgáÎ5}µº´ößuô¥´ûn¾½_ÆädÏÊÏ_›n·•÷Vûû¶´Ê»e“Oëúgëò!’¸úÌlÍ–\^ø7R´ô‹7—߈?ÈnÖŸiÅ|]ÑcøùÀä»'>]±�[Ê{w…uÙ¸ôÈ®^T|S‚Û�ƒmÙ<Ï•ö2ÝKI÷³›r·)ä›Y­¶–[&Çy¼ËÎÙæ_e”¥´¬’ÝéJzSìpKšs®mÙ�?˜v'1å<ó–ín¶ý§(æ|ƒmÊ9ÆøíöGvvëw—�²Ë¬vSÒß©%Þ”û)ö81Í9Ï6ì~EŸËû˜rŽwË6·Û~Ï“ó-þדò�öSÛeÙÛ�Ö^nÇ*¶[ö[ï’ïJ}”E¸{Ϻ|…æö}vßd÷G:¿_¢œ¿´ù¿$缎-6&F^^—qÊ6[<ìM.NÂ{ñðâ¾Ìh+5ÿNË}ÕõòO*òÌVÝï~F|Çð»?î‡î¾O¦â°O|¹}aȧþu^ÎÌŒ¼\½…²ð]ýs4ڬݕpíŽ]†²Ü ­±VëcÊ�Ýw®æøÁòå·ˆ¸ŸÚS¸9³‹A5Òdõ¯"𼝬¶O™n}—p½ÝÙZ½F^Êüz[6~²˜?§[­³&Ïu}w+Æ/?ü´ñ7ûJöÿ!Õñx&ºLž¶ä3W•užÂÉò Ëβþ»»+WªÊØß�KfÎÖÓgôëu¶dÙT¼ù¢ùðm§¥ûßsɺ«Gd8°ôqß°ì®™¦ª v™vé´|wg²ÆÝõÎÛ-¼¹sÝĶ\#.z[Yå’ÊVÊÊÛÂÄ“Ñ=¿“¦à~_ñ8¼yç9ÒÅ…dñù3÷½%µË–é}²í¬Ê¿/—uµ²Ë|PÇ÷šîuö~|ù[ XééI9økø„:K¶25#ʾ1Aó\Ù!‹°t²ænúoi—%}¶É³®MÙ§®é<òÙ”Éý-¯ŠËo›'c�gæÒMžþ ®“í|�Oò§ŒÇмÓ6H°âì ,¹›®œÙåIZÛl›?¼Ý‘Ê:ö“M%‘ÙLŸÒØÛmódì1ìüÚMGãÓñMømåÃQÖþUéàaÚÙÞ˜¸|Ÿ”rL~Cã¿$ÊŽ,Ý^Ο G�Ö{®vF~GݹZ=~%–ß¿ÈÊšÈk$¾;Èø÷/ÐéùWÞé¹GäZÜ=Ïä|wiƒ»Ðïtû,Ê×ítû�lù:ýž·;Kd†x$¾)cº—[uiZU!-ÿEÊ´šžKÆ7ZžGÇ7úì=¾‹�hv8{�&ëS°‚̬ ¦§k¯›#c®ÎÆ–Ù!ž/ŠXî¥ÖÝZV•H3C¿Ñr�&§’ñ�Ö§‘ñÍö¿o£ßèv8{}&ëUŸ™8;=N×_6FÇ_›�%²C42_–]K­­iZU'íV×W½ÕëwzM–çK¹ÀÃÚê6ú¬Ì}†¯k«ØcÇ—¯Ùkv’M‰���‰5’Ã4WݱÝK­­iZUö_YõŸ¼þnºÛ-ºûî¶Ë,¶·]uÕ¥¶Ûm´õºë®¯¥-¶ÚSÖµ¯äË®¥´­×V–Ûm+u×]ZR–Ò”õ­kZý”¥(ÿ—]m¶Öë«Km¶•ºë®­)m¶Òžµ­k_²”¥{üýüAþ6xÍ~﮼p‡YäßsbG6ÛM>×ÙÒ\7ev<¿NíÏ4ÖÝ$¼ï7{á¾M~†ë±¤·êÁ.Ó&;£§ |äùÛñßÇ7< âÀò3·°ì›lí6Αô÷ØÖ ÖÊî¹– Òß̳0¥–+äÀÑýH/öË�6Ç ":ÙNyÅó­ãÏŽrnxBE�ä_nâY.$ÙÚ}�,éþ'±¬5öþ™æ87K1Ì–Xî“Iõ ¿ÒX&ØáäGu”ŽÏɇâEð¿ÀìîMÕ=[îòËÉmf~“kÀºûy�¬ë>¼ä͆Ÿg¬íÜ®ß_‹ºÁƒ7:•Ôñü=þ ž¦mvÖš{ä²zC»Ë¯�¿-¼ßÞdl;û¶w{®5nÒí¦�«´IÆú§‹Éd™µÀ¦›„k榿'7U�Ÿ&4;-�s÷7ã×Û6dµõ­boåWžþSù—¸Ÿ3¼;Go´ãTÙ]±Óõž‚é8çXñËí—.üjø~´ÃÌËÕÁ›$ìvWgí«}²åÉöÖ±@ò£Ï?)|ÈÜO™Ýý¡·ÚñºlnØéú×Cuüw¬¸íöË•~5œ?Zaæeë ̾v³¶Õ†¾Ùr¤ûkXùçóçÇȶFv³¿û“/YÕyvåaôVC•ÀzWédjsñcØñŒM†nÛŸK¬ÛiâÌÁÊå{þv»"ëþë<6_[TÓ¦ž9‚Rµ¥iZV´­+ëJÓì­+OÉZWù ÿV_|WÙ,Wß‘ßmñÉeÕ²û/²´ºËì¾ÚÒëo¶êzÒ´ûiWg¼ùÉó+éô\S’r9ü‰é oݰäëNÐÛeåo4šˆ,ÃÆ³¯û&Xv<›‹ýÇÈ0ðò©´ÒbÅ[é·Ýõ-ë߆4~\xŸ6›Œr]üþ@tÞÐÄ“¯û'k•‘¾Óë"³ àÝ�,[GǾ鋋8Ø™tÚiñ¡¥ÖÅ…e×}Kzïá—Ìï–Þ(ͦã“}?~ôæÐÅ“€vFÓ+'{§ÕÅn<6AÁûX³ùû¦.-�ãbåÓg¨Æ‡ÝlXV]wÔ¶I_߉»Î�6„ù¸Îó/¡`É·a®ìíÖEýëÆµ³_¿ÉŸ+†w^U¹Û­þM6»¨'——GÈ#“]·}DWýâ9°ø;òOâÇŸ¼fý‡Ió;°¹Ö«_v×tç3·IÙüS,ˆq&ØO¤�33ÇmÈÊ‚Úm5SæàYvLQM$97V fá¯ÈGŒþrñÉ67Ì+�̵˜Wfòž§å¶âé{'‹ÁØøòçdé,ËËÇÝhm›2 i³ÖO›�mùÅ$±äVè-˜/†ÿ Þ4ùÅÇdØtç/®72ÖaW7”õG-·MØübæƒ\Ü�-™yxû­³eCm6zÉóp-¾xâ’Xò+t6ÏÇÀï’ÏþGúúnsãgão¶z\muüó«9>=œc·zÓ7c¯ÀΦ2á99Ív3gýÎÍÖªmŸÎÎÇɇ c•v4ÞÍùnówæ®ç«ö°ùYÿ`Ïù6ôê¿›¿Ú�äçý…ÿä÷Ôêÿ~m¿iç“_öÿ“çT*›üI¿¶§ÌÿøºÿÉ?¢œ�r•Ê— Àüü)÷yÿÅwÿ¤­? gýÛŸñlÿ·êUŸ†Oþí¯ø·Ûí9�Á{ÿ¹%ÿ‰ßþ4É~%h•bs NòÇÍÿ<&á•æ^Cö~›ˆ]—‰™?áØ×WsØ\Û#n· ˆðÍ}eÜl©6LvãÝ›%�jðæ–?½åcÇw¾šÃäÿ˜þ9øyÄ+Ëûï²tüJ™XùRñî- nÛs®c>5—¨ø¯×ýmÆÏß‘ì†ü»¬‹]‰$¶W+#:ûé¬~Nù‹ã¯‡ÜF¼»¾{#QÄé•�•/âÐVí·9æãG}~éÅ¸Ž¿ëmö^üŠY ùwY»Il®VD×ßMcò³ÌÏü!ë‰;[Ênåâ=Aî›îš«÷¹YÜ“•l)6j¸?Ðâí¹§9ÚâÓ:9r1õ’âb{òg¶9Žœ[²ðíç{È5³¹÷Ø}ÿëÇ\˜67¯-ÎÀÉŽë¡Á‡g²Âʆ’cm­üˆ­ù�ø…{ç³åÙñ?¸ì]Áä»#Þq¾ƒQÊ{{w…wÞa¤±G‘LJp+s0ç·ß,{Lüi㤘û;+öR,~_~ îøìùv|OÅN;Bp‰.ÈÅ·›ï`ÔòŽÜÝa_÷˜>´VdE°âܼ9í÷ËÏ?xé&>ÎÊý”„—ŸŸ‹�ºyýûñãÖ¶ô?­ÐÛoywŸ‹snâØRÊñìúIÆúÖKù?Sp;cËÆÙkò?IßÌ®Økòbžj²¬öÛqÏyÏfò}¯6ìŽeʹÿ2ÞMnFë–s^A¶å<—m=¶[’췛̼ížt–Ee-¶²Kuim)J}”£€\Ïœs^Çä{cØ\¿”s¾]¸’Évܧ™oö¼Ÿ‘läŽ;bŽM†ëu—›²Ì¾8¬¥¶ÖIn­¶Ò”§ÙGùŸ8æ��Èö<ðyw'ç\·q%²í¹G1ßmy7!ÙË–Å› Öë+7c™|qYKm¬’]ém)J}”D£·û»¹|‚æSö'{v¿cw/<ÉÂÇÖKÌ{Gšr.wÉEáÉ‘.¦-Ï&ØìóàÔkïÊ“îø‘ßn>=/º‘Ùm+Z<›Ë<»€=Ÿ_ö7`u?+Õó¾®ç»®y¶–é®Ôrî ÈöüS’ë>ó˜¹TÁÝèòðvXÖebÍ|RÛd”¶X¯ºË©[n­+ëø'`óήäúþk֜וõ÷1ÔýoÑœ«…r·ä:údÃ~>U˜›�.^Âò±¤º9m¶J[$wVÛ©[kZ=ìwÕü›_Ízךr®¿æšÍú3”ð¾Aµãƒ™_�“f&ãK•…Ÿy8ò]¶Û%-’;«mÔ­µ­s¤»÷»¼lç˜]ŸãÿlöMv Â�–uÇ+Üñ-ÆF®ü܆N�g‘§ËŦçŽl2õ¸÷eëré>e"¶Ùá’Úz$½àÏâRí®‘¨àžoñŠ÷ ¶–aÛÛüYªÒv¾šÊRJE‘ȸÔRj8o:ćÒ(lû"¤“É&|õ¤wȧÂïÄ7ÙÜ+#UÂ|Íã•íN%JY‰olð�f³OÙz›kY~žG#ãPɪâ\Ë*V(ë~-ºœèâ²ùo®|õ¥—H‹ÃÄ)Ù¼+#U¼Êã•í>%JY‰olp­n³OÙZ›kY)fG"ãpÉ«â|ËV(ë~-ºœèâ¶ùo»:jÒ˦)ñÅø¶y>¯7AÖ%œ&.M¦ž]f¦/'º{Žbky²ü�†&&Fó¶º�W\MïW‰dÙ™¹üB ¬l\;aÅãû‰½Ô—÷AùÒ^QuÞ»µº±øçfp]”·bSoÇòo¬úÍœxøÙY>G§Ì‹wÅù&6d2M¯ØãâæEÑßtt¶K+t¬úC¾º{É®ìî�ì?ØÜ'e}qíÛèro¬ºý…¸øÙsi·úœ¸ñ·\WÍ�Ÿ��™ ’ÙuñÒ—ÛZÍ“£;ï¦|™ë3ÜÝÙ\K¶:Ç—áG›¢åü7kÏ_-×E™:ÍŒ6û3ô<�S|´‡aªÏ‡e­Ê¶ü|¸!žËã·4²ë.²àˆ?ˆsö`öŸ÷ÀéïìÿRã—ÏìÛí_ëÏ©XZ7~wÿfÿi^�OýŸé\3üI¿±[ÌÿøºÿÊâ•â H�Š©°©žï ’Ÿ�ª. ³3ÂâgâGòcèOÕ_Yá_ñ7ñ/ù3t?곊®·ñ_ø°øãýẃõ{Ç›XÙÆÌ³Èçmöúž?©Úo·ÛMv“G¤×fí÷[­¾n6·S¨Ôëq¥ÌØí6›ÉaÃÀ×`aÃ|³Ï-öGv]u×RÚV¯Ã³ÙëtšÝ†çs°ÁÔiõ9{=¶Ûg—��­Ökp1äËÎØl3²ä‹  +ä–Y/¶8ã¶·]ZR•«ðìözÝ&·a¹Üì0u}F^ÏkµÙåãàkušÜ y2³¶ ì©"ÅÂÁÂÅŠé%–K­Ž8í­×V”¥jÿ7ßdV_,·ÙqÙuòI}Ô²Ë,²•ºûï¾êÒÛl¶ÚzÖµû)Dv¼âüF^4ôD›ÞâÞ¢Ï&»7 ¹šû¹t9³iú/�죦Çëÿ ¡¶»žÉû†~<ý,Pê¶“zÁº²úz8#æ_Ïߎ}#~ç…xÝ­³ÈþÆÄûÖ y6&lš®—Ñl,¦v7ׯ'ŠËö}�÷,Èa—éi¢³Y��-~–Þ;éèà·™?>Þ:ô•ûžㆲÏ#;¹X5䨹’jºcG°Ž™¸ßZ¼š+/Ùóÿ¹åÅ ¿KOšÜìi+ô¶ÑßODj~B?çƒ#˸à^=ßÿ .çÁöÁ|=gȰõ½Çr¤³O“ÿW»ºÌ=æ³’ÍMfÚ顇Šar.ÊÛ7/Y‘JÝdNü¹ù<óOÍ|­†7t÷æ> ™<²cõ¬¼'ª°q«™&n&ü[S=/åUÖK%mÆËßämöQÙJ[\šÒ”EóÊ�’/0¼ÂÉÏÆíÎÚÛãðœÉe¬=SÁk/ ë,L[ò~õGÖd}nSnµÿSäïrvÙñÛù´Èôû„ò›äËÿ02sñûo¶vØü+2YkUpjËÃzÏüŠåA‡‘Ç5™W”SK«H2w¹\øíü߯Z}ˆOy¿ó™òCç­›®?Úå™×O¼ÃÌÖæô_AG²êÞ¯ÍÔí´ºý.ûIÉ­ÄÛìùßdhw”Á»"lW½ÞáÃ>LÔÆ�éºÑfŒ¹ôW–ò® Èu<»„rnAÃyfƒ2-Ž‹“ñ]ÎË�r. û ÎÔîµ8{-vd7}¶K ¶_m%_{‹ò®QÂ9«–p¾I¿â§E—f~“’ñ}ÆÇAÈ4ùÑÒ´�3U¹ÔäbluùqÒêûd†K/§¯ÙWÞã§“ð�þ¯•ðÎG¾â<£G—~“’q�¾ÃA¿ÓçEëô³5{�VF&Ç_—­}²E%—Ó÷*÷ÝeÚÝ£Ò¼ËYؽ7Ù<û©{K|:nwÖ\Ç�ð>e©‡iƒ‘«ÙŬäüWcªÝàG±ÖåË�=±Oe&‚[ã¿ÖË«J÷óÃ_ÄaåÏDI¨âžEá`ùIÖØ¶ca_±ÞOîMF S0¹æØ®ø}k<ôß`fì3ä¶–×c�K«};ŸâOÏ÷”Ý/.«ŒùƒƒäŸ^ãÓM–ÖX8×mêp¢ŠX¥Ãæx8rkyMq£¥ÓËnë'aŸ7¥·l ¥k{¹>%üüyIÓê¸Ïá`ù#רöãáɱÛKí­VQG�¸|ËMw(»Ê]4¶î°rsó¥¥-»c+[Ò¡ð?ñhyYÔ;  ó“„é¼¢ëZO.Dz8†»G×=ý¡ÃÈÙê­—e÷m\:¾«ìh42íÅÕϯã¹ûɬ“/}m-»ß.¯ >J|Có³Qd½ÙxÿÃH1/ÌÝu6�Šö¾†(©îžYø´Ù¹poµø±ÝefÏÒemu°ÖKl¿"Ù}l¤¨¼Gù ñ[ÍmM³ô¯bcÝË Ä»/sÕ\Â88ÏhhcŽØïÈ“'ŒÍ—“ëÙ£¤» >NÏYòR:äýO[))¿~AüXóSSlÝ-ØxõåÐb]—¹ê¾ag³ôQÇm—Ï&G›/&-Ö-²Ùõsôù;=lwÉHîȤž¶RlÞ|“ømò#Âäåž.w›—íuX9œÏ¬·1ËÅûo¯fËÂÕäÏ�˸ßèííÀÀÍÚÛ�úk_úCŽfçÃ4X;¿¥}Ôß&ì·Y½`þ~%/ÙÍ…ü¡zÇú‡ÎÜ5üB³óþÎmý#ÊÜ:üA¿³ÿþÍýoý#ÊÜüK¿±‡Ë?ßO¿å-Ô( ƒbªð� ÿñúíßä�Ï¿\ý�?á¼þ<]©ü”¹Çë{£ü8_Ç´ÿ’§8ýnôjV_ƒ÷ö–w‡òì¿×ïŒÉݦšºÇà0~H>Tú 㧈âÅÊ®¯bw(×Ë›ÀúGŽìñ±wy¸túðÅÊ9žÎè³,á<#ïÐV 3%‚|¬é­¾Ì,lŸ¡•~?8¾@þLz3À>+�g)¾¼ó¸ù¾lÞÓZ Ž66ë?Ÿx†GËv7Å—g áwgcݹ²Á>F\¶Én6M`ɬäùù.è︬r{ëÏ;�‘àK™Â:sC±ÇÆÝgbÿG†GË67G—gáµÍ‚è-Ì–²2å¶Kpñò>†M`åÿÉÇË/‹ÿ]Y7)íÝÅ9nò FVWRxõŶxQöcl+~FvmÒÙ•gë¸6x÷Û±äy°IÇal:ùà!ægžþLùßÏ)Í{û�M³×kr3¯á½q ·#MÖ�‰�5d—Šqo½eYfEñ[SlseÍÛæE VäåÍH£¥°nòëÎ/"üÚæ¶òîòæ’çëµÓå_ĺ÷AnFŸ®x6>T—Ý$gŒ×+*ÚeßÖÅ.Ç6\Í®TQÇdùRÛt²^\yÁäWšüÒÞ]ÞÒ\ývº|«ø—_hmÈÔu×Çʒ뤃�qªåe[L«ã­±Ë°Ì—3k•qÙ>T¶Å,¬ûäWå—Ìo“>m&ç¾ùÝú~²Öì0óø?�¼}¦�¦¸Lúì�~.Þ9‘°ÎÉåÊX¶—ͽÜÏŸ²¥s§ÇÆ“_ôp ÓF¢µÍßü{¯¿•ÿ�?®~Úßÿ�†ŸÊ·ÇÖ÷m_‚_ÇÃoåWãÏëwˆ7›ãö–|xÿ.o¿_¼iÊ˵•«’€c|€wE<†ócÊáƒm>óOËû£›×‰ìòkmfŸ�h6óq޽²´²ù,²Ì^¥×Ãe–Ýu¶Y-¥Õ¥=kZO�ݽ^øó'ÉNÕ‹i~çWÉû{˜ÅÅöRVÚÝ‘Â8öÎ^/ÀéOe×ÙK ášLí¥.º”¶ÊR•­)ê­[ήÝþÞþbù%Ú±läÜjù?nó 8ÆÆZÛ[ò8OÙËÆ8%?6ëì¶È8n—;m¥×RÛl¥)ZÒžª‚>g»Æ">T<æì™¢ÕG÷Éú¿O>—6ÍŽ³iƺŠâ›ülØì²)ÿ„|k®13ï­žë)&MÔ¶ûí¥/»PšÕ1€)ø‰ñ«Ê��?:óu¬®Ï„ñîM'jöëé²ÖIĺÃþYv«{�ô/м»��¢šëÿ7þªRŸmkJW¢?þ·+"ž¶[K¤¶ÌõŠëÖ»>G ä'¢ÜòŽS»Ôq®3ÇuyûÎAȹË M¢Ñiux²æìöû�¾Ælm~¯W®Ã†ù§ÈžK"†+.ºû©m+WÊÞotœcK·äœ—qªãÜwA­ÎÜï·û͆&§K¤Óë1¤ÍÙm¶ûLù±ðuºÝ~7Ë<óIdQEenºê[JÕò÷›Í'Òí¹'%Üjø÷Ðksw;Ýöóa‰©ÒéuÜi36;]¶Ó>l|v·_‰ òÏ<ÒYQÙ[®º–Òµ~ ®×W¢Õì·{½–›K¦ÀÌÚíöû\Ì}~¯U«×ãÉ—°Ùl¶rC‰ƒ�ƒ‰ òÍ4·ÛQÛ[®­)JÕ¯’Äq%“o:ƒãïéV;)÷ Ï’ü‹MY=÷ÖÛ¾÷Sp¾AƒK)lUöÇúku�u/¯Ö¦6¶˜ù÷E—äñ]�>ëª<¤ý*×oäO Ôýhn¾±Ý÷¨º³ˆnñidŸFK¬�ôÆçèï­³Së+��Xµü�üþ]ûž©ðZ�Kô«\¿�üƒSõ¢ºúÇwÞ¢êî#ºÅ¥—Ò+l¦7÷G}m–˜ø7Y\|êÃ7åƒñRqn¶Ìå} ñ§‹°ùη;�q~SåW&Öco:¿�Ï®ým3º�É•v'fî5{ü«äÇäˆ/â—I«¶ìlC®Ï�*(”ó.iÌ;”nù¿?å<‡›s.KŸ.Ó�ò¾W¹ØrE¼ØÏéõ³¶Û�®FVÃ?*JR”­òÉuÞ”¥=})DZyw0åœÿ’îyŸ:äÛþe˹mû÷'å|ýöÿsŸ%¶Û~fÓo´Ÿ+?;"¶Ym¾ù$º´¶ÚSòR”EË—sYÏù.ã™sžK¾æ<»�æÉ±ßr~O¶ÏÞï÷9òÒÛdÌÙí¶säçfä]m´§¾K”§ä¥(‚ÏowkwÿcrnÝîÞÃåݩټË.ÞMÎ9Îó?�ò-´˜¸˜úí|l6M,ZýN¯ <Xý˜¸8PE��pEvù—œyÆ6tÁ¿“O*üäVdôï4®ã®ós'Ëäý)ÍäÏÝõ‡!›*+!ÉØG¦�7+Œr+© 7Sg©Ÿ .JãÅEÙÔ»ýðð¿äkÉÏ÷ñÍÕ<ºíÇ_eæI•Èús™É›¸ë�íù1ý,œÜ}]™xùOßßK#¾›\¸™2]VdW#Û ¿°?ß6~güfï5:>Ë2;WÇOÒ8Òò_{mŸ›Ák¯¿k´Ún%ë-œ–çìzo”lò7ÙÙæi㮿7a4y;]vÓîñElîþ<¾V5.†¹Ø˜WdAd“Wð?äËÇ¿<¸ýqønuÜ·õès¹wKr�†5ü“_²–åí¸¦ÆØ°ñ¹çÆÊõŽììHbÈÆ¥ÑW7ìˆ,’Ç�–ÏþO8;^”å±ñÞàÒñØ÷}Ÿã¿0ɇ³ºú‘gA©Øl`ÇöÉÎx-›<ÌjA¿ÔV|:YŸ‡u¸ó]�NtUÓàòÈÞ–ñ[¬7=Ãß\÷M×¼K}˜·l¶²I&fßo‘NNãZ|K'Ûr^K±‡kàÀ†l™#†I=´Ž)/³w¯~õ�=s¸í~íç~Â4×[›=¤’_“²ÙÍDøš.?ªÅŽ}§!ä9ñbKt8pÍ“%‘_},öG}Öáþôï΢ñ«®w­ÝœßQÀøFšë “e´’Kòv[9¡ÈŸE ÕbÙ>Ó�r ø±eº ,8fɒȯ¾–{#¾ëq|wçMxÃÕǼ;û±xßVu_ÕO·äÜ¿”eÝ&¸œû³t×Ù÷Hçí½þ¯.HoÀ΃êKw×Éú*¼},©v—ÁU!/ò%ó/ÝÞddoz߬oÛô¯ŽOŸƒN-¯Î¦?:ì�D¶ýÒ9{Ky­É–*`fãRûîãúùk¬�ïE•.ΰÁ‘l(~Cþd»·Ì\�ç\u”›~—ñÊYó°©Åµùßwç�©–ߺÙ/hîõ¹2EL Ìz_}Ú FÇ÷‹¢É—etPä[\çËâKòÎr^�ñZ¼«Æ�6Ú��ä±Ó/_�Ý}Ù­Øf[voðë’if̧âÚ¬{0¤ãZé-ÍÆÈ΋i±ÙáæYƒ‰Ã—eF@ì8`óžªæ:Âë^]ȸ'8âÙÖl¸ï,â›lÝ&ûO›e—ÇY°vXA“Õ†Kã’ßw²X¯º;éu—]m}oç¼×¬9v‹Ÿu×*ßpžkÆ3­ØñþQÆvyZ�Ö§2–_ ÒáçáÉñÛ6<·Å-žµ²ho¾;éu—]m}gçœÓ¬yn‹žõß)Þð®iÆsmØè9?Ùåj7Z¬Ël¾+¥ÄÎÃ’)£¶h%¾)lõ­“C}ÑßK¬ºëk–z7½»ƒÆŽÕá½ÝÐÝ…É:»µ8Ö-Çæ\[7î› ‹iX²°³1æ²}nóA¸Ã¾LM–¯> ­f×iq3 Ÿib¾q?_:œoÊÜÞ+㇕réx7‘Ù±bé8o?dzOÁûËme¿KL(ìƒ_Â{7om¶û0#öj÷µºÍ}1f›YY›üZ|Òñï'ò8Ï�þL˨á~BäÅ�¨â¼ÛÌ}O î�ŒvÖ81~é‘`ðþÅÙÇm¿ê =ºÝ®_º˜Ç’\}m&]ñoóAÇüžÉã]伺ŽäLXúŽ+ÍqìÇÕpÞçØÇmcƒîqÙìM•–Ûéƒg·]´Ë÷S˜òKº–<ü þ ^ò�«ñÓɉ8ÏVy©®‹"-šë$Ôõï‘Z\Ys/Ýð(òçȧì�V=õÝq™f¾Ì˜ãý%¨¾Ly3uÚY¤ïòKÊá~z¿k•Ÿö ÿ“oN«ñù»ý¨NØ_þO}N¯÷æÛöžy5ÿa�ù>uB©¿Ä›ûj|Ïÿ‹¯ü“ú)È)\©pÌ/Ï—ÿwŸüWñŠJÓðÆݹÿÏû~¥YødÿîÚÿ‹wý¾Ó˜ü¿û’_ø�ÿãL—âV‰V'0ÿËI4ÒG 0Ç|²Ë-öÇQGmo’I$¾´¶Èì¶•­kZÒ”¥âI,ŠËå–û#Ž;.’I$º–Y–R·_}÷ÝZ[m–ÛOZÖ¿e(ÿI1ß,·ÙQYt’I%ÖÙqÙmn¾ûﺴ¶Ë,¶•­kZúR‚-Ÿ&?ˆƒˆudÛÞ–ðC#AÙ…�&ÓOÉ;û;yÖœ?&8î·ûYa]tšþËÞãeÝ|¶lò)аEX£ÛÅ‘Ð�wȯÏgë)7]Aá>F‹±9ì_¤u{þóË‹s×äÙdï9g/Ûæn÷Y÷C˜Ø°ß™›,·Å…�‰�bãÇìÇÅÇŽÈ¢²Èì¶ÚD—²{;°û‹™î»µ9§$ìqȲ-ÈÜò~UµËÜm³+–Ã�Þrä’¸ø8Ö[64T³ ,Š,ŽËm¤K»#³{ ¸y–ë°ûO™ò>Íù öän9?*Úåî6Ù•ŽËaÇ‚¹9rIX0pqì¶lh©f>4[VY¶ÛHù ä�{ù_Ú;îçò7µ9ovg"—"ìîMÌ6wf_……6Ëa¶‹�ñ½TchøÖgmr.ÀÒêq°µÛ%¬x¸ÐÇée< ¼3€6WÅ�.»ÿÃ.Ï×öÇ�Üûeù=ÑA»ÔÖ본‡6ÓY}Õ›ŽsŽ15ôÖò=.E²]í¶[i‘‰-i‘‰.>TqOÄøÍå_zx‡Øø=ŸÑ<ã?‰îâ¾·Z›ëvo暈¯¾²qîiÆå¾šýþ¢{%¾–Òú[‘‰%ß_l|›#žÍ‡ñ£Êžòñ±°{;¢ù¾ÝÃtnµWVìÞ)̵ß}ÒñþgÇ%¾šýþ¢{%¾–ÒJ[‘‰%ÔŸ\|›#šÍàðWäSËŽ~ÏþÙ¾0vV_·i—£¿ŸuÎî9·�SÚú­d¹8š.Åáwåbãí!·3/ Ž$¸;Ý\ù_£sð¥žù+=ïŒ?˜>‘ùãúι»[Õ>Qku“IÉú�7:êëygè¼k§Ør®¥ÚfÝIyŠlH¯ÊŸW%×môöÙ5“S'693‚øáù\é¿;ôOk]wXù#­ÀšþIÕY™Õ»“[®‚³ærŽ®ÙfVÉ7ú9±c»"}}õ®×Qì–É­Ÿ(ö3xøåùXéÏ;48Oi]Yy­ÀšþGÕy™×]ƒÉm×AY³9?Wì²ël›ý$رݑ>¾ú×i©öKdÖÏ�{ ›0>%þhüqùTá[L7�Aä�ó;±=¸�3{S}$í Þ¢}—é.%>&O;åTú²ááì¶Ë}qxw¿cöK·ÏöÇu¸ùáÅ›“ qnÐÏ8~E¼vð?ŠS?³÷—ïûq¬“;„tï›'œrŠVYq1vqK%1¸¯®l2[.×:¶E[`žÜXòòcûµÚæ÷ÈŸ� qZgvvòMÿbî5’çpžžâÓbäó~OJË&&6Ã29d¦7â•ÍŠûeÚgVÈî¶ íÅ�/&/»]ϯ�o“Ÿ¾4zÓŸù#Î$³{½¿èpžáVë7ÝÃÙß}¹Rñ^!—µÔG‡UlwW7s²ÉÀÓb_ôà“*™y8˜ùIó×åwÊß?7[{ʦà}/M…ù{¡¸&~f¿ƒáâÅ6ºùytöÖ þÈä·ëâŸï›_©>]ÒÉ���µ‚�¡óƒäûÉß9vû mÉeàý=LëæÐô� ÎËÂáØØ±dâdk¥åÙÖ îÃßb߯†o½ìýÐA—I$ÁÅÁ²[¡¤+<Ýù:òoÎ-¾~5ä’ðŽ ¦uóhºK…gfapìlh²1g×ËË'¥`Íì-î-øÍ÷½—º2¾¤˜8Ø6IXi\É×Î癿%¹[N¹ÜW¡üišJLJã·Yo¶Ÿ¡yjíµ¹ÃËn³Y´í½ÎÐbV–dc`h"ÈÀƒ+S‹•I&“™®t9Öâˆíñ¾KÈøo ÒòÎ!¿Ýq^SÇ6x{®=ÉxæÓ;I¿Ñn5ÓÙ•¯Úé÷Ùñ¶Í–LvÉ ðÉd±ßm.¶êVž¯³Çy!âÝG)â{íÏäÜa‹¶Ðò.;´ÍÒït›\mŸ g¨ÛëgÆØkv“ÙKâš,’;©JÛZV�³Çy âÝG(â{ÝÏäÚ †.ÛEȸöÏ7K¼Òí0¥¶|=–§m­Ÿ?]Ÿ‰5”¾)¡’É#º”­µ¥^Ó®û#°º‡šh;#ªy×/ëNÂâ¹RçqžsÀ¹&ãˆòî?—>&FùnE¡ÌÀÛkeÈÀË– + ¶}H%¾;½l¾êVTßÿˆÓ’qYt=Açõr¹_²,F“ÈÎ?§¿'™hénE¸öKÛoSg¯1ÕcàÉoÔÛjq©º¶˜ÞéñvydG&¯�¿Ä¾ã’éz«ÎŠärN7d8ÚÝG�Z Eóò½G·"‘Ewgñ�D?üTë`Â’”»iªÆ¦ÚÚcR³âlgžL˜äÅñûóõ¾ã’éz¯ÎZär>7d8ºÍG�Mù«QížØ¢»³øÞ¦*×”ë`Â’”»i«Æ¦ÚÚcÒ³âìgžLˆæéñQø¬¿Jgh:CåÌ r®Øãê<¿áüzܼV82uÞçêÞ¤û¶2ç·+ÞCÆqbņé0bÊÓà ¹Û›f%ùŸìN-¢ç “èyŸ äúè6Üw”ñ}¶ó�ï5™4­`ÎÕmµ³ä`çbÉéZRøïºž´­?-+D±x§,âüï�éy� äZN[Äù&¿kÇù/Úan´[­fU¾ü|ý^×]6Fv$Öÿ;|wÝmš•ïåœ_�ñ½/1á\‹IËxŸ#×ÁµÐr^9³ÃÜè÷ZÜ›}ØùÚͦ¾lŒ,ÜYiù/Žû­õ¥iùiTÛ¸ß$ã¼ÏŽè9‡ßéy_åz]W$âü£�ípw¼w’qÝî M&ÿA»ÕÏ•¬Üiw̨²1r±å’ ˆ$¶HîºË©ZúW zÚGóñ)~Îl/å Ö?Ô>vá¯âýŸŸöpëoéVá×â ýŸÿöoëéVàÿâ]ýŒ>Yþúxõÿ)n¡@)W€${ø_ÿ�×nÿ$}úçè�ÿ çñâíOä¥Î?[Ý�áÂþ;ý§ü•9Çëw£R²ü¿´³¼?�Ïeþ¿|fNí6ÕÖ?8Åò÷ò»Å~>ºÒÞ×ÙZSåGakïþq ¹)›�Àt6eãßÚ<Ï_·Ù]~&T‹W�=ðݵ̥ն—ãce{yò©òwżë»8¯ ÉÑò&9æ•ájY¿›‘caáaãE>..yÌö³î¹/%ÝOI³¶YÓRÈíüØì‹ + Åłȱq1bŽ#Žì²Ú¶;ë¾»{ÉîÞç]ñßëuٯ٩7¼»—odйY¹UŠ,\<¿W¯Ç‡qáŽ;| ü;€6ÿãÛøýx=ü¯üiýsð¦Öøüx¼4þU¾;þ·¸{jüþ;þ*¿[¼A¼ß´³ãÇùsx•úýà NV]¬­\”Á^Pöf_KxÓä'pk¯‚=—Vô�jv®¹>ŸFí¯àÛÎA¬ŠûkK¾§ÖÏ×Çe,¥+[ëu-¥+ZÒŒ3äobäu��éÛu‹ï½gÓݕϰi7§Ó“?ˆpÝÎÿ+­º•¥õ›/Ë)o¥kunô¥+Zú0Ï‘�‰‘Ô>>wŸkaݹ½iÓý•Ï0>¿¥c¿aĸnç}ƒÖ××êVl¼ ,¥¾•­õº”¥+Zú1wxö¿èn•îòåôž¼O¦:»°;_”S)çȯë®'¶åûªcÁ‹NLÓþ�ÓËì²8ä¾ë½)m·WÒ•©öµ­Õ­×V·]uk[®­kZÖµ¯­kZ×í­kU_µ­kZÖµ­kZúÖµûkZ×òÖµþj°êÖµ­kZÖµ­}kZýµ­kùkZþíj¤–yçÊžlœ™¥ÈÉÈ–Iò2'’ù§žy¯¬’Í4²Vé%–Y.­×]uk[«_Zý¯ø?ãù%íøYú·dyEäþË[‘m#‡‹t7 ÜRK©‹-g¾.Âìímñz{dÈǦ?–Û½}l¶[©þ™+/ÃMÒ>·y'ä†Ç[}=-ã=#öô’êGwº±óÎÉÖÝ·Ûuöû8¤¶Ýëëm+Zz}©U~Þ’õ¿É#¶:é)í·�t—ÛRúÒ+½Õ��ö>ºèý¾Û¤³ÙÅe¶ï_[iuiéö§­ø8›[�d¹û�æÎj}>&{MÞ÷u´ËƒW«Àƒ+g¶ÙåA……�>\ðÃ}}¿'ß/=×òʶSQ6ß«|_Ôll»ˆõ&ÂÚeòjàMd˜œ»µó°+d<�‘däCnF>¾—I«ÒÒ‘Ç�Iò,›a—’•žáó»‘ìx¦¢M§ZxÙ¬ØÙê¼lÛi�Éi�5’árŽÒÎÁ¿èo·ÓäÃnLû.¿Y§ôŽ8~ñ‘› ˜-üŽüªw�|“aÅum:ׯý^ÆË¸¿VâæÒ™¼—î3G&(í Üéû{>L6äÁ¯¶ëõšHã‡ëäG&~Mf1¿>ýïòY™¾éNµƒiÒ>áò_½ë:ï*Øy×pãèó-“ŒïûÏo®ÊŸ#¬{v˜ÜSY4šÜúÃ~D»lÍ~ÆA9Då:> EÄy*àŸC͸7$ÞpþaÅö˜›¾7Ê8ÖÓ7I¿Ðíð%¶|-ž§m®›;;[iu’E}·[_ÉW âœ³”pNK¤æ\+�-ã[m¿ä¼we™§Þiv˜rRL]†¯i�4˜Yp_O[oŽûn£ÐqNYÉø'$ÒsÈw\O–q½Ž6ß�òN;²ËÓï4»L9)..~³g�,˜Yp_O[oŽûn£$õpö�@v_î>•眗¬ûC�m?LqmÄvsê·ºlÛñ²02í‡&ûr06šÌÉðó±&¶\Lü‰±²c—i#ºpß:z/'òøÏ�ž[géø�ٖǪàý—HµúÝÑÛ8zMŒß��ÄûKou×} Xb‹U¸žÚʼnL|©1°'™¯ÅwÍ.�ÉLŽ7ãç”Yº®'ßÙ6Ç«âƒH°4œ7¸s#¤áêòàŽü|/Ù{ZÝ_§‰ qkv³Û[1)�‘&> “+ø±ù Òy%‘Ç|}ò‡;SÄûï&Øõœ?°)“‡vþe”†M^\ß�ƒÆ;+iuÕúX±G·k5µ³�dI –>| ~ ®ò¯ÐxÛåÚ±óG]‰6>Ÿc�LM']ù¯ÀŠ¢Ûp¸gÉ¥8çjÙ�õ.Ûq‹iv>U°]±ÔßX%ÉÖiä �šA©5€53Í4:WÁ>•Ù÷gvló£ÕG�‡‹ñ} fò¾uÊó`ÈÉÁã|o'#ü«ñ±%žyç–l\ho’Kéém·k—>\ôÿ…}?³îNäÙæC©‡2/ãºX`Ìå×”fA‘‘ƒÆøÞFF&<¹²ãâM4²Í48øØðß$—Ûm¿n°ùqåÇPx[Ô.âî-–dZ˜³!ÒñÞ;¥†ÎOÍ9>d9\sŽaddbcË™.>$³K,ÒÃ��� òI}´¶”®«ù›æo@øÐ<»Èÿ#ùwð_�q¡¯×kµðE²æ<û˜ì¢É»Žõç^qÛ²q/ä|ӑ߉-`‚²Á‹‰‹F~~F·33¹ß=<þï?�NßÉì®ÙÚß®ãyv}_ÕZ¬É¤áýeÆó&Žû°5q]f=6œ‡gf47mwÅn^Êh¬¥i48¸¸ðóÎ~éó·µò;´vWë¸Þ¦\ü>´ë fdÒñ>¸ãÙsGuØZë.�›NA²³¶»y¢·+c4VSÒXqqq %æçœ�ÏçWkdvgì¯×q½T¹Ø�mÖ:ÌÉ¥â}sÇò¥Žêák£ºÌzm7û1â»i·š+r¶Ge=!Ƈ«_”•¯#þQ»›/œvžÎ~%Ô\si�^–ñÿG³—'…u†Š[i‹Ù3Ó_w3ì ž”¿mÈ3 ²|©ä¾BE§ñ[ÉmÝ·÷n«Wt]kÙ›L¸c»·µZ¸­÷qÎK$òY$Ý¡¬Á¶²Ç•m.®÷$›Û� òg̯áÇå³#È(´Þ,y+»²þìÕk+Zö^Ó.îíÝ^²+i^9É%žK/›³õ¸VÖHò­¥ÕÞâC%ó{s¢¾Lû&>~vð¼ùÒk¼NòƒoªÓy“ÃxÞNWåI®Óë<—â¼s“löú| ]‹=½Çµß›¼ÓáÇìÍ×ãäm𢳠„èú|õ~Ö+?ìÿ&Þ�pæïö y9ÿaù=õ;„¿6ß´óɯû ÉóªüI¿¶§ÌÿøºÿÉ?¢œ�r•Ê— Àüü)÷yÿÅwÿ¤­? gýÛŸñlÿ·êUŸ†Oþí¯ø·Ûí9�Á{ÿ¹%ÿ‰ßþ4É~%h•bs1Íy·ëŽ'¿ç|ÿ“høo âºÌ�Ï$å—g‰§Ñi5x–û²3¶[,éaÅÅ‚ÊV”õºê{®­-§­Õ¥+çyw.âÜŒï9§7äZn%Äxη'qÈy/!Øâê4š]^•“';e²Í–\Lh­§Ûu÷Rž¾”§ÛZQçyw.âÜŒï9§7äZn'ĸֻ#oÈ9'!Øâêtºmf%žüŒíŽÇ6Xqqqâ·òÝ}Ôõ­iJ}µ¥’rN;Ã8îÿ˜s þ—Šq.)¥ÚòNQÊ9&×EÇxßÑ`Ï´Ýï÷û½¤øºÍ>—O¬Å—#++"XàÇ‚;¤’ël¶µ¤ ¾[>n9ï™ÿDx럹ë¯ðò2õ›}„UÉÕs>ù¬9ÇúO“ßéoëÉbŽÚàñúRÉrmºéö•’ûàÂ×ÂOå'æK›ùeŸ¿é³¶Ü Æ\yrµ{]”tÉÕóî¤sß›CuÔ‡7�𠘬·îz/HçÉŽë¦ÙÖû¤�¿(¿1œÛËý÷IôvÛ�øÑ�6N³k²�ï:¾_Ý”Š{ã¿aÈn¯ÑÌÐðˆì¶¸z?K&ɲë¦ÙVû¤‹ ºÏ›?Äyμ¶¯?ñOÂÎç®üQØÇ—ĹßjÙ‡›Ç»7È=\yYx»¬ lÒÍ×�t¯,Ƕ;.×Ö=îó[[àÚ×77MXû¸JáZ'€½Å¹W&àÜ—C̸g Ýq>[ŶØî5ɸæÏ3M¾Ðnõy1ækvú}¶¾l|ívÇ.+d†h¯²Hï¶•¶´­sŒò~G¹“—pýöã‹r®7³ÃÝqîGÇöYz}æ�o®žÌœ ž§i�4ºüü<ˆí¾9b¾Û캔­+J¾çäü�…ò'.áûíÇå§CÞºN=+›Ø�w…JÇ6ÿë6ÿA =q+îÌö¸5–,û;Œî D€ þ!ÏÙƒÚß§¿³ýKŽ_<³oµ¯>¥ýahÜuùßý›ý¥ýzu?ö¥pÏñ&þÅo3ÿâëÿ+ŠWŠ�"*¦À¦x!üG¼5þJ~<~¨¸‚ÌÏ ÿ‰Ÿ‰É�¡?U|Qeÿ…ÄßÄ¿äÍÐÿªÎ*ºßÅâÃã�÷†êÕïmcg2Ï úü¸üÛpÏ 0w� ã–nƒ°|§ÌÇ›q´º¸Û®ÐöMmc“;“Ce÷âï»Ën­p4WÖ°bI铳¥b¶,þü§üÅñð¶Ý%ÐYZ>wäÎn<˜›lëë߈ô”3[ì»;’Å—cî¹õl»×Iuk-Þ™[*}+aÂÏáwÊ_Ì?ñoÒ�•£ç~Kfcˉ¶Ï¾°m¸—JE-¾Ë³y$Qßv>ëžû.­pt—V±bÝ铱§Ò¶,,øëüÕüùõwÆ–.OEtÆ'î?46úŒm„œG7"üÞÑš½®&&~—{ÜuÔìp6rò>A«Í�7QÆ1§ÇÎÈ×ßf~lØXyë¶PHì~ÉçÝ¿Íùev�/ßóÞ{ËsîÙò>WÉöY]ÖÛ2±Çwäåäß}ô‡á‚=°ãÁ‘Em‘Ùm´…av':ížiÈ{²ù^ó›óžWŸvË�òŽGŸ>Ëo´Ë¬qÁÓäÏuÕ¶ \Xcƒ)l8ØñYVY–[HUvaóž×æ|ƒ±;+•ï9¿8åY×l¹'äyóì¶ûLºÇÝ>L÷][`Åņ8`†Ê[>;9uØZ æìNƒä»L|Þ}Ò[íŒñꤞ·GW)à9÷Ó#øÎ.³éI‘ wá죲;3±ò+$˜Ý.øóù;îï¹]pµÏÏú3�ì`Éæý;¹ØMf·ëVû,Èå2O­NÌþëJÇ$±YvÊ;l³6 ®ƒ\^•|{|›÷o�\ª¸Z‰ççÝÈv0dó~žÜçÍn·ëVû,Éäü2O­N̾ëoÓ’h¬»c¶Y›Õ‡\nè|>üäùñ�Îô/”gònåðËm�./8蜽­2³ø66Ûc“±Ïç� “¸Ê�ˆó ~Óa‘�•§¬¸ú.OIò!Ìû®lØ»�u„¾,ùUÒ~etïîþ†å‘r~»õÃØaäYf&á¼—}· æÚ:M<ºS¥ûÜZß$Ã$YX³daÏ�“,ï¼jòg§|µê}rt�)‡’ñ=ÅnÃÎÆšËpùäXÐcÍ´âÃMYe›GÉuÊŽ²Ãu×Å42Å“�,ø“ãäK;?<—éï,ú§GÜ}%Êaä¼Oo[°óq¥²Ü>CÄù4óm8�/ÓVYfÒr]E2£¬°ÝuñM ‘dãK>$ðO-›þ1yIÐþdtïïŸ{MÙ]iÊ-’,]¾ªé ÍÔí±l†ý—åLØñöü_”ê>ñÞµùÐÓ²Y¶±É÷ìS>3Û`@üüJ_³› ùBõ�õ�¸kø„ÿgçýœ:ÛúG•¸uøƒgÿý›úßúG•¸?ø—c–¾ž=Ê[¨P A…ÅUà þÿãõÛ¿ÉŸ~¹ú Ãyüx»Sù)s�Ö÷F$øp¿Žÿiÿ%NqúÝèÔ¬¿ïí,ïä3ٯߒÐùó›‚|ø×Éû¯”Ç�¹åyrWŠõ7¾K훜v6Ë*}Fº£$RãqíT8²çí²i}•‡ûc­Ù2cÅ,£üöó;„ø1ãÏ%îNO;ŽI=ÿÁž°áu’èææ��²ÅÉ—Q¬–ë/ŽL]¾,i3¶™4º•‡JGïȾe”?žžfp¯¼zä�ÅÉ£‡oÉ'¿ø5Ö<2²]dÜÏŸìqreÔëeº;ã“G®‹LÝžE.¶°àãÉHýùÁ“Ÿùó»ª~8üTì'ûZËwqÛq´À Üàè÷}«Úèò©Äz÷A››]Ðϱ»|݆L8¹Ók4xÛºäY‰|WV�ÜÝÉÙ^Aö‡4î^ßå{kØÝ�ºŸ{ɹÎë).NT¶Ù6,E‡ªÒê0a‹8±00 ‹ã†;,¶»ÞíÎÃïŽÊæ»ÚÜ›?—óþw¸ŸwÈ·» ­÷Ï‘%¶C�‡‡�,ÅÖiõ80ʼnƒ…�dx¸8PEYvYJï»w¶û ½»'˜vçjò\þ]ϹÖâ}×"Þl.·ß>D–Ù64t³[§Õ`Ã&=‘ãaaÃÙdQÙm*0ò›Éþãó'¾;ÈÞøä×ò~Èì�Ü»]¤ÑÓ">“ÊR /âºìœ¬ÛôÜK‹jã‹ ]‰õe¬8°ÛKï’Jß%øÅ�Øá¯  ¿øöþ?^+ÿ\ü)µ¾ÿ/ ?•oŽÿ­îÚ¿¿Žÿ†ßʯǟÖïo7Æí,øñþ\Þ%~¿xÓ•—k+W%9-ó›Ìåá?ÞRåâì.×ì7ú®¼á˜NyqæÎ‹–öÏÒîõñ] ñÝ%¹c+:²ÇZû$‚Ûíº—[ZÛ^^|ÎòüŽñ«äÆnmp³÷ZŽ Ä1½³K™xü»³øVƒw…Ðße÷ý~9�›ï²µöß·Ûu+mkJòÿæg—ÏÃ~6|•ÌÃÍ®vïSÁ¸†7¶i`“.YÙÜ/EºÂŽè®²ëé7Í̬–V¾Ûâ¶ûn¥m­i^JüíöæwIüEùÕ̵ØVl27=;gQÉ÷Ýe#Áò˜qn‡ÚfÒë$޵¿Y¬ì‰²m§­iuÑR•¥Ô­m­n ö•óª9X¡ø~ú«­>1úƒodRCµíþUÙ}«¾Žû)m>÷™Ëóø>–Xî§Û$y7‚êäõ­)_[ëOÉJV³íø0ën¹øäê]�¸÷ãm{S�ögol¾Ûmú™9ü¯?‰i2-­¿mödðÞ«¾•¯ÛOw§ä¥àø5ë,nºøèê}�°]�´í>EØ=›¼²ëm§¿';•gq-4öÝO¶ûr8Ö_ë_¶žïOÉJ-�ü=}#�Ñ_þ i÷z->›“vå=Ý»ÈÔR˯ä=ÇÎù/;ëÝîß&Ø ¿+q'Rmøö4µ¾—V1l‚—]dVÖ½¨uíׇhÀoÈüêïz‡›w�rr8x¿_ð=UÛ=¾}ÖÒ|Üɤ’Ìmf�IƒKì“iÈ7»¢ÄÂŲ´ºl‰m·ÖÚzÝLOÞ=ÛÖÞ9õ_1î^Úä0qž Áõwl¶û )õrr$¾K1µÚ�N.¶]–ów±š,\,k?>|™l²žž¾´Åáݽoã§VsäížAà¼#YvËoŸ%¿W'"Kä³]¨ÔáÒëdÙo7[ ¢ÅÃÆ³óçÈ–ËiéëZÓ÷wuG�ÝMÏ{Ó¼¹Î—­úŸ¬´<—šó-ýùqÔë ¾,xbƒ Í®ëu·ØdC…­Ö`cål¶»˜0ðàŸ*xa¾¸_’?‘îÝùî}‡/äÙ»Ž3Óü{6\n éºlé6�…éì‹îߥö0âG�‡¸ç;ú{§Øìe¶I©Y~ë öáÁVWçòò Ú~{væw'ßfmøßQè3dƒªz–»K§âš¸â®7é�œ8–C‡µæ»Ëk|ÙÙÒ[,‘ý_ºÃ%1aŠÊWýòòÚ^yöÞw'ßåí¸çRè3$ƒªº›ô�%ÔqM]‘ýÛô¾Ê[aÃÚóMåµ¾lìé-’K>¯Ýa¾˜°Åe*Åù“ùŽíÏ”þåɃ~Sמ"ðA&WGô^Ã?ÙoÉÄÁÈÓÙÚÝ¥�¥–]Fã´÷ø9yU†*M�‰Åp3åÖkòr>¦~ÇiÎW>Üÿqtÿmºë.¶û.ºÛíº—[uµ­·[uµõ¶ën§¥iu+O²¯ûmÕ¶´ºÚÖÛ­­.¶ëkZVÚÒ¾´­+O¶•¥_öÛ®¶ê]mkmÖÖ—[uµ­.¶êWÖ•¥iöÒ´«ôáfæks16:켜 †L¸9ØSË‹™…™‹-“ãeâdÁ|sãdãO·Ç%—Rû/¥+JÒ´M£àÿæ{#»mã~ùmʬ“¸qq±õ)ÛÛì»þñÛ¸¸±VÌnÎvÒ¶_Ú8ÑRšýœÒRîK´†z×om²í¦3ðÏòë‘Üöh|Lò““Ù/máãA«éþÔÝåÉ\®ÖÃņêE¹¦tô­’öN»S e,”¿‘ÃO§?®ÖÏ­´˜‡ÃgˆGrÙ¡ñ?Ê.Md½³‡�³¨;Ow•}r»W­‹…ó,éé[$ì�~44¦ÆY)"ŠŸN]¥ŸWic_áßùÐÊó?Mƒá—–ܯ__*8~–ÏíS϶¹2C³ò;ˆhõ¹Ù›ŒmµoÇ· #¶x>ŸY÷œÙ)527úß~oѺ|=ŒòJ=$ä‘Ò¼`ß$|ŠêÏz[�w×rï/ÒpNª»?;î±Ã“¹Ýì'¾Ü]7ãZù²1#Ùò^G³–,L(.–«4´ºYb†Ù%³ ùß]kã/Pó^íí­Ít¼#ƒêîØgß‘OµÚæK}¸Úž; Âš|[6<‡�l¥‹ ¤ŠËç–ÚÉ$qÒù-Ã}ÿß=kã7QóNìí­Ít¼'„k.ÏλÈò6»\Ùo·SÇ´2Ï�fÇ�oöRÇ‹‡ dŽË¦–•’Hâ¥òYˆ;÷¾z£Æšì^þïc«à}UÕœo+“òþM¶šÈâÆÄ†ø±puºìu27“‘íò±õº�n5²fív™xøx±Ë‘sy¿Ûv÷>Ç´{y5s]v^³¬ºÏ a6oë~),ÑÝn·ûáÅ·g¿Ú}Þ)¶û[àŠm–U¶úY,8¸˜ÕëùÇæçkùÕÜÛÐìL‰5w_v^³¬ú×a6gëŽ+,ÑÝf·ûâÅ·g¾ÙýÞ9¶Û[áŠmŽM´ô²h±qqêgùTùBî¿”"s»W°26_ª¸�û 'Ct”;O¾qάâWâýîjý11·<ó˜K�^ÿs$UÈÌšÈq£¬zü~.6Ž4Ŧnc>¦“w¹ã[�O#ã»m–‡�h6x;­óO›“­Ûé·¼¨³u»M^ÇHrð6:üÈ,–¢¾Ù"’Ê]miZR¯¥¦ÜíøîßUÈ8þÓc£ßh¶X;�&ëQ›“­Ûiöú̘³u»M^ÇHrð6:üÈ,–¢¾É"’Ën¶´º”«éi·~;·Õr Óc£Þèö8[�.ëQ›‘®Úê6Úܘ³uÛ=fÃHrð6�Ù,3E}²E%”ºÚÒ´¥^“†óY×|»ŒsîÈ÷\?›p­þ£•qYÆö9Z~Aƹ.ƒ> ¦“{¤ÚàËf·kªÙbÇ><ñ_l‘Ke·[ZVŒ»ä�ý›ågqòŽùî=Ž»qÙ<Ó†áò}¾«U‹¤ÄÚÍÂx?àí•ú¼[ƒ‹�Ÿ§âØòåR "‚ü«äº8¢²ëc·+y ß�‹ä÷mò^îíœívϰ¹†Ää{=V² 6&ÊnÂø÷ÁØÝ¬Ä­0ñssµ\j r©±AvMò]qY[c·*yß]‹äçmr^ííŒívϰy~Ää{=^² >&ÊnÃ8÷ÁØ]­ÄôÃÅÍÎÕq¨%ʤÇÙ7ÉtqÅemŽÜûæw—£ç_’=ƒå?táq ÍìÌN�Êaàº�Ž�ŒK7_u¿ën~«i¹ßåaåí4¼/'6ÚdÖ ³¦šè#‚ Çx9…Øe«À ~~¿û¼ÿâ»ÿŒRVŸ†3þíÏø¶Ûõ*ÏÃ'ÿv×ü[¿íöœÇà½ÿÜ’ÿÄïÿd¿´J±9€Çä<‡CÄtÎWÊ·Z®7Æ8ÆŸgÈy"Þçâê´š— }–ãu¸ÚgK·U«×ãI>FD×Ù0Çu÷ÝKiZ¾Nû}¤âÚ=Ï'äÛ�gã|sS±ßr þï?W¦Ñé5sl6Û�¾Ï6X0µÚÍf<“dO5öE V]}÷RÚV¯•¾Þé8¶�sɹ.ßYÇøçÕlw¼ƒ}ºÎÆÕé´šMFÛ ®ßm²Í–=v³[��$ÓÏ-öE V]}×RÚV¯™»Ýé¸Î›oÈùßWÇø÷Õçî÷ÛíÞ~&«M¤Ój±%ÏÚmöûLù`ÁÖêõ¸0I6FDÒY1Yu÷ÝKiZ«çù�ùlÞyéϮꞣÍÛñÿºós“]%÷æk³;‹�áÍt1ö/*ÖILyq´ðÛe@ë2lúØ�ÉvNM¶eOô1 �ò×ò›¹ós›_ÕÝQ™µÐøÁÁ6Ó×O÷eë³»w}‡5b�žòml”‚\]$O×G¬È³ëcÅ}r²m³*jcáÁ_åŸå#sæÏ6¿«ú«3k¢ñ�‚m§®Ÿû²õù½·¾Ãš±GÏy6ºJA.6– YÿPõ¹}lx¯»'&Û2f¦>%iß>ÿ:ü‹Ïþ_¹ñcÆ�ÞÛŒøMÁ9%cÜìñf“_³ò“–q½œsk¹—'Ž?fN/Qh6Ø6åñmµ§Þò,‡wµ�ïök0´\7qyÆDdÀƒ‰òÎOÁ9?æ¼+�n8§/â›� ã<›�ì2µ;ÍóS•n³m©ÙáI^Ã.dŠXî¶û/¶•¥_Šò®KÁ¹.‡™pÝöÛ‹ò¾/¶ÁÞñÞE¢ÎÈÖnt›�fDyzýž³a‰$Y8™˜™1[}—Ùu.¶ê>ÿå\“ƒòM1áÛݯå|ckƒ½ã¼‹E�‘¬Üi7̈òõû-nÃH²q312b¶û/²ê]mÔd~¡íîÌè>Ìá]ÉÓ|×{×}Ÿ×{Ü^GÃy—ʦ.×KµÅ¥ö{ì÷Ù.6ff4²cfaäÇ6vÒãäE,ÉÖ4üF|šñ¯�\’\ ’=Yƒ¬Öw‚Ȱñ·VÏmøÚ¾ÍâXÖV–IÆyMø×}çÊRýFÊ’c_m ¿ |«~,~E¸ïžÝ/Zï®ÁÑùÖ¸ºígmñX)>6Ö³Ù|:ÎÅâ°[wçñ®Ov=ÿ^ R—êö6É�}+ q22§éñkò'ǼóéšþŸ¿Ißým‹®ÖvÏ‚‘ãcí~µ—A­ìN-�mß�Æù5Ð]õà¶”¿W°¶L{éXnÄŸ&׆ߕ.ò§ã�‹~·GÂû÷¬²u\CÈž±ÒfK6³Cʳ°òfÒsn%‹°ÌÍÞbõ¿ecër²uçI<øY8™ÚÛ²³¯×_�“Ö÷REuÈÄÄ9û0{Oûàt÷ö©qËç�ömö¯õçÔ¿¬-Ž¿;ÿ³´¿¯N§þÏô®þ$ߨ­æü]åaÑJñP$@ÅTØÔÏ?ˆ÷†¿ÉOÇ�ÕY™á?ñ3ñ#ù1ô'ꯊ,¿ð¯ø›ø—ü™ºõYÅW[ø¯üX|qþðÝAú½ãͬlãfYä/›?˜û|PÖl¼]ñ£{ƒ•ä#ÔVÞsÍõù8Ù¶t^‹iúŸÈë4tíÖ¿[)}+©Ä¾<»ì¬“c#ýóòÓ‹z݇�ÞQÊ9/7伋šsNE½åüÇ—ïvÜ£–rÎQ¶Ïßò^QÉwùù]ï"ä[Ý®F^Óu½Ým2åÉËËÉ–\Œœ‰o’Kî¾êÖ¿‘ù‘ð€áñ÷ò Ý?}Ó�Ù½e“v÷‡ïnÀÖvÏSlóçÅâݙűg–û1r¯²,ªiy^–™SK¥ÝE ¹܉o¶ë20ò30ò·gÁ?;;ÀÎßÇìn¹È®ï‰î냭í.­ÙgM‹Æ»�bÍ-öcdßdYTÓr�52¦—O¸Šg×O-öÝfFFfVëø-çOoxÛØý‹×YÝqMÕpuÝ¡Õû鱸×bñ¬i¥¾Ìl›ì‹&š~O§¦LÒj6ñÃ$úùä¾Û¬Ÿ|Ì<ž§|S|¬÷§Å§zEÎø$¹<˧9–N»¼:??c&.ƒŸè1dºÈöº©/¶x8ïañØ'–ýNÚÈ®¬uºì|‹fÄšho±ûÅo)z{Ìž“â}ñÒ‡ôßäðÝf¿3îØü—†ò\8à»wÂy¦§'2š^U¡“"ÊMÔ’¡’,¬igÃÈÇÈ–ÁOü”ê�-:w‹wwMïLq>KcÉÁ˦<‡‰ò Xá®ã‡rý^>N]ºŽO£–kmžI$2Ç|yòÏ‹<Ë`?�>IuO–]=Å»³§wŸ¦8§%‚èòp²é�!â|ƒÈ«¸áü»Y�“—n£“håšÛfŠ’I±_F<“bÏòÚÍâÿ“=?æDõב}Éáå]kÙz}Ο+Ý�fÓQ—öÁ¸âÜŸ+2Í?-âÛXåÁÙáV[ë�™öRëí¥·Ý±LöÏLø�çâRýœØ_ʬ¨|íÃ_Ä'û??ìáÖßÒ<­Ã¯Äû?ÿìßÖÿÒ<­ÁÿÄ»û|³ýôñëþRÝB€R (6*¯HëðÂKz÷ ÓI0Ãáç`Ë,²ßlqE}ÍÐ7É$’_Z[dv[JÖµ­iJR‰~ù,‹Íþ×–[ìŽ8üOçRI$—RË#²ÎÜèÛ¯¾ûî­-¶Ëm§­k_²”wûðãI>nv¼²ßdQEâ�:’I$ºÛ#Ž;;o£®¾ûﺴ¶Ë,¶•­kZúR‰Y~ßÚYÞÈg²ÿ_¾34såûÏý�žþVò ÷ÚÏ/CuLû^ÑZË%η;�ãæÛfï±äÂÎÇÀ– ¿flplÍ¯ÔÆƒ& \XsRûñ+%úiò»ç>›þOo7œ“t�VϵàÝ)�ù–áçi!ËŽÍça_�—5ñìûc�fgçãÁ<:¸°1f¶é1n’ý7ùYó“?ÍÏ'7›�ÎYºK«§Úp~–×Ç~]¸yÚHrã³wØãeÇ‹|{>ÄØàÙ—ùðA<:Ȱqe¶é1®’ý3üA_'¹">gn8ßäX›_¼gØr.»èªêâ×W˜í%—[‰Ù½¿vÛc¸³�bsîC Ž=E™âÿ5úémÄÆÌÈØ]?*Æs%Á�ßü{¯¿•ÿ�?®~Úßÿ�†ŸÊ·ÇÖ÷m_‚_ÇÃoåWãÏëwˆ7›ãö–|xÿ.o¿_¼iÊ˵•«’€àüMÌ�Oǯ À‚K£‹“yC×:<Ëiïô›¯ûs’[þÛí·ÛL¾=ÿ�K©ëe>Ï_Jӂ߈³{•¨ð7Škñï­‘r�$:ÿE›m=Þ’âãðŽÐäÖYw¶K)éLÞ; ß�K©ëoäõôºœüDÛÌ�O‚S ëd\ŸÈî¿Ñæ[J]é.6? ìþIm—{o²”¥2øôW}´ºž¶þO_JÒ7ŸŠ¯µµçÄ·*â?oÿon÷é^ªÿÎigÿYö»nðûe�& pÿ¸ßþ\¾Éí¯þ[öRëí’8!„Z± «>p›úßÃo¸Ø6k³8ŸŽ}/£ÚbY°ÖÍÎ]ñØ·rMe¶ÙþªÉÛRifº´¥n–û®¯ÛZ¬Üðÿ†I×^'xÍÁ'Å·3‰tQh6P[°Ö›moÐcmå–;m¶”ÈÈÙÙ,’×ÓÖé.ºµûkU™Þ ðÛúóÅø4ضáåñ>„ê=Æ b¤5¦×]À´8ûieŽÛmôÉÈÙÙ,’Ö´õºKî­~ÚÕvŒýC�ãçŽ?ô&&lÛ,^�é.©êmŽEÖ_‘Ÿ�Ö¼CÃ!ÍžèìŽ;¦Ê�KKî­¶Ûmnº¾”¥>ÆÏ¶1±LÚù»�ΟŽiö¼‡�íuº‡[�¹Þo79غ½>›O«Å—;gµÚìó¥ƒ ]­×aA|ÓÏ5öE V]}÷RÚV¯Ÿ¶Ûjô:½–óy²×é´šm~fÛq¸Ûfcëµz�^»L͆Ëe°Ì’L~$7Ë4ÒßdqGenº´¶•«ðmvº½¯e¼Þlµúm.›_™µÜn6¹˜úí^«W®Ç“3a²Ùl3$‡_ƒ‰ òÍ4·ÙQÙ[®­-¥jþSÏ43däÍ>>i¸ŠÙð3»_�ë²-ƒ*ý–ßI"ÔÕ’êõ2{)9YÿV,#Û¯9{‚þ7Âv{ /ºÃk™�ÖÚ:Ù•¯�™íâ¤ø9Ý«ÈuùC“~Ëo’Eª‡*Ë$Öj¯öR(rr³¾­`ˆ'æJo‘þêÇé^�Ùìñ<7è~E�üšI¥ÆþÞ�‹nŸcÜû-U)0øî6VFÅÈú™‘jçÈΟîÓí%×`q¹É%QÓ}}ßr�¦&�Œé6ü‹uŸ%"ÁÓèµ¹»}¦d·V”¶,M~¾Œ¼‰+ZÒ”¶Ë+W×Ñqý÷(ÚbhøÎ“oÈ·yòR,>‹[›·ÚfË_É&¿_F^D•þe–V¯¯£Ðo¹>ÓIÆ´›~CºÎ¿éáj4zÜͶÓ2Oþw‹��F^EÿèYejȽ]ÔµÞ<³€ô¯Wv/ps¬¼L¬ü^ÕÜ'’ö,ÉÀÁ¥•ÍÍÇãœOY¶ÜM‰‡Im¬²[ l�ÝOuiëFëðÏŒ‘{<üÃ"`®M”’ žUÖ<“�k¤Ž¸óå[%»Nu‡Ç5¿NHqîöÝY}·Ýu–ÒµºHévâpï�Ÿ>yÜöcè¼DïœzËm.Ž~U׻΃%—A>E·Ù²çXüs_twÅ�w¥Ô—Ò·VËi_t‘ÒíÁáÿ¾{s©£ƒEâ?|cÖ[i|Sò޾ÞpL,º ò-’Í�8Çã¸G|X÷{n¤ž—]u–Ò¾é,¥Ý&êO‚O—NêÖämxw‚½Ã¦Åƒؤ‹¶äáݲºëï̲•ÇÓw·)ë�Æ\t»ÿ[â‚û-¥ÑÖµ¥&Š·ì6ÁÊæ~&6l(eG\1Ï™½Ëãη.Û%¶—ÛnN¿cÛX¹øsR•üèæŽ9,¯Ùu´¯ØÎø¿ Ÿ'™˜ÐeÃãE‘dEdÑÛ•ÛÝ ƒ“m’[K­¤øy½¥�™‹-)_ÎŽXì¾Úý—R•ûÛáKäã3 ¸|cȲ,˜¬š;2»s¡°2m²Kiu´Ÿ ;´qó1e¥+ùÑË—Û_²´¥Y‡üÛ/šŸò0ÿÂ+Äÿñêò\¿á_å„G$»Ÿ9¾m±bÅ™u8‡&ë^Á’±M‘v-–GæÜ–i²©-•­ðYmÓG¤—YHëK«æ¹?ÃÏÉ?ŠI¶¾+òÜ»"Ç�*êqŽSÖÜÚZÇ,÷cÛlppÎk¾šlŠIo­ÐÙmÒÙg¥÷[K+Kžg“ü?ü‘ñä—kâÇ.˶,xòn§äýqͤ¬rÏv=¶Ç æ{ù¥È¤–úÝ –Ý-‘ú_u´²´¹þ$ü6¿4ñG|—x_}m²ÚÝZGä7ŠsIZSÿ(Šó’Y.ÿBÚVµiÇhøŸåHàÙ´î/ûË«uÍv<;®Õ\ã‰é2&¶ÚßtX»�Þ� Y•}¶Rµ­#–ïJQ©“âç’Ý9ƒ]§lxýÝ=m¨¤×Anë›õ�3ã:9f²ß}Öcî¶úlM^Mim=¡Íu=™Ù/y)Ó¸UÙö¿�ýÏן«tîy·Ys.5¤–[-÷]f>çm¦ÅÕäÖÛiëýk©èÑžâøËù è¹VnøUäÏãÜ'LÎSÍäéÞm¹ë}> 0ýâ}„ý›Çu~¾—[�Û&D[9 �Ò´ºêV•¥5ý‚X)£€×�ŸŸªÏÂÚjórõ»=n^6~»c�“6~~ÖdbfáeãßF.^.DvÉ‘Ýmö_m.¶´­)WëÀÏÏÕgám5y¹zÝž·/?]±ÀÉ›??k213p²ñï�#/";dŽHî¶û/¶—[ZV”«õàgçj³°¶zÌܽnË[—�Ÿ®ØàdM‡���‡5™™¸Yx÷Ç‘‹—‹‘·Ç%—[}—ÛK­­+JU÷x¿(ä¼#’ñÞiÂù÷ˆs!½ÔòŽ'Ë8¾Û?AÉx¿%Ðgãít\‹ŽïuY›M.÷K´Ä‹'/X²1²"²Hï¶ûiZXQð¥ò‘gž=E—Ö�·³×CåOj±-å¿N¸øWvŸ ²ìm~»´õÚÈé �lìÌš<>C5•ÅÇØIDt‚,øqqç�ðùòQ›�S“×Ý£³×Eä·Së1må¶ÇL|»+‡Û|þÎ×k"¤QC�L©cÄßC‹gݱ¶E=–ãÃ�43»ø€ù%�ÍžªÉëîÐÙëâòSª5¸Öò»c¦> Ý“Ä-¾ fkµ‘R(âη&Xñ7ÐãY÷l|ù"žË`‡: hm:ø!ùsÒ|›øÙ‹²wÚŒo1zK_�§ï7Šþs¨­mÂãÝãÅõ1Á¨¿KË«m Ýcëã‚-7 ²X뉇ƒ—©ûÏq]–vIÝ�ùºël¶ëïºÛ,²ÚÝu×V–Ûm¶ÓÖ뮺¾”¶ÛiOZÖ¿‘ÿ.º–Ò·]Z[m´­×]uiJ[JSÖµ­köR”£þ]u¶Û[®­-¶ÚV뮺´¥¶ÛJzÖµ­~ÊR”ã|Û|™çyËßu§Yï³kâ÷Gîö:Î�‹—Ku}™Í±~ñªÞöîn>,—ÁŸ‡‘Òáq»¦º[ñ´÷I‘e¸Òìó ¤ ¾cþE²¼ÎîÙ:ã®wyWxÝÒû�†¿‡Ç‰›Jêû#™ãÒmfëµr Ä–üLÜ)#ºl.;t·M|›äȳîòl²à¶?1_"y~fw\�s×[¬«¼pé�ÎÃ_ÃâÅ̧èÎÇæXô›Yºí<¨1d»7K+.º[¥¾ UòdYô$ÙeAme߈ÿåÏþÞAÁ“¤·úìï¼få›l|IÆ÷Ù;mO{vôµÒrɺ|Y­ã›Ãi~n›‰M‰n]™8Óì6‘çO�¶ÇÇÂâ�.7#@ÿ´¥n­-¶•ºë«J[m)ZÖµ­})JRŸmkZ”¥kZR”­kZúR”ûkZ×òR”þkþÒ•­iJRµ­kéJSí­k_ÉJS÷kWõ‚ ò§‡r2r%Ž |x#¾içžkéPÃtºIe–K©m¶ÛJÖê×ÒŸkhzßÂ/1û[‰»ë<‚ç<?×î|—Žõ:Ïã–ÒúÒ.K�ôÕöÖ•ÿÎ�·ÖŸÍl¿_øcåÏjàámºëÆ^÷æM�+v!Òõ_4Éã96ÒÊIë%®šÍi[.¥iþ¨§¯­=?+e:ÿÃO-»Wm×^3÷¯0Òl)Záò/Vó<ž5‘J[Kÿ¡òJéìÑWÖÚÒ´ÿT}¾¿c¢ýGñòƒÞm~§€ø#äÅ¿¥µ”Ükwœó¬7ýEÃ3µ·ÃdðäãsîÛÇáZeA%·Ám6ùí¯¬tº�¹ãŸß)ܧü½g‰\�+,Ǿë9aô×É­2c¬‘ÒÌ.]ØÚLÉ/¶Û}$¶ØëtW~mô¶êÒ�§Ñü2|˜ò{²p<\ÞcÇm°Ý[w�…Óüc#Ò{+%”·’ö§*ë­¶ŸŸJYZÇwæßKkö6—EðÕò[ȱîÉ×ø»¼ÇŽÛaº¶ï{¨x¾Ei‘ed²–ârnÁÔe_u¶ÓóéK+XîüÛém~ÆØ›eóSþFøExŸþ=_ßðmò¥Æñ-ÍØø—¼È†ù+,ÐvgHò¬º]H¤šµ»_Åû3qŸd~ÈëJ_tt²·V–Ҿ뭥¶ãá{äÓE�n^o‹»iâºúÇK4ý“Ó‹'ÝHï—Öì.?Øû<ËlöÇZ{«-­Þ–úû«JWúî~þL4XÖåæø¿·ž+¯¬t³MØý5Èò}ÔŽùk[°¸÷bí3-³Ûiî¬t¶·z[ëî­)Wù¶_5?äaÿ„W‰ÿãÕ§�—á7˜�7«ÌßvŸ‹}ÿÀ¸îl·7“ò^¤ç:Þ-�tŸÎ[/'›IM ·]û”ûǯ«SûÃ_-z—_�¹ì¯;Ó…èu´²¹ü“{ռϋâÒúzÛõy=t÷h)ëOÿ9û+ùZ¥Ø~ygÔºüÝÇdøÕÞ\/C®öý÷‘ïz¿™bqŒo}=mú¼–º‹´4õÿé�±©]ÉñòsЛ ¦dx1ä”ú=MÛÍÇ#á}i¼í~ ¬ÕE ù™Ý‡Õ0ó^ �Ý|ô»cK ¶ž²RÚ5‚´­+ZV•¥i_JÒ¿eiZ~ZVŸÍk[[\ê¾Ëâ¾ø¥²øäŽû¬’;í­—Ù}•­·Ù}—R—[}·SÒ´¯ÛJ€ÿ ~~¿û¼ÿâ»ÿŒRVŸ†3þíÏø¶Ûõ*ÏÃ'ÿv×ü[¿íöœÇà½ÿÜ’ÿÄïÿd¿´J±9€)ÿ‡Ê<�“Êw>ô^îûzû‚n£�È~Y«Ï¥bæü÷K—dðõv'Ý+[.ã]{´Ç¶Mµd¾ë²ùT‚±Cn³êfC÷ç{äªn{Éwô¦ê”àÜ7i]ûÊu™Ô¾Þ_ÍõvÍXâ׿Nœ‚lqí“m[¤¾ü­ätƬpS]}Ù‘ùØù&—žòM¿„�-ºöðn´²>ûåÌïu¼¿›jr­–>³Å®5~Ÿðƒl1í“m[ï¾ì­ÜtǬpÓ]uÙpMüO?3› ͯ#øÏñc�ë+DZqþáæ7?ây’el³·‘æI[¼j×o#ŠÌ=~šÜh²9µuòäO“•-œ"|Zboõ¹q_Fe @ Rµ­)JVµ­})J}µ­kù)J4êË/–û"ŠËä’Kí²8ì¶·ß}÷Ö–Ùe–[JÝu÷]_JRŸmjéÏ‹ÿ >Xcj7¼¢öü3�n-ÖdâöWpÏý­8„ú­¼Vdàoõ0îáþrí ø—ÒZehµ[Hëg§¥k[­¥Ý ñ»âkί'ñõ›®Ó>Â6¶àO�Ø]¹5ÝwÅæ×lá�' u«ÅÛA-åLŒY)-¹Zm^Ƭü—Vµ¶•èçŽþsù;³sÃzggÃxNÒÜ ñ»¶¦¯^q‰µÛ(¬ÈÂÜë1¶Ð]Ë9>–|Y>­¹Z}^Æ+¬ü•­n¶—w#Ä_éò›åÖ'"Ãé,/øÃe•¯ç>Rí¶=UvN¾L A¯ÇëÜ=*î¿nê êÏ�°¿‹Y¥Ê†.·7×éÒþàtÿáa’ì]^yid9·VÚOõåÙ±RÙ,­Öêûšn±fȬ‘{©K¥ã{.ô¯¥ôü×eºŸðÐY÷}nwyùEu2®¶Úî8¿Sð:W é}•¾Ío<ækdʶèýÖÒéxÜ5¶ïK½µ§­®ÈõGá§¶¸úÜîòò‚¶åÝKk·ãQÁ)v<7RK+}ºÞwËöÖK•Kâ÷R—KÇ!öÝé_K©ù©ô?àÝé]UdËòwÌÎÑç”ËÒk¾—è~ÄúšºG[i~Ú’sÁÉîŸáv’;î¬Xõ¦‹I=ô·êßK}ßFÍõâ߆£ã«�dÃ>Ûmäg9Š+ý×áòžÌãx˜Ùý9,ús]Âzó‡eÛgºú_ýXî÷YO·Ûî¶íÞâ߇“À=4rí³{ëœÙd•¾ìnSØÚLHf¶±ÉgÒ’îÂ8vE#¥×Òÿ[$²ÿu”üïoº•ÝÎ/øz¼ÐM›lîùæöG'¾ì~QغLHf·Ù}ŸJ[¸_âÒ:]}.õ²û.÷[O·ÓÖ•é_X~/ˆ¤‡SÊú´;»>;«uü›³ûÓ±õ;¼Š{o·Ù6?Lmú‹�Ûm+u.þ‡¯²¾¶Óíôõ¥rgù¼?ïYØᇟÿ×fGÿ˜sãoý깟øZì/úøÈ¿ó|oÿ½o4ÿ ÿþ½2‡ù¶_ ßäaÿ„W–ãÕŽ¹oá­øæäsÉ.Ÿ7È^ÿOÛ‹Ä»;G™>ÈißNîuÁ9¦M~­ôú—{ä»Òúú[í³óXû–~��îG-òiåïNeÕŽ´ƒ‰öF³2(ý‘};©eyÏæ“V’ßýïuõ­/þwÒßÍxUø{¼ä2ß&¢nóà–Ý[+l;§ð±ì¢ÄØlNcdæRZ~Šâ�ÁÃ%ÁƬ÷Ö¿¤;…æl%¶Xý-§ô>3Z_ëZþo¥-®‰öÿá©Ì³?? üšÆÊÌúßõ/‹vÿ —¸÷RJúçóî™±–Ù£­-§ô>5[oõ­3Ò–ÝËß!ÿÿTl123üOó°ø–~‹ev'ò†q¾ÄÄä|šØí»QG`õ´]_7 ÑK5·Y“-¼c‘·Òû#¾¶}98åŸÆOš¾Ý™±ï–Þcpliþ”=­Ão‹›õ†DRfÉ�ƒ>_*Ðýæ>/&Ó"?õ6&ò-^ÂKn¶¿w§ºž¼6ò“ã�Ì/«™°î>¡ÜGÂq§¬Qv‡¾>eÖù_™÷<Œ®I¥¤ßÁ›¶Y>”ÆÆÝìΗ֕¤m=xyåÇ7—þ ×3aÜ]E¸‹…cOôâìþ!||Ç®'ŠüºàáäerM-&þ ]±È§¦>6êft´­+H)ëOXËyÉðuò7àó—vçJ]ÎzEw®gzôvÆ^Ëë,l8õø9¹›~An>³Ÿõî‹ #:˜wgr��lË.³I­­—ß¡ hû‘  ©ð»Ë~Êð�ÈŽäYeÏvoη –q�¾Ý‡«ì±Ÿœ¯‚o+X2á®ó V¯‚k°3âÇ͆߼cEu»7áÿ”ý…á¯~ðžö뼉¥ËãÙtÂå¬;Ý~DðeA¸³YóÑ�Ó×ÞEõ^÷‡UîlÞðÌã8ŸŽçRì¼Å]·G™©ÛC‹“— ãû(fÀÙb}KïÃÏÆš ëïŽå�=5Û¼¾ú¯‚w'Yî#ÞpnÄã¸<“�çÛX~½˜ùvÖÜ�nÊ&ȳy¤ÏŠ\-†-oºLLìya¿Òøî¢Ç>šíÞ ß}YÁ;�­6önø?bqÜI Î¥a¦E�eÛ[2u»(`Ÿ&ÌÞ“>)p³ñk}×âfãË ÿŸÔ[áÔ½¯×½ëÖ¹ºŸ“as.´í%¢ç”`E™��ºã\�_ÏW™\-Ž6Ó[“v6E-Ÿ.31'¶øgŠ9£¾Ër³&2c!€8ƒø‡?fiÿ|žþÏõ.9|ñþ;Õþ¼ú—õ…£q×çöoö—õéÔÿÙþ•Ã?Ä›û¼Ïÿ‹¯ü¬:)^*ˆª›Z™à‡ñð×ù)øñú¢â 3<'þ&~$&>„ýUñE—þÿÿ“7Cþ«8ªëÿ‹Ž?Þ¨?W¼yµ�œlË<€8ýó òg¥øúè‹õ¼+e¯Íòs¶uû_Oè$ƒgüIJ”ÄÛöÇ#×åÇ‘ƒM7ºon»*9#Úíþœ4†lX6AÊ•ï‘]7‚=%vÎÂÎò/´p¶:¾¦Ñ_6ÂÞ9vÓkÙü‹ *ɰé¦ãš”Áƒ"9-Ùí.ŽE&4Y×ãò«åkäKMà§J]…Äó°³¼‰í -†³ª4WÃ�°·ŽcÙJcm{;�ádÛ6%4Üj³RÜ2,’ÝžÒ±ÃH¤Æ‹:ø8÷óIò«Å>,|\¿›``kùo�ý·&ï…øëÀsò¢‡_‘ÉpµðI¼ìŽY»ïÙ|«¡ÚbegãbÛõ¶yùxÏ­…f}û:èy#äÃ�o9g,Þm¹7(äÛmŽÿ‘ò=þÇ/o¼ßo6ùrçíw�®|³çl¶{,éïšyæ¾ùe–û®ºêÖµªÛýþ÷•ïw<£”nv¼�’ò=®~ó�r æ~V×u¼Ýmr¥ÎÙí¶Û<égÍØì¶9³ß4óÍ}òË-õºêÖµ­Pßï÷¼¯{¹äüŸs´ä\“‘m3÷›ýþó?+k¹Ýîv¹RçlöÛmžt³æl6[ Éï–yå¾ù%’úÝukZÖª »°9¿jó~UÙ=“Ê·¼ãŸs�îÇ“rþ_ɶ9m÷"ßm²/ËØí6›»äŸ'+&y+ZÖµô¥=)JRÚR”øÏ�ù Î=_ã’=ÛÙ7ÐÓÚØØÖÛ~V_\õ6æ˜X–_ZRËòó8î“c‹‹÷V”¥Ò_m+ZÒŸ–¬Ó×7ù ÜXòeõ/D÷fáÃí¬ùÜ­9Ÿ/ÁÇ¥ÕöÛvNnƒMŸ‹�m×}”­÷ÛO_±™úãÇ!;† 2º›¢ûƒ³1!¥µŸ7�u¯1å¸8öÝ_KnÉÍÐé³ñqìºï²•¾ûiZý�ÌêOŽŸ>{ãWÆùOøaä÷`q>a'ã\ç�ô‡bO×»|k »"ÌÌnÁ›�é…$Vþd÷çÛõ­¶ÒêÝu´®çpÿ†“þqlWé|BçøTšÜ‹¬§0Ýõÿ^]Jbßô䤶sþaÆoǺë¿òÝ$¥µšßÎŽ—Símïø€ù$åöE&§Å^i‰lÖÏu”僯¸=ô¦=õŽJK5æ~H.ºê~e/¥µ–ßγÝOµ·|_â䃗ÛúŸù¦%&¶{¬§(ßðu)�²O«g4åÚ àºë©ù”¾–ÖZ}¶{©ö·îÃoóM‘²Ùá|¶Û»Ò“yâ¶4´öÝ[kî‡#¼¢šÏ¶ß³ÖÚzÓí§ÙZUîr>>W±±çÉ“Å)®�g’Ü~ëñÓ/"ë"²é.¶ \^Ý›+&jÛoæÇ—É}Þ”¶ÚÖ´£ØMð“ò{2ÏŒR]d1ß-öÃÜ]‘5mŽÚßu"ÇÇíYgžJÒŸ›e–Ý}Õû)JÖ´£×Íð�òu2ÏŒ’Ýd1I-öÃÜ=“5mŽÚßu"ÇÇíIr'–´§æÙe·_u~ËiZÖ”_ól¾jÈÃÿ¯ÿÇ«^ù�ſȯšX7žy •t6Û|’ñ>·ßsìKmº(æ÷S7‚crL;©m’SÝé%}—RënôºÛ©LËþ4|þàò]ïÄNõË­”²·_Äø&ÛŸAJ_’Ò¿yà‘r.f4¶úý¶ßeµ£Nyä¼;o• åÜw{Å·Ø7ý<í'#Ôçèöøró¼­nÏ3ÿô/²•iç"ã<“ˆm²´<³�o8¾óúÅ›¥äZœý&ÛJ~XòµÛ,|lÌ{éüËì¥\Ó펑ä–pÞóê.ÐéŽ_&[ 8§lpW×\’ü þØ3¬Ñs NŸivÔþrZEì»÷*øO†øl`êgÅÉ'+øîïØ7›Òœ‡ »mn‡»86ò]5uQÏ[09ÿþKq/æ¼'ïMwÒÛv8wä`Ý$ž<¬~˜ü`ü…ò�;Çk°®Ã{Ñ]ƒ‘¯Ò÷ Ç–[ä³]lßO žñ¬_wÝëÌ8u'¾X㺔³c…tøW]ÓE•�ÒߌO�~Qà_xcívlw½Øý/pðÌye¾K5ÖÍôð¹çÅ­ÿw¯/áôšùcŽêRÝŽÓáÝ|WMN?w> þ`¹ÆG�XœG°9Ãaá�tòM^'xq)àÚn±úûk=�j0»ã„êµ°çlðù'Ķ7¸Úül‰9‡˜×ãdæáéå²/‰rÎ5Ïx¯ç3w®ä¼C˜èu<£‹ò-FM™š­ïß`A´Óíõ¹QÖ¶d`ìuùQÍôû.²úU`×äü{›q®?̸–ç_ȸ¯+Òjù'ßêrcÌÕîô;¼6Z�¶».*ÝNÇ&9¢’Úú_eô­?*Àî/Éø÷5ã\˜ñ-ο‘q^W¥Õò>7¿ÔäÇ™«Ýè·xPlµ;mvTU¬y8[ ˜åŠûkéu—Ò«N¸ß$ã¼ÏŽè9‡ßéy_åz]W$âü£�ípw¼w’qÝî M&ÿA»ÕÏ•¬Üiw̨²1r±å’ ˆ$¶HîºË©ZúÝ}×ÚGóñ)~Îl/å Ö?Ô>vá¯âýŸŸöpëoéVá×â ýŸÿöoëéVàÿâ]ýŒ>Yþúxõÿ)n¡@)W€6 ¡¼Žç·;kÎ<ŠÉƒn6F¾°;4Ì·ÿßÇëÁïåãO럅6·ÀÿãÅá§ò­ñßõ½ÃÛWà—ñßðÛùUøóúÝâ æøÀý¥Ÿ?˛į×ïZr²íejä Z?Ç$®/�~-ðÿfEi½î^YÉ+}³{q-¯ájédøÿü7"ÿá�kÿü.Ûd§útk¿Ç û·�Þ8q_dõý3Ü{þAï¶_Lkƒ\+3]ìšþ=ÿÂÏXïÿIm·Óý26‰G÷n�ñÇ‹û'­7ÁÈ7þûeôÆ·ø7ÂòµÔ²X?ød÷ÿ ýc¿ý%¶ßOôÈ¢þ0L‹-øÜèœJÒï©?›Ý{‘mÔ¥=”³¡üŽŠJ]__u.ºìË}=)ZzR¾¾Ÿg¬'�èCÍ\HéiõÒî6ú­LK,ûM–ºá·Ý4’æåE�‘[_²éo¾ZRÚ~í_KO­Ÿs·Õj1c’\�®Ë[�4¥ÒË>vTX°Çµô¥Ò_$´¥´¯å­_KM­Ÿs·Õj1£’\�®Ë[�4¥ÒÉ>vTX±Gµû+%òKJ[JþZÕ•:/­²;—»zw¨0ìÏ“/µ»S¯zÛ=_Ý볿#œòÝG‚Íu2é\_¿Ý.Ò”‡êÿCúžžï³Õo>>>&<¸°C�‹� XøØØñY>><6[0A vÛPÅ´¶Ûm¥-¶ÚR”§¢Õ(a‡±ñ⎠ŽÈ`‚-ŠaŠÚYQGe-²8ã²ÚRÛiJR”§¥¡C 8ÐÅ��PcÁpÁ1Û0ô²(¢ŠÊ[dqGe´¥¶Ò”¥)OJ.Ügô@oÄoò=•ĵøÓü‚|=ÿ(Öë9’[�VDͯâ(bÙq>¥®DWÉ�‹“Ëqî‡o¹²”ƺíEp`÷Ï�±Ì†‘}ü@? ™SOƒ]O¾›�r}~¿}ä&ßU—sëx–¶g«oš¤ÍÅÊåÐ_Óo>í}ÚŠáÁ[§ÆØåEHÂ|ýü€äq]D^õFúl]ÿ&×ë÷ÞAmµyPÇ>·‰ì!‹aÆz¾é¡ºLÌ\žYñm6ÑÓî÷Ý©®5ºlmŽLT‡wâ�ù[§Mõ…ŸÝÊâ�´»¯AfÓÉmçäxû~¼é}…±Ý«êܺjcŽ˜Ûžð†ëïÚcM›±ñnƒ+|>E�5·É¶Ù}mÛÿ¼ò_Í~Q~ƒ¢øNÏM¯Ë‹“ö7 ¾]Zðï©~$ý?ÊæÇš)vÁ�ÿ£5ñgn&Æ÷K$¶Y}mÛÏ<òWÍNO~ƒ£8NÏO�—/'ìMýòhºÛ‡ýKñ)}wܪh&†\ø`ͲѸçmæÇ÷I$¶Y}iÒß�_‰_5~Kù¸¾;õÔx=oªÙfj9w~v4Û+Ò¼Giƒ¬‡g6—+”A«Ûìy/'úYØVݨÐàí¶xÖìq²2 Ç¾ì«%ÏâáµñK©pu|�Ê=öïÉ^HbŸ3�ãfm8Oión³"�áj´y¸œÏ•K¬ÍŠhé•°ÙÁƒŸ�}>®ª+½h•'ŠŸ‡¿Åþ«ÃÖïü‘Üî|‰çvÇFVŽÌ­— ê�Ne+‡“dXzm&n?*ä²`eE,UŸc²³?ú}MdUõµ)Ï¿§Œ=[…­ßy¹ÜyÎiSåhãÊÙpžªÕeÓî™6Ň¨Òæãr¾I&TRÅYö0³ ºŸSY}mN‡ÂÂÓñóã6<$òb,é»?ÇÞ NG›÷É®ç<+½{Î~ÿ™c¦Ï7’pËôÙœ‡'JÒH£Úýÿ’S󡺗]mÚ7ßÿ^y-t½™ãÿ³‘fýö[¹Ï Áþ×Üâ™ù‘ÖÏÒyœ�‡]§ÊäX²WêÅÖ™ø¿R•÷Cu·]mÚ=åWÆ·�þmE›'“¾.uOfïóàÓbOØ7é$â=·f¿�åK™¨Ôâ÷Êã§…¤Åžy+\waËl—Ù$WÙ}ÖÖ/^r~nêê,¿`xsËs<‚á˜wgçdõ$ƒ[¥îm.®I‘º\¼JâqnΚ H®ú¶ãC¦ØÍ5c�_“uõ¥‘³ó;ðõöÿVáí9ω\«/½xž%s2òz×’Ç­Òöæ§]),TÓçcW ‹v,ÑcÙ%Ó[ Zlé/öG‹…•}þ–ÆïÌ¿ÃãÛÝ]‡³ç&rœ¾ôâx•ÌËÈënG·MÛz�tTú°ÓQ��\.1Ø’Ã�õ–ØbÓçI'²<\,›ï­-‡ŸÈGáìn …�Ø?½“ŸÜzœX#—7 »³sÅøÿh]%r01ä¿‚ö|\O®9M·W.|‹°wXünü,,:Ò<ížT¶CXÓïøþûŠo6üc”é6ük’qý–n›}Ç·úÜÍ>óI¸ÖäI‰±Õmõ;q³õ», ¨®Šh&ŽÉb’ÚÛu´­+Dwwº-çÝm¸ß&Ómxï"Ðìs5Í÷]—¨ÝivÚì‰1v½¶¯a>v»cƒ•ÑÍÑÙ,R[[n¶•¥h�&óE»ã�¯äº}¯ä:-†^£y¡Þkòõ;�6×_=ø¹úÍ®¯>|ívà &+£š¬²Hä¶¶ÝJV•¢Üã‚s~±å»îÙ<7•uï;â¹òj¹? ç{oÄùoÚEm—Ë­ßqÍöãOŸ’[[¡È†9)K©ZÓí£ä>Så<¨:øÓäOex¡Þ=wßÝK³�]Ízë}Û¯¼_¨ßkd¶üMïä8ø¹™œs”i²'ÁΊ9b–¸óÝX¤ŽZY%¹³ÇNÿìO»Ÿ�wŸVìlÀåü uË ¯­~«{¬’Û±wœ_��6<ÙZK¨šl,Ë#’9~ŒÕº)#–Øä³5xíß½‡ãsp>ñêí�˜»�î¡ÙcÁ“õ®Õou²[v6ïŒo±ñæÇ›+AÉ53M‡™’G/Ñš·E$rÛd–í炾iw€Mõ¿“}-·ËÅßp½¤Xü§‹Ó;îš.Îë­†N-9§XrèäÄØcdqî[¬Çút–üi¦ÖgG�²ÄúYøX“Åg÷ŠžJõß—½ÖÞBu~]eã…¡‡a.²ym“gŹ5×`ò~¼¥¶Gm»ž-½ÇŸ {­·éOX©45¾ "’ë!¼hò€ùQÑÝ{Þým•tÜgŸháØW"û.Ùq½Ü_‡È¸žêØý,³sÆwxóáäVÏX¥¾« ×Ã|wÝcÏ�>Bp/)ú?¯{Û­ò®—�sÝ; µóÉeû.7»‚ë°ùÜÒ:RËwguøsÖßX¥º/«×Ã|wÝoÿŒ~Fu‡—?õ7’�5¶»qÖÝÅÃuœÇŽI‘6¦]®ª¹vß�¹âœ–=Ówª×ó.¿ÅÊÓîð¡Ìɦß'²]tU«aYÙ�YÔ/Ä;çõþ:x÷‡âÇ\îk‰ÛÞKé¶0rœŒ)c¦oè›eŸSÉrdöåY“‰™ÙYöK¤Ãº°K Úø6ÿŸñAuÜ ùèó¢ïúÆ®·®7ly¨Øcò,Œ9qþùĺVÙeÕò<©mûÅ2±rû.Ùt¸wÖ "— ­m’)à†µàÏœ·tDbø×À6ÕÅío"5;9FÐÓ3‰ô½²Ë¬äYRR™ÉÅËì<ËeÓaÝXoŠ\(vµ¥ñO 5¬l¿·ÉôÞx�Oº›�ͬòSËm^û‰éöš]¶.ï«zkîØ=•Ø5‹î[,ì-¯(ÄίÐKoèì‹233¶X¶ei>�ð4B1 uc€c×ýyÏ;_˜húû¬¸o&çüç’åW Aĸ~—aÈyß&ÈdÉ–ÌV® œÉíÇņù¥º–{"†;¤¾¶Ùm×S×pNÎ;G–éx\q.Eι¯#É»EŸ¦£;{½ÚÏ2äÍn·]FT¶cbÁ$Ó_K}�Ãò_[l¶ë©ëx'æý¡Ë4¼®x—"ç<Ó‘d݉£âÜWQ�¼Þí'²2f·[®‡#*[1±a¾io¥¾ÈaŽù/­¶[uÔË}ÐÝÉä¿gñn—è>·å}­ÚÏ><?Ãø~²]ŽÇ"·_e¹;ù¿¡àh¸ö¢둱ÚgÍ�­ÖbY~N\ðÁ’[)o  ¿-äxúÎmç7bÍÀ°2!³*Δê=†§mÌí­öKíÇæ=�“‹·âZ9±²#²²âiñ·VäÁ%}» Ym­)%Ÿ�&ÞÁ­æ^iséx^ÑG•gNu^~³gÊí­ö{íÆåý‡‘�´ãyqç³Û6&£onD2zDZǒ•¥$¥áÿá×äÛØu¼ÇÌþ{/ Ú(ò¬éÞ¬ÏÖìù]µ¾ÊÝn7.ìŒm§ÓËöRÙ±uûkg†O[6òSÒ“Løòü#Zø`ã�™òGÙ²æeɯqÿN�Û×ûìã›Ð]«ÜÿKï9²ÇKöz�Æ«ˆÁ–KdYZîQ4uô¬›Gÿl~_.ûœ[ÑúÒ°[Ÿn=¾û½‘ÛK«JÈÏ ¼ñ#ưëÒ= ×Ü7oƒõ~‡0¿QNGØÑâ¤SÛ'`ò™w\Òè%³×úß¾…¾ë½¶[K«Jȳ¡<ñ'Æ8°«Ò} ×Ü?o�õ+/“QNGÏýfŠ�ÏKû”ɺæU†[= Ó:�[î»Ûe´º´¬¸¼fðƒÄO tQè|_ñת:^:èõœse½áüO]:å�=óˬƒœöF|y݃Ïò1&Ê’ûr7{=†Mo’ë«%kZÕ¸­®mkiÀ=äŸÆß„^YdzÈî¿úÿuÉö²M•“Øœw[üì»öbÉ‹~_<á²i9&êì?©I#ÇØÏ™…t–[Y ¾”ô®›y ñ÷áÇ”qìgîNƒà›¾G²¾éò9æ“]^Ø·åÒ ÇÊÈç\FM/%Ú[‹õ=öcæäeaÝ}´ú�ßO±§Aü~øuå {»‹¡x.ë‘l¯¾|Žy¤×W†ö%ÙŸBH ÊÈçû&jÙÑ�äÜc’ð®C¹â\Ë�o8Ÿ*ã›­G ã<—UŸ¢ä-¶ ·A›¬Üiöpbìu» I­­’Ã4vIeÔôº”ª8¼“�r>¾Ûñ^] Ýq^O ÏÈÕïxç#Õgh÷Ú]ž%õ‹+]¶ÔlàÅØk³±¤¥m’£²K.§¥iJ£™Èøß"áÛí·åÚ ×äú ü�^÷Žr-^v“{¥Ùâ_X²µÛmFÊ m†»;JVÙ"š;$²êzV”ª�‘Ö}�ÓœÛ}Ö½·À¹—Xv'Ÿ’ðNÁã;žËô¸8»L(·s�aköúë³uyÐdÃõa³ëcO¶zÙ}·Wá¾#â¼@_Ÿ…/þï?ø®ÿã•§áŒÿ»sþ-ŸöýJ³ðÉÿݵÿïû}§1ø/÷$¿ñ;ÿÆ™/Ä­¬N`ÇOš�Úø!âîFÚljä7yÛ¸á]CíŽù¦âØxÐaÙÎ;6ëm›ȥᚽ¬1ëkuÒSôÞv÷Á>4YVS“¿/¾y×ÂOò á[(ñ»ç¹ÿJðî§öÅIåãxðA�NcØ÷ÇtÐYmÜ;[²ŠÌ Ýõi]Önd‚lk2m§(>^|ð¯„þ6äcp­”xÝñÜÔÚðþ©öÇI¦ãxðA�NaØ×ÇY ¶Ë¸†·e˜»êÓôÎf×Ã6=™ÓŒÿ9'_~ï9‡ÚëíòS¹fÙu§Žioã¹¹šÞO.¿ërNÚÌã›Û²ÿKñn¢ÔåE—5k®Ù`˽ÍÓëóc³eYl®VI$šI&šKå–[î’Ydºëä’Kî­×É%÷V·_}÷Vµ­kZÖµª¿Ée’i$ši/–io¾Ye–û¤’I$º·_$—ÝZÝ}÷ÝZÖµ­kZÖ¨Ë,“I$ÓI|³K}òË,·Ý$’I%Õºù$¾êÖëï¾êÖµ­kZÖµTó¹ÜíùßkÈ9×e½ßovYÛ�Þïs�•³Ûîvû<©sv[]®Ë6Yóv-†l÷Í<ó_|²Ë}×ßun­jÿðÿšßþ8<—ùæÒñþ™ã1ë8N—'ésŽáåÖfëzã†ÒآȮFÖ \™÷œ£&,ˆ¾ï¨×Ç‘›}%¶imƒ’äż~ü|yçg0“IÔ¼z=g ÓåÒkÛ\¦Ì¼Ä}°Å“~ »qçŸ{ɦƒ"*ÁªÀ²|»¾´rÍH1~¦L{ÁáWÇç�þtòù4½KÇ£ÖðÍ>U æ�±ÊmËÀàKÛYáK°‡y÷œ–h'ްj°#Ÿ.êMd³R _©“Z>.þ<®ùJæIÖØ:ó¡8·$×è{;ÈÎiƒ“ ã3Íd; –ƒ‡jcŸ;³û E-¹?¡µóC-rp¿JgjàÏÅÉ’r |/xoá6�’ÁÅ1û·¼uµÆÌ›¹»CW…°ÌÕíà®E¹}yÃd¿;�õõ˜{ *͇“ 2·�R[ã¿g4u¥)3¿ >"|KðÛKȱx¼ÁÜúúceMÛý‘­ÄØgk¶ÐÓ_½p)%Ùœ€Ç�›�t˜³ãÓ#s ’Ý»ìô¥&cáwÄo‰¾ãé¹/ƒ·û—_÷|©»w±õØ› ívÖbË÷®Åd»3AÀ£ÆÍǺLYà¦Fæ+%º963ÙéJX·ñÛð§à¯Æþ¯G¹êî¶Åì>÷Áŧé_${__ÉûF]ŒØûÜLéxMÒãSGÔº¼�o$Ê×]�Ç1°'ÍÕÒv™;)¢ûÍýqu)Ôw[@Ÿ/?';77lLÌ<¸cÉÅËÅÉŽèr1²qæ¶øgÇžëeö_JÛuµ­+JÒ¯ã“��™�>dåbeC.6V.LVO�“�=—E<Á-·E43Eum¾Ë©[n¶µ¥ièþ98Øù¸ùy˜ðeâeÁ.6V.LQÏ�“�>DÛtSA4WVÛ캕¶ëkZVž‚7_$?‡»¦{ß[È;SÃ]~ƒ£;®ÛoÙdu¬ÝOJö¶VKò°ðuxØòEÖ<�*:ÛLiuö[¤¾H­Žl8+<¹ñG×äà‡§û¿]¼ì¿ðt]'ÜVÒí„üÚjúwž_e·ý|T¿ ?‹Þiaoû[Å\%â�“ùl6ÙWéµ¹:î‡í�”Úü8aÄçÜA‹•ÚM™®¶[¹À²i&ÍÍÊÙk·9SE$“í.¬ì>“ìWÕ}­Ä·°xNÛ#IÉø¾÷îû n~=iZV•²é1³prà¾Éñr±ï—3Hç‚I"’Ëî‡eõŸ>éÎyÊ:Ç´8®ß…sεŸMÉ8Öî A�®ÍƒÛu+K£º\lÜÈ/²|\¼y%ÅÌÆ’9à’Hd²û¡×Ù}iÏzw�ò~²ìþ-¶á|ï‡m'Óò>7º‚�gkó`ôº•¥Ö]&6n\Ù>.V<’âåãIdÐI$RY}ÕÍwÿ��Ïâ×msŒïþ½ä=aÚ\i6¯�ñnG‹H¦¶±É}¸»mF~=óê¹ÜÁm25»]|ù:Ýž–dbÏ4Y%ÞឆÀÂü4~såèy·.ðCŸnå—�óhw“ÐôΞImÕs-FÙý‰Â5Ôû¼òG‡ÉøÖÛÈb¬°bcdjs¯¶Û²6VéAþ¿4r´Ü»•øKÎw2I¤åÑm»¤)�‘,´Öò­^%s{…k­ºÉï��ñüK·�CK ÅÇŸYŸ'¥ÓçVµ“ßááó;+M˹_…ãs$šN]×°úJ™³É%ºÞU«Ä»3ŸðÍu.ŽkãÅä: [·pCKáÅÇŸY�%-º|êÖéÏþÿ‘Y&‹±~5û?‘ÛZbǺÜlý.¤7ÍnGtu.“ô·$¥·[lóGÌ5:�F¯Ýùü§c—5iK)I›%Ê–êr 'ùðßÏ/ù_Ž]ƒ?WÇÉ·Ü?yNa�Æ#æàßÄùö˜õÐÉ¿ã6å[ŸLZÅîûäuŠ·{ý/ôö×Oüëñ7ÍŸ9_�y<ên¸�“î8žÞœ²9g+¿þ-Ȱ7Ô‚º97|vÜ«s©…X}ß{ޱûýþ—{}µÔ:ߥL©ý¿ù]Q·ò{¥ãñ×È>ßèȹüº>¬ç;®g&“UnŽýݺ‰þ�6j-Øí­×]=>ߥL™ý¿ù]Péù4ð÷Oà/œ�óâOæû>ÆÒuÏ…Á¬æ[�6.ƒk¶Äæ}kÃ;+3õxYÛH²utåßsºH䥹wúÔ²/Ò³_Ø‚Z í/Å/Änɇî^E—ßy}5wSo¸~–ÌL~´‡ŸS}NU¯ÞçW"éåçœ:ºÚà×KíöÒÉþ¯Ôõõ·Ûéw`¾1¾+0þD¸·lrL®ñÉêJõ–ÿŒi,ÃÇ먹Å7TäZí®}Ù7dIÍø—èúâ~�¥”²–Mï÷úû­ôô¯_¾2~,p¾Cø¿lr<®ïÊêZõ–ÿŒi,ÃÇ븹½74äZí®}Ù7O'6âŸp®'èßm,¥“{ýþ¾ë}=+'„¿€þ±ùRñ¿³{ëŸù Ï:§'„÷vÓ¨uÜg‡ðÞ?ºƒ* 7à|Î}în×u³²K¥Í“œÛ�f”é>žé¨w7ò(z“«:û¬¢äàÛ«“{âZŽ+fæMe™yöë¯ÙÛª¤õ‚“ÍHk³ê_ézc®béîžêޤƒm&ú­ë^ ×0ï%÷].ê.Åõ|f=´ºû2s,Á“cf²“] &–‘Vÿm/»ÓÝY(ô×]ÅÔAÕ=MÖMô=_ÖÜ®áÞK‡nº]Ì\'ŒjøÔ{Y5ödæYƒ&ÆÍe&¬4šZE[ý´¾ïOueó×ùîþ½ñ·§;½{Wm]/ë.5›É¹\TÇ¿7"dÖ×#&k®®'Ëo(;Ì.ùæýëØù2[±äÙ¿vãÜzÌ«²uœ†kï–>3ÂôŸÐqb¦¿IƒôIm†+³s$Ÿ2kk‘‘5×TòIç·f|‘ùiØ^Nö>5tX»šãñn±àì²¶ºî¯ên;‘�^Áðsr«gÞò ûþFÃk•X¸û öÇ;2,l[2)�®µ­­­~¬,,Ý–f&»]‰•°Øgäǃƒ…�.Vff^L–Ã�‹‰‹’O‘“‘5ô²Ë,¶·_uiJRµ«õaafì³15ÚìL­†Ã?& , ,yr³3s2¥² \LLX,’|œœ™ï¶Èã²Û¯¾êÒ”¥kWéÂÂÌÙfbkõØ™9û ü˜0ðpp —+33/&[aÆÅÄÆ‚É&ÈÉÈšúYe–[[¯º´¥)ZÕ÷¸¯åë“qþÂxÞû˜ó[¹ÖñÎ+Äø®ŸaÈy7&ä;¬Èuú}Ðê1òö›�ÎÛ?"81±q¢’yæ¾Û,¶ë«JVG ~Ï!»ßUÏü¯ÜçøÍ×9Uƒ/„S[�²ïEƒ[±%ºÜ�&mß¡úÊ,¬i¥¶Ù6öåí1ò!öÍ©úw[%dáWáÿïnêÆÕó¯(w9Þ;𠯡—�ÂbÀÇØwFû ÿ»É[2µYµ»OÖödA-ô¶ý�¹›(&жM«¶Û­‘  ~{׺qµœçÉýÎw�+èecð¸ð1öͽûèI[ruYµ®£®lž o¥·ìíÌÙA4U²me¶Ýméx|uþ.æî n£³>AyößÇ>±‚ÜÌŽëI8îó½öXyx{˜ñçå\·e�É:ûªgƒ&šÜØðéƒÊsò1eŸ6 >dd¤|mø�øüñwN ã¿å|«KØ]»‰js<Œü ¯¾-ÎG.‹;GÄö’ݬ—hp51_éOèeRñïâ×Áo±ð¥à½Ĺ'ij û¹çhbÙÙ|ÆLü_Höø9œ®ÌýgØKï­dý‰«†úúCû(’g�¿ xÛ�‡/èN%ȹ6-˜w_ÎûCÎÊåòça{þžÛ /•ÙŸ«âû }õú•Òbk"¿ìþ‡ö&1âÅ/ǧ„’k6^:x¯ÖO˜ê2ò6þÐäÜžÆíÌìí\z�”ºžÒì\¾SÎxöË Ë­—[�‡®¥e—Ù�e$¾•é+ ®�:ð]‰Õ}_Ûú ¸§lõ¿í/|Öä_Æû‡ñîm »"ÊzY=Ú~K®Ù뮚ÊW캱û©ü׊ç½k×]©¢“‹öá]�Æe–É¥ã¼÷Šè¹†ŠI¬þrY5‡c¯¾[?rêÇZÑâ¹çZõ×ièïãŸÀxWcñ©%¶i8÷<âº._£’k?œ–ýO!ÀØà]-¿¹ucõ£Ëó^Âû'Šoøbñ/ϸ7*Öϧå3šè5<«ŠrMFU)nV«Çw¸™ú}Æ·&ÚzID2E}?-µp�ËOÃ�á§ubì÷Þ>æò»X¤—E6_5ê}†}ùdË&ׄr-…Û�GÞmº˜ñþ†Ûëð°ì¥/¦ Õ¥l»Š~Rþÿ{“c¼èÜ�ß�|îXä—ù·¬3ódÈ®D²m8F÷?ô–¯êÅý*i¶šÜ<[kïû¤Õ§²î,yEð âGqcìw�•¼ñ»�KòcÁ¡“#–õ†~móÖy$Úp­îwé=_Ö�Ò©§Úk°ñm¯¿î“VžÛ£åçá‰øæò²Ý×)éþ=±ð˶vædþé,,Yú›7i6.›þCЛ,œ^#…¥Óâj¤º,.!“Ãk‘“—4ùSd_ZzD?Í_�ï+ü äT×÷§’níî~W"ê¾S4¿|» ’Ó]6êxõóßn«o�®ÚÖ(«51ël–è©ù…ñíäÿ„êawG¾N›™fí~!~O ë.I4¶ä_./!ûž$ÚmÄÖbM[u»l}~Êèáº[ ºl·EoÌ�¯'|#ÞÓ ¹¸=÷ñ ÜËpøïkqòwýiÉ%–Ü‹ ÆÅäÄÅ›M·šÌI®·[µÇ×쮎+¥¶ ¡öËt~F¾üÞøÒÚæí{ƒ�Yͺ>]¥08ç‘}aLÎEÕûOú.˜8ܦ뱠ßõŽû#'q˜|ƒ<ìø²,Õϱ‚ äÝ¢ $i;“À jþo‘Û´{‹¾?{sum4»ü�×&ñ¿u²Ê˯èÎC%27|éé|•› ]¿²Ì�Ö¢•ûµ–ì©�ºyóñ"²R¿‡ëäíNÒïûKoOÑ[™·<“Ç­¾t¹5ýººÜ�ç/êû¥­eŃse™;�Wº�[nu3¡¬“K™‰ ’ŒøóúíFÒïûKoOÑ[©·�Çݾ|Ùý»ºÜ�×-ë ÉueŃue™;�U+ô-·:™°Öù¥ÌÅŠÉâ~•©y.ª¿}ãÈ2r·Ü{ÊüFÝçE‹%Û-�וö7Kåì,’-†Fg†Ì®CÇþ´YU¦¦›L[²qñõÚ¼KæF–šY‰¶#ùø”¿g6ò…ëê;p×ñ þÏÏû8u·ô�+pëñþÏÿû7õ¿ô�+pñ.þÆ,ÿ}D¼òÅ×r¼oå™=k°ÈÖ[orv ¸cÔ÷ës÷Ú<½Þ‡–ól�E½‰…dzµ™4ÙÁÅaÞì0¾�m¿ê_—÷§? §’ŽÌ<¾òò'©z¯'©5¼CÉ»o�aO,VKv³eÆþ²ÐC—÷V)eÅØgÁmÔ­ÑÝ-¾ž½—êÃaßûøñò»ŸÈ®ëXgƇ"ºþ  ä}§¹Æ’H¬’í~ÂÝ”½k§Çʆû«—ãfgCmÔ÷Yt¶úzöK©ÿ ¿oãÆÊînþ뵆|hr.×ð�"íÎ,’Ed—köì¥ë�4Pßuc’ülÌØmºžë.–ßOYt—àØí�¦³?Èÿ6zïƒî!ÞG÷þ)Ò]YÉ{S[°ãQM ÓW°9ß"éÌ­>ó7’Ym/ãYøø·ÖÛë\ŠR±×|¸ámñ{]mìï$ûï—äý+éI¸·¯:æ O_ºý9+�È8÷iÉX­öOëgÕõ»êYùÔúw}]Üំ³ÆŒz[Ø~@÷§(ËúWR³ðÌNÀ±ë?¦?¶Jbï8ßcËH©[eõ³ëVµ÷ÙùÔöWênÏ ü6~5àãû{Èòå_Jê}n‡Àx?ÖôÆöÉ÷mçìy>•+lÞ¶}_Zûìüê{.ú�Ö~ŸŽX°1#Ü÷ß›ûK1â·;3Yκ+S�“•Hí¤óbkr¼zÝdacÉ-+u‘ß—‘u–Ö–ÖKëOur­Ÿ†3ãòË,¶îÆòº[­¶Ûn’þÃêÚ_%iJR·ßHúR8éuõ§­}¶Ûo¯ä¥)ö2EŸ‡Á[l¶Û»Éé.¶Ûm¬—óε¥÷Ö”¥+}Ô�§lŽ—]_¶¾ÛiO_ÉJQ’lü9þ Ûe–ÝÏ|›’ëm¶ÚÉ<ëz_%iJR·ßHú‚8éuÕûkí¶ÚzþJRŒÛ¬ü)øÝ~V¯È}ÖV&&NçgÜ¿Ke¶Ÿá›g°‹Mŵ:ˆó³ä²²Ën..65$¾´Š(ìöÙLsÎ? �‰› uñõ¿�þDñ=½c¾‘æóju¯`ë,š³Au—ݪÑp¾´Ë¾+qí–ÚÙ÷Ú][ï¶ïu)el¿ÁòÿÃ_âÆn¾øø{ùƶՎêG›Ë¤ë®o®²ZË m¾ýf›…uþL‘Û ²[[i™mkuÖÝî¥-­·ø.]øm¼ZÍÀ¾>ÞÝýƶ•ŽêG™Ë¯ë¾m¯²ZË l¾ín›†uþMñÛ ²[[i—mkuÖÝî¥-­·á��àüøßŸ.š¾õóg_›-’× L®Ñ{ I®õ¬u— Ïð2²ñâõôö}ê;î¥?ŸõûZùÕ¿†÷¼Ñr]Î&‡W¬“±tÛM—èíeôƒïæ’¾—V:0W[~Žâè&||íÎ ß½qØü#«»³©»“ãò~9É:ï–ɤá|ûEÉ7Xš=^¶þÃÓlö4Öëo¤_cƒdÒ}—V:5—ª ×>ñ—˼‹éÿ1x‡gñÞ–ò³¥»ƒ“ðŽÉêÍ×Zn¡ëN·í?ÍöxZWÅùWg`òÎsúK÷x"ÈÕè5ù™7{ïŸÊûm—ªUIQ¦¨‰â¬ÎÈ�‹øI­¶ëi‰—¿ïüé¬öÒ·]‘¯×tþ>5Ô¿ùëm¶=œÔ­)öWÖž¿’ˆµ~&½†L|_ýUµ³îy»þïØOml¥o®N³]՘ص¶ÿËm–Ŷ›ÖŸé«Z1/ÄÇŸ‘ðûWmmû¦fÿ»sç¶¶R·×#[®êì|ZÛå¶ÛbÚÍëOÝ­iüÄ=ß&Ùâx�âW а~†ßy½äÙôºvUv|K¬·Ú­EaŸÝO§1y®oÔ³Ò¾û«e}iìûaȉb&Šö@�Çm?)ò¢øÆ\Ócâr>ãëVF7³ïcíù®“_4Ð}[o�ëEEn·Ýmm÷Rž´­‡Ç�.?$ïþ�ã¹RÍ.ÿ¸zÏK“>?³ïãí9®“i`ú–ßÖŽ9ë[}ÖÖßu)ëJÑ—ü{ÓAÈûó£øöT²Á�¾íþ´ÓdO³ëÃÓ™épf–©mñýXãžµ·ÝJÓÖŸm+Fï|ecÙ™òGñó‰-o¶<¯7üPÇ’ë+J_K'ïŽõ²·[u´¾–ÝözÒ´õýŲ«AÖ|®RjÇš¾Spÿ |eí!ù„˜S[ÁøæMx®ƒ/&¸×ó.³¶ºþÃñ~Ÿ»*ë·¼†xlȾ$¿“å]oÒ‚JÓZ¼¾òW‰ø�ã§g÷ß-¿høWÈ“Žhòr.Ç»–ó}�>áÃxž=ÑY.M.Þïç‚)¤Š9+‹‰õ²n·éÃ}i­ž^y'Åáøž5c¶LŸvó}<1Í$VIv.'Öɺߧ õ¦·y�ä÷_øaãuùEÙòzðþ™àÛ>W‘­³*Ì®O¼­Ðê¸wÕæK ðcnùï2Øàip/–ÚÃnn|U’´³ÝZUÍÙ��Ì»ƒ°ù¿jöænCλ”îù—,ÝOdPß±ßr„û=–E˜øöG�‰Ù9R("²È`Š–Ç¶Ùm¶Ò¶Èì>]Û\ÿ™ö>Û;æ¼ÿ“nyo(ÛÍm‘×;u½Î›a�,pEm�bbÛ4õ¶"¶Ø`ŠÛcŽÛl¶Ûi[wcö-íž}Ì»;ží¦ÞóN}Éw³“íæ¶ÈîÎÝo3¦ØgKRØ1q©4õ¶#¶Ø ŠÛc²Ûl¶ÚRœ¿#ü‚ìß*ûÛµ<‹îMÝûþÊíþe¶æ|Ÿ2“l$ÀÛa-,×qí;Lý¦n·‰q=<8ú­6 ÙÙ¯ÕaãâÇwӆׇx§‹a@‰þ%> ¹—ãþ@yA‰¿ëßë\m·âñÓ#MÎ{Ê Ò<ŒIu³Ie™\_­3lº—I¶¶”ÌÙAùšïe²Óa�ß�‹_…þQå-º÷òS{Á|w’¸Û^1Å¢®FŸ™÷>-~žF,øsÝm™|g®sã­+vÎÊ[›²‚¾šúÇl–gÅß/‹Ÿ†NOå4]ëä–.÷‚øó}q¶œc‹Çõôü˹±«ôò1§ÃšëlËã]u�i[¶vRÜÍŒôÀ¬vÉntRòø<ü7¹ÞTñî/åמºÎIüzÝ[¬ßtÿEãefq¾mÞº+�Ÿ9æÛ,[±÷|§w¸µ¤z¨±/ÅßòLy/Ï‚mv¶šü½ÄåºÏ¬:ó¦x/ë.©áœw¯øÁýÇxŸÖcêtÚÌjË&L÷E‹�e´—/;2y22²$­ùy2É4×É,—ßtÏ:ó®¸'RðÍ]õŸÐp^ Ű¾á âüg[�ªÓë1î–L‰¾Ž&5–[vFf\òO‘5þé²r%¾Yo¾Kﺳ3ëκà�MÃ4wÖ|KCÁ¸?Âû†ƒ‹ñ­v>¯Q­Ç¬²dMXqq¬²Ûòs2æ’|‰ï÷M“‘%òË}ò_}Õ°[…pŽÖÜOAÀºëˆq~Á¸¦¶ 7áœ+AªâÜS�ê1i[quZ;£ÄÁÔiõ¸Ö×Ò81áŽ+)ù-£Ý½£Ù½@”ß#ß^;ü…qì�ÞÓ®ò [‡HøÇwñ­N4»<ÚcâÛ‹‡¡ì�M—áYÏ8ÄqC±RY¡Øëé´ÄÊŠ+§‚~eü€ü[ôžZ¶Û|Xzß¼õ¸u‡Œ÷7ÖcË´žØqíƒIϵvɇ8ã1R+)“Kv ,ôÄɆ;çŠ~gyýñuОwèfÛm±aëŽò×aý5ܼwW�&Ï"ØqíƒKϵvI‡g7ãqÛvÅdÒÅ�ƒK=12aŽé¢›•?'ß^*ü£õü¸«¦þwŸãYÚ^¨ò;‰k±äç\ZË“±Ôê¹ ÓàcvO\cï2d›'�ì'‹Ò,¬Êës5y™Wg[__”þ*wg†½Å¿éùâ’ñža¦Žü ¼y.Íã|¿�fÉ=šžaÃ7”Š(7¼km\i-²kid°dC6.Lpeãäců&nM—ãåÁñߺèÀ Ó@”¿G�Òt¯ìü?绚AÖ^Fgþ�à7çåEïv¶�áÙY˜Øøñö†ƒ]f¦ëm²|Œ­¾¦­·ß%k!ïÃõæ½ýEÞO¹¾Ú�uß~æ×gÁ/ÍȆ .;ÜÚÝm±Arddcà }‘ÇõÖkn¥)4ÙL\ÙO«%k!€5$ê>ïÚx£Í¶Ô‡¯;ó6». ~nDPáqÞå×k©G~FF<0ÙØú}šë©JK6FÏ Y VÓêIŻð˜ü�KÖÝÅ̾:»+s=xWzäí;/Çù2¾�qxçqñ¾=.W`q+e‡K&m¸}™À8üyÐ]—²‡_…±ã_GüÝÜ·_:dÒ“AXó|Ç—ñ®¾â«žó=¾/áü#�ï9+ßç}O¹i8×Öenw»|Ï£³}×[«Â–i=–Ýw²ÊúRµû–r®=Áx·%æÜ»m‰¡âœ;�îyW'ÞçÝu˜:^=ǵÙ;}ÖÛ2û-¾ûq5ÚÜIf’´¶µ¥–WÒ•|YÊx÷âÜ—›rí®.‡Špíç”ò}æu×Û…¦ãÜ]“¶Ýms.ŽÉ/·]­Ä–i+m·V–Y_JUñù!Ðqó•ò½æŸŒñ~1§ÙòIÉ9Ë K ãÚ .û-Æóy¸ÙM�¯Ôéõ:ìi22rg’8`†;¯¾ëm¶µ¥[þrùSÉ|Òò›·|‰äVåaãó~I,|;A•$r]Å:óGšn Æk÷zY‰\­_ÂǦd±Ye™{ ò2ko¾këZ×<Òòo�ù�äÇjwæú™x˜ÜÃ�IÑeÉm÷q~¥²ÝG 㕲ïij/_Çñ!®mðRØò¶2ddûit×zÖÑæ“<‡Ëï%»O¾÷Öåbcs ü‘qT–_wà:k-Ôð®;[aº¸–åkøþ$5̾YNÂüŒ�oºk½iùùTó³ò/çsù+™>â. ²Ü:C�í뛼?¤8l¹:Þª»O•½äxÜ{q¼Åºmþ÷ .üá.çe4¶É©F¥µi«®v€7‹Àß{ßä·-ënŸÕ[�Ç´£öŸÚ;¨%§ ë>;�‘$0çmf¶ø¯ÚïöµÇšÍVŸêæìd†[©ô±qòò±·;Â/;¯ÎîÕ³¯:«[nÒýÃ?²{/o¿Á.¹ãÙ³É9›),¾+¶›í§Ý¦³W¨Ç¿ï{ b’ïèX¸ùyXÛ—á/ƒ}Õç_iÙ×½W­·A¥ûŽdvVÞ ‚}uÇó'’(³6R[|Wm7»O»Ëf³S�wÞöE%ßб`ËÊÆé¿ÆÅ_‘¿)Ñ_u6ðG«¸Æv¶^éïÍö¶LîÕ: ÚO5•³Üí\üÓ�n Ä’=?ÃÈŠ|Ü�KògÁ×Ç•°Å°{ÀÿŽ?þ?8�²_I²«6L³³ð«ÀO¼àÑqΪã±ly¦ËÈ9Ïmïñq29ï5Ⱥìyò!ÈØÛk¥ãåcGv.Ÿ±áAôí’úM•Yre´GÀÏŽ_~8:¶þ°ñ“¯bÑ˸‹S`vW!¿wÚÝ«´ÓcÏ×°9�˜87ì-ÿ7*\Mn:Md¹¹5ÁÂÆ¦DÔ¿~¸Ý¦ô9¡òñaãgÈOÉ¿œiâà½Õ¬ÕM…Â;ۊ뱩ËuGÕÖë9^-/ćŸðì|¯Jݮ͒ÉaŽùiƒ“…,×Ë^xyßñ¥ãÏž_'øk¨‹†vþ¿U. îÎ3¯Æ§+ÓI·É®×rL~,\çˆA•w­úÜÉm¾(ä›îYSKtÎyyÙñ«ãß�¼_"¼ÓS íí~®L>Ý\k_�NU¨’+/¿_®ä¸ô¿.qÄ`Ê»Öývd–ÉwË÷<Œ9eºg3þI~(üPù9ëî+Ý|GAÚºÍL8}eäOÕk íž¸É×älsµšø÷Çœ·¯¤ÍÛåýÿŒì¤“[‘ndÓãýÏen.ǾÏ2|&ïß»c7ª;Û‹]®žK²òx4ÔÓ'7‚ö6‡k"·�pÍìØØÖçcRÙ£ûÆ4¶CŸ�|–Ç•7Ö–Ö Þ]xoÞöŽWX÷O®“×/'ˆs]22ø_`hñ¦²?Ó|Os,ôÊŠ:MÞqe²,Üå¶Ìˆcºë}ÐZòãîïð³´2úǺ8åØRMvVOæ:ºdepΣƖÈÿMñ=İAL¨¬¤Ñýç[bÍÁ¾KlȆ;®·ÝX¿ÉgÆ7‘ÿýåS÷~¦Í×ä¶gíºs»8îe½}Û¼[x¢ÉÉÔdOõ.Ñó.;vTïøîT•ÏÔO42Ò¹:ìÍnÇ;RZ®Õ—9Àüü)÷yÿÅwÿ¤­? gýÛŸñlÿ·êUŸ†Oþí¯ø·Ûí9�Á{ÿ¹%ÿ‰ßþ4É~%h•bsŲÙk´Úìý¾ß? U©Õael¶›M–T:ín» 2³sóór¤‹ +¤–Y.¶Èì¶·]ZR•«òççàêðs6{<ÌMv·]‰‘Ÿ°ØgäC‰ƒ�ƒ‰ ùy™™yÇ.&,Ý|’_u¶Yeµ­kJRµ~Lüü^fÏg™‰®Öë±23ö üˆpðp0pá¿#/33/"øññq1qãºù$¾ël²ËkuÕ¥)ZŠÆ>N¼ÒÛyÛåÿdw52²«×ºì«¸Kjr!¿ºŽ¨âùÙöqɦÒé%ÅÚrœœ¬�Þ|wß%aÎÙK—}â¶Úã¾GüÀÙù±åo`vݹY5à:Ü›¸7Ok'Šüjë:Ã�ææÙ¡È—[mŸeÊ2rr7Y±Ët—Á™±’o¬QEmµÎ|�ù{³óWʾÀí«rrkÀµÙ7p~ ÖOøÕÖu�ÍͳC‘.$”úØÛ.O““‘¹ÍŽK¤ºÍŒ�ÛwÒŠ+m©7æ÷äS;äwÎîÅìN=½ÉÏè¯ÉÌêOuöO™ú&nºã;ˆ²»7QÇòàØö÷ ¦FþO¾aG³ÅÀÊÁÖdI-ºØkN}´I¢®@€;+ñ'ñ-̾D¹®w0æy›®ãÛC¯æÜÛ]qo9–úءͿ¯º÷#?']úfÌ)â—cŸ$yêqç†ë¢–YáŽîºüYüZò¿=¹vo0æy{ŽãÚEƒËyf¾+!Ýó-å±Å—๘Ù6ì"Äš969×Ç4zØf‹ú’Í®¸|[ü\ò¿<ùvg/æ9[~ã‡ÚEƒËynVEºæ;»#‹*þ ÁgÌÇÈÁ·c$Ñɰξ9¢ÖÃ4_Ðä–hìI'àƒà‹�|‘r?øCyM÷ð£ƒnæ×RÝlÒê9‡‘\ÇU'¦g á ÇY´=yÇòiK9/#²ŸZIêF¦¿~»?a¡°{©z�­z#®¸¯Rõ Òp¹á:Új¸ÏÐcÝ¿]‹Y¥ÊÈ–ù%’l͆Ëe�‘.VnnT³fgeÍ&FD²Í%ò];n¯êî½é^Æz»ª¸Ž›‚虐Žñ�7ݵúük¦—+"JÖû¤ÉÍØl3²%ÉËËÈ’\¬Ü¹¤žy$šKïºu}aÕý{Òü �u‡Uñ-7à® l,[gËÈ’in¶Ï|ÓË|·Öé/ºêäW½{׸qËæâã‹|�ôäüŸ„j5Z¯*ºÇM•/VrªÉ‡ª»šjà’lüž¥æ<ŠGF�o4³I©Ÿ*K,Óí¦ú¶Ë.NÂÙù5ò³ñ­Æ<ìêi¹Öku^MuÆ£&N²å>üMmyn¶&ÎÈêÎ]°š‘Ó¡ÛM,·êæÈ’Êiö“}k%‹#><ŽNü«|mqŸ:zžnCÄ5šÝW“s¨É“¬ùEoÄÖ×–ëa’\ìŽ®å» ­²,�Öie¿Y6D–[¨ÚKõm–,|Œû28Åó9ñÖŸ)Ý .>tüÊŽ°Õìsú¶§ÃŠÈ2òl‡/.î¦ìœ¸126y}]˳åûf†’eñý�öl±cȲ™ºí•u[ýóŠïw\_“éö|{’qÍ®ÃEÈ4¼�^çG»ÔeÍ�µÔmµ¹±C™¯Ùks±ä†x%²É"–Ë­º”º•¢ÍëŒn·o‘êvZCÇö› ûE¹ÂÉÖmô»�N\¸MN×[™9šý–»: áž l²H¥²ën¥.¥h€îóI¹ã;­¿äz�–‡�ñý¦~“{£ÜadëvúmΫ*\ž«i®Ì޼ Ž¿6 ážl¶H¥²ën¥+JÑT§8á<»­yŸ,ë¾ÇvÜCœðNIºâÇŠï°å×îøß'ã›�F÷G¶Áš–ˉ±Õìñ%‚hî§­²YZ>Kå¾[Ë€=÷Uög0éŽËà]·×ÛK´¼ã­ywæÜSiH£žÌMïÙãmu×äbÍK Íÿ#–϶ÝðÝtwÛu—V•÷]cØü·§ûƒv§ÙWQÌúï•hù�Øû)4xûž?±ÇÙàýëúý,ÌfÇ¥™òzÅ‘×Ç}+e×R¾ç¬»–ubð~Óà›*ê9—^r­1ã;e&�qÇö0l°¾ó�ô,Ì)fÇ¥“Á¬sÃuÑßJÙui\çã7�Ý�âo�Eä�Rl¿FöMs­8ãÿW+m‹­Ûþ‹É§é^)È¿Aì´ûLÞÍ4’äê7XqeA÷ýNvF5×RÉnZ—ã_{ñ/'º©|€àõ¶Î7ÚüI˱°>÷|úüìjY¾âÙùpG3í¸–þ­fel¶Û~õ‰%)OJ,Ìñ󺸿‘�#Õ½çÃ?¡ñîÐáz^Y‹ƒvT9™\½†-¿¦8Þ~N=)»^1º�#]™ì§¶™X²RŸe_xûÝ<_Ⱦ’êîñá¿Ðø÷gðÍ/+ÅÁ»*Ì�.^~-¿¦8Þ~N=¶Á.ÓŒncÈ×eÖÊRÚdâÉJ}”\ÉнÏÃ|�é¢ïî»»a^ Ý]k»K‰Ç·‹ w‰¡ç\w_É5ºýö&»?iƒƒ¿ÖãlmÇÏÇ‹&{qòâ’/©w³Ö¹½˜™…–@€|¦þÑŸ4”/cW'Wòqû@ü»þþÏúy\wɧíòçû÷ó?éåPÿˆÛöÏy«ûéÒòié–ƒ4U£#€&møVÿ¹Ÿ˜¿××PÿP9Ê^?†wû—ùYý~õ§ö=Êèü4ŸÜÇʯëó­?±þP±«ð{þÎß ?–—3ýFôXI;¤à–0Ÿ‰KÎë¹·cq:ówI8¯WI®ç½á.²Ò=§eí5÷ËÃø^UõņÙñ¸OØÓc‘HgÈÆ›;qrÙV³ÒØ€þ!¿6+˹çð¯�n+ë™5Üçº%–{#ÙvËéø‡ ʺ¸ðÙ‘‹ÄxÖÆ›,‹c›#\ݬ6Im™ZêÒØ„þ!_5k˹ßð»�î=üs®d×sŽæ— Y­�cØ ¦â了‹evïa­ÃÀÌ�˜í°3õÙX÷åKƲñr#Ÿ"4V‘•FuàgñûÇÞÛò‡µø¯Jô�Ïæœÿ—eÖ ~·Û&>—ìw»í”Õ·IÇ´øõ¬¹y™Y1ÓòÖêÛmÙ‡¡úµ<–í3ÓÝ7Ås9o8å95� Ò-v >Û¶ÝîÆOL]6ƒSßS'*k­Ž;})Ou÷YeÙ¢z#´¼“ìþ5Óý9Ås9o8å5‹Ò=~>Û¶ÍæÆ_L]>‡S~¦NT×[vúSí¾ë-»jü/ð»È>»ÿ‰xáãw¯'ç\–’ì¶Û]„™þ×\/_“‡�È{#²y™Ôã<�}þgž�Ï•——‘�¯ÀÇÌÙæaadØñ•ðåÐþqý8ä˜znÛò›#]uü‡¶v87djxf^Çél8÷Oêö1[wÔâcÉ~%û‰"�wµŽIî’ì\\Šk±çEñÏñ3Ò^ htüÃ}‰©í?%r0¯“}Ú›ͯâY9øÕÇÍãýQ­Ï�Ýǵ8¸ÒÉ�&Òû,Üm,’k¥»{uøó˜øëø�é?tZ~a½ÄÔö—’yWß¾í-Ž f×q<œüo£› ê­n|~î?ªÅÇ’ükö—Çfãgd“]-ØøÓۯdz»â‹áGÆ‹®fßIƒƒÛÞMò 8,ç>Dò½›Ü;+…>.Oêmd÷g׬x,Ÿ}Ÿï1bÏ~Ëuuö×g—•>>c�eu…Ùpšæ<3ˆö'Þð~{Æ4<φò}túžEŹF§yÇ÷šÌšR“àíu;(20s±dô¥kd–]OZR¿–”«Ïò¾'ÅùßÝpîmÇtœ·‰òM~F«�q®I«ÂÝh·ZÌ«}™MVÆŒ,ìI­þzÉ,ºÚÿ1çù_âüï�î¸w5ãºN[Äù¾}Vÿ�r=fçGºÖäÛíÈÁÙêöäafâËOËd–]o­)_ËJ>/$ã|w™ñÝÿæ /+â\¯Kµã|£‹òMV÷ŽòN;½ÁŸW»Ðoô›H2µ›�.ãY•.>V.DRA‘—G%·YuiXIü»|ì]Ö:k)_¾×"I¶ÚLjS#"L¼;2òðáÏò«ð¥›ãæ«‘ùâvÛ‘t¦²™;~Áê¹&ÎÝòž¦ÕÓêO“ÉxönEÙ;.SÖú¨é_¿}âIvšX)L™¯ÊæVN <>T~ó|}Õò?#ÇM¾ÑçA³Ômp'·ó¡Ì×çãG,WSùÛì¥^ó«{;�t¯eðàêþC‘Äû«¹—ì ɱ±uùÓhyoÛâotû|M†£cfÏ+ïÆËÇÈÄȲ•Žh¤Šë¬ºÏŸŽ�1´žux•Ö]û…n¿ •l0æâݩǵ×Óèq~ÒãÁ‰Êõ±ã×#*l-~βÁ¶ÖÅ,²MMN˲V·ÝrÈ/¼³Òù©âï\÷–¸8\�a‰/ì®?�%·EÆ{+ŽR,NQ¬¶¼e͇ƒŸ}ñlõÑÍ%ÓþŠØbÝ'ç]U޾ùa¥óGÅþºï ppùÃ^9Ù: -º>5ÙÍ~‹�èq©>^L·~tÙ93K|8Z½N¾ ]>fv\°aacY|ÙÇ—ßnB꾨ì~ïç¼s«ú›‡î¹ß=å™Ökô\sE�Iòòe»ó¦ÉÉšKâÃÖj° ¥ÓeæåK5—Í<±Åe÷Ó;øßã}ywÛ�ñìy7kög#¯ÔÄãÜo:Ç®ÖÙ•‰…—ȹ.ç6\MâšÌŒø-ÌÚìòq5ØŸVÏ«5žë}f­ñ×øwºc¤°ô£æ„z^÷íë~ŽË«£¾\¾‘àÙQæbfaEµÂž LŽÔÝãY‹[2mØÙoºÜ™ ®¿2‘Å›|Â<øéîšÅÒö?—Vi»ÇµmúyøÝsoÕÊéŽ=¹¹±l02`ÆŸ³¶ðY�u“þ‘²ÝÖäÉèüšÅeòÿð'àc¨:oKØþ\Y¦îîÔ·égãuÍ¿S+¦ønE³âäâűÁȇ~ÌÛãÛ�u“þ‘²ÝÖäIÜ2~œY—Xqñ{øc|ZñKÚ_ÁÄüºò"Ë,˳IºÓ_°ñÏ®rgÕS'�ðNG…{Cg‹——•[7|›Ø+Kq'ÄÓë31«‘,“5ºÝv›]�¨Ô`ajµ:¬,]n¯W­Åƒ]­×`Á.,qcaáaãElqE¶Ù–ÒÛiJR”HOWƒ‡¬ÖaâkµºìL| ~¿L HlÇÄÃÃÄDz81q1`ŽÛ#ŽËm²Ë-¥)JR”¢A˜:¼=f³]­×bcàkõøðáàà`áÃf>&&=‘ãââbãÇm‘Çe¶Ùe–ÒÛiJR”Jû_©úÀEÿ}+úÁºúWÒ›O"éZú}”­q:B´¥kù=kJWÿxŠïân¶¿¡ü1»Ò¾Úl»úÚÝé_JVì^›­´­%+u-¯§ó}*‹7âg¥Cxiw¥}´Ù÷Ý+w¥})[±zz¶Òµü”­im}?è!±øÈ¡–ïü3ž‘IX#ï “RË«rÏÀ±¯†+ä¥=–I5˜òVÛk_[©eէ䯤;Ñ9E_°)´’þ5ÒZüNÅí ly³cƒ/µ9¾ž’ñ]V~$°Ç‰‘7 ë�ŒyxÙ0ß-¿üSÏ Õ¶øo¶�éüEÞWÝÌû{€x�ÆvW_Çú°»&&ɶ,ŽÌæ:š×‹jó±eÇŽ 'â]}°·. ˆd’Û©ÉeŠïmðÝD<¿?•·s.Þà>$q��×ñþ ÁÅìÈ‚ ²m‹'²¹†ª·q�^v,°G�$üO¯óíË‚x¯’Û©É%Šïmñ]D �ž0r^sÓüg¯Ë×uÅuž@÷¬˜Yx9™¼ÿ¦Üi:ŸƒgÛôØhö�nöìÌYéXó±y6£"ÚR°[[¢ä�j6ˆR€$ðñ?ošœöO {ÏSwüº·}L+49ž{ŸŸàÅe¼J ’`á\r܈r7y^êW*ëâÀ‚—ý\¹°»«ðÏñ�o—¼Úþùî½Enñ·­·sÇÑåÒh«Üç 8²©Æ¢¤RE%œ3ŽRx§ÜdzÒÜ©.�*IïÌ¿¹ÿ ŸvùyÍdï~éÔÖ¾8u¾ïîphò¾´Wv÷9ÂŽªq¨¾”‘KãÔž÷´¦U÷ǃ/÷åɉ,ÏÃuðŸ¬òï–áùÅåw¿oã\raéî½äØÛÇ|‚ì¾;�|›�ÞM¶Ó“ôÿ[nqkf/¶íg ßÁv·*ìœ<Æ»"~8øøø˜ðbâÁ6.41cãccÅd8øøðÙlpÁ1ÛlqCvÒÛm¶”¶ÛiJRž‰Çà 8ðÅ��pAvCÙlPà VÒÈâŠ;)m‘Ç–Ò–ÛJR”¥=(œ,0Ã� XøñE<G C±C 1[K"Š(¬¥¶Gv[J[m)JR”ô¢ÆGöGôùùøøêÿ�Þ†Øu¿-‡ GÙf-žï¦;:Üh«´àÜÂl:Yn6VU˜¹¹\“K�;Íuž¶äÃSYKrñq&‡E>@¼ëO=:K;¯yd8š^ÇcØî:‡²lÆŽí¯å“bRÊC>E°M——Ã9˜ðCº×[ù¹PŶ{2ñ±'‡E¼ÿðS­|ñé<î¾åpâi¹ÿ�c¸ê>dz:í8O,›–[ ùãÍ—•Ãy˜ðCº×Ûù¹PŶRܬlY¡ç¿ÉWÆÿEü›øå»è®ß‡ø?Ȱ¾ó»êàÔêpv|è9×Ó‹îûÝè¾Ëæ�EÚ<{3‹sî½Ëãü“K›oçA—�[o‡/"ßè>Û X²ð3!­øÙØSÅ‘ ÷Å-—Ö½^ÛêŽ{Ñ�•Ìú�´8ögç¼{—Çù—2ß΃/¶ß^E¾°lu\9bËÁ̆ëñ³p§‹"ïŠK.­J>PxÙÚÞ w÷ixÛÝšKt]•Ô¼§3ŒïáǦuÚ�¬6[f“•q¬­–¯7eÄy†‡+k¨Ë“ ²õ¹�ËXì­þÚc6:c¦}Ž;Èw¼G�h¹_Ûì8ÿ&ã�g!ã»íFTØ;]&÷K›ËQ·ÖfãÝdø{ nÃ9¡–Ë©|rYK©ZV”}Ž=È7|O£å\gk�¢äœkq¬äy«É“g¦Ýé³`Øê¶ºì¸n¶l\ív~4sC%µ¥ÖIe.¥}húü»â›í')ã[Lí#ã[}nÿA»ÖdI‰²Óîôù°ìu[M~\5¶\\í~v4sE%µ¥ÖIe+O¶�e×]ƒÌz—°x'juÞó#ŒvYó.1Ø<’âA‡“•ǹ� Ýàò>3¼ÆÆØcfkò25;­lY<2Ã}ÑÒ—Ùuµ­µ´oÀ?*µ>ix�ÒþBá]�ï—ñhp»Q¯¤pÃÇû7�Ë&ƒŸêbÁ¦~Ë+_­þkçÉÖÙ“'Þ%ÔäbÏu)õh²‡Áÿ&u^^ø·Ô=õƒ\8vÜ»�E‹ÍõXVÒt]‰Çå“Eε0áÝ™�•‡®�‘àO6¾™ýiµ“cOu=%§­’žù/ªòïÅ΢âÛrÎ5/7ÕáRØ¡Ñv'’M8Ôʼn\¼Üœ=}œ‹y°-žÿ­.²li®§¤´õ¹+Á+ø¿œ~ xÿåoƒ¹:û]½Ýi1.ÚM‹Åyî«'/‹öw ÆÍÜjôÙÛl~Øú-®¢Ìï»E}¸TÉ‡Ý ±Ývâ¶½µ­´G‹ñye?Høq£è>5Ÿ64ò›’dh6cÖh¦Åê~v«}Ïn‹32 1²7{�†�U|RG,9šÌìøëé[hàÏâòŠ~™ñ'QÒaäÇ Èãù²AvD3cu ®³uÎï‡/"*Ã>ãiŸ¦ÕI ¶_^·?6Êý¶¸=ø€< ›¦üKÔt—Η—ù+È24²AtÑM�Ö.ºÝ×9¾,¬|˜®‚mÆÓ;O«¾),¾,­vvm•ô­¨à~'ÿ7dñ_ã£oÓ¼Kw·µ<ÌÝåt¶²6ânpú=¿{oñ0çÕlqvúœî=‘�Ä6ÖLYb‹™[‘žøiJÀ‘Ô"‡6wÃï{_Í~üá}Ô:»òw¼›+ï{þA‘ ÷èx À–á/;åݘºM,öÖ–ÖûeÍË’ ,jI—“Rl‡‰¾.öo˜�åÃú3«5÷͸äYËßo¦Ç’]/áØÁü"æÜ’knŠÌ}>“{imµ¾É33%ƒ~NLß±Þ(x¿Ùž`÷‡èÞ­×Ý6ã‘dW/{¾ž $Òð~�$5ä<בÍmÑÙ�¨ÒãMo¶Úßd™™rÁ‡¿'"ïÞ¯Ž_»cä�Êž ã7UÍ]{ŠËÈ{'±²tÙ[Í7Sõv›#ÎU϶úÜ\�}62aÓ6=f ™x1í7Y¸xWebÓ"¹Y�â‰]=áGFñn‡ém/èþ;¡¶LíÖ÷6Gq 9Oå6Y›¯Øa_�ƒËø/Áǡ�ö']ò ±3nã\ß�]›/Ýò>”ØùóO…›^»/3"µ_3<@í¿{ë•t/pk©ÛOHö¼c“áãäÇÇ{„ìfÉ�C͸¶FE–ýçS³û¤°Ëe+uøYøùszO�-¶×�å犣á‡xrn�í\;Ij­‹kƹ.hyß ØM““˜qÙr-¥e×l+‰,2ÙëuØyØù²WêÁ%)^——>)v�†}ÝÉzGµ0­¦ËUl{^5É0àž=:á» ²cÒs;,öÒ³k¶Ä–lõºüLìyñd¯Õ‚JR§’ߎ®ãøÈòw‘øïÚùXœ§U&$|¯©ûSO�‘¬ãý¯Ö›,¼¬m7)ÅÕdäæäqÍî.F¸;­<™UÕíq¦Žœì:âl2õY¬mesð/Ï—ÿwŸüWñŠJÓðÆݹÿÏû~¥YødÿîÚÿ‹wý¾Ó˜ü¿û’_ø�ÿãL—âV‰V'0à�âóO|)—§¸¾Ú]dyW²Ïë¬ZâM—�›ƒÕzhp¶¹³Šx±åÄ’ ¦Ã_Ç21¥’;çÄä3_»è_íâÏ7•×ô‡SõGÚ_ƒØ>Nl3ºûîÒÏf'YêáÄÎím”r[‹>$ØûvvÈ‚Kâ¾L~A$‘Öµ†ïN#üðyYAø{?Tñ½œ˜=�äÖÃ7¯ñ~í4ðfaõ¦®LîÔØÇ%¸ÓbÍ�±×çaqùà’øï“}$‘Öµ†ïH÷þ%o9ëá÷Ç3àOy·¸<¼ÊÌè.éäÚàuþ×]vOyò«5[\|™rµ1uü—ñÉ2ñm³/Y³åZü˜¤Š[,¾•ú ¨‚ú­ðîü{xOÍüùòo†tG–]F†J_Ê»G™Ò*Ëë-.Vœ�{m”ŠkgÛåß™»UÔ¤sísqì–ø ¬³G¹~xs̼ãò7‰t§–]V†´¿“ög/¶Ê_ ë�FV${ýÌvÝÖÏ·Ì“.~²Ù[&ÙæAIk?Öš=Èð?ÃÎcç‘|K¥xÔ²ê´7RþMÙ|¾ØýñðκÔdâY¾ÜÙmcšÙöÙreÃ�¬†ëkdÛ,È-–±Áõf�©ßüÏäóÌ#ÒØ±o´ý5žïμŒìM-p1¦á}[¯ÌŽ<�~£a¶�#ÎsÏsëfŸG1¶Ñ‘&ÆL,Œ vuc³C§:®º«øGMu/×ðî»ëÝ7ã]-‹¤Ÿ'/*jÿFØî6û æÌÏÍšëòs³²&Èžûæ–ûî±k©º§€tw[ðî¤êÞ7�ÄxÒãè¸Þ‡]¶\Hn¾iòrfº·O°Ûms¦—/;2{¤ÉÎÍž\‰ï¾io¾ëºŸª¸GõÇên®ã˜K�pM.>‹�èµÖ{bÅÄ†ëæŸ'&k½gÏÚí3¦—/;2k¯ÉÍÍž\‰¯¾Y/¾ëjz«ªúï£úã…uRñ?ë^»ãÚî+Ã8†‚ ±õz-®A‹‰$¾\œ™®ô¬“äO$¹9Sß|ÓI$²_}Ù-�Ù ïÀ“üIÞ `u‡hq6:ïOîí�8OpáàÁ��…­íÝv¦\î=Éí‚<‹o¬½�Åu{úXÖÇnÃK.Lòß‘±ûaÕø†<*Áë¾Åâžcp-Tx\s¸6Vp¾ÚÅÃŽ10{G_©“/Žr[ ·"—ýNyÆ56eý(,ŠÜÝEÙI~Fµ¬<ÿ—…˜=wؼWÌ>ª� ŽwÊÞÛ8¸qC&h`j¤ËãÜ–Ø,š—}NwƵ96åÖ8-ŽÜíMÙ_~F}kX~-ߎ¬>;É:ûä—¬ô™CÏ6~›òjÏŒqéøÎÛ.l¼l8æ×èa† ²³r%’.hÖ#f„°k_†ÊYùoNw7‰“mlÛ¢ßbö‡ZaåæO.]x>É›šjuXuޏøš^+αbΖ´º—I™Ên¯¥~ÚÒa߇ ÉYù_Pöß‹œ‡iõ³úŸ{�ؽy‹••uùÀ~y‘‘+Ôêñ),ƒUƹ¾%3¥º·{®ÊäµýÏÉ0OÓä”ü«¨»oÅîA´úÙýS½ÆìN¾ÅÊʾL�àG;žx9V«Y‹ìúxú®7Íq-Í–¾ïuù\–¿eiù,Yü"Þ\eö§†}¹âO"ÌŸ#qâ�bÁ¿áSK…‡SwÖW$åXúL áý-Æë?MÚÚ.U�Ÿ•—d•†î KXíŽ(¥<’ÒJ‰k€+ùMý£>hÿ(^Æþ®N®äãö�ùwýü9Ÿôò¸ï“OÚåÏ÷ïæÓÊ¡ÿ·ížóW÷Ó¤?äÓÓ-h«FGLÛð­ÿs?1¯®¡þ s”¼ ï÷/ò³úýëOì{”%Ñøi?¹�•_×çZcü¡cWà÷ý�¾@-.gú�è$°’wIÁ,`ºùoä_ñ+ÆÎäò3–Ãfn«ªøfvûS~D˜”ä\—*\}/ â¶æÅ�™v œ³˜í05¶äV)-‚ìªIu+mµ`O(»÷�x¹ãçl÷÷,ŽÌ�OYñíä:Ëò.Ãý?È'’ Oâñæ[�•÷9ùW+Øákcš±ßlReRû©[iVò‡¾øß‹Þ>öÇ~r¸ìÊÕu§ÍÞA¬¿"ìOÓÛùäƒSĸ͙vÁ“÷9¹G+Øák£š±ßlWåRë©ZR¬ å'‘\Ä�{ŸÉnÐʳ„t·_r.w¶Å¦ÇS«ÏäŒ+îÑðÝN÷;[ª—–ó®A&.›M�,ñýókŸ��m}òÛJÕ}Ϲ×)ìþsÌ»'œm§ßs>ÊwÜÏ–nò©m26Ü�“m2·;­Œ¶Çm±Ù~^Ç2Ký¶Ò–[îô¶”¥)EgœëšònÉæÜ¿±9žÎ]×/ç|›{Ì9FÞ{l²]Ÿ ä›<�¾ß:û#¶Ø£®V~d—ûm¥,¶•ô¶”¥)Ei|çšò^Éæ¼»°ùžÎm×.ç\—yËù>Þz[l»=ÿ#Ùdíöù×Ù-Ž:äçåɶÚRÛi_KiJR”SGß×Ï|�î®ÕïÞÑØÇµì.âçÜ£±yvV5¹1kìÝr½¾Vß'M‹••›6·AªûÍ1uøVû0ðaŠ +ìŽÚ<“ʼ«=_ðcÚœç‰u¯^ñüþUÎyß!Ôñ^'Ç5–Yvvç}»Ì‹[ƒe¾("úÙ3[K¤–û!ŠÏ[äºÛ-ºêzÞÁ9wgóN-×|C�Ê9¯6Þëx×ãúÛc®fÛu·Ê�+¦’x)$òÓß,·Ç 6R·É}–[uÔõœ‚òîÎæœ[¯8‡;“óNk½Öñ®1ÇõÖÙvfÛu·Ê�+¦¾,xi$òÓß,·Ç 6R·É}¶[uÔÊÝÑý­ä§nðˆèþ¸ì>ÖìþE‰Æ8gÒGrö[›dž|Œ¬¼™qõÚm›]�6vÏg›6>¿U­ÆŸ3.hq`–[,„øµøÎë•ƒQwè~[ßüç ?¹»?µ³+aìŽxø/ ÉËǃe�×Ü^ÌǬ–C6Ó&Û󧊤‹ÁŸ�oŽþ à/Ncé½5<Ÿ¼9žoö>&=×[›²öY5œ;Šeeããì`à|jjÖ<øõuÌvs}qý±áéö’ljlÇËÁÅ‚?5Ÿ:ÿ79SøûÆ©®èÞ_¹‹±¸F“ ëu=CÌ7E‡µÔãÁ[ìÔõç1ÙMô¡‚–LJ§ÚIf$5³/  ûÿ‡Â� /Ý|†x“Æuš>—Þîðáò;§ôXRaâu.äû_ºk»_„aÁmu¸½qË÷™ÐàmuQÓôã#\;'ÀÏš=4j‘àG™ ð�?™²ô§•¹nâ¸ýsäÞ°ñس2-³MÝW+7ŒÏùYqc`ÓšñûsuÛwäl6TÕEù"¢@Ÿ‡Ûˉz‡ÉmÇ�ܧmX8‘X>œv¹ý¸Zžßâø™š¡»#.,Ls;ff²jGùûõqzúGJ;ûø|¶—¨¼”ÛøáÊ6Õƒ€ù…éǢʛۅ©íÞ1‰‘™¡š²2£ÆÁ§/ãÖfk&¤qÝ>v}šÈ½}#¥üü$~wæõ‡’½…àW1ÛËüòKW¶ìž¦Á–͆L:®õë®;v'ÂÁ� ð5±vPhr§ÏÌ̾ËnŸˆëq¡¯ÕÈöÉ~šñ'V×Y¬ìÞÈÓæOYc¥Ñçï8®ƒ7[!­Ô–KfÇãÙU­ÖÒ¶ÙXéK«JÝo¬fî«6n™ñƒwv×]®ìîoªÊš²YK¬ÍÜñ]v^tŠ·}Kí– Mku)[m­”¥kJÝo¬gÿ¾¯6n›ñ‡uV×]¯ìÎq«Ê›êGK£ÍÜqmn^TŠ·R[í– Mku)[möR•­+u¾±@ü`Øò]ñÉйTöý(|Ùà˜÷ý¿�õ2z+Èi#ô§§Ûo·ÿZþçØ…²¨ƒ«Žz~²³KÌøŽâXë,zžO ÙI/¤u’Ì®&UñÒK©[l­öÅéëZV”õõz޵‹EÍx~îhî–?)ãûYb¶ûcºXµûlL¹#¶ûÿ6˯¶Ò•¯ÙOW¨àûHôœ×ˆn¦Ž²Ã¨åi,VÝm—I¿m‰—|vßæÙ[튴¥köSÕ´^s,ºóSÄÁÚÅ|ú¾ å�üËesC�$Øc¶8–ï2(çɺÌxo“êRù.¥–Ö¾·V”¥Vè­@Z`º8à»S±¸ßOu�bö×1–h8—XpnYØ\ž\k-—*Í Ðçò-½qb¾ø¬›+îé>�•ºß}þ”õ§«ÅvO>ãÝU×|ó³ùlÒãñn¹áœŸ�òIಒχ‰iswûi1âºøé,öà`Iì³ÝOuÞ”õû^+²yïê®»ç�ŸË&—‹uÏ äüë‘Ï–ÉŸl‰ä¯ÙdQÝ_ÜU%Ý}±Ê»ã·û7ºyÄödrÞÔç\£žïëÔ¦,O¸ËÛMƒ�—Év>³]÷ªcâÃJÖØqâ²;6Ú+î>ÒäÝßÛ‘Ü<Êkfå›Íy'7ÝR;å¿7‘ír¶wkð)5ò_¯YfE1±bõöÃ�‘ÛJ[m)JÉ;‹´y/vö¿c÷1šÙ¹?fs^IÍ·TŽù.ÆÆÍä{\­�ø4–ëï‡[­·"˜ø±zûaÇŠË-ô¶ÚQL7—þIrï0|¡ï�'yÅû+7Ý×Ù¼£œÙ«Ùîòù¼W�ì¶Gøã:Ȳ2´}{Ä ÁÑëi[#²-~¾ì²Ë,¶Ëq‹1»\Aá§‹<ãÌÿ$ºÃÇn[ðóùÞöË7Ü‹î”ÌÅáœ+YeÛc̳±ïÉÁ‹"= Çšh±îž æåR,X盧Çë²~"xÍÌü¾ò®: „Ý&g4ÜÛMï!û�sqxÖÙvÖrìè+‘‡Ñhô°K$0_<}ˬ8¶Il³Ùë²>#øÑ̼»ò®:…]~g3ÜÛMï û§ß1x‡ÖÙvÖrÜØ+>$SE£ÒÁ,‘A|Ðýó.°ãY}$šÆû|hxÏ~G¼ÃêïxT™z�>÷.nQÚüæìÌn·éî1&6O9æ7âkvxøÛ ñç‡U¤·2Øps9&Ó]…<øödýk-é^œëß:£€ô¯Tè1¸Ï_u¿ÁãÓÇ×cä_•ÌzŠÙýi»ÈƒÉ£ŸiÖ9ye_=lŠ¿ çÌ»"k¬ÁÄŠØêüøø‰ÚÝOg˜=q¤³ûetÖ¶<>ÒÇ×cd_•Ìz’Ùÿ7w‘=“Y>Ó¬²ò.ɾzÙAÏ—tó]f,VŧñA|bbùWâåþgõ†’ù¼€ñ‹l3¹L8_£â—Ÿø×‹“›¿æÚ¼ÚäãÇ‘“²ê<Œ¬®O¬ôÌŠ8õ×ï ·//3‘AÙ d4U²€%›ø^¼ ›WÍ;çÃíö´Õò½N?zõÎ>F^>>7%ã÷j¸‡bë° ¾fl¶œ—�æiríŽÙ}°ch2$¤u÷É}²�ü6ÞHM¯å}áâ†ï7Ó]Èõ¸ýÛÀ`šlX!ƒ¥»UÄ{ Ëã¦^Ãa¾ÒdérlŽÙ+lir/¥Ÿ�}Ô”/á½ò>mw+îß·y¾šîG­ƒºø O‹0oôµÕñ>ƒ�ñÓ+?a¾Òdé²lŽÙ=!ƒK‘},¯ºû­�wàîòêüÍW”^ r…ÒK§¿]åUáSc<¶jó¥Ðõ—tC•º¾y5¸:í~Òþ6¿Øâ¾L�ŽÆzVOèžÉ•¥¶–šp@ éþ{¼’§�"Ý�¦ÕçÇ›Ä<|Öêú�V²1éúW‰K›³ìk²àžúÛ~dz7»m}g²Ûi6.¿ÓÖ–ÛZÀOç ÈZw¿Ÿ}‰¦ÖgÅ›Äú[¬éqþóuÛq™2ö}ƒ~V<÷}:lðûw³Öß,vÛI±õÐ~Z[J ?ó{ä;×Ï~ÃÓk3£Ìâ�®Öô–аS&(ëµã2eìûüœy®ú¤±; w³×_5–ÛIqõð~Z[J«üQþ[_äOÉ–û©t[»ö={â7Ðôö³_É«¼ã9]�¹ŠÎwÛ\ƒ[ )…ÇùF.ß}…Ä÷PÛ[ç¬ü>+fº•ŽØ¢âû�nC£z°ÿàßã¿Âßµý‡ØzÚü9©�·Š‘Ã%vÙ7ãËYlÁƾ–¼| |cãülø]¤×ó�$X^Nwݺ~Ëò#*ù°3r´ºåÿºŽ Ü8)]wVqý¤�åG÷�„5ä™û‰ñ²¤ÃÈǶ>Ü»ì;·X|·|xi~A¼fÙqÍ.../}u}›ncÑ‚ZàcV~A~_¦:óm°Î¬6bñžÆÅÁ‡[ë<âl`ÁÍ’ëãľy§ò“àVŸÎÿvwS�‡‰Ý½qfÏ–tŸ š˜PÖíõر~–à›<ü»¡û¯ìlq§¿ëE.|8Y²}K1+ œÖùEð7OçgŽÛ;©ÇÄÅîι³gÊúS�ML8k~úìX¿Jð]žv]a¦7çøØQcM}fŠ%òN¦ÉÅãnôáQl9‡�½¡½ÇÉŠîØvcÃõô;=Æ®�¾/^vV&z®A �çCeŸvÙS +;W�K+gÛê6¼m´Ðïµ¹ú]æ“c›¨Üéö¸“àlõ;]nL¸[nÇ*8²°³ðs!¾)¡’ÛdŽK+mÔ¥iZ+ÓÛjvš®ÏG¼Öçé·Z]†f§q¨Úbdköz­¦»&L=†·c�—YXYø9pßÐÉm²G%µ¶êR´­ïíu[MÓe£Ýë³´û­6Ã3U·Ôm1'ÀÙêöšì‰0öíŽTqdág`åÃ|SE%¶É–ÖÛ©JÒ´T‹Ëø�(ëþYÊ88ãûŽ%ÍxO"Ýñ_ÅyN«ÆyGÙdé¹Þjó#‹3[¸Óm°¦ÆÉÇ–Ëd†hî²êRêV�œùïÀó  ~~¿û¼ÿâ»ÿŒRVŸ†3þíÏø¶Ûõ*ÏÃ'ÿv×ü[¿íöœÇà½ÿÜ’ÿÄïÿd¿´J±9€t¿<þOKägÈgfhµ¹²Ï¼u‚„ã8÷Y.=–îx†ffGefM‹vDÐÝ�wdl6XTȲ–W#_‰î§æR”€—Íç’w÷ž]…¡×fß? èxzGŽÁJÏ_¦¸ÆVVObæM‰|ÒÁnÇû`gçà]=”¶³áëq}ÔüÊR�ù·ò:^ýó¿°´ZüËçáý�Iqè)õ¢‹ôÏÊÊÉìLɱnšX)°þgçàÖ{)mgÃ×bû©ù”¥+üQ~c_äŸÉ>ï§øîþ»N³ðû‰àtþ§_È)¸ãy£¸¦?.î}þ& qG�“bnóp8Žâ ]$•Ÿ†Çﺕ¶‘ÙÆ!‰FðaWÀ7…¸¾0xU£í.E­Ž×òž-7iò ©#ôËÖuÍøs]Ô|^’[•õ®àráËwUñšKl×Û|ñí„››é["š,½ÜÐIK¾…•¤ïþ¼CÆñ¿Ãý'coµñÅÙþJE©ìÞA“|t¦^»�ˇ-Ý[Æ©-³_møöqüù7Ò¶E4y[©`’—} +KUÿÇö¿Á�>½Ür=6|yGƒ£ï¾áØdk,ÄäÍ$ÔG•ÕaŸ>wãÜ£ ®¸Â+òôûåSUÊöûÚãËXr)ëÜçg�™wd£ß$ž6G埄^Dt¦>²M¯'ÝuþË‘uÞ6,8²l/ì¾ ôù—ÄÀ›*9>çvë’i ×dIl’ì,Éã¥Ô¥õõÓo�O£ò�ÃŽüé¸5×ì¹ï‚lw| `¦]ý‹Ãk.à¸ø¹Ç'ÝmÚr].>Eö{o»*hý}/«N>@¼|�Ê»ë§a×_²ä[® ±Ýð=Â_·Ö–ázÛöÒŽ¢ü6w¤ÝòÑySgebqÞÕÙåôw(ÇÅöÿÕç‹Ù0èó%­}kKq}iöú;ãøj<š³Ç––ÕmwM'òG�ó9fnî9¯­Ù<Û“õž·I|SGf/ ä]×Â8Ϊ /¶[n‹>X½´¬´¾Ë%V¬Z~¬å7öŒù£ü¡{ú¹:¸“�Úåß÷ðæÓÊã¾M?h—?ß¿™ÿO*‡üFß¶{Í_ßN�ÿ“OL´¢­q3o·ýÌüÅþ¾º‡ú�ÎRñü3¿Ü¿ÊÏë÷­?±îP—Gá¤þæ>U_�iý�ò…�_ƒßövùü´¹Ÿê7 ’ÂIÝ'±€&üPÞPÉ­ã� áÞ‹*è¦ä¹ùضR9â’ý&¦mÇ ëLòcɶ ­~ËwNA“—�Ý[25xR[u=+D[¿O’RkxßHx�¤Êúsò\œŽîììûÄS×I¨—iÄzïêG=1òµ»MÝw™9ÉÕ³#U‰%·R´ª.¿‰ É 5¼s¤|PÒeV9¹&FGvóøí¤ñMv—Q.Óˆõæ %ŽzC•¯Ùî¿NddC$uöäjñ/¶ïZVˆqþ/2æàž?tW„ï¾y>Gmöæ¿]º‚=„}WÕÙxÐpm$ãõÆ’yø÷;í ÏÒ˜Y_V*[ŸÁ¯²”¿óý�àD¡}` ™þÏ�,~9ų<þí5—ò>[óˆx냛µÒñ(åÌãüë²£²´Ÿ/ mÉóaÉÑëî­qf�Wuõ¶l}”Û.¯ÃéàV?ã^svVž;ù,‡sź 6Y/ÑñHåËÑó.ÊÛþ¶N×”åѧÀ¿Óhõpæ_ýeÖˇðýxã^qöN¢Ëù,‹qÅú 6i/ÒqK%ËÑó.ÁŽËþ¶F&Ó”eC>£ïLycÖC™¤¸û®¶Ã?½ñi‰Òý/7Èwtñ<í¹ß:©u¾=`ïu[výuÑ·ß<Üã ¥˜±bn»¿&ÊK‰•4·ñ±&ÃÍ®6ï7,TŸ’wKôâû¯8gmp.cÖ‰¡ÃåŸq½¿åœ?êSm¡ÞaM¯Øâ_$E“�%øóÖ±Í ñÍ”¶Hï²ûmºžKžð^'Ùü'•õ×<ÒbrNÍøþ׋ò�u$û®×GºÃ›c‡%ðßDK�=ÞÉb¾Éb¿Òû.¶ûmºžKžð^'ÙÜ+•õ×;ÒbrNÍøþ׋ò�u$û®ÓIºÃ—a‰%ðßð]&<×{%Šû%ŠÿKìºÛí¶êyî]ĸ¿?â|Ÿ‚sŽ?§å¼/šñÝ×åüW�ëñ¶ÚMÅù&·'MÈ8þóU›Ø{=>ëS›66N<Ö]ÐÉu—R¶Ö´V#òá¿)ðOÊŽÆèõÙ¹ú ne¼“¬9FdwÛw2êÞC>TÜC}tßsÀÇÈÙAk¶Ÿw�îñnp2áŠë¬Ž—V¸�<|Häžù5Ïú7uvf~‡&ÎGÖ¼—2ß蜷¬÷ÓåIÅwrKnl±ãÇ—_³¬1[�ß*8«tv[uk–ó»Ä¾IáW’üû£·Wfgh°2mä]oÉ3-üþYÖ›éò¤â»¹%· ¶0G�.¿eôb¶öØ9QÅ[¬²Û«QO˿ǎóã;ÍŽÁñò¹9û~³ÛâãöwAò�®V»/mÈúg–l¶øœv›ÛõÕŽÚr^)·Òl4)$ÆÀ®nn¦LØqbÄÊÆ÷iKNÚzæ0ÓðžeÉzë™q.Àá›l½/àÜ—EËø¶ó[àÍÓò.7³ÅÜévx“Gu²E“�²ÃŽ[.¥iZ]m“†rîC×ü¿ŠóÎ#²ÈÓr¾É4|·Œî1/º<­W ã›<]Æ›c�}µ¶ë'ÂØáÇ%•¥iZ]m“†òîCÀ9ç|Ke‘¦å\/‘é9gÛâ_tyZ­ÿÙcmôûy,­·Y>Ã9-­+JÒëhÊ#ÛüËÇÞåêŽöë¹ð±¹çMv7 í.ËLÍ_ð—‚r-w&ÓE¶Á‹#üýDùúË#ÊÇú‘Ó#ëã­Ô¥ÕªÖ;ËŽy1ã÷N÷ï®=šnØëî5Ì­ÁÇË�>š=žÓ] ûþ1“•¤rlx®þÜ�nU)éíÉÅ’Úý´YÃã×rñÿ!ú7©ûËý(ôý¡Á8ï0‹ <¨snÓgmuÐ˸ã¹y0Vè¯Ùq­Í25ùT¥3'K-høõÜš!º7ª;¿ŒýõŸÁ8ï/� ,¨³k¦ÎÚë¡—sÇr²!­b¿cÆ·4È×åRŸÎdã_m~Ú.‰èŽãâ^Dt�Pwç·e཭ÄàÝC‰�»ÅãÝ�Æuœ§S…¼ÄÀÍÙaáï0p¶–C™YÙM—ÙI/öû«œY�˜Ù\ÏÄ¥û9°¿”/XÿPùÛ†¿ˆOö~Ùí¿¤y[‡_ˆ7öÿÙ¿­ÿ¤y[ƒÿ‰wö0ùgûéã×ü¥º…¤PlU^°7à{ã‹Ä?q{׳8õ˜þEù £×n¶lõðG¸ë^¬Ìú;N)À`¾_«�­Ùo£ú�ôU®<Ÿz»" K¬¥÷NÃá7ãóÅ.…Åî~ÄÐY�ä|iuû�•6XE·ëεËú;>1À¢»"—gkv;{)×{¦=õÌ®6&DU“[e÷N“áKãÿÅ^†Æî~ÄÐÙ�ßÝï¥ÀÛìi²ÁŽ=¿^õ®_ÑÙñž ÓRüÝvÃqe!Úï"ÿSÉ÷»±±2"úšëoºÓoÃïñO¨øíñ7OÏû+Šbãyyä^“SËûwg°ÇÏý?×\G>(vÜ/¢qâÚáëæÐKÅ1&³+“A=’dry§†\ŒÜMn®ø{ÖíöŽý#…øž´qgxÕ»q,“3CåO ·ïŸNû¥ÅÖmz³¸±óa¥öÒ¶Å^Â*Ý[½)[£²ž¾¾”¬~öžÜßúËmf-²äé<šáÕ¿/éßt˜ºí�ZvÖ.MžûmºØ¡ÉÏ·Ý[½-­Ö[O__JV?ÿˆßMfo„Ýg·³Ù2tžKðÿv_Ó¾é1uÛ.µíŒ\¨é}¶ÝlqdçÛ‰î­Þ”­Ö[O__JV0‹c�o9‡Å¯ä:˜é~UùaÔ¼÷“ÝX3%úZ=� í®°Ç’’ccÏ5kÉ{]o¿"èa¯»ÙKë-ñG|�B�ZXÚ¾•ç–ö§Mõ/gÙ[+gcõ—ç–V?£ôëo/âÚ®Cmcû´³ãû=6?gÓ¾û=?�º´ôªÒ~¥æ±vOUuŸbÁtwÃϺû†sXnаÝÑr®9­ÞÇtwcË>=c­™ôô¬wßg§ó·Vž•Z/Ô¼Ò>ÈꮳìH®Žø¹ï_pÎiÑV¢º>SÇ5»Ë.ŽìyfǬu·:ž•ŽûìôþvêÓÒ«Ç4{­_$Òéù“;g¥ßjõû­FË'7 a«ÚbC�¯ÎÃÌÄ–|L¼\¼Iì’9b¾øä²ê]mÕ¥iVMdA}@w|ÍÔ[^ªË‚ç�Ý‘ÃúÆë-Ì»gÕ_—Øœ«c… u¥ù8rÿ±59”¯æ}Ƕï¶û}x™óëÝÓuW�;~¬É²×|óþ'֟г+�°ÆãX7eóÞOŸ�·Rüœ)áâj²éöÙô¶Þ—=G>|;¶n«ð7mµ™1úïŽÅ:ÛÒ<ºãì ãXeóÎO��·RüŒ9¢âXú¼ª}¶ý-¯¶¿ÏQÀ�Ä»äÞÇÆï‰þàÖh6½?*òS•pï4[m4xrY‹®çTÛòÎÊ×î~û¶Ù¤å}5À9&’[£¶³Rý�žÊÙ_è–×Ú‚R ʱ€{ü3þbõÿCs¯2yF³ÛÌ»×c°à=s“=–VýwPð�Ͱï³p䎩ðײ5RÅ“ ÑR´��âKÞÉ®õ™WáÚñ3�ô‡2ò×’ë­§/îÌüîÀ2&²+¤×uGÛÒ ÎN$–]Ià»™v²k2¡–ÚÒ±qü)£¯¶[½fAøxð¶vŸ�aãSkƵ»½Ý“Íñv[¼Ïºçæj·šLn7—m)$dŸR>Há+0ÍÜéµ�OµãÛýfëC½Öçi·zm®$ú½¶£i‹.ËY²Áʲ\lÜ ü9ïŠh¤¶ë$ŽúÛu+JÖ�ÁµÕk7º½–“u¯ÂÛi·š­¶«e�n»g¬ØcɉŸ¯ÏÃȲL|¼,ÜY¯ŽX¤¶ë$²êÛu+JÖ�ÁµÕk7º½–“u¯ÂÛi·š­¶«e�n¿g¬ØcɉŸ¯ÏÃȲL|¼,ÜY¯ŽX¯¶ë$²êÛu+JÖ�ó}–KeñKe’G%—Y$wÛKì¾Ëé[o²û.¥mºË­¯¥i_²´UÙòâ¦_…Þbwo�ßÑæã|_”]¶ë½†DÓeK²ë>[�&àräçK…¯·?m�Ƕ�à줊+a¦Ó&Ë+[m¥k[OŸ1äx�åŸqt]”žN7Çyvý}�‘&FL™ýsʱâä\*ù³²1pÿHlµÚMŒx £Ž‘Wg‰‘m•­-õVí篌¹!ùcÜ=m'“ŽqîG]·ÍÈ’|™3úë”ãÅȸ]óçO�‰Lý–»I±�a,vR/ÒX™ÙZÒßUB1þ Iñíò Þ}¨ÔO«ê­¦ÞÞÕè;ï‡cfÝ-ع»^-§ÖeîvûÍÆæºÙEŸÄgØed]>~Èžê[õ)JicO~åèu>:;þ¾0yÁã?tÏ´¦›CÆ{KA¬æÛ ûÕ˜ÝuÍ/“„v,·bûoú÷ÃÂy}ñÒ”÷[-¶]ei}¶ÝMÄøýïJøßæw޽½>ÊÍN�AÙZMO1Ï–ÊÍ/æwKÂùîD�RÉ>µqx� Ì–:z{©,v]miu¶ÝMÂð¼«ã�™ž;väûu:Mdé5\Ç>K>´XÜ ™]' çsɲO­\n#¿Ì–Êz{©-–Ýmm¾Ûn§U>¼–ÉñOåÃÎÉ¿+èq¾EÚº¾™çqår9øÖм3¼âŸª6›žM•®.n›‚erÌ~KLl«k�~f– ®¬wYd±ÚH²‘d²ÝÀ9îÊÔtÏRv—pr&ÊÐuG\ó~ÊÝãc_lyŽ Æv|£e_möY4ØZ»í¶µ¥iK«JÖ�Ú½…¨êN¯ìŽÖäÍ6‡¬¸0ì-Ü8õ¥³Ë¨á|{cÉ6QÃumº–Í~¶ú[ZÒ´¥kO±à»S°u=KÕý‘Ú»øå›EÖ| ˜vê.¥³Ë©áœ{cÈöQÃuÖßm²É‡­¾–Ö´­)ZþG‘ìyÄú³�s~ÎçÛ¬N7ÁzçˆòNyÍ9}Õ³AÄø†›7�r=Ömô¥kf&«O¯šy+JWÒÈê©»—r­ï:å|Ÿ›r�ŽNß“sCºå\‹m™-óåí7¼‡e“·Ûìr¦’·I6NnÃ.IoºêÖ·]uk_µW§,åÎqʹ/4äùómy//äžQÈv™7Ý&FËyÈ69;m¶|ò_[¯¾lÌü¹$¾µ­k[®­j¬•ò}ß6å<“™ò\ùöœ�—o÷Ÿ�lòoºL�Žï±ÉÚísò$¾·_$ùyùrI}Õ­k[®­j¥+ºû_”wÏrv×ys¸Óšw?fóÎ×åôÕã×YNQؼ«kË÷ÿ£±+|µÅÁý-¸—èÇ[®öGéOZúz¼óϾ:óð�áÎ7˜up#—‡Ý�¡­»,¾Ir³6x;l›ëéwÙ^ÿâÖøù×u7wu¿Ÿýs§¦ò3.βîø1"¬xáÅ8õr¸o(­ù©©LÎÐëmL2âàëñ±¡Éâ3æäI.^ÒK«tz‘ðC¼/Ï—ÿwŸüWñŠJÓðÆݹÿÏû~¥YødÿîÚÿ‹wý¾Ó˜ü¿û’_ø�ÿãL—âV‰V'0þCvæ¿ º¹û¿i X]GÕ¼ï±dÀÈȳͬ¼?Œì·¸ºk&¾û)L�Î^˜°ÛJûä–km¶•º´¥qOzö–¿¤:S¶û“k ™XYÖÜ×°'Á¾k1ÿI¸æÇyª)d¾;~óµŸ Ühmõ÷_,¶Ûo­Õ¥«½{G_Ò=+Û}Ç´†<¬­ë~kÏæÂ–{1鲯ã»Ü:¨å¾ë)L�¬øVãEm+î¾Ym¶ß[«J1·sv·莟ínðç²gCÁºk­y×ks9µxwìvqq>¼âûN]Ȥ×kãºÉ3³¬Ó鿬0ÛZVY)Ki_µSîóu´ä›­Ç"ÞfϲÝoö› Öãc“}dÉÏÚmræÎØfäIw­oŸ+.{ï¾µûkuÕª°æëiÉ7[~E¼ÍŸg»ßí6­ÆÇ*ï~NÃi´Ë—;a›‘ÙïŸ/.{便»uÕV»Üí9çoÈwy³l·[íž~ço±É»ß‘Ÿ´ÚeK�Ÿ›=ÿéæÊËžûï¯îÝuT©v‡dsäìÎÄíîÂÚ~œçݫιods�Õ1±°¿Lss¿Ør~M´ûžPaâ~�Ým'—éEe‘GïöÛm-¥(ùo–ù� Ù¯ zo'ü­ñÿ ­‹>LÎí.+Çù$º»>¦Ã_©°�cÏ78¶Ö´§ÔÑð¼üÏZÖ–Ò�Vµ­)JÕ±þ t|¾IùAÑ]HseÀìnÉãz^Gv»×ïØ|2,Ûv|çi‹Z~Iµ;;*•¯¥)ô}kZSÖ´ØßúF_$<žè¾�¤Y’`v/dq½/"¿_J×7†Ç›fË›ìñ½?$ºŽƒ�•JÖ´¥>�­kJzÖ›÷ñiâÖ'š_!^&øÛ·À×í¸§?í�Nñ5-®ÇIç©úó cÙݹ¦ÇÚê}»<=®ß¬¸fÛè/ŠJæË),^¿RÛU0ppux8zÍf.»[®ÅÇÁ×ëðqáÄÁÁÁĆÌ|L<..<6Ù>><6RË,²”¶ËiJR”¥aááâkñ1p01q°p0q ÃÂÂÂ,lLãš­ Vcêu\¹3lî>½ÖaãEdpâ[®à¼÷] ÐÙm,Šû.²ßͶ�Yk+YÜÝ}]ïiÆ·z~G£Ì›]ºÐmuû½FÃû£ÈÁÚj²áÎ׿A%µ¥ÑÍ‹——Ûu+JÒëiWÔÑî¶œkw§äz<ÙµÛ­×_ºÓì1®­™MV\9ÚüØ/§ÛdعpY}µ§äºÚ>¦�u³ã{­?"ÒfM¯ÜèvšýÖ£?êÙ‘ƒ³ÕåÃ��™ôô­“ceAeöÖŸ’ëh÷ÝSÙ|»¥»G­»‹€gE«ç�Mϸweð�œØñåîåÜ‘k¹OΗ_èYQbnuPÉtw~mô·Ò¿eVÏõcê{�©º¿·t8óâhûS®øOcé±2o²LŒ]O8ãZÎO®ÇÈ’;m²IáÃÚYm÷[JR·Rµ¥Œug`j{c¬zç´ô1K�²øì 43ÝmóE©æ\]ȵÑM}”¥—ËÊÊ]ZR”­iö- êÞªí~²ëžÒÑE,NÊàœCŸé¡žûdšW1ãúþE¯Šk쥶_,x›)uiJRµ§Ø»7†s9Ø\;‰óîµÅßqqÆ´\Êï0d¶l-Ï亼]Ö�k‡-µ­²âìu™±MÔ¯¥Ö_J²#Þ=ÛÒ€+ùMý£>hÿ(^Æþ®N®äãö�ùwýü9Ÿôò¸ï“OÚåÏ÷ïæÓÊ¡ÿ·ížóW÷Ó¤?äÓÓ-h«FGLÛð­ÿs?1¯®¡þ s”¼ ï÷/ò³úýëOì{”%Ñøi?¹�•_×çZcü¡cWà÷ý�¾@-.gú�è$°’wIÁ,`eÿ/]õw‘_"¾Os\|�­ ã\û'©¸¥±l?Iké êx:ò݆¢z]tTÖòm¯ËÜ[lÐýûëO__Z×Qò¹Ýòw矾GrÈg’]æùSÅã¦m¹øQéz¦¸%ù𩬭ÑS[È·Z\ͽ–ÙZÛîØ]Zzú«´ùWîëûïϯ#9dß.��siú³ŒYne¹ØVizª¸-Ùš¹¬­Ñ~Žä[�.^ÚÊYZÛîØ]Z~UPˆgÉÌ�'¾X¼œÍÆÚí¶C¢·x>1ðŒ Æ&Ü{¥c›CØš¬;q©Y2õ9}ã‘ÊöX“Í}òË�°¶¿™g²+9®çsžN'6Wÿ9—^Nô·ŽœjLŒ\®ÐæÚý>Ûk‹ ñÞƒdÛ¾wʬÆÉÈć.î-Âõyû AY,®Eq©µ÷_kb¼JñóyåW’=?Ð gÄŸ²9ޝm´Æ³\�ÀŠ}ß7ä�Á•,eMÇ8v¯;:Èn¾ß¯~=#¥}×ѱ&xû¼ò£Èþ è “âÏØüà W¶ÙãY³è¸ž Sîù·$Š ™`ƒ&^9Ãõ™ÙÖCuöý{à¤t¯ºú7ŸãSÃ�—ŸpøñâÎ4¹˜ZÅçdv6ã_—ú7?EÔÜC /™vžÓU³—M¿ÁÀä–ð]t:jåâɉ6êlH%ô¶_U§ƒñn²á?®8>¢  à<_CÃ8–�ù¤ÇÓñ®1«ÅÒé5�Ë“$Ù2ǃ­ÂŽ:_%÷É·Öë«ukZÙqÂøwëÎÅ8 Õc踇ãzN#Å´˜µ–ìmGãšÜmF—YÓ_,×Ń®ÃŽ+n¾ë¯­-õºµ¯­VUð¾Æúó‡ñ^ÃuPhø� ãšN%Åô¸µ’ìmOãºÜmF›[Ó_,×Å…®ÄŽ:]}×_u-õºµ¯­Wq~1ÆøGã¼3‡htüWˆq£Œq^1ǵؚ}ãz ~>«G¡Ñêp"ƒW§Óë1"ÇÆÆ†Ë"‚í²Ëim´£Õ½3Ò¾è;ŸˆãÃÌ~íñ_ä‡ÔÇ7cx¿µ¦Ók>4ݰÜtï/ËÁÔs}ÖbaK>uÜ[y]nê;ò&³_®ÇÚ_oçÏw¯?‰°w7Š˜]ÿÇ5–MØ5ì®Ûì&dž[óvÝMÊ20µœË_Xñqî¿*î7µ·_¹²L‰).Æë?:{½x3óõâ|Ëâ¾qÍe“v�Û*í¶cÃ%Ù»n§å8zÎa�u˜Ø÷_—wÚÓ_¹²Iä¶ m�ÖSÝ=Þ±½üOž [åǦӻxž¢üþÜð§7qÜz[ñí¾L½‡Nl±5ø]ÿÇíû×!ÓéuøZþ5§×òüŒ¹1óókû–$t»:Ot �‚B5Xˆw_†o¿ïìO yÿEìöån|wí<¿Ñ8u†ëaÖõßmC•Ët[‘[«I§ÈçºþU%ÖÒ”úvV?æ¦Éøu»Î¼ûÄkÒûÿ¼n: ³3lÖa[ ÖS€öŒy<·E[æ­÷Ryò¹Æ/'º¾”·Ù¶SÓ÷SYü;�ã^{â5é�Ž}r6ý Ùy¶ë0©m·�v„y¯GK¦­×}i²y¾'&º¾”§²ÊYE˜_…É;{‡ã.N—Ùæh­ä~*w9àºÌ,ܬŽE7]öM½ÁÅ9G%ÆÊžo»[°åœÓ’éð+ #‚ìN?K)o¾9.¬�’wý&à?—ìæÂþP½cýCçnþ!?Ùùÿg¶þ‘ån~ ßÙÿÿfþ·þ‘ånþ%ߨÃåŸï§�_ò–ê�aA±Ux®_ ay•çÕòý\;N¥éŒ_íÏÙøY¸ðäkwØ\ce�ᙘù¸9ÚíŽ'+æy¸QçáMm´ÊÒCŸKn¥ÖÒ´ê§Ã§ˆ¸~[y�Ãðyf²-ŸVôö/öÝì\<Ìhò5»È8öà )Ãó"ÊÂÍ×fãr^]—‰LÜ9í¥2ôøùÖÛZ]OZuCáçÄ|?-<Ëâ<¯[Ë«ºƒûnv&fÎxì’øuðì÷1]5öÛumŽ•­)_OE¯½#Ô¼_¡zw«úS…Ç[8¯Up>-À´’I6>NnÓbjmÚl,Êy6Û{ñnÊÌ––Ò³eM$—zÝuk[>z{«øßIõO[õŽøøÇYpŽ3Á´—Kådàq�F&¦,üÿ»ÇRìö_u®FT´¶•›"[ï»Öë«U�]?Öo¥:§®:ƒ‡Çt|c¬øOàú[¤Ž²rpxÖ£UÃ?îÑÅ»=•q«‘•--¥fÈ–ûîõºêÕuXuÇéδë΢ëÝWè.Õ|‰uÇÒS''3ô?àú ã:¯¾fË>fWèý.®~¤·ß-þÏu×VêÖ¬¢ÈìŽ÷ Žþ)¡ÉÐxá妧Êfjö;_¹ÞÞf¾|�vη`õŒQàÖêÁ&¯; •}ií¥+t™ðY}ký”‹7âRñú��ÞQê°c¦f«c³è¾m�I'“''[´‡gκæÊcû«ZŒìKI&öÒ·K±†Ë«_Ì¢-߉/ "ÊÐt”:¬é—ªØìú7šçRY¯ÈÈ×m"Ù󎺲˜ÞêÁ«; ’RI½´­Òìa²ê×ó(†‡ãñ3”ôGŽhñí?¿‘õ?:ÍèÎÆÍÕñ¹2ó³:ï²ð³y7 ÜrŽKÕ®¯�p.wÅòuø1Oea“cÍ«m·Ù$”²Xt¢`‰Â¿àª^vä½ñá׌]½›»·‘n¹ßFõ¶ã•î)%%¿+œS‹kp¹Ý™ÛöW3˜âgA=?rhî§î,Öðç´¦îß‚üO[k™)æZ|ÎS­È–Û캶ðó‘wnÇȹ‘<ó?; *“Rëäëî³ÉÙpÞ7�>=m¥ø¹rúò9ýk_è¸ùÝJzzVé´~¯ãëùvlpí‹�ùÎóóp²©5·]'_õ¦FˇqÌy±ý¿S"Þ]^E7Û_è¸óÃu)éé[¬áü,ž(âtÆn£¸öº˜q9÷–üÿ“vžÏ3/C&§‘cuïÎÊ뎱ã™Ù“Ò™�ÐqÝŸ&ÔËéH~‡+’èýi%oºEÎû;à’xœ¿,>-ÁåÏ�ÝñÖ˜š»¶œß�ñœŽÔêË1°¯ÏÚS±zß'�ê5ºl{f‡þ¨ó-d9¼{ß_w²¼—RÚÝJQ Ÿ'~5CåO„ÝÛÖøºËö\ËOÆò;#¬ìÆÄ·7gý°zþ ÷úmn¢;äŠÛ3¹vZÉZ×ÙÖJÒ•¯£A¾N|m‡Êo ;³®qu·ì¹Ž›ŽdvGZÙ�‰LÍ•9ÿ_Á>ÿQ®ÔÅY#¶ÜîY…VŠ·××ÙÖJÒ•¯£ŸŸ)þ!ÇçG€>MøÛ‰®»e̹\ì7�S Yº�TßÛ�€Í�κ«ôÞë'K¬Ýs~=…­ÙÏý êê3r⤱RJßmd*ä•Ð)ÒKóð¥ÿÝçÿßüb’´ü1Ÿ÷nųþß©V~?û¶¿âÝÿo´æ?ïþä—þ'øÓ%ø•¢U‰Ì8iøˆ»j^³øÒæüs+ë“Ýý›Ö=K\s] ˜GµÌíMŒ¶Òßü½fv§«²0¤²¿ebʺ¿¸ãÏ_iß×?ÜÛA ²Û—Ü�…×=Y�• ·Å~%”ÚävVÊêû?òäYÚ^·ÉÃ’Êú[ty7Ðq£ç›´¯ë¯�i¡‚Ù-Êî>Áë®­ÇÊŠkâ“Êm2{'cwæR¿V<í?\äáÉeÞ–ÝMßô+Â/Ä—ßT迈¯"ñ°9fWåÝݱëÎ…áòá{)‘¿¯6æZÍ·cq?t–Im1yHñ~UOÙî®-d¥µ¶êÒêW¤�šÊ«ùü6ýi…Î~Fìå¹—É�3Ñ�›Ø:¯eiK&Úîr8×S}ÞZVË«[k§ìÌÙ)é[:*}¿énîgáìëÌgòü(Í÷Ò^¤éNÆçzŠÛZ{.Ûm²¸×WÖ)=mºµ¥Ú^Äͺž•·í³òþå{�ø|ºó™yùü(Ì÷Ò^¥énÅç:«­­)mÛ]¾W륭·V´»OØy·SÒ¶ý¶~_ܬ©?§Yarï’>Îì·‡i‰Ô¾*ó­–ƒ{=°ßüç\ð:Ljë²1)}ÿ^<ý·Ï临¾Ëkm1ïšÛ«O}´ºéÎ'$²0\ÏŸMò­åxE�ÂîŸÜ_ V{-·3gÑe“±–¿�w¾\Ý…ÒÏ}ßg­ò×ìýÚ×áóo«ÅÕü›y%n$qÃuz›i|QÙì¶Ü¬î’뉳¤¯ç]ï“+7êM}ßg­ÒWìýÚ×ûóeªÅÕ|™y!nqÃö¨Úßv{-·+?¥:êlé+ù×{äÊÍú“_wÙët•û?vµhþ'Žüù‹òwúGïßÛ„t'1û·Üþíúî=-ºûôwÖûÖGé«üûßÖöAíû×ÒöWé}I9å3•(þ€,¢øIì|ÞÍøÀñWm´Ù×g´ã|_”uÆUo­•— ­¹ÿ+á\gY-,¥-¶˜|;K®¤_»X+eköÖ« þû3²>7übÛl³«�±ÐqnA×Óû«edÄÃëžoɸOÁ’–R”¶˜üWGƒôéù~•l­~ÚÕaWÃß`eö?ÇŒ›]Žus¶<‹ò¿È÷ÖÚɇ‰×\Û’ð¾?ƒ%,¥)m1ø¶—éÓòý*Ùëöú­Úø=î{Çâ[Àþi¿#Yf“¢t½?v6U!¶Yfñçk¸è�…´Çš{>ï·Èë;²á­n¥õŠkk}¶_î²Þ«:bésªÀ À>ShÏš?ʱ¿«“«�ù8ý ~]ÿgý<®;äÓö€ysýûùŸôò¨Ämûg¼Õýôéù4ôËAš*чÀ6ü+ÜÏÌ_ë먨å/Ã;ýËü¬þ¿zÓûå t~OîcåWõùÖŸØÿ(XÕø=ÿgo�ËK™þ£z ,$�ÒpKˆü€ìÿí%ÐýÙÜÿq‹gý¨º�²;?ôl÷ß;à Üò¿¸Í$u¤–E—ú'éÝ[kK©K½iö±wxö=�9Ò½ÁÛ²aÙ°�ªú·°;M}÷_m™Öp~'¶ä÷aßt~’[fMº¿ekoÛJ]ö}¬]Þ�gNô·oöä˜vl#êήçý�~—_lyÖpŽ'¶ä×aßtu¤–Ù“Mg²µ·í¥.û>Ö=í¾ÍâÝ)Õ=›Ü¼ç*üÔ�{Í;7˜fÅÓI‡Å¸Ùr®A•VRëå¿Sªšú[JV·Vž”TÕ••‘�““›™4™9ysÍ••‘5Õ¾iò2$ºYæ–û½n¾Ie¾·]Zýµ­UwåedfääfæO.N^\óeedÏ}ÒM‘‘‘%ÒÏ<Ò]ZÝ$²Ë}nºê×Öµ¯ª¯ü¬¬ŒÜœŒÜÉåÉËËžl¬¬™¯ºI²22$ºYç–K«[¯–Yo­×V¿mk_U&\»–rN{Êù?:æ[¬þIÌ9§!ÝrÎWȶ“W#g¾äœ�e“¸Þî¶9¥+>~Ói™,ó__礒µý×ððyà° ¯�ðî{ Èo+wšùo‡ƒètÝ-×ù9PO¯—{Ì%³•vn\´¬Ø{Î;¢Ñ鱩X¾Û°÷ó[uimÞ—JðÙxû×›wߓۜn‹ˆéµ;ÁrfÆ‚lw¢X¹O=ÊÆšJVl]Æ‹O¨ÓAmÑúVì]ÔÖ]_K½+'¿Ãsãü[^oß>Mî0eº.#¦ÔôÿÉ› °¤Ür‰#åï+y)Y±w=>£O·Gé[±wS[uiK½.œÿàåñV:cyiæöç å’}'нq°ÇÝdýç8cãݹÝx{n9lVáÖ »§àë³n¾ùit9±[m–ÖúÉ3t¹ÝNDå¹Ï ã}‘Ây‡]ó-l[ž!Ïx· á|«Q5×[×�ò�N^�y­–ë+möÅ�¬Î–+«JÒ´¥ßcÍó.#ǹÿå\–ëaÜñNmÆ÷œG“j2iZãíx÷$Öei·ZÙéJÒµ‡;[›,Wú¥º¯7̸�çüC•p>[®‡qÅy·ÞqM©ÈõúN?É5™ZmκomiwÑÍ×fÉÞŸo¥Õ|~EÇt¿�ïxŸ+Ñê97åmŸä¼o�k°÷A ÝáO­Üè÷z�„9MFÛ]“$8ÓÇ|3Ã%Ö_mÖÝZV¨^õê}ßCw_mô—$š,­ïRvG5ë�¦n=’G‹±ËἋa “g…lÖÙ%p6p¦D­)î†KkûªÂ»·«w!Ü}©Ó{ùìËÜõgas¿ØgE ¸øû,Ž%¿ÏÑ×i‰ô¤´ÁÚ[…Lˆ+_ç¡–ÚÒµ¥}UŒ÷_Wî:G¸{K§yÖdîz·°yØæÅ°ãìr8žû?IvÏ9©I)ƒ³· ™V¿ÏC%µü•RÿæoŽ[<±ò'Æ]ÍÛ¼‰:O·¹ÏÔíùží×”ñ-6÷.>ÍåÔVI¬ÃÄçœ2ü Î/Ó’H¯ÅÎŽøï¾;­º¸©ŒŬà þ^ßþy»Ø}Q�²ÊÇÕwGHo-×ë"÷]�²æýw½Ór}4ÙV}K#·î2~GXä­·ÝmÒûiéKîªA_‡3µoâ~bvWåì&ÇÕvçLí¤Ã×Yîº-‡2ëýî£{§’k~¥¶[÷%�È+mõ¶ë©[ý´ô¥×U ?çڗq?0»¬2óçƒWÛ}7¶» _ºèvÇ€ï5íD“Ûõ-²ß¹q<Íým¾¶Ýu+¶ž”¾ê¦ø:;ŸkÇü¹ò¿ÇûqµŸÁîÕñçCÚù» ï¾Ý¬|�£{UÅ´½u¾´ŠLM†Ÿ¾öóäÒ´­ô®U·ì¥éÞ¦¼šªÂ�?—ìæÂþP½cýCçnþ!?Ùùÿg¶þ‘ån~ ßÙÿÿfþ·þ‘ånþ%ߨÃåŸï§�_ò–ê�aA±Uxx¿†�Ç‹z×¾cß+àßù%ÙYÓëòþõ‘fu×RI³á\nÚâ[J߉•�ÎryUn­õ¥e†H«Kim-ºù¶þî…�®ü;å×�‰ô·¾CvÃ'2Ùì¾™< «fØðÎ=˜öÖë ÈÆærrkë[½+$SGZ[í¥.ºl‡“¡ãëϹGugbV-ç��…Ÿ“‡—I¬ºÜžÕólxo†ü{}×Á‘�Ì¥ä·V·V•’)£­-¥´¥×Ù§øU<_Öt§ÆN¿ºòq0næ¾Zvo4ì]ŽÂí š®AƒÁzÿs°êNÄv{ ïº}Þ£;ˆî¹¶Z[6ÇÊdúv×Ýt’HÍß·}R]iÏÈg\ÃÛ> ùuÀ¤×I¶ÊÜxõÚ¹Z=|6[,Óò®?Ä6œ“‡ýî²ÿ©,<§Q‡}´¥)un¶žÚÛ_JÓT|ìàö‡†Spku÷msw} Ù÷i0,²Ù$Ÿ“긖ÓuÅ>•—[ºX¹.»ûiJ{½ÖÓÒ´¯¥iª^up;CÃ/)8?Ü/Úfnº#³¯ÒàÇm·É?&Ôñ=žïŠý;.¶ït‘rMn-öÒŸ�ZÛOJÒ¾•¦´ù¡Ô›¾ýðóÊÞ‹ã7cGÉ»›ÆÞñê¾7.\vË�!ì²äüSG‘4wI .�kµŠúþ}žžß_u¿–•f«EV¨¥”;/Ã!ÜxÜÇÂÞÍéü�”ù;î—îÍ–dé)Zìàý›¡Önøý`¾·}Ÿ~æ^K}ÑÒ”¥·[îõ­o­)5ïÛÛprÿ{ªr¶7änº{¸vscë®õ­ºÞغ}v÷E|wVêúS;–ëyÕ¶”¥)[=}k[«é5?éÛ0rï;ª²v2dnºƒ¸6sÁ¯»Ö¶k¸gbiõÛÍñÝ[«í¦o,Öò«e)JR¶{½k[«éf_áCïŠö—Å´=Y™MF6ÃÆ~õí·ÁÂÃβ}¶gæù:îñÖr=Ʋ9pmÏå=¡¼×ãWóãšÍMÕ¶ïu·Ùd‘’ H&Pr=œÒ¼Çå'ȸbÍ—7]ñ:«…ë~§Þ-·š¾¥áYÛœ(bÉŠ+¢‹”í3é_m+—Öé,ºëo¥Õ€Î0—–|•wÎ=2ïËÀáØaÃõžÿ¯m¸Ñ`õ‡Úmq"Ž{#¬vCÉwßÎÓéßuk}µº—zÖ7œÂþYòMßöåß—�Ä0zˇëkw×¶Ü{0:ljlö¸‘Ç=‘Ý�rM¾u?6žËî­o¶·RêVµzþ)NÛÛö?ËÇiðí–X¸}Ô½Ôš ãȬ×쵞 ‡ÞófÏ`Š˜’Ǿî¼ÜjGK¥¥lǶÿu+}l³ŽÎK96ŽÈ°?}5qü™ô/éUÛ^?ÕvrÞåÞ[mn¶˜p~9›ü Úßu¶ÝénflôwR•ô¥×z[ëöºÃð—ÔvvÏÈ·J_�¬·g¢ë9gnnì»ÝJaßÄxþ\GgëKn¥.Àì]¾–JzúRµ¥)ëëZ:¹ð£Ô±öÇÈŸJÝ�­¦ÏGÖPò¾ÚÜÙZÝJaÉÄ4qq-•n¶ÚÒ•ÁìM¶šJzúRµ¥)ëëZ$Iø\z6þàùlë~Y|ú¿Ñ¾9õ?ow–×]´Å¦_é{$Ðãt®– u·Ù|Pí5¯¹µûXe»Ò±~�­ÖV’RʬwXŸâÐÀ1~dºfÞðøÖò§ŽÃ�‰6ÛˆuýÝ¿¥ÈɆÉfÁÈéÝžcm¯×VïK¢Ølx·Ø`[[?>ë3.²”¯¿Ò¼çùiêK;—ãÏÉÎ?dX÷lx¿“µu9Cd³bdu.à °öq­Ô­Ñæltw3•·óî³.ë)ü÷£�?,½Kgr|{y3Çã‡ý—àrv¦¦yb²Y±2:›a…Ø; àÖêVèó6<�æàÒ¶þ}Öe]e?žôs;æW£°ü‰ø´ó›¬òìÞÏ<}Ëû3EƒÆ±¯ÍÝm¹‡EÓ¼x>‹ (r'Í·}Ìzë hb²é¥‚{ì�óëj´uêïU,?ü==‡�Íþ0ú«C+\®¨ç}·×›+}—V\ŒŽs³ìŒZÖ–ß}c¤:~ÃÅŠ”º–WÛ+ééZ]t÷~ùÖ?0øßêÍ4U­ù]kÌ{O‚ìoºKoºì‰¹ÎÛžâÒ´¶û®Ž‘iùÎ,vÛu-¯¶ÊWÓÒ´­g£ðIΠæ}]¦Žµ»'­y�ip]…õ¾Ëî¾y¹¾×žbÒ´¶KîŽ�êyÎ4vÛu-¯¶ÊWÓÒ´º¶¸þnm›Î~<<ÉÚïk¾Üq¼.áá9²I<3dêð¸Ÿ}v~§Šh²-‡Ò¸ôÔðhuqAeô¥ÿt¤WWÖ—Rêö娷aÝ¿EûñHöz¿ÚÕûµšÍ–ëe¯ÓiµùÛm¾Û;YªÕk1'ÏÙlöYóÇ‹ƒ¯×àâÇ.VnvnT¶ÇQÛt’Iu-¶•­iE®þ)tí¾>xËÐ=!\| |¾¬ê¾á;�Ñ”²˜Y|�EÆ5¸\ŸiÖ[m³_¸ä1åeÉ'§¬²MuõûnªÏ:žÎ‰ñã¤:j‘áÙ?YuWá{)0m²ÜlÍÞ‡�ë°7Û;k‘Ù$›mÔYRIéJÉ$×]_¶«9ühê‹:+Çž‘éºE‡GYõ_á›;°ie1²÷š.9¯Áßì­º;#¶Y6Û¸²2d“ÛJÉ$×]_¶«²: ¨t^>tWJô'ÌÏØñž�ê^¹ê;°Úß›LíZðý7 Ôfl¯†8¡¿?+_¥Žù«m¶ÛY.­iJSìgöoföZU¡òÑQøÓæï“½+‡©ý¢áý¹Éçẟ«Y¾ã×¼¯"Îi×}k«[¤õà|‹]wº¿m}~ßµZ�žý)ŽþeùÔšÈôºN-Ú\‹+‰j¢¾²G�Àù\±ó.¿Æ¶û«[®ú|'�àSÖ¿o¯åV½çŸKGãÏ™>Gu&²Í6—‹ö—"ÊâZ¨ï¬–`ðNU-œÇ€cÛ}kZÝK8W Àûköÿ7íTó;ãîŒ)>kõžý5º,~æØö7×ñí\z]/ã=ç¦Ò÷—âÝ\>˜øxÜ/CØØúš[-Š¿rõŽÛl­¶ÓMšŠÔ‡1@üü)÷yÿÅwÿ¤­? gýÛŸñlÿ·êUŸ†Oþí¯ø·Ûí9�Á{ÿ¹%ÿ‰ßþ4É~%h•bs‘þ*ns‘�ż4ëLm…µÄÜòçç;�U“þ}¹k]×z5°ÈÆ¥ÿe·EÊöÑÃ}Ö×Ö´––Öž—R±lüLÜÛ+Šx‘×8ù–× uÈ{o›mµöÍùöåq�oÑqìɱé¥-¾.]³²+î·òÛ%-¯óÔ¬\ÿÿ5ÉÆâ¾%õÔ–×sÈ;kšíuöÍ_}¹á“Iãw†=Mue‹³;¿°»†ý·æúaIÑÜ …Ç®¯¬”¿×io�×KOK.§ú�í­¿e.‡%H™«øfN•ò¼@ÿÊï¼?Ûžsb¿ç4ùÿ+žïÿnyÍðÿ8Ûæ{üµvŸàCÆŸñ2Λò3þZ>Bÿ„mçý<ÿœãäü®ûÃý¹çóš|€•Ïwÿ·<ãüão™ïòÕÚ�ÄÉÿ:oÈÏùhù þ·Ÿôóþs��?ò»ïöçœÎiòþW=ßþÜó�ó�¾g¿ËWiþ@ÿÊï¼?Ûžqÿ9§Èù\÷ûsÎ?Î6ùžÿ-]§øñ§üLŸó¦üŒÿ–��¿áyÿO?ç8ùÿ+¾ðÿnyÇüæŸ åsÝÿíÏ8ÿ8Ûæ{üµvŸàCÆŸñ2Λò3þZ>Bÿ„mçý<ÿœãäü®ûÃý¹çóš|€•Ïwÿ·<ãüão™ïòÕÚ�ÄÉÿ:oÈÏùhù þ·Ÿôóþs��?ò»ïöçœÎiòþW=ßþÜó�ó�¾g¿ËWiþ@ÿÊï¼?Ûžqÿ9§Èù\÷ûsÎ?Î6ùžÿ-]§øñ§üLŸó¦üŒÿ–��¿áyÿO?ç8ùÿ+¾ðÿnyÇüæŸ åsÝÿíÏ8ÿ8Ûæ{üµvŸàCÆŸñ2Λò3þZ>Bÿ„mçý<ÿœãäü®ûÃý¹çóš|€•Ïwÿ·<ãüão™ïòÕÚ�ÄÉÿ:oÈÏùhù þ·Ÿôóþs��?ò»ïöçœÎiòþW=ßþÜó�ó�¾g¿ËWiþ@ÿÊï¼?Ûžqÿ9§Èù\÷ûsÎ?Î6ùžÿ-]§øñ§üLŸó¦üŒÿ–��¿áyÿO?ç8ùÿ+¾ðÿnyÇüæŸ åsÝÿíÏ8ÿ8Ûæ{üµvŸàCÆŸñ2Λò3þZ>Bÿ„mçý<ÿœãäü®ûÃý¹çóš|€•Ïwÿ·<ãüão™ïòÕÚ�ÄËR;K¶;+»¹Æß²ûw›ò>Åçûû5±îù,ÙO·ßm#Óë0ôº»3v7]4ôÀÔëàÇ‹Ý_ÌŠ+m§ÙJ5K³{G±;ŸšíûµyŽûŸs­õšè÷<«’çI±Üì¬Ôë0ôÚËr²åüù-ÂÕká‚?ü¦8­§î5W³;G°û›šm»µyŽ÷žó�íšè÷§’çI±ÜìlÔëq4úÛr²åüùmÂÕàC~¿ÎÇ´ýÇ2<œò›¿<Éí½¿zù+ؙݡڻÝf�M²å9º~5Ǿ¦«�ëaÔé°1tœCKǸæ» SÛ�‡Õ–ëå“ß,—ßv=x'‚kðx_†3•[¸ð3²¸Ü¹W3ˆy9Í!‹´üìM6ó®ú³k…%.¤1ÙXò¶×ì=)[ï¾—Yukéml¢mŸ‡+“]·ðŸè'Ëú³ñ/#y–.6%iOv&£oÀúÓq�u+l6R±äí²sëJVëï¥ÖÝëZ[í¥&Ãøt95vþóþ?6WÕÈâ~Eó l|ZÒžì]Fß‚u®ßú]l1Ò±äír3«JVëï¥ÖÝëZ[í¥,Ïü'½©¹ì/Šjq-¤wYƒÑ~J÷UñË«.4”›M·ÖðnîžKlƒ_‡,Ó�÷}žÉ¤Ë’µ¶·RZGu�C#g÷I„XÊoíóGùBö7õrup?'´Ë¿ïáÌÿ§•Ç|š~Ð.¿3þžUø�¿l÷š¿¾�!ÿ&ž™h3EZ0â8fß…oû™ù‹ý}uõœ¥ãøg¹•Ÿ×ïZcÜ¡.�ÃIýÌ|ªþ¿:Óûå ¿¿ìíòùis?ÔoA%„“ºN c9=ó‰Í¦à¿TçáçE…°Þè¸' Ķù)d™Ðó^Ôàükyƒ�oԎ饓Œìs¯ºÚV¾‘Y}Õ¥m¶´sæW™äð��'69pâçnt<7†cÛ%þËòñ¹¯epÞ/»Ä‚ÚIÒÍÚf][iZúYe×V•¶•£˜ß2<Ê~ñ·äÎÃ.,\í΋†ðÜ{d¿Ù~\Ó²xwÝbAm$Žée¿Žìó/­´­¡ÙukJÛJÑÉï�ßÉèÿ‰;9¦& › w])“ÔcÉÖ¶õ•ȹ6gã9ðY[i[1²úëŠi.·Öµ÷}·R¾—R”ŸÿÂ'RãõWÇGMdÝ�.ïµ3ù�mr?©_üíÈ䜇/QƳ㶶Û[bÉëÞ;¥ôü¾¾ž´¯¥hŸ—ÂGSãõgÇgNeW\×iì9�lr*KëO½är.C—¨ãyÑÛ[m­±dõÿÓVž¾¾ïOu+éZ-£øè:øõñ%á×ÌÄÒÅ¿ì~—ß›ýŽŸÌ{·tïmþ׳x~^ÞK}oÌÝjúÓ‘hõSK}kZY®²:zYe¶Ó²Ž´ºÌìh½ïÄSÓ˜ý]òEÉùV ˜±`÷·Wu×lS .Ç«wßlVÙ¹Û ‡ZݱȓÓÝ,¹õ¾úÖû®º°Eùøêl~¸ùäŸ ‘Y‰Ý}e×ý�tØÖccâlqqö=g´‡Ò;,Ž\¼Ìžºûü÷ÿ=|¹µ¾úÖë«ZÁCçË©ñúãä�r|+a�ººÓ€ömÐccY��‹²ÆÇØõ®Ò/H¬²92órºóïóßüô’æÖûë[®­kZoâÍè­Yü›é{_K©Üãây!ãß]sŽK»Í¾yµ>Äày܇§özí%òAÆý×|Šß“�ó’æRjý³ý¼(qEÅd`@-øuì{ú·äÏÃþGdvMnß´i×E%ÒÛÖvïÞõ_Ôº�Òêß~,œÆÙ£¥iìú±Û[½)JÖ�ø–çÿÚÛäcÅ=ým¶û6½‹'¾9.–‘Ýý´øÞû­"ºêEK«uÐdrË%²•§·êYmnô¥+ZtGâoŸ×­þE9…ÊyIJEu–_¹|Çu�5ÖÝOu·IZVµ­+ZÙyà¿YCÓ¾øÇ×1ëÿEåñî“ëÛ·¸^ÊÇ[9nçŽàïù”×Çu¶_d™|³k›5Ôºžê]%i_ZúÕeoƒ=gOxqã']G¯®³+�ô§__¼Ã­—Guœ¯uÇp¹1–øï¶Ëã“/•ís%ºÛ©î¶ëëJú×Ö«¨<9é\ϼIñ‹ÇÝ–N»;oÒ^?ôÿUïvZœ:kõÛŽAÀøƒ�o÷xØ~ù.†ÝÞç[>]Þëï’릭oºë«[«¸�¬m[dâX¢ž)!š8æ†hïŠX¥²Ù"–)-­’G$wÒ¶ßöÖ´­+JÒ´«üI’Ù|RÙd‘ÉeÑÉ–Òû$²úVÛì¾Ë©[n²ëkéZWì­âHãš;â–Ë%Š[.ŽHä¶Ûã’;í­·Ù}—R¶ßeöÖ´­+OJÐT×ä/Veôo}÷WKçIY²ºŸµû®dÈôüܺpÎWµãñæÇ_J{¡Í‹_l¶]ééu—Ò´üª½{÷¬§énòî> É¾IeêîÐçœïÛZ]—åM>u=ÖÛî�;Ù¬º”ô¾Ëéu>ÊÑX}õžGK÷�põU×É7Xv;à?^JV•Ê‹‰ò}žŽ Ûk[m÷Å›²Ùu)é}—Òê}•S çÏDâøÅæç–^?êô›®?ÆúŸÈ>×áü]Èl–ݵÝq®æ{zõ®Ó"I¬Žü¨w¼ ]vtÚ[“�“d¶þmôaæ$bf¤$iøgûòî»óg™ô†Ãa•“ÈžªÚãëu°YmðgvTÝ‘Íô9•ºK>”Zþ*¥·R—Ýõ'¶ßJRêÖ’ü:Ýã^æ3éœüù`Ó÷çYl#Öëã²—[ŸÏz¾ùù~ŽYîº[>œX\~OZV–ßuo’ÚzR•º´ïçáÝîúð?09—MgçKŸ¾ºÏa»;)u™Üó¬/Ÿ—håžë¥³Ù›“zV–ßw¾KiéJVêÒ]¿ƒûÈì>æ/‘^4mòøæ·Ⱦ˜Ós>?‘³Ë¿�n» wÛ õ\GŒCYmƒ:¹½}Ú<§o›²é­ƒEI-­,²_Yâ&ÊšâÄ0[ß$ÛY7? þmeËw¾è|§ïMU+ì¤~‘è»#�é!·ÛoÙ_d:ëiîü·z{«öÕZïÈnÞmß�þbæOZVø|”îmEµ¥”�úç»Í==¶Ö´­mÇÖÛJÝùn¯çWíª¶¿�­´»¯;|ÂÌš´­ðù%ÜššV–R?èZy¼ÑAOmµ­+[`ÖÛJÝùnüµûj¨æó´)Ûß,Þxòº][©©ïÎEÕþµÄ¦}zCWÒ÷[ôi,Þú[wô¤Þ´ûÅ)õ}¶ûý´Ò†œ´íÊð§? /]ɳïß*;f”þ…º†õÝõô‹ì“´9¤¼–*{«—dôõ·¨oû-‚K+éùÒGZ[l²aü4|ô�vù7Ú>Ÿì;«8wõô�ìþÙ<·'‘z{«“d´õþÕ?’Ød¶¾Ÿ�|u¥¶É%ŸÃUÀ«±îÏ&{CÓìáýYÃø»Ò?²½‘Ëry )î®M²ÓÝýªÿ%°ßm}?:û+Km’lÿƒ/‚q­‡`yýÙ¹ZÜiy‡áÞ:ðMâèë\Ì5Ø»®ãä¯[¾úRÌm®Ó«´ÒËo¶¾ë°£¯­=¿l×Ký<�“çÜ7Qؼšu÷ ²²èy×ä|7w¾¾²j9>Ÿ3I²²ž—Y_[ðó¯§ÙZÑ£Ís>+«ç\?•ð�ä[KÌxÖ÷Šî"ÿ纾C«ÊÔl#û+m?.ú~Z~WšæœWWθw,á¸ë.—˜ñ­ïÛÅO_Y5|‡W•¨ÏŽž—Y_[ñ3/§å§ýñÈǃ. ñrbŽ|l˜dÇÈ‚[)|SA5—G,RÙu+mñÉÕ¥Ô¯ÙZUQÖV<¸y9“R”›yqå¥=kJK—G%)ZÒ•ô¥ÖÕVN<¸™³Ûí›yqæ·ÿ)–.ŽK~ÚR¿eöÕVžN<¸™9“ÓÛ6,òãÍoþS,2]”ûiJý—ÛUÜã‡îº÷šóÈãŠCÁùO áûè`ºKà‹uÆvÙz]¤pß,PK|VgaIKku–]ZSí¶•ûÁü_Á倡ü/\†¹~w‡º=Ú?'·›‹2®–—G|�«:·ÜHáöRèë�7’K®÷V—ýzSÒžÚúÍ3ðÛî¾óá×qè.ŽOv£É=îÎÙî’—Yt;¾±ë {q£‹ÛëЗI%õ¯­iufü”ô¯¬Ð¿ öî¹>÷º9=Ú�$7›;gºJ]eÐï:Ϭ ·8½¾±ý ´’_ZúÖ—VoÉOJúÙ#øC7;]§ÆGkal3òrñ8çš}¡¦ÑcÏ%o‹WªŸ¦ü|ä3`aÛ_üµ�&ï{™•[ùöM÷~êJé$,”ø?Н�Ö~Eá7¶,¦³Kß|†\�«JÅ“]æwQëqá¤>Ê]d˜_Áén­ÞêÒêdzzSÛZÖ&߉³uõw~ñÛc’Ú`j»Óu$¿R•ŠÒÙ}MƒKÛJÛ&/èY+î­këI½)JzWÖ'߉“wYwžñËc’Ê`j»Ëw,ßR•Šzíòú§þ—¶•¶LOÐ’WÝZ×Ö“zR”ô¯¬¿æçkŸã§�CŸ““i³ò»s°Ö[%i‰—µÑbøç…§Ïž/É~N»‘çGßémÊ’Ÿé‘E�„'á¯Öv§—~-õ¦î;eÑóÿ!úc‡ï#»ß[oÒr.Å㺭½µ¤ScKw®»*_²Ù#º¿’—[_¶›â ×v?•Þ3uþâ+gÓs^ÿéî/¹†ïw¶M>ó°xþ»ke}’Á%}uù}–ßeÕü”­+éVÃx�Âõý�åg�o³iù§~uÜÅw»ÒM>ïŸñývÖÚÒÉ`¾ï]~DŸe·Ùu%+JúU¿Ÿ+/°~L|ã¸8»+%òïÇýÎÓ6H£ÅÈã¼S³xç+åKõ«K%ôãšLªÒ/Ë5Ô¥”ûn¢ÖUœ 4— 'ø•zç]Ã~Cµ|·_Kþ§nt]sMÝ÷Ge–þ�Ñîy—ZV8ï³íš–qî ­º·]ùÔ­þßçmµ_Ä?Àp8ŸžzŽS�ößÚ=À¹fæjÅe–K½Òîù‡_Ýe·ÙJVjÅÇøv·Öë½n§º–ÿ;KPüC<‰ùá©å81ßK»C£8+ÜKX¬²É7š]×/àGeöR•š±h8~ºµºï[©ïöÿ;KUËþ/þ¶Ëãÿ ]Ù¸ú ]v‡²[t‡¾Z×K㦿�ãCî›Û_&ì¾ÄÖæËKn–¸ô¬°ñ,zV¶YmõöSßuÔ¥”¶_‰�edùgÒéÚÏÝlþû¾ñY.·Òߣët´’ŠJ /Ð`)¿´gÍå ØßÕÉÕÀüœ~Ð?.ÿ¿‡3þžWòiû@<¹þýüÏúyT?â6ý³Þjþút‡üšze ÍhÃˆà ›~¿îgæ/õõÔ?Ôr—�á�þåþV_½iý�r„º? '÷1ò«úüëOì”,jüÿ³·Èå¥ÌÿQ½–Né8%Œ�gâ_äšoŽþ9®Åɾ9g’½kÇö1[“,æáãpîÎåVcIrYfm–l8Ì}))}”º*IéîŽÛ­áâ#ä9z_ôšÜiïŠ[ä']ñìøíÉ– dâcqŽÂåvA|Qßm¹–[ŸÆ`“éßK­¥ÑÒOOu–Öœ'üCœƒ+MàN—]�=ÑCË<‚ëÎ?�¹2AL¬\n1Ø<¦È/ŠËí·2Ûs¸Ô2ý;éu´¬t“ÓÝeµ¤w?—aãð¿ˆ^×ãsO¯†NÜí~�ëÌHóv6`äådkyökÝ£ëí»q°·¬e–ìkiuÖâG6G§¤­ 4ƒ¢ê¼€Âx¿Àrz§Æ�ú»69bÌën‹êN—ñÍ ñäðîÇøôñÍF>&DRÙ.º´ºÛâŠûkOK¬¶¾´¥ ~9ðYz»Ç¾ˆë9íšÙú了ÂÍnE²Ù=²ñ£ÐIlöM,ÖMKõõ÷Rø£º—zúÛm~ÊYéã§—¬<|螵Ȳ['ëΚë 5“Û-“Ù/á:=–MdðbÍd¶ß¯­.¥ñGu+ùm¶¿e.ééÞ¯ã=!Ô]YÒÜ.)àáÝC׫øœ2Y6Lå�üÒi¢Ç‡ˆw×Oòygž Œ˜!‹AØ\wk$³cbR¹YÇf%ktqÒ²_m+K~ÚѼ™TÂù'ø÷ʾ[àŽ7üR¾il÷úÛ�ý½ø2)ZGJßu—A[©u´¥}ÖÖ´ô¯¯¢×5�k7W&€#ùø”¿g6ò…ëê;p×ñ þÏÏû8u·ô�+pëñþÏÿû7õ¿ô�+pñ.þÆ,ÿ}<¹yIºÛbkl¶ X/Šl™«vOæÇeÖÝ}Þ”¥iZú½Gã÷òÞiÄ8¬vËtœ›”h8ý–Á¹SÝ~çm‰®¶Øqa¾)²e­Ù?›—[uõû)ZV¾­‡ñªõ=ë凌=#¾­ôÑw�ý+Õ{ªÙd×ÓSØ]“Æ¸ŽÆ¶C¯ÏÕgË}0ö÷ú[V4×Wì²Xîô¾–ç[m¶[m–[m–Ym-¶ÛiKm¶Ûiém¶ÛOJ[m´§¥)OÈ´þÛim)m´¥¶ÛJ[m¶Ò”¥´¥=)JRŸe)J-/¶Ûm¶–ÛJ[m´¥¶Ûm)Km¶”ô¥)J}”¥(º¥þŸõÿ@x߈K£¿´ÿÉ=丘¸š.úá¸õ8úøï²8325óðW÷Ê]e¶~•Ù󟳟Û[½ÔØ[}kJßZR?<�5^«ù朓 \}7wðžÚúØp¡¾(qó.À›€òHò.­–Ç~Ë?’ð\­”þÚÝZþ�¶êúVïJ@ïç{¦ëÕ œÓ‘âãâãi{»…ð®ÖÖÃ…ÑÃeøðNKfEke¶]²ÏäÜ/c?¶·zýþÛ«ZVÿJVwø¯|z›ª>O¿·$;ÙôžQô§]sü�¦~±h æý{…/Jïø·ÍŽ;lÊ»UĺÿŽìó,ºë¦ŠmÝ+w¥’GGeq­0kñ»º÷ž8w÷M÷ÏŠü­¯Rö?çqë­È›ÍÖ'ÜâæíxöTøòE5¸‹Uø94¶ë}Ðd_o¯¥Y—Ç~ãÜø÷ÞÝGÝú%ŸcÕ݃ÅùŸÜ"žüoÓm®6Nã�Ï4wY}˜|‡Qlø3ú]OtÓ×íf?{‡qãïzu'vèl“#cÕý�Åù•0#žüzn0tÛ\l�¾†y£¾;íÃä›gž”ºßt9SÖž­¼ðÊ]§…gxÛå.¶MÕ1z{µ8ï å¸\r\ûí÷ZgË'íŽ%¨³u|Zºl9—YnvÚ¨¯–HiÙ”¾’Åu´’ÛZ8g/ã�…Ãø§>áÛ\}ççkEËø¶ïÝ÷]Ç亼]Ö�k�ï¶Ëþï°ÖfÅ5ž´¥}·ÓÖ”YÇĹWç\WŒón'´ÆÝñ^cÇôÜ«�n°î­ø{~?ÈuØÛ}6Óë©m×cgë²ã–ÊÖ”­m¾‹5xŸ)Ðsž+Æy·Ùã1ãúnSÆ·8•­Ø›m!×cm´Û<[®¥·]�Ÿ®ËŽ[+ZRµ¶ú.`Ònô¼›K¨äœso¬ä{�jð7zö“?k¦Ýév¸‘gêöú�¦ ³àìµ›,ã›"ïŠh¯¶û.­µ¥^•è�õLy“¶Îßù}å^÷g%³l·^H÷–Ûa-‘Ù ’çl{?”feÉdQÒØâ¶ù溴¶ÚRÛi_J}ŠÈ<¾Ùeî|³ò‡oŸ}²gm|‰î½–l–YlV_—�Ù\›+"ûc²”²;nšZÖ–Ò”¥)öQYo—[,½Ï•þOn3ï¶Lí¯�ÝӲ͒Ë-ŠÉ2ó»'’ådßlvR–GmÓKuim)JRŸeÚüŸþÒÏ�ïåÍå¯ë÷Ÿµ¹®ÍyhÈcß…SYt\OÍ�Ånжgò.…Ö[e-¯Ö¶íN·¶ò¯ºK½=+ôÝ[K)ëöV—7í–·á•Õ]óyìyJj©-­&¶í6³³s.¾Ký=.Šúo©K)û•¶ï楟øguwCļ¾Ýûâ­»GÒÚºYKkI­»O¬ìÌ»¯’ÿOK¢¾›ÊRÊ~åm»ù« ¿7�Ä,»&—`ÖnYä–Ÿƒßdx–Ù²¶>½ë7¾Šì¼ÿOvN ×vuôdž¿dRk©ÿ—j–ÒRID¦ ©³ÈÍ'ò½øÎ¾L™°8ïrö~‹ \Ëâ“.\MG7Þkñ¤Ê’qá¿&øqí­õ²;-­Õ­im´û)Wב:\^9äzñì2%ÂÐ÷gipå˺;ò¤ÅÕómÞ<™7Ã_‘|PR·ÖÛ,¶·V¾–ÒŸb°�!´Ø¼w¿ûË�áI<¸Z.áìÍ6$¹WG~T˜ºÎi»ÂÇ“&øb‚+ç¾()[ëm–[[½}-¥>Å5¿&¸öaü‘üƒbE[î�Íÿ+ñãºúÒ·ÖÈ;ãžÅeo­¶Ûmo­¶ý¾”¥=q†˜q‡šB™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀA~)e¯•>8ÁYd¬øý—,pÖû«rÍØÜ¦É¥²:×Ùl’ÙtºêSÖêYm+ù(†ÿâU–ZùãÄ5’JÃIm%Ž*ßub²Y¹ÞêÉd²:×Ûl’Û”º´§­Ô²ß_ÉD9ÿt²WÈß¡¬—ÖúOg-‘Vû«’KηVË%–zûm¾Ka²—V”õº–[ëù(‚¯ã=¾úíþ:#­÷V;5¾Wßluºµ²ÛäÊñÆÙ/¶ß_m.¾Ø­¥kO¶´¶ž¿’ˆÂ#n�ú ŽüCè-äŸ%¾k¯Ê»˜ýìßÒ[b¤Õ¾î)«Ûr‹1kedŽ”·6ý=!­þµ¬t“Ýéw§¶½ ø¡Ò[È>E|MÀ¿"ìZAÚîé%±ÒZÝwÑõ¶·ÙJ[—~ª‘Vï_[)»Ò¾ž•è7Å>–ÝÿÈ�‰ø7d]‹H;CuI-Š’Öë¸Ö—qȬÇöÖøý-Ë¿UH«w¯æRÿw¥Þž•î'á»Æ·+æ“Â论ëinGäÒ¶úzÖì?»¿.Ë~ß_;ø)Jÿ¡U›k–)-lCñQñZaö‡‡œßîqÙw!à]·Åk°¤õº\ªpÞC¶öáß�õ+H£À¯:­ößì·êW&êzÝìô¶#?‰—Ž[‹Ø>%rêbÇeûÎÛìq±þœµú¸z¹û§:o«K+ ŸB}¾=¾Û®¥×}O[iZ[umœÇà½ÿÜ’ÿÄïÿdÃÁJå9€`)¿´gÍå ØßÕÉÕÀüœ~Ð?.ÿ¿‡3þžWòiû@<¹þýüÏúyT?â6ý³Þjþút‡üšze ÍhÃˆà ›~¿îgæ/õõÔ?Ôr—�á�þåþV_½iý�r„º? '÷1ò«úüëOì”,jüÿ³·Èå¥ÌÿQ½–Né8%Œ�Çâ‚ÍÄ�Á.š×_“ ¹ù^ZðÌÜl;¤¶™â`tïyA›“U¯¾øq&ÙcÛ%Ô§¥·Me+üõñüH¹XÖxOÔ¸WO¹™Sq ¨1«}´žll>¥î˜rçŽ*×ß|XòçCm÷Rž–Ö[i_ç¨�·â?ÊÆ³Â®¦Á¾x­ÌÈò�ˆeA‹[í¤ócaõ?s×oº”ô¶²ÛJÿ=DSìÓèÿåÍÖŸ¨/&PNB• p Ý«†<�ž»k}ðÏ�‰ ¶zÝo¾9r#²û}ÖVÛ­÷[uiëJÒ´~ÝlQälp –ß|SfâÅ-¾·[îŽIã²û}ÖÖÛ­÷[uiëJÒ´~Ýd1älµøó[ïŠ|ìHe³Öë}Ñˇº¸4ä]wÉsë­ûϧô/Ò¿ÁzKìõþ‰÷/_ôˆâ~%-=³øÃÐ[ÿ¸RKµ�ó.ž›O£K®Ã·y×ܯ6ì Oéë6UãÔ’¶zþÝ)_ôˆäþ$½='ñ— ·ÿp¤•Öw´úzm>—­pé¼ëþQ›\ Oéë6UãÔ’¶§û§¯úTRÿ–Wã[£e­–VK<äë{,’¶Ò·Ûd� ä­ÒYmþžël¾è­­iO²µ¶ž¿’ˆB¡¤†ê¸0¢â µ³‹íq­Šü�g"Òì ²{oºæÂÙcdÅlÖÇ|wÝ×ÇJ]Kn¶µ§ä­?+Ðñ”ú^YÆ7¶C&N§‘ivXöd[}Ð_>Ë*&¶9"’èn’*Rê[uµ­=}+Jý¯EÄ6SéùgÛâÙ™:®E¤ÙcÙ=·Ýóàì±²¢²kc¾).Šé"¥.¥·[ZÓòV•û['áŸ*ØpO0ŒåZÌ}”sͯŸaÇ»C‹íð¡Ï‹#&\)rpí¶[c–+î²µ¥·Û_K©n²Ôu¦Ë§@üüJ_³› ùBõ�õ�¸kø„ÿgçýœ:ÛúG•¸uøƒgÿý›úßúG•¸?ø—c–¾ž=Ê[¨P A…ÅUà ÿâñ¦ñ«ûÿôßëŽ3×ŠßÆÇïõÓÿ¬.:Îþ-¿ÿ¿¿Q~°8óy¾0?igÇ�òæñ+õûÀ¼¬îY¹(ïÄùã´üÇÇn’òSK�&FwJóÝ�åòâ`Òûâá=¯�…÷ ÆÛamÔ¾=~“šñ\{/¥Öýã_mm­×{£�ø�ú~_ãïOù ¨Á—#7¦ùÖwå2bâÙ_¡Ã;G Ÿ¥–×Ýõ=kwÙKe™øg6²MżÁÒWîÿK_È:Okg·Ý÷¯©¸×v†$¿[ú%mû¿·EgÓô²Úû½þµ»ì¥¶þ.{M�Š>^uÞ´·ÝÃü…ãö¸POK¹=;#­õœzÜ­®7ß/¬Z\ÊõUlÀ¿îñ}Ià̧ԗÙí†[©J¥˜˜¦!·Ñr®þïO<˜�rNàìÍô8²ßl’ãE¸æ›½„xòÉe)eòCfE-º´¥)ZÓÖŠ½¼„ÝÅÉ{ó¼90I‹ÿ·û/w4·[|¸ñmyžë:8$¾ÊRÛäŠÙémkO²µ¢°O wQòNùîÞE ãC¾íÞÉÝE�-Öß&<{NeºÎ² /¶”¶ûâ¶z[ZÒž•­ÖüšdY™òGò —.¶<¯7¼¯ÈŽÛéJ_m“÷Ç=–Ú_Kku´º–ÝöúV´õaö b&�€&uøW"–�]æõŠJA'>êX£š¶]H¤–=Í/š+$­=—I“Ç[­¥}m¥öÖ¿–‰zþ襧UùS5c’�ÉØ=qrÖË©òÃÇ9òÇd•§¶é"¶{+u)_[i}¾¿–‰uþX¤§VyQ5c¾�ÉÏúâ+%­—R;䋎òK¥ŽËý=·_³Y[©JúÛKíõü´XÙø>,¾Ÿ]ý%lº‘ßæ§6²Ù+mie×ÇÑž?]%–Ýéí­Ö[-µ­)öÒ—S×òÑ+äœÒmKOÅ#�‘o”Þ8eÝÖâOиÐå]ôÇ›#±y4¹PE5múrM�d7Im+[¬¶[+ZR—[ëÄ­�‘o‘~;e]ÖâÍÒ»lxrnŠúcË‘�ζÒdÁÕ·éÉ65ò%âfÆXlžÜžÕÂãô²L›qm¶NY©ÛqX¦¤·E5.¿]Ͳ[¥+5ÖÒ:]mn÷S _›øøßÈw‰Û!²{r;SAK/É·Ûdåz­¯ŠjKtRÒëñåÜRKcô¥fºÚGK­­Þêv÷ðáì Ö|ÐøY“‘|QÇ.zëíºi¬‚ÊÏ·ñ�ºuX¶Rûÿ6édÉͲØìü²_ZYO¶ê,àXƱYk˜ÿŠ«�S'—øQÅ~Œv×MÆûã�W"“û¥–œ—gÔúêC~7²•Š8+Äëu·û«õ+%Ôô§³Ö±/üM{šOÉ|9ãßJÊWW¢ï-Íg¤Þé$¦û?ª°iÐ{)X솼rµ¶ÿu}õ¾´ô§³í‰·âdÝR~MáçúVRº½wî«5&÷I%7û¬Á¤WÁí¥c²ñºÖÛýÕ÷Öû©éOgÛÆ…ÿ¸ÚÿŽ'þ+(“¢Î‹‚ à ~~¿û¼ÿâ»ÿŒRVŸ†3þíÏø¶Ûõ*ÏÃ'ÿv×ü[¿íöœÇà½ÿÜ’ÿÄïÿd¿´J±9€?oí�ûÀuõSš ×ø†ÿ�ö÷…ëŸêß7AÓñ ÌOï×?Õžj®ñ�~ÒÎ�þC=iúýòeÀ' Ü2E4+ϹýÕ<Áþ÷ýQý‘ó  ×÷CòÓúÌê�êç6IãðÑtO,¬¾«þ®s4æ?ïþä—þ'øÓ&x—:\ Ì+ùMý£>hÿ(^Æþ®N®äãö�ùwýü9Ÿôò¸ï“OÚåÏ÷ïæÓÊ¡ÿ·ížóW÷Ó¤?äÓÓ-h«FGLÛð­ÿs?1¯®¡þ s”¼ ï÷/ò³úýëOì{”%Ñøi?¹�•_×çZcü¡cWà÷ý�¾@-.gú�è$°’wIÁ,`o?>=ÞôîUÐCvT>\ðŒxrnŠÊäE�“Ó}ï&LM[~¤päI‰×ÛJÒÛîŠÊÖ•­´ô�§âE†¼#ê|‹¢Ž³ÅåO †9ëeµš8gêNí¾x¬’´÷Ù×ãÇ[í¥}.¬vÖ¿ÎÓÒ?_ˆúnð—ª2.Š*äEå? †)뵚8gê^ë¾x£–´÷Ù×ãG[í¥}.¬vÖ¿ÎÓÒ)¿Œ öiôòæëOÔ“(%!D…b¸íÖÏ.Ç&__¥�›‹<žÚzÝôáž9/öÓìõ¯¶ÚúQúð'³? &OwÓÇËÆžOm=nöE5’]í¥kJVïm¿gÚýšùìÅØ`äËîúxù˜ÓÉí§­ÞÈf²Ký´­iJÝí·ìdž™åZ® ÜSÍ·¿y¦“‡vOå[ŠáÃLŒºj¸÷(ÕíöuÇ­ñÒ|Ÿºb_ì³ÝoºïJzÓ×ÕoZÕÕ©‹¹À{üR{¨`ñ‹Æ®;vÂø§Ú÷Æ×uª’ËK3aÐuö÷m…ÐSú—ë/ä±ÇKîüë)—Z[ö]r7ÿ‰Oo>4xý¡®uÑϲïL½¼zß©%-Ë‹IÀ9ÙÕ†ŸÐ¯»_~þ8éuß�gÞ«K~Ë®Gñ&í¢‡ÆÏ´Uκ9ö]ã›¶�[I$¥™qix!Û:è©ýûµ÷ï쎗]ùÖÓ*´·ìºäR¿–Sã[£b­öÒKüãëy,Ž·Sßu‘ô'’¶É}¶úû«m—Km+_ÉJÝOæÑ´5ÐâWô|;]&ß—q]L2Y»NG£×E,žêÇ™»<\k$’–Ò·VË.–•¯¥=}(ôœ7Y&ï—ñ]4RÙ »nI£ÖE4”ºèâ“?g‹‹d²[oçVÈî–•­)öúQé8n¶MÏ/âºx¤²v¼“G­Ži)uÑÅ&vÓÉ/¥¿�[,ºZV´§ÛéFÍxSÄçç¾ex•Áqs"×dóO&º‰ãì'Šù ÁŸ‘ö§ÓÅ™41Ýd’Å�&e/ºÛkJÝK})_Uº«Qš.–Góñ)~Îl/å Ö?Ô>vá¯âýŸŸöpëoéVá×â ýŸÿöoëéVàÿâ]ýŒ>Yþúxõÿ)n¡@)W€3ÿ‰ÿƛƯïÿÓ¬^8Ï^+ÿ?¿×Oþ°¸ë;øµüfüsþþýEúÀãÍæøÀý¥Ÿ?˛į×ïZò³¹g ä ¶y‰ãÞ§ÊÏ»ËÇ­½¸t·´z÷y¡Òåì+50µÆ¬Ûð>C‘H-º[£ã|×[¯Ø{imÞêãzzV•ô®¾ù[Ñ:Ÿ&üpîn†Ü[‡Hû+�î´Z¬¼ûd¿QÊ£ŠÝŸ äY.’ïàß/ÀÁ϶”¥k[±©öWò5ûÊΊÕy3ã�rôFÞÜJGÙ<s£ÕegRJâjyTQ[³á{ù­ŠÛ¤ºœw—à`çÒ”¥}nÇ¥=+ù'Éþ€áÞUøëݾ7sïH¸Ÿvõ—0ë�¦ÆÍn³k›Ç¯äú\½v·–ép÷Ùzßá'ÚË×W,‘Ý÷}Ž[éu–Ö•Wo4›~3»Üq½þ»/Q¾ãû]†“w©ØA&.~¯oªË›e®ÍÆ–ÛeÇËÂÌǾ),º”ºËí­+OZ+-Þi6Üku¸ã›ý~^£{ Úl4›­N|7cçk6Ú¬¹°vZüÈ/¥/ƒ/ 2 ã’Êý¶ßmi_Ȭów¥Ûq½ÖߎïµùZ�æƒi°ÒîµYÑ]n³mªË—c¯Ì‚úRør°ó ¾9,¯ÛmöÖ•RóÙyÌ:�±9ïTvš^;Ϻǚrž¼ç~y±²'Ñs¼Îã|—M6FÙ“ˬÝkg‚ëâ’øî­ž¶Ý[kJ×å¾[å¼X£¿>kÏà�™}Û[Iòµ‡#¶n´îœ zRû§ë^W—ƒ÷íÄqS;"\¾¼ÁÁÞÅ=¶deþ�»’GfL•tãÌY¼'ò׃v~Ó#"�kÉ-“®ûƒVúÉ×Ü›/ ¹[¨â·:yr¸fë upYnFU¸â[}–äßW@þ2¼Â›ÂŸ,¸7glò2)ÖÜŽÙ:ó·ðá¥o¬�Érð¾õ¹Ž+qs'—+†îpð÷1Ç –Ï•L ñm¾Ër/«´_¿"ŸóuùëÁ¹W2ÞI¬ñó»#ÇéŽÿ�'+.Ý>—�rž,œ_´21-ØcëcËêÎ[f>vFl˜ù™PñÉ·رý\ßUš89Ø;L=ž³3c­Øâãçkö9åàç`åÃfF&f^=òA•‹•–ß–]u—Ùu+JÖ•¥V-beâl11sð2±ópsqáËÂÍÄš<œL¼L˜í›+"¯‡#"é}—ÙZÛ}µ¥iZÒ«pó16˜¹øXÙØØÐfafáÏN&f&LVÍ�•‹“ ×Ñ�‘ ößeö][o¶´­+ZUlsõ?Cô ¹þKôsñï�¿602#¤Rdy=Ý;Ëm§×ô¬Ÿžîù&,Ÿê‹#“Ölmµ—×ÒžÏ[¿2µ³ÛZÖËò-¢Èã¾zy…¯É²ØäÈò3¶÷¶ÛoÖô®?(æ{nM‰}~ñr{¥ÅÛÙu})ìõ­}•ºÏmÕ­ËäOG?óÇÌ ‹-ŽL�"ûgym¶ÖjÒ°r~e¶ä˜·×ëÙþéq¶Ö]_J{=k_ekg¶µ¨¿ç;«áê–ÿ;xœظ¶m»³/´.‹+g™ Ów‡ãÝÓ‘“|Ûkmʳ+2~tÓÅe>íò_5k�dU®�´Å¦ŽO€%!øZ; '[äo“ÝQmÕ¦'4é>7ØSÙëOmÙ=cΰøÞ-Õ§¯­kd]¹5)öWò×ò~ì•¿ 7=—]ß~Iu}+wÑæCƹì”û=µ—­ùœ\z*×íõ÷RÎÕ¿ÓìýÚÿäÉ?ðÖóÉuÝõä‡XÒë¾�/ê9Ï$·ý-eëŽg†êý¾¾ëlí;ý>ÊþZþOÝš×àÎìŽ1«í=:‡/.–s>sÀz²48¶zäñŽªä=¡Æ9v]-­Ô’´ÂÚ÷&’ÊÖ”­´ûÇÛZV´õ›BbI„§¬ñ]•ε]×<ÿ³9kM]ðžUηu¥ÿN´Ôq~ÿeZIì“ÙZaëïû}·z~_JþG’çÜËU×\šöúÿ§£à¼K‘ó-Ì•»ÙK5\cO™»Øß[ý·û=¸x7××Û_OæUä¹÷1Õuß潃½»Ù¤à¼K‘óÅþÿ§ìÕq�>fïaw¿Û³Û‰ƒ}}}+éüÊ¿Ïe¯Ókv�¶f6»UªÁËÙl¶’Ù&¿ 2³32§’´Žl\h®¾ûî­)m¶ÖµTƒ<ÒdÏ6DÕ÷K‘,“Ku)Kit’ßY/­-¶”¶ß[®¯ÙO²Š²&šL‰¥ž[½ÒÏ%óKw¥-÷I%Õ¾û½¶Ò–ÓÖë«öR”¢­™æ“"i²&»Ý,òÉ4·zRßt’ß[ï»Ûm)m¾·]_²”¥(£ë”ò]Ç3äü�˜r›37ü¯}·ä»Ì»1àų+q½Ødm6y6bâÇ65“æå_u#ŽËc²•ô¶”¥)Gò7ò|é¿ ç�ÂNëäõ¾o¼ï¼¡ä:¯£uÑW˜\w«:¦|iâ¶Û~­“K•¿È²O}Þ•¶+=´§Û[¦£ønt–bøkÛÛúÝ/×Üù+ȵÿN·GX>é¤ë.­– £¶Û~­²É“¸žÛýÕô­#·Ò”ûktÑÿŽÌO;w�Vé~¾çÉ.A¯úuº*Á÷MZu|�M¶ÓêÛ,™;‰í¿Ý_JÒ;})O¶·Y3øD¸æçIñ‰Ù[=¦Ýpy‡™}¥ÈøäÿxÅ›ôŽ›©ºˆÏ™ôñç–lOg!âÙøÿN{b–¿CêRÚÅ|wß%$…ÒJ\¿ÅYÇí‡�xIʬ’kïÙiûû�Ïm·îð[¤Íê Ž$–ßOϬÙuäÒêW쥰ÓÓ÷Q8üMš[bÜømÈm¾[®ÏÖw¶–hëm¿Bu9]I��}·Òžï«“]ÔÔ­+_OH©éû¨ ~&]-"ÜøsÈí¾[®ÏÕ÷¦–hëm¿BjrúŸ;ûo§ç}\šî¦¥i_³Ò*z~ê_ŒóMµŸOñÓÈaÀÉ“I«Ùù]¦Øl펵ÄÄÚïq|sÍÓàO/ä³'c‰Ç3¤ŠßôÖâÉ_ô¨�¢¼‹2 € Œðû°uýM埌=¡·­´Óõç�}7Íw>é) ·iøÏañÝÆÚ˦úU‚Ù5Ø’[[é—Yë­)FÁx—Î0:ÏÊ;k}‘êx/}u-Ûß|”ŠÊj8ÿ?Ðm6”¾jÑôl»JVúG}l¥}iJÖ”l‰Üã¬ü¤ñ»±6—Ûf«ƒwÇRr͵×ÉH¬ý çº žÒ—ÍXr>�·`cIJßHï­”¯­)ZÒ�ôø³æ¹={òUàG+ÇÜãh Ãò÷ǽvço™fؘœ[‘v—ã|¿ïWl!Ÿ~-¶ÌŠùý-“Ûë,wÙ%–ßm­‹9–l.<@ïñ7óŒCç‡]q ~tYUëïø†ë; ºýo!ä|ã°ù˜“û-¬öK/ÌÕäRÙ.þrkkm´¥ÕºøMþ#~g‹½óW®øžlY6ðo8¼Lhë×ë·Ü‹šóÝÔØÓû-úöK7“Y=-’ïOd¶Ým¶û«uð¤üFÏ{æŸ^ñ<,ȲmàÞ?ñŒ}¦Oç|RÛæ–—[L(ë—Éâ¶’Ö—R·Ýl~ž²zÒ=Š#€AÌ~ûŸ‘K&=’uGeôO?ŶmtY²K>dz´][tx™2O ú|ŠcvT—W&;e¾è­¾m-žë쀢 è=*æZïâo:Êí[ä–µ¦NuÔ¯¾êßëüõ}}V|xÉÍçìϼ}ì|«©v_?驿™u¥o»ýUÊ8&‡w“JÖ[ä—Ýlù×R¾ë«w¯å¯¯ªÎŸy´ý•ãw�Ý‹•u/Êç½%Õ\Ë*ê]}ßê®MÁt[¬šV²]|žêO›u+î­nõüµõ]ÁÒ©¡ï^–êî⵺î1Ü}_À;S�Ý}’Å}ÚÂâzž]§­ñMm“Guuûxým¾”º•û+JU°,àÍì ‡Wâ§çxƒÃ>²ÆÉŸô¦£MÝïu‡ë[q©�Èózë�ñŒš[lÞ’Ï\Ž-·¶µ¾:};iOeÕ÷ßJDãñ3óLI·>"õæ>Tß~×k;‡šn0½×[�÷MÖW_høÞW²Ù½²MI´[=n�ÖË�»óï¢(‰ƒšbM¸ñ®ñògûö»YÛüÓo‡îºÜºn²¸�Ždûm›Û,ô›Cµ³Öèýl·ùÛ¿>ú!9øÌ{y®ëŸ:Ÿ&”ã|¿š÷ÿaîðé•™moÞuÆ‹ª¸×É¿ <«p%¤x©¸¶Ùf†ù£÷]l7Ùmó[$G^E™ ÆøwÄ#ì.<[àsE�4ÓÈ®“â™fR[°ïÇä•Ƶ9̤ºº}»¾¯²žêGëèØOøÄ\ÛÊÏxlñÁ6?+ò ¦xÞDYT’ì[ñ÷}�Ç5³Û•HiY~íX²kõ=¿�ìõôl‰|b>måO�6hàšWäMñ¹ãɤ·c_�»ìN9­ž™4†•—îßK&¿SÛùÔ³×ѽ¿:ü�§ÉwǶ. û«æ×‹93âããI—wÜ0;¿„gìò%†;$¯Üðµ¸ÒÍ=õ§²8#¾ûëKm­ikZÎEšË�ÀüüJ_³› ùBõ�õ�¸kø„ÿgçýœ:ÛúG•¸uøƒgÿý›úßúG•¸?ø—c–¾ž=Ê[¨P A…ÅUà ÿâñ¦ñ«ûÿôßëŽ3×ŠßÆÇïõÓÿ¬.:Îþ-¿ÿ¿¿Q~°8óy¾0?igÇ�òæñ+õûÀ¼¬îY¹(_âñ"ï|æÛöŸÔß…×>Sk²;SW<™k±»#"néìÌžyé�´ÊßÍ�Ȳ«o²Øÿ„vGm”¶ÚzÁ'ç‡ÅŸíæ†Ë³4ºáp&5Ù—¯’ [à×ãö&$ðkûWW÷Í5s69{™ñyUÿ™Koä6ÙKiKh‚§Îß‹ÚÌÝ—eèu—að/%µÙ•�,8²C¯Çìfqœ¾O²ƒ]«¼zæ='íbÔêpq>ë*Óm8ÿ Ÿ7*érvÛÝ®Úk¯ºë/­8HâkŠHº€'7øw>D¡îÞš»ÃÒßÛ'ltF–Éz«/i—‘&o7éL{ìÇÆÓA6\ùyÚõDòÇ�l6V[ —Ø"ºÜ<¹mšOÀžÐ÷/P]â7dnéhtvš;úÛ+a‘%Ù\Ǧà¾Ì\=\Ï<ßxÙõ|ÒǯúVÒmÒK�H£¿îÙR[3ï�_=!î>¢»ÄžÈÝÛgôŽž;úÛ+a‘%Ù|ǧqï³U™M÷�ŸXÍ$xJߥJieÁ¤Qß÷l©-²¿ðÅü ãùyâÍž!v†Þ;¼„ñ‰è4šIó24xù]“ã¶-ÑqÎ ÈuºüYñ6™».©¤xœg}5pk�ˤʟ3';i‘lR]H}!¤ @ÌüúpyxgÊ?�và]�®ç:¾©ç¯ZMôó"Îê¾!¥ÜgÅ|Þ¿RÜžS¡Ø{«mke²RëiéíöÒ9<3#ˆü”w†mØs×ó�GWó=GÙ--ÊÇÉëN- ÚfYtž¾ÿ¯É¸öº¶Ö¶ÒúV”ôôô¤þpødÜK䓼3+‡\<m©ëgªüÙifT9]kÅô[LË.—×ßIù.ƒ>·VÚÖÚ_K­§§§¥*þüS�CŸÖ¿.}‹Ìó3£ËÇò §:C·µPYOÎÖ`ix�:L¿6ßY$ÚtŽNM>Û¿3"ß·÷)Æ×#œ“G@×?ƒäÆé¯“OgÚí%ÕñþÌËå7¹¬T­ß¤2{�lµ¼'W%”¾;n‹3³!ÑÒµ¯­,ô÷R•­´£ª¿ ¹Qü‹tL›-�uzÈ—“uæêzÖ™Óó®=�‹Ãµ—SêGmi�Ùº[~ß_JúV”­iGT>{j¥ùè©v;;µš.Æ›’õ.âêzÖ™Óóž=Ÿ‰Ãõ—[õ#¶ës{MoÛëé_JÒ•­(��á„ïÚô�ËgTq̬Ž?�¡ò3®»O ·ûMþe˜4Á¦~ŠÕâXúi¥’(dä�²:ŸI§Å†êÖ¹lktº[ã¥l…X&°!hø—?4Ñ^�øÓò‹y‰�ƒ‰ºç*›Òcæ{n“gomð8#Ä×ÙwóÛ^ ºÚæÇZz]¸—IO¶Ê9·òëە鿎ÿ%÷xù8�íy‡ �©µ8ùU³ßŸwlmp8ò +/­=ù¸|Gy±Ì¶´üë-źú}¶¹½òãÛŸÚsãÏÉMÞ>V$naÃ#ê}F>Ulú› û[iƒÁw�`Ù}iïÎÄâ;­Žeµ§çGn-×ÓíµÌš^ïÅñïâ«ÎŽÄÉ‹k$¹½ Éú«S&“#î›=#ïɰ:'Œnñ²i>4˜öqîCØøÙòßô–Èq¯º:Vú[JÖš®í^B à‰‡Ç®`àŸ}Eº¶—ٛڼ˵ûgñ};£žœëmÀuõ¥}kYlÉÐp<9­»ìüÙiOÜõ¬ù~ ø\/ã{©v´ŽH³{#”ö=ØÅ$_Nëe¯:ÜpÍ|”¯­k,y:‡5—}ž¶ÉJ~JzÖ{?œ.ñÅÔû_eñfv?)ìîy°ŠH¾�ÖKw8ÛðÝ}ô¯­k-™:.‰5·}ž¶ÉJ~Jz­‚ü;=}ºë�‡ õ|�E~ƒuÈt=¡Ø3A-–Y>ÇKØ}ÝÙ\Ç„oe­•¯Ô³w×û�VD7Wíû½ñÒ¾žžŽØ;ëûµÀÁ~)½‡kâçŽ=§îõÊáñ±à¶ÅJGZý˲¸÷y•‘Z×éidY]]�e}³Yo¬´õ²JûnŽ8_‰GƒA²ñ› »#ÖµËâã•Ãc²–ÇZÓ °8'!ÜeÍ[«�t¶ÙfO\ã[öKeµ¬”÷Y}}µŽ9‰+ƒC²ñ£ û××+ˆw~W²Ê[ekL.ÁwûŒ©«uqî–Û,Éë¬{+é-–Ö²SÖÛëí­‘Pü_JmÏÆïNrŒ=EùÙ7Ì>.Çg4R_¤ã›þ¤îÝN\ÓåV:Ï‹��Èë©‚ê[},’{¡¥Ôº´²¶Â D7•¿?í+[kK­­mºÚÒ¶ÝJÖ•¥i_ZV•§ÛJÒ¥+ZV•¥kJÒ¾´­>ÊÒ´ü•¥šÿ´­iZV•­+JúÒ´û+JÓòV•ýÊÑý`ž|YáÉÆš\|œycŸ" /†x'†úIÐËm’)b’Ú]mÖÖ•¶´õ§Úµ«Äç³È�=�'+ÕYuŸe²é¹-2ñ%³òÇ,[_¶‹74Ödaæiº·¬õÛ<)c­Ñß‹ºÇâtͶ¶ý—}ã×òÖª—>~»óÈo–ï1ù¯'ë¾…ÐzM ºÊ_§Èèn;¨êÞm�¨†)ò"ÇãûNÎã[ݦ%)u.–=‡Õ¾Û$’ûiÏF„´5Ç ¿? _ýÞñ]ÿÆ)+OÃÿvçü[?íú•gá“ÿ»kþ-ßöûNcð^ÿîIâwÿ�2_‰Z%XœÀŠs€Ë®ï?»F¶V�ó§ç\É?¡z]/[ó E%Ÿ›“tþ¶ÙÚ¶Wó¡ŽÏ·óo’¾ûcˆâ^áÀî/»#ÙéNWÖœë„}OH½n¯_ò�^ûÙZÒ{¦þ‡ý³=:+-üïͺúû©dB?¿ »¸¼aìe)g*ëNsÂi'¤^·]À9N³{u•­'ºoK)ÙT­=ÑYoçWÛuõ÷RÈ'þ3®7­Çßüxòø0l·q·ÓùEÆö{;bþ‰>·ŽæôÏEƒ4Ôû+f.W(ØÉµü•šJÓ÷Q[FYäÀtøkð7¤þC<Šì^™îÎGÙücSÅúWoÙÚ\þ­ÞñMÚmŽŸœð>+>&ÆþYÂ9¾FºüngïôŠ(%¶XíüúÛZÑÕ/‰ zoÏ>üçýEÜ{þÉãšî7Ô>ÇÒçu¦ëŒi¶2æêyŸ ã8›¹G æX¹RAËé})pIl‘Óó®¥kJu;âWÂ~œóþ¹ÿQ÷ÿ²8æ»�õϱ´ÙÝkºã:m”¹ºžeÂøÎF.ÆîQÃ9–.F ðrê_JG[%”üë©ZÒ�îø øÐñC庻ߥ<�çáÃ9êí'iuçö˜å<ŽK²Ó`òÌn'Ï„QsŽ®ìxóãÄÍ嚺Ý�~ a¬²Òûg¤–Ö #ÿšÿà/ûîùþߺcü@$þm烿ï©åoûxêñ�Où¸^¾Ÿ•í㨿ÄjRæ~üiÿ¿‡œßá/ ¿ö™�ó_üÿ}ß/ÿÛ÷LˆüÛÏßSÊßöñÔ?â0ÿ7 Â÷Óò¯ý¼uø�?ÌýøÓÿ9¿Â_Aí3æ¿ø þû¾_ÿ·î˜ÿù·žÿ¾§•¿íã¨Äaþn„ï§å_ûxê/ñ™ûñ§þþs„¾‚ÿÚf?Íðý÷|¿ÿoÝ1þ óo<ÿ}O+ÛÇPÿˆÃüÜ/?ßOÊ¿öñÔ_â4ÿ3÷ãOýü<æÿ }ÿ´Ìšÿà/ûîùþߺcü@æÞx;þúžVÿ·Ž¡ÿ‡ù¸^¾Ÿ•í㨿ÄiþgïÆŸûøyÍþú ÿi˜ÿ5ÿÀ_÷Ýòÿý¿tÇø€?ͼðwýõ<­ÿoCþ#óp¼ ÿ}?*ÿÛÇQˆÓüÏß�?÷ðó›ü%ôþÓ1þkÿ€¿ï»åÿû~é�ñ›yàïûêy[þÞ:‡üFæáxAþú~Uÿ·Ž¢ÿ§ùŸ¿ïáç7øKè/ý¦cü×ÿßwËÿöýÓâÿ6óÁß÷Ôò·ý¼uøŒ?ÍÂðƒýôü«ÿoEþ#Oó?~4ÿßÃÎoð—Ð_ûLÇù¯þÿ¾ï—ÿíû¦?Äþm烿ï©åoûxêñ›…áûéùWþÞ:‹üFŸæ~üiÿ¿‡œßá/ ¿ö™�ó_üÿ}ß/ÿÛ÷LˆüÛÏßSÊßöñÔ?â0ÿ7 Â÷Óò¯ý¼uø�?ÌýøÓÿ9¿Â_Aí3æ¿ø þû¾_ÿ·î˜ÿù·žÿ¾§•¿íã¨Äaþn„ï§å_ûxê/ñ™ûñ§þþs„¾‚ÿÚf?Íðý÷|¿ÿoÝ1þ óo<ÿ}O+ÛÇPÿˆÃüÜ/?ßOÊ¿öñÔ_â4ÿ3÷ãOýü<æÿ }ÿ´Ìšÿà/ûîùþߺcü@æÞx;þúžVÿ·Ž¡ÿ‡ù¸^¾Ÿ•í㨿ÄiþgïÆŸûøyÍþú ÿi˜ÿ5ÿÀ_÷Ýòÿý¿tÇø€?ͼðwýõ<­ÿoCþ#óp¼ ÿ}?*ÿÛÇQˆÓüÏß�?÷ðó›ü%ôþÓ1þkÿ€¿ï»åÿû~é�ñ›yàïûêy[þÞ:‡üFæáxAþú~Uÿ·Ž¢ÿ§ùŸ¿ïáç7øKè/ý¦cü×ÿßwËÿöýÓâÿ6óÁß÷Ôò·ý¼uøŒ?ÍÂðƒýôü«ÿoEþ#Oó?~4ÿßÃÎoð—Ð_ûLÇù¯þÿ¾ï—ÿíû¦?Äþm烿ï©åoûxêñ›…áûéùWþÞ:‹üFŸæ~üiÿ¿‡œßá/ ¿ö™�ó_üÿ}ß/ÿÛ÷LˆüÛÏßSÊßöñÔ?â0ÿ7 Â÷Óò¯ý¼uø�?ÌýøÓÿ9¿Â_Aí3æ¿ø þû¾_ÿ·î˜ÿù·žÿ¾§•¿íã¨Äaþn„ï§å_ûxê/ñ™ûñ§þþs„¾‚ÿÚf?Íðý÷|¿ÿoÝ1þ óo<ÿ}O+ÛÇPÿˆÃüÜ/?ßOÊ¿öñÔ_â4ÿ3÷ãOýü<æÿ }ÿ´Ìšÿà/ûîùþߺcü@æÞx;þúžVÿ·Ž¡ÿ‡ù¸^¾Ÿ•í㨿ÄiþgïÆŸûøyÍþú ÿi™Ò/�ÏŒ.�øÞ�¶#èîWÛœ¦½Ç¿”KÚÛ�o/·I޼G€ðkq¬›øm™\�¼S&·ÖØý•�Ûw¿ Þ üptoÇÍ�¡gLr~Ôäµí«ø]Ü–NÎÞq-Íø”à”åtÓY¦þ ðŽf5’ÿ rë‘õé‘[ël~ÚÙí»ÝÐ?þ8ú;ãò>ϳ¦¹7iòZöÍü2þI'fï8žæLJp[yU4Öi¿‚Ü#†Y�d¿Ã ºÏõé‘[ëHýµ³Ûw»­ŸŸþ2|SéûSã·$î[7vìøfÇ™îû�‘pÎC¸¶.‹É1¸Ö¯S' ëþ½ÀÆÖaÝ˶2ÖÙ±çšérn¯Ô¥´¥´èÛ -þuXÊoíóGùBö7õrup?'´Ë¿ïáÌÿ§•Ç|š~Ð.¿3þžUø�¿l÷š¿¾�!ÿ&ž™h3EZ0â8fß…oû™ù‹ý}uõœ¥ãøg¹•Ÿ×ïZcÜ¡.�ÃIýÌ|ªþ¿:Óûå ¿¿ìíòùis?ÔoA%„“ºN c9WókÁs»âïËF³_úCa¦â¼C�EKa²Y°ðzó³8W7änºÛ®ƒîœgC›t·ÛéwÐú”õô­\Ðùˆá¼ûãoÊ=F·ïyºž)Æy½”¶+$— ¯{ˆó�æ\U¾•¬_C�è2ë%Öú]ôk}?%jæŸÌ' ÌçŸÞPjuØ|ÍÔñ^3Íl¥±Y,¸Ø}Øž@ÂâGÄmÆvßK²äÝ[ÄvÞ?ò| <´¾þ9ý¤ùÏ‚ðMFæu×b�[Ç6RÛw¥o�a´§¶K]žuÍÖ÷g€è~!Þå�µ~Iù·ÃÈÁËÖt_]ußQaäëæ²xf˦¿/²·ödɲÇ]–¯‘v6^¿"ßͺ+ð¾•öÒèê�ÇÏwmEÙ!|·ŽâK‹>Kuçê¼|Œ)¬žŒªáfv6çêß²Ç÷í~ë°ò0'·ÒÛ£“éÝOu•A矶cì¿�~[DZ&ÄÈ×ô¿_p­ÇÈšɡ›.¸Y�‰º¤·Ç,‘ýÿ_»ì Œéù·G&Óºžë*¬ÿñcw†»³~Q1:ËQ—º¬>8ø÷Ö=uÈõÙÕ¾ÍD\ã™dò.êÍÚh¡ú—E}› Ùœv ™ém·ß> c»Ö�Ú᳋Î1£é¿Ã_]S´>MüCã×Ý‘iû+±d’_²;kÔÜO‘ö~=¾¿wÉþ‰>_Ž(ÿ6ž²_m=ñÿåËz;ñÀãìo‘ÏtS{)«žçó˯’Ÿ™d�cÄycbz×èdR—ɛŢ²ÏͧôK­§ºÏçíèÏÄ�³±~F¹ßâò5¹ÅºêÛfD6Éf»c�K­�e¢Þkg›c…/¬ØcËmÑÉ}µÉ½5Ûý�Ð]¥Â;“«wÙožõö÷}Ƕ°VúÙI¢¶L|Ív oŽÝ†“w­È› a‰}~ŽfD°IJÇ%Ô®KéÎÞçý Ú'¸º»}‘ÆùçÞcï¸ö×·VÊM²cækööße» .ë]<ØYø’V°æadK”¬r]Jì7ŠPvdžCõ_“�!³ÁÖvORrK7úK7¹Üw‡‘‰•¨ä|K“àaæë3³x·1ã[½^Ê,l¬L«°²äúóý9£³#ãÿÎn±óûÇ®;Ý\ë5ø~—í.½Ÿ*Ü�·]v.,2ítsÉíŽí†“6Ù)—©ØRË-Î×Ëe×ÙM™ÐX­à×™½sç/Bñîã൳U¹·éèû#‚O•fVÏ€s¼LheÚè²&¥‘W?S‘ImÊÖgRÈé›�,wß3Ò|xlJðs̾¹ó‹¡øÿpðjÙªÜÙôô�‘Á&ʳ'gÀ9Î.4Rm4yÒØ®ÎÔäÒJdë3©dtÍÀ–;î²é><6ì|~ùÉÔ_">-õç“�A—‹'ÅýÎøE6ðn7}QÙúœL)9�Yòy¢Æ×ÏM¦‚|è¦ÆžlL:í5x[(¡·6 Ý»­Ånt€7ØÝ}Åy7 á\£œñ9Ì;mÎ?_q]÷$Óê99É㸸ÙÛün¦Øfcìy.N— 2)²£ÃŽkñá’Û略×Õä÷|÷ƒq®Eĸ‡#æ|WAË9ô»|~ Æw\ƒS«ßóL�$9ûÌ~)§Î˃aÈr4Ø91Ï•f$s]7Òûémµõy=×=àÜk‘q>#ȹ—Ðr¾{.Ûƒq­× Ôê÷üÏ#C‹vóŠê3²àÏä9|,˜æÊ³9¯‚é}ô¶Úú¾Ë•q}6ç�ñÍÇ$Ðjy2ÉÙañ Ëq®ÁÜò¬½6¯#w¸Åãz¼¬ˆ³w™:�.$¹y6bÙ-ÐbÅ|·ÒØí­ÔöOXõ�¼…oâ˜êIµ]áâçzÇ5²bóž¬å½S•�Òì,®­å–òÜLœœ�m"º»x;v[!·Ö·ú`Iëöz"ø–º²MoqxÕÝqÉY!æ}mÊúÃ.!“Û…?[r{9V ÓÏéô~¦Ú.ÔžØíõ÷Ö˜7þå(ˆGâSêÙ5½ÃãgtÇ}d‡™u¿*ë¸l†Onýoɬå8SdOíú^ý¬]¥=±[ëï­0¯ýÊQoÆMÐÑcr¿ <žÕq½”“n¸÷gô7=åö[}ú|8¸ÆËEØ]IÆòn÷}<}–Êþ]Ͳ ¥)îš,I}kéYFYä"›…rþA׼ˉsî%±ÈÓò®É´<¿Œí±d¾,­_ ã[\]Ö›c�,w[|y[(å²ëkJÒëiZUé8o-ßpaÅ9ßÏ—WÉøW$ÑrÞ9³‚ëì›]¾ã{L]ÆŸ>ãºËí—a‡–Ö•¥i[~ÊÑé8o,Þð._Åy×ΗWɸ_$ÑòÎ;²‚ëìŸ]½ã›<]Æ£:ãºÉ,—a‡–ÖÚÒ´­¿ehÊ=Û|› {«¨;ß…E­Ÿ™t§hõÿmñ(78òfiæäÝqË5<ÇCÛñ¦ÊÖɵÓEIã¶Hî¾*Ým.¶µõ¥¯ ÜõdÚøy&£g~£6|oè_¤ôÓä_‰—g¥·E“ –]m·[[ig7Jö¯ï.¡ë.ãâ—Yüìî Æy¾² eA›.¾.G¨ÅÙɨ̟úé=.FEø™vz[tY0ÉeÖÛuµ¶—PuGfðþëêÞµîN½ØK¶à=µÀ8wfð}¬øy:ùö|CžqÝw)ã[ °3#‹/\Í.Ö.†[m’+®­·R—R´e¦Odç¿DGñHydzïõæøŸJ”–l¶œ®+M.­×Á›>g&ºxkJ[ïÅ‚ÿ¶¾ßlXÿ_�E«èuY–ÖlÌÍŸzslO¥m$‹ -ŸëšÙ“K«}ÐæO—ÉnžÒ–û± ¿ó«K}°ÅüažVcñÎ’ñ“Ã-Öo{;�m»ë±0õ¼“#k¯áo­ÌâUÈøÖ5-�kÅy÷/æ;,ìY2núVl¸].ŽË䎗ÅTN‘C@$ÿiJÝZ[m+u×V”¶ÚRµ­kZúR”¥>ÚÖµ)JÖ´¥)ZÖµô¥)öÖµ¯ä¥)ü×ý¥+ZÒ”¥kZ×Ò”§ÛZÖ¿’”§îÖ¯ëåO642ädäKøðG|ÓÏ<×Ò8¡†(ét’Ë,—RÛm¶•­Õ¯¥>Õ«Þtü½âO��1™«ÆÓnzç¥:ã�rŒ Kþ¤6óLN-­¿›å}OuöÉ.Ç—K›“%Ö×Ût’Ý[iJzR–qx“Õ3tg‹Þ>uf6»m×½=×Üg‘ãbWÝyf×[˲­»Ý}/¿aÉîËȾ´¯¶·ËZÓÒž”Y¥âWTËÑÞ/øýÔYxúݯ_tÿ_ñ¾E‹‹_t5å˜\k]ü/Ê¥Þëét›O~^Eõ¥}µ¾ZÖž”ô¢ëŸ:jÏ á¦.†Û#¶+öŸ¡~½m¥¶úVOÉF̶°¬äã¿Ï7UCÚ?½ùÙ¹cDzeš·Öù瓘[Èá­=¾‘A4¥kJú[f¯áYò§]ÞØ%Ÿ³Â—žx�ØÜ¯®6Zé¹»®M•×¼ïk°í·å›M~M>õ¢ÑO‘É7\gQ .¿°q))ÖÒË¢ŽFNü»æ’ØN¾@|“Æñà {óïÖ`ïxg_í1¸%÷cÙõ{/”Ö.'×Ý…5}¹x´æ»¼2­ô¿Ù‡d²VÚÛeÍPó“ÈLoð».Ì=Çà»(xe×ãÙ—I» ‘Ö.1×Ð_‰'­21næ;Œ+²iéu,ŶI.¥m²æ¨ùÑå'ð§Ä!ü¦äŸ¢åƒ¦ºÇ�rm«u>ÃYÊ9öDVi:ǃåfë0¶¸óÞÇÛj´¶ä[ ÖãߟI/­¶[uÔ«>I$šI&šKå–[î’Ydºëä’Kî­×É%÷V·_}÷Vµ­kZÖµª´™e’i$ši/–io¾Ye–û¤’I$º·_$—ÝZÝ}÷ÝZÖµ­kZÖªÕå–I¤’i¤¾Y¥¾ùe–[î’I$’êÝ|’_uku÷ßukZÖµ­kZ©�Üîvü‹oµäƒk²Þï·»,íÎïw¹ÎÊÙí÷;}žT¹»-®×e›,ù» –Ã6{æžy¯¾Ye¾ëﺷVµ‡ø‡ÍKóð¥ÿÝçÿßüb’´ü1Ÿ÷nųþß©V~?û¶¿âÝÿo´æ?ïþä—þ'øÓ%ø•¢U‰Ì#ø¢úº=ï‹~=vüx“;®;ÃcÂoŠ;}ÐE¦í>³ÙçgdÖ‘Ý[)Û¬uðYuo¶žìŸOK«ZzGñ&u¼{¿z+µ#�"\î¾îŒ¾#}"³Ý>—²xv×??3&êGZÇe›~½×CmÞëm÷äR•¥kZzG7ñ"õÅ›¯ú/´âŽyszÿ¹²ø””ŠÏtXúnÈâLììÌšÒ:Ö;,Ûuö¾kî¶žüŠSÒµ­="½øº:§eÍ>7zÛ±ôÜn]¶GMyEÁ¶Ü›{vÝ^+À9¯ìNŸ—•%:, ¯`ìøÆ}+é\‰a¥iüÈ?¡ †Ê·\~ û�¦¾MˆŸmŸv¿IØ™�©vWÙed®Ng<ãùúî�[)éù¹½…¢:×òYJû¾ßOD„ÿ �0ôoËGVñÜù4ØšO#ºã³¼~Ûíw9Öà[¬›g«Âí>%¶ëãº<­Ï#ì.¨Óéq ­ÖV[ö~–Ö·ûlºÈu‚Ë–Ž€+ùMý£>hÿ(^Æþ®N®äãö�ùwýü9Ÿôò¸ï“OÚåÏ÷ïæÓÊ¡ÿ·ížóW÷Ó¤?äÓÓ-h«FGLÛð­ÿs?1¯®¡þ s”¼ ï÷/ò³úýëOì{”%Ñøi?¹�•_×çZcü¡cWà÷ý�¾@-.gú�è$°’wIÁ,`-ï.´Æî~”îžÌȳ¶:·°:Ó+.Kk|x¸Üï‰íø¼ùÙm.ºû ‹k[«JRµ­(ÆýÇט�»Ô=©ÕóS³úßœuænEÔ­Öãâs^1´ãy3]m´­×R(vW]ZSí¯£w^âöçQv§TçMn6guÇ8ëÜÌ‹í­ÖãâóN1´ãy]m´­×[[*ÝZSí­(ñ݉À¸§jõÿ9êþy¨Æä²8w&à\ÏC™e²aîø§0ÒæñîE¨ËŽú]d˜Û-FÆhd¶´­+mõ¥U3lµÙº�Ž~§g�6ÇW›•®ØaäYtsâfáO&6V4ÑÝJ]dÐOÖÝJý´­+E]›-vvŸcŸ©Úb̓²Õæåk¶8YV<Œ<ì)äÆËÅž:ý±Í�‘Ö]Jý´º•¢¯­Ž»7Q°ÏÔìñ¦ÂØë3rµÛ <‹+ø™¸Sß�—�5•ûlš âºË©_ÉZUI‡að[Õ\ÿœõ>ÓäqÞuÖüÓpkÇòîŠü­-áû¬Þ=Èôù7Á|°Ý‘¬Ük¦‚úÙuÖÖë+éZÓí~'â~7�JÛð»ù‹³¼„ñcw°úPöÒ÷ÇÊØC�‹NCÁ§¯æÚín ¾—çî¹ä:컩}öañùo­µ²ÊÝdž¿ Ÿŧìúñ›qŸ[!æº GmðŒlŒÈ ųyÄ'§æ¸X8²zK›·Þè÷šÌšÛku¸š9o­µ¶Û®¶N_†ë¿¢Óv{øÓ¸Ï¬póm£¶xF6Nl0c[¼ây¦,ž—æm÷š=æ·&¶ÇZÝn.’[ëmm¶µ¶s?ƒ“Ê{)o–Þn¶v[}oãþRõ®ž,žù)[t½OÝ»�"³ÖHég�OKëîË–?u)/¶hIw%Ìœ¸Èv:ã]_À¹¿eó<êë8]ñKι^Ê�É‘]âZlÝþ÷:�EK¥ž¸š½|²{-¥n»ÛéO¶¯-ÎyŸëŽÌ;—çSWÄøäÏ”lîŽùi®ã¼_S—¼ÝgV(©t’Ó[ƒ,žÛi[®öúSíynsÌø÷\ð®aØ\»:šÎ)Á8· æ|ŸeXï–šþ=Åõ9{½ÖubŽ—I-15¸2Éí¶•ºïo¥>×Ãäü›�p®5Èy—.Ýk8×âZ=·&äü�u™»M ãÚ �®ëu¶Ødß6³U­Ä–yæ’ëlŠ(îºêÒ”­UBw_joûϸ»Sº9U˜ñr^ØìNeØÛÈ0í¾Ì,]§3ä;C™‡ƒ—É|Xsl+Vêû"²Úz×ÑXGqönóº»k³{ƒ“Y<ƒ´y÷.çûŒlZÉ\<=‡.ßgorppi5×Éf ™Õ† kZû!²ÛqX¿qvnóº{g³;“Y \ƒ´9ï-çÛŒ|ZÉ÷LLþ[½ÎÞd`áR[¯’Ì3« ÖµöEe¶þâ—�/ü‰ßymå/�Lr8v[îížmØXÚmžÚMîOÐo÷™yÃÄæ½¶$¾•Ž,}N�™ÐMOZ]usãôû)ru ¥¦„°tÏÄ¥û9°¿”/XÿPùÛ†¿ˆOö~Ùí¿¤y[‡_ˆ7öÿÙ¿­ÿ¤y[ƒÿ‰wö0ùgûéã×ü¥º…¤PlU^Ïþ'ÿo¿¿ÿMþ±xã=x­ügüpþÿ]?úÂã¬ïâ×ñ›ñÏûûõë�7›ãö–|xÿ.o¿_¼kÊÎåœ+’€Õüæø+'‡bo9OÓÙƒÒGOºìî¸û–<ºM.$žžûu|£Oõ$ŸMµ²Ë¦Àɾë/¶l<ŒÌL�ÕðKÎÓðGºp{7€Í]¿ÜS MÚ}q›‘|z>ÀâåÒiq$þ~Ý_'Ô{ä›O´²Û¥Áɺëo¶l<ŒÌLž¦|K|¢vÇÅÇ’ºÞÎâù^EÓß#IÇ<†ê8²/—_Ï8&6ʒݸÓë26ZíL=ŸÂ±ò²§ã»¤Š°K‘‘‰$”ÂÎÍŽ["|_òƒ¦¼Âé®/Þ�Ê#ä¼/’ÇtóÛ/"â\‹,y7Gu/•½KÆ»£¥ù,\‹‡ò(®ŠX¥¶916:ì¸âËÂËŠø¥²Ûí­ÎŒäÍ  䯒=Mâ_Ló>öî®Coàü/_~L¶ALy·œ‹m-—ÓQĸ¦·#+ÍÇ*äYvÒ <D|›|±ùòæHCËù·XqN¥ßßO¸_äYÜ{?£4ݶÏUÉôû;� šþÝä{-FÓw¾ÆÈ»:Lì|lx2-ÀÖë ÅîŸÆwâ&äœôKy÷—¶æœ>+5Ún;äf¿]§8ãpÇt1S¶u8V]�εø~’KºÃŽ^AJÃuÙm&Ⱥh{Qñ×óç¾áôÑôÿœ¹[>YÅ¢‹ U ò  ]—0ÑEðâAgij0b»3™k ïºMÎrï=Ðû²`ÙM=ùv�ã»ç·}Ä)£ê8²¶|³‹E«Aä».a¡Š;áÄ‚ÎÐÔàÅvg2ÖC‰ùÒnpã—wî‹Ý‘ÆYïž) üF~)ÊÉñßäç*Ú]%ùú¾;æN¯8£¥o¬3ñÍ?{pn9¨²8­¥k> 9fš*[J}Çôž¶”¦Ë|˜ß æÜ;±ø®�œõ÷*ãÜÛ†rl6|w•ñMÆÿ�nõò]}–æjöúÉòps`ú‘ÝenŽû©möÝm}.¥iIfñ_Å9ÿÓs> Ét\Ljò<öZOÆ6Ø;Íç_5n¤yšÍ¶¶|œÜ{®¶´÷Ç}Ô¥Ô­?-+D±x�/âœÿ�i¹Ÿäº.aÄy{-&ã;\-Þ‡s�-n¶<Íf×]6Fn=×Y[}Ñßu)uµ¥~ÚV‰½ñ®KÇyŸÐs¿Òò¾%Êôº¾IÅùGÚàïxï$ã»Ì6zMþƒw«Ÿ+Y¸Òî5¹QdbåcË$Il‘Ýu—RµõFôo´á_â è[»‡ã«“ó]v?ÖßøõϸolâÛ�¯ûÞÃ3A‘‘“ל·_ö[Ypõ¸š®oMÆ]ßùn¶ií­ßÎÒ´âçÏOHIÛ^r~Y¯‚I·�Íø—kbÇ‹…nNnf–é²x'*ÃúÔ§ÕÄÖáiy�û|«©_mmÔÛ[¿�§§>y:FþÙð“rÍ|ͼèžmÄûOÌl;rsrô·Ï“ÁyV&¥>®.»MÌnÛeV•öÖÝUµ»ùÚzpcñ'øÇ‘ä¯Äïwæéµ[m×/ñÃwÄüœâؼüLmÇë¹6:ÍÚîmÌ­‘çjxçGs^U²¬_d·äbEXý÷ÛH¯¯m¤•[&ñøg|ßôO=ðß•m¾§.èÍŽw?ëlIþ�²æõ5Ü[/!¶()$ÖpÎÈÚI6L³É[ýœ“(©ôá­,™?áØòÊwÒ\׾M´¥ü³¥só9¿^âÏXm—7ªy†Ö“nñ0íŽÏ¯‘NØ[)dÉ–k½m��âE­‘Ö–L{ðïya:éNkâo&ÚRþWÒùùœÛ¯±g¬VË›Õ|¿kIwx˜vÇgÖžÞ!ØdÉ–k½i ÄŠ?[#­-±ïð¢yç‰ÞþrO ¹· �#µ|HÛÍ? ÄØçGvבøùÏvy›n=“‡vÏ‘gïwÒu¿8ÈÙé³+�ƒ‹«Ñér¸æ%µ­óÒ‰@$‚‘ÒV ›¹Üê8æŸkÈwû< .‡E­ÎÜî÷;\¸05z�F¯\í–Ïe�•|XØXp_,ÒÉu¶G•ºêÒ”­_ƒkµÖhµ{-Þëa…©Óið36»m®Ë&-v³Y¯Ç“/?aŸ™‘|xø˜XX°ß$²Éu¶GeµºêÒ”­_ƒkµÖhµ{-Þëa…©Óið36»m®Ë&-~³Y¯Ç“/?aŸ™‘|xø˜XX°ß$²ßu¶GeµºêÒ”­_æûìŠËå–û#Ž;.¾I/º–Ye–R·_}÷ÝZ[m–ÛOZÖ¿e(«›äʬÿ4|¿î¿ åûÔYÊ.Öõþ³.Ûá›QÖ¼W5Á0çîVd8[<Ž;¬‡/cd7Ö¶y92[üú¶?<|›ÌòûÊîàïk¾õƒ“r*ë8».’Å.¯®øÆ4<{…ãK‡&NTzý†^“]f|Q_ô«²ÊȾßçê­ÃÎï&s<¼ò³·ûÖï½C¡ä¼ŠºÞ ®Ê¤±I«ëÞ1�á˜Òâ_‘“~^“]f|q]ô«²ÊȾßçê¨3æ#Î;þAþA;Û¿u\í‡Vânb눃'3i‘‡‰Ó=utúN-³ÔánuÚ­�Æì=‡ß¹vN²L{.ÁÙr¨®­÷R·Ý¦MBj+˜€ æøÒ輜ó»Æ>ŸŸg¢Üv†›’s< Ûî‹3€uå³ö<Áž[òÝÛ>'Æ2ñbûiïžk,§Ûu«ñÓÑ×ùæßŽY.>ÃM±ì�G%娙”¾˜y\¯é7=æx9Yéô¿Jq®7“‰kZRì‰ã¶ŸmÔ¥wKã·£¯ò+Í�º²L(3ôÛÈÔr^]‰—ï·'„p MÎùžD–åªìøßÉÄŠµ­=ÓÏ´ûn¥+ÖŸƒö^P|ªxuÂñãË·EÀûOUß|Óc“‘jð8ÏB{»V˜<�•·I͹×ñªåd]ôbÊÝÃOl—ÝdRZ$²Md2Üp5î~µÖ÷7Ov¿Onr.ÄÓö¿Zó®µÚåYÕ¿[θ¾Ó‹çdY¾?©t8»Kë}kOOZ~V?íž½ÖvßUögT 7fõ÷3ëÝ´Ö[[ï‡YÍ8æË�çËe”¾:ß|x»+ëJ{­õ­?-?+öÏ_k{kªû/ª·2ÝŸ³:ÿ™uöÖ{,¬—C­æ|seÇ3¥¶Ê_oº<]•Õ¥=ÖúÖŸ–Ÿ•ä{�ñ>Óà\߬yö—’p^Æâ<“�óN;ŸmoÀßñ>_¦Íãü�K›e+J߉µÓì&‚JR´õ²JªmäÜouù'!â<—_“©ä\Wy¶ã{ýVdWÁ—¬Ýhóò5›]~T2Ûd�äágbÉöÝJ]mÖÖ•¥*«Ë’ñÍ×ä|ƒ‰r< �W!âÛ½¯ßjò£º,­nëIŸ>³i�“ô¶øò0ó±dŽûkJV—[ZUX%㻞!È÷üO‘àO«ä<_wµã»ÝfU—E“®Üé3§Öí02"¾–ßøy¸×Ç}µ¥+K­­*¥ ·ú»–twmv�Jóì|LNuÓý‹Íº»šbàeS;–uÿ%Ùñ>G�…›K"¦f$;�LÖÇ--·êYJ]éO_GÃ|GÄc \><ÉÄðãÎ~´å›{u]OÜÐW¥û;+*x ÖiðyVÃN%ËóåÎÌÃ×ëñ8§6ÃÀ›;:[«v&š\úÙJÖúÛwTþ<¶Åñ;Í.›Êv´Öu‡pcÿjÄÈÉÈŠ n¦KŸ…'å™×eåâkðñø×0Åû/6[½q5çVß_um»©ß>Yâø¡æ ÎåJkzÇ·±ÿµaäddE»U$Ï“Šr¬Û²²qp11ø×/Åÿ/2ZúâêfÍ­¾¾êÛt…? Wžpxiò!ƺÿšns0:kËü=_CrèîØgŤÓö>~æ,ŽŽç V\;,Ìc•7·#"ìl}V»–gæË5±C}·Ù°U`RÑÀ-?/šxüǰúóÂ.³³#QÕRaö—qÝ�/º;û¦Ÿ‚ql‹nÅŽøçâ¼#q>Æo§4°O^A·[dØ•¥"ø‹<Àƒ”óžá¯ÙY>¯­ïÄìÞÜ»_u·ó­Þªh87žŸF9"›�ðý¬û)émòÃ?éÌoZ[.-iHˆþ"_/ å<ç�øqÃöVO¬ë‹ñ;/¶îÆ—ÝeüëuªšÆ2)ô£¾9¸çÚO±ž”¾Xfý7�JÒÙq«JAwñtüƒbeÍÕ_oÈbžmfV§½¼”®ŸkK¯ÃË“ 3¦z³yM7%õŽ[°6\·k¦Üêÿœ—‹ì±%ûkTT‘�FU¿? _ýÞñ]ÿÆ)+OÃÿvçü[?íú•gá“ÿ»kþ-ßöûNcð^ÿîIâwÿ�2_‰Z%XœÀ™¿1]/z|myUű"Å»oÆ:úîÚÓM‘d’ ž�Úëû+c¶—_nÃmǸÎn¾/Oç¾ù[kö]W;¾XzŠþêøöòs‹âÙvœ€IÙú™/†³MfWRì°;/Ûl¾ÿ¿m´ük+Ïm=nûÕmû=\ðù]ê+û§ãëÉž1‹Wm8ÿ¿³õ_eš<®§Ù`v&\86Ûm÷ý÷k¦ãyX6{iëwÞ«oÙëêæ×Ì�¿ð øÄó_§ Åä¾Fr^wÄõ[ï{Ýÿ<鉰{£¯¸Þ%#–LÏá'8ëý~°Ùme–‹í³ÒúÛUg*êÛ)ô}þ)Ê7ü”ñ¾iÅ6yZNQÄ7ú~QÆ÷82V,ÝFÿ�ìq¶Ú}ž$´ûcÊÀØâG,wSí¥öR¯½Å¹6÷…r~9̸¶Ë#Mɸ–ûQɸæãêY—ªÞèvû]FËúÒ´·#a‰¶WÒ¾—YGÝâü—yÂù7æí¯µ8.£‘åkñ'¿*-$¥—ëy—»&øâ»##ˆòü íd×ûiK¥Äº´û=jøÇÞÜsÉ¿ú“¾¸µqìÕvo Õr°q²~÷f�}ì»–qy2}¶}|¾)Êp³u“ÝJR•ŸÿE˜þ2w¯òg :—¾8µqìÕög Õr ðq²+•f“}ì¿•ñ™2keŸ[+Šòœ,Íl÷zR•›ïO±s÷�@õÿ•~?ôï‘ý[›noî~¾ã\ÿCÙÚœüý=»Ýt9;+È$Ñì6º¼^[ÃvÿxÔîq#È–¸\,Œkëõ"º”ØFugFpVò›ûF|ÑþP½�ý\�\ÉÇíòïûøs?éåqß&Ÿ´ËŸïßÌÿ§•Cþ#oÛ=æ¯ï§Hɧ¦Z ÑVŒ8Ž™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ Ñ>dº~G¼™ãvbäáç\ÖNéâ¹2àýÃ7SÛðÙÎ61ê#¶8à—U¡å»]ž¢;âõ²—믷ìºÛ©Jî¾\zF~‰ùò'Cf.\:>s˯î.3“‘‹÷L|íokEo1Ú~жÛmŠM^——ìvz¸î³óiv¾ë~ÊÛZR¼o–ΓŸ¢¾@|ˆÐÙ‹•“œrëûƒŒäO‰÷LlÝgjÅo0ÙÓWe,²)5šnY±Ùjìº?Ì¥Ø[öVÚÒ•Kþ#?àÍòÉä]ºÝ,š~ßÒh¼›á—M³®Î}¿öÙÆž^ÌÝI[¿¢`Ç™Þú^[l8×åœx㥿ÐýŽa9¸ç†à ‘ñÈÎEâW“]/äW·"|Þ®æúÝÞËY‹5˜Ór'—lÚnqÅé‘$rÙ�o)á»<ýud­·}*dûéO[hØoü€Þx³äwO÷ö‚9òrz×™`möZÜi#†}çÌŽ}/4ã‘M5·Å�$â<ì IZWéW#ßùhØO{ûyâß‘�Cßš 'ÈÊë~cƒ·Ùk±¤Ž·|_29´ÜÏŽÇ4¶Ýr>#²ÎÁ¤•¥~ŸÞ=ß–�ãø×ó7ÀÏ9%¼Æ²hñ÷k“êñwZMœ1dÇLQçksc’–Ie’YîôºÚ]JÒ–^p¾cÆ»‡ñN}Ãv¸ûÞ!ÍøÞ“—qmÞ-%·oÇy·o¥ÙÁlÖE5‘gk³#–Ûo¶ÛéK½.¥+ëE•ü/˜q¾ÃáüWŸpݬÎ#͸擖ñ}Ö-$·mǹ·o¦ÙAlÖE5‘fë²ã’–ßm·ÛK½.¥+ëEÆ_“ñ¾oÆxï3áÛí?*â»E¨äüW“ñíŽ&ãAÈøÞÿ_�µÑï´{l gÁÚi÷̸²1²a¾ø§†Ko²êÛu*õoLô¯ºŽ�â>ó ¦òCeL-¤8Ù5³;QÕE21)É÷VàꢶhþŽv›-­n‚êR7ŠΘüTøôÙôoÛÍ�Ûžjgm:‡MnócfëzwQ·cß{ÿYt[]NÇ_´ã»L•….F]Ðó åã_up¥¶�5B- ubÀ æ~_ïëo¹gvm5ßvÝùÚ[]†¯6™I÷î»êûgávÙq©O~$øÜ✦ê{«ë$2Ç})í­µºnŸ‡ƒ£.ëÏ 9'pl°#ƒsßý•¶Ùksm–Û¤Ëà]qIxV‚ˆiO~<¸ÜÊ.I}¾êþ|3Çu)éZVéµ~n�»¯|6ä�¿²À³qß½•µÙk³-–Û¯Ìà}uIx^†)à¥=øòãó¹%öû«ùðÍÔ§¶´­Ör~Ï2:OâßMÙ{ˆ ³ånvoÇõ´sêwšÞ¡›¨xŽ“e•—™]nMýs�È5²Ûþ§®'!¥Ñzûî¾ù;èïrI€þ~%/ÙÍ…ü¡zÇú‡ÎÜ5üB³óþÎmý#ÊÜ:üA¿³ÿþÍýoý#ÊÜüK¿±‡Ë?ßO¿å-Ô( ƒbªðñ?øÓxÕýÿúoõ‹ÇëÅoã?ã‡÷úéÿÖg¿ŒßŽßߨ¿Xy¼ß´³ãÇùsx•úýà ^Vw,á\”Ð’ï¸ÿŸž)ón•É»]­çø³šô×+Øý{1øÇfè±ríÓÝ�>5²Í‡’aeäj6UúY?K >üˆá¿"=º9òᶇÎ?ù—Ne]�®æØµ³—õ/'ØVhñøßdhñò©¦“6lxò%�I¿ÅÊÈÕl«Hr.�:I£Šé↶è÷Èg‡/8U4òfM�dÒǤßâådj¶5¤Y� :I£Šé↶èGÉ��üäwîÓñ‹™_…ªÝïp¢åKÎ2p°²§ëŽãâñdåp^_�.V¯m“‡®“&yµ[¿¹Y~gÚl°àž ò~­•“rÞ'Éx)䜙èö\g—ðýöÛ‹òž9¹Å“ o¡ä:ùõ{�>Ïj[..~·a‹$2Çu=l’ÊÒªæ9Wä\“ò.ËôÙüw•ñ-æ×�rmÖ ±vzMþ�:}fßS°Æ¿óàÍ×ì1dŠ[+öÛ}•¢ºnUÅùäÜ‹†rí6å|Kyµã\—Aµ‚ì]ž“}£ÎŸY·Ôì1¯üø3uùøÒE-•ûm¾ÊÑP7kõ_`twfóîœín5™Ã{+«ùvû‚ó®-�>^F�”q�–F§q¯ûö¯+?S²‚Ìk¾–^FFTU¶h%–ì¾ï<ø/‚Çà ôøÿù ï?�>Ü�°z·;ôç ßÍ�‹Ú½C¸ÎŸˆöV‡¹ÃUÑÇ“v‹•i¬ÌšMNæ¯ÈÀšû­¾Ìœ9²°òwÁo=»«ÀÎÑ�œu®oé¾¼Ÿ Íê�¾dÐq^ÃÒâV{#Žy,‡&M'ÕY—-ú½¾oÖÙ¿¦øvî|(;/ª¶Ù“AÅ{ M‹õìŽ9ï²,›ô|›Uf\·ë6ðG|øs][d³#LœLޝ|Wü¼ù'ñeÙ·íºë*þ}Ñ|»u‡±íï·Û*`ñ~q÷|95´ÝqýÄšÝÆW瘸7ÙH¶Ø0_L�»cÅŸ�›� p[aß…vø÷ç¯W[Ù]Éþñ•¬®&':ëÝåØ˜]�×;|Ë&»_ÊôPeeV,M�Ýf® æ×ì-†ZA5ÒCmuµ�‡ÒÜ’™3à}×™ð]ÍØ˜|ë€m2íšì|S¤ƒ+*°A�÷Y«‡› ó`ç[ ŸBkîŠkc´[¯:óu“lX±ro¹ŠÚ}ï>謤vzA�dpÛét¾C~D»[ÏÞͳ{È©‘Ä:ŸŠädÅÖ=Q‰°¿+[DZ¥§Ò›{¼È²{6Ý÷#¦Gêž-‘“XõN&}ùZÞ=‹->”ÛÍÜöG�ó™m¢¥>ó™tvÛ~�Am‘R¾ú¿>eþfû{å[¶1õØPnºÇÄŽ´Ýdåt·HM�s6;+qòµ—v÷nß­žmfûµ7:̹¡ÅÇŽL�oÖdÉ®×I<¹m®ç›®y¹î☣|žù1ñíË,Ÿ¬wÕå=M·ÜE³ç}Êò²&àüž·E&nÇSu-Ÿ+„rù°bŽÛ6Úê[|—cãÛ™n<6ãW > |‘yàw'¶^»Ý :³k¶‡cÍz_”edKÃyke˜Ù» =ô¶|žËg²Û-Ú`[JÉtô̇6-Ço÷ƒ#ÞDx#É­—®÷_Â~®Úí¡Øó^˜äùSËù ke˜Ù» Eô¶|ŽÊæÂŽÛmÚ`[JÉtS2È!·Øï‹o›_-~/9è^!Ÿ^äñËo“ ¼«Æþ¿ÚCűïý'.Ç;}Õ{Û"Údu'4Ø}ó*ÌŒ¼ü—xÏò ÃcÚu7%·CÙZ½]3¹×Hò̬,NÄâ5†\|LÝ„QËXùW·7.aÜëþ¦5i‘ ™6âeßv,s|ð“ä?Ç_;8�»N¬äTÓv³_\¾iÓ¼£# °8§Ñ—/>Ìæ¾>IÅ+•— !ÛàV\ZÓ"(ò)�•uø±ÍÇÂo�ϼéâVìú»�Û¦ì f¿ï|ϧ¹>N'>ⵆL|l¼èðcšèù'ûÞTV÷Áú˜Õ¤ñY‘Ll«¯ÆŽÊO~M#ù-̼Dò®;ï…RL¼Î¹¶íçûÝp±yÙGv¿–q,éë\PÅ»ÒäK3ß ÿsˤ9VYY`³Ó|þ4¼íçŸ~bõW“ü.Ìí¶£�gOÆ»Wƒclv8�“Ô¢°ásž›ÓU‹��f,qmtŸº}~&Õës¦Çž˜”ŠëBzW¸ú÷È>¨à=ÕÕ;ünM×Ý‘Ç0y7Ûc_×Ý‹—mÖdë¶0E,ߣ·º=„S`ìpä¯×ÁÏÇ›Z[,WÛK#º‡¶xzõ�íþ±ÞãrNØ< ‘ñͶ5ÖV²beÛ[fÂ΂Ûï¿_¹ÔfÇ.~ž“áfÁ.<¶Û,wÛK z‡¶8'zu—íî²Þcr> Ø< ‘ñÝ®=ÖVëñrí­³agAeò]�¹ÔfÇ.~$•¤øY°K¶Û$wÛK~ú›µº÷½:Ç€÷/SrŒiÖ�ŸÄô|߃r­|Y¸ØÛÎ5Ȱ Ùj³¾å³ÅÁÚë2oÆÈ¥'Ä̃3z_DQMñÛ”‘™?Ä_ò�Ô=…e½öö�xê¡ÙöÖ^³+.ÎÓ?{º‘èfÈŬE´íM†ø²ÁYd­48Ù–dAHö²Ý/ŸŸÞÊî½U™ýŸ“�6Ty|üîÇê~¦‡Ä»Üû{'ºuVgövNù1äñ¡®MÑÓO,ø÷C{.ÌÎÄ“ø}òÖš\|Ë'†Û3qdº-_Š äïÅO/ðì7RCä—<[cƒÊfÂý$¼Ƽ¬œÝ6ÚfÓ'"LŒ}—ndbåq�g¦±É®�y=¹™xxu–hb!œ­�-ïÂóâü¹œ‡¿<Ãß`Wî:\ n„댉âž ÷[õÓ²öô–Û³pvm^6ƒˆ½¶Ë×2u}/µ)ßÃkãt¹;®òòÇwƒ_ºk0±ú;¯§–Üib›e°¿UÌ{68¥²¹8¹z¼ }4Ö–ÉÃ./_²ú%+øoG1ÈÃñ¯¨3g»y…—/Ñdê9ïrm#ÂÈŃE¹ã»îOgÂÂÏ‚\‰!Øqíž5~•m¾’Li,´°ÓlWó³ã¤¾>|Ž÷&Vø|S½©®ïþ--ÓS"¹ssûòìçÓI%>Èe»µ5Û톾—Ç�|5ôöÝm×WýócÐtOÈkæaàW ‹wlzîòãrRJÍLœŽk\˜yܲÉOÍ�&^ÌÔîfú5ôº8&е§¶ûkX|Õô�ç÷kfaàW‹÷]šþðãr}O­LœŽk\˜¹Ì’IOÍ�"þÊÕneúUôºÈ%е§¶ûku^߉÷ÄûürùC牥Òãë:ÿËÆ{×Av›Œæé¸þ'3¬pnÚÓɶ¾•Õòe²æÜR^Y¸»êK9n5ÓÙK¦¶IxðäÓˆì,Dø8ù ÆóKÅÝç܆Ìß"¼}ÀÕpþ{Ç7##yÍøl0[‰Â;Né2ãú› ö¸˜õ×î$¤ù3Óo…~NGÑ·a‹móâøgóËÌ/0xo8ÞÛ—ß½‡¬â\ò<ì™æÛó/Æá½—Y2iuùómðñîÁÚÉI¦š›l9gš�Ù›‹móÕøkóÃËÿ°xg7ÞY—ß}‡¬â|ê,ì©¥Ûó-ÆáÝ•[òm­ÙÒí±1îÁÚIIfš›\9'š‘Y›�mÖ¾üüœa|”x_£Ùs¿Þ|˜è8´eä^&E5Ðæoöÿ£²)Ã{_7²Í_léµäOuq°"��àíñ±ñé‰�²öÑØg`ݳh�ÈÇœœ'Àywuò*àí9žU²q^žáTÈ’¼ã³vxyRi5ÙQbM‹<\oQ<›Äôš ǮŒȯ®\¸ÑK¥~}ù™Ã|ñÏ•÷!û®Ï•MmÜgª¸lÿV빟clñ2dÒkr,‚libÐk,Ç“?k=%ŠèµØ²Û×dß�º[çÇ™\;ÁÏyWpòºl¹TöÝÆº«‡Oõn»™ö.Ë&M.º{ ›h´:Ë1äÏÚÏIb¬züYmŠë²dÇŠ]ùó»¬>8üNìo'û6È7ñØ"ÐuǦëG´í^ÖßA—N׺lܨ²å†íŒ¸sfì²qñs§Öh°3öÅÈ·ø®¬‹žó®[Úß—öG=Ýåò^mÏ9.ë—òÞAŸôþ÷¹ä\‡c‘µÛìg¶â‚;ò󲯿Ù–Ge+í²Ûm¥)JåyÏ7å]—Ìù_aó�Ö_#æ|ß�íùW)ßg}*em÷ÛÜéö[Lù¬ÇŽhk“™‘}ÔŽ+,Š:V–Ùm¶Ò–Òº.s͹Wes>UØ\çu—ȹ—6ä~SÊ7¹ßJ™[mö÷:}ŽÓ>k Žx«‘—‘}ÔŽ;,Š:V–Ùm¶Ò”¥@ýáÝ}�äwnö'z÷7+Øs~Ñí>U´æ<Ó“l«’çíö³VK¡ÃÃDz,F—W�Hñ5ÚüHáÂÖàA.4QcÃvù'•yf+Kóð¥ÿÝçÿßüb’´ü1Ÿ÷nųþß©V~?û¶¿âÝÿo´æ?ïþä—þ'øÓ%ø•¢U‰Ì?Ç_ƒ·×çj¶˜xÛ fÏ'_±À̆̌LìØoÆËÃÊÇ–Û¢Ÿ'K¬¾Ë©[n¶êÒ´ô«ófábl°òõÛ h3p3ñ§ÂÍÃÊŠÉñ²ñ2¢¾ œlˆd¥ÑÍðÉu—Ûu+K­­i_±ùs°°öXYzí†,¸ \Œ,ì,¨¬Ÿ/*+àÉÅɆJ]ÐdC%Ö_mÔ­.¶µ¥~ÁT×–½ ¶ñƒÉžòè Å™ŸWªû'“ñ}f^~=Ø™;Ž/�°“'†ò?»ß|—G(âY8[:ïXr­¯­}}U�ySÒ?|ŽîžŒÚE™eÝiØ|�Žê²3âú;^/uù|;�]Ô—Ù$âyX[éî­~žM¿j²ß)zKg㇑�ÑÑ»Hòí¿­{ ‘ñÝfFt5ƒ#kÆbΓ+ˆoë o’¶EÈø®V|^·]_¥“oÚ¦Çä³ÅÛ¼.óÛÊ¿1õh¸ïZvÿ#³¯5rnéȲqú‡—WžtÍùۚ͑6fÏ/ª9Fšl¯­}r#È’øæô–ÛéMx`hèS_‡ä ­9ÞïÁŽÓÝÃ…Ä{gw/+èÍ®Ï.81tý©&&>.û€ýY ¯¶ÆÖ`Á6¶Ë²"†=Æ ÐC¹[oZIƒðúù߇×ÜÏságfncÄã�¸›”ô¦Ïc•ôñµ]“~$Poø'Õš+쇞k°bÈ×YY †Í¶�ǹ[KRXü?~vaõ÷2Üx_Ù{˜ñ8Çgn&å+²ØäÒv66·“ì5:‹£ÉÙcëfË›YÖ}êØ«ZÒšýÇü©ñó”÷Ç3ñ—GÚ\k3½8·Yµä½ytÙ›x±¶šÿÒÖÙª—3_ȳ5º»ãÈØA®›*]tsÇ\›b­Ô£_¸ÿ•>òŽ÷æ^3hûK�fw—Öëvœ�¯n›#m>Ï_]µ#ÕK™���ȳuº»£ÈØA¯›*]tsG\›b­Þ”Á<{Éß9_�…â¯íî·ò/ªxwìÁéìMÌsn3Ãù�¶ºMÞ^®ïeg‡èäáM™K6³qª›6Ìx¶úËòöM°-�g``)¿´gÍå ØßÕÉÕÀüœ~Ð?.ÿ¿‡3þžWòiû@<¹þýüÏúyT?â6ý³Þjþút‡üšze ÍhÃˆà ›~¿îgæ/õõÔ?Ôr—�á�þåþV_½iý�r„º? '÷1ò«úüëOì”,jüÿ³·Èå¥ÌÿQ½–Né8%Œ‰Ïâ�ñj]çèÏ0øö¾³fp|éúC²ò!·2|ŠqnG‘ŸÊ:ãg5–Yv§CÊ)¸Ãškî²ùr÷ø±ÓÝö{cø‘ÈÃþ#ߤÝð®šò¿A�õrøNlÝ=ؙٕ6Gð_�äfr±ŸÛJâaê´\›ô®$’][o“+{�e=}>È|~/ åìO:_Í®%¥²~Aã§-¿­;[3[¨‹*N¢íÌìn/¾äÌœÜm®N·ƒvƳ]¯‚,¯LŽo•?¶+,šúÃ5dGÕë&mømþAqù'Úø ÙûÈìäœ:=Ï3ñã/a‘‹›¾!4Ù;žu×µ’è236œW>i÷x1[÷™åÖdçÿå¬}lvÖ]¿‡ÃÎè9'Øø=Ù¨¬ä|>-¯,è\­†TK¼â3M“¶æ}�Y~”¹{.)›4»|(©vDòk23)JE�­¶•—'áöó®GŶ>ö>æ;y‹kË:+?& %Ýñ)fÈÚó.�YkÙ[.+›4»l(­¬óI­ÈÌ¥)>ºÚVßµò‰vôžoÇ·rò˜¤í¯4÷mú;u²Úäí»¡¾ñ'߸t;¾cM´èý†DXؘÖåCux¶v X_wÒæÏIa$ö“¢^àØÝ‡Ã:—�sÏìMöà¼�íùo,äÿR¸º�� m†Ç.øàŽ\œ™,Ç‚´Žl’iä­±Çe÷Ýmµò\÷�q>°á<¯±yæï�ð¾Çö¼£”o³«'ÝuZ=.ÙûÉ,†Ér'º,h.öE—Ë-þ–Ym×Ýmµò\÷�q>±á\¯±yÞï�ðÎÇö¼£”os«'ÝuzM.¹û ¹,†Égžèñá»ÙV_,·úYe·_u¶×Ïrî[Åøäüïœr ?á|+Žî¹o/å\‡a�©Ðqž/Æõ¹;žAÈ7›\Ù!ÃÖiôºœ)²rr&¾Ø¡†;¯º´¶•ª±�29O�žTv7o­ÍÀÐl³-ã}aÅó$¾ë¸oVñéò¡â¡ûæ~>>Êx'“c´û¼Ÿw—sŸ—4VÛd”¶•Äyãå¿$ócÉ®Þ[«s049ù6qεãY—Dâ]g¡Ÿ*>+¤’+s3à‡e‘D» �!–ìy6ùÙREKc¾Ûi\·�ÞZrO5|—çÝ㺷3EŸ“oë~7™wçñ>´ÐÏ•ÒI¹yÐC±ž<‰v/£-ÐI¶ÎÊ’*[eöÛJŠ~]þC·Ÿ&>lv�uÆÏÔuž£¬z‹íquØ›n9Ó×K¦Á¶û«Kl»+c›žµû)îõ¯ØÊÝÓ¼·Èníê„àTÂþwOfpn«â’m%ž V? çÜ—[ÅõY{|œl|¹ñ4øy›;%˚ؤ¬8ö_¶¾ßE¬Þ;ô·ñË¢z�¢8ŸÒ¿CÔý}Åø>&dx‘àÝ·ŸEªÇÃÙr ¼h®¾Ë6\�idÙùU÷][òrdº·Vµ­kg7Duè>—êÞ–âÞËô]_Á8× ÂÊ·¼ÃÊ×ðžë¾ÕÜ#;6}žvào[Å8æ6nË*ë²¶9�iõ0Û,òÖ²M%+}Õ­Õ«2²Ã,2(?Ÿ‰Kösa(^±þ¡ó· Ÿìüÿ³‡[Hò·¿oìÿÿ³[ÿHò·ÿïìaòÏ÷ÓǯùKu H0 Øª¼ŸüOþ4Þ5þ›ýbñÆzñ[øÏøáýþºõ…ÇYßůã7ãŸ÷÷ê/Öo7Æí,øñþ\Þ%~¿xו�Ë8W%"1ø‹>3çÚÓò Ò¼~²åë±5šŸ&øþ·Òé²5Ø–bé¸Çpâà×ú$·ë1íÇÔïé�[½¸¶aæýl‡e”‹ÏÇÇdû||�:úE$ùúÜ=n«ÈͶÛo“#U‡®;ÛXødòɨǶ ^ò�ÖOn˜™ŸJ8ñö95‹WÏ—Ç„ûh'ó£¨4wÍ›­Ä×jüŠÑkm¶ùr5x–cê¸ïlcáúRidÕcÛ³wHk·ÌLÏ¥dpl2k ?Å1ñ)'kð¬�’®ƒãräö?WqÜG”\KŽq¸'ËæÝQ¡Æ¾Wwdäjm�i™È:‡Y xÉò`Í­ü>82/ÈÁÃã·Y“äLÑ Œ9땾;«mµ¦[é÷íß{UÚ='Ï7½}͵‘ŶÒd[H¶ÈÆÊŸK¾ÕäÙ‘©ä|6|8®È×çÁ‘‡=c¶·Çum¶´Ú<Êò;Á^äÓw·Œ}“·ë®w¬Ç›Y²³‘l8Ç3ãY•²í—ç¼S>Ù´|¿‹çß’ýÛ2+‹™ ¸·ãçccdÃ6?ŽÏÄ!ÑEãéº×Ë98玽×-ÕÃÅå³dυѼêoX¾ï~.ÿm—•7[ng¶ù)~&ç"ýußF—E²ºlˆð㘯€ß;�%ä:Ž»ò~ý@÷$·Ó’K“>'Lsyëm•ŠL-öÓ'&^½ÛM_©mØ{Œ‹ðnúv}Œ“On$s ð#ç_¥<€‡Q×~Nß è>â–êaâòYr&Äé®m=iea¿y³ÉÉ—¯¶ÓúÉK°öù`ÝXíú[ æžÌHìGø¯üHþ*ùÍê_!ò8ÿŠÞSì/Ñh14<ƒo4};Û¼�7Ly§êîu¶¥1øÎËk¸Å¥˜Üc‘dǰúÛ <-vvó#ëId‹¢–)âŽhdŽhfŽÉb–+í’)b’Ú_‘ÉekmñßmiZV•­+J»é–Ke’Å}’G%–É‘ÝKì’ËéK¬¾Ëí­mºË­¯­+O²´wÂ9#š;%Šû%Š[-’9#ºÛã’;í¥Ö_eöÖ¶ßeöÖ•¥i_JÑ$—û·ûjùù¨ñ›Á\Mçãùø=ãä†<2ccuOÚÅ~§‡ìn¾x)7krÜK30x­poǺé5R}ìž±Rìl|yí̳’¾}|¾øçáN.ã†j3±;—È#»«x¦Ò°8®uÿZËgìþQ�n^·°Öëõ–S#u'¾\hqæ¦]œ˜óãåçÇ_ 1w3O�‡Üž@Aøøý_Åv�߯â¹÷}k-›³¹>5¹x|ZÜK¡­×ë#¤û©=ÑzãCÔʳ�'8¾|dë6\[–îÿ·?’uÄ“ô/�Ým¹Õßʵ™y~mb¶\ÌŒ™ýÒÝ O)ü¸ïO2;+3³»Ë—K½Ù{²¡ãœo_dºþÁ4Ù[-œ{…ñêädE©ÖÃlqÛ|²_>veÑÛ.fFDþén­sääŸÊß’¾ÔÃìß%ù´9ø|nŽ\u‡ÆÈÐõ_Vêv¹Veì°¸g“7>Jl6²A3öû ŒýÞÆ,LXr³%ƒ(5e¬íihHØpÁç=UÌt…Ö¼»‘pNqų¬ÙqÞYÅ6ÙºMöŸ6Ë/޳`ì°&ƒ&« —Ç%¾ïd±_twÒë.ºÚúÞ Ïy¯Xrí>ë®U¾á<׌g[±ãü£Œìòµ­Ne,¾¥ÃÏÃ’)ã¶lyoŠ[=kdÐß|wÒë.ºÚúÎ Ï9§XòÝ=ë¾S½á\ÓŒæÛ±Ðr~5³ÊÔnµY–Ù|WK‰�‡$SGlÐK|RÙë[&†û£¾—YuÖ×,ôo{w�«Ã{»¡» ’uwjp¬[Ž-̸¶oÝ69Ò±eafcÍdúÝæƒq‡|˜›-^|ZÍ®ÒâfA>4ÒÅ|Êþ3ÿì¯Ð�3ç¦f›¯{iuºž;ä6£®9†DÞÜ?ofëp㇭·’å}9.ÚcGg’“ItÖjbǶ¹áøëùíâ]�M7Qy»™§àòÿÑúÍ{bãcê:ó—ä_ZbV�‰��d8s»šo§%û,³�Kõ%º[uQÃe³Ëcã»ç£‰ö-4ýGæÖ^Ÿ€ó»ë�­Ñw¦.6>§¯¹lòzbÖ�‰ƒ�d8=w»šod—l`²Î?/Ô–émÕÇ ”žÀOˆÿÄñÓžJÅÅúÏ|®-ã÷|Ùª¦¯¼ò³±4ÛÛL)}±Ó{>gÐÄéno³Ößl—E™5Üka—�‘\|½t¹:ý5Ò—ÇÈÇËǃ+xrqra‹#'[&ÇÈÇšËd†x&Žë£–cº—[uµ­·[ZV•ôIZ¡È†,Œycž ã²h'†ûe†he¶—Ç,RY[¬’9,º•¶êV´­+ëD”!š˜bÈÇ–)ñçŠ9 ž-–¡–Ú_±Ken²H¤²êVÛ©ZÒ´¯­Ógô@ ?ÄÕáäÜ;·ºçÍ>+¬¿ø;Ûø8=aÚ¹ÇubÂì¾§­8Nß6y2oº²òî¼ÖW(ãŠØ¡·ŒVë®÷ÏOX|~#¤â�£×¾`q�mÔÑv¦&'Zö~DÓéãvÕV¼/m›-ùK$ü§�ë¯Á²Øâ¶(lãt­×{ç¶•ˆâ*ñBN+Ú=}åïÖÝMiâbu·gdC>–/`ñMU�›lÉožé/Ÿ”ðM}øV[dtŠ8å+uÞù­¥`ƒø¼üýɺ_ä;€ñë#Áäøø½ä&F¯Y�¼“Snߦ¹ÞæÍ~²Í¶Ïe¼ãÑløî~×a“$8ØÚM~ÊÉ·Å�äiP�H3à×åj¾v~8÷Žê–øËÛŽ ð7û,«bÇé>ÁÚß œªìœ‹í‡€ò:Û[ØäºØðn²=ŒwGHólËïÂ÷É×ü¹Õž=÷Vî–xéÙÛøoÖo¶yVE‹Ó|ïg|XŸÂ+ò².¶,N Èî¤QëaÁ’Ë6V:[›Lží|1ü›ÿÁC�Yãïtn½ž;vnþ õ»í–U‘bôï:ÙßðŠLœ‹­‹‚ò+©{›/ºØ°d²Ìû+-̦L´¿ ÇÍŽ›Ä^OƒžYsŒ�GŒý‹Èk7Kv'$Øûø¿��“ȶO²Ñr ¬¿[¸·Oöfç7ëäæÒKu|w�I~Ç.(0ö;�¦$ù±ò1òñàÊÅžœ\˜bÈÆÉǖɱò1æ²Ù!ž £ºèå†Xî¥ÖÝmkmÖÖ•¥}„†hr!‹#Xç‚xìš á¾Ùašm¥ñË–Vë$ŽK.¥mº•­+JúÑ7¨f‡&²1åŠ|yâŽh'†Ke†he¶—Å,RÙ[¬’),º•¶êV´­+ëE‹Ïìþ�è9ûò9ò ÕŸ� ¶ìž_™®ÜvO!ÆÙiúW««‘_ÒýƒÌ¢‚*RI1`–<¬NÆ.̇+y±­ÑÅ��u�G}Ù¹XXó迟þuõ—�½!³ìN[•ƒ¶ì ô OOõ¥2=6Üû—C ^—_RY‘‡ÃøÝÙpäîö5­‘bãÝd6]~nV>FŒùûçOZx!Ò;>Ãå™X;nÀÞÁ°ÕuZýãÓmÏ9lPÅéuøñIfF'ã—eÓºØV¶G‹�u�Ùuù™8xóóßä«äƒ¢þ2+ÆuYÛÞIÉwÎ?Çôz¼i36{�Þç6 v«U®Ä†Û¦ÊÎØçäÇ 1ÛJÝ|—ÒÚSÖ¯¯Çô¾W¾Òqn5«ÎÞr>K·Öè4Mf<™{-Æïq›»U«×âCK¥ÊÎØgdÇ QÛJÝ|—Ò”ûjö]u×ÜǶ»‚u_]èò9?`vg2ã}ÁxÖ$øxÙ\‡˜ó=Þã:ÝË.óœïa›"(s%ÂÏäû Ÿ¹Û5>¤ÁÙlVÒ–Ox]ãfŸÄú“¡uuÃÈÍá|f¹vÛßtƒžîå—wÎ7‘M&t7Šœh³t�3À5¼s;y>~ œ»™fÍ•È{žWY²Ür �4¼ÿ°·;MÝø6æO�ƒ~}qà­ Ž;i·-¤miÀ³üK^*ÉÚ^)ð�&8î¾É¹/�\®¸|ªX©‰ Óõ_heê8þÂy«ôþÿ´›�ó¼]%ØðÛulÅÅÎÏž´¥¿Rä{¿ŒÒvgŒ;È} 'ä^;rŠÁÈÇ,ÝeÙ9:�Òkì¶²öSé9¦.–Hc÷{1q2³¦­)O}QôüC>3ÉÙ~1pÿ!´6ÍȼxäõÇä’Gô#–n³ìŒ�V‹e5öÛs6Si9ž.šøc¥ÞÌ\\¬é½)O}Q‹üU>dùñ÷¬ò‰idÚö'†\Âny7Ý#ä; œÝ)Ø6긷p`ë4úˆò5Ò~ŠØëøß%ØgæÇlzÝ'Î’“GmÒ[$ШBÉZgü;òϵ|'ïÞßýG²¬¾5‘vÿ�äÍ-šyÂö7Ão$àüŸ?[rôÛ¼hm­·VÛ¤ÂÍ‹3¶ecA%›%âg”}™á×yðþôêÜë£ÜqÙïÃßh'Èš-'8áû á·�ð¾G ~ëru;ˆ!¶ë.ºËïÃ͇2nN47Ù²'yCÙ~w�ï.®Î¬{~==ø{ÝDòÇ¥æüCa|VòÈá�ÝnF§q�·Yum¾ü<Ƞˇۑ� öoÆ÷Ÿ]§ñµåwòg¬±ÿOâê>¿ìιÉÛfé´ý©Õ›Ùq¿…|#i�‡lÿrÉ—î�gês%Ç͇[¼ÁÂÌ“*Ì{±¥³Ä,úwÍN�â½ïÒ»Û6|sÜ÷zL©1íä¼–âA�.óƒó-lM]_#Òß“en·Öèr±e‡/ù±2 šK|Yò‡©¼Àé®3Ý�;»·gÇ·‘ýÓo§Ê¿>GÂ9N,òn¸_/×A4ÿ¢ùšì›+u¾ë¡ÉÆ–¬i&ÄÈ‚y,AñoÉþ§òû¦øÏuôþîÝ—ÞG÷M¾Ÿ*ü{9 å°Á&ç†rítOú3�éîȲ·[iaÊǾl\ˆ&’Ú¯ü¸èï8º ‚ùãç)³’ðq¯ŽKñ2«…(á|Ž|y7¼žiðs¶QqþoŲr)v-'žúÙ>4Ù“cäM³ ‰l;eÀ—¼ûË«¼nê®cÝÉÊõü7¯x6®]¦ïqŸ}=ò]ëH°u:œ:Wï}þë6û1pp ¥ùyRÙvÖë©F/î~åëozÏ–ö÷mòŒ!À¸V²MžïqŸÛ_JÛ³[‹o®N×y¸Í’ƒ.wÛìsdÊ—êlywŽ|¢Ž(£Š+K«[¯–µDñø»^5Ù}Uå·ÖÝf£²µ¶õWdäA�Kqáç|O}‡ Úçe_“|“lyO �' Ë,ŠÈ⃌ÛZÖ·ÉTB§‹õã]—Õ~YñÝmÖj;'[oVvFDVÜxy×ß?…ms²¯È¾év£…G‘…dvGdqAÆ­­k[äª Œ'ÃIqw^7yïÆpîû–Óÿ»n¶K®†6¸rNÄéÝÔZì| 6™¹Û}tÜ«eŸ‘‘$PìÔcYm•ºžø¬#*ŒêÀ¡¨Ûít]fûE³ÏÒîô› -¾›qªÌÈ×í5;]nLYšíž·?Hr°sð2á²XfŠûdŠK)uµ¥iJ¿~«k´Ðí5»ÍË?MºÓgáítû}VfF¿iªÚk²#Ì×ìµ¹ø’C—ƒŸƒ— ’Ã4WÛ$R[K­­+JUûõ[]¦‹i­Ýé69ú}Ö›?k¨Ûêòò5û=^Ó_‘^Ç]Ÿ‰$YXYø9pÙ,3E}²G%´ºÚÒ´¥_Šr¾OÁ9Oç'�î¸�3á»ý7+â<¯�ìó4œ‹Œr~;±ÆÛè9ƒs®›a©Ýiv¸qdâåA$scÏ·Ùu·[JÒ‡–¾7çw]kúŸ·w:�/–ü KK9&bÅÔbw‡Y±l~'� "³sX)mÛýf-‘Ç‹•[òq¡‹ [aŞĿÊ/óg¯p:Ï´wÍO”ÜMmœ›_tXºœ~ÔÒ묲í‰Åð`¤8TØÉ¶íÞ¿ÈãÄË­ÓÁ XrǶø›ùBãÞkuþYö†ßY©ò“ƒéí³’`].«µ4ºë,‡ûaqŒ(-‡ Ý”‘RÛ·zì[#³*·O1aËdPZ‹ð�ó×_'½�Åùvï[ÇüÏê./¯�½:ë"Ü\ÜÏ_‡.&š>öëœ,[1±6¼“çdcÛ·ÇĆ98¶ó.˜90Ù‰•¦ÍÚwÙwdÝÌqsæå_ŒøÕSð~¹Úé÷~VöF£&.Çn»a^·ÒfÁ™‰^ÚåZÙñ3ð/ÆÖfEíÔkóm¥›lëkZÙ..6])È�•ß“n5à¿YKÃø&ËUºòs°µ91ð^9[±ó®à:|¸³1?¶Ÿ)×Í�›‡÷nlU³Wƒ—m-ÛfÙu)d˜Øù~ÎF|¬ü™q¿ºÎ^Á6Z�ד]ƒªÈ�ƒqÚÝ��^§Ë‹/½£Ê5ócfa×]™³Wƒ—m-ÚæÙ_K$ÆÇËöñ?æ�æ3«>-:J}v¢}?9òó´xîÂ�ÔWe[,zxg¿+SNåí ÷äjºÏ‹ì¡—îØµúY|¯g‰~· øb‹i³Ô×½dv=ƒ^Ù‡œòè»F¼®^w^Æ�‘mìç5æÓí/ÝÏ˫ʭ˦ò¼–mÄ—e_�õþóvEÕ’·ûëZ }asØ9Ý{FkË!컹<ÜÖîÁ‹�í£æµæ9;7ª¼¢ÌºnëȧÛKvMù¿_ïOud­õºµª Qv<ƒ�W´ 朮Ê»“MÍ.ì¹Ö>i^a‘±¿o‘ÊkÉìʦ꼆}¬·dß™õ¾ñt÷Vúß[«ZªâÔù Þšð—Émmsý/�ó]×bÏÜ:žM´×vÜÛ’egær.G/#Ãȇa&Ã}6Ó*™µ­õ³*,™b–Û£’û+4ŠOŸž9Ý2h|~óq¡àݱ$xºÎÞsS�ðNËʶ‘ãÅ©çqYn6£‚sœ»¿>,Ë>†“g}nŽ–àäRs%íñ‰ó�Ç»’MDy�´Òð¾Õ–×è^aH¬ÒöoÙñêgI�net{¯K3ø¿(Çžû,Ÿ;Šr|,=ž5·V–×#Ï_±ƒ¼–è®3äÏBv·CòêEf›³8~Ë�Û�&=2«¤ÝzYŸÅù6>5×ÙlÙ¼W“áaì±í­imgų×ìa%<ëÏ*º·üqíl¿ðçà�€òʼn§ÌÙjbÞëåÇÀåv›ífçU…Ëø~×èm´¹²âÏ]~Û &Ë~¤VúUwÙ{Ëz—°yÇVóíLš.o×\³�pŽ]§’Xr+­ä|ck•¦ÜaÛ“�|¸¹qCŸ‡},š+ïŠk=/²ë­º•­g}‰ÀyWUsîiÖ\çY~›™uÿ)ÞðÞSª¾H§û†ûŽl²u;Lxò`ºü|¸,ÌÅ¿éÍ×E4~—ÙuÖÝJÖ´žÃà\§ªùï3ëNq¬¿M̸(ÞðîQª¾Hæû†÷Žì²u[<{2!ºø2¡³/ÿ§4w]Öz_ekmÔ­i ò¢;Æòí{kUú±zw�rʱ£‹cf¿/;CŸ.$[Í Û]~«7eÅy.±lu×cÅfÃY•LtúrÛZø·�xæ{.¼ìeÔüï‡öo]ïóx·:à<�QËx—"×Ò²ôûýt;ftqdÅ>&M°åAo¾£’ ¬õŽK/Žë­¯¯à÷—õo7â}‘×ûÜÎ1͸>ÿWÊ8· À¤åjwšl¸³uù–C•FU‘Ï >¤3Ç,Ç[£–Ëãºëkë¸<åý]͸§cð îgæÜ«äü[�HnÊÕnôÙqfàeÙLSâdÙðÓêC6¾C¸Ÿ9‡»²]7ï!ƒ‰‡Üi‡“5+ªÙû®Æ�•ñ¬|ëïÎÉášøþ¶5k~Eúùd» y¥–*M5�ÿ¿!|Ïn�ÄÝY.Ÿ�÷ÁÄÄíîµÄÉ–•Õì½ÕÆ�•ñ¨3o»7+„r[ãúØÕ­ùëå’¸SÍ,‘Ri­£ø§ù8éÏ”?ôÝ·Á³õ>Ùâ˜úž?ä7MG™}ÛÞ©çÙ0e}))‰—ussºû›Û­ÈÎã[{+66v,sbß-»=~Ï¨Žº6é¸ ¿<-¿%·™øáÉeÏèžÈ-Èínw¦Í’ÍOqó�Oú‹C¦’ ÙMÇZð�œ5šÌ‹®“u¸Ž<¼{.ÇÂÁÌÉ…ÿÍÇÊ6·È]ÆW‰ž?ïäÍé~¿¶nÑæú¼É,ÖvÇ4Ñezáè4Õ‡Ùnׯ8fÒ­nM÷I‹¸ÛEN=µÇÂÃËʆGÍ—Ê»ÈMÆO‰ý¿“7¦8fúÙ»C›jòä³YÚüÏE•뉡Óݲ›N¾á›8+5¹7]~.ßmy0[\|<<¼ªþÿÍvtì¹GÆï‹û||®¬áü¢.Åk›™‘e²Ç“‡Ÿ™«šÚzWÕ ¿ÃçâmÝ«äo"ò[“`] ñ㘼_ï8òWqÚÜÓ_°×ëë Óá̓› âõÌÍžÛe�'?/W5¿e}S ü#^ MØþDö‡žüÃW•N%ãž«?«:*X3¡ÄÚwGeñÙðù¶×e‡´ÇÆÈ—¯z‡o.n¿/")†Ø™1ݸ¶Ö³´MY4Õ„€þ~%/ÙÍ…ü¡zÇú‡ÎÜ5üB³óþÎmý#ÊÜ:üA¿³ÿþÍýoý#ÊÜüK¿±‡Ë?ßO¿å-Ô( ƒbªðñ?øÓxÕýÿúoõ‹ÇëÅoã?ã‡÷úéÿÖg¿ŒßŽßߨ¿Xy¼ß´³ãÇùsx•úýà ^Vw,á\”ù»�6Ÿ‘éö¼{�êµ»íû[�¦Þh÷88»M>çO´Å—gªÚë3¢Ÿ c­ØáO|3Á5—Å4WÝeöÖÚÖ�Ÿ¶Ôê÷Ú½–�y­×ît›�~f§q§Ûaãìu{m^ÇL=†·e¯ÌŽlLí~v$×Å42Ù|rÇ}mº•¶µ£ðmuZ½ö¯e£Þkuû�.ç_™ªÜiö¸xû^×W±Ç“a­Ùkó#›;_�‰5ñM ¶_±ß[n¥m­hþSÁL3cdÃF>DRA<Çd°Ï ¶V9aš))u’E%—VÛ­º•¥i_J«±ùŽø½ÞxÝòοÓís¼Vím¶NGYr ®ËÙÅÁ7óÇ“°ÏêM¶ŸêÏfÏU<ÓéfË’évºxëu%ÈÉÃØV(|µümm¼íßáWÕís|fí ¦LÝwº’ì½”|}-™ù½SÈvÓýY©�®Ç†YôÒåÉtû-LWWêädaç_d ¾Y¾7¶þ våyOÕí3|hìý¦LÝwº¾ì½Œ|#{,sçæõ_!ÚÍõf·?_2ϧ—*û¦Ùj¢º¿R|ŒLëì¬ñü5]ñ¿Ü¸]ÛѸ[ßûï‘ì­â¸RcdOEv<öensúoe·¥%‡aÆ3° ÈÏâ9sÝ|šÜl¬ ›r&Õß³Øñ�ȧ#ÑÎt÷—4|¦ëþ{g`uF5¶CNší‹¶œ¯ƒkñé6D×ÓˆÝfÏÁ%¬¹“K[59˜ØsÏ}$ÉÇÈ­¶Ò�#ðëå_Ëß )‡¡áÚÎ{ÕØÖÇ:�´nÙrn®Å¶[äºÞ%-›-ï’Ÿ^[í�Y— “Éõ21²+m´§Gü=ùSòïÃ:aè¸O6·žu~5±ÃN¤ìû¶\›‡ëñ­–ù.·‰ËfÃ{Âd¥g–úY¬Ëƒ I¤ú™Ù¶”wGãÃñyýà Z ^a’^?j¿Cë¬éŽòÙnw’ñ~7­»Ž`]ªêNȳ*NaÖÿrâÜ{ôn§ûöÜOUL™r-ÑM5}Õ–_ˆÿˆwÂ/ ­ÕqîãÉÚx«Ø™ÒÁ‰vaäSyÖY¹92EºÞØÔàb`kpãÇ¥²ädò?‹n­´–J[ïºQ,|ôxkßj´=¯—±ñ—°3¤‹üÀȦ߮23'–[l¦»´õ¸8ºÌ ( ²ÛæÉßáè`Žë½–Ý'¥.ºP>-|ñxqÞÖjô]­—±ñŸŸgK-ø<û#ô¿]dfO-öÙMhëpquºü(á¶ÛæÈßaè ŠêÖÚ_¥.ºjž~%�?,ðtzNÃìZø�ÛYñV� ò+CÁ)�ƒÇcÞn²ôñ ,êëøÌ6Ï…ƒ?"Êâûm–L4¶=e—Í ’w?‰òî'Ïxî«—ðnOÇyŸÞã×/IÊ8žë[ȸîçÙdÇ»'U»ÓäækvöÏ öVøe¾Ú_eiëëJ»AÆyGæšg)áÜ‹EË8Æëïz~GÆvúýö‡m‹õ/‹ï:;¯#+_Ÿ�õcºß|R_oºÚÓ×Ö•voŒòŽ3Í4ZÞSù‹–q�Î?Þ´ü�ŒíõûíÛ’_ ru»}^FV¿;’Åu¾ø¤¾ßuµ§¯­*�~× i«ÍÄÙk6X˜ùúíŽL9˜9ø9�Ù‘‰›…—�|˜ùX™Xò[|rYuÖ_eÔºÚÖ•¥^…÷_uúÀÆò×åÿÀÿíÚj¹ßpàsÞÄÖ_&<�MÓ_qì>uv>u¸ºÝÍø[N#Ã6Zû«t“covºÌŠÅÕŠ9/­–]Î)>Uü'ñ2Ýž³œöÆ4çÚËä‚^¬ê_¹sÎsv>Tx¹zݵ¸yø¼cˆlpýõ–ø7›=d·Eek²]í¶îuyEò§áO‰Öìõœãµðy§=ÖÝ$uwS}Ï�ó{3`ʳ+]¶¦~7â; Oud¾ ÞË[5Ñ[ZÇm÷VÛnæ—›Ÿ/?ÿø[¬^ÿïî1dj,­–ôW\äcvwåì%ÒÛ¿Õësx‡*IøE»Ýd‘ß…°äóèôóVXé\Ë}öúÄ«ÎoÄå·”ØÛŽ Ó´ÿ‚çPl)&&NÝegv¯$×ßf5%Çä}Ÿn>¯/W‰<¸÷]÷m.¦µ‚{ñ²§Íе­ÑpóCç_Ê?$ñöÜ'¨mÿƒ_UgRLYñø~ß#;´y ÔŠÛâÞö=°kgÔbä_ oû¾�[%±Ë~>FNdU­k3¾t|¡òKmºŽßø6õ^u$ÅŸ‡íò3{?�`ßlVÉ÷±mƒ]>§"økÝô˜ÚÛé·ãÏ‘™kZ“äcñPùgä¬\‡¬ü6ÓåxÓÙ²çë¯çx{8wIòý-×r=m“ÝÌ1m·EÓ‘ntû »±xÜyýF×דM�%ñÝÁ{ï¾[ï’Kî’I.ºù$¾êß}÷ßZÝu÷Ýuku×]u}kZýµ«ˆß|—Ý$—]|—Ýu÷ß}Õºûﺵºë®ºêÖ뮺êúÖµûkW¯¾ù/¾I/ºI$ºëï¾û«u÷ßuku×ßuÕ­×]uÕõ­köÖ¨²ìö{-ÖËa¹Üì3¶Û}¶v^ÏkµÙåÏŸ²Ùì³ç“+;a°ÎÊ’\¬ÜìÜ©n’Ydºé$’êÝukZÖ¯òÿ/òü Î|küÒy àfV—¯94™ýÙã$y3Ù•Õ{ÍŸ³y Ϻ˧Øõ?'Ì�*n=÷l›~ñv–©¤ËºL�lx™YWçÙן�/—þøð“+OÀy<™ýÇã•™7Ù“ÖÛ�•mÞðœ|ºÙIóú»‘æY‘&’Üim¦Et³ûôÙWÖjYfNL™ÖõÓãÛåë½ü'ÊÔp>M&qxéfEñäu¶çe[wœ/.¶}|þ¯äy–dˤ·[~½tÓûôùW]5,³#&üÛdñ#ø‹üŒøý§é~ú‹’y3â^5üwO¯ã›=åÙ=±ÒoWf.š±ôÇ!ÞåÓ gÆ5N?¶k¦Ãã–=æ]âöWDó�*Ôý<[7Ú9.�˜p�¦M³úhù¯’[óô:_‹5#­ô»2È«6$Ùõ²k¦¯ã'•ýåï^böOEsŒSªúx¶o´r]0á;<«f­4|×�_-ùú�/Å–‘Öú]�™dU›lŒzÙ-Ö1ø³åÇŽžku6·»|bíN7Ûy°ÌŸS‘²ÑÉ“�´ã¼ƒ \¬Þ/̸Î×’pÞQ‰‡��‘~¿g‰‹•\L˜2m²ì|ˆ%“hØÖÇ5‡Ì¿¸‡™~4vÇŽ|ÎXð0»ŽI�¤ä5Ä®lÜG™jç‡qÂùv>5™sd×�r\l‰qìž¾c[.5×Û×µËËoø§–Þ;ö‡@rùlÁÁçÜzL]Nö¸·fKŹfºxvÜC•A‹N¹WqþIƒ�“&=³ÃL¸,“ûé·µÏËOx¯–ž<öArélÂÂç¼zL]Nö¸×fKŹfºhvÜC•A�fF¹7h96D�[44Ë‚É1ムË{[¼Àñ‡¯üÏñ�ºü]ìèý8sp]ŸÈÙY‹fvWÞRè6¼;œjðåš2w| ™kuû¬ %º�Ý›�$¥l÷Rµpö�Yó^™ì~qÔÝ�¤›Žsιå;®Ët“ÉEuûÝ|ÚìøbËÅ’l<üK¦‚·Á“òcäÃu²Å}ñßmÕ­s³:ã˜ôÿasN¬ì-<Ü›õÿ%Ûñ>Q¨šøæ®ãI›.e°dÁt˜ÙØRÉÔÇɆëàÉ‚û%Šë£¾Û«[WeõÏ0êÁæ�]ØytÛ€r]¿äú‰¯Žo¹n4™²àæYLI�›‡$�ûàȆëàÉ‚ë%Žë£¾Û«No’>=ö‡Š=ïÚž9÷6ŠN=Ù}AÌ6œ;“áV Œ89²a_lÚ¾GÇåÚëõYÛ>#Ëô™Ûm.}ØñY±ÔæãeGO§5•¯ƒxwˆa Žþ#~sy'ŠQñüªÌÝs_à¿KÄ9Ô0äny§F`ß%öAØ�Ç>Ï™u¦ò[îÀ�êlµ8–×ô}¹ÅºùüX|Òo¼cƒ�øýäîVã˜x÷�v.ŸˆóˆbÈÜrþ–×Ý}ÑÉ&$1ͳåÝs®¬–úaGõvZ¬[kn™G½ /‹O™í÷Œ°ñ¾€òo+qËü}Ç»OĹ´0ämùwL`Ý}ÖC‹&$1Ͳå½w¯¬–ÿ¨£ú›V-µ·܈ã‡_Y|þ$ <\ã|oÄ/>öÜ‹“tƒ_¤éŽûÆÄÙòŽaÒZljÛ‹_óíVyÛþmÔz¼?oè\œ²w\f(GÇ�°ÖIƒ’q½Ø\µ¸gìN´å¼{�ð^Y�nÏ�òÎ+µÄÝhw85’H.›cƒ,ØòÖ ˜d†[=}ðÏñÉKo²ëi3^Îxoeñ=;ëîQ¢æœ3“àÙ²ãü£Œìñ7=¾÷ß×ÁØàË64ôŽx¯ŽJRïtrÙu—ÒÛíºÚL³„sžÙ\OEÎúû”h¹§ äø6l¸ÿ'ã[–Y•‘ù9Õ¶Kp±òkEaæÿŸß&�àg»�åÃÏ;¯u®—+…ô·c ›¬˜î¶¶co¹žu‘åY¸…ٖٓ‘ù9Õ¶Kp±ò~ŽEaå·É¯Ë—Š_½s—·íÞE/îÍ×›uÕ7ñM–=;°æ›/'S©Øì¯úÐuï]_·ÃÈ·/‘ìâú‡2Í|=„k¦¯sÊï+»§Ìþèä�çÞ¼–»îY½­¸zÍf³âqnÅñfÈ—OÂxFšlœºi8¶–™RV(«$¹Ë.VTÙ™ÀóÉß'»ƒËÎÞä=ÓÝ|Š»¾S»­¸zív³âqžÆñfÈ—SÃxfžlœ¿Ð¼gO÷™+U’Yò'–\¬©²3222%‚G“~Nvÿ—]»È;£ºy w|£u[q5Úì;fÅã\;�âÍ<ºžÃuäe~†ãZ¼ÉX㬒Ï<òK“•.F\ùÕËçwž^C|‰wÞ÷¿üˆä±ì·™‘×UÄx~–™x|¬¸|3Ë6»†p=&V^múÍ6%òÝ$²K,Ù™ùWÉ“•4ÓÉ|•Öö¾µù¦@ ,~_«ÜÝ÷¹ó°tåõ§�9_£zëô†=$Àä韉F&n>]�qÇs>ûw­b—i°ÖÏ Õºél‡÷…•ížìÜy]δ”ÊëÞ‰Èý×ÿ~‚—áï;Ÿa‹ ðçAØócåÙ×\{3ï·zÖ9qöyúÙ⺷C}-™oá1øá›°;g˜üŒö‡¾îÓ7múÓÇKvO;ÞÞßj~çØ÷Ì}æ%ù8]qÁv÷j ¦n»7Y™±ä’Í�4{ ~œèDLí`Ävg^q^Ýë®yÕ|ç_úW…öG䜕ë©%a¿3�r­F^�o3Ò—]�=ø9·ý9m§º;ý.·í¥?°¸'íÍ:×™àSiÄ{ŠòÉõÕ¾±×7AɵYZm¶5’Ò•º%Á̾–_OβïK©öÒ�!Ø<�v‡æ�mÌð)´â<ÿŠïøo&×Vï§\Ý%Õåiö¸öKJVèe“ 2úY}?:˽.§ÛJ<ß3áÜW±8+ëþuÇõ\³„s®5½áÜÇ‹opâØi9/äÚ¼­'!ãûœ ‹oƒ;U¹ÔgM�‘ ô­’Ã%ÖÖž•U‰å‡Ž\ÇÄŸ"ûoÇnuK¥Þõ‡-ÍÒC´¬u‡’qÜ‹!Úq[‰�õg®>-â¹ø{(cºû¯Š<šY¥öÝJV…å�ܳŞÿí>‚æ~éw=oÊrõlë ¸Ñò=‘[N'ʱ±íŸ&˜Øœ§‹çáì#в]|6dÒ;ý/¶êRµ(:•ø·ßݥМÏÝ.ã®yN^¢ •a¦4|‡�äYÏŠrœl{fÈû¾'(ãØ™ñÅ[î¾r)þ—Ûu)NGÈ�†|»À/1ûÇÅŽW~v~?]rɯ༟:ã¿›õw!Š=ÿ[sÝ�eºúçn¸ŽÃ»q®’,­™Xuº²c_JkË0+J€Ñà7Èy|{w}—ÔÙöm¸Îö˜ÞÒê½ÎVEœC²¸ÞD’ʼn°²*Kv£’éþó5ú�ÌÝ•®ši-­³bdfbdžuw?�ݱgbu~e›^9»¦»³:Ço•<|S±xî$òK.u"¶jêy¦™3_©ÜCù:饒ÚÛ>$ùx™;¡àÿœýËà�kÙØ}a›nÓŽnþã¯ì®³ÛdÏìN=‰<’Ç‹�Hí–ºžEª¦D×ê¶ðÇvN¾Yd¶¶Í‹>^&OP>-þV|†ø³îŠóÞ­–>gÕœ¿'U‰ÝÈ69xœS±´94­r°2 ·'ø#Øzœ)g·O¾‹&¸rK[21³pïŸk üù ñÛÏ›tß&ƒ–k1`þØ]G¾ËÅÇì>¼Ù]l6Éf×Smô¿iÇ2fš”ÁÝaÛ&»6ž±Òøò¢ÉÆ‚yùÛÐ^suä\˨y$0òMf>=9çWnò1±¹ïØËd~èöÚª_ïØh²e¿Ó o‰Iuùž—Yl–äE‘� ï|1ó¯ üãëØ¹�QòHaäºÜlzs¾¯Ýdccóα’ÈýñíuT¾—ì4YÉ釷ŤšüÏK¬¶ûr"ÈdžÑ�þB¼VùêˆûcÆÅÆäðkñ´tç¼snÐê}Ö÷#'ŽvGûÖ\Úlë¦×æA�›�6n—i&MúÜìØ"ºVò·5¹mØj/˜Þqxéà¯[]ØýÿÍ"ÓS>Í„\7„j-‡iØ=‰µ×cÙ<úŽÆ«“�&mñ]<6dfäI�«À»"½åAIc­Ú³å—™]ág]ÉØ}çÌ"ÓÇ—||K‡j­‡eÎùþÓ_sK¨áÜv¹ÒgÍeóÃdùSÉ�­Á»"*åäÁd–Ý]ZòÃÌž„ð¿¯oì.òåñj,̳>>'Ãõvòç\ûi�“K¨âz¹òfËeÓCdÙsÉ�­Â»"*ådÁl–Ý]Nó3Í¿¼ém÷zù/ØZÞŵxù1è41K‰™Îû#‘Ç/Âá=gÄïËÅÎ弯e%öÒ‘E[1p ­ù™óâ`A“— |Ÿ"ÿ'=óò)Ø4ÚsŒ‹¸WPq½–FGZtž�e““ƸÌuûÎ>6ï}“u˜´åüòm~MÑdmf‚m¶I#ă Hóÿä{»<ûçVgó ®áÝMÇ69=wÓºmŽFFƒ�Ò´ÈÆƒy¿ÉºÌjr¾o&¿"è¥ÙK VÇd’G‹4RIÐTóóäkºüøçVì9„×pþ§ã» ò:ó§´Û Œ�¥i‘�ï}‘u˜Ôå\Ú\ ‹¢›e,1[’I,8ÐÉ|wVIóóEÞŸ*�‹‰ªš Þªñ[¯öòæõ?Dal~ñ÷½µ‘æ`Ó´;[c�[1ù_eçë³eÇÆ²Ú~ŒãšùoÄ×ÙY²6›¯7ôsÝŰ¿? _ýÞñ]ÿÆ)+OÃÿvçü[?íú•gá“ÿ»kþ-ßöûNcð^ÿîIâwÿ�2_‰Z%XœÀP<ôñOI毉ÝÃã¾Öì,=§2ã—eð]îm‘Ò>5Øüs"-÷Ýß•÷†^¾>C�>Êìhþñ6§#* +O­Vªùµã&—Ì{g ö·âaçó=vG ÝåÛO§Çyþ‚x·œ'u$öâååcàAÈp ‹?îöÒyõ“dÁmiIjÕo5üfÒù}ãkô.ÖüLLþ_ÇîÈáÛ¬»iôøï?ÐÏï…n¯žÜl¼¬|7ø0ÅŸ÷{i4úÙ²`¶´¤µj—œ~'ðÏ9À=wçÜÓ«y§ì^ºäÛŽÎ8nãÅùFƒ2L ¾—o�%%ÆÌÃÉŠ´­·[ZVÛ캗G,w]eöÝe×[_YÁ9ß1ëcÇ;¯¹Ûˆó^#¶ÅÞq¾I£Ê¿i¨Úaßïƒ'{?-+öÛ$wRèåŽë£’Û¬ºëkêø79ægÌ8ç?à�kÄy§Úâîøß$Òe_…´Ôm0¯÷Á•‹=Ÿù6ßeÔº9cºë/¶ë.ºÚånŽïÚñ«¶¸{tO<ÞõŸmužö.EÂù§–6­…�χ•�‘‹™^¯u¢ÝjòçÀÚjóàÊÖmµ™SáfÁ>$óC|åþ,¾yºÏÉÜN+ѾXl´=Qäeöcé4üß"¸ÚN±î\û-¶,) ɾèµüŸíý=²k&º=n~o¥5ÒG&L:Èf‡ñ£óm×>Ibq®™òwa£êï n¦6—QËr/ÇÓõÇofú[øy2Ý ç[Kÿ2M\×Yƒ�—[GÉl™ë ™ÏƯÍ�]y!‹Æúkɽ†“«ûþêciµ<·"ì}?\öæo¥�á߉“%ÑápÎs´»ó/ÖMu˜Ùu§èù-¿"=t6J|=þ"…ùÂÐôŸ‘\OÇŸ0kv›O­Ðfì®Õõ�|lö3C¬†n¡Ün²¥»YÌs6òÅx†Ã*M”µÊ†º¹ö–Û™L»òï¢HÀúü¨|çõgˆx\Ÿ¤üoÎÐöß”K›¡ÛæÃ%»N»é<èm¬9Ù§3êâòŽs­È­a�C�%cÃʲJìå†è)ƒ—¿’ÿš.²ñG�ôÿ@åé;SÉäÍÓm'ŠKv< §³!¶è2çåy˜÷}Ḛ2}b�E�'¦,ñÉ]Œ°V+q2¸aòYó;Ö~*br.Ÿè,½'iù™š]¤ñInÇ�ôþlVÖ©ùNf=ÕÇä\Ç"µŠ=4•¦.DrWc,Cn&T{>`?�ßúýïPuExçù‘>«*Ì>ºÃÚ]—Àº{a“°êöÝë¸Ñå×�™Þ™vñ,,Œ}öv-–W"mN>f|�LìÞÎìççܧ´{S–î¹×`ó]¬ÛžOʹ]Ù›M®|¶Ù·I¥‘cââcEØÐÙ6&4Qà qÅ–[ ÈìŽwÛüë“vgfò­Ç5çœÇg.ã’r}îMr¶[<é,²++}Ô¥�ãbbbÃØÐÙ6&4QÁ qÅ–[ ÎÇì~uÛÜã“vWfr�¿5ç|Ãg.ß’rmîMr¶;<Ù-²+k}Þ–C�‹‰�pccÃdxø¸ÑG 6GvYmg½íß¿äßmóŽöï®}½ìÞÙìmÅû¾_̹ ˜ÿ}ØåÒpñ1q0°1ðµ=›[�·Y¯ÇÅÖêõøðâbA41Eg„x—‰b@�>(¾x9¿Œâ~>ye™¹ìo1«‹¡ã�镹ìN•ÖÖë Á‚KiõóùÏ[é­üßÑõ¥û]^æà]<8øúˤñ‡óķƟ‘®EË»/§é“�ƒ×^IÍn×vWTbË%˜¹:Ò‚;s9guÖ5·Ó'e 2ù6›Ù6?ÓÛbM‡¦q<ŸðŽÓá¼{°úß•è9Çåšè¶Üo•ñ�ž.ãEº×ËuñÓ'a…$¸óR9£¾9-õ÷Å-—G}-¾Û­¤Ì¸W6áý‘Å4<ë€rm'2἟_×�r~9²ÅÛéwù«u¶dàl0ä—{)%—Yu)wºÉ-ºË©K­­)2Ž͸dqM:à›IÌxo'×Å´ãÜ›Žl±vÚ]¾µºÛrpsðä—k-’ˬ¾”¯º9,ºË©K­­)`å–L5‹†m6;<û-Žè¸Î,W],µ¤S‘ŠFIiñïÈNÚñs¶¸ŸvtŸ,Íáüû‡æÓ#;µ—eƒ-m·eÇù¶ë©�ºã›¬jVÌ9©X掿éo¶Û­Ì] ß}§ã?iñŽãéÎO—Åy¿ËúعPúË�´À–¶Óc ßë«u°n8öâ ~–V,¾¶IeiZVÛí²ûsC÷ÇhøÕÚ²àìðe­¶ì4;í}×[�¸ãûŒ{~–V,¾¶Ie}ií¾Û/·k<.óGÈ<€â>HxßË¿ƒ<ëŒýMvÛS±�'?…v7 ÏÉÃÈä]mÙÇ:ëoäü"äž]}n¿o©¶9mȲ\xâÏÉ�—ÇWÊÇIùÙÇp8ÖnF·¬¼‹×aVœ—©¶yöÛ »ëær^°Ï˺۹7š8ä–\?Zí5TŽKr#¾ aÍÉ�GÇoʧJyÓǰ8ÎnF»¬ü‰×áWøIÔû<ûm‹�_‡‹õó9XçåÝmÜ—A,qÉ,˜~µÚjéöäG|Å›‘h_ÅoÌÇ‹¿)¼3'�å]ÖBñ=.Ó²|væl)ùN¯K`ÇÎå<kdx1vg\âí¥û­û<\||¼ $Ç¦Ï _vn¹|uQÕ^@”ò®3Áøæï˜sNC¤â\O�k2÷\‹“rM¦“A¢Ô`CvFvÓo·ÙO��®×âAeo’i¤²;-§­kGÄ䜗ŽðÝç•òíöŸ‹ñ~;­ËÜòGÈvXzm“S¯†üœížÛm±›]¯ÃÇŽëåši,Ž;)ZÝZQñy'$ã¼;A¹å|·{§âücŽër÷þEÈ6Xz}“S� ùÛ=¶×a6>¿ .¾Y¥¾Èì¶•­kJ>?!ä:% Þr¾W¼ÓñŽ/Æ4û>CÉ9'!Ùáit{A¥ÂŸe¸Þo7)ñµÚ�>§]�&FNND‘Ã1Ý}÷[mµ­!Aòóó½™ä·�xÏá~ãwÇ:_an›³;’�fhy?mk§²|,Î%Ä12lÇÛñ~´ØcI_¿Ï=˜û]Õ—}Úøñpi‘Æß*ß6Y�݃¼ñ×ÃýÆßAÔy±fj{·­‡+MÈ»C {'ÃËâ¼G&Èvœo¯31äÿVäËf>ÏqK¾ï[1°­žÍŒ?¾T¾ks;· y㿈[�¾ƒ©3cÌÔö/n[N›‘v~öO‡—Ÿ–>E�ìøç^æcÉþ¬É–Ì}žÞ•û½lÆÂ¤öl ‡ó…ø•3¹¦G-ñ#ãsšåkxw!å|»{©ã§_�g­óæìvQÔûn¾úQïú§«ùÏvöw^ôçXè¥äý‹ÚœÓŒõïãÐäáa_¹åœ¿q‡¡Ð뫟²ÈÃÖk¢ÊÙçGl™93C��ek$²Y·]K?~<|:ã¾ øŸÖ]©ûŽ_%Öà »S‘`]õ`å�¯Èññr9žê «ð5yzœ\˜cÖꮞò,Ókñ#—ÖK.º¶Cøâoð³Æ.ºèÍOܲ·úÜáer Kâå]�¿ƒ#—î¢É»[‘•¬ƒ&+5ú˧†Ìˆõ8‘Kë|uº¶<ø!âð»Æ>ºèíOܲ·úÜádò /Ï‹”önþlŽ]¹‹"ì-~FN¶ ˜¬×ënš2,ÔàâÇ'­öVêÜGññá—ðÄ.—ñcƒ]‡°·®øÄ5朳 L9ÿfï/»sØœï&ŒŒÜè£ä\§3"L,iò2nÖë-ÅÁ²JË)»MÃn s€üüJ_³› ùBõ�õ�¸kø„ÿgçýœ:ÛúG•¸uøƒgÿý›úßúG•¸?ø—c–¾ž=Ê[¨P A…ÅUà ÿâñ¦ñ«ûÿôßëŽ3×ŠßÆÇïõÓÿ¬.:Îþ-¿ÿ¿¿Q~°8óy¾0?igÇ�òæñ+õûÀ¼¬îY¹(†ü€è>®ò{¨y·Gw'‡”uÿ<Õ]¬Ûà]u ÍÚ9,ÉÖo4™Ô²ù5|ƒE±†,¼,«)[¡ÈŠÛ½.§­µÄýãÒ]mägUóší®=&àœãWv·o¯’¿K'K$³']·ÔæRÛ¥Öï4›bÊÂɳóàÉŠËéëéé\QÞ%ÖþEõg0é¾Ùãðr^ Íõ—kvø]ô²qä²K2uÛ}Ne-ºMnóK°†,¬<›?> ˆ¬ºž¾•¥q_wô�Ty#ÔÜ÷¢û˃i{#©û7A“ƹ¯ ßÙ‘÷¶²{âȆX2ðgÃÚéwZ�†<9ºÝžF.ËU±Æƒ3x2 †k+gùøøí¿�ñËë>ydÜ‹�òçî:ƒµðõùšÄâ°dYe޾恵™qêdÃïSYd“yc’ËæÃÉÃÊȯ[ÏÏ;CÀžçÉë¾gl܃ƒò fíú£³±°'ÅÒó¾3E‘ÉeÕº—ã빦D1n5ŸVKðå–)-ºL\œ\‰ëãóçÁ.Ïð3¹²zó™ÒmÿäÍÛõWfã`O‹¥ç\j ‹,¾Ë½Ô¿]Ë´Ȇ-ƳêÉ~²Å%·I‹‘‹‘5Rÿ0¿Ý—ñ]ä=Ü3+'qÎ|~ìJì7ý Û¹zéc®ãEdÖdð^g›�¯ÂÑEÚ\2/¥nÊ,JYf,ØÙñE�fUqq´ £ r,™:{Èžúñóm6ó£{“³:“g—XHO×ÜÓ�qh¶ÖAÔ‹u‡©ÏÆÂÝb[}=~Ž\sEZþ[Y©<�ï.…Ù˸é^Ýì^­ÏÊ’3îàÜ¿yÇq¶¿wº—Eë_­Íƒ_»Æ²´§ô,¸¦Š¿»mYw©»û¼:g.ã¥ûo±:»?&ø¯Î¿ƒòíçÇÚRÒèâÜ`ës`ÀÜã[ZSúTSE_O¶ÖÜxÇç·™Þìq3¼aò[·z{ {'&Ÿˆq®]Ÿ?Zn÷Òk몿gËz§uvÓ¬ù¦GÜ=±Óô¾§6Û~œwR”º8ëod:oñ|‰õ½ŸtçÙ�GßxWÉ�K§ì.¾ƒŽoqq`ô¶HuÛ.©Îëý}&ÈŽž•›7>ÿwç}¿m+Ö®¤üA~{õý–bs¹ú¯¼0®ž Ë“Îx4\s$Uô“]±ëÞ«ŽYlû>¶^¿:êV”­iZúúõ›©'ž�e¸œç#«;» ²Áõ29Ï‹�oaÅŠ¾—Á�±ë<Þ­²Ylû+.^mÞ´õ­+__^øxíø¸~BúÎÎ5§ï¾»è�&8þ³#&þI¾Êãû. ížO�<—I òn™^¬Ñ_‡JÒË/‹ƒKŸK¯õ¾½Aë_ÅAÖ¹ÙXýÁâ/9âØ¶Ç‰fÃoÖ½› ç¹Ëë™ù8\w”q®·Žëo¾H`¿i%i_lwK_¶WHºïñ0u¾tÐCÛ>,s~/�m˜¶elºï°ô<îi¤õŠÜ̈4œ—A×6cYéï¾(nØK_²Û.—í­ôèÿ^~%ž¸Í›×ñk›qœ{lų/e×�‡¢çSK'ô;sr1ôœ“A×vcÙéï¾(nØK_ÉeÒþ[ÝwêÆ9âvëQ�7~x‹ä?Zï£Ê­šÝoPrN¶îýF^¶ž™Û¾g¶ñó3_•[½iôc×äÙéO_«öúSqxŸâNøäärÛâ 8—RJÖ~Y֜ȬöE–Òêp^kÍ'­f¾úÇo¥•¥/²¾ïm¾Û®Û>)ø„þ?9ÖÛ¸¯xp:WêzßÊúÛ2Û}‘G%¾êpnWÌïþ‹}õŽßJWÒû+îöÛíºí±â¿ˆ7ãÿ�Ým»íßÁiwÔõ¿•u¾e¶{"ŽK}Ôᯘß_«}õŽßJWÒû+îô·ÛuÝëßÄýðïÍ4pí¹'}sî£Ï–‘Öþ3Ø] Ûû-æ?¿ &êM‘Õ£Ðh –ëiédwäÛ|·~m”ºï±ûïùÖø§ŽËä»Ë um²Û¯º–u~K}im+uidqõ]òIuiO²Ûi[«_²”õ~Ûþj¾1ì²ëîòƒ¶Ùm×V–ugxÉ}im+ZÒØãë+¤¾ïJ}”¶•­köRž¯ÝÍ'Ædv_}ÞOkëm–Ý}igWwt—Ö–Òµ­,Ž>´ºI.ô§Ùm´­k_²”õ{ýŸÎïÄV§[°ÚåyÓÔâë0rö1k!æ{­”�aA&LÑëôÚn+Ÿ·Ûg_u¤XذM“<•¥‘G}÷[mqÿ.üAßoï:nëåý�7Ñ–OÑüG¦{W 3ßÑ[f?¿žq^�õ§¤•ºÊý§JYwºëkí¥Þ+“|í|jhq+‘ªînMÍfúRI÷3Ô]¥‰—[ìº:[ /æ\S‰`ýY©}km~·²”²¾ë­¯¶—xŽMó«ñ±¡ÅûÆ«¹9?5›éI'ÜxÏQö†&WºË£¶Ø}üËŠñ,/«5/­m¯ÖöR–WÝuµöÒì;'âPøW²Ëï·Ì»åºÛnºØ£ñ×ÊÚ_%iJÖ–YYz68éuÕû)îºÛ}-iF¨öâ‚ðÛM¯ÉþÖý#äO;ÞE5lƃ‘àu÷ã™Q[%öVoÓpsngº‚·Ûm/²Ûµ­mºžïmÞ¶ÓXyÇâDñQ‡—oêþæ»xeºÌX÷zÞ Â¸þdvÉ%Ÿ[ô½œÓ”nqí’Ûi}”»UîöÝOu-º•¶šÇÎ?<«xPwß5ÛÅ-ÖcG»×p^Çòã¶Kìúߥìæ\£s/¶Ú_e·j½kmÔ÷{nõ¶š™Øÿ‹kâ÷…ï3´ÜOŠùWÛØ¸ñd]‰Ê¸?Vp½/ØMnn$1Ùogv—_ó k2¡Å�&—I¦§·&:]JMIa‹�Áø£<‘ä7O�ÑÞ:uWàω6539þ÷•vÖÿy,öG±×äjnêÝ<ð×Öû"É×çEK½)Ô¶•÷hk~%ÿÞVH:gÇÞ«ëœY1æ‚ì¾y¾äÝ¥¸Š[ì­‘ç`K¨þÖ:¬Y⺾ûcÈÄÍŠ—zRê_JW×A»Wñ$÷öö²ÁÓ^?õg\âË�,Ëç{ÞKÚ;ˆe¾ÊÙfv ºŸíi©Æž:×ßlyy±RïJ]KéJúò‹»¿7ní0rp|oð£®86ÎÎE0ù?wv‡'í\—�"¶âÝ“Á8‹¦ò4›œ¼Ki|”·‘ì Ç’ïe+5-÷߯">Küëò£3WÝ>Jö.÷Œì1¾çŸÂ8ön_pŽ%·Û$pm¸O_aqŽ3¼úWÙJÛ&v6LÞ´õ­õ¯­\ˆï¿‘6<—ÆËÕöï�üûoÆóñåÂÎáœw/€ð�†—Û%16üC�áñÍúÈ«e)mùðeKOO¶úÖµ­yß_"^jy-�—¬íß!yîߎgã߉›Ãxö^'áøw_l–ãmx�Ä㺠å±VÊ{oσ&o³í¾µ­k^ ùKó‘ò�åÔ7dùaÏ8ŸØMÉ#»®ºN¸=%Ä®Ñò¬o¸m8Žòþ¸ÆÐrŽÀ⟣½qãÄå-ç¶+¯õºë¤–ëô]¥-,r`9߀O‹n_ãÌò÷½âÝñžÜí~%à�[<› Tü¬ö™xI¶½ƒ­ºìzO̹~N·h5“Çu4¸GtÕûöL¸úù¥ü|krϸÞw•Û�¸ã}¯Ú\^í ëiåÏÖäp®·Øå`m$ÏçzË®‚Ûù‡+Ê×cϺ{/®Ÿ (þµi›‘>> Ð>¾6y_Ž©éÈò°¹ŸO͸\râêìínQ¶‚,ìû6PålxæPkãý•6óy-¤:�ÂOàš~#¯ŽIùNž?ú‹E.NûŒkõ¶j˱âx6C©áý¯ô,²,ܼÎ-ÐÓn/¥ro¦«îR8qµùsV/ˆãú^Q¦‡În©ÑÉ‘¾ã8:Î=ä§Y��tÛ>)‡d:Î+ÚE"ÌÊËâ‘ÒNÚú}æúj~å7¶ mvL·F çûÀy6ž8ú¯I&F÷�`ëx÷�Z­n<M²âØvC¬â½�tQÛ^V_Ž�êv·ÓïÓU÷9½°ãkòe¬;¿ïÅ%;“¬,ùè®)�¥Òš 5~KhøïËÈÛöKëíŽÝ_ie×S$”ÉÜô|6ßfÓ&l)%“ˆMtùYÐañÜx$†’#h’+àoO…?#>TxÉïÛôW;ºœOe›LÞWÔ¼¾Ì­ÿVòékf)»Ör-Ê+n­—K�¸Ôdækó#¶úV•º9.¥+OD‚õ;mVÿW­Þh¶zýÖ—qƒ‰´Ôn59¸Û^×YŸ™X;nÃI±3°sq¥¶H¦Šû£’;©uµ­+J½CÑ=è5Èÿ=¼<ñ'&Nÿò¯x.Û˜õ¯ ·kw%ìiíËŠi±fÇëŽ'óœË…=°VŸzû…1,º¶Òùm÷[ë«>@ùµâ—‹X³ËÞ�çÀø>Ê!’Þ'vÎí÷>É�"É/‚|N¾ã0îy¦NŸOÒ¹à×ÊÖß|–û©ë«^@y±âŸ‹˜³ËÞ]åÁ8NÊ a¾œN»:ï¹îE™É$bð5ã™äbIHý>ñnݬ­ÖÒù-÷[ë«þMy­âg†œjîUå�]_ÒØj6›ÍV¯˜r|(y—*×ék»JðN½Á»;žvfùQÛ~.�[°Ê÷Im)kZQ_:¿¯av߯|ợãÙ±I…—Ý]…�©Øö†V<–áÖZðî#�>ã‰ð{ýÑäÁ÷̹÷YrãÍd°Y®ÊŽÛ­�šˆ{œól=§ðÉgu~“*Ëñ2{‹Ÿbê¶‹“ ÕŬµâ?«ÉØÖ,\ÍÖ$¹}QÓ{+6ÚÈqöA®ÍÁÛòï«/ÝæÈÅÉãpMHòl‹×'å—›r×.æ\‡wË9_#Ùen9%ä›\íæÿ{¶Î–éóv{}¾Î|�†Ëa—5õ¾I¦’ù/º¾µ­j�§$ä¼�™oöü«—ï÷\«”r ü�®û‘ò=¦vï{ºÙåÉYr¶;m¾Ê|œýŽvL•­ÒK4—É}këZÕ¾IÉy1ßmùW.ßnyO'ßçämw¼‹‘m3w[ÝÖÏ.JË•±Úí¶Säçì3²d­n’Yd¾ûë_ZÖ¨[ö_göGsóžCÙÝ»Ïy‡gv7-ʃ7“ó®{ȶܳ–o²qp±u˜rmwÛ̼ݞoÜu˜0bÁmòVØ1 Ž(élqÙm>â>#€3Ÿ�^?õ>º,þmÙøtøæVxõ:M|qK�¾åüŒX2²1x÷ÑâägçIRËLl{érI[#»5øëÐ]…äÿtp‹êì³¹‡`nâÕâM—YcÕi5ÑG&fó“ïg‚,‰ñ´veñÇ,ßB ­Š9%­‘Ýš|wèNÁòw¹øFõ†yÜ¿ŸîâÕâM•Y¬Õé5ñÙ~fï“ogLJ"|mÓãÏ›™|qË/ЂêE’ÖÈîÛŸ<1íï?<�ë/ºgY‘? ç[xää|šìJdé:Û¯µÒÃ75ì¾S|¹zìh´¿êçfbÁ- ~+øÛ×¾"ôZxõÖ8×Çźç�Å­ý!‘g·cÉw¹sK²äü»qé$´ý-ÊyfNtöYwцéþ”4²ã²Û"¼jñ÷�ø³ÑÝuÐýo�$|_¯´1k,ÎɶÚlyã"Yv�”íëmn²»nO¾ËÈÎȶÏHc’zǶEe–[cç�~?pOú?®ú#®qï�ŒuþŠ-m¹ÙÛnÃ�n2%—aȹNÚ¶ÝuŸ¥y6û/#6{l­"ŠùëVÙ–YmÀž3øëÕþ%tTøÝÓ_Ð]kÓü?]Ä8Þ4±kcÙl~íõ2wŸ‘K¨×j5ÛaÌ·ùY[}Ölx°}ÿm�‘“u”º[¨Ø&sg6sEßñ"x /huwów­tuÉæÝ-­�Šw>&»Ys7ÝC—Ÿ|Ú>[$vfRËæë>A°šÜšÅ‰$òj¶ÒÏ‘5˜Ú«)HÜ~ Ïeì¾²Ñù�ךk²y§Në£ã=·‰�DÙ{ΦÉÎ’}O&º(òn²éºã}°š¹�²ßªÚM>DÖãëc¶‘¾ü@ÞKÙ=i£ó¯t×dó>Ÿ×ÇÆ»k ¥ÊÝõFNt“êy5ñY“[n›®·Ùòýâ±ã]-ú½œÓäMn>¶ÊR(?Š{ãBO$ülÖyËÕº[²û“ľ;›�ÙXxQäÍŸÌüh“?+s¼žÈ1u{ 23zW{±Éä6Vùð0ñ´Ûùç’icĉô7�èW÷ý]Ú½‘Ò|ï�vRsnG×��Å3+›ÇùgÙäjwùdŠLl˜­ÈǾÚd`l0æ“/jI�—�,�Í‘_}—{¾´ìþÃé®o ìž«æ\ƒ�s¾/—÷Ý'ã;õ›L.Žø2!ú°ÝK2°3ñe¾ ¬Y­“/Káš9"¾û.÷]kÙÝ…ÓœÛCØýYÌyç\c/ïš>OÆv3ë6˜2_ðdCõ º–äàgâË|X³[&6^4—Ã5—Å}öW5xûäoyø§Ú\wº¼uíYÔ��Ų`›WÊ8–ÃîÒäcE›‡°›GÈ5y1åi9oÚdká¦ÃMµÆÍÔ졳éeãMkee‡àßâcŦŸ€ùéÂ2îβL=|}ûÔúl{àÈŠûµ¸uÙv'XC&=ØÒAO½ffçñ»¤¤”­�âé,­+uòƒðÃñáWQÁ¼Ýá¹VgYv�åÖ˜eÅÉŽêàâݲçÝo ðK‡$4¦F^fo¬Ô–·[6š?oºé>xgøˆð«‰©ámpì«3¬»ÎðëL2ãdÇup±nØóÞ¹†ø$Ä’*R|¬¼Î?Y©-n¶,}<~ß[§ ñãø¶ú«•j´Ý{ò=Ász»˜ãÛ�ƒÿN ã›nQÖ[È¢‹k&FÛžuž»nÂvVË…nãðò\mŽvTÒÛ‡¨ÆŽÈ’oè-|hòŸIfûǾîëÎÔÆýnÓ3Yƹ-ܯGƒ~MØ‘ÍÊ8Fup¹—ºL‹}¶Ù³ÀÄ’ïuµ¥¾—[ZÈË£| ñçÉm-›îˆî.Ù¸Ÿr�a™ƒÇ7¸²rM.,²Ö®ä¼C.¸¼¯‹K$ž”¤[,,YkK­¯·ÒëkYôw“þ=y)¥³{Ñ]ÃÁ;3îVl2ðxæ÷NI¦Å’jãÇ$âuÅå|^Kåô¥#ØábÉZ]m}¾—[ZË[¢|‘ñÿÉþNã·sõŸvpûdÄÇÌÞu§3Ðòìm>Ã3_‹µ‹KÈ¢ÓfåeqžEdRM­ØG�Ÿ�ïöË —zÒ›Ï îÍ` ÞþRxëゼ“ÈçëΨ×_…—Ÿ��˹&û{�ƒ[-Ë·‹qk$›“òܸn’ÚWY‡—=kZzYVî¯$zÇ-ð‡¼ûw�u~ºL\¼¼ùg"À×í÷qàÒÚåGƸïÕ“�rŒÈi}=qõعS××첬)Ý>Gô/Žš?áyvçë t˜¹Yx1ò¾C��·ÝE…Kk•ã¿VNAÉòá÷ÓÖ v.Tõõû,«÷G�ã�·�ùÜ}cÒœ6Lºë±¹/ióž7Á4ùûOºäçY§Õæò]–º-®ï#[áÂÆ¬¹SR:ý8î­=Ÿósñ3qÌ M¿ðG�eov·Ý›¯¯zöÖ¦Mv‡Ê]™‰fÓ�õ¥g³q·’h®‡//�_®¦<–V<�FE·V”Ž×˜ÿˆ³Žkñ¶¼/ÂŽ‘È6—]—ƒ^éí-\Úî?‹m.›Í— ëºÍëq|ñ_dø¹;Ûõ´Ç–Ïdú¼‹.­)17×ãí8g…|##´ºì¼*÷Ghêæ×è1m¥gdzcºò³E¹Ûß4WÙ‘�•¼¿]LylöO«È²êúDÿÏÏÅÁÑ�}‡ŸÂ¾>‘A qÛm´‹§n÷'i÷ß<Ývoqó®CØœë5Òìy#λ+"‘VYf‡[­Æ²ØµúMgºÜM~8ø8qV‘ÁqÒ–Ò/}¹Ü}£ß<ïsÙ�ÅÎyaó�ü×K±ä�6쬊Eõe–v»ÊE�¥Ò`Ök­ÄÀ‹ /HàŠ8éKi&|­ò/ÌŽÏÎî_'{w—wbæâY­�yÊrà¦&“KfnÆ9Äxæ«]ÅøOÆØì²rbÕið°uÑddÍ-�Û$²]v-c65kà_Ÿ…/þï?ø®ÿã•§áŒÿ»sþ-ŸöýJ³ðÉÿݵÿïû}§1ø/÷$¿ñ;ÿÆ™/Ä­¬N` ßăñï‘Áùö»Ï¯Ð×øØù:¾)ßXZ¬/L~7ØÑcÛ�Æ9þ]˜ò]f6³Ÿëq£ÀÌ—èC{¬HäšY2¶ÖÑÄà„ü?›`y¹ÖúK«Ä¹öF¯‹w–·JÃÇùäXökøÇ<ʶä‹W͵ØÑk³%úPAßä’\­­(ˆOâðN~Ͱ<Ùë}%ßÁ.{>³‹÷†&·úÃÇùäPY�ÆyÖU°_|xºÎm®Æ‹_—/Ò†öø°ß$’dí)Dÿ_ÆTØ›-ÉïTjo“g^#Õ¾Uë±-ØäI‹±ƒŠõpÍJaäááër°±p¸vÖI2±b�&=˜øòË•›5±YFUÔÀ°ð7çSËŸ °ôü•dÇäoGjíÃÂÃà]�ºÏ‹•q=F-˜Xúλ쟥´Úè5¸Ø¸ðE¿a�·ÕbÁÓÄÆÆ­÷É^ÅøIóWåGˆøšžʲ,ò¦µ¶bâað®ÀÜgCʸ¾¯¸¸úþ ØŸGg´Óëà†8#‹a�¶×ccÁH±1ñ}÷_^ÃxOóMå7‰8š®ʲ,ò¦õÖââbp¾¸Î‹”ñ�f&?Ý`×ðNÃú[=žŸ_1CX; }®»i.>5o¾úÉwãƒñ9ùŸáÖ.�¬ü‰�'ÌŽŠ×{0±,ç|�'½xf¾LÌÌ‹ÿƒ}»—�¹Ëåø—쯾Ýw'ÇÚKX1±°p¶¬Hém%ÅâwÍo€~Wãàk°;k ¥û *–G/\÷´ÚÞ³“*êâCl:.S•°Èà<šüÌ̪Nj�‰´»g5±Öûðâ§¥�ñæÁ¿( Â×ë{O©9æOÓŠþ½î¹uük.T²cãÅ“�ågäpŽK.n\Õ³ g.ÆKm÷_‹­(”¯Œ?/¾y=»´°ú—�äý8ïëîé—_Áv’dÉv<Ã¥ä99óðžI&fTÕ³g&ÂKl­×âÅJÒ‰½øOóWñËçœz-OOy â�£½“U…EwD˜}[Û×ï7¤nÃãz &ó>ýen­‡Y$“ÛÄ6|†kn�êËeÒ[mzÉmÖßm·Ùu·Ù}´ºÛ­­.¶ën§­·[u=iu·R¾´­?+§öÝK©K­­.¶êRën¶´­.¥iëJÒ´û+JÑÓËn¶ëiuµ¥ÖÝJ]mÖÖ•¶ëkOZV•§ÙZVެ?Óþ¿èæW•/þø�Ó_Îû»Qιæ³ëGVôÅq;+œÝ�‹�÷ÝNƺ¬ø8‡Ûá_m÷É�¿ÛjdöG_m.º¶[w:ü™ùVðÅh¶˜\ã¹5Ç›êë47õ—R]‰Ø\âí†.M1²µ9ñë3b㥻°yµÙøÙ?uÊÔç٬͋Œñ]¦5iu×C¼Ùêëí²¾ž·VËnæÇš_.Ÿ~`o¡ïï"xu�‡£³"tw^æãö/wæî?@dò=N“+¯xÌùyü.îG‡±ál¹4š=gȆٳᤶÝX”ùëóýå?•˜û¾½ék'ñ‹¥vVɃ•…ÄwSåöÇ-ÖÝ÷zK(ìŒxuÙ]~møõºì :ÿ\|‰q2ò¶Vµº-þnüèù/äÌžÓvOãPçûñ2!âÛ‰ò{W”ëé$UºÎCØÑëåÑ`æýÞ—_ƒ¤‡ ´Šiqrró຾èºù·ó—䯓nxOY?Ž=CŸïÄȃ‹î'Éí.Q¯÷Eï��óühð%Ò`æÖuø:Xp«ô¦“'+: «î„ïÉOâ˜ò›Éè·ý[ᆳqâKäåÉ�w`an~·“|ÏY�™¾Š ¤æ\Šizku¬Ë×dO¯ã—fnð6[$Ÿyq®à•kZÖµ­kZÖ¾µ­~ÚÖµüµ­šáÛˆH­_}òß|²ß|’I}×É%÷Vûï¾úÖëï¾û«[®¾ë«ëZ×í­@�O~8~T{ÿ㯘Iš½ƒÑÜ“iw?éA²ÈÇÒl&§Òƒ#‘ðÍ—ÒÌ¿„sjàÇH«›2ãfYdVfãdÒ zÁÒ?�¯“Nñð–_šN{Ò¼ƒaW8齿ÆxuSzÙü‹†çÖ<ºðÞc÷[>�ÙPÅ&6tvÇff>GÑÆº�ü~ü˜w‡�<®èxìÒsΗär¹¿No63è˗Ö8çä<;:¶eW‡sºÙôîʆ)1³l¶;s Èú8÷A܈?œo!~0y®«‡î²7½Ïáîë7".mл½~óÄ+³Ì›7+�ô†Çe|˜Ü7—ál²¥ÊËÖV¶i9’Ïe˜ù’bmõÖ x‡æw@y¿ÕX±Ð|Æ öº±áÃÊx®Ãîø<ç¯7™Pß-ÜoœñÛ2rfÓìã¾mŠ[/›:È®›#"lµ�wŠÞ[ôw™=a�Ú}Ë!Þk/³GÆó¾†3à{¹âºI8ç5ÐY‘‘.£i ñIK$²ù°³l޳bO‘�[%ºt~+ùmÑþcõ–hô‡,‡w­¾ÌXyÎú\Ë‚n²"ºI8ï4ÐY‘‘.§gÑIHä¶ù°³l޳bO‘m–ë1ü=ó'ǯ;º7ŽyãO<Âç{4ú½Œ¶˜§ƒòí|“îø`q¹d¿?Šó-$yÐI.,Þ±äaäãçbI“�—‰•>Ô¶]²Í¢Vò›ûF|ÑþP½�ý\�\ÉÇíòïûøs?éåqß&Ÿ´ËŸïßÌÿ§•Cþ#oÛ=æ¯ï§Hɧ¦Z ÑVŒ8Ž™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ MÞý'×ÞGôïbôgjj)ºà=�Æ6_�â[n%s1¢Ë¶Ù0wZi³q3±ð9ÚCÃY—Xd»a‹ öÓݬaÝ==Á<€êŽ{Ó›©³uÁ»Žgq½þ³¹0Å•m·áíµSeãeÿÐl¢‡;]•ôï¿?ì§¾;Xú:‚w÷TóÞ™ìÍM»®ؼs;�ï°ý¸ÿy†,«m“mª›+.ö‡c9Ú쯧}Ø™ØÐÍe=ñÚÆ�ËÓÝoäTvHöÿÂæ�cÚ|KuÂ9¿Η/-¯ßaKƒ›úüŒM¦£e²RlLì9àÍÀËŽ<Œibž(䶯ÿ1üQìŸ ü…çþ>ö~ÔÚñ-•óqÞEL915\ó„gË5üW�h=ÒäG~¯�kã÷]²ËvdsáÍu21¦²ÚÞ<¶ñ°ü<ï®sÑ=��5v<_>³ñîEL)pµœç†gÉ-üg›h©|¹1ß­ÞaGùñÙ4ÕÂÍŽ|9nûÆ4ÖÛ\7–ž0ö‡ýñÎ:+±±f®ÇŒgÝ?ä4“YÎ8nt’߯¹®Ž—É‘úÝÞŸe“M\<ÈçÄ–ï¯�-¶Ô òCàwe|pyoÙ/ö6D»Ìn?.7$ëNÀ³QŸ¦Öv�Tr;òeá¼ëW‹�²É¦³}vÚ i³q5܃]°À�+&˜•žý`kk[Ú(ú:�¾Û�íu»íÓc¤Þisñ6º}Σ7'[µÔìð'�+c­ØáKf~TVÉ Ñ_d‘Ém.¶´­)WÐÔí¶º¦·y£Ùì4»­6~&ÓQ¸Ôædë¶š­ž¿"<¼ Ž·a‡$9x9øYQ[$3E}²G%´ºÚÒ´¥_¿UµÚhvšíÞ�e°Ón´ùØ›MFßU™‘®ÚjöxÇ•ƒ±×l0ä‡/; &+dŠh¯¶Hä¶—[ZV”«Ñq_Ë:ÿ”qþqÀùG"á<׉n09åüGw²ã|£ŒïõY1æj÷œ�i²p¶ÚmÆ·2+%ÇÉÆš9¡’Ú]eÔº”ªL^~$Nãê,M7]y›Æv]ûÁð¬ÅÀÄín5~»]Ý:M~<BßÓØy²køÏhV8áŠÚM“>£k}×K>Vvl·RÔŠü(üAý±Õ¸ºžåçØw ó ³øÝuÚþßÔaA ñ[úw.MìzÙlpÙlÓM©ÙWú,ù9y²ÝJ$KáWâ í~®ÅÔð/.¸æwpì;1ð±{;ŽW_¯íÝF]¿§0räÀã��[,Ž+-–iµ[+«YfÉËÍ–êQ2_Ž_Å«Ø<*/Xü�plÎ×ÑEu0ñ¼�ê]6‹MÙXVO��4sv7XcÝÇøO0Äó:>ÿ¡»E™‹¯À÷_¯ÛçM|·J·Æ?‘_ <¿ÄÕÓ£;óƒoù>Î*V�m»ÙYÄ;K"<8s3ñ®ëîOú/’ìcÖRjÇ.fšë¤²ÿ¥“-´÷$Ûã—žþ"ù[‹®þÒ�áÂù"ØE}ÿÚÿk°·‹vV4�cÃ>tRp.GMg%ˇ_Y½’eâcäëï¾Úý)ä¶žêÉ›ÇO=|Gò«]ý¥û¿†oùÂ*ßN¿Ûl-âÝ“�,8ñdgC'ä£ydZúËì“/ œ ï¶ï¥<–ÓÝY•ø¯çç†>mj#Ûx³ä‡W÷ÿ¢²7¹¼OG¾·YÙz.6Öí$›^YÔüš'gpí|›;iSmuvOI#¾:ß±Ý~ì7·Í¾|QʸÇÐm9g4äz!Å´xÕÍÝò^Q¸×è4|;o²:åí7;\ŒMv¿’Imµ’i,³ÖêS×Ö¯�Èyâ:M—%å{í7ãšlk³6ûþC´ÁÒé5X–]m·ål¶»)ñ°pq­ºêR·Ë%¶Òµ§ÚøÜ‡‘ñî#¥Øò^W¾ÓqŽ9§Ç®^ßÈv˜:].¯Û­²ì­Ž×e>6=·ßJVùd¶ÚV´§¯Úüùyx¸¹9ÙÙ8øXXXóeæfeÍ>.&.ºh¯»Ù6†ýßRÏeöGJûéÇ/*¾tü4è mž—¬·3y/ظ—Ë�—­³,ÇàùQÖ*Òý¿kdáæqü�t±_ul›Eúµ’ÏeöGJûéÁo8ïÆ·‡Úm¶·†vž–ý½ÕÑõ׎{mo.â÷æfë79‰¹gwbß—Õ¼{InßWÎÍ~nó‘k~÷¿¡§²—R�ðó“äÿË/?7YÜÖÝ'YálcØñΓà”ËÑu¦“'`ÆØfknÊÊØòíô6ÖKìÏÜdçO�~DÖâ}Ú +"uæ�É'”>rm¯�µ9}4}q‰ŸLî?Óœ'ïZ^¼ÔM ‘Å�›ŸƒvNF.ÞCK.¾Ìí´ù’ãÉ<ÔĦ,2ÖE Ìï’'üáÛ_jrúi:ç>Üî?Ó¼+ïZ^½ÔÍ –G�›ŸƒvLùü·w-ºû3¶ÓæK�$óS˜ÐÉXi¯“›Ï4~L¶»^7Î9EýGã•ÙÓÝ£ñ³­6Û,.•�ów¤Ÿµ6þ¸{.ãäú¬�.¾k2v‘ÇªÄØb}ïW¬Ö_4ÖÝσ´)ÇP´¿'Ç4»Íäþö悟 xü»n3ãn«g�?·mÈí®^“™v¬qIX±æÀãv}ãK©¾êdY&ÆLé½°Í��-ò“ü?˶ÛÍçOié)úMvÛ�øó­ÏŽ_M–ò•ËÒs.ͤ7R8$ÃÑYIôÚ»®¬Öß›&tµ²)pñe¾Q_‡ÿÀ9vÛy¼æí-%)¦Ó_µãž>kvËé²ÞR¹Zn_Ù”ŠúG˜Z+>¾ŸWu~µ·ç_›/¶)p±¥¾x?…/â·?Œkó¾M»Ë‹ß‡µäš½ÇñW²›_~N/Ø[³ã�™Ý3jé‹‘²Õdò8¬›ŽqédÉÞMMÛ‰äÄ› c«ÌºdÉj¥œ›x?Ÿ‰Kösa(^±þ¡ó· Ÿìüÿ³‡[Hò·¿oìÿÿ³[ÿHò·ÿïìaòÏ÷ÓǯùKu H0 Øª¼ŸüOþ4Þ5þ›ýbñÆzñ[øÏøáýþºõ…ÇYßůã7ãŸ÷÷ê/Öo7Æí,øñþ\Þ%~¿xו�Ë8W%5»Êÿ:[Í–ä½ÞœjÝïÞÛLÍ^Ó°bòžÊq`È‹M͸Næ\|ªéyF–¹RR9+¸ù8òÍ‹•FFF<ºùä÷Œ=AåçPr–îž=MßÝÒܽnËèqy/ 丰äE¨æ|3o.>MtÜ›M\™)•ŽX2 –\\¨²0ò21å×ï'&fMÞwx Üþvœœ#±±?Nð�ôû ž®í}V�qžÁÑbKºúEY²®ãü¯WDVí4óË|ØRßKã“'\l̈ù×àOsxÚRpžÅÄý;ÂwÓì2z¿µuX’AÆ»E‰,~ë©fÊ»AÊõqdEnÓO<·Í‡-öß™8’ãeäU•ò¹ñ äGÅ—iÓ[Íñ¥ç}Íw›<>™ï­>cÑr˜1ì¿>3˰£¾{¸_cáê¿¢O­Èº°åÒæ×Í•3_ƒ4m£ÎJ€%ŸðqðÁ—Érø‡šþ\q;!áðÙ‹Èú ¦¹.º—Ï˧¾–déûc�ê³l­¸üKÚÓ#�릎²m¤¬{ if ˜´ÙJOá‹â#+u•Å<Ãò›‹ÙŠÌmÿFõ'!À¥óò)陵žÐæºÌË+lv kIôZù£¬› +f|”³ÌjgJ/á£â/+u•Å|Àò—ŒYŠÌmÿGõ'!À¥ór)¯¥™®ÏæºÌË+lz kIôzù£¬› +ft”³ÌjfÏ‹ðíüåõv_ ùóƒ„EcG)ñ£¡yv²·äõ®Mõ‡7GÝ}�¥Î¶‘ãöVÆH|°|pò;–]ßÕççø×ÙûLÜάät“#agØÝI3vUɳgº\¬}Ö†/tšÉ²o¾»mM´–ɦÉÇØÙ�o•ÏŽ^Aà¿rK»â:Ììÿ{;g››ÕÜŠ’dgÙÅvRL݇VrlÉî—'u¡‹Ý&¶l‹ïý-ª¶’Ù,¹8ûñªìü@Ÿ¹ß>@ÿmn—ã{Ù|2ï}Æ^oÎúluÝ9Ø6äì·½¸ÝEKþŽqC6ÉÉ�H²stvË‹õ3²uù²ñéÉ×(QçdN·íîØé½å97Pöwaug#¤wÅú®yŸ#á;Ÿ£%µ²Hk³ã[-nmÐÉekmÖVÿmÔ¯¥iZ=ÿ^v¿iuÞîAÕ•ϺË}|WA~ﯹ�!á›kẕ¶èoØñÍŽ·.øn¶µ¥m­õ¶´¯¥h÷Ý{ÚÝ£Ô›zòªìŽ{Ö{룺·]Ëù Û] Ô­·Å~ÇŽì5ÙwÅ}µ­+mo­µ¥}+F}éO+<Ÿñ®»OøúEI½•¿wθ�!ßVÿYë[®ûÕ.¿ÒÚ_[©m´¦öð˜?’ƒf·Oå+ÛaÛlvVœã�õÿbg_H©7²·î¹×äßu+=kuÔÊ¥dô·ß[©m´§WxWâSù“áÙ:jäù[ƒÍu:|kñ@s^�èM†6Ò/¹M‰Û�î§­4œ×;'I-ȶoÒ¶Í,ñ[õ®–ÊßeùŸñ |¢ÁPËÜœ/2Hì¶Ëò²zk¬-ž{©OJË-¸œk—Ýû¾Èì·ù”£0âüó|‘A� 2ö� Í’(í²ü¼®¦à6dd]m=+,Öáé±1-’ÿËZGv2Ú37ÏÈüðÃ/hp¬Ù#ŽÛ/ËÉê~fFEÔ§¥ešÜ=6&-²_û´Ž+-þe(Û]_âçù?×ëpprúÿÃÍæV&,ù�§Uö|;-¤ÑGm’ggE¥ï >¢<¬›©ï¾˜Ø¸ðRê×Ù–ú[O“¶ü@?*;Ò¸~Aht¤T�Û©éN”𕾗ÖúÍ_Ó¼uwÕºÚûkO_g¶Ÿe´¯­kò7:%û;­®xè8õ)±ÖÍ?OõöÝu$­õšêïø^òï«uµöV”­,öÓì¶—~säíþs¾JöWR¸]á ãô¤VÇ[uCÔ“[uÔ’·Öjþžá›»¾­Ö×ÙZR´³ÛO²Ú]ëV:ì¿ÅcòËÎð¤Åâ܃ zZ{ñ#Æ·eÖ�1‰µÍŠk3-É»>8û‹Û:êåÍ µÇºÛ º CuklvËé-4ã´þG¼óØ��å¿|n5ìzbnxÞ·°·¼Oˆlñ=‘Ù\\ÞÃòtV|k鵺;°ý—ßOuÔ­Õ­k©�™òçoÙ±ÇçÞS÷^Ë[¸Ç®.×A©ç~!Ŷ8µ²;.ÆÌâ|:mÈǾ‘[[¬»¶Ýw­Õ¥n­k]Nì¿�o7{vÍŽ?=ò—º¶ZݼÅÚh5\ãoĸ¾Ã¶GeØÙœW‡Í¡ãy÷Ò+ku—bÖÛ®õº´­Õ­k¡]Ëó…òÏßë¢ç>vw–¢-eÓVú‡k¥ñêÌšO`–=½½¤ë:o ¬uû#ÎûÅ–×ó©J]ö´²ûï–ûä’û¤’K®¾I/º·ß}÷Ö·]}÷]ZÝu×]_ZÖ¿mjÓ»ï¾Kî’K®¾Kîºûï¾êÝ}÷ÝZÝu×]uku×]u}kZýµ«Oo¾ù/¾I/ºI$ºëï¾û«u÷ßuku×ßuÕ­×]uÕõ­köÖ®\lö{-ÖËa¹Üì3¶Û}¶v^ÏkµÙåÏŸ²Ùì³ç“+;a°ÎÊ’\¬ÜìÜ©n’Ydºé$’êÝukZÖ¯òÿ/òü ï‹‹“�“�……�>ffdðââbbÃ&FNVND–Å64[|³Ï<·ÒÛ,¶•ºë«JR•­_£+?+#373"L<E áü?Aºå|·•îµ\o‹ñ~7ªÎÞò.IÈ·¹Ðjôš “WVÏqºÜlò¢ÇÅÅÇŠIò'’Øã¶ëî¥+`ÿÂÅõž ôÜÝ·Û:¨+äÿvi0%ä¸ù‘ýë©ø·A´Õõ~.DÖW*=Þ^M�çrK¬¬q_Ÿ>%,’Ýu™9¾øoøÝ³Â³ûGU‹_$»‡O…/$Ž\hïÊë-ðlµ�g‹—%µžÍ¶FDqfò¢¬pɰРZR[5ñdÏ;?‡OŽ <1êY{?´5xÕòC¸4øRò8¦ÆŽìž±á2ÝËYÖ¸¹RÙ\‹6ÙE™ÈnޱÅ&|PbÒ’Ù¯�&{K¾¾ ôÿ^:ÿ »SŽè2|ÎîÝ~6wsr\\ø9�}Æ>´yüw¢ø¾ïùuk8õöG—È25•¾ Ï!­Þì͆»O$Òv‰Ùçz/w¤Órm.ߎr-V»{Çù¯a¤Þé6øxûNçMµÄ—gªÙëòã—;]°Âžøf†[nŽXïºÛ©ZV´|í¾£UÈ5;M÷[�¹ÒnõÙº�Οi‰~³kªÙcK‡±Öìpr¬—7;kâš)-ºÉ#º¶ÝJÒµ£çmõ®A©ÚhwºÜΓw®ÍÔnuLX3µ›]VË\=†·cƒ“d¸Ù˜9Ø“_ÑImÖIÕ¶êV•­Êx!Ɇll˜bÈÇÈŠH'‚xì–á–ÊÇ,3E%.²H¤²êÛu·R´­+éUl,ß܃ãßÉM—Õãì6Ùrí¹oEr¬ˆr¯²¼~™–]´ëý®|òd[•ʺælè12oú·I™….uÖC\¿¡z¿(þï<ò?A«ÇÌÍéÇ—kÊzS‘ËfT”³EnTwløÓ7&LŠär^¿›:Y䬷ߙ….&môŽì« Š¾”/wž y Ÿ Öcæfô—bË´å-É&³*J[£·*;¶|i›‘&GÞ9/›:i﬷ߗ….&eÔŽì«¡Šª�ž/‰­ÏÆo”9[޾ÑäWÄ>øÚnù/AíãØåî-á9PÉg(éE•²›#wÓ�˰²ºŒœér®Ûñùq¥ûîV~>ÖÌ^[¹¤æ³…`×�°ÏÕfâìµyٚݎ ñäál02fÃÍÃÉŠê_F.V=ñÏ�>³³°ù†¾!Ž;m²Ë"¥¶[OKiJ}�áë�“Ÿ?º¦¸ôâ^WvìðâCf>.5ßEÙúÜlx­ŽÈqñõ�™‰ËµÐÁ q[m–[-²Ê{m¥)ö:«Õ>?/ý3Æmâ|CÎ^ÎÜjíÊÉ˦Wkèz×¾95eʖ饲îiÞ<±9�ø¶ß}iϬ0ÙélvÛm)Jm³ñ|¦`G}™]íÆ7W_ìöͳé~ ŠHý¶Ö—RÊi¸^¦*ýJ×Öïu·}¿“ÒŸcdµ_;%Zè¯�3¹8¾úû«emŸkÔ]_ ±ûmöÝK)£âÚh+I+ù×{¬º¾¿“ÒŸcdµ_:¿$Úø¯�/¸ø¾öû«emŸkÔ}a ±ûmöÝK)¤âÚx+I+ùÕ÷YwÛù=)ö7³ƒþ,¯•>'ªý¾Âñ‡³rý1©úwœuÿkë5�KwÑënÅëí'®d¶Vi?Ô~”’êÒ?d~Û)ý6ˆ3å'6bÆï)¨¾Ùm’¹:þ˜êif¾ÊY}µ‚ëv¼Cg�H®­ôºµ¶:_ëm=.¥=i_ë²ùÞù(΂Èq{{‰id¶kd®V·¨úÖYä²–Imqï·qÆöØÔ†ë¯¥Õ­±Û'­”ôº”÷R¿Óeó³òI�‘böïÓÉl¶É\­oRu´³Ée,¾ÚÁ}»~9µÆ¤W]}.­mŽÛým§¥Ô§º•ýÜ«ñjü¦r|8ZŽ?âŸÉ‹6<«öüW©9ŽfÃ& ÈŠílÑs~ÕæZ›p¦’{eºèñcÈ¥ðÙKe¶ÊÉeúÓÙß-$�¹}×rï1;“ÛíöI]î°ú{[+�-ÑMƒÔZî‡42C=ö]m¾êÝuÔ­×]Zë§cü¦|…ö§ÙÊ|²íœ=:.¿Ú`u,Ûô,ǬsAÕ:î ðߟ�eöÝe÷V·V•ºë«]vìo”��~Óõ§)ò¿¶0¬»ùø¸Ó©à–ß¡f=c›ªõÜ2 ¡¾+?:Ë­­—ÝZÝu+u×VºÚ?0]ÅÅäâ·Î>ÉÔjdʃ2쾬ã=]Ñ|¢’ãû¾�‘óŽ’à}{ÍbÅ»Ýùð[°¤ýžû.ô£Ÿ›]¶×{°ËÛîö{ ÆÛa=ù9û=®fNÃa›‘%}ÒdeæåÉ6NLò]_[¯¾ë®­-Z'µÛmw»ÍÆïg°Üí¶]“Ÿ´Úædì69Ùÿ?>fn\“däͧÛ}÷]ušÑm®Ûi¼Øeí÷{-†ãkŸ5ÙÛ=®fFÃa›‘óóåæåÉ6NL×z}·_u×Wù®Xvdv'nrýÇav¿=æ��Ϲ°ÏÈ9Çar�ç4åûÙññ¡ÃÇ›qÉy&vËu³– Lxâ²éç¾¶Çe¶ÓÒÚR”ùïžùï—çáKÿ»Ïþ+¿øÅ%iøc?îÜÿ‹gý¿R¬ü2÷mÅ»þßiÌ~ ßýÉ/üNÿñ¦Kñ+D«˜c®ÜêŽÞ�eÎz´xö*ëþÅã›.-ÊtYÖúÙ—­ÙCXë6,öúO¯Úë祙8Y�ÝfNd1dC}“Geöø>Ñë.ÜýwÌú§²t8œ›‚óî=±ã<›I™K©ff³eÐÉ~<ñÖÜŒ Ž•¶|L¸.³#*(ç†û%ŽËéàûC¬øGrõß2ê®ÈÐârn ϸþÇŒòm.e+K2õ»(.†ûñç²¶Ï�±Ã’¶Ï‰• ÖdbeEðßd±Ù}W×Ý“ÄyçcavLx\‡‰rÝF^‹�鲥ß26ZœùaºøeŽk)ºËíº”º•šü†ø+ØÿÞEr>šæŸWsÅsk?#ê^ÀŽ Å¯çý{——4Z½Ÿ²”ö`ò-]ÖWqƒZÖ¸™ñ_ôî—Ll‰ë©óÛžÀðW¿·ýKËmŸmÅs«? ê®yH©f:ày9RǯϻÙm±áò-UÖýÏo…ZR¸Ù±ÝtS\l‰ë¶óÏÂÎàÇ~oú›–Òm·ͬÛþ¬çvÃHðyÏÉÊ–=~´²”³�j›l*ý¸Ù±ÝXë&,¸ÙT³òýñ‡Ø_žVoú»? ‘îz#�en9W�]§¹‡hùÏ_щ~Vƒg¶ÖÁ�«Ÿ±:Öm®>³�AlRß%ØÛpñðvx>ýiKK¨m_DùËæŒÖáct_‘ݳךm~MÙ˜ÜOYËv9ܹ7ý—Í“×û¹6œ':Kéüõg×Éëû­Ÿé?5|²ñÒÜ,~–ò ´8.£_-óâñ\.O›²àöÍ'þ\–n½»iÃ2¤¿Óí¬¸Ö­œé_4|¯ñÚÜ<~˜ò´8.£k§Æâø<Ÿ;aÂ)5ÿÏË7Ýݳá¹R]ûµ—úÕÑŸþ\¾J¼FÀ×é:Ì~ãã|WMÇñø¯àœ«q¯í®¸âú [ýøºî%ÖÝÁ¬çœ‰Ù�_²;õºìYl³óiu-ûEëÄ…òEÂ!ºSŸÒÇuÖÝgÞ»«DåGî’ þ¤?Ú—�õ†%ŽÈ®¶ß|7Ùé%ÕºÛ«K+oIzÿñ|‚ðìo»òLž—í‰+mÖ×;žõ¬šÌÊ{¯†ï©m:»�õÆ%²Øî¶ßX.³ÒK½mº´¶¶ô�€~ ��.�ô9&WLö½õ²ë+™Ï:Úýf]=×ÅwÔ²�aÈ:ç’Ùlw[o¬Yé%Þ¶Ö´¶¶õ÷�þ.�“^-¨ãúnW×~#öeúºbEºå�­{GÌ94_gß'Ê“„wáZÍž\4º–ɉ¢‹+î¥ßwºÚ{+Ÿ0g—‘ãÛnËÇßòòéuþù°`ìí~=ÖÖêû)n,üûg%—[oÙZÖk½köúSò3žâQòŽ/àGô=´³i¦ím½ÿyõ“ú7ÔÄíM%¿CÛ[)ôý¾ï[k_çR–üí¯âLòæi=t}%㎺»û}›]gfîdû׺Jýo«‡Ù~ïí­”ú~Ïw­µ¯ÔüêRߟµüH¾[M'®“¤üs×Å÷o³k¬ìÍÄŸz÷Iýêâv6Šß»ûke>Ÿ³ÝëmkïüêRßÏ?li•ý«ü&éîuúYà¯=í.iÙÇäWS3îÛ\«x÷êªæib­øþü +ò})=3,ú¶ýtì/ÄWòiÍ2nŸ�óž¬ê8®­kL>½ê>3²Æ²•¥ií¶î×þÙÙu¥=}~Ùk_³òþ_\Ï~~~Ey„µ“�rî°ê«+w­!à][¢ØÅm=+ù´¯hKÙ{këû·Ö¿gåü¬Ï>|þD¹|·IÇùwXõe•»Ö�p>®Ðìb¶ŸoæÛ^Γ±¦öý¿»}kö~_Ëë§�‘ø´¾S¹ÏËÐñ�/Šý7µÉ¥i6ë~£å›NO¯õ²ëi\LNÞíÕá—ÖÛ®¥Ôúú‰éî¶ž¾´õ¥y©Ýžoùäty¸�Ýä�pö›a•÷ÌŽ+¹ç¨x=2iJÛI±x·'†à]mµ­)ô0c¥)_±ÏäóCË lÎÆî?!{c�j6RÒ|¾/²æ;l^t´¥ÖÒøx6ª}Ä­-¾´þƒƒÙ_OÈçŸqy›åw�fãw��¯Îu; ¾¾WÙs ¶/ ¬Þ—[j§Àâ•ößZA³쯧är‹ÈŸ—/’ß*ìÚá÷o™ýá¼Ðoøì¼O�ðŽ#ɬêN¶äœ{"’Y•®ä�aÔ|¯9™±Ëu™fk'—"?KdºëiJSVZÈÖw:â§“ýõâGqñÎÙñÛ•ìøç:Æž-Tš¼hfÚiyÆ«;& dp¾WÆm»èr�âk#§Ý®·ëG‘lSâß\0OÂøÃä�yx³Û|{³º“ìô\Öˆ5’j1¡Ÿg¦æúÜ̈­›‡rŽ5eßG“i¶òÒËi�Z}x²)ø·Ã—ǰ~1ùÞ.vÏìÞ‚äÛ=4‡" dšœhgÙéù¶»3"+eáüŸ�ÙwÑäº}´´¶ÚcÝO­ôŽ|káÊŠ£Þ�Ÿ5üÂð�È>!ͼ5ä<’^Ë9'ãY]?�«Ýs)ßRåmk¯Ñu¯.ëM4ñfs›·¹›‰0µöaVæ&Fu×ê²±3nŽkmè¾IÙüæúÇ•wOÕõ¯lr.Ç÷=�ÀtÛ‰÷ºî%ÉöèrvZ8¶Y¸²]6 ²{f‡×"ÌYýð[“—ltʚɎšßö/*ê~ºäݹÃuÝyÙ»þ¡Ûóž ªÛɽÁ✓?_NËGÎL|zÍ.òVÉl·ëYÔº+22m²™ÙÓ[þÅå]O×|—·8n»¯{7}Ãô[nsÁõ;i7˜­ìnCÂ8¦óŸu”<§[Îbë¾i¶Ñ`gòŽ 5Ócaéù}œKw‘>6x‘G�Ÿ÷¯´ŽûhÊÌ–Éor¬å7öŒù£ü¡{ú¹:¸“�Úåß÷ðæÓÊã¾M?h—?ß¿™ÿO*‡üFß¶{Í_ßN�ÿ“OL´¢­q3o·ýÌüÅþ¾º‡ú�ÎRñü3¿Ü¿ÊÏë÷­?±îP—Gá¤þæ>U_�iý�ò…�_ƒßövùü´¹Ÿê7 ’ÂIÝ'±€rù�øËÕ|€t=Û¾¨ÕãyCÔx9»>§ÞË<»ùŽ£ÖìÍßQïöSVÌ;õ¼Š¶ÖmLÙwY³sK.¤øØ¹[ËÉÏ–Ž�W�]%v×…ëuø¾Hu^vÏ«7RO¶¼«]w®Vç«wÙóÖÌ;õ¼†±ý]dÙWYf·om—Ò||\Œÿ­Éÿ–?ŽÍ_œý'v×…ëuø¾GufvÏ«·RMº¼«]w®Vã«·¹óVÌKõ¼‚¶}]lÙ7Yf»mm—Òh1²3þ·~k¾)8·Êg‹õâúœ¼^/ä‡N~žæ>:ól« ý.û?ÝÿXs %·êÃÁ{6-N$P_dú­–&Ɣɇ#_�]VÿA¼â»Ý×äú}Ÿäœsk°Ñr ï'W¹Ñîõs`mumnlPæköZÜìy!ž l²H¥²ën¥.¥h€vóGºã­Çäz�–ƒ�ñý¦ÃG¾Ñnp²u›}.çS—.ÓSµÖæGf¿e®Î‚øg‚[,’)lºÛ©K©Z 7¼ÒnxÎëoÆù§e¡ä<iŸ¤Þè÷Y:ݾ›sªÊ—gªÚk³#‡/c¯Í‚øg†[-’)lºÛ©JÒ´UÎ8O.ë^gË:ïŸñÝ·ç<’n¸‡1â»ì9uû¾7ÉøæÇ'Q½Ñí°f¥²âlu{ÊÒ´ü•¥r´X'Ÿxrq¦—'XçÇÈ‚Káž á¾’E42Ç[dŠX¤¶—[uµ¥m­=iö·ªþC<åéL}vYùaÞüsM¨†ì}_“±y÷ˆ`Cu´³ébðîI™¸âÑYm¶ÓÛJaþo§ÙèÛž²óçÍNž�¯<¢îÍ&¯W�\]oÊç»ÞEÅ0qëm-¤X¼G“enxÌ6Ùm´¥¾ÜJVßÜôm¯Yùëæ�OыמO÷V�W¬‚¸Úî=‘Ï7œƒŠáA[m·éâñ>K•¸ãPÛm¶Ò–ûq)[sÑÔ>˜ù±ù^èLŒÌŽço}ìþù‡Ø�µÈðü�ÕbbÅôiz�7}ë{+S£¾;`¶ÛoƒJ[ëm.ôºêWtu??ß*zëé~g�úMõ¾Ëíú[n“èøl­o­+mõ®‹¯t²{ã¥=-üïo¥~ÚV¾•¦Þj>t>Lu²ýLÞ÷Òr =—Ûôvý=Ó°Åî¾¶ÖÙ=tI7¾:SÒßÏöúV¾´­}=6ïQó—ò]­“ߙ޺NAo²û~Žß¨:~ýo­µ¶O]ÒKô·ó½¾•¯­+özo¯X~*o–Αò¾eÑÝÛ}°æEtýŸÒš=L²_““ðä_N˜Ïê()6¾++ 4¶Ëcº;ëYm–OmöýÉ¿§Ê$‘K�ÃÂqï’;ì²xzo¬î– ¯¶¶Û4TŸŽÏd޵÷[Kì¾ÏZ}¶ÖŸcïMóÑòC,2Çgfðl{äŽû,ȇ©ø-ÓAuöÖÛfŠÜ�LðVH«_u´¾Ëìõ§ÛmiëGÝ›ç‹äzXeŽÎÍàø÷ÉöY‘Tp[¦‚ëí­¶Í¹™à¬‘V¾ëi}—ÙëO¶ÚÓÖŒ±™ø»>O2q2±¡ë uódcOYø}YÚ÷åàË,WGf-™ýï��~N5×Rû)<C[­§¾Ëíõ¶¸‹œ|Üü£v²]>ßËO¨À–´­kÁø_WuæÎÊÓÙëô¹àúG.­žµ¥2ý>ÚÒž”­hÄÜ¿æSä§šëæÕm< äÌ)½=‚/¬8>Â?J[Jýïá:]ü^êÛëù¹Tüµôô¥}›˜|Ç|’óm|º½Ÿ“܇W…/§¯ð?†õŸØYéK=~–óˆp½.þ?ulõ¯¦WîÖ”ô¥}�Ë÷Ì·&Øçeëü§Óð­vl1Anƒ‰t/�¿£°if5�K.Óu�%åM•}µ–ë®Øß[$¾¿OéÛKm·žÝŸÝ�ÉÝ›{wýÉÛ=•Û¸éKbÛvG8äÜßa–ÙHì‹/’löSãÃVÒËl²¶Ùm”¥´¥)JQ¡Ý‘ܽ½Ü{(÷¹Ú}‹ÚX~È6=…Íy'2ÌÇ·ÛHé6G"ÙleÆŠØí¥¶Ûem¶ÛiJR”¥)F‰v?rvïql¬Üößiv'gí¢û!Øö4ä|Ç3Ú[K-�#�ìvãÅdt¥¶Ùem¶ÛiJR”¥)G+;ÛÌ-< Ž_#<—ïnïÖaïóy>¯Aګ͹§ÐïväÇ“°ãAâñ|ší4×’êùys¬«dØz²d® ã™’Å.|Û�Wx0¯’—Ù…�NuÑOLZcOÒ_Œ�¾Sç¿zãqüŸÒ.’àk7ÝÏÍ1cºÙ ÒË‘}Ø\/ŽäßeØÖói÷I Å¾ÿu˜8ÑäfÝßw·~�üeüòŸ=;Ó�äþ�Ñô§›Y½î^i‹ÖK–\‹îÂáœ{*øïƳ˜ó?ºË-÷û¬ÂÆŽ|Ë£›îöãÏܯƒ_ˆ^iòwäN÷™i²µ~ô¯ Òìûï˜däl5vsIíºÍž¿£8F^·+o™Ë9®,_õS/ x)Æôr_™.DY¹Œ]�’|3‡qn»â)Æø'ã¼+‡iuüs‰ñ-&³Žq­ª qµº]›~¯Yƒ�oÙ.=‘ÙO¶¾Û~ÚÖ¿jÔÎ7Æøï ãºÃô^)ĸ¦—UÆø¿ãz¬ã|wEƒ¯I Ði5pbë4ú]>³,|\\x£ƒíŽ;m²ÚRž•è�ö€üüJ_³› ùBõ�õ�¸kø„ÿgçýœ:ÛúG•¸uøƒgÿý›úßúG•¸?ø—c–¾ž=Ê[¨P A…ÅUà ÿâñ¦ñ«ûÿôßëŽ3×ŠßÆÇïõÓÿ¬.:Îþ-¿ÿ¿¿Q~°8óy¾0?igÇ�òæñ+õûÀ¼¬îY¹(…¼�ñç§|¤êÞAÓ]ëÂ5|÷¯ù%±_—©Øýx2p6Þúào4;l)qöšA¬ºK«�›‰49Òë­÷VËï¶ìGÞ=ÕHõ¶û©;£†ë9ÇäVG÷ÝNÇëC6.f?º¸[}6Ï\}ž�y®¾úÝ�™‰,YVµ¥.öÝu+ˆûÇ¢z£É·ßu/tpÝg8༆È뙪Ø}hgÄÍÇ÷W q¦ÙáË�³Ñï5×ßZãæbËñ{®¥.öÝuµÅåÑ�Gä·Ró®Šï~¡ìΦì­¼™p¾E ×àm0o–¼\œl¼9ñ6ºMö—g‹v¯i�‘‹³Ôì± ÍÂȃ*f²¿�“ï‡Þìøûä›g¡�mÚÞ/ló£þ vλaÄ+Ÿ“f6í­~ C�o"ËšÌ|m•–ÛªÛû⺠ɒM~4>H~'û‡Á=öǘè¬Úv�{ ø©ÇûC —l¸—é ˆñð¸×j`àÅH4{ˆòæ³e¶êö·_Ñ×&[° ƒ'ÈïÅpx-¾Øs ›NÎñ·a�8ÿgâá{¶>Ê;mÕík|WG\|™oÁ‚´�™OÃÿÝŸy;žðê ÷�ßá†NæÌhù½ÐÁ—Ø}/]œÐæÔwV³U��‡v£;2¹aò�~ÚÖµÛmºë©m´­×]Z[m¶Òµºë«_JR”§ÛZÖ¯Ó……™²ÌÄ×k±2söù0a`àáA.VfnfT¶A�‰‰�’O“““<–Ùv[[ï¾´¥)ZÕ/߈�‰"—ŠùCçoöÝÚþGÖ>6ò,Y¬š,ˆf¿#�÷Ž“7:SÙXáÈÆãÖën¥Ô·mg§Õ×ÖW_ b߯<“ó[Š{r,® ë÷øÒÙ.$ÑM|øþåÓfcYé=µŽ,Œn9-n¶Ú]Jm,÷}\ egñMð›~,œgÉ/4ø¯¶{+ƒÈ:ßÇÝþ4¶K‹4s_>ÿ¹4ù˜ñÒ“[X⟎I[­¥.¥6–{¾® 'ïð'øuq:v.#æ¯È/ ‹7¹k.¯“ô?�܆K�Ó¶Ct; Wdw®K~–Ç·¥–Û%ÔñÙ½ø¼N*S#>Ù·²G�Çeô•bT‰˜0W’~8u?–=5Í:+º8ì|ƒ„sMlØ“_¸–nøæÖØ¥·SËxžÃ/:=7,ã™RS#+éKm’[í’9a¾H¯ÃAtXù;Ô¼¿¥»wAû†slØyÛq­Ûhö4ŽJk9?ÎÊÅ̳SÉôWÓ# 'éÉHå·Òû$Žëã¿ ùÐ]cäßSrî—íÝ[îÌ5ÒâOí·ݶ�cH䦳“ñ¬ìœlË5<›C“}'ÂÉúwÒ9mô¾É"ºøïÄ=ûÐýQä÷Mv/@÷‡ÕsÞ«í>7—ÅùÛÁ±dbOtY8;=vEÖÝ>Ÿ’ñͶ6>ËQ³Æº<íV×3H²`ŠK+yùøôî�ìËë~}ü—�o®ÊÚu/n`jò°¸ßañ¨æôö×ß\ˆ4ÜÏGl‘ŹÓÝ<²àÍ}’Y|øy™YôyõàWkxÜ9\šÅ‘ȸ&ò¹.®íL=fV'ç<~Ùn§Ñ¾ë¾¶6«˜ém­‘íµUžYq$¾Élº\Iñ²&¯ÓÏ{WÀÞàÊà\Ò<ŽEÁ7•ÈÙuiáë2±8ÿ8Ð[-Ôú2]wÖÆÕs -µ²=®ª³K&$—Y-—K‹>6DÕU|¾üFv÷Å—zdh2íä=�ã7Éû÷Kw•úØ5» lé6ÒÃ×<çi��×v×ÄÔM~V&<”³aƒd{ x¡ŽY1±t ¢­r¥+ZÒ”¥kZ×Ò”§ÛZÖ¿’”§óAþ¬²ùo²(¬¾I$¾Û#ŽËk}÷ß}im–Ye´­×_uÕô¥)öÖ©¥ü|3ÍÕÕãiykÄ.‡³.³{Ð}KÈqý/ë¨'†é1;3�i2 þ‡ØRÅ%’hð'ût×ï“GM¯Ý¿FKÿáƒâ2n²¯òûÊ^-|“txûž�ê½ì¶N½ÇȂ뱻œiò!¥lçóÃ-·éõó}º.ûÔñÓiX-ÕËÛá—âFn³¯òóÊ.-|�txûž’êÝì5¶N¿ƒ" ®Æì.m¨È†•³žÏ ¶ß¨À›íÑ[_½Me6BšËçÀþg‹8kΟ28†N³É�ÎNoHõê²Å•ÑO�iï×ßÊyî¢ø¡»¹y6§c‘º©ë%xκzY•d{‰gÆÕJµ&”˜’íj'œ>õ‡�Þ<òÞ‚ìê]¯‹kX÷|'™â`߸뮫ÇË‹ŽóMF4ÓbÓ*ü¾M�™‹I±þÿ­ÉÉŬ±Ro©f¬ù“âW[ù«Ðü§£{&:âA´ön8‡,ÇÇ7mÀyλ*-1ÓÃ,¸ÿZ|¾KN=&ƒïºüœŒ[¤ŽÙ«}5kÌ:ßÍ>ˆå=ÙW �cÜqY�‡nÛ�s�tQèy~¢¤Ç¤Óa}îX2±é,?}×ädbÖHíš·Û©žnx]Ѿ~øçμjïþ9çˆòÜo¾hwضE*ëny¯ÆË�ŠögÚÝmdÓòî-“—}c»ó±³ðæÉ×çE“®ÍÌÄž´�)|^íÿ;«—tOvqë´|¿Šå]\lìjeKÇ9‡žyãÓs^´ÊÅ¿sÅ98÷_�=bŠXï¶H2"ƒ*àŠ»%¼lí{‡”ôŸrh.ÒòÎ5‘[ñsq©“7å¼~y§�O̸~Ï'íÇßG�uøóV8åŽû$ÇÈŠ ¨g‚*ñ<”ñ»µ¼OíþSҽš»MÊøÞEnÆÍǦL¼–qùæž=G1â,ŒlK¶üc{=×ãÍXã–;í’ ˆáʆx#© Î;ÛããÈ®eã‡qÙ5œ��IúK‹ò|82kÄ{;�gdåCÇ;�í'ŽË6Üg}f–WÓú>|8vC�‰“ZòÀ¬Óðj|Œø×¿Äÿ‰¾£ò[¶¸ön«È®ýòs¯áÄÕl¤—7®zWeÔ}ѽÑñlíU’Ö,nIÌstø›­¥¹ûÎvaaß6F>e’öÈ/�;¼]øºê¿!;KA™«ïþñò;‚Ù³eõq³ºû¨vWÛû�'ÌÖÒk­Æä«/U‹¸ÙÛ=¶äâÛLL9"ÇŸ*Ù{äǵÞ/ü_ug�}£¡ËÖwïxyÁ¬ƒ[±¬¸ÙÝÔYýYÛ»�/Ì×RZÛ�È9V^¯o³¶zS'Ûq0ï‹|l«d’w›¸¾|t'ww Ä×ùƒä�™½W¼åón´ g3éî¤Ùô�{^'Ñnr.ÍÓí.È×C½å0Y‡ªÈ»u<:Üè²@ád׊Î<¸þ�ˆ_Ÿ…/þï?ø®ÿã•§áŒÿ»sþ-ŸöýJ³ðÉÿݵÿïû}§1ø/÷$¿ñ;ÿÆ™/Ä­¬N`¡ß!¾õ?ÈWCìz§ŸÅ�™é>û»êÑÆÁ�'}ÖüÊ\kb·*/K¡›gÅ7Ô‚,}Þ¦é,‡a�e—ÛX³1°²±´ŸÏ/:¿Ï’Øu:²=/+Ô×/sÕ}“‹‡fNï¯9}øôŠÌ¸¬÷Á&ÓŒîmŠ87»¤²,ükmºÛ¡ËÇÃÊÆÒŸ;üë;ºOaÖæÈ´¼¯S\½ÏVvF6™;®¾å×ãÒ+2â·ß›>5¹¶(àÜjî’ȳ±­¶ën‹/+C¾Fþ>ºgä§ÆNYã¯nÙ]>^MoäaÙX:è¶{þ¦ì¼,¼n?͵Reë¿KbA÷¹1öz˲q¬Úë'ŸëcÉ|y0ÖÝä§Ž±âossNˆîž?^?ÎxV}1ò~…òdé·º¼›~¾Ÿ”q�•ðãþ–ã{ü­ÈÄŸÙþÛ«±Å=’Åe{žDx÷Ú>-öï.é>áÐþ‚æœG6�Ív=òdé·º¬ŠV]G'ã[+áÇý+Ç7¸u¶lY½‘ÉJV±ÍSÇ,1×Ñä7�½¡âïnrÞ”íýè>gÄsi Õ‚ù2tûÍ^E>®§“q½�ðãþ”ã»Ü:Û64ÞÈ䥵¬rÇñË•8ù±á_}øä/0ñ·È®/úšñŸ£²ÒîõÕËËá�‘Â6SåÃÇ;#®wÙXx?Â.Ⱦã5‘Mô¢ÉÃÍÆÉ×çA‹±ÂÌÄÇÁ,$Â�NLWà7â3ôD|WÏ&x¥JäE‰¿ñ›®ùÑI§Ž²M~'tòM.~$u®Ë&+bŸ‰RúÝd0IMµ¶Ö[µ³Á-ƒïŠÚicãmyÆkúfx±·~;õþ÷h®ÒEuò×·y£7:ÝµÉŠØæâöß[£Ç‚JlémÓݯ›Xÿ?¡£ã>kùÆkMÄñcnüxàÜYb¿M×Kv7nr=Fn,u»i“#›Œ[}kdßM�-¬×`M�`ÏáÂø-Íè .+òåÿ›»ùš|¿:O{¬Ÿ3¥øææ ðïìîÃÃØÁövÇ,ÔKuº�U-¤|kO“tùwK·Ì¦6‚\iL%"˜¸°”ßÚ3æ�ò…ìoêäêà~N?h—ßÙÿO+Žù4ý \ÿ~þgý<ªñ~Ùï5}:CþM=2ÐfŠ´aÄpÍ¿ ß÷3óúúêê9KÇðÎÿrÿ+?¯Þ´þǹB]†“û˜ùUý~u§ö?Ê5~ÙÛäòÒæ¨Þ‚K 'tœÆFwçáÏ/É\-¿—>.q«2{ÿI®¤ý­×¨¤®ouñíF¿wÅñlº±OÙükYƒlT²ÊI¿Â¶‘E[³àǃ:;¿3?Y>Falü¥ñ»�G7{é5ô—²¸º9>÷Üz N>.&Ó�ÁmÕŠ^Çãšì+b³Û)~ïÚCÕ̃¨ñüÊ|Näy‡´ò�Ç?dÝë¥×ÒnÊàzèïû×qhuX8ø¸»^?·V){Žë°­ŠÌ[l¥û¼;i—W2xr£óÿðWªóï‹gyQâÿÒé<Ôá:[#二jãið¼�áº-tXºÎ7Èr$¾ l]³Åu¸VbñÍÞEb®f –iö3]‰ªmDf†\ye‚x¤‚x$¾¡šË£–cº¶I±ßKoŽHï¶´ºÚÒ•¥iéT/¦†li¥ÇÈŠ\|Œyd†x&Žø¦†h¯­’Å,WÒÛã–;í­.¶´¥iZzU ‰¡›iqò"—#Y!ž £¾)¡š+ëd±Kô¶øåŽûkK­­)ZVž•V¹½Ñnø¾ïsÆy6›kÇy'Úì4\ƒ�ïuùz�Þ‹w¨Ë›_¶Ónu;qóõ›]f~<�dcÏ“C5—Y}¶ÝmiOæþOäù@ Úð7ÀÞêùê| ë´šêáìû3³6xsÍÅ:ÏŠM=Ñ_¶Û_Ðþ�Üì>Œ±êµQËfNË&Ë©K¢Ç‹''qü"ð‹¸<êî >°ë ?ÑÚ]wÜö]‘Ù,9çâýqÅçžøë´ÚV;àý#ºØý cÕj£–Ì�–E—~t8ðåecî/„¾öÿœý¿‡Ö=c‡ú?K¯û¦Ë±»e‰<üc®xÄóßv{:Ç|¤7;¡$z½\rY‘±È²ï΋,œœ~–|^ü^÷ïÊG~ãõOTãßźë‹_«Û÷�xíõyÜC©8†vDöC,°Ù>¼—žr[p²!ãÜzˆr6¹Ë$’âk±6 +"¼JñG¨<.èÞ%н+¢·UÆ8ä?{ÜmòmŽNEι~f6$<ƒžs „vY]—%ä2áÇY.¥-ƒ(q1cƒ¬ñsÆ.¨ñ ¦8·Hôö’š¾7ÇâûÖÓi“H¥äוeããE½æü»aqþ’ä›é1l¬—RÛ Æ‚8qqcƒ¬ñ{ÆN©ñ¦¸¿Itþ–š¾7ÇâûÖÓi“HåßóNU—��ó›rÌøã�ô—#ÞÉ‹edº–Ù4Å‹�8˜øðEk�ˆÞ&ô§„]×þ6ô»�uï_êìÄ‹#:üL®KËw³Ûd›þsͶØxZèw|Ï•ì)vV~M�cÁKî¤XÐãâÅ<[(ØVÁ¶LÏÄ¥û9°¿”/XÿPùÛ†¿ˆOö~Ùí¿¤y[‡_ˆ7öÿÙ¿­ÿ¤y[ƒÿ‰wö0ùgûéã×ü¥º…¤PlU^Ïþ'ÿo¿¿ÿMþ±xã=x­ügüpþÿ]?úÂã¬ïâ×ñ›ñÏûûõë�7›ãö–|xÿ.o¿_¼kÊÎåœ+’€' Ñr½ãŒr�.§’q®C¬ÎÒoøöÿ[‡¸Ñï4ÛfïI¦äº}¯äz�fÿ�ïuÙº�Þ‹w�‹µÓîu;y16½®³:)ð¶ìüI¯Šh&²ø¥ŽêÛuµ¥kGäÏÀÁÚàæêö˜X›-fË'c®ÏƇ3?2ñòð³q2,“++K£’9-ºË캶ÝJÒµ¢!ÿ&‡S")7ýÕñ÷‹løÞÛö|‡Æ]®Ò´ÊŠï©O¾åt÷%ÝeV̈½—Ö{´;\›d¶–KL¹.»[lVþE>&¬»ÎàðOˬ¾µØïüsÚl©Ö]uÔ¦nOSr-¾E"º?Îú÷höy{iIi…•w®6º‘bùøš’o;Álk/ŽïvÇ}ã®Ïeì’Ë«u>ù“Ôü‡o‘ôï³Òï¯v“g‘mm¥²Ó *ê×_Haü¹~~;ØÙ‡¿þ2°4|žl¶Û}ÿ3ñKs·Àã}kÈnØC.|’ô>óc÷m?YmnÝÛu¶q½žF?¤¾Ü­.¾¸“ò>7ÈxvÿuÅ9n‡sÅùOÚgi9äZÌÝ.ûC¹ÖdI‰±Ôîu(1¶Íž\WE4Gd±ImmºÚV•¢,܃�oø–÷oÅùV�qÆy/Øæi÷Üw�kstÛÍ&ß_=ø¹ú½¾§c6~·cƒ“ÑÍÑÙ,wÛ[n¶•¥h‹ ãÛþ'¼ÛñŽU¤Ûñ®KÇö9š}÷ßës4ûÍ&Û_=øÙú½¶§c>~»c…“ÑÍ ÑÙ$wÛ[n¥+JÑ~ÀëÎ}ÔüË�u×hðžY×<ÿ‰ç~�äü#œñí·åœ{aXbÉ·uÇ·˜˜;]fMøÙËm“EenŽKo§­·Rµø¯Žøïɽ;Ó=§äbq¾¦éŽ È{°ùfe¸ZN1Æð®ËÌ›í¥rs³fº±áiôšÈ=gÍØfK5—Í‘,QY}ôÈýMÔ=›ÞÜ÷AÕýAÂwý�Ï96U˜šŽ9Çpë••'­ÖÒ|ìÙïº,>—]ßW3?2X0pqíºl‰cŠË¯¦Fê~¢ìÎô纰ê¾çüó“e[‹¨ã¼{쬩)î¶“çfÏucÂÔiµñÝõs3òåƒ Û¦žXâ¶ë響I|lW»{ƒeÇ|€ó 7S��5ý%ÝuÒ{Ê»ýWJkwxñìrw¹¹WÉ‹?0ÏÇÃÛäë,¦>&¢ ½ž>tƒ�ØwM"@¾yAâÿMy…Ó\£¢ûÓ‹ÇÉx_%ŽÙñò º,^EĹ,Yé¹§ ÜÉ�“~‡–hoɾ¸ù²H¥ŠIq²bÈÃÈÈÇ—ùã�Rù[Ô¼—¥û£�Eȸ"ŠÙb–+£ÆÞñ�î4sY©å¼Km|3ߥäú[ç¾ì|‹l¾Ëì¾H'ŽlY§‚\äoŽ]Må_SrN˜î~7"áüŠ+eŠX®�yÆw˜ÑÍf«–q=­ðÏ~—“iožû žÛ/²û/’ ã›ià— yãŸMycÓ\ã »÷ƒê»«»U~¯�qý¥—[|wÛu³ë·Z]ŒGŸ¡äºøãË×lq$‹/ .+%Šûo¶•W¡òSñ[ÞŸ|ÚÙ÷±MØ Ê6™8½wÝl ìÖÏ%ožl^)Îñ,¶û8‡<³/«L{ï¿a’I…4ßG*fÃa�•-‘C V_$²]Km¥kZQô´Ú]Ç#ÛêøÿÔì·ÛíæÃQ¥Òi°r¶›}¾×c‘&¿Y«ÖáE>f~Ã;*["†¬¾Idº–ÛJÖ´£éi´ÛŽG·Õñþ=ªÙowÛ½†§K¤Ó`äìöÛ}®Ã"v0çwG|˜îÊ‚k)÷-}¿[_¢ôú÷Ý‘°º/Ñsø¢øSÂé|ž?ä�—º]nó¶±ëƒ¹ënŸÈ¬;-VæE|9¸|¯™_ò`ï»[)÷,;>®¿Iu>½nÈϬ7kf ñKð³…Ó9Èÿ.´ºíßlA\-Ï[õOÑÙi:¿.+áÌÂå\Æøï“{ؘÒÙJááÛõp4µ§×ºìŒúÃ]uЇ?‹xc‘Ãü©óOŒö/•pcj¹êûi¬äÜÆýß¿i…±¿eoß5<ߺ8ÜñÇKv¸—I§ÑgÙ%ú©sd‹mYE¤�’V iü˜|iõGÈ¿QS�o«‹Ä;›†bì2z‡¶"Ĭ¹|{c“m²ÍÇyPÒ“îx.úxl¦^7­eƾ”ÉÇô–ÚÛ/=~D><ú¿ÏΩþoë�Å»_‰cçåuOhC‹õ³8îË&Ûo›I¼Š/lÛ~»šíÌÅõ÷Åu-ž Û-ŸŸÏo�ï�^°óçªÿƒÛêãqn×âxÙÙ=UÙðâý\¾=±È¶Ù%Ñï#‹Û6߆næ†ÊeãzûẔž Û-ž—óäëãÇÿ”òz¸1¿ƒ<ëŒþ•ÝtoxéuxÛkÓ\×a��ùØ0K‘¯þð^Mú?KƧÊÇÄÝâcÃ}“aìðõ{M}u>Gø×ÜÞ&öÇ én÷áyü+œñúÙ?ݲ+nN¯{¦É’k5¼›‹î ÷`ïøÞÛî÷ý ¼{®·ß‘IK'ŠX¬€w�ž;öï‹}£¾éîìâ9¼Gšh½™Ã5i‘ªÞé²dšÍo%ãx©\=ïÚýÞÿ£• Õ¥$ŽHd¤sÅ4QÀoÈ/{sÅÞÐÞõuñ,Þ#Ìô~Éé Õ¦F¯y§É’k5Ü“Œí¢õÃÞñݧÝïú90Ý[i$rE%#ž)b²ª5<'òÀûå:ù%Ãÿƒ<×Aé±Ònµ³O²á‘Ã2òó1t=�ÖüŽlLá ä_q—èË|8Ù¸y1Oƒ°ÆÂØâåáãà–aF§$»ð=ñ17�¼ÃKæ/‘äçº<»)ó7_“�K7]aÃ6q]õiîû¦ßo�÷9>¾6>Ãùü%ü\MÞœ«Kå·~ñ™+Ò\+m\¾®â;¬iaÆíŽm¦É²°oó°ò ¥»^·â;®­ÖÒï»mvØôÆ“êâãçcÍ"?…‹ÉûÏ•i|³ïÎ3%zO…í«™ÕüOu�,XݱÍtùV} îvD·k×TrÁeZÿ _žOül÷ýQäg Ÿ]�ºŸ{‘ÕýŸ§¶Lî¸íÞ9¢Î‡+}·öÛô®ÊÄ‹;]†§+èn5çc}órqî—XZÜׇ€¥kZR”­kZúR”ûkZ×òR”þh?Õ–_-öE—É$—ÛdqÙmo¾ûï­-²Ë,¶•ºë”¥>ÚÕ,ß…ÿƒœÎM™Å¼·óc†Û‚Ýw$鞃䘶_71šû"ÏÕsÞÖÓÏmÔÆâÔºÉuÜ&ÚK¶–”—a˜[�²”�Ä?Ã^ï/�yKæ'¤n kùQô!Ʋù¹,×Ùv³›öv¦j]L~9�K¬“E‘m%ØKO©�˜–[��(Ÿˆ¯†œ½Þ_ò“Ì%H8ÜÀäGÒƒËåä²ßdYºÎoÙš™©u1øæ=.²L E´—a->®tvbYn>t÷~îgWæp¿7þ@¸5�v,jùgBxÕʰ­¾~¹žûcØi»'º4YQÖÜnÇÆ¶±Ï©ãY6ý^7/¤û8ãÜGn&ªeik¥žšˆ°”ßÚ3æ�ò…ìoêäêà~N?h—ßÙÿO+Žù4ý \ÿ~þgý<ªñ~Ùï5}:CþM=2ÐfŠ´aÄpÍ¿ ß÷3óúúêê9KÇðÎÿrÿ+?¯Þ´þǹB]†“û˜ùUý~u§ö?Ê5~ÙÛäòÒæ¨Þ‚K 'tœÆFçåãàÏ�ùK/(òKÄýn£ˆy•\­ß8ëêÍ‹¦âå°­¶_“²ÇÉÊÈÅÔpþÌÎ¥—]&eõ‹]¹Ê»êg]L³çß_•_†“7rO ¼b×j¸¯�Ó}çqËøEfÆÔq^êÌ¥–]‘—NTøÚ®)ع–Ç[¾û}bÀÛdÝîκ¥—9¯•†m’÷ò? ¼d×긯�sÓ#qËøEeÆÔñnèÌ¥¶]>dY93ãjø¯bf[ek\Ëë×"¾ìÛ¡šIs‘šùËøàß z=ï’^0ktwæÞ—¹»|Yf‹MÂüšÔëµñcAʤÒ˳Œv~.–hy=i4§èÝÕnÅ¿ g£ƒo+â|£‚r]ï æÜwwÄywÚæèù'亼Ý&ÿC¹×O~6~«q¨ØÃ�Ÿ®ØaäGu’E,v_eÔô­(…ÿ(âÜ—„r-׿\uÅ9_ÙeiùäZÌÍ6÷I¶Á–è35»]VÂ|Ü ìY­­·Å-–ßmiöÑ žQÅù/ ä[®!Ìtž+ʸÞË+OÈ8ß!Öæi÷šM® ·A™­ÚêöãæàfâÍmm¾)l¶ûkO¶ŠÝ»_©»3¢»•õ'qð^OÖ�™Á¶_¢9gæ:Œ½'!Ñç]�n=¹x‘Ç%q¶ì¸r±2,÷ãæaÏDÉ ±ßwŸ|ÂcÐE>=¾4<ƒùì84�wª›Šõ>—k'e÷Žû]<¼7…c[ Y¹Zý}Ÿ[îaÍæÂš?ºip夵¿"2åÃúü¸÷÷Àߎ®öóÛ�Å©àZéx¿Vé¶‘aö'tnõóKÄøŒVÁnN»]Öÿ–ó90¦Ž¸úŒIm¿Ý‘ ™Râbß\›wçÁŽÞõóÏ�E©àzéx¿Wé¶‘bvsnõóKÅ8”VÃfN»]Öû–s)0掸úœYm“Ý<2eK‰‹}rmì_įÃ/‘_*<û+/Ž[“ÕÞ4p�Î>¯µ<ƒÝj«—©ÃÙß�n¼®µ3äëëÏû]|‘M“��-¸z<\œ|�”ðW/]}ˆž"ø�Ñþt曥:'Ž_¨ãØݱÞï¶—ãæòþuÉr"Ž,þYÍ7PbáÓm»Ì²,§²(q±qã�qâŽ+g¹âÇŠ}5áÏRiús¤øõÚž?�%û Îçc|¼¯šò<ˆâ‹?•sÌ8Ø”Ûo3¬†Ë=mŽ,|lxãÇÆŠh¢ŠÉéx³â¯MøwÔÚŽžé^?v§A�%Ùû�ÎÆø39W4äYÅ)æˆq±?Jîóm†Ë=mŽ,|h#�(qâŠ+-ðÿÿ¼è¾1ãÇ�œ„õ÷“'e™%ò×cɹ§+ÙG�f÷�óÎE5–æò~a½ûœ6Í“/¶<|\||,H±°111`ÚǶ5³Àþ~%/ÙÍ…ü¡zÇú‡ÎÜ5üB³óþÎmý#ÊÜ:üA¿³ÿþÍýoý#ÊÜüK¿±‡Ë?ßO¿å-Ô( ƒbªðñ?øÓxÕýÿúoõ‹ÇëÅoã?ã‡÷úéÿÖg¿ŒßŽßߨ¿Xy¼ß´³ãÇùsx•úýà ^Vw,á\”ç�¿¾)yý¥ž~ÓâUâý¯‰­¿ Œ÷�SØšÇd_qÄÝËXo×sž=�tÙL ´YB 妸rËYé ^jülxÅç6žY{;Š×�vf&¾¸\s¹x]˜º®{©¶/eøx›\‹ “˜è º?e0v‘d[RÍ÷Kñ&’³ÓA<ÓøÚñ“Î=<²ög¯ìÌ]}p¸ïrpË1µ\óSH©eØx»Yë ˜<ÇC�t~Ê`í"ȶ¤—î—âK%f§;|ýø±ðÇ䣈áòW¬£ÈæŒ(°8wvðip¸¯vp\\y6³A�¡æõÖì­ÙñاÞfMMëm »/"¹W`×*ȦŽw|+y…ᛞYo“¼ú3윘û{¬uy¹´Òj¢»2Kr;„Ùvo!à—Á…‹Ir²ë÷ÝKdtÙI-ÞÊCÇÍ�‡¯,<8¿mÊbÑ_Ý}-ƒ\Œ‹{S®5y¹Wéu°Ó.jäö ¶üíï ¤x•—'+Ý�¥ƒêYgé ¥»ÙH}y­ðÿåw‡wí¹Dz+û«¦0k>E½§×:ÌÜ«´ÚØ©•/Þ{†Y~n÷…} L_©‘•뛥ƒêYgé ¤»Ø¯ÿäƒðÜùÅàÝû^yÕ:Ìß0<}IJ\¹9ßTq]��‘Ãðqðq2³'ìžšÅËäíf»[³+úWK“½ÕÃ…\­„ÚÛ¦³œ‚r‰Êtx@žøùøEò§ÍûôÜë‘àät�y¿êŸí£ÎtÙï9^!Çž?ígÀfÈÖm¹DÖåÇôö¹2`hë&¬YyÁ\[ú÷à�Ãg“d]§æÜ“~‹èlßnWöÇæZ©®Þò¬ÁñW®x<Óëö|‡6™}=žUø:zÅY/‡'&hnÆ»®Þ |8ù/æ5ú~kÈðçèΈÍôÊþØÜÇU5ÛÞQ…ôbš/íuÁæŸ_³äfÓ"?§³É¿OX«%ÐääÍ q®’_Æá¬òóÍœž/Ú^Cáî±ë�¦=³ñÜ]†>¾;"ßòhð°íÇÙbìuØ[Ü{d…9 < ñŸÁ w è ³a²ƒÞcØ»ûàÜöO>ÊÅŽËlÉå<¢¸Ø×Ý‹d¶Ý,:ì(°õ8’É%øø±]$•ºg$xEã·…"¼;£8T:ÌÝ„vrÞ}»º ·aó¬¬8ély\§’ýÛI`²Zß,X‘âjñ$–K±ña¬—û¦_âW„¾ÙüCÀ;‰ñîsÂ9NúÎGÄùV§ {Ç÷Xßd¿vØêö0äbdÙdñY%žëkXå²Ûí­/¶ÚÓÌó.Ä{‹nøG=ã:.eù. šÞAÆ96¯ u¢ÜàIu·Ý‹±Öl!Ÿ**I·ÛK쯶ûmºž—R•§™æ\/ˆv/ÝðŽ{Æt\ˇòLõ»þ1ɵx{­ãK¬’¸Û fÂñ2¢¤±Û}¾ëk[$¶Û­ôºÚVŸ”ñn1ÎxÏ"á\׎hy‡ åú=¯åœK”ê5üƒŒò~7¾ÁŸW¼ãÜ‹C¶ÇËÕn´{�fT¸ùx™1K�‘—G%—YuiXeü“þŽuÖór>ãðB¯euõgÙî6Þ>åÏ.epœ a®}ÐõÖÏ*y2{?K�[&Š tµþGe1â�ô¼òK-‘ù ø æ]&ÿ¶¼$‡gØ—ìvÛ^‡ÌÈ›?°¸Ž–W2èº÷g—4¹=�¨Ç²’Ǻ{¿„[d1EvÚyo¾È‘|„üó.¿—Û>óì õ6]§DåÏ.`ñ;#®eÑuþË*i2{Q�KeŽ|×,¶ØbŽ»Y¤¾û ÉòËøV¶:_á/}ücG6ÛI�‡vß’ø�É7y|‹Xò«LüŽˆæ›ì‰.ßb[¯šÜŠñÍþ]6×"¸;,é²0õ1EÏa¯ÏÔçæê¶¸9šÍž·/#c®ØcM…Ÿ��‰-ðeáæáäÙF.^4ñÝd‘Ém·Ù}µ¥iJÑ݆¿?SŸ›«Úaeëvzܼœ Ž»a�6~v×ãåáfâdYF.^.DwY$r[mö_mmº”­+Dmsð3µYÙº½¦^·g®ËÈÀØk³ñ¦ÃÎÀÎÄ–ü|¼<ÜL‹#ÈÅËÅž;¬’;í¶û/¶´­)Zz!OÊø§)àœŸ�ðžqÆ·ü7™ñÖÏ�ò¾#Êôû;ÉøÇ"ÒæM®Üè9‡o�‡µÒîµ; y ÊÅÉŠ)àšË¬¾Ûn¶´§ã~Gä|ÞxàÇ’¾róë8'�½›¿‹/_.ç{_«¨ë޾ÃÏ–¶Û³æ|²Ly±°m·9g� Ì­¶lpIL5¾ü|øýÖë¹¶e˜�Á䬸yQí»�{ª¶ ~/nÊ âæhú¯�dM›×SKñ¦ØÖùw %ž’O,ôÁ†l¿Ÿ}঻ —Mf?kù ‘�‘fÓ·7ºÈ೎ٟ�\\½/Zh¥—2>'¬®—Á6md—kŸlÓRYìÆ–Ü8fµñïñ?Ñ ë°¹töcö¯�sâäY´í­æ²Ècã¶gã×/KÖº)¥Ë‹Šk~é}ðM›[åÚç[4Ô’{1¥·%~,¾¼RøµãY[. �7ny Éuøx\ßÈÎy¥Öcò™1¢Â¶Þ5Öz8dØãõOÏÎ’l™ð12ó6; ¯ŠÍžËcf1{껪ŽÂ4sÎß�Ïþ@ú²þ¾î=%Ø<�OIòú뵸ô�óÞ¹ÜKKk|ºŒùâ¾Í—Ú{-�e§Ê÷ágGKo¥"ˇ/M<Ôð[¢¼éëJð>ÝÒÝ‹½Ôý|¾Ùz±`çm5¶ÒYµ ¢’ÌíËéÛfÇU“I0³l¶ËëlyPâäãé·š^ ô_œýkwí½-Ø»ÝM'Êà=—¢‹oÀ6Ó[oÔ›SŸ4WÙ�¤Øý;lØj²i&m–Û¶<˜q²`Ò�;>>¼aùéÉúgÉŽ~ó]‹~fw çz|]7eu ÍdzNI×¼¦| •º¬ûí†*Ï‹•�›ªØRìÍÄÉŽËl¥~~}üjyñïÏ&Òv–†NGÖ{m•ø½}Ýük*þÍ1äŽ|œL<‰n¬òq>ef&4•ÊÒç_Lˆï†[ñ¯ËäYrÁSÎ_Žß ¼æ²ê{'K!ë}®ÎLNÜÜw&îËá¾9²pðò¯º³IÅ9}0ñä®NŸ6ÿ­mðK~4™x–Ù—$üâøñòÁ.k.§²4·ò¹Úl¯Åà�ÉÇp²ná\ºìŸ'*û«5üW—SJäéóoúÖ] ·ãI—‹m™RVCò�ð»å—ÅÏ'¦Ã°uÑv��\ƒw™©àGp}fT\Og-³]v·EØ+ò69ýUÎóõ×Y5ºÜéòprï¤ök6;*aåI=šÑ!BøŒø3æ^Mgñ"ü²Ñíø?�–×_É8§Í·+OÌ{ã ïvFºûédØ»n!Ö[×ì+Hów87ÒšêÇD{(¤#ñ[ð½Êü‰Ìâ¾@ùG¥Úðï©v!✠6Ì�_+îÌ:û§Àž{m›gÅzß>¶Ç-s+ly›œ+é\§ñçÛ ¯‹†>Wä>oïÿ(t»NãýµÁäW�æÙ‘«å]Õ‰_tøMe²ãìø¿\gûc–¹•¶<ÍÆôû�Ó†xóí˜ÿÁ/áÊÞ÷NOó'ä ‡l8ÇMaçâïz‹Æ~Y§Ì×r^äûœ’É‹Í{kS±·/Œõ7ÞâŠMfšx®ÌåÑû²2,ÇÒýÚ»ÙÑi4š^3¥Ôq¾7¨Õñþ;Çõzý&‡C¤×âjtºM.§, ^£Q«À‹[«ÖàãÇ><1Ù1Ym–[KiJRhÚ}>§�juz ¯]£ÑhõØZ}.—O…�¬Ôéõ:Ìh°µÚ½^» (põúí~6EYdQEe¶Ûm-¥(™î£Q©ãú�^‡C«×i4ZMv£K¥Ôacku:�N·,-v¯W®ÂŠ< v6EYdQEe¶Ûm-¥(°' L<TY²Íšûä·‹½'åÇYlú£¼¸v')㙕»+UŸßqäÜCylWljɸvþ+.ÌÑop½ßeÖû ÉŠ·cåE‘‹,ÐI®ÞRx�ãך]E·èÏ&ºÃAÚ�k·ÌÄÛSO¹ûÞ&~��kâʃ[Ê8—"Ôäëùåülüˆ"Økr±²¾ë•>=×Ý�‘¶�îÛ®´éý½Õ²|M¦d—Ù~'>ì]-´§ÑÊ­.Ôjs+t¸vedÁ‡²Žcß\;Ç ¾5Þ¾RǤì~õÀú;Ž-Àbú;~ºêm¯­²ál§ºK.ÆçÿOJRè²ï¶º½^ek&™ø›+f)ñ�ð‘ürËã}çåZNÆï< A·âü /£¶ëΧÚúÛ.Æ{¤²ìnoÏu¥.‹*ëk¬ÕåÖ²aÙ‘‘.ÆË>/Jø#'ïï)/âþ@ùy©Úãr-v,yyý7ÑyÚë­—M•À5[­~»+˜vm>ûü%ÚâÅú?&ÌoÑXXY—ìs¤–�zAI7+ùMý£>hÿ(^Æþ®N®äãö�ùwýü9Ÿôò¸ï“OÚåÏ÷ïæÓÊ¡ÿ·ížóW÷Ó¤?äÓÓ-h«FGLÛð­ÿs?1¯®¡þ s”¼ ï÷/ò³úýëOì{”%Ñøi?¹�•_×çZcü¡cWà÷ý�¾@-.gú�è$°’wIÁ,`,~E~%|pù Ò_¸äxÿÚýõ˜TÅã=åÅ5X¹;«¡†C‹¦çÚJÏ®Çì3�m–}(gÈÇÏÃ¥žÜLÌxïš9¹©ç×Å×�žyénÚr Á8žºÞ=Æ8î xÍv7Ô“"kíŽ:{ò3s²æ“#+&k¤ÉËÊ–Iæ’Id¾û§«Àºÿ‚u_ãÝyÖ\3‹uçâZøõ<_…p�¯‹ñ^;¬Šûä³I Òbájõ˜”–[¯úpÅeµ¾ë®­=kZ× =ÃÜ=p?Ÿ‰Kösa(^±þ¡ó· Ÿìüÿ³‡[Hò·¿oìÿÿ³[ÿHò·ÿïìaòÏ÷ÓǯùKu H0 Øª¼ŸüOþ4Þ5þ›ýbñÆzñ[øÏøáýþºõ…ÇYßůã7ãŸ÷÷ê/Öo7Æí,øñþ\Þ%~¿xו�Ë8W%8ßæ·ÁÏ„¾aI·åxZO{ƒc÷¼«»§ðuÚ­vçk=¹2[™Îzîø¢â|Ÿêçä×#/#Í^ç6úR’l}™óáŸÃ�,¤ÚrŒn1/Fö¾}r²¯ì§Ä×êq79é—-2ù§¾x·%¬û ºäeäûq™}´¥û [ö99å÷Ã_‡~WÉ´äøÜbNŽí\ÿ¼äߨQ‰¯ÕaíösÓ&Jes> |6ñ~Iõ³ò«‘—‘ zí¾eô¥/Ï¥¿cŽ>~|üzü„ålùaõŒýMÝ{9ïÍËïnˆ“SÁ9æó:I/Ÿ"~w¯›O¶á�>ÂjGnFvçU•º¦NÕA‡\› Å¦ï«¦­üÆŒ�ÓëMn¦ÍÞ,uõ¿/ìªpþ?�¿Kò®‰ÆÓù©ÕZÜLý�û>ªÀ»�w»W¯ÕO‘~碷{}–ßq°Ùlsr ×`ñ §/ÎÉ‹éf‹é#мVËÄË×åd`çâäafâM&6^\2cebäC}cš Œy­²XfŠûkmÖÝJ]miéZ8÷—‰—�•“ƒŸ‹‘…›‡<¸¹˜ypÉ�•‹“÷E>>N<ÖÙ4Ã-µ¶û.¥.¶êV•§«�9x™xY89ø¹Y¸sË‹—‡— ˜ÙX¹0_tsãäãÍm’Á<2[[o²êRën¥iZz£—ʸŸ)àœ—{Ã9¿ßðÞaŶ¹Ú.MÄùV›cǹ/Þk2/ÅÙi·º-¾6ÓQµ×åGtsãäEÑImmºÚV•£ó¿;ó¾©~|9ùÏæeÚ�çêüž²êÍ•1ò)ÛÝÇno âY:ìœirñ¶c[‘…?0çx™±ÃXáÉÓës0)=ÖÙ>LÝï§L$ø·WÛITøgð“âGŠòjùo2Ö×ÈÎÚÁúSÇË»+OƒüÒgÅt•·+‡õ�eÙèµ³G_¥$yI÷Yø¹0Ò\\Œz×ÛIPømð�âW‹2jùo2Ö×È®ÙÁúsÇË{'QƒüÒgÇtµ·+ˆu•eÙèõóYëñäm&ÜçcdÃIq²1ë_m'ñ×ø<øù“Žs¼n�þDémÖgÓ¼û«[¹—�ò!ùí‹™ºì^!'îâ}oxuŘ:NsY Ň_.ŠLYôÝ�¨Æ³©Ò ràÅŽè°²°ý÷^çgšÞ*ùÁ‹•¶ì.)'íjbÒorõõ0´¼Þ—A<8xüšÙ1r5çUxqCH¶�O‘�‹K£ÃÈÄ­õ‘Îï4~0¼Wów+mØRNÚ¿u¤:îäëûp´ÜÖ—CáAÉ­“}O8ÕAf$QR-œdA�mÑáäbVúÞæ�È/Ä�„%š8bò+­eÂìmf¿UÆ;ë­2uü?»8¾« c]„zlnU>£uªäÜzŸxËŽ=_!×nu˜•ØdÏ‹�eôɶj|yµâmûžQÄ8ÕÞJõù2ÃÍú“WŸ›Ë5š˜«<�æs>ª¶ìþU¤¾Ì<{çÊ›[vïU…=fη֔¬L¼¿øJóÅûö¼�ˆñúùÕxrÍ|<êõyÙ\³]®²µ¬Y<·«­¿?“joú6_,òëoÝkpâ³Ý>ež´¢'^^ü(y‡ã û^GÄøý|ˆê¼9§’aÕšÌì¾U®ÖÙ[®‹+–õ…·çr]MôÇŽùr%×]¹ÖáÅoºlË=iD|üü0^|x™]¿4è+<ÖéÌI,’,ΦÐgà÷žŸûxî-?„]\­Öëw$ûÝÔøø¿Á þS5Ø:ùsó¡ÖE_§g/²ø¯¾9,º9#ºë$Žûkeö_ekmÖ_mÔ¥ÖÝmÔô­+öÒ®<ßeñßtr[u’YuÖ_eöÖÛì¾ÚÖÛ­ºÛ©K­ºÛ©éZWí¥\¾Ëã¾øä²èäŽë¬¾Ëí­·Ù}µ­·Y}·R—[u·SÒ´¯ÛJ£{³Öl´»-†›s¯ÎÔíõ9ÙzÍ®«g‰>ËY²ÀžL\í~Ã*8²°³°²¢º9b’ÛdŽKkmÔ¥iZ?Ëü¿Ëð€3gDøßßNsx@õO4í^S}`»'‰éçÌÄÓãdäY‹Ë’îåú.+¦ûÄ–ÙvvË';®§ºJ3'IxóÝþHrØø?Eõ‡/ìÞIZã×+Œj¥ÉÂÓãåKô Ïä›É«‹‹êoŸó+™±ÉÅŶﲲR¬ÇÒ~>wo‘ܲ>ѽcË»3’]ô.ÉÄã:©rpõùÒsùêjÁ£ã«¦­,®fÇ'Û«JVJV­±ñCÁo.üäå¹�ë¬ðàÛZÓ &¸û±á² ÌšÛK§ÆËÁãöÃKo¶Ù`ÜIekmd±á—áÝÃÅ»UÎ<Úæ6ld¥1s£èþ²ÚÏ·V˜y5Áç�� �eäÖ”º|l¬-°ÒÛí¶X6ò[ëmf—ñÝøIú³€Iªì‘Ž‹Ý\š/Lœoº�mȸïR믭»¬z[Î;"‘qÎÃç³V ¬ »1u1qŒl †$°Ë>ã_JÊ¿«º§­zOƒèºÓ¨ø7ë®Ʊ©‹¤â¼KS‰¦Ôa[Zû§ž¸ø‘Ù÷­†tÕ¬¹YSVLœ¹ïºY¤¾Kî¾²l뎴ëî áº^¼êÞÇ8ã¸õÆÓqŽ+ªÄÓê0¬¾êÉ<ÖââG³fæÏuÓdäIïŸ&{î–[ï’뮬™zç­:û¨8n—¯z»†ñÞÂxî?Ý´Üg‹j±uœ+.º²O51±#Ž“ææOuÒädI[ò2g¾ée¾ù.ºêÌ#‚ð. ÕüGC×ýiÂøŸ]ð>-ƒn³Œpž ÇtüKˆñÍm²I5ºýÐaëôÚŒe–û©<1ÇK®­}=kV@{‡¸zÀ‘缃ö—ßu÷dñ9Ï87)Âýȸ—-Óàï¸öë “E“dVÊ Œ<›aÊ‚9£­ÖV覎É,­·ÛmÔòüׄpîÈ⻾Ø[AÍxo$î¿ŹF§y¡ÜaÖHæ¦>ÃW±‡#*Èç†É,÷YZÙ%–ßo¥ÖÒ´òüÓ„ðþÇâÛ¾Ïø¾ƒšpîI‡]~ÿ‹ò�VïC¸Ã¬‘ÍL}†¯cFM‘Ï ’Yî²µ²K-¾ßK­¥ið9_âÜïŒrÎ8Öƒ™pÎ]¥Ùñ¾WÄy^›]ȸÇ'㻼9µÛ�!Ðmñ³5[­.Û_‘$X¹1KD2]eöÝmÕ¥bQç÷á®Ê²mßfx½Šlkë.vOŽ=�¼¬y0Vù²$¿¬»+w•X² ²É!Žw$š9,²9$»o-÷G�H·yÏøyf¤»ŽÄð_uÐÝõ32<~ç{»¬–*úä_~7\v&ë&ë&²´º!×ò)¬­´¶Y.Û_ZÇ�HºùÉø{'¤»ŽÄðkuð]õ32:œî®²X«[§¾ünºì=ÎMÖM)tVC¯ä3Yu¶Û$—mo­c‚�Îù7ü'|;™ÉÈ;ãO�ëzã�V ­¦ÇÅÞÆÝí28æ|=Ee¿©{'k.ÛuÃ÷­†¤Z®E&^šLí�Õ·i¥×ãÇ�H¥vgUö_Ls §_vßåÝkÎ4·EM§æÜgÆ÷¸–O&ÅÈ¿]µÆÅÈ¿6 ­– í¥ÐÏÖß×Yu+XÅv?Xö/OòÝ—íN ʺï™ê+gé3Ìt{?¹ÇŽjVül¯¸ìññæ—2/è˜ùRè2"­/Žû¬­.¬d»¬»¨9fË‚vŸå]y̵³ô‡æ:=�ÜcÇ5=øù?rÙA�4¸Y‘~|ÙK¡ž:ÒøîºÊÒµ„—�Þ3yâoc캓É¢ç]5ØZß½Küç<�_é}n.Ûe£þqM¯¤ºNiÃóvš|¨°÷ZŒœíNкìl‰l§¹à^á˜0ŸMØüŠ¿Füët¸vǧÐbÏuöE±å\—>LN;ÅurIel·'c•� òzGmÕ¾ëm®âxqà'–þ|óëzÿÅž–å�““‹�…‡Êyt8•Ôõ§^ÇŸ‰²Øcfvcm~éÄx„yzý.d¸peå[�´¿ø0 ÊÉ­�_3o�ÿÃÛÔ~:eèûwˬ®9ß}Ç…m35{�‰~wIp=‡Ö²ü|ɱ6øxÙ�—Ȱ⊞ɶøÚ¬i&¾–`Ï48ùÖ˳ãëà‡ª¼|ËÒö¯”™zñíÜõf§„A‹&_Np|ï|wcå݇¶ÄÇÌìMþ‘Ö±äl1ñõ¸÷Íw³Iàƒ6’âøýø&êÏòô½©å^‡»ûo Ó3S ſ/§¸FÔ²ì|»ðö¸˜ù}‡¿Ã²:Ö9óññõ¸÷Ëw³Y¡ƒ2–|O~ÿ<ÙñöòGg¦òsÊž?•©ä|~yu“GÒ5ɰð±g‹+¯xÆâ 3ùÇ*ã»û§›”oaǾ+áÂÌÀÔj3ñ¾½òFH) T™ÂüPĨ¿•ÿýLwú?‰øŽõ_ò­àÿªóGûñÿ¬þU|õEÞH¦þ0/Ù§ÑÿË›­?P^L ˆ„ò à@üü)÷yÿÅwÿ¤­? gýÛŸñlÿ·êUŸ†Oþí¯ø·Ûí9�Á{ÿ¹%ÿ‰ßþ4É~%h•bsóu¶ßmÖ_m·Ù}µ¶ën¥.¶ën§¥ÖÝm}iu·R¾•¥+þ]m.¥mº”ºÛ©[n¶êR´º•§¥iZWì­+GüºÛn¶¶ÝJ]mÔ­·[u)[n¶´ô­+Jý•¥h8ç�áùñ_ÊI¶üÿ£/Æñw¸ó~¾nMüKI_Pòìÿ»[m”äs�>»ŒfeÍ�·ì4aÙmÒÏ““ƒ°È¿ÕÃÏ6þ _$Ÿ‡Áÿ>vü‡´¸e¹ž'ùÈssöû®ÌêÎ?­ÙðŽu¿Ümµû¾ÿµ:z|Í£’ï²­·a-û-6Ë�ís¶[+òöy[§d(y�ñ‰æOƒ› Ù;£ª¶9| Ÿ¡�Üœ™\éöqß‘~.$’ò|L8'âÙ; c»îø;ìmNÆ[iî·¶z]X¢ùgñ¿ådžY¹’ö÷Xçfph'úX�¹Á)•Ëz»>Ëç·I¹.^|Ü‹«l›Ü]V|ÞÚÝdYéub›å�Ç–ÞgfKÛ½e�—ÁàŸéaöצW,ë …—ÍL|i&äX¸pdq‰ófõ¶ Mæ6¯>j[[¬‚¶z]X)yëð{òñï.Û}ÚA“Ù]9¬ÆÈØ_ßýfß°ú³ Y…‰¦ÈÏÎæ2è×òî®ÆÀÉÞE‡tÜ£U§ÃÌÌŠkp'ÍŠ*Í^´U£D€?VnÏ3]­ÃÊØl3ò!ÄÁÀÁÇ—/33/"KbÇÅÅÅ‚É'ÈÈž[©m–YmnºêÒ”¥jýX89»<ÜMn·+a±ÏɃ\¼ÜÜÌ™-‡ $Ÿ''"kée‘Ùm×_uiJRµ«õaafì³1uÚìL­†Ã;"<,yr³32òd¶|\\X,’|Œ‰å¾–Ùe–Öë®­)JVµz%Äy_>äúÁxÇ!æ¼Ï–mð8ÿâ?̯r¼0øò‡Èõ<·¾bÉñ£ª§º©`äúï¼vÿ ¤’}Lm?_M&<¼Ré«�tWär ð§Æú±Ïl›^âxgðEäÿ�êyo{E“ãWUÏt9RÃÉõÕÈíÝþ/“ß�§ëù¤Ç—‹]5ØõŠü�ýøSãÒ['‹6?ͬ¦>:¿ Ÿ•¾CÉÇ;Í�Üþ$ôîmºÍ¿ö¿Ç‹]¿òK—j2?ƒ{KpÿƒÒW/‹tÕ»}Ï: äòóy—mƒôs8Íñßï¤Ì<;ð'ÆxeüKÇÞ¼ÅÒçìq±áå�‡½’=÷fs™qã‚ß­Ê9tØðä_‹tÐRk5ØQáiñ§¾ûññ!­÷úË‹Äÿüpð³‡ÝÅ:#�bés3ññaåïq|{®ÄæÓbǶär~U4dMf²³Y�‰f&«i$»}Ô¬¶¼Qð—Ç/ ø…ÜW¢¸.›3?OηǺì>i.5‘R“ònS4dK�Y£¬Ö`bY‰ªÆšKîÇŇßu+;Ï ¼ñ[À^²‹ªüZêN=×:l˜°kʹ qݵìÄØëåÙäcn»�ì«‘Éy~Ã'u›v%™S݇«‹*ül11)f=›�Û&Ø6ìXÊoíóGùBö7õrup?'´Ë¿ïáÌÿ§•Ç|š~Ð.¿3þžUø�¿l÷š¿¾�!ÿ&ž™h3EZ0â8fß…oû™ù‹ý}uõœ¥ãøg¹•Ÿ×ïZcÜ¡.�ÃIýÌ|ªþ¿:Óûå ¿¿ìíòùis?ÔoA%„“ºN c<�;à´xžç�öOãû„ò,ja︗1Ñëy'Üc[,sÇÇM·ÆËÀ˶,ˆ¬’Ï|u­’Ymöú]m+O1ÌøOìn1·á]�Åxï7áûüo¹ï8¿,ÓkùÛâÒK%¶ Ž£k�•�—“GmöÒøîößm.§¥Ô¥iæ9Ÿ áÝ�Æ6ü+Ÿñ^;͸†ÿî{¾/ÊôÚýþƒm‹ï²[`Øjv˜ùX9vY4vßo¾Êûo¶—SÒêR´ø¯ŠqnwÆ9 çkA̸g.Òìøß+â<¯M®ä\c“ñÝÞÚí΃�è6øÙš­Ö—m¯È’ ¬\˜¥ƒ".²ûn¶êÒ±jó“ðÐñK.ãŸx'ͱøÊèåÌ¿¢{7cµÙñ ©¬²ydÇá�-v¼�A$ÖÅdxø[˜¶PK‘-n¿c‡´¶ØÔùŸøv¸Ÿ$—kÎ|*æ8Üe}—åIÒ½�Ÿ´Ùq ©m¶ë䇇óûëµä\~ù,‹ÛÖ-œ2äKùÙØp[Km�‡™¿‡‡Šr9v¼ãÂÎa�ÁvWY&Uý-عÛM�Ê–Ûo’Hx>¾»NC ¾K#¥°áíbÙC.Dµ­ÙØp[Km‰ÈÇá:è®éÌÛö_€×UãG>Í“'7;¥ùí܇}Ð<ƒc�²Öß$Ü{�`Ù¿ç�=ÓlrdÇÇÃ亩dû®§Ëä¬R¼„ñ[ȯ¹eÜ+Èn æ½Y»º|È5Òr=Uÿ 9 0$¶<¼®#Ëp/Íâ¼Ã] ÷Ò•ÊÕæåãúÖ”÷ú£ß1÷ÿŒ\š¼G¾ºŸ˜õžÞùò`À—¬ººõpë2§âܯ_~oå˜PV[ivF³3. k_Jßëö##Þþ2÷šî%ß=QÌ:Óotù0`K¿Ö]]ûîu²Ü©ø·*À¿3‹ò¼(.’Ú]‘­ÌÊ‚•¯§¿×ìB_Ë¿�O3üäè|ªñóžõV>^tÝ/2ËÂÇäcÉóòu×mâ×ñ^Óâ™[μä[k5–Öiðq6RçaÒۭɆ,¾Ëuý‚X-¦`)ZÖ”¥+ZÖ¾”¥>ÚÖµü”¥?šõe—Ë}‘EeòI%öÙv[[ï¾ûëKl²Ë-¥nºû®¯¥)O¶µvŸ„?7<Å—QÉ2ød½Ó¹õÃÊ“´;‹[²ÑϵÔdÓ[•÷Î×òG�˹�rõe`e]¿C›Hî�ô¤wÓÑÖo~üÇòÂ]W Øq9z#©ónÅÈ“±{g[°ÔflõsýÃ#ï\'€ßf7*åwdë3é“…“%šý.e¶]e66]OGX¼@ømóÊéu|ƒ?‰ËÑ]Q›\\‰;¶5Û F^ËW=0r>õ¸öcòžW\�nm20ò$³M—m—[M�—}‰ø øh>AüÅŸ_Ê{sŽËáoMÍ,–är¾òã;XûSiWíðåþô ™Zc4¸»-|ºþK“Å0²°2ìÊÀÉÍ¥¿Né‹xðéâ‚6j9WãwöÏyáã[÷žììœLLýÖ»:H1ìÌ“¯¸Õ¿[A×x•š9~„¸¶Ï¹·"üyöY1Ö´¬²ü&ø�ñOÂ{5|—�qË»7¹ñ1í¦Oqö6.Ãwƒ™$G—ÐÒ’h¸-×Ö[b“É6ßvžè23òlKÂ�Š|*³WÉxÿ»³;›”Èî.ÁÆÅÏÝ`æI veßÁô4¤š.�uÿVؤIJM­1¦¾ óò#­}ggñáðÍà¿Æ¾¿kÒ�qw/î{°¥ÄÜùÚ÷àòîÜη"Mí'Æã¹öëð8÷Zê¥ÖòuÓcq�~§ôž¾ kv—ì2!¦MÝ]tÝÓWVüüJ_³› ùBõ�õ�¸kø„ÿgçýœ:ÛúG•¸uøƒgÿý›úßúG•¸?ø—c–¾ž=Ê[¨P A…ÅUà ÿâñ¦ñ«ûÿôßëŽ3×ŠßÆÇïõÓÿ¬.:Îþ-¿ÿ¿¿Q~°8óy¾0?igÇ�òæñ+õûÀ¼¬îY¹(¨¾Jxâ—˜òSÈNƒà\ûqtbÅÌî×ÍÇ{ÉlÄÄÄì^+‘¤æÐkñ¾­n·™õĺêR·Ew¥·ä'…*ùS�|}ñÒ�ìë8°ò¹p%ÑóÌ5öw¿HðŽu³¬cCÊäÀ“GÏ0ñ ¶ûqñp¹ïŸOÌ1°a¬•º˜ÖæýÚë©JÝÞ”jç“~x“ægþ yEã×VwN,:m¾ƒQ¸æ<[~mÄõ{ëñ$ÜGÀ»Ü.}×¹[ p ¾L�Ë_“[á²ï©JÛmi_" —_mi°Üx¯ä_#áùBùp¸7viq9v—':LšÝ\x¹÷ƒ�î8þ§ïHé.“w“ZÙO|·{«u¼'ïÏÃc×Û_¿m¼hïÞGÃò«ÓbðÎßÓârí,ù’äÖû1qù¯‹�î4:¬\k½¶}mVë&êÙO|•­ku8Yß_†ß€m>ýµñ«¿9ɬrK‹Ã{{O‹Ë4ÓæK‘[­Æƒšñx´}¯ïmŸ[S¹Èº¶Sß%kun¤iü¡ü 'v~vÿÅ?!ûOÇLÜíÆÛi/ çÚ]{õÞ»#ëµÚë;:hééZV·è£¥+ÿFž±ÛòðÃ|³ômùY\gªzÿÉ9�¤ËÞl9Aön“>ü;1+}nÔGÃ{B.®ì�ÿ ’=öbé´Û:KZÒÈï¾OK\²æœ œõ¾û7Šö' åœ “륾 ‡æœwqÅ·ØÇZRHstûÌ<†,±Ö´÷[|vÖž¿kšÇ‚ó~ºÞeqŽÁáÜ«‚ò\î‹;�s=·ãÌ9-ô÷G•©Ýáàçãßo¯ÛKã¥hæ§0àÜÛ¯7y\gŸðþSÁ¹ —E›Çù‡Ûq�Þ¶V”¾<­Vë ?K+ZzÒøéZ8›Ü=÷ç�;mnƒ¿º?·ú;{¹Å—;O¥î´æ}g¶ÚàÁtVO™­ÖóM.“3;玗É—Ymo¶•¯ÛG“yW–bú²Ëå¾Èã²é$’ëlŽ;-­÷ß}õ¥¶Ùe¶Ò·]u×WÒ”§ÛZ¿Õ–_%öÇ·_%÷[e–Ymn¾ûî­-¶Ûm¶•ºë®º¾”¥>ÚÕþ¬²ù/²8ìºI$ºÛ,²Ëku÷ßuim¶Ym´­×]uÕô¥)ö֯ݬÖl·[-~›M¯ÎÛmöÙØšÍV«Y‰>~Ëg²Ïž<\~¿9r³s³r¥¶8¢ŽÛ¤’K©m´­kJ7§¢~2¼÷òFí|ÝMâ×kìôÛ\;¶î]ÊtTë~ ŸƒÞÙ2uüç±ò8§ÙÒß·ÒW|’Ë¥œ7ÃØá:xvZ\-—&ïm~'@ið0·RÅKwø¸½¿—Ãù.Òk±d®FMüo=±[ZY’ÖÈîíïŽ?…ë·ùºÍÏ”ýùÄ:çQuÚ¼ÜÞÔZìÎ{Ì20ä¾É6º\ÞWÈ ãœW‹na‡Ö;2±qy.%%üê[%”üîÊxÿøm{Kw6»oäÇyñ^ ª¬ºì¬ÎÔúì¾kÊ2°döß±Õer¾Aãœcq µ¬vÏ�…ÈqiçR’[ù{І÷´·rë¶þKw�àzºÉ¯ÊÌáÝS¯Ëæ¼£' OmûV_*ßàãœgo ž¶[>>!Å¥ÿ�JIoå�Ç‹Ÿƒ“{4úÝÏšž\j°1aØl£ÛõÏ‹Üw/k•±ÕWÛ¨Ì×÷?kj5é6TϿݓ‹'ØÅô£öG‘î“߆¼VøðÄYõ[ÎÒzÎiØ�»Ë�ÚÅ,]‘Í!ÏÃÌ‹; s©�k‹âÌ9à³éæhµ:¹í¶•§»ó®÷w›ÆoŠ�¼V›U¹à}9¬å|ïS÷iqû/µ¤�°y”[ <‹2qw:¿Ò¸öqŽ'º‚XíöäèõšÉ)OZzúVï^ðxÑñWá‹ê÷§µ¼³�j¾ï.?dö´‘ö1‹?"<¬]¾®›LhøÇÜã˾Ì�&³Y-)ëO_K®õ”W‡__> þ‡ÚøûãO×öž¸ù0w'9Ç›²ûŠÍ½4ã{=ÆŸŸóy7[~ ×ß/ß°xÝtÚ‰/ɛه²V×M�tMÒ@€ù]ñá™qí3û—¤8í¼ëgEkÛ<ÊpNЋ>\8ðqö™Ü£Af?ð·#]6S ülý”þ�Z}�òwã‹ÃŸ.lØæöÿLñëù¦|rúv²¼/²bË»˜8Ùùœ£Cn,¼žMlÛ÷|}Ü{<ëm=`­)èÑÿ&þ9|;òÚ͆ootß¿™çÇ7§gpë+Âû",»ñ)…�Ÿ—É´4Æ““I®†Ë~ïê=žu¶ŸÐ+O±¢¾_ühx/ç~ ñùCã‡_ö!¶ l>ÇÆÃÊáÝ·­ÆÕG±·U¯Âín•Çùÿè ¶“MML» uMZ]6,¾ÚzGKÉ/Âë͵³l÷^&y £åß©™‘�À{Ó[?äøpÃ|¸Ø0v ×í4œ‹o—%´ŠÊÏ¢ÑbÛui[å²ßZÛÀ!? �+À“?oâç}êy/žl.Ýšé´{˜qbŠé#Æ�ŸðÝvÃS»ÙäßJGe%Ñj1éui[å¶ßZÛÀß ¿ Ç,À“a·ñ{¾u<‡—dM…Â;«_6�u4Qß$X±sî¯Øjw[<›©Hìúº=F=.­+|¶ÛZÖØ±ùUø9µ3Y°ÝøGå–nÖaj¢Öõ§”š(¶8Ù{;³çý;±ÊÒ`Ï©ÖÙ¬’?¹`S�çÍõ¡º’æÖÙit<:îïŠo�ïdÈ¿°¼Rí\�V.&NÇ#’õîž.ÚâØšÜZÝõvNAÖ<·[¡‚Û-÷ÖÝ…ø’ÛgÛu–¸ÉÜ^{ôT“]Î|aìÜÍl0efIÈ:ÿUjqÜ}~%nú» –ç­²yV‹±ÛïôØ]‰-,ûn²ß¶”ãGq|cùçÑrMw9ñ“³2õ�A“™/ à:¨»KŽcëñkwÔÏØîzß#”áèñþ�¾ÿLû±d¶ß¶ë-ûhŽÿ‘?_,Þ6åd]È<>ì.ÑãõäYüMÊ|w¦~bò p¾¥ño±ø§Yäï»3ŽñÝŒ}H27ÚEß�K$²9}c§?gÇŸi1² ›"ëÐOðÍöÿ=d‘Im·Ù}?v•¥*Ñ,Œ|ŒIåÆÊ‚ll˜$º)ñò"¾á–Êú_±Im²G%µ§¥iZR´«E²1ò1'—* ±²`¾è§ÇÈŠøg†K+étrÅ%¶Éö×ì­+JVŽIòŽ+Ê8G Ûq.iÆ÷ÜC•h3$×ïxÏ(Óì4ƒKŸ)YpvÚm¶>&Ë]™.§º)£²úzý´ñÁdÞ±é^ãî½Ïðw§:£²;_}Hïšý7\pŽKͶqÁ)tÙaq½fË"x,¯ºI/¶Û,·íº´§ÚÉqÓ�¹Ü{K´�IÕ݇ÚˆíºIu}{Âù2Ï‚+)K¤š|^=®ØÍ[u.¾ûém–[öÖ´§ÚÈýqÓ½·Ü;K´�KÕý…ÙÛ‹-ºù5�Ã91Ά;)K¤š|n?®ØM[_u÷ßKl²ß¶µ¥>ÖÉt_†þZù9er|vñŸ½û³[ëŽçoºÇª¹·2ãmÆmØöÉÈyN�K™Ç8Ý,³.9%“?+(!»êËu‘Ò·S¯]øz¾F{š¸Yü»…ð¿8Öf&Æ=¯ps ;w™Çl˜Ðp® 3åzíÜ]Y+‰¸ÆÔSó}·ÉeÕ£«=ð3çïm݉›ËxŸè~;“�…�n˵9fûŒŒ,™#¤Ñbqg/ä8[Œh/ºÿºmâÔý¶û.’ÊÕÕN�øóï¶®ÄÍåœO‡tOÉÇÄηeÚ|³í¾FD‘ÒX±¸��—ò-Ä][þë¶‹UöÛíºK+Wyê¹07žBóNsäÏ#Å–y$Ò_Yz§«ä²H,¦%³qÎ/·Øs|ܽvOºÿ©%·&”¶Ù0ýžû/î7Ž_‡‹ÄÞ±¿sÞü³™ùȱ¥šI5S]/Yu½öÝmµÃ¥üo�ms¹~^N Þ·]t¼Š¸™>”¤˜´³ÝeÝÁñÏðôx¡ÖWán{Û–s/"¹4³I&¦ZËÖ}q}·GoÝ)'ã{\î_—“ƒ?­Õ¾^C÷\šRÚI‹ì÷Yt–|6ü,>9þ„ä½íNgæGdknÔgM‘ÙY7ð¾ ÅÞiwì±öNžá» nÎÖlq«6ÃUÊ÷¼»Y™d7R°Ûd·Æïï[uWXôׯáGל#¬8v$òåcñn¿âÚN! �2{!�#7ôNƒ  ìì«1줳ÖÊË/²žû«Z;—×Ýi×]MƱ¸oVð>×Kirq¸ÏãZn)¡‡+"‘Û‘—n«G‡ƒ…÷¼šEoÕ–¶}I+m+ukWqºÿ­z喝Þ7 êî Ãúç‰bM.F7àüoOÅtQdÏlväeÛ«ÑáàáW/&�Ûõe­•’JÛJÝukD�x/àÝ_Ät\­8_ë¾ŰmÖqžÁx柉q9­¶I&·_¢ãz =~›Qƒl²Ýu"dž8éuÕ¯§­j÷ÏlöÏXÀBx¯ã·•¼NÞä?Pp¾ÕÑAfUšÚòMg¦ó�]�ô)›‘Äùfº\WÄ3²íÅŽÙ25y¸“ßm”¶·Ö”ô`îöñ«¡|›âßÀÞúê®!Ùº(ìÊ·_NC®õÜhoβ(ó2¸·'Á“ “q=†LpÙmù:ÌÌL‹­¶”÷ú}ŒÞ¾5ô7“\_øß=WÄ{3G™VàS�ë¿êÆŠüÛb³/+‹òl 0¹7Ïɲ-¿'[™‰=ÖÛJVÿO±„|€ñ¯ |ªëÌþ©ò;¨8sð ÿ½ËN=ϸæ¿{§e™§Ùèkȸ¾~D_¥x/ÂÕnr¢ÂÝjgÂÛkë=×ãdE'çR4þR~¾5µÈÌßøsßqig’Ûâënùƒ'o¡‚ùò2¥È®·³8vª]þ»[…�t1câåèvÙ7ûnº\êÖ´¢<^JþŽ5µŸ7}âgvOÅ¥šû¤‡®û® �Ɔ)gÉÈ–û5Ý‹Åu×ïõz¼,{£Š |­&ß*ÿmn“2ê×Ñ�$ÿ ÿÚO™½ñ?ºæâòÍ}d‹¯;ª �¾Š)fÈÈ–Ku݇Åu×ﵺÌ8.Š(qò´»l›ýµºLÊÖ¾ˆ¨ù—øB<~çso¹w„=ëÉúw‘fëc¯ê>ÛÇËí«“>H1©ÇøÞ‡œÁ“‡Ú�A>DWÃ<2Ù_Kã–)-¶Hä¶´ô­+JV•hF>F$òãeA66LÝøù_ ðÉe}.ŽX¤¶Ù#¾Úý•¥iJÑÆþQÅyGän%Í8Þûˆr­dšýïå}†ƒ�isâ¥+.ÛM¶ÇÄÙk³#¥Ô÷E4v_O_¶�âþ/âø Û®Öìvù¸ÚÍNnÓc›5˜øzýv,ù¹¹sÉu-Žl\håžy¯º¾”¶ÛkZ×òQûuÚÝŽã;W©ÀÍÚl³¦³ ]®ÅŸ7;3"JúG.&4rädM%~Ê[eµºµü”~Ív»a·ÍÆÖjp3v{Ù¬ÇÃ×ë±gÍÍËžJúY6&5’Ï<××쥶[Z×ù�aÀzóŸö¯-Óð¯àÜò9×"È»�ð®Æw\Öïr¬ŠùïÆÓñÎ=…±Ülò-†+¯­�C}Ô¶Ú×ÓÒ•uǯ…¿‘¿#'ßIãÇ#ë.1‘—…�•Ì»Òêu6«:?¯ÖÝ&ŽÀßj~‡çÖ}F—cm)éOËu´¯J:áûä ¿¦ÄŸSÐÛÞµã™PcärÎì¿ûWk°bȳêŰ¯ßÅo?Ûë+ç}}f›:ÏOOÝ­)^’tGÄŸýù6$úž‡Þõ¿ÈÊÇÇÈå�Õ}:Ã]ƒMŸV-‡èüvsÝÆ¶±SÝõµš|ë}=?vëi^Öøµøo~W|œ®«g™ÑX^7ð­®Ë*>eäöþ�k66F³",Ñ9ý_¯Àåç¬Ùl½÷݉~_ÇÁ–ÈëuÙ1Ûu•¾D&~>Œà·êù?—½Ÿ¹îýìTÇÈÉën¾®Ç€uœÒ³Û“�´äÖOgarì;©XïŠ|KøÍö]K­¾9-«½ž.þî’á7`r/*{qÝ;«) Óp]�ë¬y}²[“…±ßÁ•N{Ê`÷{/Š|i¸õÖ×ÖÛᾟk½/þ^”áW`r/)ûoÜû«) Óp]‡ë¸%ô’Ùð¶;ør­ç|¢_eñÏ�/ºÚúÛ|WÓíJwÃÂaá'IO�Ê<±çœÏÌ]Ó]¤9ý/ÓZúIM>N“ñ®%Év]�Éw:lü,«)‘“É¡Ôgâe{24þë)rHOÓ½UÑ<+Y×=5×¼C¬¸6¢”®áz-~‡WLŠÁ<û¸p!Ší–ç:Ìk+•›“Ys2ä·ß4—ßZÝYõ�Tõ§Kpýwê^Å:ç…ê}nÂã|;I�¢ÕÙ=ñÅF|øøÃ÷Ý®oѶ윹ë&VL”÷Ë%÷Ö·V@=eÕ=iÒüC]À:›‚q^ºášªV¸\s‡é04Z»g¾8£Ÿ?" aûö×6�ÛvN\õ“+&J{å’ûë[«(»ënºê¤ëž¦à>ÿç"è.#ÑßÛoûLÿ{ƒAÚÿÂ�àöÄûÿè>ØCôèOá§û¯Þ¿‡_xû×Þäö}×éý¾¯¾>~|‘x)ÿ87Gq^™þÚÚ�ø3Úú>Îþ?‡ß}ý Ä9×ýúø_Âþí÷Ÿá§×ûÏÞ¤ö}ÛÙô®úžû9ÿò=à¿üà}ÅzkûijOàÏkhû7øIüþý÷ô7ç럹ýóûbýëïŸ~—éýÏéý ¾¯Ô‹„?æ¥ÿìùÿà»ÿ·âwù±Ÿû;Ÿø6ì}qOüÙ?ý�¯ü¿ö=£�þeïÿ—ÿ¿ýºcüÔ¿ýŸ?üöâ�óc?öw?ðlÿØú›'ÿ³µÿƒwþdzüËßþ9/þûtÇù©û>ø.ÿíÅæÆìîàÙÿ±ôÿ6Oÿgkÿïý�gù—¿ür_üÿöé�óRÿö|ÿð]ÿÛŠ?ÍŒÿÙÜÿÁ³ÿcéþlŸþÎ×þ ßûÏó/øä¿øÿíÓæ¥ÿìùÿà»ÿ·›ÿ³¹ÿƒgþÇÓüÙ?ý�¯ü¿ö=Ÿæ^ÿñÉð;ÿÛ¦?ÍKÿÙóÿÁwÿn(ÿ63ÿgsÿÏý�§ù²û;_ø7ì{?̽ÿã’ÿàwÿ·Lë÷Å/Å/üØ¿Ûëÿ·×öîþÝßÚ»ÿÕwöµþ k_í‹ÿ­Ÿ~šý5ü>ÿóO»}Óÿ†ý_è}ZøÅøÅÿ›�ûwöîþÜŸÛ“ûZÿúµþןÁÏíyü>ÿûûœþ—ý/ü9ÿó_»ý×ÿ†}Oè}VøÉøÉÿ››ûvÿöíþÜÛ�û[ú¶þ׿ÁÏí{ü>ÿûûœ~—ý/ü8ÿó_»ý×ÿ†}Oè}Ìø^ø^ÿšþ_ÿÒ_ð…ÿ„/ö�ÿõ;ý©¿‚Ú›ûiÿëSì¿Óÿ§ÿ¶_ÿ™}×î_ü;ëBëó«N«;˜ÿÅñI ÑÇ43G|RÅ-–ɱIml’9#¾•¶øï¶µ¥iZV•¥_âHì–Ëâ–Ë$ŽK.ŽHä¶—Ù%—Ò¶ßeö]JÛu—[_JÒ¿ehÿGÑß¶Y,RÙtrG%¶ß‘ßmm¾Ë캕¶û/¶µ¥iZzVƒŽþPüüyù/\ݾ/VÍÐ\ã.¶Éü.èœ.‰-ðâäC9ý}6»kÖ“bË“5³eI�¨ÃØäÝ)\»}n­yAä—ÂÇ�žEÝ™¶‡¬åég•u’W•ô^F †[á‹"–E�Á¥×í:æxrrg¤¹SC©ÇØäÝe=ríõ­kÊ?$~|ò&ìÍ´=k/Is<«¬’¼¯£²0ø\ß9Çw—_³ë¹áÈÈš’åK«a‘u”õÊ·Öµ¯|ÊøøÇóf}Ç ç=�ÔÝ›¹¤×äö8�GÌåÎÍÞK¿Ûï7]n·?«ù·&ÝåäÍfVבqÝÆÆøe­¶ÏemŽë#Ýäá�òÇ‚Wc´ñïµzÏ¿4¸ÑÇ&�yLž¢ì<éd²—I‹�®Üår}¸×úÛõ²9Õ¥)wÓ²µ­–ðƒ½ÿG“¼.» —Cv‡]wvŸ/«‹¤ÞÛ•Õ|ó2[©K©…‡…²Èä"zÅ_[~¾Fû’zRï§gº¶ÛÂnöü9þMðºç캳ºï»tøñÒ\]&òÜž­çyr]JWîx¸[<�÷›è×ÖÚÏ‘¾Â¤ž”»éÙî­¶Æ+ÉÏÁáäÈÍÜx‹åZv®–²ò]…œ3¼4[Χæ:ív=~ñÅøî§”q8;ŠóžCŸ®>Fnn?Á¤öÛ'²8äº�ñ‡»|ó[ÇK·÷/ŒËõªam9}xV×�uô9]}±Ùcñh·| :Ù«ßNü}”¶IJzÛZÓír#¸¼ó ¯ÚßÛ>6öçÕé%¶ —*�ˆl¹‚Këu¶R>ÁâÑîøFU$­•öÝÂKn¥=iZÑÈÞàð_ÌN„»klxßÛ|[W¥š˜û.SgÙr ·]}¶[?âñî¸N]²V:û.‡a%·Òž´­hà/xü4|¦xí™n'eø3ßÒÅú"]îF문�;Û‰ëu�]u'›wÌú73±¸–†himnº ÜØ'¶:{ëg·íji[k[n¥mºÚÖ—[ZV•¥i_JÒ´¯ÛJÒ­O­+JÖ•¥iZWÒ´¯ÙZVŸ–•§óZ¥ZV•­+JÒ´¯¥i_²´­?-+OÜ­Ñž ñg›&qòqå’ Œyã¾àžë°Í”¶H¥ŠKkmÖÝJVÚÓÒ¿kþøþ@߬Õì÷Y¸úÍ>»?m±Ê’رuúÌ<ŒüÜ™oº–Û>.,rÏ4—]ZR”¶ÚÖµ«÷ë5[MÞv>¯M­ÏÛìó$¤XšífF~vT·WÒØññ1#—"y.¯ä¥¶Öµ~ýf«gºÎÇÖiµÙûm–]ô�_¬ÃÈÏÎÉ’¿e#ÇÄÅŽYæ¾µü”¶ÚÕîºã«»3¸¹^ê>»ç]§Î6–Ë~·†õÇßó~W±²oÖ¿Žñ�~Ïo—l^ú{«7RßZzþWG:Káû䃾¥¥ÜOÅ^ÇâúºW ù÷Ý»‡‹ÓZ»0óî§ÑØáÙÙ™\ckÈ0펿Rêêñs¤¤~—RÊúÛëÐNœøžùîém»�xÍϸ¾²’âS#yÚøØ½G®‡2¾±ì1àì<ž=¹ÞaYçÝ]f&uô³Ò´¶¾´õèNüQ|�÷l¶ÝƼhçÜcYI1>¾ïµq±z—_6eiôöÐvOÜo0­²¾û«­ÅÍ¿ÙéZ[_ZzõãÆ¯ÃÇòÍ会<Ü/÷]!Ä6û\ÍVg4òWkƒÓ1ñ»°©}%ÏÞuÎþ³w•ú©d²–C‘�Å3¬ŸÝK£÷Yë};�ãwáp;üÇ–þFãcÅl²×7€xÿ«“&i ¬vÝ‹öÐçú¬xñ'¶ZÖÙೋd[[iý'í¥Ôìß�Ÿ†»"ë°¶ÞSyvRéþùÁº'Yt·ßg¥µÃ–�•Îõ‘[ ô¯­'Ç·ŒIOÜ�#ý3²Þ>þ\›®ÂÛyIßðÅ/—õ·Í$‘ÖÛnž�‘Îu�Ù”»Ö“Áo–žŸÎd¦H‡Ä¯Ááךxõ‹Íß(y5ÙýË#eÕž8j âø.ÿíÅæÆìîàÙÿ±ôÿ6Oÿgkÿïý�gù—¿ür_üÿöé�óRÿö|ÿð]ÿÛŠ?ÍŒÿÙÜÿÁ³ÿcéþlŸþÎ×þ ßûÏó/øä¿øÿíÓæ¥ÿìùÿà»ÿ·›ÿ³¹ÿƒgþÇÓüÙ?ý�¯ü¿ö=Ÿæ^ÿñÉð;ÿÛ¦?ÍKÿÙóÿÁwÿn(ÿ63ÿgsÿÏý�§ù²û;_ø7ì{?̽ÿã’ÿàwÿ·Lš—ÿ³çÿ‚ïþÜQþlgþÎçþ ŸûOódÿöv¿ðnÿØö™{ÿÇ%ÿÀïÿn™Ù¿Šß‹ÿù³xÏqñßíáý»?¶Î÷ˆn¾ùý­?µ¿èি{ƒ÷o»ÿl{úWïÿ¦½Þÿ~7Òú^žÛýÞ¶õçã;ã‡þn¾/ÚÜoûrní�¿ã;ϾÿkÏí}úø;®Ú`}×îÿÞoúK½þÿ©�ôýžžÛ½}i×O�ŽOù»¸Çjñ¿íÇý¸?¶nû�nþùý¯µÿèOàî¿i�÷_»ÿ9·é/¾~’÷ûþ¦?Óöz{nõõ¤�~þ/æ ñÛ°:ûxÿo¿á×tî»øYý­?µoè¿Òü€p¿à÷è/í�Øß~û¿ðï?{ûä>ÿ½}?£oÓ÷ÉÖNÝ8uŒä9ß_ð>ÑâÛNÙ|/Šv ÝÙ{Ž%͸ö§”ñ½¥�MfDì4›ÌLín_ÐÈŠÙ#ú‘Ý[$¶—[éu)W–朅vGÙðÞÃâí©2·=3¶å¾6òl›«}qôËϺîY¥ÈÈÈÊËŸ…ò�¬¬Yä¬ÖÙ:í¥¶c~ê<~c~¿�_&/ßò~¨ãü¯ÄÉÛW�l¢Ùô¦tY}_“È·Šü �×Kò¯Òs]ÆtsÅZçâ\: Ç%ö}Kiôëg{ÿðìü†ôýùyý à~Eq¨#Î˦oXrÜM_$ÆÀžï£M—ìx–Ã#m— )}¸šywUõ¯¶Ûî»ì¯ »Óà'Ï.©—+3€éø?ñØ©™‘_]òŒM?#ÇÀÆ­kû>'ÏkÅòdÙdÇO[qu9‹½6—Ý_±Ã~óø󿪤ÉÍàz~ß¼z+rò,Ëë¾O‹¨äXø8Õ¯Ó»eÄùßðc*]–Lt÷[‹©Ÿow¯æÒë®ûžòwð—üŠu.^Óaã¿(éÿ,¸¥›L<-Ÿ’bôÇjfàM‹ù{�ïí-†[i1°³=ñý,^m´È’:[}¶zÝtvqóµ:½:/a«ºzo´z›c=´¿±ø(á’fEu×Ùdø_Â^¾ÜÜy.Žê[$U¾Ë½+éZú9?ÙÝÝ�)›»¸:‹²ú»2zÖ˜Ðöä¼Gï–Òé,¤¸ïu¸1gÁ}Ñ]í’¯Žú[ZÛZÑÊ>Íè¾êélÛ5Ý¿Ô}•Õù³WýO?ᓉW.ßuö[. ûÍn YÐIX®öI ×Ù})ZÛZÑÁžýðOÍ!ÙgùâÇ}tþƒU¿¿ŒMÌù¿WòíO^æo-’HìÂÑö5ú»ø'$¦Ubº¸òë¶9PäÙOtWßoÚÄlRÅmQ¸ã’[ìŠ+/–I.¥–G·_}÷]_Km²Ëi[®ºµü”§ÚÿVY|—Ûv]$—ÝK,²Ëku÷ßu}-¶Ûm¥nºë«_JRŸm_êË/–û#ŽËä’K©e‘Ùmo¾û¶Ùm´­×]ukéJSí«èju;]öÏI¢Öl7[�®\½F§ 'c³ÙgåIl8ØXqÍ•™—“-Ô¶8ã²ëﺴ¥)Z·Ã£þ/üÿò&ºéº¿Å^ÚËÓí°ë±Öò¾_¡§Yp­Ž¾žï\Ío3ì©øŸÚÅ_mim1r¦¾K©[l¶ë¾Æït߯Ç�}õv�sãhÍ«ØbÛŸƒÉy~–Þ¶âYØzÿ«5Ü«±r8¶ƒk¥µô¦.Dò_Zz[m×}�Ûé¿�Ï9ûæ¸Rõ׌½¡6«c‹nv%åÚ[z߉fà××ýW¯å]‰?Ðí"ô¶¾”ÅÈšûëOKmºï±Öo>þXü�ž·q? »G€é±÷ÝNÓ‘wæ6�øZض4ŠOÓpiûs7‰sS¢ÁÇ—êO>‡W¶ºÚ[[,²ù}#¯oüoü/©½“så_ñn¬¬¸99<¦õ¹\ß”åaImnÎ×fó>M�ÇøßÛã×ÒÛeÆ×ò,Z×Ö´­iJzö[ÇßÃiØû‰p¶þN÷¯áZß­‹>O ê ~O/äyx2EîÊÂÊæ\›E¡ã[lykí¥ðkwø×zV´º¿c²?~¾ÈÜI…¶òk¼øß ×}\Iò8gQëòy#ÊÂ’?v^W0ä˜Ú-ÚÁ}}´¾ vû¿miZÑ!o¿.Âù°÷>nùi‡��Vâ þ¹ñ[I6\Ù˜UÃŽÞ?µÄîžÛãØQër­Ï¾û³5÷ð».†:[m.’·Å"¾&üðÞMVë«z[S¿ì=UØyýµÚWÇØ]‹ÇYåÅÜi¶;|ktœ'kg×­·Kǵú�«m¶ûéum¥]òñã¿oÖmºË§µ;žy«»x{K².³�ö {/­HvÚ�žÞÑ\7c['­·ß ÂÔÙ-´§¾Úúz»ÛãÆ'…Þ&_¬Ûu§Pjw<ïYv,ðö�cÖ>wÏâØaÖjÁµÔì¶Ð~ŠáûlÕ¶ûô8Z«d¥)ï¶µ§ªRÞüRøàm0v>6øÛÁ¸Ç:†K.íÎK_`w ³fiaÑn¥ÃìŽo“½äÜg �áGusuºYµš{ïšOf–ß[]t¿Î‡9ýòSà·üá¾9ÃÐÛKûPýÃãóø[üþû¿ƒ˜;Ü/Ñ_ …Ü'Óýß_ïµúKÓéÝîõ·F¾B¼/ÿ‡��¿Ú+ûdÿj¯þ.8×3þÿ¿‡ìz ´?£Aÿ xþv~”õúß|þ‡ìþrïwÙ£¿ þûÇÿíý²?µ_ÿo™ �ÿÃ�ö=ÚÑ¿¡?…ŸæÉÿìíàÝÿ±ìÿ2÷ÿŽKÿ�ßþÝ3 u?á„þÕý§Ö�—ÿ ÿÓŸÚï°8o:ý ÿ¯ÑŸ¦?‚\‹[¿ýúKûl?Gþ�ýô¾¿ÐŸé{ýßNÿOm}ïU~?íeÚoÙðÉý7ý¯¹ïæÿ¡¿àóú7ô¿ðS�ë·ß£?Hÿo,ÿ¸}ÿîKë} ¾—¿Ýôïôö×ÞugáÉþÖ��×=�ÿ Ó_ÀyÄ9·èoø=~�ý-üäýïèÏÒ?ÛÇ?îû‡ÒúßBo¥ï÷};ý=µÎ~/þoø7y-ãÇ‘ó€ ÿ´'yõ/tÿà©üþÿjÞ}ÇùÇðkøAÿ ïè/Ó¿ ¾ë÷ϸæ}×êýO¡/·éÝ+Ä�ÒpL”åyŸàý�¢Èâ݇Ãx§<ã9wÙ&_æ|{QÊ4Y2GmöG&F£y‡�¯šû-–êR·GZÒ—WùµyÎWø�;ÓdqÎqÅxß2ãÙw[vV‡•èõœ‹M“u¶ßm·djöø¹˜3]m²]JVë+éK«ü×›å|;ˆs½4üsœq^7̸öUÖß“¢åz=g"Ódße/¶Ë§Õíñs0fºËdº”­ÖV´¥kü׿ÌÃÄØbdàgâãg`æA..fddâecOec›'ko†x&ŽêÛu—R¶Ým}+OG?;3áÛã7¶säÙòŸzÇU™$qÇZõ½üŸ§q)Xìö[/è¾¥ä+Utõþzûî‚·Kwç_[«ëVŠv7Ä÷Ç_ieW7’ø£ÖºÌŠÛe¾½yNAÔ�zÇo¶ÛëƒÕ{¾�|•ü·ÝtWVK¾ÛýÕhÇbüQ|wvŽ]ÙÜ—ÅN¶ÖäÝe–Ö½}NAÔØþ±Ûí¶Jàõnï‡kî–¾¾·ÝtU¬—}·ûªçpü:|[w¦Ÿ7Kϼñ»=–Âí®Ãq×wªé^a™°’{²gÉÈç�3NÍ¥»+"û¯š•Ø{f­Õú”»Ö­柆sãë‘Ë�“ƹg‘ý{,ÑM÷ -`q Æ—&èå¦=Òcóºä;¬ÌH¥¾ÚßvÉ%¶{i-•­ni?1ü;>r)²òx÷#ïþ|±K÷<?Ï8ÆÛQ‰=l—è]$<Ã�ò=ÆV©bÅ­¶_}¸ðÒI)[ém”¯²š³Èÿ ¿ ÊÊšþ#åï(Òa],ÕÇÇä}5ªå9QCtõ»9³5��âžX±½,¾û`Ž’IO}-²•öSVùážá™93_Äü»äúL;¤š¸ðr.œÕrŒ˜¡ºzÝ�dÙzÞÅâO$XÞ–_u°ÇKï§¾–ÙJû)¥]¡ø8¼SÛ`ÉKù{ä'ÙV+©_hq~¸íì'ûÜ7Û$šþ)®é 劘Él¦M•¬×['º–ÛX¯Á<§ð°w6%'þùmÖ<‚¶æû1¿…=qʸ}&×úIþªžº�÷9û¾o­,þ�oÕ³ó«ýóiîÂü�ðÏvž-³|ªëýåÖåRÌzr>·ä\ZÙp½/õÈšíg%æ5ƒ*•¥¿Ðm¤–}µþ‹öS× r?ÃKÚ˜´›ø#å?_îënW² r>¹ä|Z’`ú_ë‘5uœ�˜} ¯Z[ýÚIg¥kýì¥.×YÿþE±Ö¸ß$Í/­=,ŸÄ+ñã­=:µ–?'²®¥iOÉOe}Ðc�ávó.)#¦§¾|cÍŠ¶zË~ÇkÚš¹,“Ý_Kc‹¬vöÉg·Ò¾êße}~ÏOÝcmŸá²òÎ)"¦›¼|vÏŠ¶V³_³Îì­L‘ÉîüÛb‹¯·vËemûkuo²´¯Ùé_ÊÇ/Ãqå„RGM?xxñ�l­f¿e›Ù:©,“Ýö[x½¹¶[+oÛ[«}•¥~ÏJþV9埃K¸ðç÷‚ùÓÖ|�ø¥®Â^YÒœ§…Ï�=/¶�LJ�§ç¼ö<褎µ­×ß.=l­)J[u+ëOŸþkל?ïÙâŸûhíßñ4ùŸæÛù™þü^1ÿ¶NÔÿ�›þmï™ïÃã7ûbí?ñLò_æoù=þXÝ þÒ» ÿ™{-gálò~\¼K7Iô.÷ÙLì�f³°¶ÙxÑÖŸŸ~&VƒKmöÝù-¿#—Sý5?#Ûk¿ G�ÒåbÙ¶ò3¦0°¯¾Úfäk´œßi•�eiù÷bâdë´ñf_mße)|ðR´û}iùÏ_økü…“'ͯ‘=3…‡}öS6}~››ìòqì­?>ì\L�n¢,»í¯ä¥óAK©û´üŒÑ�ø0¶÷Ýß>EõÐ[[}f®7Š9wG²µ­±[/‘ØTšß©özÖ¶WÓíôõû»EøTvbÃ/%ó‡6µš“àè¼x›e‹e)}Ô‚èv»éÕK-oŠ”ºûnóÛukm+ZSÝ\»¨ü29—ãÅ&ûÌìllªÖ_¯‡¨è)sqí¥.º�Ö-Žgqëä¾·YíºêWßJÖ¶Òµô÷W.éÿ ÎeøñI¿ó+*µ–“áéú\Ü{iK®¤5‹e›Ü:ù/­Öz]u+‰o¥k[iZú{«ìxÇàÆâ¸›h¦æ!|ƒ¢¤SÒ}oñ‡[ÄvÒOtu¦4‘n¶½óͰáŠ)}+}•×ÉY-û)u•üæÉð?Âå⎲ ¿¶g�þ@ó,ÊWÖ+ød=yט?–Ÿ›>ã‹vF\¶Öß³ó2¢¯¯Ûëû�ˆáŸ†ÃÅÍ~=iØïß<«2—R¶IÄé×ü´¥iZÛ.׉óì«í­¿gæäÙ_ô[ Ã? ¿‹úøký°;Û½ùV]+JÙ·€ðl*úV•­³bm8¯=ʾÚÓÖŸ›“Ñn�ü!_ZœÝfvë·<ÌåTš9³µý�ÓúÝâÛ+ZÝ‹•f“¡µûü\Iiö]÷}ŒSSý,”nŸ]~ÿ‹þ‹ {N“ä�›±‚ê_në±{S°²2¯ô–i=&Õpý÷âÓÛí–Û=/×VžÈ­ÿM[î¿p8Á§Æçƒ™ý1»ì-Ž5Þëvü÷²¹ö\ò×ß-þ™ž7¾ã_"ßl”³Òý}iíŽÚÿ=îºí¾à_?& zgôÎï°¶÷{­ÛsÞÉç¹sÉ_|·zdj¸Þ÷ŒqŒ‹}²RÏKð+Olvÿ¦÷]wC:Çð÷|<õ?!×r®?áWä­l3C;;�vïnqìªOéKäØð^ÍìWÀ6R”§²éuwV?Ëg¶µ­kÒN¨ñOÆ.‰ÊƒaÒþ<ô¯Vm Â®ºÝçëÅù ¸wÇH¥‡/�ê4ø»¬ï¯=%ºiäº_Ë}n­]ëütéL‹3zƒ¢:¬¶VâýÊí¿ëŽ#Æ7SãV:E|y›­>£k›õl§¤—M5÷Iþ𵫠}cãGŽÝ-‘foQtOPõžÆÌZáWoÁºë‰q�ÔøÕŽ‘_^ëQ©ÅÚæÖhéé%ÓM}Ò¦­]<ê:ÇÍFvƒ úC¨:CC³Ê®vËIÔkÃ:×Q°Í­´¶¹™ÚÞ¥ÒáååVÛiO©%—_éOÊÏÌÞÍ̾Äý™Ð½Ý6âYÜ}1Ôý³fWAƒgfu×ç–áÁ}Ò]|8–ò�>Ö˜ñ_t·V¶ÙéJÖêÿ6¬eØ�+Ó}½>?lõ/Yv„‘Ö,H{�ñnk4W]}×G�%Õlì†;®’êÖ–Ò”­n¯óXϰúW§;v,x{_©zϳáÄŽèqbì>ʬXÐÝu×]<|“U²¶îºû«[m¥)ZÖ¿Íx¾Àënºíž/²àý©ÀxWeð­Ìãn8`qm2âû\iâ“l}—äx;-NtA-ö]d°ßmÖ]ZVž•­%;~/yþÂmž÷Ä> �“‘}×ß ä}�ÖºûkwÑõ¤:޹æœWU�gô }-Ž-§­Þ”üû½t«š|BünóÜé6;Ï8>D—]uÑð½ß<ë|Vÿ£ëHõ�yËx¶¶+iô-ô¥°Ò–úÝééï»×Jù—ÄOÇ;Í“a»ñ[„àÏ-×]t|7wÏ:ë •»èúÒ=g_rÞ1®†Ú} })dVÒž·zRžûýÜòí�…_ŠèÕG¦æxé§ÃŽÚÛlÝOÂbèM­iv^m~¦û£2:ëy-ß[[)[²+ZEt±SúóÙ&­òoÃñ±¾šéuZŽëáVWH)�Æ{Vl¸l–ûåºÜËnæZ>[‘\˜i%-¶•’°Ö–[î²êû«v¶ò?Ãûñá»’ëõšÚáöÝ�,4‡Žv~~TvI%ÒVÌ»k˵ܦjäAKéKiuõŠ´²žë.¯º·k"øøôÝË|šÝmq .Ç’AÇ{;;&(ä¾én·.ÊòÍo(š¹ÒJRÚ]}ÑV–[î²êû«v°í ÃÃ#ĽžŠY±² �;Uä/’óebË4wYn=›¾ßÜa]“‰u}ñÒXd†·Ržøï·ÖÚãïóc¾>ÿßÊÏð‡ÖâY�ÍÉðSÿWß'?Ûï\Š€ÿ7GÁoý^¼™ÿo½uþ(˜güÕ/‰�ýV;çü4ì¿ë3÷ëÿ ×Çž×K“˼�ÛÙtWGLm‡cðH¡²úßeÔžÛµ]Q¬È¬¶ÒÊÛJ]%lôº¾¶Ö¾•§ÑÖþoðg¾l®Mäfê;¡º:bì» †ÅwÖøî¦E—iúÏS“Y­¶ÊÛJ]%Ñú__[k_miô5¿‡wÀÜï—+’y¸Žè®Ž˜».ÁáñAõ¾Ë©=—j:×U“Ym¶ÊÛJ]%Öz]_[k_mi鸯ápøƒãÛ ³výGÚ|ï\)1lÔr®óì<=~4÷Ï�-»(eáN¶»6àº+m“*LzÙ5õº+¯¤wÙ�¸wáøø·âøÖŵèîOϲm‹;vǸ»V,Ÿ©�lÖÍ‘v7 åœCS|¹ßV•–—cV+k¿JÈÿ;Ýšø¿ÁgƧDz-�Iòg‘dXÖS;”v×iG‘Y ¶KeÈ¿‹rÞ1¬¾\ÊßJÉJãÖ:VÊ};,§º•Í<[àÇãcŽÁl{•äÏ"Èñ¬¦w)íŽÐŽ|Ël³Ý�Ź_Ö_&ed¥d¥ØõŽ•²ß§mŸ�îÌü{ðå|2q�¦¿qƒán¯7;[w¾yuyÊ5yVàºí‡ä]óã»+n¶JÝìŸHí¿Òëm¥m¶´ÛÞ¸øÈøøêŒHq8o‡]K±åŽl}—*ë½aò%Šëî²ø97acòžEÔºÿ_Íʧ¯¥=�·ÓkxÇg‚�e��ñ7¢c“[&ÅÙò½Ðs}þ<ÑÖû¬’Iͱy Žûk}~ÚdÓ÷?™OM«à?Þ õž><KÄþ‹ŽLYl›eÈzûEÍ·ØòÇ[«d�r>m‹Èwñ_Jß_¶Üš~çó)é¿=iññàoMo´œ³©ü-ñS®ù‡·ÓEÍ8�ýU¢æº»½•Žéqy†‡’Ù“%•ô¾_½VKéüõÕnÖ³Y­Òàbj´úüN¯_˜¸Ýf&>4Tô�8±ñ ŽŸe¶Ym¶ÒŸ’�ÂÀÀÁÕáãkµ˜Xší~6cáààcC‰‡‰tôŽ l\{#‚l§ÙKm¶–ÒŸ’�ÀÀ×àj°±µº¼,Mn» 1ðð01¡ÃÂÄ‚:zG6.=‘Á6Sò[m´¶ŸÌmû÷?[õ€ CÙ¾>ô/u×½ËÒ=CÛu×ÙtXìÞµáœö¸1ßZVøðëÊ´»_»Y}iJÖ–{iZÑ‹;£:O¸¬Å³·:{«;NÌVÜ+;¯¸—7³Û«K®·ÞM¨ÙÓ—]JV´³ÓÖ´bÞÅèÞ”î qlí¾ŸêÞÒ³ÚÛ…obõÿæÖáÛuiuÖâÛɵ:cÛuÔ¥kK==jǽ›Ô�SÝ|[+ƒw/Yuïmðœëã—7‡öo ã|ó‹fI Ô¾)2¸ÿ*Öíu9Å}´­µ¾ÖÚÓÖ�å¿_ÜÓ/+;gâoÖååRÿ[¸—:힉÷Rÿl˜ºŽ!Ï´ºX+ÒzÒÚc}:úR•¶´¥(ÑÞYðáñ­Ì²çÏÚx¹ÆµùSÒï·‰óÍá‘]w¿Òø5\?›hôñÖÊÉZÒß»û>ÊR´­)J4•ü:|ms©óv~/q½~Tô»íâœÃ³8V,7Ýõ=/ƒWÄy®—Ql¬•­-û½lû)JÛZR”s¯·~>%»ºÌXùŸ‚¥·øo†½E¦Ûxý}õ‚™²™Rô.筥βêdÝï¶zÉl•¥•º•¬v{u‘þ_ŽäÙRë)ß|:Ì‹ ¶,n9Ú89PàÖ*GI$Å»—qS‘}ù5²µ¿ëI5´­õöRÊ{in¸ò/Ããññ»“*ýn/tñ g²b‹Žö\y1á]#¥òb×–qÎQ-÷ä{+ïú×Mm=õöRßÍô×>Aø}þ>÷2e_­ÆîŽ%nE�Û\²¢É� ±R:_~-yWäÒß~El­oú×Mm=õöRßÍöë?#ü2ß ;Ívf³ÆGòr¡²(7sÈ ò¶:ëí’É.ÈË—vo)Ô_4–Û[+LŒYãöÝ_Kiw¥ÔñßæÇ|}ÿ¾•Ÿá¬?ijÆ›“à§þ¯¾N·Þ¸ÿþn�‚ßú½y3þßzëüQ1Gùª_ú¬wÏøiÙÖg¥ã߆¯ãŸK<ì³ü‡åÑÅ•÷‰1yghq É‹Ùe¿ržî)À¸ÆM¸¾ëkw¬rG7ºêÿDôô¥=?ü<ÿúYb“e?zòë#Ê®EðòÈÕcEå-x§ ãS—[[½l¾É½×Wú'§¥)éxÿáîð M,RlgïNYly?^øycê±¢ž/e–ýÊZñnƦ¦5.¶·zÙ}“{®¯ôOOJS"pïÃðçÆpäÆÝxóÍ{kóeÊ·cÌ{óºð³"‚Lh ³[œ›ðm}p±å†é¬ºø.Ȭ’ÝK¥º:Ye›OÂþ¾.84¸9Zÿøþï; (l»+šs®ÔæÑgMqY~Vv›“ó�Ÿ’\›â÷ße˜qÁK®º–Gmµö¶[‡|6ülp‰±2õÞ0ñí¾n,QYtüǘvO6‡.HìŠÛò2õ§™mt7Ë=Ñ{®¶ÌK"¥nº–Ùmµö¶S‡ü8ümð©±2µþ1ñý¾f,qYvG0åý‘Í!Ì–;"¶üŒ½O(æ;MòOt^ëí³Èi[®¥¶[mkkj:ƒàçâS£íγ…ø!Ñ›ªl%–iëÛú­×�—G|ÑbE}0oïÝße߬ЖáÙ[#ƬVYudºÚRée­ýë.‡è7¦ºgª:“clvl ë.»â û"»Ý™±q]>ªÌ«c»í¶—ÒêR¿‘½½sÒÝ9ÓØÙX}IÔÝgÕ¸™Ô¶™¸½sÁ8·ÆÌ¥—Vë)•Õk"È¥—Vµ§¾•ô­[××]/Ó½A�•‰Ô½OÖ�]‹�K-ÍÆë® Å¸N>e±Ý[ã·*5ªÖG‘K/ºµ¥/¥}+_WH:ë¬:×§ø¦»‚u/^pn®àú{kf§†õ×Ðpž)«²ïç¬×qÞ5¯Öj0­»Óí¤PÛJ²³&2[Ü€0Gfx·ã?tgK´íÿ:?´¶Óc[‰~ß°º§‚ó¿Ý¬÷}(¬Úò Ãa°ÖêÖ?l–Ö;¾Û}+ö°¿bøßãÏoåÝŸÚýÓ�™°¾ q«±çÝgÃ9vÆØ#÷}8ãØoô¹ù‘RÝZÙí¾•²µõ·Ò¬1Øž8øõÛ¹Wçö·Et÷el$‚Ük¶<ó­8g-ØÒ=ßN8öÝ6~dT†·V¶VÛé[+öÛé_µ‹;_£:O¾xýxŸyt÷Vw7¬°O^5Úý}Ä»�Öl\ˆòñ¦®›—ê7êË�• ’ÙwÓõ²Kiu=+JU£¼§áâÓ—ædgìüKãX997É%ÔâÜ÷·¸^wÉ|²V¸ú¾!ØZMV=–ß-}¶Y#¶”¶Ú[í¶ÚSL¹GÃƧ/Ì—?iâ×ÂÈšëï­¼_™ö� òëî’úÖ-_ç:=\VÛtµöÛl4²”¥)J{m¶”ÓNOðçñ±ËræÎÙø»Æð²&ºûëN1ÌûG†bY|—Ë%k·ˆó�&²+mºZûl¶Ym)m´§¶ÛiN}öçÁßįvÇ‹2ðC¢´¶áÉ‘W¨ôûä¾è¾ñì¦T½ ºëis£¯Þ®÷[=Ò[¥žêWéÇíÖŽCøo>77SÉ6·¼¸”wâýÞÜ^=ÚväÁÞ’SïÑÝÊøß&É®WçÓì¾K üÊCþ{ݯ{ïÃóñ縖I5úŽßâ¶_‹÷{`ÐövFDPËé%>ýy>Ÿ‘Ï\ª{éö_}Ð~e?¡ÿ=î×Íïáþø÷ÛË$šýOoñ{/Åû½°h»6|ˆ¡—ÒJ}ö:òm7"ž¹T÷Ò¾—ßt?™OèÏzë®ÓðÐü0ì1òaÄñ7k£’|I±¢ËÕù ä¬ÙsKd–YŸ�në·w·eãÝ}.²Ù¢– Ýe=ñÝOu.ð¿æÇ|}ÿ¾•Ÿá¬?ijÁ›“à§þ¯¾N·Þ¸ÿ þn�‚ßú½y3þßzëüQ0ßùª_ú¬wÏøiÙÖgèÄü2¿øùM77ò“>8î­oÄËìn»³zVÚÓÛ-Ø=A…—KiZúþd¶WÖŸ—ÓÖ•ý8‡CÀü\˜§Ÿ˜y'±Š;«[ð³;‚Y�=+mÖÒÙo×õ^e-¥këO§-•õ¥>ßOZWôáþ�q²bžn]ä†Â(î­oÃÌçüÌié[kOl·`une-¥këù’Ù_Z~_OZWëè /Ä^£m‡±ØuÇqrœ§cI –+lÌ›Œ]Ç7¶[ ’R[~ï›k}–ÒêÝgºË²÷ ü;ÿœb¶×wÖ��Ø´¶\‰+o2îoƒKíš+cŽ¿µöÂÝH±o¶²G[kKë}kïºû}-¦Zâ¿ÿÜ{Ûú_¬ùÏ:ô’këü*íŽu‡ï¶Xí²È®þmxw¤x×[[ì­=.­Õ¯¾·[ém2·øøâãÞßÒýkιפ“_Zr®×ç~ëeŽÛ,ŠïàFÓ‡Wéã]mo²´ô¾·]_}×[ém2䆷áfédžM•u+ZÖiü‰ò¢Ù+ë_ZR´Æîüx½-ü”ô¶•þkgúûáûã?¬³-Ïã~õ>Æ{=ÞÛ{ ÿlâ~uµ¶µ®¿µ7œÏêÒ•û+Xë[köÓÒ´õloø§øíë™íÈãþ&u^ÂKiu)o;ÃÛö”ŸJÛ_\nÍÛrük«JWì­l­m¯ÛOJÒ��à¿_u‘nOñ?«6YK©KyÖß´ üêV•­Ø½—¶å¸×V”¯ÙZÙZÛ_¶ž•nø�ø½ëíF6—�øâFV.%ô’ ¾aÑ}Ø»Ûd¶ïu·ßÉû IÊ9$×YwÛO~]ÞžŸgäo?_uGWu.²m'Uu·ë=6MñK‘¨ëîÇxf³"H,º8$›Žkµ¸’ß wÖÛ+u•­´­iO²­ÐàÝk×=a¬¿KÖ¼„õæšK£¾MOâº.%¬’èl¬pÝ~ƒ_‹}ÑYuimkg­´¯¥�Áú×®zÇ[&›­x ëÝ<×E|º®Åt\O[-ðÙXá¾L¿û¢ŽêÛmkekm+éFýé4ZN3©ÀÐñ½>«�èµX°ájôºM~&«S­ÃDz‘Á‰�®À‡í¥¶G–Ùm´ô¥)G¾{gµ}PçËÄÅÏÅÉÁÎÆÇÍÁÍÇ›3.òqrñrcºŒlœy­¾ñç†úÙ}—Ò¶ÝmkJÒ´«øäãcæcχ™9X™PË�•‹““ãäãÏeÑOðKmÑM Ñ][o²êVÛ­­iZz?ŽN6>n>Ff<x™pK�•‹“sãäãÏÑO�‘¶ÝÐMÕ¶û.¥mºÚÖ•§ ÒîÅøÜðµ ž.máÿ�™“åII2vÚ^²ã<;‘Í}.¶ú}NQÃ0xÿ#ºßu¿’¹^•¥kOOJ××QùïÇ÷„=›fM9—Š]Ÿ“™}$ÊÛkzÛŒñ®A=öÖ—R²rN/�¦ßÝöÛù>óéZzÓòV¾º‘Ï|ð�³#žÞcâ§Dçde_l™;]g\q®3¿šën¶ê}NGÅðtÛë©ëoä®O¥iZÓòV¾º¯Ú^ øMÞ<›+›wG‡Þ/v×5Íă/™vOAuW7åÙ8µ–ì\Iy7$â›-娸×O}c³ëûc­÷{}=jÓÞWðñgÉ1o‡]Ð;î•%²ÛúOŠwoÝ•mdǬßf/)æÜŸMeØ×zIg¦/¥ÒSú%/¶·[]Lä;ìy"Âè�¿É’Ù-ý!Ç;o¶ë‘eo‚°Ç|x܃šïõVÝÞ’[þ¦ôºú~}/¶µ¶º�É>>5wøòE…Ñ{Ž'“%²ÛúC�öÏl}âÊßaŽøñ¹3ä›nÇ»ÒKÔÞ—_OÏ¥ô­m¯=y'áÑøeå[ŒÝîË­.&v}1é4\o¹<‹áúx¾í‰UÂãœO·ô¼w]ZÅ�mÒ} Hé4µºY=Ò_}÷kþÇðÍ|yæËd˜Ü·ÉÝE–ÇK.ƒ]ÙX¤º—]wÖ¾»n¨ÙÏI+K©o¥·Ûg¥´üß_Z×lÿ¿�YóG&/$ò+Ke‘û.ÇÖv–n÷]wÖ’íÇZí§¤•¥Ô·ÒÛí³Ò”üß_Z×ì¿÷�¹ÓY&/#ò#Me‘Ò˱õ½ƒÄ%†[½×]õ¯»oÖÛYé%iu-ô¶ûlô¥?7×ÖµÂܯð¹ü@ò,¼l�GOv† jÁ.ïNÉÌÄÍ–²ß'ß2oç;žgŸfM-º–zC<0ûm§æ{½n¯àÿ6;ãïýðü¬ÿ}aþ%Ÿ7üÜŸ?õ}òsý¾õÇø |ïót|ÿÕëÉŸöû×_â‰åÍRø˜ÿÕc¾ÃNËþ³=Çü7_º9c“g‰ÞœÆË1nǺIÚqbÅ4µ­•¦l•âvÊx¬²ÛöY±ñ®ÕãºÈór+en¾Ü|||zVêû"¶ž”¦ÐqO„Ÿ‹nŸËQâ?ÌȆ·VÈù_6íŽy�_}µ¶¿WUÎyÿ"ÕÏJRï³ß ÞÚý´ô­(Øî)ðïñ¯Ã3­ØjWË;3ž`×Ýml¯»YÎ9·"Ößö]ö{¢¯¥~Ú}´¥[Å~þ6¸vm™ú�x¦döVµ¤|«–v_:¯ºÚÛ_~·›ónC®’ž•û)tUô¯ÛO¶”l¿Qü!üLô–6N' ð; ÷1eß|’ßÛœ{?È ›.¿ùêcf÷ÎÓ²s0¬§î[ ‘ÛoîR�ïë?¼~éir'é΋éΦŸ.ÊÇ—7Yõ� àråGZRÚÇ‘'Òj¯šÊÒÚSÒêÖž”n¿\ô7Fôõr.êN™êŽ­»2•¦]Ýs×|C„W*•¥)ZdWŒéõ•ž•¥´§çzþFêõßCôwP]=ýMÓ=QÕ÷åR´Ê»®úïˆp›²iu)mÔžî5§ÖVjV–Ò•÷zý”t+«ú{©:CŒGÂz[«zç¨xd9åCÄz¿„qžÆ"Ê’Èã“&=ÖjuVdIVÛuôŠ—VÛiJ×Ò”f6WefFk—?óÄ~¨å{. Ú^SxåÖ¼ãMn ûŽÏû¿¬øo+ÕY³ÀÆÚëoÙqÞEÉõÛ|v¼Ør`¬°ÙIqæ²K}lºÚ×_ùÏ–>,u‡'ØðžËò[Çþ¼æz‹pïÛqsÜ�uĹ>²ÍŽ6Ï_vÇC¿äzý®¹úÜÈr!¬‘[I`–É-õ¶ëk\Î|¯ñk¬y6Ã…vW’�×¼ËSnû^%Î{�®øŸ&ÖY±ÂÇÙëïØhwü�_µÂ·;[—D5’+i,Ù%¾¶ÝJ×X;SÍÏ ú+—OÀ;»Ë¿:sžbààìòxOjwïTõï.ÇÖìã¬ÚÝ„üo—rÍFæ,„VÖè%ºG-´õ¶µ£ÆÿÎà/ùpø�ÿ|·L»W’ÿ‡‡ƒ¿å—â—ýñCþìKþ¾–OŠŸ÷Ãuû¯c�ùÏþ4ÿ÷aÞ ÿßkÐ_îüÿœ'À_òáñþùn˜ÿv§ü<<ÿ,¿¿ïˆê÷`÷ÂòÉñSþøn¢ÿuçüçÿû°ïïµè/÷~Îà/ùpø�ÿ|·L»Sþÿ–_Š_÷Äuû°?áÛáùdø©ÿ|7Qºóþsÿ�?ýØwƒ?÷Úôû¿?ç ðü¸|@ÿ¾[¦?Ý©ÿË/Å/ûâ:‡ýØðíðƒü²|Tÿ¾¨¿Ýyÿ9ÿÆŸþì;ÁŸûíz ýßŸó„ø þ\> ß-ÓîÔÿ‡‡ƒ¿å—â—ýñCþìøvøAþY>*ß Ô_î¼ÿœÿãOÿvàÏýö½þïÏùÂ|ÿ.?ï–é�÷jÃÃÁßòËñKþøŽ¡ÿvü;| ÿ,Ÿ?ï†ê/÷^Îñ§ÿ»ðgþû^‚ÿwçüá>ÿ—ˆ÷ËtÇûµ?ááàïùeø¥ÿ|GPÿ»þ¾–OŠŸ÷Ãuû¯?ç?øÓÿ݇x3ÿ}¯A»óþpŸˇÄûåºcýÚŸðððwü²üRÿ¾#¨Ý�ÿß?Ë'ÅOûẋýןóŸüiÿîüÿ¾× ¿Ýûd¸/?à�£Å5<ï¬ù¯ì^¿·.ý2༓M˸¦êÌ ü­VuúžEÇóv��¸[Lñ¦¬3_Hò!¾;½/²êSa8W9á=“Æ5|Û®¹‡çÜ3ynUú^]¹§•q�Ř9Ù:ÌÛµ{í^~«anË lyk·Ò9á¾;½.¶êS`ø_8á]‘Æu|×®ù‡ç¼7ynUú^[Âù§”ñ�½˜Y¹:ÌÛõ{í^v«an&Ë lyk·Ò9â¾;½.¶êSn¸/=àÝ¡Ät<ÿ­9§ìNÊpmÙñŽmÁyŸ–ñG­ºI!·a¡äš ͆›oƒt°ßm%ÇšHëuµ§¯­*õ¯RõX®cçãö«ù%ûÙÒ?¨n´@ ç'ö›y ûÝÓŸ©»@?çö™yûÛÓ¿©N½Vø¨ÿkw8þò]'ýŽå¸Ü䣒èã,�ø,ý•'~övÇëç´Öü,~ÌŸÿ{»CõÝÙk>¿foŒ½½�úë앺?_²GÁ/ï%…ý‘rZÝIuÕðs?µ_É/ÞΑýCu¢_9?´ÛÈOÞîœýHõÚÿ8Ÿ´ËÈÞÞ�ýJuê°�ÅGû[¹Ç÷’é?ìw-Æç%—GdoÁgì¨ñ;÷³¶?_=¦°ácödø¿ûÝÚ®îËX)ð½û3|`ýíìï×_d­Ñø2ý’> y,/ì‹�:ÖêK¨Ž¯€+˜ùøýªþI~öt�ê­ùÉý¦ÞB~÷tçêG®ÐùÄý¦^@þöôïêS¯U„~*?ÚÝÎ?¼—Iÿc¹n79(äº8À #~ ?eG‰ß½�±úùí5ƒÿ ³'ÅÿÞîÐýwvZÁO…ïÙ›ãïog~ºû%n�Á—ì‘ðKûÉad\�Ö·R]Du|\ÇÏÇíWòK÷³¤PÝh€ÎOí6ò÷»§?R=v€Î'í2ò÷·§R�z¬#ñQþÖîqýäºOûËq¹ÉG%ÑÆYðYû*/þ÷v‡ë»²Ö |/~Ìß?{{;õ×Ù+t~ ¿d�‚_ÞK û"䵺’ê#«à æ>~?j¿’_½�#ú†ëD¾ri·�Ÿ½Ý9ú‘ë´þq?i—�?½½;ú”ëÕaŠ�ö·s�ï%ÒØî[�ÎJ9.Ž0Èß‚ÏÙQâwïgl~¾{M`ÿÂÇìÉñ÷»´?]Ý–°Sá{öføÁûÛÙß®¾É[£ðeû$|þòX_Ù u­Ô—Q_W1óñûUü’ýìéÔ7Z ó“ûM¼„ýîéÏÔ�] ó‰ûL¼�ýíéßÔ§^«üTµ»œy.“þÇrÜnrQÉtq€Fü~Ê�¿{;cõóÚkþ?fO‹ÿ½Ý¡úî쵂Ÿ ß³7ÆÞÞÎýuöJ݃/Ù#à—÷’Âþȹ­n¤ºˆêø¹�Ÿ�Ú¯ä—ïgHþ¡ºÑ/œŸÚmä'ïwN~¤zíÿœOÚeäïoNþ¥:õXGâ£ý­ÜãûÉtŸö;–ãs’ŽK£Œ²7à³öTx�ûÙÛ¯žÓX?ð±û2|_ýîí×we¬ø^ý™¾0~ööw믲Vèü~É¿¼–öEÈku%ÔGWÀÌ|ü~Õ$¿{:Gõ Öˆ|äþÓo!?{ºsõ#×hüâ~Ó/ {zwõ)תÂ?ínçÞK¤ÿ±Ü·œ”r]`‘¿Ÿ²£ÄïÞÎØý|öšÁÿ…�Ù“âÿïwh~»»-`§Â÷ìÍñƒ÷·³¿]}’·GàËöHø%ýä°¿².@ë[©.¢:¾®cçãö«ù%ûÙÒ?¨n´@ ç'ö›y ûÝÓŸ©»@?çö™yûÛÓ¿©N½Vø¨ÿkw8þò]'ýŽå¸Ü䣒èã,�ø,ý•'~övÇëç´Öü,~ÌŸÿ{»CõÝÙk>¿foŒ½½�úë앺?_²GÁ/ï%…ý‘rZÝIuÕðs?µ_É/ÞΑýCu¢_9?´ÛÈOÞîœýHõÚÿ8Ÿ´ËÈÞÞ�ýJuê°�ÅGû[¹Ç÷’é?ìw-Æç%—GdoÁgì¨ñ;÷³¶?_=¦°ácödø¿ûÝÚ®îËX)ð½û3|`ýíìï×_d­Ñø2ý’> y,/ì‹�:ÖêK¨Ž¯€+˜ùøýªþI~öt�ê­ùÉý¦ÞB~÷tçêG®ÐùÄý¦^@þöôïêS¯U„~*?ÚÝÎ?¼—Iÿc¹n79(äº8À #~ ?eG‰ß½�±úùí5ƒÿ ³'ÅÿÞîÐýwvZÁO…ïÙ›ãïog~ºû%n�Á—ì‘ðKûÉad\�Ö·R]Du|\ÇÏÇíWòK÷³¤PÝh€ÎOí6ò÷»§?R=v€Î'í2ò÷·§R�z¬#ñQþÖîqýäºOûËq¹ÉG%ÑÆYðYû*/þ÷v‡ë»²Ö |/~Ìß?{{;õ×Ù+t~ ¿d�‚_ÞK û"䵺’ê#«à æ>~?j¿’_½�#ú†ëD¾ri·�Ÿ½Ý9ú‘ë´þq?i—�?½½;ú”ëÕaŠ�ö·s�ï%ÒØî[�ÎJ9.Ž0Èß‚ÏÙQâwïgl~¾{M`ÿÂÇìÉñ÷»´?]Ý–°Sá{öføÁûÛÙß®¾É[£ðeû$|þòX_Ù u­Ô—Q_W1óñûUü’ýìéÔ7Z ó“ûM¼„ýîéÏÔ�] ó‰ûL¼�ýíéßÔ§^«üTµ»œy.“þÇrÜnrQÉtq€Fü~Ê�¿{;cõóÚkþ?fO‹ÿ½Ý¡úî쵂Ÿ ß³7ÆÞÞÎýuöJ݃/Ù#à—÷’Âþȹ­n¤ºˆêø¹�Ÿ�Ú¯ä—ïgHþ¡ºÑ/œŸÚmä'ïwN~¤zíÿœOÚeäïoNþ¥:õXGâ£ý­ÜãûÉtŸö;–ãs’ŽK£Œ²7à³öTx�ûÙÛ¯žÓX?ð±û2|_ýîí×we¬ø^ý™¾0~ööw믲Vèü~É¿¼–öEÈku%ÔGWÀÌ|ü~Õ$¿{:Gõ Öˆ|äþÓo!?{ºsõ#×hüâ~Ó/ {zwõ)תÂ?ínçÞK¤ÿ±Ü·œ”r]`‘¿Ÿ²£ÄïÞÎØý|öšÁÿ…�Ù“âÿïwh~»»-`§Â÷ìÍñƒ÷·³¿]}’·GàËöHø%ýä°¿².@ë[©.¢:¾®cçãö«ù%ûÙÒ?¨n´@ ç'ö›y ûÝÓŸ©»@?çö™yûÛÓ¿©N½Vø¨ÿkw8þò]'ýŽå¸Ü䣒èã,�ø,ý•'~övÇëç´Öü,~ÌŸÿ{»CõÝÙk>¿foŒ½½�úë앺?_²GÁ/ï%…ý‘rZÝIuÕðs?µ_É/ÞΑýCu¢_9?´ÛÈOÞîœýHõÚÿ8Ÿ´ËÈÞÞ�ýJuê°�ÅGû[¹Ç÷’é?ìw-Æç%—GdoÁgì¨ñ;÷³¶?_=¦°ácödø¿ûÝÚ®îËX)ð½û3|`ýíìï×_d­Ñø2ý’> y,/ì‹�:ÖêK¨Ž¯€+˜ùøýªþI~öt�ê­ùÉý¦ÞB~÷tçêG®ÐùÄý¦^@þöôïêS¯U„~*?ÚÝÎ?¼—Iÿc¹n79(äº8À #~ ?eG‰ß½�±úùí5ƒÿ ³'ÅÿÞîÐýwvZÁO…ïÙ›ãïog~ºû%n�Á—ì‘ðKûÉad\�Ö·R]Du|\ÇÏÇíWòK÷³¤PÝh€ÎOí6ò÷»§?R=v€Î'í2ò÷·§R�z¬#ñQþÖîqýäºOûËq¹ÉG%ÑÆYðYû*/þ÷v‡ë»²Ö |/~Ìß?{{;õ×Ù+t~ ¿d�‚_ÞK û"䵺’ê#«à�¾ä\‹k27\Ÿ{§ãšlJ{²¶Û힣Y�o¥k둟°Ÿz[Zþuôû(ù›�Þ›�kçÛr ¾¯GªÅ·Ý“³Ügâk5øöúV¾éó3eƒ+})_¶ë©ù/s¼ÒñÝ|ûnA¸ÕèµXÔ÷dì÷; Mf¿ßJ×Ö|ÌÙ ÆŠž–Ö¿�u>Ê kÛùÝá‚Y Þy‘â¶Ÿ"O©�²ò©p²m­Ñ[=–×#—G=o¾©}¶Òß[­­+JWÖ�yÛù«á¿žL]ï–~4i²áöý\M§{uvU•¾+g²Û±²¹LSÒé ¾Ûí§·Öën¥iöV�|Ûù¥áךLmï–5i²¡öý\]§zu~U•¾+g²Úãdòˆç÷ß ößm=¾·[u+OZV�rìÿ1ܘ0®Âìþïë>—nfV›LlJãr¾O©š™9Émɲ?o¾ø.¤”¥l­*À»ï˜/Œî94l<Äê|‹òbºhîÐÏ¿åPÛe·û+IòxÆ‹q�‹/¯ä²[¬¾´ûiOOµ…w_*¿ú a‡;ËN¨žüˆî–Êév;.K¶Ûw²´š~9«ÚÁ�'¯ä²K¬¾´ûiOOµ…w_*_:a‡;˪žù㬶WK°Ùò8­¶Û½•¤Óñí^Ò i=%’]mõ§ÛJz}¬9“ò—ñ›‰ìú¿!žÝõ=Þß»yOÒ¾žßo¯¿î|Þ§ëîû=Þž¿o§¯¥Xòï�Š›.ºÊùeª­mº¶Ö¶õO{ßmkm}+í¾Î®ºËíõ§ÙZV´¯î1åÿ4ŸQß|wyC«­Ö_u—Vβî¹l­m­m­l–.¶¾9,­iö]mkmiöÒµ£_ó=ñ�÷Çw”ºÝe×YulëNè–ÊÖÚÖÚÖÉ#ë‹ã’ÚÖŸeÖÖ¶ÖŸm+èÂù_8¿¸yY8“yßÑwË‹<ØÒß‹·ÜgbÝ$]÷cfáir0³ ºëk[%†Kâ’ßK¬ºëkJ×üÿϧñQþV:ÏðOß?â±þ?ç§øÉÿ* wø/îïñhÿ?óÐüfÿ”þ·ü÷_ø¶ùó~$Ë·¤¿ôw‘¹÷Ô×|ÝüYíi5q¼·ãR ÙKÿHð>ßÓÖï©îöýmúó¹§²¾êÇîöýž¾ž´õúú¿˜Ï�]½&»Ê~/ ­”¿ô§í %n¬”º¶ýw\ìŠSÙ_uc¥Ô·ìõôõ§¯ÖÖ|Ãülí©5ؾRñx©l¥ÿ¤ø‡gékud¥Õ·èÛ¸àødRžÊû«.¥¿g¯§­=}¿ù�ø¯æ‘fͧóóż;0$†)éË{gŒðdº{o¾ÊáAβøäû(í¤u÷ß�l¶G_J_[ku)\ãÅþM¾<¹|T—Qæ§�Xô¬™ÇÉ;w†ðÌ›¬Î¶·A˜œÃi¢Ê¿*žž’ÃK+4zRKl­iFfãÿ"žòh©.³Ìà¥aÅž–o{k†ql‹­Ë¶·Ce¸¼Ÿm§É»"”§¤‘RÏ« Þ”’ÛkZQ˜ø÷È—�üž:K¬òûÇh)t8³ÛûµøwȺÜËkt6[‹Ê6º|›²)éé$T³êÃw¥$¶ÚÖ”d¬“?�íž^»äÂ<üý†F>•�‘››™—-�ââbâEÏ/ÈŸ+"i-²Èí¶·Ý}im)ë_FÄð/ zµe¤_ÝÝEÙÝ[)l< ²xo0–µ’™wGJGÇ·[ë[íÀž´û>ÚC'§ó—zgÎÝý/Ù·[o[ö÷Wö ×{}¶ðŽÅ9e×{é•[=´Ðí³ë_}0fôþoÑ¿ÿ(»Ó>pžîé~˺Û:ã·zð/¿Ûí³„óî)Êî»ßLªÙí·E¶Ï­}ô›Óù¿Fÿü¢ïM¿ã\¯‹s=V6÷‡ò]+ÑæÇõ°÷å—Å8–ëeu¶MJ_L{å¬wÒënôºË©L¿ùløäãræÃ±ò»®ò/À²;箃–r¸¯¤±E5´ÂŸ‹ñÍÄédÔ¥ÖãÝ-Ö]K­º”ºÛ©L¾ùhøçã’æC°ò³¯2/Á¶;箇–r˜¯¤±E5´ÃŸŒqͼ©d´¥ÖãÝ-Ö]K­º”ºÛ©L!¾ùføÁ㘠ŽÃäìŒ}lÉÈ�Cä?Vò¬ù#¥¶Ý[uú¾1É·=´þ—SÒ,Xf–µõ§·Ö•ôñÿóéüT•޳ü÷Ïø¬xÏùéþ2ʃ]þ û»üZŒüŸ_§ƒ#Û[+ïúw]ôéé[ý´­+\�¢ùMøóäRË¿Ë>¥Ç¾¥ï®÷k�Åâ»ë]u–})ù6»QG¶¶×ßôî»éÓÒ·zR´­s^7Ê_Æn_¿é|†xGoÓöû¾óå?HazûýޞϾsˆ>§§·íöúú}ž¾ž´lGó[Ãnc›·ˆykã/*ØåM>.ÞfääO;r­ŒòÅoºú×y›4ÓÍ<0Å‹«äÙSÉ,ÙÙ¶ÒÚÖëï¶Ú}µ¥׎ywâ1Ì‹]Ä|Ÿñã”ì'–8 Áã�ÕÖÛÌɦšh±á†,mg%ÊšIeÈžË-¶–Ö·_}¶Óí­(اòsÆÎû†lž‹ò£û£ûìÈŸ©ûc�v40_°Á%“KÃ÷û‹"¾9òc²ê]ZV—Ém+öÝO]’ÄËÅÏÅÆÎÁÉÇÍÁÍLJ/3hòqrñrc¶l|œlˆn¾ñç†ú_eöV¶ÝmiZV´«`ñ²qó1àÌÞ¬L¨bÉÅÊÆ–Éñòqç²Ù`ž âºè¦†h®¥Ö_mkmÖÖ•¥}�“�›��™‡‘^&\dâåcKøù8óÇl°dcÏ×E4Eu.²ûk[n¶´­+èÎÐþÏì5÷—yiâ·_åÍ�Ï<˜ñ÷„çc]K20¹wsuÏËǺè~ñm³cny&Ñ]\Ï¥.¶•ö~wäûX;–ù;ã_È—�ù ÑÜ+* ©døÜ·¶xȆúÅ\Š[,;�þ‘ÝX)[ý+JWÙöþOµƒùg“ž6ð,‰q9Ï��Ã2ñãrÎØà\w"ëÞ)l°î7ørGu`üÿJÒ•ö}¿“ícîÂí®«ê=<¼‹µ»3¯ºÇ�Ál×Ͻì.gÇ8^ž1±2v˳ä›-nvÁ�‡4÷Ö·Ò–Ã÷×ÒÛn­0?,ùNøæávd߸óCǼÊbC òÓ‰ö&—žß}“ÉlV[�›‘ß›5·]ë|pÒI#·Öëí¶ÚV¬'É~J<â–dI´òû ²­Æ†)ä§ìm3¾ë&¾‘Ùn<\?+{&\Ôº¿�T¾K-üë©JRµa>KòMà/³"ý§—� •Lh¢šJq®ÃÑs;î²kíŽÊcÇÃòw²eËK®üøâ¥÷Ço­×R–Òµj¼Ÿ(1G|—|‡x9[l¶·V‘ùaÐóIZSÿ(Š{$²]þ…´­jûœ‹d¶C“寖û㤶×]×�Ë·Š–Öë¬ô¾}O\æÁž¶Wó.º—úzWÓÒ´­q.ÏæOãKS4peyKÇ%¾Hþ­·k8?kî¡¥µºë=$ÈÓð,ø"“ÖÚþe×RÿOJúzV•®(Ù|Æükj¦² ¯(øä·É%¶íoí]Ì4¶·]g¥ùŽ�RzÛ_̺ê_éé_OJÒµÆ|¯æs⣆eãamüùñ£2l¼jåEì}W<IJ*K|^Üœþ ^E�…“î²µú3IÕ·Òïo¶´­üúåc¬ÿýóþ+7þzŒŸò ×‚þîÿ��ÿ=ÆoùOëÁ�uÿ‹g•ÿŸ7âGü»zKÿGyûŸ|oœ¿ŠÌ©âÆ‹Ë=-²M}#²ìž°ï8)uß’²åfu”°YüÛ¯¾Ûiûµ~ŒO™ïŒÜ̘qaò�Md³ßHì»/®»“Û«û³fguÖ>&=ŸgóÒ_m´þkôbüÌ|hædC‹”:[$šúYeÙ]yÜ88öÝ_Ý›/7¯1ñ1ìÿߤ¾Ûiü×ÔÒüÛ|Noö˜Z|<ü~ƒ/>_£Û®S?ÕDzëýswœ‹_ªÒkböÙ_è™YëéO_ZÒ•ÉÜ[å¿ã_˜ËHu>dô¾%÷fc`Ò¼§}•Á¢úÙwRȯ¬üÛ�Án•»ú.En¦<4ûd¾ÚRµdÎ7ò‰ñíʤ¤zÏ.:kêåA‡ëÉ9/ð:?«“u-ŽúË˱´qSÚ×ú$þ¿F*}·ßm(É?«“u-ŽúÍ˱´�ÓÚ×ú$õ»èÅO¶ûí£.ãü¤|geYY"ù ð†Ûmº¶V™Utfþ´¥.õ¤y|æ .·Òïç©JÛëözúÒ­€ã^hxwÌó1õÜ?ËyfÃ.K"ÅÁã]íÕÛÜÌ™dš˜ñÇ��«å9SM$“×Ùm-¶µ­ÿ›O·ìg5忊|Ó.äß�|³;"û#ƒ �wG\orç’Y~„vC�«ä™SI|“~e´¶ÚÖ·}”ûYÇ�ùkâ¯2ʇˆy3ãç*ÍȾÈñðøßsõÆó*y%—èE8úÎI•,·É?æ[KiZÖÿ²Ÿkbú«ÈŸ»×ͧHw§Nw²Häš=�Uvw ì,á‹3'],¶eñÞßèâØaM×RïKf†û+ùÖÝJl†>F>^<X³Ã“‹“ YÙ8òÙ6>F<Ö[$3Á4w]°ËÔºÛ­­mºÚÒ´¯£`!šˆbÈÇ–9àž;&‚xo¶Xf†[i|rÅ%•ºÉ#’Ë©[n¥kJÒ¾´gèf‡&²1åŠ|yâŽh'†Ke†he¶—Å,RÙ[¬’),º•¶êV´­+ëFcgô@øv[=n›#g·Ø`굸–Û~VÃe—5—_lvß‘—•$PCmÒ_m´­×R•º´§å«òggàë1&ÏÙfâkðq­¥ù¹ÙâbAen¶Ê]6FEñõ¾êR•º´§­iGäÎÏÁÕâMŸ³ÍÄ×`ã[Kò3s²aÄÄDz뭲—M“‘|pÅmoº”¥nº”õ­(5?”|‚x'Ã"Ê“’y‘ãºL:ÅIð)Þ}m›¸¥e–­¤Z=#ËÜdV—Om×}8.öGZßw¥”­ÔÖIçG…œF™4ä^Zxᬟ±Ó#_tõÜûk+-ñGe-Óbrö²Ö•šÛ®öCw¶ÊÖû½-¥kMcäžrø_Ä-Éþyeã–¶|JÇIð/î~½ÈÛ[YdŠ;if›�dmf­+5µ»Ù ÞË+[îô¶•­5‹³¼Úðˤ÷–ñŽæòãÆ.£ä—ÛuöqîÎᆰà[ˬ²üúåc¬ÿýóþ+þzŒŸò ×‚þîÿ�…ÿ=ÆoùOëÁ�uÿ‹gƒÿŸ7âGü»zKÿGyûŸ}MoÍçÅž×ë}×Ë~1Ðú~ÿÒ\ ¸4Þï«õ=¿Gô¿^`ýçÓé×ÝôýÞϳÝéî·×ëjþc¾56ÿ_îžSqˆ¾ïô¾§éNÚ:?wÖúžÏ¡úk‚ëþõéô«îú^ÿg­=Þžë}~®³æ#ãcmõþëå/‹îÿOê~“áÝ¡¥÷}_©íú¦x6Þ}>�}ßOßìõ§»ÓÝo¯³â?2_œÛô‡èo>ü_Âý÷O¼ÿ »O�ð©÷ß¼ýÑÿÃÌŽ7ú[Ù÷K¾¯Ý~·Ðõ³êû>¤~ìÿƾI~>ùlWI¦óOÆ:VÛ­¶˜ûžéàäàz»ëX¾·¬X[Ýæ·2km�íº¶ÙZ[ZV•ô­+FpÐ|‚x1ɬ­ú¯/|qõ¥Ô¶�m;‡‚è3/­búßа·Û½ndÖÛ+[«e•¥µ¥i_JÒ´f9ò3ñíÌ78|sˆùÝá§)ä;¼~�ÐñÏ'úGw¹Îû¦,ùÙsÕë9ÆVvWÝpqešOe—{"Žëëémµ­6�„vW]vfºM¿[óîØ:˜«e%ÚpŽU¢åzè«-óÇ$ÍÐççãYY$Å–Û}n§­cº”þv¾›)Äyïì ¶¼ šq>o«³ÙïÙqG§ä˜ýKæ²Ïvf›37ß}øòRž·}µ²ê¥¯¦Éñ}Á{í§æœO›k,öRý�äz~I�gÔºk#÷fi³3qí÷ß�%)ëwÛ[.¥?%}7+7,¬<˜2ñg·ßN,ÑÏ�5žµ§¾)¢ºøä·ÖŸ–•­Õë±ýÀÌ|ü~Õ$¿{:Gõ Öˆ|äþÓo!?{ºsõ#×hüâ~Ó/ {zwõ)תÂ?ínçÞK¤ÿ±Ü·œ”r]`‘¿Ÿ²£ÄïÞÎØý|öšÁÿ…�Ù“âÿïwh~»»-`§Â÷ìÍñƒ÷·³¿]}’·GàËöHø%ýä°¿².@ë[©.¢:¾ÿËI4ÒG 0Ç|²Ë-öÇQGmo’I$¾´¶Èì¶•­kZÒ”¥âI,ŠËå–û#Ž;.’I$º–Y–R·_}÷ÝZ[m–ÛOZÖ¿e(ÿI1ß,·ÙQYt’I%ÖÙqÙmn¾ûﺴ¶Ë,¶•­kZúRƒ›Ýõòíñ×ã¥ùX<çÉî¾äXÐeßüê¹³{s{\ÌKåŠM>ö¼Åä:Ž7·ºhkm!Ûåëý¾´­õ¶Û©uyýÝÿ)Þøÿ&F8òCƒm·øöe[^1ÖófvžöÌÜJËdš­„\�`ñÝ�ÒÃ[i×#”­i[®¶•¥k Ýò—àW@ß‘…ͼŽàû}þ¼c­æÌí-幘·Keú¼øø.ÿ_ǶwI m¤{\ŒRµ¥n­¶ÝJמ>M|²|pø{›²ÓyæNñ.W£ßAƹ_qíÎoiv�Üdàþ’‡“õ_Sk¹Ïcq¬záVÙ+“Ÿ«ÇƲ’GK¤¥eŽ—q¯º?·UjåÌÁñïÅÞuÍ-» Xðù'ns^˳ô­±OwâX�‰‘·ÕÙwç{k¶×O%¿e~•'$;ñ+õn®\Œ.‰ñ»œs*Wxàßö�,Òuþ>>u=ÖÁ‘o㽃“¶×{©K½·ì5Ó_oÙ_§_ÉÉ^ÞüJ_¬—' ¢|pæÜƕƚ<~AÚ<¯KÀqñóéî¶,Šq¾3‹Ïòvºïu)w¶íŽºkí¯¥i'{ÿñ„øÃ$É×øãã?x÷¾Ç}�­›kÏ÷œS¡øNÛM‰5ðãr>1µÅ�·¹ž\:YõaÅÚqÝ6M±]oÕ¶)=ÑÛÊ®×üGŸ$þXÿ�;Ž èÜh>¥¶Ù×�c¯ä9yV]%÷Ç~Ç'·ó{7L›,º–Öìh1#º–Ò¿N•õ­y—Ùÿˆ#äœÝm¼7eÔý1�×Ò;¸']aï³¦Š²ß|Èí\îÄÛ"Øî¥—_6-—RßZYJú¹�Ù߈ äœ_m¼;eÕ3�o¤uà½u‡¾Íš:Ëuñ×?#µ3{\‹#­,­ÐccYu)ëì¥}\aïÅ¿òAØpò½?PpOü|Óí3%»ŠoôÜ3�v?gñ=mký'Ø\£iÖ|�ce>É2$á¸ðßþ—Ç:ûä‡Ï®Ñ—ew4óÈ|Ü]½·Ù²Ój;K•ñ.3—òÖk¡“ŠqŽ‹�[�õ+ëHíÄ¥–Ò–Ò”¥-¶”Ð^yòç7edgMË|²ï¬ˆvV_v«IÙ“ˆqܘï–鮊þ1Ãó´LU–Éî‡/C»ÀÇÊ‚éã¶úÙ%·Y[©JÖž´fνòWÈ®¥¥lêÞúî^¹Šê{dÇàý›Í8¶,ñý[g¬Yš]Ö6LMe.ºÉ,ºË®§­iVkëï$üˆêoZuw|w]G}¾É`á—Ìø¾4ñý[g¬98º]Î6LMe.ºÉ-ºË®§­iVðuçÉ—ÈŸTÝ ³¯üæòËŽëxÎ\yšn7v~w Ç–)«‘ôr8F×’çq – ²Öµ—+li½kK캕«£ý?óÛòkÔ³j"Ëîí_mè5Sÿ‰¾ààœ_’Ã³ŽŸ–-¿,ÒàñŽÊͶïÝ¿ôå²ÓÓ캟o¯Az§çäc«çÖY›ÜŽÕÒk)m´ãÝ­ÁøÖò ë-õõ·gÉt|_°3+w­=n»sKþϲê}¾½ꯛï‘^°›[fgoê{OK­öÒœµxGÞÃ�e=}lÙr]çùžïݺ»Š_ö}—Síõê�Lþ)Ï–þ®ÉÚMÌùÿLù���‹�ƒ¯în•ãZœm ØßR—äêò:~ŽÚådåÒûi-v99Öþe+e¶V·Vîµt/â—¯¿]¬òÅêR;¦’»niÐÜ¢¾è ­ŸÐm×õ—`ÍZO-%§ç]'-²•¶ïZ[ëo¥ÝFéijn×ë¼�ñ¶û-õ’»^_Ò<’Ù+K}ŸÐm×õÇ<–:V¿RŸ�tœªŸe~Ë}ié^¡ô�âRŽ·`kü�ñ¾ë-­ÒWi˺K’RúÛg³ú¸uÎæ�ÝwÔ§ç]'(§æ×ì·ÖŸ�Û?÷Omc³_åo‡½�ÁçÅÓk¬»•ô4ã}�‹¾äU»éí%¿‚vö¨ÈáºR”–mäüšzÖ;½Þß©wq¼où…øôò~íf·„ù Æøw2ØãA%xpÇ?Vòh32ri�—3”Ó‡òmÔ—ßmi�¤Úí.­·}•¯¥Ô·³Þ>|­xä•úíg ïž9ÆyvÂ{©Á{NÉú×’Ç›“-!‹O‡7(·�r]Å×V•ú:mŽÊµ¥}iZú]éÙ�¾UüòBý~·…÷¿ã·>/þö•“õ·#�3"o££~Onä›k®­+ôtÛ•km}i_²ïIø�òßñÍæÎv&ƒÇ�*úÛ’s|üøõ8gÊçÚõohí¶µÔÓu“‡Æzó´5¼C•óXµø4¾¹zL]–wÅ%¿^µ²ïN›:&è‹£`Ï——‹�‹“��“�…ƒ…�6^ff\ÑãbâbãGtÙ99]d0cÁ •¾ûï­-¶ÚVµ­)GñÉÉÇÃÇŸ32xqq1a—'++&[ ÇÆÇ‚Ë¥žyç–ëb†b¶·_}Õ¥¶ÛJÖµôœœ|,|ŒÌ̈111 —'++&XàÇÆÇ‚;¥Ÿ#"yn¶(`†+ku÷ÝZ[m´­k_A€¿áqâ�ùNxõþ:ßýÒ°¯ü&ümÿ(N�ÿ  þ¿°·ü&¼nÿ(>�ÿ üþ¿¾_éÍ/þž5ú�Äÿ§ð¸ñGü§�ÿ üþ¿Ÿ§4¿úxÕÿêCþœÂãÅòœñëü4u¿û¥?á7ãoùBtøXà_õüÿ„×�ßåÒá_�ÿ×óôæ—ÿO¿ýHbÓ�ø\x£þSž=†Ž·ÿt§ü&ümÿ(N�ÿ  þ¿Ÿðšñ»ü úCü+ð?úþ~œÒÿéãWÿ© Oúqÿ �ÊsǯðÑÖÿî”ÿ„ß�¿å Ñÿác�×óþ^7”H…~ÿ_ÏÓš_ý7ɸß1ÒáòN#Èt|«�l~ñú?}ÆöØÍ.ÜòçÀËûžÓY‘•ƒ•÷\ìYa“Ù%ÞÉcºË½.¶´§ï†x2b¶ly¢È†ÿw²Xd²X¯öÝ[.öÉen²ïmÖÖ•ô¯ÙZz= î>ãú€<×/æ|?¯xæ×˜óîWƸ?ÑãýïwÊy~÷WƸæŸÝmŸyÚï7YXZÍ~?¾úSß4¶[ëZS×íyþUËx¯ãûNY͹7áÜWI�vfë’ò­Î»�qýF•¥·em7;|œMv5·]JVùd²ÚV¿•çùO,â¼A³åœÛ“qþÅ´¸õËÜò^S¹×qí§ÚÒÛ²¶{�¶N&»Û®¥+|²Ym+_Êù{½Þ—Œévü�’mõ|�qýf~ï}¾Ýçâj´ºM6«\í¦ßo´Ï– n³[ƒóddM%‘C—_}Ô¶•«‹ÞG~ ï�Š—a¦áü¿•ùË0ïØá×]Ó: r¸¾6Ë*×ïÝ…Ë2ø×ÏÓgOém3tRïie¾·R;ý)m܉òçgÀž“—;QÅùw%ïîQ‡.núþžÑÙ�Çqó ‚²bß•Ï96_â› >\նϽégÜÖÊV·};½=+ÈÎÿùÕð3¥eÎÔqnYÉ»÷“âI›‰v¿¨t–æqÜ|ìxk~5Ù|ï“eñÞ-�¨Ë›ÒϽé¦Üû)ëu#»ÓÒ¼;ò§ñüRx·6f¢.õÌòK˜`åiâÈâ~+i°ûZàî0äÍ·o‰ÙÙ›®+Ñ{<]e–Ùff>/+›e�4”ޏµ¾Ù)»Ãñ?ùEËk±Àè^�ê^›ÔåáÓmËówÝ·Íu¹?›îÙësé^ ã–êÛù°æhöYmkJÖJú\ã?r~$_#ù=Ù˜]!Ò�]ÔúÌœ[±âÙòüíçiòÜ)þÏMŽ».*ð^-µôõ¤9ZŒøí¥}+[ý)s�ÝÉø�¼ŽäõÍÂé–êþ¨ÖäâÝ�Ï—go;K–áOözluùq׃ñh%¯§­!ÊÔgÇe+éZßZRäz|ƒüc½ß·ž˜^+øÕÜq²²þGߜǕv¶nÿ�zK¦x8‡_ßÓø¼3{_Ìš{/Üò k+ëµ¾ž’×”=©òÕòCÜYWdòß0{“WmÑ}Þ¸]k¾‡§5—cû}•†moRáð¬<«o³ì¾³Y%òRµ÷ÖïZ¹ƒÙ)Ÿ!=±?ÖåWöƲßebû¯]î1º� èkJÛô¦Ãê¼.]µ²¾—Vkdºú=[¾×1;/åä¶'¬Ü£ÊÞ×ÖY[+Ý:óq�Ô¸7CZVÚÅ6Váðü|«n²¾—Vkdºú=[¾×;·çÿåÛ½õ›#ó;žð�v×ô®>¿¤´¼'£vzŸfM¹8úÝw;ê®3Å;.íV?¶‘ý�ÖMfŽ•¶k¥÷]îÐm÷$ä\«c‘·åý×$ÛeÍ&FVÓ}µÎÜlrgš¶ÖY²3vädÍ4µ¶žë®ºµ»Òžµhîÿ’ò.W±ŸqÊ7û®I¶Ê–ù²¶›ý®vãc“4¾•’Yóv3ädÍ,žÚ{®ºêÖ¾ŸkH7Ü“‘r�„ûnO¾Ýr=®L—Í“³ßm3¶û ‰¥öýIgÍØO‘“,’{iîºë«ZúSÕÌnØïŽñï½Æ7"ïNæínèävÏn&÷¶;—ö.ãÜ«1#É·gÌ7ŒØ-È� d¥·ÓßHc¥}ie¾Ÿñ_Š@Ó…vGbu®Óy×\÷šp Þ,¿_q¹NóŠí1¦úrCõ±ö,ì ¸eúRÝoºÛé_mÕ§ä­^ˆv7`õÆÊ-Ï^s¾eÀ÷Iõ¡ÚðÎO»âû(e¤wÅIbÎÑç`åG'Ò–ë}Ô¾•öÝZ~JÕìxobv]lbÜu÷:æ<oŸZ§ äû¾1±†_§|_V,í&vLr}).·ÝK©_mÕ§ä­Yó¨<¬ò‹Ç¼<ýAù%ß��µÉ¦fÓ¨;‡°úÓe™lU‚ܬünÈ´°ædÛ Õ²—Ém×RÚúzú:Õ2?&=}iǼµìŽM‹%ñ×' µë¤î;2#Žú_ôi�ÚžW¹À²JÓÒë±2±å­>ÏwÚÞ®¯ùoùêolz/(¹÷%ì‘ß‘‡Úé{gïQÇ}/¬l»UÉw¸‘ËééuعxòVŸe.£z:Ãå¯äCª+K4~PsÞI‰Y#»# ³íÓv½2£Žú_X+±ì]_$ÝáÇ'§¥×båA%iöRê:•Ò?ˆÛå뤮â6yI7lñ>%5—IÄ;»€p·•âÙñþ�åý‡?×÷VÒÖh¹F>en¶ŸÑý+u.êïJþ(ß 8ÿÒÃïïz»³±#ÆÆÆ³mÖ܇‘õFö²Çì¶}žÎ Ý��¤ÚdÏe·]Xq`ÕCï»óke´öº{Ô‰G¼4žÌ^òñï­ûر±ìÚuÏ äc·³ÙôìÈÏØbï,ì�V×*[iußKÍdU¾¿el·ìtã¨Wwèþž'xøýׇ�lXøöí:ï¿ë=ŵ³éÛ>Ãa�»³±µ;L™m¥×V,xõ‘Vúþml·ìv‹ÇŸÆEÏð¿Ejü°ðׇrO¼o¿êß9ñçžn¸Oè~1¥?êWTöL�ü$ßbúV¿Ñ¹–«_Oè>žµío�_ˆ㟼æÄÕr>yʼ|ä¹SkðáÖwwý¥ÊÌ˲”É“�q<î]Ã5Úœ,ŠûnÊÜfê+[+Kë´÷û; Ðß:>wT¸ÚÍï:ä}ȲgÄćYÝ~šmNFDöSëÍ5ãYܧ…ëõ˜ÓWÛõö¹ÚË®¶´º±ÛOu-ìD|æøÝãk7¼ç‘ôW!É›-grñêiõ99Ò“Ë4ã9¼§‡`k1g¯¶³í3u—VÚÒúÇm=ÞÙx³øŠ¾(¼¤³ ?"1üyæxÛlÙxg”Úì~�¿_ƒªž~¾oddì÷=\�•2-¿ Y6Âk)u~ïJÙ}-ì¯æ³u§ÊÌÖçÃI,ºß|Rßoº•§¯­l㫌sm»”pÎG¡åÜgqÞu<‹Œn5ûíÓÝuŸx×mõYzüØ=öVžø¤ºßZVž®´qŽUÆ9¶‹]Ê8o#ÐòÞ3·‡ï:žEÆ7ýö‹i�îºÏ¯®Ûê²2õù°ûì­=ÑIu¾´­=]¹ÀÏÀÚàám5y¸›-fË?]±Àɇ3?2213p²ñï“++KoŽK.ºË캗[ZÒ´«Ñ¾óï?XZ»çÌü`ÄÈÉïÞýêþ°Éƒ Ý�œ{‘r½wðÓa…|‘ÇIôœ\ÞkÈ-÷KmkL ŠÒßέ=´­i¯½Ùå�^8âI“Þ=áÖÝk=˜uØC¤ä\£[,ØáÒø£ºm' Å›'–ï©mÓ[ëL,,‹©JúÖžžµkïuù]ã_ŽX²äw‡wõ·[äY‡÷ø´œ‡”k£å› O|qÖm7 Å—+–om¥ÒÛëL,)ëKkëZzRµ`ÞðòwÆÿµZ½ß‘]÷Ó�«Þ×egÊí¾Éâ}NI>Ÿ,½¦‡”íõ“ò=†<ñÝ~6 ¹ÓêYOg­öÒ¼_ïÄÅá/_Wg­é^Û~AnqoŠší¤œn­ëÝ¥•÷}jþžæU—žaIiOm/â·[­:ž”õä/uþ"o ¸'é  â]§Þû|{c»_²ÅÔCÖÜa[½ÿRÉ7|ËÓ›áIiOËÆd¶êWùï³íäWtþ"x%vEÄ{K½vØôŽºý–.¦·à›u.ú–ߺæþá_iOO^5}·z×ó¾Ï·‚}ÿø­¾-zšë0z³3»üžÙeiö9˜ù�aÖ9|3‰kwX·Ý‹“n;Ã;«¹-6Ò—ýïS¦ÝãÇ­ß�¤Wr_¸?חܲM¾Mô×Itþ›:+¢Öfoläý§Íô—]m)Lˆ7™[.!ó²l»Ö´ûǾ/ܬuýÞ]v¿âAò£“K±Æê>ŸéÞªÔåÁ|XY;úò^Íåú™.¶Ú[“�¸“aø®LñÝëZR} ±×Ö”­•ôûy}Ú߈ëÊ~M&Ë©zƒ§º¯S—ñadï¿„�—˵7]m´·#q.âÙ3Çu+Z}} ‘×Ö”­•ô¯¯;Ÿñ�ù9¾®¦ž=øwÑ}YdQåÛ¿¯psŽ{ÞÓì$’–S ]œ6/!ÑWÒêÉL˜¶tšµ·ÓÙJV—sC³þb>L;nè«É|¾í-xñß õ„º.𶨝’é}%—©´Ü/'6Kn»Ò’äI,Ô·Òß¶”£�}‘òÍò+ÚT²>Cå_c顎ÛãŠ.·®‹©+dwËYimù]_¦â9ÙWY[½)$òË/·ì÷z9ÝØÿ,_"=£ì·�ùSØúh¢²øá‹®.Ñõ/²;䬾Ûò:ÃOij2¯²µô¤“Ë,´·ó}ÞŸc’�³øŠ>`ûs “é³ü¼ÜpN9ɥɯèn¦ëΪë|Þ?‰‘}n³_Æ9ïá8½«©‹ßÍŽÓòfúS󧺾µh�9í~ÒìýŒûŽÊìžØ{l›ï“'iÎy�"廉%¾’I|ù»ýŽÃ&[ä¾Ú]unºµ­iëV”snÓìîÌΓgØý�Ï;e5÷É.ÛrþAÊó¥’K­¾KäËßlsç¾ûï¶•­kukZÒ•i_5í.ÍìœÙv}‹Ø¼ïŸl¦¾ù&Øs^]È9Vl²Iu·É|¹{݆|ò_}öRµ­n­kZRµswµ|Ëòÿ½´´ã}ßåg’]ÉÇiOJh{W¼û?°ô´§Öƒ#Òš¾]Ê6ø4§Þ1c“ÿ-ÿ?·~[iZxƒxF¶€=wìy×Û(7< ›rî¸Å¾²ãm¸‡$Üñ­–<•§¶²A�¦ÍÂÊŠúÛözÛ}+èõ¼GŸó¾¿ØÇ·à|×–ð�´7ýHvœG‘î8ÞÆ)=¾ß|yºlÌ,›/ö×ÓÖ—R¾�YĹ÷:à;öÜšrε†ÿ©ωr=ÇØE'§·êG™§ÌÃɲÿoÙëK©_Fyê*<žñó7¡<Žïž�ÅÙMvFǨ{°z×?"û,ŽùóaᜇKT×G¶ÖëéuÕ¶ÚS×Ò”oßV|Â|–õ’_Ƽ½í]üsûi>?iæj»ž9,¶´¯²9{gWÌòð½ÞŸmØòC'þüÞN³ùcùêšÝN=åOdï ’ëk6?fO©íÊKe.¥ÕŽÜžÏÕò͆%·úzVìy¡¿Óì¥ÔoZ|¯ü‡uUÕþùOÙ;Ød­¿[²ò5]·l¶[wº±Ó#³uœ¯?—þJÝ�42z}”ºŽœt—â"ùzèì^1¦×ùk»ì¾-Ʋmšþ=ݼ3€ö®W#ǧ¯¿_Éû“q©û‹c�-kO[ìä�äÓÓóe·íõêI~(?'xµøX]óÐÝIÛÚœ\ qdÙp��"ê>a�›JÛé¶Ùçd_Ø|S"úÛëïÇÄÓkã¾¾žÛ£§¯¯Kú{ñ$ùÇ$ÇÄîî�êÎÒÖA‡5søVË�ug(Èʶëi~Ïe—™`ñ¼»î³ÖµƒW¯Žë½=.²Ž–t÷âBò'Ž_�‰Ý½ÕÝ¥¬ƒã˜ôŽ‹.8î·݇(ÄÊš_HéÔºËní?ìÚk™õ§7â‰ÃöuššÞWÁy.›–ñ­…qä¬SÓ{ ÍØjòë¶ÖÛþœ·{n§¥}*ëç ç<'±øî/ëÎaŹçÚRKµœ£†r O(ã»E}b–¸;­^v·.‘Imm»éËw¶êzWíuã†sžØÜ{—uï0âÜïŠlé%u¼Ÿ†r O(ãÛ E}c–¸;­&^v·.‘Imm»éËw¶êzWíw#Œò~5Íxö——pÞC£å¼O’ë0÷\s“ñ�¶û�oôÛ,É×í´»­VF^·k¬ÎÆ’Ù!ž dŠ[.¥ÖÝZV•z÷©z—Ü~|¼¼\ \œììœ|,,y²ó32æ�;¦ÈÉÉÈšë!ƒl­÷ß}im¶Òµ­iJ?ŽNN><ù™“ˉ‹ ¹9YY2Ù>6<],óÏ<·[0õºûî­-¶ÚVµ¯£øääãácäffdA‰‰‰¹9YY2Ç>6<Ý,ùËu±C1[[¯¾êÒÛm¥kZú ÿ �ÊsǯðÑÖÿî•…á7ãoùBtøXà_õý…¿á5ãwùAô‡øWàõýòÿNiôñ«ÿÔ†'ý8ÿ…ÇŠ?å9ã×øhë÷JÂoÆßò„èÿð±À¿ëùÿ ¯¿Ê¤?¿ÿ¯çéÍ/þž5ú�Äÿ§ð¸ñGü§�ÿ üþ¿Ÿ§4¿úxÕÿêCþœÂãÅòœñëü4u¿û¥?á7ãoùBtøXà_õüÿ„×�ßåÒá_�ÿ×óôæ—ÿO¿ýHbÓ�ø\x£þSž=†Ž·ÿt§ü&ümÿ(N�ÿ  þ¿Ÿðšñ»ü úCü+ð?úþ~œÒÿéãWÿ© Oúqÿ �ÊsǯðÑÖÿî”ÿ„ß�¿å Ñÿác�×óþ^7”H…~ÿ_ÏÓš_ýø{‰}þBwÇàÛŸ£øü",ÌŽMØùÐåÅ4¸Y8�wÅq·\ʺܺÁ[-Í¿ Ì /­´’k=Ô«U|‹ówÅO1.›¾{³†ð�¥b‚|n!L¹ù?·*Ù+‹“…ÀxÎ>ã—ͯžè«oß+‡L8î­>¤¶R¾­Wò'Í¿¼Rĺ^÷î®Âv•б¸�¹Sò}�M’]‹“‡À¸Î>ã—I¯žè«oÞë‡nwVŸR[)ZUª~SyÉâ'„üfÎUåG�}kÓYZü½®›MÉ÷–äs®W�¯Í×ëöSð^µÑŶìN}]fV×™Viuy÷ã[-/––Yëu#Ñä_â�â˜4ÍÔx£ã†ß=q­¦:ï�Ì v>}·Ël´þÖü;mŸ¸ÖÝe,º9näºÉëëZ] µ£ƒ]ýø”x¦ 3u^1xý¸ßÏ\kíÄæ½Ù·ÇÐk±¶I%”º�}Ã36ùû­\–RÛì¾þA«È­+[nŠÊý®÷çâJâ¸TÌÕxÇãþß=qî·šw^Ú »>Ù$²·S¯øffÛ?q¬’Ê[}—ßÈ5“×Ö´º+jŒ•?ŒÇ>‘µã¾ xÛØ]á°‚¼ŸWaöÖó §¸™ØÖ}+Êx÷ÖaóŽwÍxÖ~Mk‘‘ƒ³·†li�e±úÅ$—]»¿çä·¼$Øãäù ²êÎ?�›&f7é .§­#Ô[%/¶Ü=w/ÕcÍÙ·áCeþ–Û•½Éº¾”ºë®º”¹Ç>åùžùîY60MÞÙ½c¡ÏÈ¿";ÓZ]__Gª¥Ô’Úcë¹f 9=“÷xì“Ò”Ÿy=~Ê]ZÖê{œxîO™Ÿ�îä“aÝë›ÖZ,ì™2!ãÝ7¦Öuüzº_õ)L}+Á‡'±þí—ú[I÷s×ì¥Õ­n§¹.üüN_-ýÝ>t\¸¸'Žüwi¡¿C°âÝÕÜsW•šÙlÈÝ`óÍþÚ]¥Ç÷ÒÇ'¥¹ÎA…H+m·ClwÒ·W™<ó³;´÷y<›³»›ö7$Ì–é³9<å{î_»ËšúÖ·Ë“¶äû ùå¾·Vµºé+ZÖ®ssŽÈìNÎÜKÈ{+žóNÂßÏ}òO¼ç§yË7_%}×ß.Ï�°Í’ûëöÖµ¾µ­\éæý�Ø}™·›�vG<æ}ƒ¿È¾ùr7œß”o9^Þy$¯­òM²ßggæË}õüµºúÖ®6w”KùMM¾@ùÞ�én‚é/Ñ[Ü}·Ïû:š[æ¶ë&¿SNkÈ7t×]-—V—Ven¥kJ¼Kżc€?Nnf¿",¼ ¼œ¸.¥ðåaÏ.6D7ÛZ]mñM öIÖÝJV•¥iZV�Ó‰™—¯É‡3+' /ûeÇÊÄž\l˜$²´ºÙ!ž¬–+í­=i[kJÒ¯Ó‰™—¯É‹3+' /ûdƒ+yq²a’ÚÒ¶ÉðÝd±ßmiëJÒ´­*õ|+�ó޵äšîe×<Ë•ð_§–“ê9W ä[~+É5sÛu·Û6»y¢ÌÀÙàËmÖR´º)m­+J1»�Gòeç÷GO¬“®|·îü,-4u‡Yǹ/4Øv†²Ök¡ƒ„v%yW¶¥º·Vß¹zzÝuÓW×rú·äkήšŸ ^åGrcbëbúNMËs; ‹aÃﬕ‹ˆv9OŠÊÉukéL:}µ¯ójÜŽ­ù󟦦Á“�yKÜx¸ºÈþŽ“’òÜÎÀâØ�ûî’±AÄ{øQÅ㎲]Z֔ĥ=kZþí]2é�œ¯–®‡®Óøç_wò 72C&t}Ï´Óùm´ƒÙé®þýÓvTüz9))}5×âVÿZÖµõ­jëŸ@þ'-ø-uÚÞþê¾­ï­.4rÙ™¹ÓÓ7¨û>Y+oÓÈÈÚé`ä<Ù�JV¿F7�Y}}+%¿Ï:¡Ñ߈ãÊ\yõwZ÷f££ÉÛi.Îê¾u™5×SÓ/+c­‡‘p©-†ßZý4žúýŸRßËN¦tâ1òƒ†W_Þ]aÖÝרǎèòvÚZæõg9Ì–úÓÓ''c¬‡�p»í‚ßZÒ4¾ÿÉY-ü´î_Ž?ŒOÈž;™»ÊÏzŸ´´—Ë Áþt—!äýCÊux1M<‹}²ÒrüŽÖã¼Ós>º||,Yx¾%Ùúu–(ï¥Ñw»Æ/Ÿ¯�!çÖèy79ÝxåÍs¾é�M7yàbhx¼ùÒcÝ&oÜ{3O�¹àØš¼I¬­–dn²ô²Ïî¶¶ÁK«[míÿŽ_8þ÷ÜØ:mÿ8Úô.Ëú~ˆî¼,N?Ǧʬ“.ìNÄÖgmø>.»[ke“m³53Oëmi+ZÛon¼tùÂðG¾¦ÁÓoù¶× ¹v]1âýÝ8Xš=6]ÐÖLºáö³;mÂqµØòÛ[,›k—©šjVÚÒVµ¶Ù%ø—øˆ>+|µ®« ¿âñóœæÅµÉþùKƒ…ÓÙ�bë6ºø/¯aM·Þt~f~îìÈäÀÖâr¼�¶DUº¿u¶±Ëm�¢Ôíõ[ý^»y¢Ùë÷Z]¾6ËS¸Ôæãlµ{Mvl6daçë¶r͉›…—�%·Å,WÝ–]K­­iZU×�nË]¹×àíôû -®«g‰�Ÿ­Ùër Î×ì0râ¶|\ÜÜY%ÆËÄÉ‚ûoŽHîºËì­+JÖ•uÏY³ÖîµØ;}>Ãm©Úbcçë6šÌ¸3õÛ ¸­Ÿ77Iq²ñ2`¾Ûã’;®²û+JÒµ¥]¼¥iu)uµ¥iZR´­+ëJÒ¿m+JÓì­+GÐ~×îÐs?µ_É/ÞΑýCu¢_9?´ÛÈOÞîœýHõÚÿ8Ÿ´ËÈÞÞ�ýJuê°�ÅGû[¹Ç÷’é?ìw-Æç%—GdoÁgì¨ñ;÷³¶?_=¦°ácödø¿ûÝÚ®îËX)ð½û3|`ýíìï×_d­Ñø2ý’> y,/ì‹�:ÖêK¨Ž¯€4›Ê�‘_ ¼2ÇÈ‹¿;ÇŠqÞS6Ë�ÖÚIfåý�™tøìuôþñ˜¶{íN&Ò iH3¶1akkuöRì›)u*Óï%¼÷ñ+Ä|yìï.çâüs‘Ç rÁÀ5RÍÊ»3ï8’f`VÞÇ"Ùoõø{ãöÛ›.»Ýu¾ü‹)ZU¨Jùéâg‰8óÛÞËÅøï#Ž%ƒ€j¥—”ö6gÞ1/ÌÁöðž;Ë}¯ÄØÇe)ntXºÿuöûò,¥iVŸycçÿ†~ hñ·¾Vy×�?÷üX³ôükoŸ—¼ìNC®“cfªíŸêÞ#…È{'”êñ3ä¤y9ZíVV>'¥nšøí¶ë©¿(ÿ5ÛI²ãþtN¯ˆë.·3²{ÊzrQ48PÇ�°ÖuÏÙcñ¾7¶ÕæÖ[ã®fç�âäR‘ÖL{iKãº<~I~$ŽY²“?GâIkøÎ¾ër ÇìëŸôß"–,ŒHã³/¯¸¦Î ‡e­Ë­÷Å~Vçw�=)edǶžèî�O’_‰–l¯ÏÑø¡Òšþ3¯ºÜœ|~Áî™é»äRE‘‰qåëúÿ‹l¡ÐèvZìªÉ|Wån7xÓÒ–VL{iK£º'¾]þ1 V%Û;ோÒm岸río'ö2ak+, ìÜãAÒýi¾·g°×æëcŽì „ÜÓ]‘’Ö¹ßèNøõùç�˜^YO“ÿ!;°59Sãå]Ã¥ÜS�uÜ9X–V¶â�h¸dVV´úñk¬šïZÖëëZÖµàçy½åŸ”2d[Þ�óØçS“&,Òq+ö–qþlø^¿uʃ¯8¬>l>ïü¿f¾Ùî¯Ûuõ¯Úá~yµå‡“òäS¼»ßŸó�VL¸óßÄïÚÛ àQωJÓ'¯¸¼:N�™+ÿ—ìÀ¶k¿-×Ö¿j-þW|¸ü�ù­‹²Òùåwfï¸Fß[&—gÕüC7«:§m¥®Þ»¼mo$ëž²Áâ\OšW:–}½æ.Ëam�ÅmÙ};}5!ªíYsˆÐ/~Q<ÞðÒ]VNw"—‚jîŠ:u/=’þyÕÒké°·g—¬ÁâÛùr?‚PìòiwÞ24êvÛ%ô¦Eµ¾ê×{<`ù(ó/Äy5x=Oܛ쎬¬1ÛÕœêûù¿Z߯�2¹óë0xæîY¯âxùù]YæÐϪ;—Ýé==ÕõÞŸþI<Èñ&MfTwú~­¬QÛÕÜæûù·[_�fgß§ÖàñÍܳ_Å`ÏÈ­ß^mú¬Ûé}Ô¤ô÷Wׯþüëü–x1“�uÇ}gö_Uè¡ÃÃÃéO Ìí~»ÆÔê´sht|�äìvx‡×¼kG�|rã븾ÿG…tØñýX¥Ž—Gt¥¼2üJ>®eÐC/0á?|Ê’–GøÛ<,{)Y26YJÕ2Ïÿ/ƒžKì4|É­&çÃÍÛÏ6ï—íáæ}³ØelõZ�f?öØ×ëtÛ.>ÃïÓfeMÉtš¾=©ÃÆ¿ëÙï‘ÿåÜSžñ½?1àÜŸ�sN#Èp£Øè9WÝkyÞkå­ÖÅŸ§Þiòs5›<).²´¶X%¾ÊÖ•ôª@Üc”ñžmÇõ³†r=.â»ü(¶Z.MÆ6úýÿÝë§¥k~£sªÈË×l°¦¥?2Xd¾Ë¿r©qŽSÆy·ÔòÎÈ´\»‹oðâØè¹/Ûë÷Üu¯›×èçjw:¬Œ½vÇ_Jûe†Kì»ÓìªM¼{�è9nƒGÊø¦óOÉø¿'Óë9äœ{g…ºÐrë –Ÿy£ÜkgÉ×m´ûmvLyÙ8òI ðÉmö]u·Rµô/ºû¯°×¯.?Š�“ŸÉëº?Vü•ƒü›þ-¾BxþØþÀ·ì!ä×ñnòûÈv¿ö¿|½çú˸ýëØJLª1X:±e` ¾d÷Šösÿ”—q,øDý—þ1ÿÙ£þP�°° á'öaøËÿfùAöºÖOÃeû¼0ÿŒWü¬;Õ×çV�Vw0…ûßȾ�ñ‹ƒevO~ö‡êÎ�t�Ç´å+qçÚæÅ�.]uK±·]–dxòåþŠãú¨mŸsÉwrccÉ|xü|¬Ém²êÙÞ•b~ëï~˜ñï¶ý­ß}£Ázƒ®´~–l9`òM_ÓS2HgŸS…‘³È‚»]öÎÜkíÃ×âÛ6vl´úxñI%imb¹æ—âlØO&ã„ø/Ö°àâý<Ý};øµÔÈØ_uñäâ[µàýc‹—÷,;±åúyXY{üœ«e¥}™Z{=+mc=å÷â2˾]·ð·®âƒ–æ`S¹»g²å_uÖåâSeÃ:ß*�cý©V^ó&jIJû2u6úVÚÆ—ËÏÄY—$»ná‡^E��KrðiÜ�±ƒYr¯ºër±i²á½q�•L|o¥}#ÉÃËÝäÏI)_fN¦ßJÒ±óçñvp>/>ç€üvõ%�›±‚û±,ò½5ûÎ=ÀkXòm¶\¾Ô8y:.yÉ0óu×ÖìlÍîw›.ÏèÚ¬¸~ËãßÞSyå7'»—ùÜ\ïµwäædëã弉ôZ çÉõr±¸ŸÆû¯âéoû~ë«ÃÄÆ§îYDr;ÓÉ®ÿòc‘]Ê{ã¶ù·fí-ÉÉʇ‘î&“C£¿/Û\˜¸¿Äû¯âxrÖÚV¸úÌ®ãÛ�~žº m·ê^+�¢ë>-·®¢ûá›3_ªÆÊʬÒÉ<’Ë4·ß€Ø1ƒZnÎ}ä×�^1r{y‡@w>ê}åÓáO�äºý^öÝ~E™X¸|§�Vùx÷.ÕYÆké/#ûãÆþGo*è¾Ùça¸»##7ø+½ËÂÕnþá-'ÅÆäüvûæãܯ[´õ®&Ë/ÿôÑÖŒÓÒžFw·Ž\ŠÞUÑ�±Î:Çqtø“æ×‹orðµ{¿¸ÍløØ¼›�ß|¼•kl–ÚV¸›,\¬[ÿÓGZ6óÅo>|Ëð�uM׊þFöwOG&ÒmÞÇ‹h·Ÿ¤úß�î'ÔÉ£®Ó—uO&ƒyÖ\Ïa²O§»mFeøõ²;â­’EÖÈóÄ/ÄåÙœ^íWóKªð»;I¶cäv¿Qck¸·`Çe>ó$™ÛΛ•…ÀùF\ÒÉt¦»#ŒÅVï¥<ŸÏHÅ?ÄoØÓ'ôŽß*,{þï‡�Ir².·ÛwÝéF?ìÎ×ë˜â¹œç¶û‡õ·Áº‘dr.kÈu|sU÷›ã’Hp1ò¶™8Öflòí†êA‹ÔÈžú{c²ë«J1ÿfv¿YtÇËçµÏø‡[ñ¾”ü‡šr gÕ×&襖 l�žN=¹»<«aº�bÃõ2'ºžØìºïJ-rq24üWm¤ÙÅ$u‡eK.¬–ÇOÉ›_‘$åÏÃØ÷¦Ë¨8ŽfEg‹„ô3u~¿;±nÄ›îW¬ÌÉìݶ»*+î¬¸Û î^5÷ÝZý:R–Òœòæ?Ïï!$ÍÄÍîŒî¦âÙSY4|?¢à›­°ñilcÉð«3/²v8Yv_ZÍ�™¼ÉƾêÿåºSÒ”àg�_1^}ù&n&osçu?ÊšÙ£â Ýo‡‹JAv<˜õå8y]‘±ÂÊŽúÖl|ÍÞN5÷Wÿ-Òž”¤T|²üJ?)þPÛ´Òh»WâÿÙbÃ…ñ›O?Ý]nÖMŽÎîÚÛì9/rk7·Åô±ò¯Óïµ8QEí®–É-·ò‹'''7"l¼Ì‰òòòe¾|Œœ™dŸ#"i.­òM4ÒÝ|’Ë%Õ­nºêÖµ¯ås''#3"|¼Ì‰ò²ò¦“#'+&Y'ÈÈžkë$³O4·]$³K}պ뮭kukëZ¹‰““‘™‘>^fDùYY2É>NNL²O‘‘<·VùfžinºIe’û«[®ºµ­k_Z¸SÉùG&æü‹wËùŸ"Þòî[Évy›®GÊ9>ßa¿ä\ƒs°šüœý¶ïwµÈËÙmvyÙ]$ÓÏ,’Ë}Õºë«Zú¿ƒø¿‹á3/IùÞž7ò›y§CöÏ=ê~I_»Û—ŸÂy&ËKß",¸µÜ‡[�55|“Q\ˆl¾ü-„8’VÚ{ã¹—ºo¿û³ÇžKN_Ò¥ÍúÃ�]\zeåñ þvª ¼³Û“ ÖE-u\�UIì¥×bgÁ“‹}iùÑÕ—zs¿;«Ç¾KN_Ò]£ÍºÇ�Wè[—™Ä7ÙÚ¨6ÐcMnDX;ýlRþ‹äZºOe.»> ŒkëJ{£«k|Yó›ËÏ ¹5yW‹AöWMfO°‡k¶Òñ­åÙ\”çâëóõxYç¬÷±múçž×]‡³ž˜¶îµYöcI}$Š–Im—Û$�?wfñY5¼SÍŽ²Åí qG�7lu§‹ö4WG]÷æï¸^^¯€r™ó2®‚:þŽŸŒÇ‹ ·ßH²oô²²ñ;ñv»_Æ<Åëˆ;M1ãÉÚ=S…«ã¼öÛ¢ƒ"·fo¸6ff³ƒò<œìªÅmÕ×OÇbÅŠ—]lÝéedâ‡â-ì.7v¿Œù…×0v6š8c‚NÐê¬-gç¶]3ÖìÍïËËÖpŽG‘™“tVÝ]|üz,h©uÖÃ=Þ–VZþþ/mžz^ òÒ?¦"‡ ]^þñçm‘.>.‡WËžô×!Ûcjr²¶ì6›}§ÛàAºÌ}öúR’­ñ{Ì¿<Ëâó?ûcŽvx•ä:Li'Õó.#6mr,ÆÇåÜ/q%ãÕÉ›{1åÈÆ·3è_v4³Go½&¿ü¶ñßËn)//èÐãÜûÌZïu8²O®åœZ\˲cŃ•q ´8<“�Ý•.öãÉ“�dt†ûñä–:{ÒdñÇËO|´â²òî‚ìþ=Ïp°¬Æ®÷S‹$ÚîYť̻"Ìh9WÛC…Èôdˇ5°I‘�dt†ûñä–:{Óñ‡ÌüÏëøû;ÅÞëà½ÍÃé÷[6YSgu7œc+:É¥ÃÕ󎵃[̸ï&.–Ì Ö¿6èiI)²´º»<ØÖÆ6Dá»?‡EØ�iØ}‘<°sž ËxtÑeäeâbË&Ðl4²G“•�Jçccßfmi|�Ò²Ùmk[?:”yÁâÐóžÍøVDpË�Ì8‡%âÓÅ“>N6<�ò .n¦XçÉÂÿVcÃ}™u¥÷ÅýËkZÛùÔ£Èv‹œpq§² `æC’ñi¢ÉŸ'H¹—7S%™8T®f<Ù—Z_|_Ñl·Ö¶þu(þSÃLcÍo¾ˆ¤†[=×[ïŠ[+d–û¬­·[î²êÓÖ•¥iûŠ�c*ÖQ¨ÿ´­m­.¶µ¶ëkJÛu+ZV•¥}iZVŸm+J”­iZV•­+JúÒ´û+JÓòV•þkþÒµ¥iZV´­+ëJÓì­+OÉZW÷+Gõ‚yñg‡'iqòqåŽ||ˆ$¾àžé$SC,u¶H¥ŠKiu·[ZVÚÓÖŸjÛ~§ævö?Vu§aÙí¥œó¯øo3¶–Á~-´·”qÝnòßn4’Í~=¾™ße—_}lü•º¾ž«M:Û•ÙÏ:ï€ó˜ý)3á|[•ÙKa»žÎE£ÁÛÛé�|’ß=¹ŸÎVû«oä­kéê´‹­¹]¼ó®¸8²”¥œË…qnWe-†ìzRÞE£ÀÛÛJcß$·ÁOLÏç+}Õ·òVµôõ^MñdÁD7{áÈŠ9â¿Òë}ÑKe$ŽïmÔ¶ë}ÖÝJúV”­íÕý@ïókåÄ0%Åî>}]×dI‹V¯¥ºîÌOÚðäýÚì|¬ý=û~¯ˆë'ÆÊ¤ñenó5ÐåCŸu®D–}:èo˜Ÿ$~)øGƒ&?ns¯ÒœþLhrµ�AÀìÃä�—°‡#é]�••§»?_Åõ³Á-e‹/s—¯ÇÉŽ9)�tÒ[ôë¡þa|�ø«á6 ˜ýµÎJóù1¢ÊÖu³‘öN|9Šì|œ½=ÙØ8c[<2ýX²·Zø2cŽúcÝ4–ý:óßÏ”o þ8¸¥û¿%ûsY«æš‹w^N\Ò]ò³j0ñq«|»šÉ}±RU^üúøýÞ_¡ø7“xx>:v~U`³•M•6OHò<Ù.ÁDzK9d’ìºæ\¬‰æ’±në&¯t›{ä¾ØÒ£ð£ç¿ ;ƺ~ äÎŽÝ›•X0ãå3eM“Ò|‹6K°±ì¾ÎE™$».»—+"y¤¬[ªÉ«ÄǃÝ&Úé/¤iËüq~'/ <Àý ×^GK…á·{eA?§`r,Iz˜ì"ÆØO<œW·³¬Õáq<œˆu•—ô)‡Ue“åãààçí²nõºF™x¹ø¸ÙØ98ù¸9¸ðåáæbMN.^.LvÍ�“�‘ ×Ã><ðßKì¾ÊÖÛ­­+JÖ•wç'3 Ì9áÊÄʆ,œ\¬ilŸ'{-– àž+®ŠhfŠê]eöÖ¶ÝmiZWÑßds[.ß'üˆ+uرäÝmlsßÌ_“Ÿ¼+ÅË×v77·•ö\vÒ˜½=×Rk¹`RY!Çž ù%sñ5œ/X2ãšÙvù8—äA[®Å�&ëkc›Þ{|±ø=ñà›+ÈÎÛÁ¯`Kƒ‹Ÿ è~½®»™wŸ'ÆÏ·:ý~f�g�N=£ØSU•l;žA•¦ÑÉ4ALß¼]WÄ?ÌŸÄ濒ߤxÇTlqüUë,™f²=oWm3gìí–÷âK{þÝž Ö,øÓbÝ[nãØ¼~’E=ñdS"ÏDU|¶ùÛó Èkvk«sqüd묛åŽÜ·ÙædvVÃúâß{žÔ𠮏óc]u—èqtW]×Å=g·Ñß->u|¿ò›7ÕÙ°xÍ×y7Ëe˜=q³ËÈì�†%õƾ8÷=§48\I`—êÙv‡G[£–èæ¬öú ýç¿â¥ókÉ+7|Å]V¿Ã^ªÏ‹3]&óŽçÃÌ»û­ÊÄÛjò«‘Ù[ f«¯ãÏÆËÇËǧÖaouÐSèï'³í¯ ²òòöYÙùY¹¹sI“—™—4™9YY_Y&Ÿ#"k¯–i¥¾êÝu×V·]ZúÖ®)åååçådçgådfææO.Vff\ÒdåedÏ}ÒÏ‘“‘5×Í<óKun¾û«[®ºµ­kêâÎ^^^~VNv~VFnndòåeæeÍ&NVVL÷Ý$ù9]|³Ï4—Vëﺵºë«ZÖ¾¨Èr®YÊyß%Þó>oÉwü˘r�®v÷“rÎU¹ØòKÈ÷›<‹ò¶[�î÷o“™´ÛívR]$ùÉ4²][®ºµ­jüïÎüï€Ý>B|ªðW”Ó}Ð}‘�®ãÙ™‘åò~®äŸ[�õg1¥$źjo8†FLPclr!òÒšÙ07AîŽ,¸ì¾ú]¸Þ$ùçäß…<’›ž�ì¼-^]¹\‹­9'ßõ—+¯»³]¹â’åA.Æx°ãŠ»-t¸há¥c�*Ë.¾Û·ÄÏ<<™ð³’SsÒ�—… ËË·+‘u¯"úûî´åu÷cVjî8¬¹0E‹°ž×ZÉøl¿b·†ñŠÿ•‡zºüêÓªÎæ?ŽFF>&<ùYSÃ�‹� ¹99Ù>><6]$ÓÏ4—[Pŵºë®­-¶ÚVµ¯£ùÍ48ðË‘‘,pAwÍ<ó_lPà VÖù%–Këm‘Ç–Ö·]ZÒ”¥=jþsM42ädKðE$ÓÏ4–Å 0Åmo–Ye¾¶ÙQÙmkuÕ­)JSÖ¢3ß"ˆ£©úJmßUx[‰Çû׳ñ/É×m;gc|ù}'ÄsaÊû¼ÖqÛµ¹Xy�­³Ž8e­“áäch¬­ðM^Âß­�HíyñóçÕ½3.ã¬üCÅÐ÷wdãV\-�ggK6WMq\¨ò.†k4’ër±s;;epß[eŸKgÕ†[33ilØÔ�žß=½]ÓsmúÏÄ\]vv>5fÂÙvvt³etçÊ�"°ÍfŽMvV.gf죎+ël¸sãi¬¬�Ëf^m).5"áò•ø�¼kñ#’t爼oÊï"5¿C?”cmk™ão]çdëò²¯³sÌxÞ·jr-FEØVäé¸îF> Ÿ_" �Þà lÃg¾ü�ï(yþwg÷÷fò®Ðæ¹´š+6œ“?êãj0&ÍÊØþ„ãL[1´\KŽA››4�ëu˜Ø˜]%ßN+}ÕD‹¼|‚îŸ$ùÆ_cw§dr~ÊåùVË{C›Kñu82åO›]GÒbG‹¢âº²òd’=~· +亶Emn¯¬Jû¿ÈèòK›åö/yv7&ìž]•Ib�cÈ3i~.§ \©ók¨ãz\Xñt|[C^L’G�­ÆÅŠé.­‘[[«ë�-¼Üò£ÎžÃ§gùSÜü·¶¹.-“A ÄÛM‹¬â3 '[‹—¯à|C‹ªá|' aŸüËu˜8µØdCLœªÍ“uó]…u‡šªõœžs~²åZ~u×Óð.kDzk™¡å¼7{³ã\“M•tWÁ|úÍÖŸ+c…$�K|wV9-÷G}Ö×ÖÚÖ•õ/›ó>¸äúžk×ܳ’pna¡žì­')â;½—ä:œ‹á“IµÛ�FN&Ãùqæ¾;ë–ûã¾ëkëmÕ¥}G æüË®y6§šu÷+ä|˜hr.ÊÒrž%ºØñîA©È¾) ’mvãS“‰Ÿ‰|°K|wÖÉ-÷Ç}Ö×ÖÚÖ•Ê7ݽ¿ãÇah»c¢û/šõ/dq©$¿KÍ8"Ùq­ö$Y},Ü óu™_™¨ÚcV°fáOõ13q¯¾ã’+î²²„ðñ'sÎ!.�­¼ðãòv/¥Øºìnúຬ<ÁÒÅtÙ”ûï?áx_rÐsl{gÆŽì­MšÍŒ˜ÒI~>×._¶Iž~!~gÅåÓuï›z)9ß÷ãàc÷� Õáás�DrddSï|ï†áS GËp1cȆÛòµ6köbâÝ}Ø»L©}k$O?�3âòiºûÍ�œïŽûñð îþ«ÃÂæúˆ¤Ÿ"Ÿ{ç<; Ü='-Áƶxm¿+Uf¿>\kï»g•'­fqñ«ø´³qnÒuWÉ·“e“bëðü¨êN/‹n?Þsõ¿îžÐʼn‰>6F~nfÛ†ÁnE°ããâãq¼‰o“%/N˜ï¢ò#¯ôݧÒ‡Åû3€ï¢ŽüEŶQçcÅ‘~&.lº�¾%iÇ�ò3bûæ³>m† —ý<ˆb’•¶’©ê>äêÎùàº~ËéÎyÆûƒob²LAÆvçcY5ØðdˬÙãþfv“{�U”Ë×fÅ�Ÿ…-~œðÇ%+m%EÔ�ÇÕ½ñÁµ•Ó¼ï�ö'ÞEeø<ƒŒì#ÎDz{±ñò¥Ölñÿ3;I½Á‹*:eëóbÇÎÃ’ï§<1ÉJÛI²õGmu‡zõï펙çüK´:Ó™aKŸÅùÇÞëùÝcãfdë³i‡´ÖO‘�vN·i…>&\VÙñ3 – ¬Žhï²Ü¨Él”È` 'ß>GôWŒ'±»ÿ´¸‡Vq zOl;Q³¶ ÍÆV4eK«ã,k2¹-ÞWÊßf¯/6KiZÙÞŒ?Ý�ÿÒÞ8pÌ®Áï.Êâ½iÄñ©=±ì9.ÆØ2v¹Xøòeɬãšl{r7œ§wv4W_·/6Z[_dWz1u÷÷KøåÃr{¼{'Šõ¯Ƥô�aÉ66Á“µÉÇÇ¿*MgÓcÛ‘¼å«±ãºû0u¸ÙY’Ò•öEs÷�~t·�=s¼íÎþíÔ]oÇcºíŸ.ç{ì²¹?wÈÉÆÔk~÷-¹;ÎG´³û0u˜1älvR‘cA,·[eb�æ�âlåY6¼/Áž¶‡‹k+fVöëîv.דÍuñ[›Ö¸¹™Ë�Í[â’ v;®±²çÑê/ÆÈ¶“cdî²6–äEu-ŸYô­¨ÈywøŠùÆMŸðǯaãºÿfVöáí�~>Ë�Í[âºvC¯1²çÑê/ÆÈ·ëcdîr6–äEu-Ÿ[ô­¨p|€þ.ÍF«/sן}M�É«�‘›®—ȾûÕlð´9‘E'!ÖI±ëN›×ì5<ƒ'.*ëvš­·'Î×ËÔ›;�]ö^ŒGtwßtùÌr;½;C›v¯/ȲH,Ýs]þ~ê]~ ùY9´Ôé1re®ÑcåfK|81cáAY.¤qYJú#…Ü=çÜ^@òÙù×uöW1ìÞU5²ÃÛ—îó6×k°åÊŸ6º½&Ò~�ãúX²²d¾,±ðá­õúqÛJú#“ÛýãÜÿË'ç=ÓÙ<ó9TÖË{^]»ÌÚݯ×&lÏÑš\I¤û†ƒMND—Å…ƒ>$5¾¿N;i_D9¼¢ó?ÊŸ59·öÁò—½9ÿsr(fÊ›S)ÛR./ÅiŸYøÜ�éáÕð^¯Ïý óâé5¸ÓKgԾˤ­n®#b¦+kÜuÏfvPs7aõ_7å=wÎxô÷äiyg Þlxöÿ]|±ßE˜û-^F6M1òñ¤º)â­ÕŠxoº9-ºË®¶¾×¯;#Ÿõ/.Ôsî°æ|›€sM ×M¨åKsŸ¢Ý`Ý%•ŠxâÎ×Í×båÁuÑO Õºáºèä¶ë.ºÚûN¾ì~}ÔÜ·SÏzÇ™r^Í4S]6£“ñ-Îv‹uƒt–Ö9ã‹;_4]�— ×G<7V觊룒۬º¶×4ô‘�ëâ¿fhû‹Ç^Õæ�?Ù<~\k°ùG ÜäjçÌÂÇÚk·qþE�JÉ©å¼CgŸ©Ç®ÃKµÇÍÔl㊑eãMkeeŸñáø‘lË—IÕ_ XbË%mÁ×ù%Ãtt‹ûëìú7vŸ_èq=˜¾û½ô»kÇñé¾±Y~²Ëi6ZQÞþ èsdÓu��XPáäIu˜8Bñ-5°àÉul·é_Ù¼K�ì¾Ym¾—lô8ô‚•¾+oÖÅe³e¥ àgâ‹6M7YyÏ…&D—Yƒ�äÓÛ—VÛ~•ý™Áô¸ÞÜ;¤’—Ríž‹�R·Çmú謶l¤äþ+¿gìɸE|‘ëµ}_Ïóeãü_Iå'‡©¹FmøY87í»³�Y-—u6Óe²ÅÄ®FãSO»+e4Ù¼sY…[î–Ÿå\gœqÍ'0á|‡IËxŸ%Öbn¸ï&ã{L-Þƒ{¨Ï†ÜŒ¦£o­Ÿ'c¯Ë‚ú_ÐÉ|wÛ_ZV©Eq¾KÇy–ƒMÊøŽûOÊ8¿"Öânxÿ#ãÛ,=Î�w©ØCfNÏS¶×M‘ƒ±×æcÉmñM —Ç%•¥m­h”'äœw˜è4ܯ‰otü£Œr-n&ãAȸþËq£Ýêsá³#g©ÚëæÈÁØ`fA}·Å4WßöÖ•¥kDÇ8÷!ÐrÝ�•ñM染ñ~O§Ör7É8öÏ u ä: Ö->óG¸ÖÏ“®ÛiöÚì˜ò1²qä’á’Ûìºën¥k÷ŸmöŸ`Qg3ÕÛ¤æ<³Kl´šÝG%ÞêíšØ©²Û�´ÊĤ¶ÁKä¤4’‘zÒÏuÞß_OZþUWœÓSnƒ˜òÍ’Û=š^K½ÔÛ=°ÓÙ­×m2°í–ÜzI- ¶JCëK)uÞß_OZúzªÌæš«t<Ç–hì–ÙìÓr]îªÙ­†˜öÍn»i•‡l¶ãÒùi²RZYK®öúúz×ÓÕK7š<«üÅòË­0&ÆÈÀë¿%ûß‚ádaë¢ÓâO‰Ä»K•h1¦ÅÔC>L:¼ia×Ûu˜ÖI%°[ZYK®¥¾µóO4óMhZmñ鏯߸á^Ó"›gñSÇøg’ ®º;6=WŰ6˜Þ·V·{ðöXÒÃ}+ZÖ—ÙZzÕe×�ûXw^x²†x²>¿Œ}dòCu×Ynv/XñŒMŒ·Vë½øÙðIÔ­kZ]eiëU•ž maÝøKâÊâÈúþ2ôe“É nºËs±zÏŒâl`õ¾µ»ß‹Ÿ‘]ëZÖ—Y_¶«ž¼æ›NÈðGÂŽÄÞG[®{âGŽÓo-ÙcG´å=9Ãw›ñîËŸ'.è,Ëξ–VY$’¶Òžë®¯­k¸M®mcm€W›óžÖœKÏ{ ”èxO âÚùv¼�•r}¦—C¥×C[m¿/c³Ï– \h¾¥öÙoºêVù.¶Ûi[®¥+æù‡2â}{Æ7œÛ�rMâkm§ äÜ“g‡¦ÑéµÐz}\ÍŽÏ>X11`¶·RÚVû©ëuÔ¶žµ­)_7Ì9�ëî1»æ¼ë’i8‡ãXí7ü›’lñ4úM>¾JI—±ÙgK.,4ºê[JÝu=×]KiëuiJü^KÉxï ã»þaÌ7ú^)ĸ¦—iÉ9G(ä»\ã|wGƒ>Ïw¿ßîö“âë4ú]>·\Œ¬¬‰cƒî’K­²ÚÖ�èù$üFŸ™ßÈ:wÀ[³øgº¹ú�Ï‘›�t˜|Û‘Áõ®Æ¾^«ãÛ(­—ƒêrñ¬ºè¶»(N]döÝ6¯"I|M~Aþy+¿yÕ ×7‰ñ‹¾õ¬Ûy¹×]‹Ë÷Ñ}k ’î³ã»H)ÕåbÇZÙ´ÙCúbë'õ‡[<6Í|O~A>}ù+¿yÕW7‰ñŠýëY¶ïýƾì^]¾‹ë]—u§ÙÁKø†¯+ÊÖÍžÆÓY=+6¶xm–øDü­þ*ûñ2yFü_ý åÆŸY�¹ó•ñülÜ[¯†ù²·Z¾—êþe¤Ÿ.ßn6%Ü�’bßöýú<=EÖ×tŠæóy»äû�¯#ä›�¯!ä;݆^Ûw½Þl2öÛ�Æ×>{òs¶{]¦|ÙÛ †nL—I,Ó_|’_unºµ­kTf7[½Ï%Üm9#Ûì÷ûýÞ~^×u¼Ýgåmw}¦|÷äçl¶›<égÍÏÏÌÉ’é%š[ï’Kî­×Vµ­j�ëw¹ä›}Ÿ ä[mžÿ}»ÏÊÚnw{¬ü­¦ßo³ÎšüœÝŽÏe�,ù¹ùù™]$³K}òI}ÕºêÖµ­P‰çï›öw-ßsîÉæ\«°¹ß*Ï“kÉù¯8ä;~YËyÒ[l²]–û‘ï³3÷Œù,ŽÚ]6DÒIZ[JV¿e-ó1å@gøðù…òwÀ-Ž»Œal²;ÇÏ«ôö]Í79”ÖéqçØK±ÌÎë ýñlrºëo6N^D’G9œ¹rd—'iþ”Ñu#À�–?#¼Ï×ñ¬m†Glt5$ºÍ‡Lòí¾U0tÐÏ™~vNo[o¤‹?+€ìäÉži/Š(§ÕdÉ‘,™ROX狨žü¯yàö~¿�ãl2;[¢)%lØtß.ÛåSOÙ’fåfu¾öHóò¸ÎLŒ‰ä¾(bŸU“&D’daÉ=cž)üQþ ß+>:³xÏWvfßÈÿq2,ÃÉêNU¹ºîcÖú©0qõÑäô�9ÙY›—Ç0´´ÃÇš>3™YøæE‘äE�¯+:]œsÒðÏÎ<ïëZöG@óÒŸ£k…�Í86ö(5=…×›\ø/ŸWÌ8å™Yu¦M!–ÜlìYrµy×cÍL\™« ¾Éºø“æAy¯×tì>ŒåŸ¤íÀû”»†ncƒYÏ8Ó: æƒW˸ý™9tºo£-¸ù˜òåksn‚_ºäÍH¤­³kñ+Ì΄óS¯)Ø]ÊÿIÛƒ÷89o Üdz�ð-¦l7ͯ–ñû2r«‹tßF[qó1åÉÖæÖ ~í“5"“ÛcÇ…žqxÝçÿIh;ãÆŽ{‰Ëx¾Îx9Íû¶»ŸõŸ'º*ÝŸÁ»3‰Û•—•ÅùV®[/·Û[æÁØAK35Ù9ºùñ²æÛöÕ¶¥¶à æ>~?j¿’_½�#ú†ëD¾ri·�Ÿ½Ý9ú‘ë´þq?i—�?½½;ú”ëÕaŠ�ö·s�ï%ÒØî[�ÎJ9.Ž0Èß‚ÏÙQâwïgl~¾{M`ÿÂÇìÉñ÷»´?]Ý–°Sá{öføÁûÛÙß®¾É[£ðeû$|þòX_Ù u­Ô—Q_`/#¼¡è_ºë;´ü‚ìž?×9Åõ0Û‘½æ|Ëe­ÓeI�¤Ôbçn67Au˜¸ÓIOj _ˆÈ ¥Üõ׉‘nümê «‘ƒ?1‹3�éÌðé‘“K2¯ä:Ù%Åêì<¬jÁwÝ4“M²†XïõÛI ·AlC<êùîï.ô¿mÀ7<Û�‡«í©a¶l¾M£ËŽË,·�{/Ùbä~~ƹpÍ&f�>2>s·|g3GÑ^pr<�ï’,]O òafFo#ãSC¸øš¾Ø’ &ËäÚ<«,²Ë7þËöX³þ~¹PÍ&^âþ¿”™9}âÉo%Dz³]…úûÌ­ÆM˜¶}i&®?Õù1“5öbA¤‘ëëÎí¤QÇm°dò×>AI•j6úžA©Õï´;MvïG»×amô»­Fn6ËS·Ôì±¢Ì×m5{9fÃÏ×gáÍd°O÷Ç,wÛu·VÚÒ©më6zÝÞ·_¹Ól0vú}¾&ÏS¶ÖeãçëvzÜüxòðvüìI%ÅÍÁÍÅ–É"–;îŽHî¥ÖÖ´­*–ž³g­Ýëuû�6Ão§Ûàâlõ[]f^>~·g­ÏÇ�+a¯ÎÅ’\\ÜÜYm’)cºèäŽê]mkJÒ©ÈÙ}’Ùd±_d‘Ée·Ç%—Rû/²úRë/²ûk[n²ëkëJÓì­EûŸ¹þ€õåÇñQòsù=wGêß’°“Å·ÈOïÛØý„<šþ-ÞAÿy×þÀ÷ï—¼ÿYw½{éI•F+V,£Œc×À¯ìžñOþÎò’î%�ߟ²ÿÆ?û4ʶ|$þÌ?ìÏÿ(>×ZÉøl¿b·†ñŠÿ•‡zºüêÓªÎæ0��¾Fô·ŠÝa¹îúçºn½àz[ìÅ»eµ’I36û|ˆ2rp¸ïÓâY>Û’ò]Œ8s_3dÉ2Ií¤qI}˜{½{÷¨|ië�Çk÷o8Óð.¦ºØ$Ùí$’ü�–Îhr'ÄÑqýV,sí9!Ï‹[ ÁÆl™,Šûég²;î·÷§~u�]s¸ínìæúŽÂ4×Y›-¤’_“²ÙÍDøz-«Éöœƒ�gÅ‹-ÐaaÃ6L–E}ô³Ù÷[ˆ{ã¿:kƨæ=áßÝ‹Æú³ªøª}¿&åü£.è11âŠ;®Ç×k0q¢ÉÛò>I¸ž–ãk5Ü|½®×:HñpñçÉ–8¯�OÉÇÍgzùÛŸÈ:˯eÛôÇŠõØ]ÁÌ�˜öF¿ëìÂÙööó[4–æE™Z×+ø;‡5Ú\9k²Ý±Èć>°†ùù‡î¯5s7�sÀoÛô÷�5Κ,^…™d»°õøõ’<þÖÜëf¾Ìˆ2캹ãØs_¨Å’û-šýŒØ°fÒ"ÿ0=Ñæžfó®xûn ñª¹ÒE‹Â0³,ƒ–ö5×ҰíMκkìȇ*Ú×#ø?‰5úŒY.²Ù¯ØM�b¸/—ŸÄkßÞ{þŸé©Êülñ'3ǹ«m/mwΛq\�nM�©¼ÒM$o…l¸íõÆ—ˆj²§ÂÈ·/2ͦvÞ1aÂâ[Ž.;#TÚÏ<Õò7Â>ÆÅì{eÆ2$ËÀ—”ðü¹26=}Øzü ä[f—žqò"×ïpþ훑3Óéluÿx¾\œ\�lÖìï‹>aùá·>ƒŸôG;Ïã“K•�'&âY—ͱà|÷ùýšŽoÅnž,-Î%qó2"‹"ÚðÁ¦D’adãOZKM›ñoÌÿðçžÁϺ+�gñÉ¥ÉÁ“’ñ<»æØðNy�ƒtÔ³QÍxµóяŮ>\ñE=µ‡aƒIï“'zÒZoï€ß&~_|nvÜ߯žÉÉÖhvù8²s΢å6äò.Ÿìœ\|­|÷CË8\™xðC·º u˜Ño5’ë¹$“A‹Ÿ YÙ$ï¾3þe: Ïý~Ÿ¯÷Waôÿ”ër¥Ûu.ß>·ê¹•Ú˜+“²Ýõ/ ʶy&ÚøîÌ›Q/³u®Š<šÖ<¬LK¶2Í›ã·å³£<èÀÕð}¥Ø�Oä|x9wìú«oŸõ5ü²º¼{²³·=[¾È² 9&uñß—6²JY¸×ÇEn‹#¹òÍoãÃ壣¼åÀÕð�­Ø�Sätx9Rlú³mŸõ5ü®º¸.ÉÏÜõv÷&ØmäxUÀŽì¹µ’R;qdVè²1q«Ÿ-“ß7¾(ü¡è?@q옺OÉ]U²W‘øßÎy&»7‘lqa×ͳŸ’uG"¦.—µø–6..E2äÅÃÄÛjîžý†»\,œÎÇ:Êë³`Á´Úk4zÍŽïw±ÀÓé´ø›M¾ßi™�¯Öjõšüy2óö;ü¹!ÅÁÀÁņùfš[íŽ(í­×V”¥jü{Ž¿Q¯ÎÛm³°õz­^VÇg³ØåA…¯×kð ¿'3;;3&ø±±0ñ1¢ºIe’ëlŽËkuÕ¥)Z¿Çc¯ÓëóöÛlü=^«W‡•±Ùìö9PakõÚü(/ÉÍÏÏÍɾ,lLÚÖµ¯ÙJRˆÅü†þ#^²ê›÷ýUá¿QÜ���÷ýN˺÷qä]Ô|W>¾ç,¼7]eqs»Ga�u“V,ÊI‹¡¥ô‚xeÚãß|HäyåóÿÖÝ_vï­<6ÁÕvÿ<ƒïšÌþßÜY‘wTqœØr+‰5üS;±s»/:¥-bʲL]­`žöpÝ|(çyáóõÖýavï­<8ÁÕvï;ƒïšÌþÞÜY‘wUqœØ§®$·ñL(îÆÎì¬è>œµ‹*É1tt»èÏÛ(n¾$S~N?§�¾0˽êµÜ{ËïÁºÌLÞÄ›7*¾3pŒÏ©¶‡.*òÃ{Ü›Œ 0qn®7ŸE6.Æ’G¿®N4ø5‡‡{yÝÞNsÜþÎï¾Ìå]¡Í³é$_¥ù6³ì“&|ÊiøæŸÌm'ãðdäÉ|:ín6& _wÓŠßZ¢sݽýÜþGó|ÎÅï.Çäý•Ì2é|VìùwÔƒY‡|òäþŠãÚ|Xñ´œcID×ß»+¯º¶Emk_X¡wg~w/‘¼Û3±;ñy7drüÊ_»>E�õ`Öâ_<™?¢øþŸÌm'ÒG<×ß»+¯º¶Gmk_Xùaæ¿•>qö ;7Ê®ëæ]ÁÉñ¬¾$ÌœMwâ³ájp2ð¸ãØšŽ Àð¶qhñdÎ�O®Â·a—r²¾¶UòM~b!jШŸŸ+~C|wòŠ`ñŒ»û¡÷»XsùÇFò=”Ñi2fºëcËäitY²ð.e.-=—åcE&&u,Š™Ø¹_C°t«ãóä÷¾< ä¶àèr¥ì�ÝlcËæ=1¿ØÍ¢In¾”Éßð��ñfIÂ9mðV¶É>tüŽøÈåº È3wÝ×á¾^ft«¡6›knÌáîöm3¹ŸFmöu–ÎÉp·9“çäéë}šA\¬»2£ÆÎɃq�`ω^^to›=¥îž‡å6ï¸î}lÁÞé3â³–ðNOf.>Nˇs]%%žíO Õýâßul’l<¸«fF$ù²Ã<“³ñoÊž˜ó©´ÝÃÒ<–›Î=°­¸[�Flv`òžÉbƃ#eÄ9–š“Ov««ûžïeóbåEu™“äbË òN£Åß)úģÓ÷IòZnøþ}mÂÜê3c³ ”p®I4#Ìtô–{µ[ígÞ-÷{/›*+¬ÈÄ›#Xg’ÌüÄñûξ‹ãCøÙÎ1¹·_r92u¹‘ßuÜ›…ò½lx×ïx'<ã³_voæ/¾CtØÒû£ÈÅÈÇÍÄ—'/*}›l[b›<ª{ÍœSÌ_,x¼×ãÉ7ò_½ôɉmÖb_&Ÿ´¹Vºûñm¾Èî·ë±ë[)[m­-ôû)ù’y‰§¿Žùså7–èo“Eäowéä¿ÚÛ�}úÎÌäøWÝ·[eÖÃuÐzÛJÒ•¥¾Ÿe˜ù�¦¿Žykå–è/“Cä_v餿ÚÙ�}ú¾ËäØ7Ý�mÖÙu°]t¶R´¥io§ÙE6¿'ÿ´³ä;ùsykúýçíhk“]Z2²ûᛑÍÊ~0ü@ÙÏ•f]ø½w´ã–ËdÇ¥�ðîsË8Ž6-c¶8©uø8ú;aºÿJÖ[£­õ­Õ»Ý[¾%9œ£ã“Å ”™VeÝ�×yOä‘ü÷[ÀzïŒÒÌ['È­2wœ§‘eÁ•>£†p­ WS?”s å¸R×Û¯¤PË‘-bÆ‚yâ¯_ä{åG¿þE9„qò©«×ÝÆö“çp‘ãû,Œ�&¾jýX1ù3Ù},;ù¿6¦ •Š™³Ã6—Ëf65'Ȭð?ùù4ï>ye�ò¤à]+Çöep~›Ñìg›Q‹7­ñÁȹ–}#į2æ?u¿éÛ•4Qã`Çt–aããýl›§‚OÈɇxyíÊ퇑M'é~?°›+„tæ�c<ÚŒI}dŽCÌs©f-y�1û­ÿNÜ©¢� ˤ· ­‘tõ“|¾|ãy ò}Ív¼?K‘½é�t¹¸ñpž…×më÷ž_]fd9¸¼ï»ö:ÛãÆæ\¿7e‹V&²•¿IÇ,ŠpìÈÌ�/o±æ›Žp8pÎ^:ù!Üþ)vž‡¹:#œm8/9ÐÝX©™ƒ}&×nõ3MÙÜo“éç¥úîCÆö•ƳïyVI×Yd–ÒÙcŽû3W�þBö÷‹ý�£íÞ“æ;Í4•ºäbÝõµ»½DòÁ.äÚ™}pwüwgv4uŸ"Ûã­ñÙ-žÉ¢ŠK3G@ùÛ¾1vf“¶úS˜ìxo3ÒV°ýçêK®Ýjf– s¸ï$ÔÍîÁßqÝ•Øñý|L‹/Ž·Çd–ûeŽ9,Û? üÛò'À÷ã^Bx×Í$âüÇIu¸;ÝÆ9ö\²x„ù8ùž½ìž5^œ“‡o­Æ³êGl¸ù¸YE›¯ÉÂØãbfAa/ůʷV|ŒõäØ3C­ëï$86«#´:›ï—]�›‰JãáÉØ}o~d²gn8Ãc=‘Ë ÷Ë�¡Ëš’ðN¯Wu¬zníò‚ìLˆé±sï“„õ†T–V<<ÞÙÜë'Ž{sé%k5œwk6³Ãú¦]lSâäËÆï‘ߘ~›ðž ¯Zð 5]Ãä�p¥¶Î‹›|œC®rg³Û…›Ú[}tÖM]µ»ë٠ÖݦD6[õä×Å‘�•'~F¾`ús¨v�m×öj»ƒÉâKe¼76ù8‡]dÍg·7´6ú鬚<ËkwÖ³C‡-»Iⲟ^M|SãäÉÂß–Ï�Ï~1ô»Î»ÕË�Þ>`K¯ÕI è]Îü|ÿ› )î®S‹^? ÓE¥’<ì}Dt—�íìÊÂúXøúü»¶Ø°DòKÊ.÷òç²¶³äaî{˜æEn$¹õ‡MÇu_uøú)Çuñbé8Î�ûøpEd“_|ÒýIä–[á;ä?’ÝÛåWbgv‡{sÍ·9åYqÓ¹u‹KÇuV][ Ñq^?…f>ŸŽi ºµ¿èbÃ%šëç–²O$²ß Ÿ!|”î¿*;;´;Óžm¹Ï*ËŽ˜¸uˬXºn=ª²êÝ�‹h0¬ÇÔqÝ4V·} Xc¤³]|ÒÖIä’[ëlósÏÏ)¾Bûg/·<Ÿì½�0ØC™¹¿…pœϪë>ªÓngľn1Ö\.ÜœŒ9©·[‡ÙÝ‘¶ÚS öy™Ùt»"üÁLÓ@„¾þf7^nµ>:y¶Úr¹Òèô{É-ËÛn¼~Ým²î—'s¦ÆŠÜŒí—Yì³².›q§†ÛåÄ–ûöû+=rñ6=çø�ùrÛx™¶Õxÿä×c¼ñ—y±¬Z-ì´ÉÙíº3m³ÉºI¶ZØc¶|͇\ì3'º]ž²+o—[ïͲ²×'7¼??-Ûo¶Ú®€ïý®Çwã>ïcX´[Ùi“²ÛtnÛe“t“lµ°ÇlÙ™ýuŸ™5Òìõ‘[|¸rß~n•–¹8Ù²ïü?߈+Æ,®#á?›»#7ÆÜÜŒ]Lw>ÿ*LŒ¯ò²$¶ ̳çºùgéIå¾–beßZÝÅ.­-ºµÔWþ¦OKI»Òòm.£’q½¾¯�qÞA«×îô;í&Ãm¥ÝévØ‘gêöú�¦¹;-^Ë"9±ò!’ø¦Šûo²êÛZV³pÓî5<‡S«ßè6šíæ‹y®ÂÜiwZ|Ümž§q©ÙãE›®Újö8R͇°×l0æ²X'Šûâ–+íºÛ«miTÙµ}O Ôê÷Ú¦»w¢Ýë°¶ú]Ö£7e©ÛêvXÑfë¶š½Ž³açë³ðæ²X'Šûâ–+íºÛ«miU‡8™x¹ø¸ÙØ98ù¸Y¸ðåáæbMF.^.DvÍ�“�‘ ×Å><ñ_K¬¾ÚÖÛ­­+JÖ•}GÑ}èk×—ÅGÉÏäõÝ«~JÁþMÿß!?¼l`[öòkø·yýä;_ûß¾^óýeÜ~õì?¥&U¬X²Ž0�_¿²{Å?û9ÿÊK¸–|"~ËÿÿìÑÿ(NØXð“û0üeÿ³?ü û]k'á²ýŠÞÆ+þVêëó«N«;˜Ô/5¼ÛèïúoaÜ=×¹–Ȥ–Mg á:k±gæ]‰É¾—Õ‹AŵÙ9öItVV’æeË}˜¸8ÿÑ%¾•­–ߪþ_y‡Ó>u&ö{‹q$Xõ’ýwâ:šãOËyï#¬U’×dO�dòÛgôLœ‰/� VI¯¶žÚ]«^ùƒÓ^u.ö;‡q$PVKõÜOˆêk�?-眊±}Ht=“ImŸÑ2r$¾Ìl8=d–û6—j—š^ixÿà�ü·É$9wðkƒq¯§®Ôj5±ãl9¯có\ülÌŽ=ÖÝkÇr35ÿÂ^sÉ~á5`‚³câbbcäì68zÌ<ÜÜjê|öùïO�^Þ›²;ki]wÒ_±ÀêÞ«ÔäÉwë>5›“l×`ë£úXߦ9ÊØ!»k¹ÈŽ™›bŽßèX�bbc@CÎ:{§ÎîÖ›°»Gc]wÓ_Ÿ…Ö}a«Ê’þ)×w/"ÙnÂÀ¶±c~—ä;+aŠí¦ã":eìeŠË}!ăžnùÍÝuö¬ÝƒÚë¸Öžüü.µë-^L—ñNºãÙyËv•‹ô¿!ØÛ Wm7ÇL½„±Yo¤8°bbãUuòƒòµäÊ7seóŽÓÙÏ勎m3«ÒÞ?èöräð®°ÑKm1`›&zckîæ}�³Ã²—í¹dO•<—Ç‹ºÜm~6�4¹¦.`€>†£oµÐmu›ÝÏa¤Ýésðöº}Σ7'[µÔí5ùåàlµ›9aËÀÏÁʆÉaš+ì’)-¥ÖÖ•¥*ýú­®ÓC´Öï4{,ý6ëMŸ‡µÔn5Y™í¦«i¯È�/e­ØaÉ^~\6K Ñ_l‘Im.¶´­)WïÕmvš-¦»w¤Ùgé·Z|üM¦£oªÌÈ×í5{=~Dyxv~$�åàçáeEd°ÍöÉ–ÒëkJÒ•}þ)ÊùOäü{›p~K¿á¼Ïˆîµœ“Šrî)¹ØñÞOÆ9“2Ž›Ç·úŒœ=®—u©ØcÇ>.V4±O�4vßeÖÝm+I’üIüúàs+8×�¾xrl=O1ú°é¸’{œ�~«�òë�ø§çæçˆp 61`jz«½éf¿ˆpòÜYп‰slìÂã=wÛ{y!¤ºËñ¾é ä™S]�‰�¯Ø~�ÂÛK '´�îjŸ—žix÷àïXdvŸ�<Ò.?¯šÜè8¯ÖYÏžvç '¿�ðn5vN$›m…k<6Ë<²ãk°~¼rfdãCwÔ¦³yUåçDøiÖ¹]�Þœ¾=ºëráã|k]d;mÏ7°Ù/ð…qÛ²qdÛloºh­’Y$ÇÀ¤¶É™‘�¬´Ö*<ºè¯ ºß'³;Ï—G£×_n\o�k¬‡cÍyÞãeýÂøõÙÒmv7ÖXí’Y$ÇÀä¶I—‘� k%5sÌ?2üxðC£ù�Ksì Á4’E­ÖcR–çr¾uÊóaÈ›OÁ:ûŒÅ%›WÌ76bK$xÐR‘ãbA‘›™.6.^T#ùù†òCäg�Å/ÉÈéÿbšÑ]'Å·Á¾û¦U™¸ÛŽÐäQÁ¯Èç[{ráŠXq¯‡QƒX!º JdÛ.\ð�ùùcòÎ�ŽwûÆGSøÿð~Œéî7µžk7¿sÈ·/kÙ{øâÁŸšlé™rÅ‹X ÔáVn‡ï1ß—4#>@>Wü€ó›c�žñ?Tô SCú3¨8ÞÖilÞ}Ó"ܬ}¯dïã‹~g³¦\qË5ÑAªÂ¬0Ý7Þ,“*jÞ¾Z¿W“Ÿ#Y¼«¨úâm§�~K·–=oXh³þïÏ»GK‹�\,<Þ÷åÚÌ›¿KÁ±¾Ió+ŵ×ÇÇpîš²Lekñ¶·r=Êç,‘÷mç…k÷W‚=Õ§î^šÜ{o·èëy· ÙM‘^'ØüN¹Í™Æy67SÝeÞ•¿.Ê}ç_“íš©Z]mÛ]áϘÝÁá/pjûk©vž´¯Ñ×óN°šà¿`ñ¯Ir¸÷!ÅŠ¿eiö߇™e>ñ�‘é,Uþ~Ëö¯ÃÏ0ûÂŽßÕöÏSm?ùο™ðÍ„Óÿ;Œ}zK•ǹ,Uÿ£~&]”ûÆG¤±Wùû/èÿÆ/É×�>@cwOäÿ x7%ý¥ïŽÝm2p8Wr𬠜‰`ÀÏž,}‡ðkœñ¯ÒSñ®K.F^“/"k/‡3Y™´Õì,ð“Ín›óã´Ý×ÓÛ cŠùmÓsn³’/á7]ó(1qòv<[^–KtvdY.&dTû¾v-öK¥ku–X%áç—ÝIæ¿Léû‹©¶SY)©åÜOc|Tä| –Á�FÇŒoᎾ˥‚Ì‹$ÇÉ�Ö Ìi,š:ú][m°Ãï/z—Í^šÓ÷Sì$¤ÉMO.â{â§"à|¶ h26g} ™t�Ù‘d˜ù1ÿ@ÌÆ¾Éc¯çVÛmdð·Í/üüñÿˆù!ã-þðnKõ5»}FÆfðkœñ¯ÒÖx)6F&^&F6Ã_“™¬ÌÂÍÉÛÖÔ6�µ  µ¾G1íÆùór;c¬T»ÊþÿÈ­µ¥Ô­nËí.Q•|Ÿ�öúMtÕ¾Ÿ¹éwÙöz+SùƦ/�^cÅH®‡Ýä÷yäû/¥ô­k™Ù\“.²ÓßùÞÙë?¾ßÜ­·SÓìôVÅò�L_:<ÅŠ‘]»É¾ðÉö]KéZ×3²9]e§¿íöÏYýôýÊÒêz}žŠ„~jø® ù^ó×Q®Ô˦ÇÌò+›r©0æ¦]/›;�Ë�Íö›jS6ûæú[íŸ!›:ÊÛ_£[2iô©H½”¦˜5 ¨Ž^€,‚øÈ»+â‹Å/¶Ûkn?tcÒ–úú{q<ˆíÌK.¯­k_uÖCJ×÷=j°Wá.{ò>0üa’ú[JÛoÁJ[JÒžÌ^üíw/çü¿;.n?×}wÇæË»’ó~Kv¿wÇú°ãããÃ>nløšìLÌÌzàüòó˺¾@;«?¶;c>ºí&º¹šÎ³ë=fdóqN³â“Ol¶ju6Kl?¤7;£›]¬‘Y“²É²ÚÖØ±âÆÆÇ¯¿Íß7{ƒÎ®àÌìþÏÌý¥×}ó[Öýo­Ìž~/×^yì’š½]$²Ò;­�ЊM®ÖH¬ÈÙdYoæÃ�..=žmyµÛþsöþggvvgèý.¿ïzÞ¹ë�n\óñ޹ãÏd”Ök)%�~�Üì>„rm6’GfFÇ"Ë6,x±±±ê ùBùBïß”ŽýÈínÖÈ¿‹u׿i¨èîŽÔm23¸‡Rq ìˆ/š(¦¾ +y/<ä¶ácÍÈy Øðämr!Š8âÄ×bkõøZJÓ†�9¦É=AÛý•Ð}•Ä;ƒ§ù~Û‚v?ÛEºãŸK-–eàåÙd�dcäcÏø;MNÓyqs°r¢› ? iqò"– dŽì…Õ=­Ø�ØœSµú£•íxO`ð�¬{Ž7É4òYnVU¶IðOñÍ…²Õl°¦—7 *)°ó°æ—")!’HîÈ=UÚ½‡Ò‡Å;[ªy^Ó…v ÚG·ãœ�Q%–åae[d�OðOØ{-^Ëi1³p²c›;i1ò#’/²ìåãw’=ÏâOsðŽþèo´àŸÀ6–ìt{ÍuÔ¾ ˆ/¥aÙh·ºÙ©~ûŒï°o¿a¯Ê²LlÌi.ŽKkJ¬^ø¬ù:à_#=7vvL:¾ä/^â`âw/ZbK50l›"ëñðyï®lÓæfðNM$5­"’I²ôÙžü,›æ¶˜¹¹Ó÷øÍùá>uvìâ]íÁ1°°ûw®ðåŸîPäOt�`ó^÷ÙfÌÊá\–è.ºÈ¤’l�NW¿"I©n>fdû>4>E¸OŸ}G]„ðë8Ÿzð\l,NÜë¼9gûœSÏt�`ó^÷É&ËËá|’è+u±I$Ù:œ¯~D“RÜ|ÌËZþ#>QºÏå3Æ\>ÑÑÙ¤â�ÕÁo×ñ¯!z‡]°ÈËŸ€rìËv¡÷ZèöG±Ÿ�v¦|í&UÕžË~–Vùeë²ëN¨º^é[ª` æ>~?j¿’_½�#ú†ëD¾ri·�Ÿ½Ý9ú‘ë´þq?i—�?½½;ú”ëÕaŠ�ö·s�ï%ÒØî[�ÎJ9.Ž0Èß‚ÏÙQâwïgl~¾{M`ÿÂÇìÉñ÷»´?]Ý–°Sá{öføÁûÛÙß®¾É[£ðeû$|þòX_Ù u­Ô—Q_DkåÇçÒÍMywŒ¾ò{%ÚÛ~_ì?&´Y•º-]ñI‘…ºãÝ'—”³#aî³é]Ëb’è¡·ßv§ß%qöpÅ—åGç =7ð£Ç/ ¹%“nm¿+CÏüŠÒäûáÓÝ™{~=Ô6Gôòv—]gÒ¿”E}ÐãÙï»Yïšè68ñlùMù½³O^Qã§…<ŽÉvö_•¡çÞDir½ðênŠüŒM¿ê›,úy*ÝoÒ¿“Ç}ÑAo¾ºÏ|·A°‚ÿ:_ˆ÷ 3yˆü§OÈ{¿}ž‹ºü‡ÄƒsÆúg3iõù�{ÖeÃ6§•öÄyÝúWo}¹:~7´Ä‚Üͼ¹5ÐCÏ++';''77&|ÌÌÉæÊËËÊšLŒœ¬œ‰.–|œ™åºùgžyo­×ßuku×Vµ­kZ¢s——•Ÿ•“��“‘™›™‘6^ff\ÒdeeådItÙ99]|³äO-õºûî­nºêÖµ­kTPr²²³²²s³²r33s2&ÊÌÌÊšLŒ¬¬¬‰.›#''"k¯–|‰å¾·_}Õ­×]ZÖµ­j€$䜋™ò-ÿ0æý×+弯uµäœ£”rM®v÷‘rNE½ÎŸi»ßï÷{Iò¶{�Öãg•.FVVD²O‘<—I%×_uk_àüïàø  P||±ÏÖœ‹�x-äFû'#®ùŽÚ-w�Ü÷o°’juï.ÙIHàêíÌ™³]H¸?.̺–é䊴ý¸—è]˜¹ßW]&oƒo”,žÈ4wæòlŽÊöQkºœm³ï’î Êsï¶,n°ÛI™-ÖÓ‡rœªÒÝ5ñÝmÚݬŸv­’ãfÛ~¾Kߟ'Ù¡ð·¾·“dpNU²‹_ÑßmŸ$•àü£>ûbÇë-¬™’Ým¼;”e]Ji×[µ“î÷Y&>m·àNðË|×o)ºáß>XsœlÝN7ðï²¹~ÎHöº­†%>ž»ÆÍÞÿa5øû6v)·&H§Âšxî5ùdh°0¦°˜_IÖ5ëË�â£äçòzî�Õ¿%`ÿ&ÿ‹o�ŸÞ?¶?°-ûy5ü[¼ƒþò¯ý�ïß/yþ²î?zöÒ“*ŒV¬YGǯ�_Ù=âŸýœÿå%ÜK¾?eÿŒöhÿ”'l,øIý˜~2ÿÙŸþP}®µ“ðÙ~Åo ?ãÿ+õo·•Qõ‡%Ë»ë»wõÓpî+‘c`a[�“É9‡#̶Zhø_ ÕddâSqÊ7ÓÅu°EY"†¬—'&X11ò2"Þ%ü’ê¯zs•÷qnîÔq./�m°áaÛG!å[ü«d¦›ˆq-dù8–í¹.÷":ÙU’(bŽÙ22%ƒç‹w¼•òCªüOéîWݽû®£‰ñŒ{lƒ Ø29*ße[%4ÜG‰ë'ÈÄ·mÉ7“Ç[ вG QÛ&FD°âÃ<ñõOÉß%:�ćìŸ#{Ï‘ÙÆ:Ï«¸ôûíîe´‚]–Ê{¤�MÆxîFN{^UÊ·Y8úíf'Õ�ï9Ù1G[ì¥Õº•¯ùÝç'oùùÞÛ~çíIãÖáCú>¼ëífTùs®8\92Ï… ÕÝ5±_Ÿ°žI+>Ëc$q˰̾ëý‘Clðסæ×š]¯ç7um;s³&�[�riúÿ€ër¦ÈãÝ{Ä#ȾlM&¶Il†ý†Ë&û¾¾Ëc$qËŸ—uo¥�ÁlÐW·æ¿™ý«çtí;o²å�[ƒriøÖäÍ‘ÇúûˆÇ‘|ØšMmòÙ Û ŽE÷}}ŽÂHã—?.êßì†`Ç‚¦ÿ”/“ÎøùHò#·û^[¸¿âÖlô]Ò:­®Vê�—ùcI$XQòyÊkƒ�?$äRcA“¸ÈÆ‚+#Å×akuø:^Ô¡¹°•·ÍÎOVÏżCó/˜I“ÕsWŽôÇwò\Úß7V_KbÂÕuÿbm²¯­Òu…Ô¶Èu{iï­Üj¾Ø2n®ž±Ë§“/ÃÿÌŽO[äq¯¼»å²du¤ÕÂÐõsò,Ï|Ýi}-�YÁ»k“wº^·¿ÒȵÛiî­ÜvïH2o®¦¶K¨’÷Ä'ÌnO\dq¿<¸å’dõ´ÕÂÑu'sr,Êß7[_KcÃÖð~ÀÚäÝî—®oô²-vÖ{«w»Ò›«©­’êgðø‰¶�m8§…?!}…~Ã[°¿Yƺ#Éžc�2ôùtŠ v«­û£�äÖ˳µY×YešÎQ�%ù8Ù7ýœÒcÉfVÔíºÛí¶û.¶û/¶—[uµ¥ÖÝmÔõ¶ën§­.¶êWÖ•§åL Û©u)uµ¥ÖÝJ]mÖÖ•¥Ô­=iZVŸeiZ%ómÖÝm.¶´ºÛ©K­ºÚÒ¶ÝmiëJÒ´û+JÑ:×ú×ý¨ù;ùXéÏŽ~ LX}�ä70×I“×=9‡²·øð«5qnæý…��nFGá“Y%¸ÿÐþùºËŠìl;idY¹˜<Ëùù7ê?x\¹‘âö{òÍ|™]Ô˜[q¦¦Ö»îeγ ³"n3Ãq§²û ­c®^ß*+ññ-öÅ™“‡Ìï‘�“>¤ð†A‹—/?ïnW¯“'€u.ÂÜymÃúµÆ¯0çY°[‘7áØÓÙ}°V¶W+o“øø–ûbËÉÃå7ÊgË¿�g�»ì¹oìëåú¼ÜÞ¤ñó�íc×r¾q\\‹p«¸Þîn×î1ºÿ�cgV±Ï¹ÌÆžé+ÑàâçdC~=+áòkÊNðòÿµ·=ÉßœßaÌùŽÖÚbaY-k�¡âºgŸ#ŠðíwWŽñ½t™2]4¥dšI'šér&ši ‡äw’ýÍågíûo¼9Žw-å{:}× ëv6‡‹hâši°x¿ÒY}Øz;®º{ëSÝ,·É<÷Ë“,ÓIÿ#<”î_+{;oÛ]ßÌs¹o*ÙSî¸Q_Zãh¸Æ’)¦› Œñ-$w×Aǵ÷O}l‚*{¤–ù'šùr%ši*òó‡Ï&þC{—eÝ~LóÙ¹.âù¶ñ¨·+UÖÝ[Çs²,šÎ!Ö¼J\ÜøôfbìqîÅÈ—½¹ŽÎ8¯ºË«vï"-ί"•Žûí¥¹šÌøe¶ž¾ê[})u)w­)\wÊ>»7Wòå¾6|cO/qòŒqÝu—Vì-Äx›}lô¬w_m-Ê×gE-)ëî¥/ôº”­+JW7ò‡®ÍÖ|ƒùk�ŸØóËÜ\‡cdw]eÕ» on.Û]=+×ÛíÊ׿Å-)ëî¥/¥+JWÖ”ª3ñC.?ÍšvKo²ë³ú*jSÝmÞ±døÇÒ¹Ýëmn§çÃ-µôü´õô¯¥}hç»BâÄOÃ×�™—ñwÓÐdäÍ<:Îmܘ:ø¥¾·Y‡‡'fr-•øØö×쎳öÍ[iö}In¯å­Sãø"ËÉÉøÖéègžIb×ò¾ÜÄÂŽû«u¸¸×öo&Ͼi_ç#»36ikJ§’êþêzÿùyY?}CDòÍ¿–vÞ&r][­ÅÅ¿²¹&}ðCJÿ9Ù¹³KZSý<—W÷V²~/Ø­á‡üb¿åaÞ®µ÷nõçBu7î>×ä¸ÿHñpq}±Å�‹¾³ì6Û\ÙbÄÁÆÛòssg‹/–K,¯Q;[´¸IuÇ2ížÎä8\W�p-_!ä»Üë®úx˜8”¥,‡évF~Ïc•$xØx�[~Nf\ÑÁ —Ë%–W¨=«Ú\¤úç˜öÇfò>-Àø�/�r]æuÕúx¸8´¥,‡évF~Ïa•$xØx�Û~Ff\ÑÁ —Ë%–ײ�™Ù\¦úó›öÇgòmw 뾸âÛ¾i͹VÚ驯Ðqž;¯Ÿi¸ÙäÙ�F\öâác_u±C³Í¥‘Y|—[mkYù$ùìï�îýÙvG)Ÿ?GÖ|bMŽ‹¥:¹õ3‚ðÙr­¿ïYXðË&&g8åTLJ'w±õ¾L‰¬‹Ëé…‡‡5åü…ùߨ~z÷–`ò)sô½qÆ®ÏÑôï\_‘þ áœN\«o»33 ¥ÂÈæœ£îðäns©[ïšHâDzÿºbbEzÿ!vöž]áŸØŠ\í/\ñ»³ô�?×7dzàpÞ)&M/»3/dÃÉæ|ŸèE‘¸Î¥o¾i#‹Ëþ鉉UCüÊü°sß”ï%2y~4<ƒ…øáÖ×çq¿º§i±¾ùðt_y’™�‹Íu¸y™:(û?Ÿûl›>ÜJͳ <]dy9–á×;/žÍ h{�à ŒñGÊ^ÛðÛ¼¸g~tÆïô_+âyt³;W—vDœwšq|© ®ÿƒó ~<ø×í8Ç"Æ‚‘Ï·Ç<ÛN4�eÁ�<[ã’ý§â?tqðê-ÍÚÞKÆ2©ÃY‘|÷h9—Éš ·¼+–`Ã,5Ùq½þ<²[)u“A5‘dãI\Oñ‹É^Ññ+¹ø�wõâío%ã9vÙŸ¬È¾{´<ÇŒäÍÛÎÊð`–ìxæÿ Y-”ºÙ –زqä‡*&�s|ó‹¸~<|Ÿëÿ&ºka“]—ÉýÍøev“ë4]«Ö[LÌy‡Yr»£Ç΂M7!ƒ)`–\lšë6¸¸{(#ûÖ=ÖÙ©â•]eæ��Ü È>ªÍ¶ý0Àº=Ɔ|˜r7<—ë«n?&á<Š8íŠø6ú‡­´ºèã³3ø3!¥ØÙ0ß}‹þ*ù1×>]ô_ï~±Ì÷è¹vÖì´¹G&ã‡òœ ©�Èøvþ+)eÐí´[n³Ý[-�/è² ÷ãÏ —ا⿒ÝuåÏFp~õë,¿~�–àVÝž–y£—oÃùFÔÆä\?–Çt;]ÂÛ¬÷VËlËǺ,¨}ØóÃ%öîx¡åRùŸãÏVù7ÑÛ\í¯ZvÏý9£ý/‡»}§ÍÃÎÌÒò>+Éuðäçbárn!ɵ™š½„xùX´ËÄ’¸ùV9¤Ù†Ä6°à æ>~?j¿’_½�#ú†ëD¾ri·�Ÿ½Ý9ú‘ë´þq?i—�?½½;ú”ëÕaŠ�ö·s�ï%ÒØî[�ÎJ9.Ž0Èß‚ÏÙQâwïgl~¾{M`ÿÂÇìÉñ÷»´?]Ý–°Sá{öføÁûÛÙß®¾É[£ðeû$|þòX_Ù u­Ô—Q_b.ýéž5äOJöFs Ÿ'ÒñŽÔ᛾»ÚðÝîgä˜;¼KñeÉÖm0¯·ó£­Ôú¸ÙOƒ�¿2 ŒY¦‚LYÞGÇ»ï¨;#¥ùfÇ‘êxßfñ × Ýlø–ë+AÈp°7xrbM‘­ÙbÝéï²—þ~<öO…™». ñ¥š1owõ'ïž¡ì~™å{E¨ã�™Ä7\;s³â{œ­!ÂÁÝbI‹.F»c‹u?>ÏçãÏdØY‘{±ò¡ŸYa¿÷'VñîðêNÎéŽ[›Èµ¼[¶8/ëŽG°â[¬Ž;Ê0t|ÓCŸÇv™|{�l’ê·ø[îÇ›Ù%¶KJVë/·ÖË«óoÃŽÓð_È.]ÐÝ¥‰,Óje®×…s°&ÁÒv?ÎÉÉ��sm²K“e1¶6c_V=³O]~Æ Œ9$ºH/­k‘ó'ÄŽË𫽹OHöV<™7ëd®Ó†òøpfÁÓöÍÈÈ�EÌ4±É.M±Ã›n=ñecÛ4õÀφ|[ä¾økukžóľËð·½9GIöN<™ëd®Ï‡rè°fÂÓöÌÈÈ�EËô¶I.M‘Ûf=ñecÛ4õÁφ|[ä¾ønºµü›üm÷gÆ’›¾‰ío§Éx®Î<ŽIÓ¿©ÀÈÁã=·×re]ïi2oãü³O}mÃä9&žMNÊÚÒ)ó5ó`lsu%« Zs´°}eâW”ÝÑ…]ŸQxßÞÝ›©¶Kb“sÁ:Ÿ�r�,2_JÖÛ27mf³·R•ôú’ÛêÏ}wâ·“}»‡fÏ«|yîÞÃÔß%‘ÓsÃz·›r-%—ÉJÖÊM¹Õé2uxôº”¯¥ošÚ¢Ï=wâÇ“=¹‰nÇ«|{î¾ÃÕÝ}‘×qÃz¿šr,wÉJÖÊO¸Õéru˜ôº”õ¥d–Ú7¿ªþ.þG;®N5^µðsÊ]ö¯˜CfOå™])ÏxßÙaËÓE�obò­.“�ãkæŽßÌÈ›c7×Ò–ÝZÖ”®îpïƒÏ”®o®‹m¬ñGªÂš–Ößá�>êN±¶·CÖÒ].çÚnA kl”¥}Ø´¥·úÛw¥ÖÝJn7øcù-å¸0ì°Jyv [,7:Ü9©JÛü)ç�UÄs¬÷EdÖÒ]7%çZ½äU­·Ò•÷cSÛ­·zV—R›÷Å ßÌ·#Ùãàìü\ÐðŒŒyg®û•w÷�³k1ëd_V,|ŒN#Ù¼¯‘Y‘“_̶”ÁºÛo¯ôJÙOZÓ=qïÃ�òaº·&í—êN#X-ƬVrÚÒdÝ›Yé5e·¼O“ÙeØJÚIõ«+õ-úu¿ÒÿnsÐþß�ý½¹l4KÅ« ¸õŽÍ÷gàd]—Y©-d·¼c]Èì¶ìO§JIõk+õ-öVÿÎöçÀ'ÈfÞÙîØqþ§âõ†ÜzÇf÷³õùeVjKY-‚¼g_Ȭ¶ì_§JIõk+ï·Ù[ÿ;Û·˜„äÏ*ÊÝ?oøK®»ÛßO3³»¶ûë[éZÝe+¯ñÓ:?tU§¥ß�éZ×óku=^“üÛ/‘ÏþyÐáCiþâÞƒüÞO?¿ó÷¡Â6ïýÄ>ÿù½~~çßD„mÏûŠ~Ïó?~K?ßÃÁŸð—ß¿ûLÏ)É?§É¦Ž¶ÓYÁú¯˜û±¥žµã}¹Æ±id±úû0îþÓŠÖ¹3ú~mmõ†ž¿�}¿o§˜ä?_"z_OѼ[«9o®4³×ø=Ú:|ol±úû0ëü+Æã?ê™ý?2´õ†ž¿�}¿o§™ßüüˆi½?Gqn®åž¸òÏ_С¦Æö˻ه_áN?ÿTOéùµ§¬Tõ§ºû~ßO™µü!&úüIòq;[ÂÍìÑcdÏ«´;’¼©`вE‡û¿4ø6äæ]Odu–há¥ßùrû-üæ¿ó¿„¿”N¼×ߵܸ™Ë6ø6V”§ð—u—cì/õ¿:û4< ›r.CZ[~M)Zןe·ÝO[,ºê`ŽeðáòOÁð$Ùí|^ä›L8ý¿ì7—õ¯<Ï¿Öèl¯ÓÒpÎi¼ß_í¾zR¿ê_ÉK®þvÛ®¦ æ_¿$œM–ׯI´Ã�Óíá¼»­ùæuþ·Ce~ž“†ó-æöïmóÒ•ÿRþKnºž¶ÛuÔÔYøi~e¸ÆËc‰ƒâ¦¯™ëp-Ǿ>CÄûçÇÉ5»*M‹�<´×k¹hqÞW}Ø“Mt[.¶*ÖX¯¬t¾/d—è§høáä/HVw7EwSÓ&žìY;#­y— ‡.ÚWÛïÄŸ‘éµÐåGî§§º;®·×÷ZSÙ=wçMÙ�¹Ò]·ÕðÏ_L|ŽÁëž_ñ²?éÒ¸Ù<‡O¯ƒ&˯û)[.º•¯ä«K;#ÇÎúéÊGmô§lõ„s剻®ùwÇɧ¿éÒìlŽA¨×Á“eoû)u—]J×òU;ãøÿóŸÇ½>û“ww‡¾KuãVaïyß1éNÃÓõîòÍf4½�?§ ȃ'"KlŠX³¤Š[®¥,ºêÕ†}ˆ†ÿ¶Ýu—[}—]möÝK­ºÚÖÛ­ºÚúÛu·SÒ´º•§ÙWý¶êÛZ]mkmÖÖ—[uµ­+mi_ZV•§ÛJÒ¯ûm×[u.¶µ¶ëkK­ºÚÖ—[u+ëJÒ´ûiZUúp³s5¹˜›v^NÃ& Üì)åÅÌÂÌÅ–Éñ²ñ2`¾9ñ²q§ŽÛã’Ë©}—Ò•¥iZ, ø.ù&»ÍN�¿¨»OyL¿$¼Ôjõ|�3gµ³+sÚ]uJÙ­ã}�Hòî·g�¸À’Øõ|Ž_\¯ú¡÷lÙ¦¶ý¬pE<…ß�˼ÄèÙ:¿²÷6dy ÑšÍf³‘ågì¾ñ¶ì®ùšþ;Ù4�.´ÏÌÚã_mšÞA%.É¥3éd²Ç]¤PG;_†�‹¼Áèëú¿²·6dùÑÚÍn³‘egìi>ײxæàqîǤyU·?/k‹}¶kwòR¹>™ÿC.Yl»gÚ}ðò¹O’¯.Ðö¦ç[–^<Ç¥âÍ�.F¾nÇrqoƒ‚÷n6-c®¯ëóXuÙ8»È0¥öbò ™©‰¯ÁÏÖAwuݧv‰Þ�½yqüT|œþO]Ñú·ä¬äßñmòûÇöÇö¿a&¿‹w�ÞCµÿ°=ûåï?Ö]Çï^ÃúReQŠÁÕ‹(ãXÑð_¹Óñψo9!Úët: ·ÈÎóy¹ÎÅÕéôÚ}_‘Ï�³ÚívyÒÁ…®Öë° ¾içšû"†+.¾û©m+Tÿ¾vÚ½Å_ŽÛÍæË_¦Òiµýé¶Ün6Ù˜úí^§W®ïÎÝÌØl¶[ É!ÄÁ×àâC|³M-öGvVë«KiZ§çð½µÕè~+üwÞo6Zý6—M¯ï=®ãqµÌÇ×jõZ½w}væfÃe²ØfI&¿åšio²8£²·]Z[JÕjÿḞo… ²rf‹/#§žyä²(`†/+{ÞIfšY+m‘Å–Ö뮺´¥)OZ¢òýòW¿ùïü˜8¶vV�½M°Úèúc�}<œJò ]-1vý©È±²l‡&í÷2ûµ·bA,q~‹Õ[?Ó·"¹³dÅWåwäCwç_yÏ‹Åóò°üwêìí–Ÿ¨ô?K# œ‚úßLm·goñrl‡*ý×,úýÎ㊺ÍU°ÁH¬È¿6\ˆ­ü¬|†îüéï)ñ¸ÆvV�=_�²Óõ&‹éäaÓud¦6׳wØÙ6C•~ë–} k‰ ÑÇ]n®Ø`¤Vd]™.D*¾þ_v$^DKÖ=AÉ·±øaÐÛܬ³Ô_uºíokóü 6m÷}íuP]tÙl±ó208¥¹÷ß‘�Çoºzcëó6ÛLJr1Ê·,õe ÑýÕÚ3Ç‹Ö]AÚ=‹“4°C?ëþY˧–l©£ÇÅŠ8¸þ§a%òäÏ5–Gm)Zß}ô¥=kZQ“¸GI÷7f_}qÔ��ØÍ|1Egà\«•ß,™ÙÇn‡UŸ[ïži-²ÊSÖ·]u)OZÖŒ™ÂºW¹;*ø£ë®¥ìÞ}$×Ã1ð®ÊyUòÉ‘-�AVèµY÷I|óKm–Ržµºë©JzÖ´m—ð#ΞÕã˜<ëü-ò˲8ŽÒ²5¼§�xçÜÃŽlqæ²’C>ï�p펳.cº—[trÝmÖ×Ö•ôg¸>9¾@r#¤±øEå•¶Öµ¥)?�}­�'Ù_JúÅ“Åb–”þeko¥qšáðΉ줖xoå ¶Öµ§¤ÝÙØ×úÓòúÅ‘Æb’”þe}=*Ípxç6Dt’?|Ÿ¶ÚÖ´ôŸ¢»3OZ~_X²xÔRҟ̯§¥[Æ~~U¹n¢Þ«Ào&q0ç’x¬‡“u¶ß…mí»[¡’³qþef‡}�×Ûëeòc[d¶ú]en¶´«ëæüf|†àE�4þy7%™vÖø©…Ó|ãe-”¥¶]é“»O•>Þ’Sóf¶;«_ZzzÛZSìçüqùí­‹l�¼ˆ’Ì»ktTÀê¾[µ–ÊRÛ/­2 Õës'»ÒJ}“[k_Z~ZV”û9ÿÞzk¢Æ›#Ä!¤³.ÚÝ0:³–m%²”¶Ë«L˜5šÜÉð®ô’ž”šØëZúÓòÒ´¦Qä¿ß0\OY�´ðs²2 ÛRêâÙÆ¹?VóLÈ©lQM_ÒzîÏw» -}“R”¦dPV·ÒëiùÖÝJy}¿Ç÷�ÚiówyY¯ÁÅ‚¹9YóøùÛÀÅ‚—VÚË•�o»ÛkO·ß}¾”­+_²´y}Ÿ‚žmi ›+gâ“Øx˜ð×##2^‡í¹cÃJÖ•“#2Î/v4ÛZ}¾û©éOJ×ì­_i࿚úX&ÊÚx‡äÞ¸XðÒµ¶²dfYÆ/Æ‚–ÖŸo¾úz}•¯å£r>²ƒU¹øüò÷7+#=„rñƒì~­¶ gÉÆ¶9÷<AÉ5ÙÔ“êÝ�$ödÙl¾èéd‘Ývµrn̸VtÚ¾eĹ7ÙãK|9îM¡Úès š9$Šø¦ÄÚâbdE-’Ã}µ¶ëiZ]miùiV½ò~ÌxVtšÎeĹ7ÙC}ÑK¯äú®‡:),¾HïŽLM®.$ö_d�ßmi[iZ]miùiV¾r^Ìx^lšÞcÄù/ØÃ%ÑK�Ét[MlRÙ|‘_˜»L\Yì’É!¾ÚÒ¶Ò´ºÚÓòÒ­eíÏ|™ñþÛ;&ÛfÖÏéf?–úýLy=šºVøoÀ‹{_†ïçS›ë¸7Ç7˜¼ÆÌ.}®Ç×q/û{‘åK\nÂÁ‡êÁ«èžs½ÍʾÌ.{®Çú8¼;&ZY�½ÆŽÝEÒY¶�[æc d%€š° ˆ+ãOŸu/oò¿9xœ“œtçoò \¾Ñ—?7oȶ�CÏö?tÕâ[²ÌØO�•‹Ö¼§"�ãé¤÷Û…§Ë­š›mdží\SC ço㿚õiòo4ø<¼‹™uGjï±'ìÛ³óv܇eÕ|ç>ÜM^ÙÙ¹óçebõ×%šÈqõ7VûpõwGªŽÜx/ÖA|2þu~<ù¯Xv—&óC„KÈy�Tö¦÷~Ë»?3mÈ6]YÎ3íÅÖa×;7>|ì¬^»ä³Y>¦êßn§*èõqÛ7ëa¾¿ÿÅñ!ÊúÿŸò/“ž¤Üs>o×}“ºãú¯#øÏ%Ûòc³ên_>>â\Ó�ovÙ;<Ü�ä–báiìÕO%˜Ü[k\LMuߣsñ5úˆÏ#¨ŽÚàwÀº·³{Woêþºç}‘¾È–8 Òp.#È9†ÞyåºÛ"†o×ìsd–Kï¶–Ûm•­kZRŸ•î8/XöOhm,Ñõ§^óŽÄÝI}‘G¨à¼O}Ëv—É-ÖÙvkô »¯’û©KiK=kZÒ”{~ Ö}‘ÙÛK4�k×Üß°÷RÉdQê87ßrͤ’Ëu¶Ç˜ †]òIuô¥¶ÒÏZÖ´ôgΜñOÊ/"pöÛ¼mïÎôÀÐäÙ‡½ÎéΞì>ÎÃÒæISlj¶ÉáÈôþx™Úzï_Oö}‰§ê¯ËL”õ¯hmø§æâ]Jÿ2êÙoóÒGK·7�üTü‡ö=iN=âwik«_Oöy‰¨êº}±äËOZö~߈RŸ›‰u>ßÉul·ùé#¥Ý"ᇣæC°xö'ÐøG˰5»(mŸ›öWEõŸ!ŽË®¾Ú[ŸÄ{#´x§+ÕMJÇ_Xò°¡’”ô­môº•®ÊqÿÃÙò‰¹—=�Nð®'dùvcÉ? îN²É‹énÏȧä|–k±"÷Ö—R+%Ÿó+é7ݱ{àkä�ut캷…q*K•n<—òÙà6âÃu±V¹ÓÓŠn¹5÷bÙY+JÒ*I?­—zGZ{kvÃh>>H7AnÇ«x_¤¹VcÉ~ÿ¶8M¸±]H«vtôâû®I}رÖJÒ´Ž’OëeÞ‘ÖžÚÝ»\'ð•|¦r­m3·»ÿúÓ*¹5‚ºnmÛ|Ç?elTÅÆÈ¦me랪çú»VYîƒÒ™u›êÃ}~ŸÓ¬rI’¿Í²ùÿç�þ6Ÿî-‘Íäóûÿ?zü#nÿÜC!ÿ›×ççþ}ôGøFÜÿ¸§ºÿ3÷ä³ýü<ÿ }ûÿ´ÌùÛoÃòM®×äfáêúG“Òúz�OjÙÃ/êOWýÞMîƒKª·èG}e¿êäÅëe•¥¾ëý¶]ø6Ÿ‡Ãä|¼L~”ÝäEô¾ž¯WÙsÅ�•õ&Ž+¾„›®5¨ÖÛôl¾²]õ2#õ²ÊÒßuþÛnü?Ãïò �ƒ>^.7Kî§‹éý=f³²¥‹;'ß4qÝô$ÜñÍN¶ß£eõ’ï©‘­–V–û®öÛ_å7áù/Š+¤³º|ɺßo¤0ög{[-þ·RÚûk‘ãf<4öÒ¾µõ¾Ÿe>ÏZúR¸k�| |©hcÌȳƬmö.rM|ܸ:K6I¢Š*IuØzÜŽÄÃÜfI_¶Ûb�鯺ž–Ù_Zzáý÷Á÷É–“ïREãÞ&ûÉ%»+CÛ=9“õ£Ž:Iuqp3yîß&úúÖÛc³²Ýu¾”¶¾´õÄ[ï„?’í2¤‹ÇÜMö6%’IvN‡µú{&³Gt’ë±p39ÞÛ&úúÖÛc³²Ýu=)m}ië®=�ø\~_8}0ø¯QõgvcÖzÃ]¯X÷Ÿ]ê°-Ž˜ÐOLªÇÜ»N¤Ú} ¥šè)JcV_«Õö}:Ù%úmØ_¾ruV>Ï;Ÿø�äGÔi©=ÛNC'Ró]‡Â�—]4ór�N£?ŽÓÛ,­Ô–™UŽë)[©uiJÕ©œ÷Àï4úÆ=ŽO7ñ[¾´ÚÝM³ß±ÞYÖ·kƱ"Ç¥Õšy¹>—W±ãôǶÛ+wÔ¦Ml­´­Ô­iöµ7žx%æ‡YEŸ•ÍüYï�.·Wl×ì7–õ�,Úq¼HñéuÒÍ7%Ók6 cÛe•»ê}çÙ[i[©ZÒž­íï…¯•®�Ùâêy·�žEmr3 Êɳ+©øLÝ÷¥‚?£õîØr.ŠÈì}ªïIé[-ÉɆùiKëe.¥—ûuX¥‚I!š9!š+îŽX¥²èäŽûkéu’Y})u—ÛZzV•§­*Õ b’$†hïŠh¯º9b–Ë£’9,º¶ß–_J]eö]JÒ´­)ZUªrÅ,I ÑÉ Ñ_trÅ-—G$rYZÛ|rY})u—Ùu=+JÒ•¥\ÖÞh÷\gs´ã¼“O´ãüƒGŸ•ªÝh·šü½NçO´ÀšülínÓYŸ>n¿?";£–l²Hï¶¶ÝJV•£ù¿Ãü>XC_$òxµÝ±xÃÚûè1<~ïîG�£gµžX°úϸöbjt[ï¼RÙ!Äãœê˜ØÚ�µe¶Èqæ· 6ù±àÆË¬Ýìø6ù —ÆÞâ‡Æ¾ÐÝÅ�Ñ}åÈ1áÒl67Én/]væÂÌ]^—oL›i}¸¼w›Ò }^ÏêÛôqò)‡—t¸ðC™t½åø<ù —Æîá‡ÆîÏÝÅ�Ñ�á¿Ç‡M°ÙI%˜½wÛ[ 1ušmÅ2)KìÅãÜÛècêö_RÚC�‘L<»¥Ç† ºË.oÂóòËŸÐ=Í�ñíÞ\§>nŽïîK^?æl)f/Xwÿ!ÌÇÆÇâPeM,; w î©î·Ìh«•7,û¤�ãAúWo›Yï&ø›šÄ j?5ºüÝgÊ/—XÙøòbÏ/4â›ã“Ûî» oÖk­È§¶·Séåë³b–ßÝößO_JýŠò>bð²µÿ%^SÁ™ øóIÊx†m–_éëv.ˬ8>Çj{kZ{2pr£’ßÝößO_JýŠôþap²µÿ$¾RÁ™øóIʸ–m‘ßéî»eÖ|'cƒ==+Z{2pr£’ßÝößO_µTßâMýµ>gÿÅ×þIýå«™Îi¸fŸ¯á­ýœÙ¿Ê³¿¨|9ïÃÙû??ìáÙ?Ò&~úy ÿ)nÞqƒñ ü�d÷·pOá�Too¯NôW ’ÞÒÌÄŠì{y×xj%Í×ìu2dÝ-Ófq®®¶Kðc�éãÙ6òìÙ/·"NNLò[dqÙmo¾úÒ”¥kW¼Ñu_gò‰0¡ã=qÏyÛ9nƒ[‹‡ò¼› ­¾ø®‡ Í~»"ì¹m’;­­±ÒêÒëkOËJ½æ‹ª»C”]…oë~{È®ÙIt:ët\?�íîÏ–É/Šø°©¯×dW*K%Šëkl~êÒëkOËJ½Þ�«;?“]…oëŽyÈ.ØÉt:ût|C�í®Ï–É/Šè°©�®È®T–ËÖÖÛ=Õ¥ÖÖŸ–•m¯øøóã›k0w\7Â/¹n›g÷ŸÑ»n1ãWsïµ›¹ädbf}Ç?UÂòñrþ땉,R};îúrG}·zVÚÒž£{ãŸ�œ_=‡&èŽåã¸dÙ‡n÷¬9¾£\¹"šhñcÉØhñá¿&øqä¾–RêÝ[lº´§¥µôôÛ¯¼‚㘱çrŠî=¹âE™ºëm«Å“*øåšÌhò3´�E~EñA}Ô²•­Õ¶Ë«ééJ½.çÇžÿ㸱æò�î³Û‹^ç¬ù¦³L«ã–k1£ÈÍÒAóßÝK)_um²êúzR¯A‘ñ•òG‡e%Ëøùó;¯¥–É‘â‡|Aeo­.º–Rùx¶ÖúÛmkéù})V/Úè·z)ïÅÝéöº|˜î²Ûñöºü½|ö]$VÍe·Ã— 2[uðßKéJÓí¶´¯ä«ít[½Mø{Í>×M—l¶L]®¿/]“dŠÉã¥ðfC ¶Öød¶úzÓí¶êVŸehÆ{]ïE‘~&óOµÓeÇ[-“k¯Ë×dYt‘Y¦{e°Ñ<´ð\€+˜ùøýªþI~öt�ê­ùÉý¦ÞB~÷tçêG®ÐùÄý¦^@þöôïêS¯U„~*?ÚÝÎ?¼—Iÿc¹n79(äº8À #~ ?eG‰ß½�±úùí5ƒÿ ³'ÅÿÞîÐýwvZÁO…ïÙ›ãïog~ºû%n�Á—ì‘ðKûÉad\�Ö·R]Du|Ο“�>¾ùñ÷e×{‹5:Ùâ‘çïºC³3qå¬Ü7—K ?_Y²ÉŠlùxG1³,MÆ5±ÏOe±eÇ ™Xxµ·A¾D¼à^{tVÀn£Öi;;ŒGŸ»é®ÆÊ‚_¼pîW,0Òl ìŒHåÍ›†òË1"ÅÛâÒÉ­º;bʲ+²±1n³B>Cüà~yôfÃ�ncÖi;7ŒÇ�ºéÎÅÊ‚ZÏÄ9T±Cõ°3ò1#“6nÊìÄ‹m�K&¶¶[U‘]•‰�uœæù<øÖé?“ÿ÷=Úv]Ç9ŽšìÎKÒ]¿«Ä‹'’u/cS ü|-´P_|4ä7ym¶br-²G×__X¥ÃØãë¶85²ö÷Qö7Cvg3éîÚâ».ؼw‘ åske”ÈÂÍ‚–KøóÃ|¸›-VÏh²°sq¤—;h² ’He²û«Òí~ªçý!ØÜé»K�l8‡?à›œ�%ãû+-¤øy�Ûd°Ï�&d4²X§‚x®“a«ÙaÍVdÉ�›‡4SÁ}ñIe÷T¹ä¿Ž=³â?{v_Ž}ãÇ$âÝ�Õ|Ž~=ÈõÞ³Iƒ—ma‡;OÈt9“ãâß´âü«G™�²ÕæR;-Ë×åC5-¥/ô¦7cÖ>`°êÁÁÍÙæb뵸y[ †~D8˜88òåæfeäIlXø¸¸°Y$ùËu-²Ë-­×]ZR”­_«7g›‰­Öáål69ù0aàà`ãË—››™“%°ãbbbÁd“äääM},²;-ºëî­)JVµ~¬,,Ý–f.»]‰•°ØgdC‡ƒƒ…�.Vff^L–Ã�‹‹‹’O‘‘<·ÒÛ,²ÚÝuÕ¥)JÖ¯Cĸ�+çÜŸAÂx/ä<×™ò;âÜG‰iv\�“ò]ö×"<=f“A Óãfmw;}Ž\¶Å6Noé~'“>Ï {¡»35<ÃgÃ2í»*ÊYï­’Ò9øóøeü?ëÉ0¶½ÿØý—äNß{ïŸI‰}�A×yØ÷Cm¶ãåéøÆÇoÏ«43û®¤°rŒkn§¥+Ù_wuúðéx™À®ÃÙw�<ì^þÛA|µÉÔA5:§€æÅ|vÒË'ÔqŒý�:²x$÷]I"ä±Yu+JV?²¾½Òè�óâ¿eÞ<ó±;ók—×#Q´ê®™ñÛK#ŸSÆv.qIá—Ýu$‹’CeÔ­)XþÊúÉ«Æ_Â}ñ¯ÔX½ñ�ÛÞXò»´¸¸ù1ØuW[S�Å4säòN'Ãú‹/�sm-¹N±Ûƒ¶åœƒȯ­.ú—úINËt��ÞxãnªN–ñ£§¸>ÛIw¿[Ë1¸^«oÏ ¿ßl–ßbr(vüë*û/²•¶³ld­•§æú:ãÓžø—ãõ5²t÷�OÂvš›in'Âáú­‡7²–ßKí¬üóuË™æÝeöÒ¶Ölù+m~ÚV޶ôï…ž&t5²u�=Q¶šŸüáäø|?W±æÑÖ—Rûn—žnáÙó<»¬¾ÚVÚÍŸ%m¯äôwÏ¢üEñ[Æ3)ã§ŽÑÓìõºýNçeÕ}]Âø>ë�àjéZ`CÉwÜ{M�ºä·ãÝZßõ3çÈ–é.ºû®­÷]uvѳÍ�l@üÝm·Ûu—Ûmö_mmºÛ©K­ºÛ©éu·[_Z]mÔ¯¥i_Êÿ—[K©[n¥.¶êVÛ­º”­.¥iéZV•û+JÑÿ.¶Û­­·R—[u+mÖÝJVÛ­­=+JÒ¿eiZ~÷¯ÅoÇÏ‘qOdx±ÕÐî'¿;"þU×ú‰:§–ËŸŸe)~ÇcÈzÓ#Šì9L2[I#·gvl¿×Ö:ÒëévŒ÷_Æ—‚�ÿMý‰ãGZÓm•.VTÜŸ„êoë>Y>v]¶Òìý‡#ëÉøÎÓyêWÖÊÛuô»Fû§ãWÁ~ý�"þÃñ«­­ÛdÉ—‘/'á:›úÓ•Í�—m´»??�õìügg»ÈŠû)}–ì/ˆ—zúÙ[n¾—hÇ“žù‡nú#Aá×}Ïõ¬²9pú˾±c¾“Vد“2Ì.Òᚸi’Km,ÄÆÊã¾ßÎ¥&Î¥)Y+ï%? ¶-aØo|Lï ˆò-¥²âõ×xcÇ,SWÒé2¬Âì®!«†üzÓÓÙ‹�“ “ÝZÒ“f[éY+Ãÿ$¿ î5aØo|NîéìžÚ[./]÷ve0àæÜg}˜¼¿†ç_$9X´�s¨ºÿ»M,Ó><|Ë,¬ØñV›7áç“ÜËÃï!úë¾8eùYñM¼pò®;e0àæ¼gu˜¼¿‡g_,9X´�s©ºÿ»Ë,3S ><|»,úØñÖ�øÂó¿˜üqù�ÔÞMñ¿Ò»3¥Ø_Å{‹†jò2,»°zc•K�‰Ï8½ØQît8;M®.40î4qçd~�ƒ’êµÙSÙ}˜þÚÚ+Ö]‘Ã;‹®øGkuÖîGÁ;‹i9—ÞcÇ4l´<‡_Ï[‘&.Tpåàä×"ÚM�=‘Ï�5.Š[,’Ë­¥“=wØGµø ìÞ¸‡�p®}Æ´¼»Šî`Žx,Øè·ølµ¹7ceEf÷cd[õ`ž8ç‚J]–[}·[K"úïŸñ.Öà|;³8âÿ ç¼kMËx¶ææ†ÍŽ�}�Ë]‘~6LpåáÏv6E´– ¬Žx$¥ÑÉe·Ûu´¸�s®#Ú…ö_ß`òžØœOŽó®Éõ—Iv·‘ñ[§Ãßñ½ö¾é£†[°vúm„9Vë-º±ÉOZR¿cܽ“Ù=`^¼¸þ*>N'®èý[òVòoø¶ù ýãûcûß°‡“_Å»È?ï!ÚÿØýò÷Ÿë.ã÷¯aý)2¨Å`êÅ”q€;¿ùQÜ`üRõ'ÇXhy7ÛëöÜú>íìIsð!Áå\‘vŸ7ì�w âÐáK&Ö-ļ§-̳W²C�~%-Ÿ2oN°îþLöx}[ñûÖÚ^KÆw8{>srsùs°±ðy ßöo1ìÅãÁžM­¸›«ù.4[‰'®5/ƒLOdøù’ûz¹»ù/Ùà|cõ€o¥äœgq‡³çw>—7  ‘p½ÿeóÁÁáüb<äÚY‹¹¿’ãE·’»Òø0äÅ¥“ãåËí�¶_ÍŽ ø2ꟋŽãý‹Æ{w7/´x¿�½§´‹]¨ârt÷;î®Ìí<ÎÕ»N9Î$仇:ÕrÌ /!®ãW‹ƒúí®°eS6,¼~?9<å= [Á¸:ìþQ«á=mÃ9W`s-ܵƒOÄøWÛrŽIµšÛkuÑëôšLLí–eÖYJÝwÓŽïm´õ¯¥¯…pNoÙ<�_úï‡òŽyËv×ݯ‹ðÝדr�ö[î¾Ü-6—7a•XìûnöGw¶Ÿm~Ǫá\›vG#×ðþ¼áü£�òݵ÷G«ã;C´ä¼ƒc}´÷_nŸM‹›°É­–ý·{#¯¶Ÿm~ÆX鎇îÏ#9¶?[ôRö/sóÜŒ96Vñ²áÛîk¾ƒSV^ï;_ÇðsçÖèuù[,ÉÏȤXx¿ZÚË%”­*ïo‹ß†óÍ^å�Y¿ï-ß ñ{ˆf]_NÁí/¹M¯·3 ;�ñ]Ž?Dzl‰-‚l}§!Õì1n¥õ¿µ¶–_Üÿ˜³»wÝ;ÎãoË­÷K‡¼¾��ÙtÆ¿ ÌœÌ~Æ6Üzr&’‘KÃ�ë³ñ«K«~5km-»·^8þo/»b=vï¹÷œ?Ç/—[®—y}9÷eSL;rp³ á|g?�ÃóIH¥‡aÈ5ÙØÕ¥Õ¿ÖÚ[t�üMü#kö®>&ûÊîÙëO4™ٗ׉iヾûc?e<{]gäZ«×ë6úêIL’úÒŸ�öQÔ²ñ3Åþ™¿#ª¼{é®±ÖÛ¸»¾5×<"î|¸y÷‹}/°ÈϾÙ37ZNªá|ª{í¥´¶·óå^”¥´§¥3)OF¦wo�~ù#³Ûoûçį;g”îõ–éöÛ›ô÷ÞsûµÑÇô`ƒŸåhîæzÛ±cû!“: ÿáwZäÇ~~o »í†Ë¢{µ|{ÜeåY&&§##·º÷W‰Kdú˜ØÚ.Q•§çÓËuõ³Û,üªZ[mµ¥lºµ÷S—]ãøt¼FçuÏØt¯<ìÞ†ÛäËepõ’eAÚœ [ )/ÔŽ-''È×sœ™o¾ë=/—“ÝKm¶´öÖµ÷S—½ßøv®‚”—ßZ^O‘®æùÝ}Öz_/&¾”¶ÚÓÛZ×ÝNùøO>3{g/+£ò»ŸÅ^IËÖèàáœó?´:úîC}ò˅ɹwîi9Ÿ8ÞÓëí²\-g*ÐÃ46R”º9+YkÀ�(>>@üx‹;yÄ8n›ÉN‹ùÛô~Vfã–ccÝ›~.,Ýc¸ÃÕslݬ±ÒÉd‡I�»‚ïõºzûoöðãÉ?‚ß:z<ýßâº�"8v%’dþ“éÜŒ½�,ƒï7A�w[m±µÜ»3i5¾Ù.ƒKî8ì»Ö³}—{x{ä‡ÁŸœÝvë‰q]G�Ü?Ëò?IôîF^Ë•Á�÷‹ ‚,Þ¸Ûck¹nfÎ[il—C¦‹sv]JÖo²ïll|Êü+_!þ:ãïy_CeðŸ2:óUvnTVuÜ—ð~ç‹G¬ÑßµÎÛí:‡—æÉƒŸ“&\baêøÇ"å;ŒÙkÓÅ÷I[,âŽËY²Ól35;�~v«k®É—a¬ÙbOƒ°ÀËǾ±Ï‹™‡•Y8¹0Émm¾Ëí¶ëkOJÒ•qÛe­Øé¶š�¾¿7U´×d͇°Öì±gÁØ`fcßX²130ò£‹#& -­·Ç}¶ÝmÔô­)W6ZÝŽŸ?3U·ÀÍÕm5Ù2áì5»,Yðsðrྱϋ™‡•YÙ0Émm¾Ëí¶ëkOJÓÕîqÁ9¿YrÝ÷ìžʺ÷�qlù5\Ÿ…sŽ=·â|·Žm"¶ËåÖï¸æûq¨ÏŽÉ-­ÐäC”¥Ô­iöÑøŸ‰øžTÿmºë.¶û.ºÛíº—[uµ­·[uµõ¶ën§¥iu+O²¯ûmÕ¶´ºÚÖÛ­­.¶ëkZVÚÒ¾´­+O¶•¥_öÛ®¶ê]mkmÖÖ—[uµ­.¶êWÖ•¥iöÒ´«ôáfæks16:켜 †L¸9ØSË‹™…™‹-“ãeâdÁ|sãdãO·Ç%—Rû/¥+JÒ´OKà—åZï,úú/;ï•_™ä·Vè©'ä{ÉýÛéëmLqcÙ´Ÿc5õ»oØœ6Ùßê×ï›,/§±­re·g43zøSù5»Ê^gŽ]ÛÈåËò'¬tvÉ¡ä™ë&wpuæ²È±¬ÛͰ–û¤Ùóî'ec‡oIÕ[ ZŰ÷dIúFø&ßð±òew”|?û³‘É•ä?Yé-“C¿ÜOïÎíþ½ÖY5›Y³å¾·ìùï޶E¶¤¿ê­†-cØ{²$ý#$vþŸ˜ë>EºW#¡;·6ؼÀñëˆj¤å[ió"’ÞôëL\¬n?®íü,yf»a,Õæda`røkmøµÙæbì1ä²Í¥uúé»ÚïBG@+ÎxGì¾Êz÷žè5ü§…smÓŒr®9µŠ³k·ZÖ¸=v]–ÝdŸK'{­÷Yu²YZÒën¶êR´ó|ˇñŽÂâ|“‚óm%âÃG³ã|›�í!úúíÎ�s‡.Ï]™­µº ¬Iî²µ¶¶ÝO_[kJÒ•§›æ)ñúdYm1öøÅ—²_½âa×ùò‹ñÍÊüî?f¦™¼‡ÇþÆËØì:‹™Íur2ðl†úMŸ×¼Â[bŠØ9oŽk~œô§ÐÛ`]TU¶jfbáÀ#åã«•xÜT�S÷ÞAÐ=‹—±Øu2žêäeàÙô›?¯¹t¶Å°òÎ3Ö{&¥>†×èòb­²Ó/­o�ÿ‡ŽKñ™ß³óޝã{ÌÏ ;—}-ý9Ê¥ÍËäõÏ)ËÅÌÛí:#—î³/›kçI�ƒ••Dz67Ë>ï�Ãï¦^nv¿ot¡rùÌ'€¿âÏÇše]_Bô_+äoʲ5:=¾v¾™qß6ºLÝ…#¯ºÜ{¨Üß>>|½òâè²úK¥¹6ï‹_=a›°·´ÆâwÒž3>—0äÓk5;œ­w×¶ùðõ·æçÛ}i~ÆåxÕñûåß–·E—Ò}/É·|Zùë ݃¼·ˆõäŠha̤\ÒͬÔî2°>½·Í‡®¿3>Û+ëH+ö:µá?Â�Èçž‘è¹Mxÿºã=S¼“U$=çÜ’ÝÕÝQvŸoúFÜnK¡ÙïqëÉ»+AÚÉ#É—ˆj¹˜²VËeŽß}ž²)ñËð·àGßËO$2r&­gûïñÿQ6=ž”ŽìI-ìþÀÕÏ6E·Ö·[>=8¬5¥)OfG­}mï¿�ÿ†·Yx{_(ü‚ËÌÈ­·ýó„ô^®,>ú>ëÆ|gà<£i÷|8²wÝ­‹‘Û{<Œœ?mmÙÁadr .‹c,¶{ï»W‰�g»ùÛ-·ÒŽÇtßÅÇ÷HCx×�\ ”mi�‰Fÿµ±r;gg““‰ìºÝœ0vûý&�c,–{ï¿W‡ƒg»ùÛ-·ìHSÇχ¿Œ_©‰/OøSѸ;�nÿ”j9o<ãR÷?>Ðï°þ�q6s°{Ÿ7Ÿón5\I"¶H¢ÀÏÆ‚)iï²Ëo­jéŸK§ãº¼-'Ôë4Z]dâëu| ]f¯_�ek[1ðµøQA‰‰µ­},ŽËm§¯ät WªÖi5øš�.»QªÀ†Ü| f¯_…�g¯² L,HâÆÆ†Ï_²Û-¶Ú1Ð ^«W£×âj4ºÜ >«_ ¸øÍ^>¿_…�g¯² L,HáÆÆ†ß_²Û-¶Ú1ÒgÒ~÷ï ¹üVñ«È¬i`ïN‡ê~Õ–MtÚ˜¶¼Û‚ñÝß"×`ÏmÖß““å`]È´Ùï­c—+h®üë.¶ïµ„»sÆ¿;ïì~ééN¯ìû¾å6¾ þiÂxþ÷u®ÅžËì¾Ý6ÿ7MÞŽki%kd¸yKÕ÷YuµûXK¶ükñ󾱯Çî~•ëͺì)uðì9— Ðn÷züY¬¾Ë­Óò ¼÷šI­¤•­’áäA,wWÖË­¯ÚĽÁÐ}ä&ƒŠwçKõ?wñ}v}6ºþ9Û½uÄ;#E�´¶+à·gƒ©æ:}ÎÆØdºÊOvËKkZRïJ¸äŸá£ðç³iŸ·ñóšö�œŠ{¡»O\‰»c¬á¶8îûÅ¿ 9fÓ�Å“›7¥ßRÞMv<·RÌjÛí¶Þ4ù øw¼Iì{sv�Ë9×�‚jÃ\me™v]ÅK)ZäÖí*Ùcs;23/ô­/³‘Û�­}¸õ·ÒÚq¿ÈOÃÉâ_c[›´è¾YÎ|zäV+±µväMÙÝw¶Ú×#Ý¡å;,ngùrzV—ÙÈ©µöãÖßm´�¯–ÿ…_ãw¿1öû®Š·°ÚUc'ž³y·â.·YÎwuÙ÷Ç@ߪëŽÒ—3&l�·'ÕýÊJõ÷efÉ‘.DùÃK¯›:y%º\�Ö«>jÙrEmgûðáæü¾dø­®Âæ›�Ò=ÝÑ÷ëzû³¤ÊȾm§"Àûœ—p^ÅˬÙY3KË´ø2Á™<·Òü�γ>JYdWÅJÏÇá×ÍÉ|Æñg]ƒÌöÿ¤;³¤/ÖõÿfI•‘|û>E¯¦÷pnÄ˺i²r&—–ê0eƒ2y/÷änu¹ÒÒË#¾*VØÿ‚Ÿ’˜þIüã·šn°³<�é|Œ>§ò'ëh1v{ŽK®ÖÅ?í«tŒ«'×è{k�[L¿¯\ nÜ‹u…ƒ qõôº½”u­ÖwgÀÂüõ~Ö+?ìÿ&Þ�WãówûP<œÿ°¿üžú�_ïÍ·í<òkþÃò|ê…S‰7öÔùŸÿ_ù'ôS�.R¹Rá˜Q^|�aø?ð�Ý»Ý#×[Þ\·É®Åë.ŒÐÇ™ªý7§Ýò¾²ë<Éùüº©-Ÿ2]7Ä®ÃgfDØÓâͳ�[­·&ÚÛ%/|ëÇð×៸wÚ�þöää^Eóþ´é=-™ßÒÚ­ß(ë¾»ØMÍe×_Tòê¸F4» ¥³dcÍ‹>Â<\n²Ü›=²Lð“ÎŒ þ;{{¨ß`n>GäO>ën•ÒÙ‘­ý-ªÝò~½ëìùy¤ºëâÉÈ—U±¤Ølí›#liöãaIu–äYí�çÇWÉMÿ?†‡;º)µÕìû;IܽËÐÞ3ñ¼è"¬ÛK°90ÔS>Ì=&Ê ì> ó{Ìò±ö•†Í–¶íu¹PÝ“‡JEæi¥È–Yç–IçžKæši¯ºIf–K«|’Ë%õºù$’û«[®­kZÖ¾µFÊi¦Éš\Œ‰eÈÈÈ–I§ži/–i¦–úß,²Ë}n¾Id¾êÖë«ZÖµ¯­Q¸ši²f—#"Yr22%’içšKåši¥¾·Ë,²ß[¯’Y/ºµºêÖµ­këT7»Ýß(Ýîy7&Üíy$ä[]†÷�r îÃ/o»Þîöùsl6Û�ÎÛa6F~Ïk³ÏÈ’|Œ‰ä¾i¦¾ëïºë®­küßÉüŸ(°=x­äO•ܶœ+ÇŽ¡æ�§¼Žl(¶WñÝei¡ã¶ìeº<¾[Ë6áqn!®žK.¥¹;<ÌLzÖÚþÙVvèo;ûÉþO^!Ð�SËû3s ¸‘ìdÐkýº=3¯’Ì<ŽSÊvaqŽ)…“|7ÒÉöY˜°Ý[kJ]ëJ³§CøËßžNòzñ†ê®]Ù{ˆ¥Ä�c&‡_íÑèi�}ñâdrŽS°“ Œñ\,‹ãº–ϱÌņµ¶¾—}•no‡ÿ¾fyéÈóxïŠ]Í{VÝDµƒ�ò¼hõüo®x¶E1©›n'ì¾]�¡àZ=¶F¬ØØ 6¶[wÝ šêz$ãá|ì ô:ÞAå×|j¸,¿pÊÉë~—Á�–ršâäA$™ºý§`ò818¿ÝkæöG_¹ê¹µ­õ¶oKm­ò ñÃðÜs]ÌXß*»»]Ã1¥¦$ù}Ó¸qòNG÷yáºLŒ=‡=ä˜ØüwE¶Á—ÛeÖâê·¸²WÝ['ô¶•¾@Þ9~þk¹‹yå?vk¸f<´Äž~¿éü8ù'"û¼ñ]~NÞr<ðß[/²úVÛ­­iZV•œl|Ìyðó ‡+*q²±rb²||œyìº)àž mº)¡š+«mö]JÛuµ­+OGñÉÆÇÍÇÈÃÌǃ/. q²±rbŽ||œyãº)ñò –Û¢š ¢º¶ßeÔ­·[ZÒ´ôƒÙÿž w&>Î.Åñ/ ·™[‰äÊØï±zÏ‹ñÎ_‘“=÷I>E9¯×éyt3äIun’èólºK½+ukZQªý‘৆}¹ÆÞÁñ£·Ù›i$›?{g\ñ�7*ži¯ºI¦·˜huú¾SÓIun¾øó,ºû¾ÚÖµjÏcø-á¯mð³°�Ó2=fë”qͦÓ[÷�»Ç[«ÑÝ[£²¾¾¶ÛZrgÈÃKádÙ³Øô‡+íyM¸Öë00vÕíN¹×]Ó¦\ÙYÂ{#œeäj4YT¯ÓÖñí¿ŽH/º/©g¤Wů2~<çñ gË«ÃpûÛªµ¶dååvJÛ´äshõ¸öfdÝ™Ìx6F/2ãÐbk°®ÈÍÌ‹;M…miI3ýkOXùùmðÃæ�Џ›UÅï²×Ó''#œtü{-öf›]Þfûß-àóáAËtqÁ�‹tùy0cçêp¬û$Î¥kOXüygðÏæŠø›SÅîî³À¦FNG7ê 6[ì½6ºdM÷¾WÂgÂÇåºH Áƺ|¼˜1óµXv}’fúÖž±WóÃðÈü†x‡Û™õ&ŸÌî¢ÁÉ—éîúCK···µzÚM¢ÁÃÌå= ‘vÓ“Ë“²ØíåöEÅ3y„x˜x3egˇ )W+JÒµ¥iZV•ô­+öV•§å¥iü×&�GBû/Šûâ–Ëã’;î²Hï¶¶_eöV¶ßeö]J]möÝOJÒ¿m*ü€,Hø'ó¦ÿ0ü;Õðþg¹ÈÚwo�–éº×°2sä¾}‡"âÒcfjîq‘‘tu®Tû~;©—[™,²Í—“³ÓeeMZ}æ:Ý>O…o4ïòËÄÍgåû‰ö}Éãå5=wϧΖùö;þ9~>WöµæÙÉYeÊ›sÇõ’`eÏ4²edí5yúRxýÓØø[óBÿ,ÓUלò|éo›a¿ã—ãåk~i‘=ÿRL™· dšü¹¦–Lœ�ž§+"_JOº×_€�‘ª|‡xö<ß�Û¶ò3Çë°:g¾íÍ΂}÷!ÏÔk¬»€öÖd3ò-ï ̓´¸t1Ë�´Í�<þY¯ÞÇ‹¸ø¶V½°vØnÀÌ|ü~Õ$¿{:Gõ Öˆ|äþÓo!?{ºsõ#×hüâ~Ó/ {zwõ)תÂ?ínçÞK¤ÿ±Ü·œ”r]`‘¿Ÿ²£ÄïÞÎØý|öšÁÿ…�Ù“âÿïwh~»»-`§Â÷ìÍñƒ÷·³¿]}’·GàËöHø%ýä°¿².@ë[©.¢:¾á?ÍWÅ/��oqtæ&•]S¡ŸE�[±ðñ{‡…b_“±—®6Ù“V(±9DóOdze¾Ü{rf—*¶A•nV>`~1qüØëÈ»W©ðq1|šë $ðha¾ü|,nÖáøÒdl&뽦lÕŠ,Mî.DóO ÌšûqìÊš\\šÇWÞ°ø¹òûñ‘�æ¯^ÇÚ½Qƒ‰‹äÏYifƒE ×ãáãv¯Æ“#>n½ÚfMX¢ÄÞâdO4úÉo·Ì™¥ÅÈ­�åS+‚_<wäס.æý_Çôzÿ4z[E›/Oò{å×h²;/ŒC6NÛaÑÛ~& ºmÆfFFOÈÙM.‡}‘%öäaáìv÷Ï_V~~«?7W´ÂËÖìõ¹y8v~4Øyøøs_�—…›‰‘dy¹x¹Ý‘Ém·Ù}µ¶êR´­IÏÀÏÕgæêö˜YzÝž·/'c®ÏÆ›??kñòð³q2,�#/";£’9-¶û/¶¶ÝJV•¢ ¹øÚ¬ìÝfÏ /[²Öåä`luÙøóaç`gaÍ~>^n&E‘äbåâäGu’G}¶ßeöÖÛ©JÒ´U³Ê8¿%á—‘p¾iÇw¼C˜ñ îÛ‹òÎ'Ê59úKÅù.ƒ?#U½ã¼‹EµÇÄÚiwº]¦$¸Ùx™1E‘�‘ñÉe·ÛZSò?#ò>éi´ÛŽE¶Öè8þ«e½Þîs±µš}.›+g¶Úì³e³_­×aE>fvv\÷ÛdQEeÒI}iKiZ×Ñôtú}¿!Úë´Z ^Çy¼Üfãkum>NÏk´ØæËf>»]…ùyÙ¹sÉm‘E—I%õ¥-¥k_GÑÔiö܃k®Ñhu{Þïo›�­ÔéõY;-®Óa™-°b`kµøQM—››•=öÙQYu÷ßZRÚVµz®Á9¿grÝënÊ» �ò¬øõ\c…p~=·åœ·‘í%¶ûâÖèxæ‡?q¸Ï’Èî­°ãÃ$•¥µ­)öU#¿'�½ÙŸžùm¼Éñ³®r®ÄÍ��âAƒ¹ïG¬–Ívg²}t·äñî±·7 .Xí¿k÷ݾ^=Ñäé­¶¶ßYøaø}ûÛ·ìÕso*7™>p‹±s#àØ`n;›¯–Ì<¯lø²]•Çzâܬ\«í¶ý—ßö˜¹0_N¦ÊVÛë / ¿÷zöýš®må6ï#ÇÞ‘v6\|,ÇrïµòY‡“íŸK²x÷\Û•‹“}¶ß±ûöÏ&£ÈÕYJÛ}eÝñãøJû§´bÖöÈo=ÍñïˆK}ÒãôgVçñnUÝ[Œ[¡ÝcÙ/%çŸüSõ¯ZYf\zìØaÇÇåY™˜sO�“£*Ê]Ihx�ñÙℚ|LN…ê §•Gƒ\=§jr8bå=³¿úø˜x»+¶ëi ›=~Úì&ŸY«ý¦¶jÝtXq{«D¢ü[ð+Å_µ8¸�Ôú?$³ ì-—eo �’v†öÙñðáØWaͶqK´ÄÂÙɃdÒëµõÂÔÛ7­ÑbEëè”'‹¾x¯á槤z§E¨äva]‡²ì�ä1ò>ÏÞRx1!ØWaÍvqI´ÄÂÙ߇d²ëõõÂÔÛ7­ÑbÇëèšoˆxÆ?ƒ>+ôêérp.Öïyž. »ÞÏ娒l/Û_�Ì{G’M·ç¼›=œ·M�‡—°“Ö‘âÃ6Y»¶Ü6à·,áû¬úó¸xfï®»W„ñ~Äàœ�Ìmçæ:M Ð죊[21ïÈ×l ÈÇ®FTVM�5)IqçŽÉcºÙ-¶êxîלµx–ã�v_ã|÷…r,ÇÝqn[¦ÀßhöQÅ,yW']±ƒ#ù±2¢²hdöÒH&²Ù#ºÛí¶êxî}×¼µ8–ã�öWã|ó…ïà·sÅùfŸ{£ØÇ¶OruÛ21î›&+%†J[I šËdŽëo¶Û©åù¯á}“Ä÷ü±x‡çÜ•ëgÓrŽÍtžSÄù&£*”·+U¿ã»ÜLý>ã[“m=$ƒ""¾Ÿ–Ú¢òOøs¶œS‘w7€4ÙrN?…‹�¹ßøÙ¼Øäí9n¾g»"ztÿ#Ø_._.ÆÆ×ÉZÙ£ÚÍ~âêbÝL\ÍŽND8vÅSä/à aƱ·Ý»à½6í&6F×wã¾ëc‘²äØQE-ÒdWª9&ÊYrù<a_î·K´šý¥ôǾ˜Ù™ÙÁ…lVþB>6o}Û~ Ó?}¤ÄÇÊÚîüzÜì26\›(¥ºYÿµO"ØË&W'‚ +ýÖé¶s]´º˜÷S/?"xpí…wÊÿáUÔl±¹yü`Åú'sl˜™{Ïù7 õÒlbûµ`Úlzc±¹Žæ³i¶7eEUü�eÉ…-r2®ÂÙaǦXŸì5ùúœüÝV×3Y³Öåä`luÛ i°³ð3±%¾ ¼<Ü<›#ÈÅËÆž;¬’9-¶û/¶´­)Z"ý°×çêsóu{L,½nÏ[—“�±×l1¦ÃÏÀÎÚü|¼,ÜL‹#ÈÅËÅÈŽë$ŽKm¾Ëí­·R•¥hŒ>~v«;7W´ÂËÖìõÙy v~4ØyØØ’ß�—‡›‰‘dy¹x³Çu’G}¶ßeöÖ•¥+ODù_å<“òÎ8Öÿ†ó>#ºÙñ¾WÄy^ŸcÇy?äZ\ɵÛ�!Ðíñ°öº]Ö§a�$X¹1E<Yu—ÛmÖÖ”üoÈü�€š—á–ó*ngÖ]“áG0ÚÖ}×RW#´ú†þ½óKÖÜ›u?=ÑAu˜Ôă‰síÆ6uŸVZäO%’–[X±«ì˜7áÐòÚ^aÖ½…á÷+ÙýmÏTÝ‘Ù]Y ßRé¥ë®K¸¶k¦‚±ã[�/ç[X3)Yeºy®äw[e>–?æK÷ðëùi//ënÁñ•ìþ¶ãªîŸ²z¶)ë%ÓKל“od<×MlǤâñ^qµƒ2ÚË-ÓKw"ºÛ)ôñÿ6ÄßÂiçå;›Æ.sà×>ädö7‹¹’rÞ¬ƒa›‡úO{ãÿ8ÛÉ.VeͺÛYÕ}‘Ÿ>>^L–[�ƒ®äzL(½-ŽÛi*d˜’[KŒ¯^\'?“×t~­ù+ù7ü[|„þñý±ý�oØCɯâÝä÷�íì~ù{Ïõ—qû×°þ”™Tb°ubÊ8À_AÇ÷ܯy¨ã[I·ä¼“�l°´Ú= Öæn7›½ÆË"®‹E¼å­Oã:m¯"ä[펣G Ñk²öû­ÖÛc‘.¿W©ÕëáÈÎØìs²¥¶8`†;å–K©m¶Öµ¥WG¢Ýò}ΫŽq­>×�òîÃQ£Ðèõù{mÎçk°žÌ\ f«W�FvÇa›“-±Ã 6_$’]Km¥kZQê¸?æý�Ët<­¸o*ì.wʳãÕqŽÁøöß–rÞG´–Ûï‹[¡ãš<ýÆã>K#º¶Ã� ’V–Ö´§ÙTœ< ü7=©Ùðèû';C°é^—&Ϧø¤ºüÎßÞadcß‘ |¯s4{=Ö‘ßõ ºü_¥´Ûûk6>D:Ü‹)}$uàÿáïìžÄ‹MØ^eo³º�ˆdÙ‹°Äê->_iîqfŽY¢�•n¯³a¡ëØd¥`¾ì[#Ùm+òÁo¡~Ï;*ì{$­‘ÖË=-¤§ú Åï¼]ãÁƒê~!Öš™¡Æ‡e“£×Ö^EÈ~ç&L˜“r¾]²“;•rÌœK³%¤2ì³r¤†Ëëe•¶ÏKi)~ƒñƒÇÿøÏðK¡:§ˆõ¶ªXqáÙdé5õ—‘r bI“.,¼«–ì¤Îå\¯'ì¹i»Ì©"²úÙem³ÒÚMOǼxñ/¯ðz·ÆÎ›à1Á°â×Û.Ÿƒqü=Tû̽f³O�¼åûºY&ÿ›ò©µØ1Y“¸Üegms+o¿#"[ë[«±LôÏ,ðÒO.~;éƒ.æSaߤìû+¿€ý—¡“]ÍxÆ<Û8fÉÄÆÍ·aô©dVø®†ïÈáÿòcÅXw]�ÐÒçù5Ò86ål3.ãÚ{ íî­ŠXkëÉø&ÙWr�v$94÷ìôUȯÓÇŸ'/_”­bUç?Ág‘>4G·çý&‘�;‡LŒÜ«4Z«¢íž¯¶{}?„+ ¹œ H'²—ìt•šúÛÙ88öRêÄËÎ_ƒ/"Lï"º{™¹VèõWEÚü;_d¶×ÿŠ…\�á>$3ÛK¶:ZÍ}mŠiòpp`²—V %¿…‹ÉìßvŸ„M÷–}1…esr:Þlk|›â”·k4öÃÇtxzçÀÂÆÂÅ·ëñøpw¹YyôŠ?X`“.îÖ•¶µ¶êVÛ­­iuµ¥iZV•ô­+Jý´­*á}iZV´­+JÒ¾•¥~ÊÒ´ü´­?šáµiZV´­+JÒ¾•¥~ÊÒ´ü´­?r´E*x'Åžll˜eÇÉÇ–H21çŽøg‚xo¬rÃ4RRÙ"–)-­·[u)[kOJý¯ø?ãù2/QöÏ`ôOfpžàê®KŸÄ;¯wø\“ŠòuÖýl-ŽÕ¥bÈ‚[oÆØjö×É�›‡=’bæáÍ.<öI —Ùv@êžÒç�%Øü7¶zÇ‘fñ^yÀ·¸|‡�o0/ô“;êÒø2a»Ö †¯c‹|˜¹¸sÛ~6nÒÁ5—Å%ö]�:«´yßJv7í~²ä9¼[žp=î!ã[Ìý$ÅÎĺ´¾ ˆnõƒ?W±Å¾LlÜI­¿3i`šËâ’û.Î5yÚ^&wÏVyÒÛÛøïfu-Àåœg6²g[�›X-—oÆ÷ðksµ¹›.%Ëô9yZ�ι[ŸªÍÈÆ¾êY-Ë6~>üÙà^|øÑûۇGn§w'¯íuÕº~ Ùº|<)y'¶K¤š¹zié›~«&·{òuy˜÷Ël9[|ó„yÃã·îÞ#º­´Þî9Øü>é).GìmN.œ‡Aõi|•ÊÕËLÈsu™­²dêòñä–ÈgºX"±_Á�0xG›ÞëâQÛ«ÛËëÇ;ˆ]ÔŸ„ö.§NC¡úžù+•«–™‘fë2+Z_“¬ËǾ["ž²Á½ŸqõgÈ—Š}iåUW[‹Ë°¥Ösž ]æûsÕ]™¤¤0s.¹äYx¸øÝ›¥Ì–Éð²20ð%ÚirðvVcEl6·i¸mÁn ^|¦ñ¨¼Äé.cнע»qùn-µ‡; ¸øü�‰rJIv“™ðý¦F6eºŽQ É’·ãÍX¥†Xî“")ñgž pG’ž8õg•ý9˺C¸4·m¸�,ÄöÙ—‡\x7ü_y�KîÓrÞ)±ÉÆÌ�UÉt97ýLynŠXd¶·Ã,—Ý|7VµÜù•â'gxQÞ|—¥{/üŸ¸ß~Ó…ó0¤ÂÓvÊÉÈ‹KË´Ñß6U°Ù—n=ÑeâÒi¯ÀΊlkï¾è½÷W‰æGˆý›á_yr^–ì¬i2>ã}û>Ì!“MØ<'+'"-/-ÓYtÙVCf]¸÷E•‹I¦¿:)±¯¾ë£÷]RïÉçÆŸw|_y!µéÔ§ðŸ†î¢Éä})ÜšÍlºþ/Û¼™Ã×üœêñî_ š[0ùŠiæŸU�íº9rõÙ:ý†n£5Qªîr€:{àïÄo˜þvˬßðNÓ¹’]Yû¿³£Ïãü# ³1ò‚[‰‘¾ì»rµÓãÔœ\Œ83,¤Y™X”¯¾�#ðÇâ³Ë_5¤Öï¸W§êL¹=Ów/dG›¢áù8±O<5â8¶âͼç™Vˇ<×Y�. Yqý¼¼__u:?á§Å‡–~iÉ­Þð¾NÔÙrVé»�±ãÍÑpüœX¦È‡"¼KÜi·œï*“a͵ÖcÍ…]ŸK++×ßNåüpþ¿;þC-ã\ö>1޾7në…Ÿý½û‡]›…NIÇç—�dKŸÔ�meøœÃ³$Íãûë³µy×SSÄöUßôì{+/Ï þ<ñvÍg#çüzï(ûK*]?'íýF¿#�áfV̘e—�u/ØñlX$Šk/³ôÔ¼ƒ/"*K�“ })IZx‘ðá§�6ky6ãõòK³1b¶éù?kêð2xfec’)¥ã]SïÏãx÷û­’*í¤ÞæâÏe$ƒ*:úR’¬ñ+áÃoìÖòm¡¯’—‹·MÉ»[W�‘Ã0ó=’Ç,¼oª©~Âú]eñ×m&ó7{)$1×ì¤æüü=¿ž ÁÇùL�k’]ëª�[›?r÷þŠä8uã[:ìºÛ­%Ç“¯zòÝ_*㿤4¹Öáçò­U¹2c×y“}kÛÈ¢Š£†ã†c²(¢ŠËcŠ(£¶–GqÙJ[dv[JR”¥)JRŽÇG‘YdQYdqÇe±ÇvÒË#²ÊRÛ,²ËiKm²ÛiéJS쥊Ž8áŽÈ¢²È¢ŠËcŽ8í¶ÈãŽËim–Ye´¥¶Ye´¥)JSÒ”w)þßíþÀ >C¾¼gòúCØ�5‰©ñÏÈlÊgìë¾ãZÚAÖ<ÿu=°ËK{„kã·_™Ÿ‘ õ—u¥�:³åÍ•›ÒJYоz|&øéå|î{Õš¾€ï¬Ï¾ì«È8æ¶�õç9ÜOtyÓŸðÌ cÆÅÌØäY%eÜêlÇϬùRdæG²ºÛ!qgÏ…/|¬ƒ}Ï:£WÐ=ó™\Í•wüw]Hzóœmæ¬sÝN}Ã0-�/a‘d•—q©³:³åI“—Êûm‰Áo“¿ÃãáÈ~6ûŸñ�>»Æ/(6Y7lîîþ´ãuÓó]žVße¸ÜÍÜ�g…›Çô]…²ßän²¤ÈßG6»“Ý•Lk§ØåbbýÂX6ùCâ�{øqÚ›>žò‚ì8_,ŠÌýfMÕ·;�rí÷]n'á|�Ý¬äš Ë¬ºÏ«õ“&9qrlƒ. àŽ>Jx¿Ý¾$önÏ©ûÓ…gqM…m2õ™~¿}ã|·G-Õ¦%á܆ W]ÈtY~•·êCwÔÆžÙ1²cƒ.àŽ~IxÃÝ~%ö^˪{Ï…çqM‡e35¹U­3xç+ÒKui‰É8 Ç¥u܃G—é[~¤7}Lií“&82¡šëˆó�ãÿÊ?ŽþÝÍéÿ&úï3ŒfÉ™·³„óÍU™»N®í�6ž\;fäýaÌæÀÀÆäzŸ»lðæŸH±vÚ¿¾EË *·cÛ®�`—€:�ð÷æ]¾yÁÖ¼Û�íoÖõ?bßwQ÷ºë)‡�ùŽV$zþO—[ñ²ï† ˱µÛ‰ï‚?½Iƒ‡“�iL‹éwJþ'<¹§‡Þeõï-ßm/×õwa_^ªíŸ}ö[‡�ŹfV,zÞM—Yl–˜ððžW��µžX¬®MuøÙPGÿ�Rî“üPyoO<Èëî[¾Ù߯êþÁ¾½WÛû¬¦7åyX±ë¹.UdŽo¡ åP`m'–++“v6L×ú=Ô¯w¿ߟ~|ŠõÖ›’ï2p:7Ê<�_@vÖ¹rÙ¦×í9>Î8z�°ò±²¹ã˜Y<±r± ÌÛçÓ+ôWÛï+WK=Y,=XbµH\·Ï½–Ûò±ä¥m¶Ûk&»¤o¾´¥)[î§Aõ•”ºúÒž·]K,¥=köúR”ýÄ>qì²ß“�!î¶Ûm¬˜7|•¶ÚR·ßNŽëˆéuõ¥=nº–Ym¾µû})J~â¿8vYoÉŸ�—[m¶ÖMNß%m¶”­÷Ó¤zê:]}iO[®¥–[o­~ßJRŸ¸«÷ñM``aüºv>F&&.F×§zC?i>640M²Ï�‡Û«�7>X¬¶ü̸õšÜlkd’·_H1㎕öYm)Ç%œ™GDõ´ ï*ÞiøÇÒíy$ä;<&ƒ�èµù{}Þós´É‹ [©Ôj°!ÈÎÙl¶“YCòË%ô¶ÛkuiGÔÑè÷\Ÿu©ãœoO´ä‡²ÂÓh´:=~^ÛsºÛìòcÃÖêµ:¼§ÎØì¶“YCeòË%ô¶ÛkuiGÔÒh÷\›s©ãœoQ´ä‡}²ÂÓèôZL ­®çs·ÙdLJ®Õ굘1O›°ØçåÍdPÁ —Ë,—RÛm­kJ=G àüϲ¹wàwÄù';ç<¿m‡¡â¼;ˆiv<�“òMÞÂZCƒ©ÑèµÙ{=¦Ç.[©lpÁò]_ÉD³¾:¿ üÛœMm|€fçê±2lƒgªñ³‰më…¶š­�4vÇ9ÔdW#U^-’Û6›E4yÑÛ,W߳ƚ9°é(ÿ¿�û<]7hyÕ™›�E±gëŸ+Ye÷m·8yrbÇ-~³êε鎫ëÞ¤à\K­¸>–ÛíÕñ^ ÖñÍ-òúW#&Ý~« 3³$§¿#"ú]>Dµ­ò_uõ­Õ”^u·^õ'ÖpN¯á<[¯xfžÛíÖqŽ£×qí&%Ó]ïÈž=~¯ì¼¹kY'šëk,òV·Éu×Vµ¬¢z÷­ºû©x¦·‚õ âÝ}Ãtößn·Œpý»�éq.–¾ü‰ì×ë1ñ±îËË—óçšê],òV·Éu×Vµ¬Ìø/àÝ_Ät<­8_ë¾ŰmÖqŽÁxq9­¶I&·_¡ãz =~›Qƒl³_u"dž8éuÕ¯§­j÷¯lö¯XáŸÉÁ§ŽÞháòÇêlM'@ù)‘~ÆÎY£ÖÓ¯»u-ÒæR.Òâú¨=¶æí3d¾“rl4ÚÙt÷M“ΑGŽãÈ7ÃAù�‰¾ì³ÅÓôw‘9eçGË4Úúãp~}¶ºùréfq�dt޹{<©o¶]öTÚÇt¿["Í�°Ç�^3ü�ü3ô/—¸›îÖxº~�òx²³£åzm}q¸?=ÚÝt¹T‡²¸Ö²*G\½žT—Û.û*m#º_«‘fÂØ£Æp£å[àGÄ¿’}g"ì=>·_Ð\dí—]ß¼CSt˜|ÆMDypE¦în‰™­Óó¼MŽ]°Wwm1ù>0ð-·:m~uy0LòGÆ^íñ'µ7=7ßœeÁ9Æž(scÆÊºÍNûI—t¶ëù'Þá_>«‘qí…ÐIlyX²Ée“E,ý<ˆfŠ8Qù ã—qø¯ÙÛŽ¢ïŸÃ9–¦8s"†{£ËÔï´¹wKn¿‘ñ�Ö-ÒëwüaX$¶<œy/¥“Å,Ò<ˆf†8Vù ã§qxµÙ»~¤îþŸÃ9–¦8s"†{¢ËÕotÙwIn¿‘q�Ö%Òëwú úÃ}¶dcÉ}¶M°KHò!š(ë]óWÁï#¾?ûË‘t’¼#Šò­DÓäqîG�÷­�ìÞ+õî�YÏzÏ•K‡…'â[ˆ©K­º±cçàOïÂØâálqò°àÀÌÂ-HuŸá_Ì|Aó·¬ö[½Å5�YÜ“ÅÒݧnLðA¬ÇÔóìX¸·'ÍŸ2ûq5Ñpþq»7'2¾’C«¦lvÝKg’•êWÃ×•Òø­æÏ]æn6ÿ£ºÓ·æ�§û*̉£‹[,ÌÆ�Šò<»òe�œ[šÇ�“>eÔ÷Á¬®lvÖÛf¿×¨¿þVK⿚Ýw™¸Û~Žë^Ýš> ì›2%Ž=t8¯3>-ȲïÉ’ÌLx¿4�&|Ë©ï‡Y\Èí­-šÿYþß93¼Bù#à}¿Þäàô÷—õ×øýÎu—ɾÊÖYÏ7y×]Ðü¦Í&¢_¹är.Êɇ�Ű͊X5ZnQµ–µŠÛ¯–Û$V,ZN®cçãö«ù%ûÙÒ?¨n´@ ç'ö›y ûÝÓŸ©»@?çö™yûÛÓ¿©N½Vø¨ÿkw8þò]'ýŽå¸Ü䣒èã,�ø,ý•'~övÇëç´Öü,~ÌŸÿ{»CõÝÙk>¿foŒ½½�úë앺?_²GÁ/ï%…ý‘rZÝIuÕðKþ~>%1¹·”ùéão’ÎQ­Ž]Ï’½{ǵô¾C§Ç†é3û³K¯Ä·ß÷Ot¿“ÇwG›‰K¶·ý)àØM›œ�‹l~G®ä¾nø÷Çd�“ë!¿kä/ÑàÒøy£+¯Ìî 6->¤í<1ÒîGv]n%+²»éO�›&l_¾p¾.±ù»’ùµãï’ÎM­†ý¯�|GƒK¡äŒh®¿7·ôøX´÷ýÔC.äQÇÑæâR»+¾œðfəěð�°òGE¾ùñKŽA•Þ¼�Û?��Y¨À­»Nëà\k8qûˆY‰k±ín¾ÑâR,½|¶ÖNC Æ²ÌI)±×bàmᮈú% ó€<øøø òsä#{Ç…jmëΑ×m1ðygzó< »8¾%´ž¶ìµÜ'Ymع½�Êðñ¢’êáaI&<¿J<üÜ O —ô‡Á‹ï#¼ðÜE±âËxM`ìaÄäÝ×Ëðr©Ç ¥¹5‹a®ázËoÅÌìO‡RÝ\LI"ÃÇ–Û#ÎÍÂúÐÝG|øÂò3ÎÝÄ[#¬·€ôÞÆNMÝ<»*Þ9-Èú{  Ö[~.g?äØ�G%k‰‰$X�Km‘ææa}h®¿·¿ŸÞ^|™åjùæ.%ÝâÔ·ìk—äG=Ðåæaò{õ[uºžžáTÍÓí;;iÒ܈f̳#�bI¯Ì‚}�™ðǃ<é¼ø¹ñ;ÀM.<½OÃ?„}¥‘¯» ‘÷;·uØÛŸ¯Y®ÌÅÕåÓw ÐÍlÿJ¸x1#ž¢ûåùsÙYîš…ÿž/ø9¦†î¬â§{##ü>AÜœÖÜMÏaî,ȾI2±0s­Æƒ‰hå¥öÅ\L‘O1W*¹SÙYîš/†?Þ0ø=§†î®â4Þö<ø7âoû�šÛ‰¸ì½¹_~^.m¸Ð`ñ-´¾‘ýÇU$SE UÊ®LöV{¬dðâÓÃ/�Ž'ñ«¬aÇåûLLŒ.aݼæí*îþu�—~žL� çvêõ·køÜ“ñü)é¢Ócj¸ý™xÿz³Ü©&ž^‹7á¾Î‡€8óðÑÆ|Õã»NùñóQ¤â~YñÍmófáEL-&‡¿õ8Ö±ñÞS•}q°5ý‹‰t�M¾ÈºË&¶–köRSî™z¾|³|Gñß0ô;.îè�VŸ‹ùIÇõÕ“3Ϲé´�ëªÀƒÒ=Éò¯®>?ÃÇŠ‘i·“Ýe’ÙmšýŒ”Ħ.^¯‡?,_|wÌË»z/W§ãPñý}eÌÄ�îz}'yêð ô‹�òl«ë�…�ϱ1ã¤Z}Ü÷[d¶[fÆJbÓ+YœŸ‚ÞòEÂö]áÑÚÞ=ÀüÚáúkFon³M£ïÍ6Ÿ<|ÙYÔ¶(bäx˜8‘ãh9õ­ØvÙeõÀú2`@_y£Üñ�Öß�ò=VÇCÈ4<ý.÷G¸ÂÈ×m´û�^T¸;-^Ï_—YX; ~dÅ42Ùl‘Iemº”­+D7Zm¿Üm¸÷ Õì4{íË;M»Òíðò5Û]FßW•.ËW³×åÇVÃ2 âšl¶H¤²¶ÝJV•¢û�6ߎn6¼{�jöMö‹e�¦ÝévØyý®£m¬É— c¬Ù`eÇV~dÅ42[l‘Éemº”­+Ecüã„òîµæ|³®ùÿÛqsÁ9&ëˆs+¾Ã—_»ã|ŸŽlruÝÛj[.&ÇW³Ä– £ºž¶Éehùošù¯.Ü¿�Ÿ'³<<ó¢;öܼœn?Ŷ`Å‹3%ÎëSKøÏbbE�|°Ã��gÚåO…e÷RÛ3á‚JVÛ£¶êmß�ÞHåx›å¯I÷‡Þòqxÿæzî}4Reß™Öüž—ñÎ{¸Ë› ¸8ÎÏ#';ëí·>$§¥Ö[ZmÇ‚>GexŸå—JwÞòq´g—áëùäxÐÝ•~g\rkoã¼óÌKyÙpñ�žFF—×ÛntIOK¬¶êtûáÃͼ ùñ×¼¶[ûô=gŸË êÞò¾}¶ßY¢“¦»:X8Ç/Ürh4¸ù™[Ý_]O•‰Ë À¬Y‘´ãØ•öÒûl¾ÛJ¢–)âŽhdŽhfŽÉb–+í’)b’Ú_‘ÉekmñßmiZV•­+J¬ªŽK%²Éb¾É#’ËdŽHî¥öIeô¥Ö_eöÖ¶ÝeÖ×Ö•§ÙZ,�ŽHæŽÉb¾Éb–ËdŽHî¶øäŽûiu—Ù}µ­·Ù}µ¥iZWÒ´[äÿoöÿ`^¼¸þ*>N'®èý[òVòoø¶ù ýãûcûß°‡“_Å»È?ï!ÚÿØýò÷Ÿë.ã÷¯aý)2¨Å`êÅ”q€7+ŸüˆóÛ²ï랇âÖeãié�•λ~F·¯úëU±šhq6¯{.]Ödç}ÖzáëñbÉÙgÓjÁ–A=ñíχ~wל=�_ô§²|]M¸Y\ßžo.ÈÀཪϚX±3y6æ,|›©—°®4ßr×ãG‘°Îú] 7GòE¶þøOß>ov-ü¥x噺šaesny¼º| À5yóK.w%ÜÇ�“u2ó«�7Üð1£Ÿa�ô%ºnŽ ï‹¥ÿ^L|¡÷ö»éMT=~'ÀÑCÓëë\Î7Íxž~Nn.—žpE–àr¾»›]‘9˜õõ‡/#&È3±r± ÄlTÅmbuûá‡ä&ü¦×Å̶·cøùÝòé¸/rC7Õ¿ŽVÜɬâ¡dP×êR~°ÙM÷Ïm³]~—7:Û!“#îõ³«ß¾xÍá?’Ø8ü¿iv?C÷4º~Û1MZ׎Kn\±ñ>Ë¥)u·Çw ÎØÍLÚÓê{ôù™žØ¥žÜgV¾"¼î›ÂŸ%0`åÛ;±ú'¹%ÔpÎØŠjÝv'’ܹlâ�•m¶Ö—Ù ÎØMLÊÛI+~Ÿ32–Å$ôÇöHƒðçü¥Ià—x½KÚÜ¢]оQìtœ'žWm¸Ôk8¿WvlÙ�kúó»3s7�Ù�§ÓkdÉ“OÉ%·?Y‰M&ÃôŽmÙWip`¥�ÝmöÛ}—[}—ÛK­ºÚÒën¶êzÛu·SÖ—[u+ëJÓò¬ ¶ê]J]miu·R—[uµ¥iu+OZV•§ÙZV‰þÛu·[K­­.¶êRën¶´­·[ZzÒ´­>ÊÒ´Z ÿOúÿ �Ÿ%ßÝ{òãîË®7vj4«Å©›¿é>ÎÌþL®Ëo†;gÖçåaÇ~Ê^ËâÆ�q‰e&²ëlƒ.ØdÊÂÅ­šòàóÛ¢³úûyf³GÙ\kï›Î�쌌K¤ÌᜪHc¶l¼Œ[.Λ†ò¸ñ£ÅÛáÒ’Ç%–C•lWeaâ_†|†x%À|óèÌþ¿ÞY¬ÑöOûÞó§û#é38w)¾í›/#ÚçMùTxñãmñ-¤‘ßm°å[ùXx·Gο“o�~Žù>ñ¿oÑݳÜ{–éîÎä}+Ûú¼+397Qv%Øbaï1±k“ƒNGÄvÔöcoô3O>Û”¬râl1õû*Ù;‹§û# {;štçnñMŸ ì^¿ÝO¢äüsk³#*;#ÈÆÊÆšÊÝ�²Óíõùæ`fãß&&~DY0I$2Ç}Õèö×SöFvO1êNÓã[!Ïø&æ}$Ð죭“cdÇdsãeâËOX6�¶¾xs03 ºülìâÈ‚ûá–Ëî¯{¶z£°:7²9‡Rö—Øq}Áw3é9‡el›&;#Ÿ+ZzðÔm°'‹/2¯ÆÎž,ˆ/¾l¾ê–¼œñ¿¶Ëç››­º /Ŷè°0k“�‰6ë�íòäÆÒñŽ9…>\Väì¶9¸8ßRß«-ž´õË�#ÐÝÁä?ÖuHp ÿbó}­>¬Z�=•‹™ø³n7Ûl¹qtÜoA‰‘—“ì6ØP]%´’[}Öúå¾’èŽÞò7Ÿk:ä¸ÿ°ù¶×ÒHµ<{+ 'ÇÅ›q¾ÚåÉ�§ãz Iò¢¶}†Ã# Ém$–ßu=vƒÄŸ ü–ó›¶uý+âÿUïû?›åc˱Ùטúî3Ä4XöI~O$ç|Ãm6ášëÒ�#a•2ó$‹“ædcãË5O�ÃÉÑÞ>Ù¦ìÏ//ãÞD÷ _—‹Á)‹.gDpÉ©,uÅ÷i÷8™½�·Š(®­ómñáÕ[÷ŠÇn¶ù �6ùƒxð1ÒÝf§±<¨»Aßݵ oÉÆáµÅ—/¤¸„µúvÁm4Û|<\®ÈÚEe—Öì�¶<:Û~¿¶Íue‚<»åûàoÀ÷LtMšžÄòší~vÌ5»+†×\¾”âV±Ò i§Úâcev6Ò+,¾·O¶‚-m>·¶Ýue‚<»ì/ø·ü4~*øWƒµü��Žykä®/èÍ®$œ‡GtÝÕ{{5sG™‹Á8êÙ!ç{<ž|·Cȹ.5òÒü<,Í~³K™—I#¬|||Lx1q`‡±ñ±±â²||xl¶8`‚í¶8¡Š;im¶ÛJ[m´¥)OD€¡†xbÇÇŠ8 ‚;!‚l¶(a†+idqE”¶ÈãŽËiKm¥)JRž”wòaƆ,|x¢ƒ£†!ŽØ¡†­¥‘EVRÛ#Š;-¥-¶”¥)JzQ&öGô£æ7†=çPmºƒ¼¸Ä;<9bËÉâ\»>†TSCZØÖO,|Gé_3:£kÔÝÑÆãÙàONGå6cãòþÈd‡éãr~¸’¯×l±ï¶Ï©Öɉ›µƒ*) ºè묾Wø‘ÒþeuNÓª;ŸŽE²Áš<œŽ1Ê0¬‚]Ày�ýLõùù·á_lx/Ý;¤ìØ¡Ùëò¡“w×ÜûW�ñþÂá÷åK�‹»ÀŠùg“W´Æ’*ñÖÍ%óàeR´¥ócß�“‘_ÿ›v¿ƒ]ѱê^ÌŠ–T2núÿžk ’-`q ò¦ÆÄÝàE|“ɬÙãÉaØëe¾ù°r­­)tÐ_�“=Sß(_}ññoä&GPv¼WrŽÊlÙïz7»µZ¬¬!ÛÜ/.2'ÆŽIscãüó‹W;IÇdÉŸ'O‘“¶I•®ÍÖì3´µ§ÍAs`e¯Ã�•2ùoàLs]ÎÂ]—>ëÌ)zK³rr&ÍËÌÈåým‰¯ÀÃÛlv÷_>ÇmÊøNfŸs™7ºê}ïe%¾¾¶Õb?Äß“ryQàßPó=¾Âý—:àØRôÿdäO‘•™™?.ëÜl}®Ï;2ŸW7mʸ†V«u•}+u´ÈÙ_o­}µXuñ?äÌžSx=Ô|Ëo°¿cÎx>½AØùÏ•—™?-ëì|,}¦Ç7/ú.n×”ñ ­Vç*úVëi‘±¾ß_ͪß�‡o3îóËã·Ç>ùÝm¢ÚvUœN�mÝW_´ÕìvßÛw¬e¯廽ôŒL ]&aÛ¯Çåpkþ„uÅ×o±­§ºÚÛ%ýCt…ÑçNÌ|ü~Õ$¿{:Gõ Öˆ|äþÓo!?{ºsõ#×hüâ~Ó/ {zwõ)תÂ?ínçÞK¤ÿ±Ü·œ”r]`SéN”í"{?ˆô×Lðí¯;ì~q³·WǸî¦Èé$ÒR;ò33óó2dƒ_§Òj0a“+;?.Xpðq"’yäŽ+.º™7§:o²ûÿ²8·RuÙs^̶×èô:Ëc¶ëëm—O—ŸŸ™‘$8:�6¯;ò33r¤‹;åšK#¶ë©“zw§;+¿»#‹u/Qq=—5çÜÇ>šý‹YlvÝ}m²é²óóó2$‡S¦ÕâG~FfnT‘bâcG|²ÉdvÝu6KÄÏ»ãÍÎòâ><øéÂrù·brÙd›ÙK«‡¡âü{ øiºæ|Ó{}—âq¾! ‹"˲ræõ­ÒI<M•><Ø1ñqðÿÓŸœ[QÍù<nÎò«kª’œ«´åÅ’}_ »k‡L}Ÿê\løb›Q£Ç‚I1gÛI ;mÅ—Ët¿vÅšÝ|¶øÖø¦êoøÆ³—òu�äÖ×[ð§³dÆ’mo®Ë�ì8�VcçCÚ�42I�6Òø¡Ún)|·Í÷|icÀÇ�OÆ×ÅOSø-Æuœ¿�èìo&6ºÛéÊ;2Li&Öñ*ì±-‡aĺ»:(¦Õhñâ¾Ly¶wÅÓomòÝ/Ýñå³Ðˆ?†>†ø±ëJçãÅ«ìÿ*9ÖŸÚïŒýt7äâA|X“åõ·S[—‰êìMž=&šŸ™±äqÇ•²¾¶cë°µ½�u•Ö'g@3çÏ€]!ò ÓY½gÚšÈõ¼«O�²Íê®ÖÖáA/-ëQ™¹úÙ/ºìøÞÚ\(#Üéå’Ìmž4Vú]\8™xÚeæ÷ƒ�3çWRåõÏgë"Áäz¸6}iÙšü8%å}oÈó#Ç÷gêæ’±]�¢ÚI…›mT’[�±ÇŠß¶,˜qrqô×Í¿úoÎŽ¦Ëë®ÎÖEƒÈõpl2º×²õøpKʺã‘eÇ®v®këÙÚ-œ¸PYµÕÉ%¸ûx­ûbÈ‹'J<ñðÆÿ‘NŽÝô��ü7k ¸{ ø'`ëðð"ìN¥äÙ–bÝ,ëÎG“‹‘‘¦Ï¦F¿¹xß�ƒµÇ†˜Ù°Ï�ucVïåW‹=Ãá·vrÞ†îýš^aÅæŽl\ü %Ìã\Ã�çVûô|χmäƒô¿ÞãÇ[¡’±Å‘�5’âåC�™�‘�|^MøÑÛ>$w(éåÑ[§åœnHçÅÎÂ’\¾9Ëxîmd»KÌ8†ÖX1¿Kñ½Ü1]X¤¬qOÖK‹•>f>Fwð?³x.ÎIíãý�Á6y¸Øß¥¸îÛîÒÁ'¥>®Ç+&Øòñ2"�]X€Úvÿ´­m­.¶µ¶ëkJÛu+ZV•¥}iZVŸm+J”­iZV•­+JúÒ´û+JÓòV•þkþÒµ¥iZV´­+ëJÓì­+OÉZW÷+Gõ‚yñg‡'iqòqåŽ||ˆ$¾àžé$SC,u¶H¥ŠKiu·[ZVÚÓÖŸjÏÏ‹ÿ'«å߃Ü{-•Û>g/ƒ…vdÙ±fl¤ì~¾’þ#ʶ{_§év6W+ÊÕ[»Ž+ÿ>Ü]œ5­kî¥Õ²+ã�Èû¼ªðâûw?:ìî[•Äáâ}‡,ù1dg_Øi8§)ØçÛk\iy.v«ô¼Q_ùöâì"­}}iZØõñËäm|¨ðÏ£{o?:ìþ[‘Å!â]…$Ù1äç_ØY8¯'Øl)k\i¹&f²›x£¿óíÅØEZúúÒµ¹+ã_ËüãðKÆ/(ä¾ ·ý£Ö:Ûùý¸zyôÈ;c‡ægp.߯Ñêr³vY8œs´8¶ÞÍg¾yn—_l2{«ïõoÛw›ºÞs?µ_É/ÞΑýCu¢_9?´ÛÈOÞîœýHõÚÿ8Ÿ´ËÈÞÞ�ýJuê°�ÅGû[¹Ç÷’é?ìw-Æç%—GdoÁgì¨ñ;÷³¶?_=¦°ácödø¿ûÝÚ®îËX)ð½û3|`ýíìï×_d­Ñø2ý’> y,/ì‹�:ÖêK¨Ž¯€?ÍÖÛ}·Y}¶ßeöÖÛ­º”ºÛ­ºž—[uµõ¥ÖÝJúV•ü¯ùu´º•¶êRën¥mºÛ©JÒêVž•¥i_²´­òëmºÚÛu)u·R¶ÝmÔ¥mºÚÓÒ´­+öV• �ÇÎ�ÄÌþ)ó��”þ>ñK!ñ“°·1W—ñ�,”ÃèÎ{¸È¶?º}Â+n³WÖ¼Ëc7»S$~˜zÍ„·k+n4wë#È„·Í?ÅÝþ2s ï&z‹Û�ã§;ÛÅü.ãz,kþåÒüão‘lt‚Ü­ºÍW]rý„´ý}ž˜zÝ„µ×RÜxäÖÅ4'þh¾/äñ›˜gy/Ñ>ó±åÇ—iÄøÅmºŸCUeÐís®õ¿"ìL{-�:K¿;NÈ‹Iß>ié6¼s�KL}— 講³4Ü£šC%–Ïï±d‚Lm§ã[u>†®Ë¡Úç]ë|÷bcÙe™³�øVü25ɳ�ù_ò]Å%Š #ÁåÝwáÆóLy䤱ǙǷ>JbßXòqke/³2î ZY-/¤8ü†¶últ7LÿŽq¾;ôn)Ät:^-Å¸æ³ KǸ×Õàèô6·Ì]v§M§ÖA‹®Õë01b¶8`‚;"Š;im¶Ò”¥ìãüCÄôzŽ1Å´šŽ5Ƹþ·M ãÜ[…¦Ñé5ìxñ5ú­F§]6·[�‹±C1ÙQÛKm¶”¥(—&ƒ�èx¦�QÆ8¶“QƸß×aéô<{A­ÃÓhôš�|bàjõ�t8ØÝv4VÇ ÇdQGm-¶ÚR”¢qÚM&›ŒéµsŽj5|�qý^“C¡Ò`bj´ÚM6«, ^£Q«ÀŠ n¯[ƒpããÑC–Ùe´¶”£í>»ë¾˜!ÿˆÇã[ ºù>@º_ŽÃ‹•‹6·MäÞ“[Xâ³6Éð´œK·qµßNßvlYsC©ßVîº[$ÁËúú{«¢·óùñå‹6¾ÿ:º‹Cd˜Wë´þFiµÔŠË3q'—QÅûV [e÷fâäË«wX®’é!¿+èÙHsòo‹Ï·Ç®,ºûüçê- peá߯Ôy¦×R;-ÌÅÈ›SÆ;R Ye×fcd˯wX®¾é"¿+é[Hsò.†¯âžøŸ‡´:ú_’Žˆâ·ËÙ�[©Öi¼¡ãÜkŽÓ#;�u>¶5ú^äή¦ß¿æïº80w3cå]^Hò'ÉÄÁã´¶X{¢vŠ"¿�”_ ÞDSÈïŽO·YÛ lîUÕúLŽŠæQãG5—alzªè´¶®‹c“-.­$›6êý•õ¶–|@÷Ý<�øÿè�Öf~6g'ë­$Ý-ˣǬ÷I‡±ë ìÐñúl$ȾI&Úmxºm–LžêÒùs«_³×Ò–üB÷Õ<€ð ¢·Y™Øùœ›®´³t¿-Ž­Y16=etz-3¤žù/Ÿg´àÓlr$÷V—Ë›u~ÊÖ´¥½? ^Qdy}ñ‹â?onvy;^i‡Ö¸ÝYØÙ›Mõœ‹‘çsΙÏÍêíï#å9ßV\»7¼÷ø+#ºÌŸLŠÁ¸Šû½Ô¾Ûî곦N—ºŽ×¯.?Š�“ŸÉëº?Vü•ƒü›þ-¾BxþØþÀ·ì!ä×ñnòûÈv¿ö¿|½çú˸ýëØJLª1X:±e`¥|`|]ö¯ÈÏgI-û Ð<+?ÞÕí˰i-˜×ÝdyVpŽU¿tÝóÝ®,–Ýí¯¿U‹%2²é_v66_K~7>6;3ÏþÇ’8/Ïá]ÃsñíìþÔ®$¤RV8²ìá<*<«~é¸ç[LI,º´¯¿S‹%¹YT»ß‹‹™ÒŸ�ÿ�ÎÌó󱤎 óøWEðìü{{7´ë‡I) •²<«8_ �&ߺn9ÆÏKn­+ïÆÕbÉnVU.÷âãeöóá‹ásµþT»NmÞòmÿXøƒ×¬|Nàî|<\xö»Íœpãl+ÔÝ?]¶n«oÙ[=~TRåæMV·‹kò#ÍΊy§Öë6–øÝãWLø—ÔÜ{¥z'†kø_ãöÖ{âÆ²ÉvÜ‹y6>.>Ë•òݽl·/‘r­Å˜qS'2zÖúÙpÙHà†£žŸ�þ=õ'Œa¡ê•âþÂô6}O»âÙmû-æÞX1àÏ伟iu´Ëßr]­¸±ýã2zÝ%ÖÇdvûbŽ8ìž?@xùÔ¾0õ†‡¨z[ˆàq¡³ê} k-“e¼ÛË<ü—“m.¶™{îIµ·?¼eÏ[¯ºØìŽßd1Å–Šô7At׌Qú; zï�õoUð-\:ž3Ä8Æ-ðâcÅ–Ó#c³ÎÊ—+oÈù&ã"—dì¶Û,Œ½¦×6I2³2'È–InÎìÐÍ ¾ÐO�_Žî�ù ê9øfa[ çº ]†GS÷« ÉùO[ò »a¾²Y gij‘q-¬Ø±YµÓdKd9°Sݘ¹‘ãfcè÷�¾ôÇž�U7ìl;t|ÛG�Ÿ“Õݯ«Ã²~M×›ì»"­ÒÇgÄ·Åv²bÅf×O<¶A› ´º91³"ÅÌÇÒ:¼ é¯Mz++§»ÓGf!ÒY¶Ùõoið࿞tÿ/ÙbC»ž=•}ø÷ltk°±­Ýhç–šýÄ8ÐÖJG•�…—‹\—•Þ(÷G†ÑÉz/½8ÕÚ.Y¢ºÜÍfÏéò¸¿7âùSäE¦æÜ's.>-7|_wLY)•Ž)ñçŠ\\¨±ó1ò1â¯ïÉï{Ä>ßä=-Ý–¶åë¶8—M“ƹ—Éšxµ<LJm¥ƒ›Ž5¸¦5ôŽJÙøóÇ..TPeÁ‘�T¾yøäÇ_�‹Çï xïܶ˜_SiÂù¦®<™¸Whp©²fƒUÍøFÖxbûî«7éVÉྖeëòì“*8çŽû)­í|kóK€þ�;®ò‡ÅJtO;Ûý︼[ÃÑñ æÌ“ÌÎUÔ91dbu¾ö(ã¦<¹™<_[~‡>ûb’ëlÃÂÈÉšüŒúÖ³¤ø+óbÿ%]øöåòn¬Ê�'®÷–[x×edñì=lš<Û©d²R˜X™93_>w­g3ðaæµþIxËN™æ»[²ûgÆÌ]7ž|¹1íËäÝ[“N7^îã¶Ë1ïÊÉãØšëô™·RÉ/¶˜x™ß>w­m)ü9#Ù>yø/®á=‹Év|‹Èß®Ðu_ll÷rl3¶ü»ˆl1ö×tÇelwYqÍM¶Ï’ñŽ=—ªÙO>fNÓ3udzsòée¹¸÷Ißwq¿HÂÏšŠ;l’Å…�Ú|Gü�Œýq´Ì’–Á�¼Žyæ›A—5ÖcÇ™5øÙ7Å�“vV'~^~2püÖëªv—Uàaây5ÖzIããÖß,xXý¥Ä°ïÉØÍ×[L©)H1÷‘Ï<ÒèræºÈ#Ì–ül›âÇÉ®N'¾w~¸ßɯ�“s´ãºl?4zWG™“Ò¼¦ì¼?7añø²%Úmú+˜î³nÇÖehùÒÏ‘ÇçØË .A/Ô·+ ?q÷šù¶üýN~v«k…—­Ùë32uûv~<¸™ØøS_�™…™‰=‘Ï�—‹‘ÑÉöÛ}—ÛZV”­EØkóõ9ùº½¦^·g­ËÉ×ìuÙøÓaç`gáÍ~>^n&E‘äbåâäGtrG}¶ßeöÖÛ©JÒ´AC?;U�›«Úaeë¶zܼœ Ž¿?\Lì ì9¯ÇËÂÌÄȲ9ñrñr#ºÉ#¾Ûo²ûkJÒ•¥Uiò¾)Éø')ä¼›qí׿|7¸âœ»ŠrMff—‘qŽOÇv9:�÷ßé¶0ãì5;­.×\l¬Yã²h'Šë/¶Û­­)Ò_Ž_‹~þùæ’GÃqîà�+ƶØúþÄïA­ÉÈãºI½˜ùy\{ŠáRL:ónwn·&É©­‚xbƲhoÍÈÄŽx/“¡¿¿½ãçç/šÞ)Ü#§xÞÎ }Ü»ÍvFF‹U‘[ ÊÈã¼_ ’bW˜ójkò,ŸîÍX±K ™“ãY>=fèO€ßçÇ/–Þ) Ü'§¸æÎ {Ü›½tù=Lõ² ¬Ž=Åð©&%y‡5¦¿"ɾá ÑE‹°ß™>5“ÁY{#ðýð£ß)ÜþMä²í:ƒÅ°².Êï}†–iëºÏ�ÖúõÇO`æ[1ì ºRÚæÏY?Fq¬+é•�t™ëµ›K<;ð‡Ço:Ò.µè–<¸ðdæ×k÷}—`v×"‹qÍy-¸ØÒìeŽì‰¯ÇĆÌ}n g–ÜLh,’ëk:ß<9èO úê¹èÞ!š ‹0¤å\»gX6<çží0¢’;7<Ë‘[��.Ç"Ûç–èq¢² ~Ö’ÜLx#º¶'Gâ�‡} á�]Ã×]Ä!ÓÑfœ§–ì«ÜsͦWÇfã˜rcã˰žÛ¦–èq¢² ~Ö’Ü\x,º¶ÖÍü,ñÇÀî–ÑtGŒýw¬àœ7UJeî64²<ÞeÏù,¶R݇4ì^W$Vlùw+ÙV”¶³ä]ôpñl‹ ,\ |lXvÝ´m¡mHžŸ%þp��ùVí`ÔêûCŽEŸÊ:7ŸçÙ$2ðîÀ‹ ë1ðóö˜ÙYöð¾_dV`nñí‹"Ë ¬yVA~^ñè‡È‡ƒÜ3Îï·Ýg¶‡[­ìm y¼—¦yÎ]%‚N#Ï!þÌl|ìÜ\l¼Ïà(¶Ëp·8ô‹"Ûñî³&ÈnËÄÄ’-ùð‡†ù×ãÞû­6°ëu½‹¡�3’t×8̤�ÉĹÔXwÙ��››‹�•™üåÙnã‘OmØ÷Y“d7eâb_>>N>=ºÃä»ÄÞmã—`É]' ­næ=9Ø0R·eõ¯oéu›<^%Ê$‚‘MM§ž›)õû¼ ÛKótÙ¹1Á&6_ݳ1ë5æG“uÿ-äüši6k˜pÎA¸â¼«�m »g¢ä\a‘ªÜê6×þ|ºíŽ$�Ëe�¾ÊÑ]7-âœ�‚ržIÂ9†Ÿ7�rξÛñ~O¡ÙEôv^A¡ÏÈÕî5YÐúÝôòõû Y"’žµ¥/²¿j»NYÅyå<“„òý>oåœC{¶ã›C²Š°lt»ý|ú½¾«:Öµ‹/a‹$R[öú]mU vïTsþˆí.Ãé~Õã¹O²º¯™r.ÎxæNFmú~OŶ™:}Æ{ fNf¯i‰ff-Õƒ/yñ2á­“A,‘_e÷yÇŸyö;J{ð½ùüîÎüñ‡q›[uÝ£Âu=§Ã£ÊØËf4·­³oÓò-f§YZ] »>OŹm™yÒ¶ß÷n9m+öÉ�ðÛwåÚÝï?¶¹rS_ؼ?WÚJ<œúÙ‰‹É¸oè^E�­×V•¶]Ÿ&㜦™ä¶´­1øý)_ZRž’[ü7½÷]nw�Ž\»é¯ì^!¬ìî%N}Öbãòn™ú�ëõºúÒ¶K³äœs”C“<”­.¦>‚ž¾´¥=&íø9<¡ÌÇåž[ø[·ÏÛäë¶ü{�y?׺Ë1p) ÒçñýŽŸª»ƒ7';Ö=¥w<§’p{1á­%ƒèi§ºŸJï_­5¤ÁRýNè\ÇÏÇíWòK÷³¤PÝh€ÎOí6ò÷»§?R=v€Î'í2ò÷·§R�z¬#ñQþÖîqýäºOûËq¹ÉG%ÑÆzžÂ9ges/×ÜA²å\Óšïµ|c‹qÍ>=Ù[=Þ÷u™³[…¿ÏÏ••=¶Òµ­-·×ÖêÒÚV´ôü+†r®ÆåÜk�pmÓóa»ÖñÎ3ǵP×#c¸Ým²£Ã×àbEëmµ“#"[iÙe=nº´¶•­=7 ᜧ±yoà|E°äÜØnõÜw�qýTÈØî7[l¨ðõø‘R´¥eÈÈ–ÚzÝ[l¶ž·]Z[JÖ™S¤:K´ü�í¾¿è®’á›nÀí^Ð䘳ëùç‚,¬ ¤Ì¶Ëò&­#Èßl!·/&–Gÿ~0¾5øG€�[vFκîWä?`k0oínÀ†Ï­‹¯ŠÚÙ™]ðI2 ‡' …irémÓMu±äîó£¦^M¶G ü~1~6øO€ý]vFÏôw*òŸëpoíN Ÿ[_·Y™^pYg‚œ.¦Ê¥·M5m�#u�2²)dqábaZÉðññ7ÖÞ=[Äñr5œÓÈnÊÅÑî|ƒí|;&“sÈp1$®/ àòæàë¶XÝgÂòs²c×}â2öÍ6vLPß=˜¸½iuÔ^sù˜øÜÕyéã¦vó„éq¿á/Ó½¯"ê-¤WS+–ëì¶ÌîGÔû9iñæãr¼|JÝ©úÞËp·–Áu'ƒ|ï«Éß–ÿ��_›Þ?çmx†¦øCõ»iÈz£eÑãdòŒ[c·/Õû)o²±dâr¸1}uµ–è­ÃÜÙÿZis)/(~Zþ?5žntv׈ê`ÿ„7Rk¶|ƒªvQVÌ|žO‹e–æoºÃc-ÖV<¬NU/®¶²Ý¸›‹`¿ëC�.e%ã¯ÍŸÅçù=ñqô¸ØXGu[Þ}ãg+š m$»–ݯ‚íßWmv; ½ešþ!Ûøšœmv\Õˆ ”íœÖäY®û¬õËÍ ¸òËñIðI|3C5—G,2Çul’)c¾–ß‘ßmiuµ¥+JÓÒªÿ¦†li¥ÇÈŠ\|Œyd†x&Žø¦†h¯­’Å,WÒÛã–;í­.¶´¥iZzU©¡›iqò"—#Y!ž £¾)¡š+ëd±Kô¶øåŽûkK­­)ZVž•TݽÑnø¾ïsÆy6›kÇy'Úì4\ƒ�ïuùz�Þ‹w¨Ë›_¶Ónu;qóõ›]f~<�dcÏ“C5—Y}¶ÝmiOæþOäù@ …~¯ ~¦»É¿ö› {±2ø÷~ð}]1m­×G™^ö†l¹ÔüúG ˜|JÈ¡¯­¾ée¾ßJÖïYaþŽõ¤ºÏ#|gÙgGKðót=åÃuÔǧ֖<èq8dfK—ùi-øZÈ¢¯­=ÓÉu=+[½ewøkûÒ’k|‹ñ§e�e/ÄÌÐ÷‡×S•–Hó¡Äà��—&_óÔ‹L/lqW֞餺ž•­Þ³óü>IE½èÿ,|JÚOƒî;'ŒwÇûÎæY·;miñøøG1ÀÖé&º¶bñþ¹ë ^D²ÁùµÌäõú”¥×Û[åÜ•*S©ž€+˜ùøýªþI~öt�ê­ùÉý¦ÞB~÷tçêG®ÐùÄý¦^@þöôïêS¯U„~*?ÚÝÎ?¼—Iÿc¹n79(äº8À #~ ?eG‰ß½�±úùí5ƒÿ ³'ÅÿÞîÐýwvZÁO…ïÙ›ãïog~ºû%n�Á—ì‘ðKûÉad\�Ö·R]Du|åy×â=™Ã9O^sí»•pžm Úñ~WÆöÐÖ}nïA»Ã›_´ÖåÇmÖ_ô²±'ºßu—[}•¯ºÛ­º”­<ß2áü_°¸Ÿ%àœÛG¯ä¼C˜hö|o“qý¬?_]¹ÑîpåÀÙë³"õ¶·A—‰=ÖV¶ÖÛ©ëëmiZR´ó|ˇñ~Ââ|“‚óm&%âÃG³ã|›�í!úúýÎ�q‰.Ë]—­·V¬Iî²µ¶¶Ýo¯­µ¥iJÓárŽ/ÆyÇä\/šqÝ/áÜ¿E·âü³‰ò�Nÿ�rŽ3¿×äjwÜw‘h¶¸ùz½Ö‹w«Ë—/&)qòqå¾9,ºË«Jð{ÂÃÿО+y)Ï;מïâîÝ7å´Ïñ‡…ò]e’âõþª¶bì±¹GaY,vëùcñ½Œ²`jäŽ5Øöb[µúTÏÉLJQÄß þ z;Æ_"9¯vó=å�êÐòšgøßÃù¶Ûñx¾¶ãìqù/9¶ZW –öÏ’ü-\¶Ef5¸´Ú}/¿Ï�§Š^|tŒþCs^ëæ{Ë;ƒU¢åÏñˇòm·âð-}mÇØãòNql´û—,çü{>KðµrY˜8ÖâÓgô¾ý>ÓO¸ÖeË�“�5—Å<2]eöÖÛ«EY¾eøÓÈü?ò{¹|säÒÏ™“ÖœÃ/[¦ÜdErN³ƒ}ÁyEÐbäeãâÉÉ8vÓ6H-–úãK=Ðß_|wR•¦ywã¶ÿÄÿ${sÇþC,Ù“uç+ÉÁÒí²,‚9yÙCó„òI"Å–||y·üKg‡—,ßwݦ–øn¯º;©JÖ|¸ñã}â—‘ý·Ð<‚IòåëÞW“ƒ¦ÛdY Rò!²† ß äwÅ�,øøóoøžË.Xm¾ï»Í-ðÝ_twR”ðüœxU¸øúóƒ¾|^Ì®Ó3Œp¾U]¿UòµgÈÊå=?Ì1¡äÝm¸ÉÚÝ£ãØ­æ7ÙC¯Ýσ‹f |ƒ?Ö�U¬�pk“B€óü,½ãìÌò£ÆÍŽÏ&﯋Ã{LJi½=páû¤²ð.ÊÙúÖïèy9?}â‘}”¯¾Ø¾ÚÓÛOYX~Nçô›É�v9kw?ÓúzÃgÒº^Ø»?w»óe“ëqh½)O¶–}µû(•?á«î_Iü–ñïa²–¿R.%ܼKQéëŸFùxGaì½kwæË/×ã}”üêYö×ì¢{ƒwÈÒ=QæW‹óèq�݇Àü€âxßyöò}ïöÉãsõ×aO÷;®þ�¡âŸÚ£Œ[õ,·úFçÛ}¢F˜*W X¦ª×¯.?Š�“ŸÉëº?Vü•ƒü›þ-¾BxþØþÀ·ì!ä×ñnòûÈv¿ö¿|½çú˸ýëØJLª1X:±e`‚|p|}vÈw}ëúψǛ ë®7v¿yÜý£÷+æÕp!<ó[1K%•ÄÌæœ¦¸sãi5Õ¯¿*håžú[‡‰™4;ßññà—byíÞ8]wÆmÍÑõ÷ûŽï·û"Ü;¦×pŽ+<òÙ<IØyÔWl}6 Õ­ùÅ4÷[÷\L©bÞÏ�¿;Ï>ïÂë¾3nn�¯øïÜw]»Øôúmw âÓO-±cÁ,–W'—òzâM�§Áºµ»"Xåžëi‹‹•,]wølø§æ¿*~LӀɲÝð�zÆ _+ò´4ØQͳÓq¬Ü¹¢Óð^“Ÿ…�¢ƒ²»\˜5rgG>6¿7e~6u¸ÀʲW¡z'¬|iêNÒ]=Ʊ8·àZl}FŸ_�åfIe¾ý†÷w•ÿ¥y&ÿ:é2ö·ÛIròæ¾K¾Û–ôŸKuÏ�=[Ãzs©øî'à¼Q§O®ÆŽgȺÊ{ó·|˜¢†íŸ Þg_&^~d”ú¹ysI-õ­×U`ÏJt¿\øõÕÜ;§º£�bqž Áõjuühá¶|‹¬§¿;q·ÉŠ(k³äÌÛäËÏÌ’ŸW+.i%¾¾ëªµ£¥:Sª|rêž ÑýÁ´]mÕ=m¢ƒŽðÎÇ`’-v£]’äÏ$“äË“±Ûn6Û™³v;Ù²6MŽDùysÏ•<²ß—ÙQ”ÙDÎ�’ÿ�»ùè<î¿Þ[¬ãý¯Ä¢ÙnºG³2àȺ^ÊòcÆ®N»e~»/'†rë0!ÄÛcû'¥,¶,¨â¿'ÓA¾D< ëÿ=ú?7�ïl×èû3‹Ç°ÜtçcÏ×dpÞQ‘7Þ0³¯ÄõÉÊâªÌ±v¸¾Ù­¬vÇ“uÊÅÆºÍù ð;€yéÒ¼yn¿GÙœb<ýÇNö6D_?äùãýã :ìO\œ®#ÊlÁ‹k�í–žËcÉŽ;²qqî·�&ßýò}ã~ߣ»f;¸÷-ÓÝ�ÈúW·õxVfrn¢ìK°$ÄÃÞcb×'œ�ˆí©ìÆßèfž}¶ )XåÄØcëö8U²vïRöDvg5éþÖã9ü?°ºû}™Ç9OØ[eeÃØaÝOl¸ù]&.ÇW°Æ¾<œ<Ì{äÆÍÄš9á¾ø¤²û«Ñí~¬ç�#ØüË©»;�fñ^{À·™|’èó­§ÔÅÎÅ­.²|iì­Øùú½Ž,‘äáeÃuøù˜“G<7ß–_Z÷»W«yßIv71ê~ÍãÙ¼[žp=æ_äš<ëiõ1sqn¥lŸ{+túÍŽ-ñäáåÃuøù˜“G<7ß–]Z–¼œñ¿¶cötãÌm‡ƒždõWtI›“ËØÓ€÷&x¾=ŸSó ¬;Ë®éýg#ÝÝ?”\{�뢮Wbaábý|>OÀÿÔ÷kxßbrñí×î2ò¬›xæ·gô$ÎÇÉ‹kß7>:‹Ë%¸G|iyL�W¯Þí«'’<{E¯†¹ƒ‡…�Y°¹ ’�× ŽóÝܰÛ�¶ÉȲ|Yá–Í…"®f>D{>ù³ð›Ô~Zù)Â{ßMÊoêÍ~ómY<�ãú-|UÈì <,jͅȸmô†¸\{žnæ†Ü ¶NE“bÏ ¶l)Ù�OË€Ÿ"?‡ÇÅï�ß0ú�ÊîaÉöý{Me¸šÏ%8/ÒccKäf‹�bEº~a‡²Öì8W%ÂÇÆ�OµÙÛ�±ËØqèñ±ñ¤×σMý·ê~¥ën‹ë¾)ÔÝCÃ4}×\#W£ŒñN=‹÷]v»Ûäžy¤ºû¥ËØí6Y³I•��•$Ù¹ù“K““,³Ë$—v+¬:¿¯z_�qž¯ê®#¥à¼‡k­ÕñÎ1 Å¦.»_�õ$Èž[½k~FnÇc›<¹9™™Ë—�—4¹K4’IwazǬ:û¦8ë«â:^ Àx~ºÝ_ã ZbëõøÔ’Iç–ïZß‘›±ØfM&NffEòåæåÍ&FD’M$’]Ü>µë>¼é¾ ÆzéøG뎻áºëu·ÉžK®“"µC“ñø‘_wòωk®‡Ž÷dvðÞÇû¼S´8¦ª/Ð{I¥¬ŸF)¹· ­–Ãvú͡ɞK®“"µ@oñwø‡Ãû§þC¸6® ]wmÏ�ÐýñXoÇëvOã™›.¤å²C~Mû=–Óu¿Øé²å²+10±ø¦ºÊ×ëeþ|`‘»G 0ô|f÷}þ;yïâ·j_±ƒS©Övïã<¯c•mdÅÂàý‹$½uÎs'ŽÛo­öáñ.U™5¾–ÖëoŽÛ­üëhÝoŽNã¿¡¼äñ�²nÍÇ×ëp»WAÆ9&v_­qpøaÝ/^ó Ùí¶Ûëu¸:ü±øOÌlƒa›®æ]½®è½Þ³ k&«3È< þ–ÅÍÛÖ—Û~«ŠïyÆòìy©u—Ë«ŽêR’[eÖÚ ²Id*Üps?µ_É/ÞΑýCu¢_9?´ÛÈOÞîœýHõÚÿ8Ÿ´ËÈÞÞ�ýJuê°�ÅGû[¹Ç÷’é?ìw-Æç%—G7ïÃÕñ—�Ó½{¯ó—¹´0]ÚÝ­¡’�¥Úëä®o\õ^æ ¢—šÛL裦*í=|•®4ÐY[ ã2YK2+n×/9–üüsãõ/Àó;·tX÷ö�gé/¯LêvX—ß—×ýa¸Çö]ˬ·.+,Â噃%kÐÛt�qÉ,¶ÉéM–f=“#ø#øëÇên�æ_nh »´;;I'ö›Ôì°ï»/€uŽãÙw-²Ì¸¬·“öV •¬Ãm×ÁÇd²Û'ôÙfcÙd?á£ø‚Äñ'¥p<ÛïÞ#X| ïþ/G\ê¶¹–dËÓ}É0õ›m<¡¢†Ø4Ý•Ú[fÃo$òäçktÿrÖÒÝvMÛÌ\™=$v‘ªU ~!Ÿñ¼lò×½øV¿î½eåmy4È‚+½Ñi;‹W—‹'gà[egÈžz|8ÇñóÊ<~ðáÚÿºõÏ“¿§9nT0ý°é»sY“�'caûiYo‡“]¶ÄÞE|·ÓëfgçG–Å�JRÿ<~Aãï”8ýÝÃð>í×^Mþ›å™PÃöæí­fN4�‹…K)t²E�É.Ûbnâ¾[­úÙyù±Åe"Æ¥)\wâ±øïÇñçÊž3æ§ZqË5ÝSåµù¸ý�§]ô5ñÛÊ­&ºû"æšßKsì¼m~6>›¾#5ü¯€fì3aöÏŸ¼ßè÷›œotÔ­Öaha²Û«e”¶È�~$þ€‹YÌúɽ>¾û#åz}ÇNóœÈ1`ƒÍ¿–NUÀ²3'Š–Ë™¸Ýj6û¨+|”­Öâ顲—VÛ-¶Ø’þ$Ž�‹WÌúÉ�>¾ë#åz}ÇOóŒ¸1 ƒÝ¿–þSÁ2s2"ô—7oºÔm÷{å¥n·O ”º¶Ùm¶AÛñ�øµ�~³ÄŸ64ø±ecgïüZì]¤Û\šææÁ›�¼íž˜À×é.­Ø–âë$×óÙ32쥲Vü¼hï÷ÛHý‘:Eá™ø 휎­ù8é< ÷?¡´]³¤ì>¦äžë½±m1÷|CeÈø¾š_²¾ï¿ö/ÒR:óûlußà×´2zÛäo¨uÿ¤íÕé»OEÏú¿�Öû½±çãm8žÃ“ñíeße}×fóÎ'¨¶:}ž²Ò×\¾ûC#­¾Eú‹»KuznÒÑóî¯ä¾ïlyøû^+ŸÉxî®ÿ²¾ë³yçÔ[?vZZ‘·á_î<ޱùoàœ.dyðùÒ½ÙÓ™™’KH«£ÇÓq¼nþ�gµ¥~¼“æôl8>ϳós+wú_JؾŸŠ|Ë>€õåÇñQòsù=wGêß’°“Å·ÈOïÛØý„<šþ-ÞAÿy×þÀ÷ï—¼ÿYw½{éI•F+V,£Œ”úG¥ûÈŽØà}'Ô¼w/•vcr ^=Æôø–ÝétóRIó6; �mc×hôzØ&ÎØæKíƒ l‰n¶(ﺙ7¦ºƒŸ÷ïipŽ›êÝG$罃½ÆÐñíT¿ÙY¥¶LŒÍŽÂ{l’Ü &“[�6nÃ.ú}<yg’´Ž;«L™Ó}CÏûï´8ONõv‡#’sÎÀÞchxþ«—RÚÍ-²O™±Ïž–ßf—K­‚lÜü¹=!ÃÂÇ–y+lqÝZlO‰ž/ö¯šEõGŒ]+®ÃØv/nrx¸þ¢]¤óâè´Zü|\�¿%åü“/;3ŒpÎ1®ÌÚìoÇÇÉʦ”‚ æ¬pßf—€¾õÇ€þ9ñN‹àU³mµ‹×�öW:—<]—avËÍç#ÊŠÏuq5Ñ[�³·I\=f4_$ÓRYå±Âzû€âÝ)Á«fÏa�Jï{šI‰&ÞóíŽ>=›ÎI— —Iv.)�.¿·Ë\=v<Ý$²[|ÒX¥á‡Ýá@ñ~–àþÍ–Âú¹ØÎL[1vóžì1ñãÝò<¨mºK±p©Lxñuøµ¾Jáë±áŠé%’Ûæ’Ýÿ|"éÏ�_z÷Æ^—ÕáÇ©â¸tÙs>aMV6³�v¯em1pìæ�Ì/Šl¼Œ½÷#ÈÊ8l›'&šÍV.³úa`âÇé¶ñ·MÈGñühÃä‡PdùqÔ:(+Þ}Æò²yîÖcävOKéaÌÙíiô+ÓÏå½ueÒçáWßÙZ¿¾bÓïÛ¯‚Þ|çüwGä7SÏåUé,¿ºúO�eOÌp0ëSöPê£ÊÙmaú�Ó;”ðn—?ÒøåÉ××/”ÈŸîYÀoœŸ�¼„ê‰ü¡êÍ,w÷OJñü™ùŽl‹#°ú�UVÇiѬ~™Ü£�[t¹ø5¥ñË“¯®^5)‘7Üb²6ˆëâVï=ür�È~–ã·çyeã'Úçi5ZM[ÿvtü2änù/SRì+cÞl9É›'wÄà�ï¾üù3õ°bV}×Þq ‚„² ÆÀüùsO&|âœ+�m-ÍìŸrqºg’E,˜ÿ~Éášürz›{v6ÚV´¯>þRºȯ¼‚átć#�ñ¾™Ú\&ZàYŸŸ�ÊúÂ˹†6&žÛ«mØÛO«Öeé>­µ¥ÖÁ´’Ÿm+ZWž,^*_æŸÇg•þ=ëu ï1ä½W·äÝaªÔK¬ÅÚìûs­&ÆìŽ­Òáçmì® <“œñLfeõ¾ݯÍÈ�êÇKë}+.W<®­NðýY}ñ_d‘ßtrGu·Ç%—VËì¾ÊÒëo²ëkK­ºÛ©ëJÓí¥_êËïŽûdŽë¬’Ë­¾Ë캶ßeöÖ—[u·[Z]mÖÝOZVŸm*ÿV_|wÙ$wÝ‘Ýmö_eÕ¶û/¶´ºÛìºÚÒën¶êzÒ´ûiWîÖlöZ]–¿s¦ØgjvúœìMž«k¬ËŸe¬Ù`OVÃ_�‹$YXYØYQ[$RÇu²G%´ºÚÒ´¥VÃxËÚw÷�Ž] ÜÓ]�vWktßZv}¸u�îðl¹‡ Óo¶x–[æÃvÃ:Ho�ì¬WÙ[+JVÚÒ–‚xóÙuînƒé>Ý¿è[7gõ7^sü¨ñëgÒÇÍåÜKS½ÎÄ¥¶V¶Çv&^uñ]gå²ë+miJÒ´YëãÏe]ܽ Ò}¹Фý�Ôý{Ïr£Æö}3ygÔï3±m¶ÊÖØ®ÄÌξ+¬ü±Ýem­)ZV‹µzSµøÇ|t×Rw� ®UÜ7¹ºÇ�v¿»;\LÚñŽÄ⺮_¡®f,öE66Uu[ˆ¾¤wÛmÖ_ëJÒ•§£830²h¹�Ÿ�Ú¯ä—ïgHþ¡ºÑ/œŸÚmä'ïwN~¤zíÿœOÚeäïoNþ¥:õXGâ£ý­ÜãûÉtŸö;–ãs’ŽK£Œëß�Wùáå΃CÊõsåt_RE‡ØýדXò¬ÂÚj13iëºæÁ#‹;°÷�ýÞøþ¶<õÓãlg‚ÿ«�JW¨ž]æÇ•MG(ÖM•Ò½Sf`÷ ÷Y‘nÓ[�™[x×_Ý“ÅnW=Ýcýb¬¸ó]§ÆØÍ ”– i^Ÿ|LøGwšÞSi5Ÿ[.OKõU˜]�ÛóÝdöáí5¸ÙžÞ9Àk“WEnW<ÜÁôe޲A-uÛ ¡¾’Ám+ßÃÁñ§‰òæî!ì�.ëÆÏ¿ƒ}«Üšý–>›c¨æ»ü�ŽuÝOÔ[m>ãk‹µÒsíÿÌÊÜcÏ….gÒl°ï—|¬[ëd¬QEQà qà 1ÙQEe±ÅQÛK#Ž8ì¥-²;-¥)JR”¥)E„QÇdVYVYqÙlqÇ´²Èì²”¶Ë,²ÚRÛl¶ÚzR”û)E‚ñÇ1ÙVYQYlqǶÙqÙm-²Ë,¶”¶Ë,¶”¥)JzR‹OßíþßìrãæGÅŒ,~?û³‹aàY™ÎzËS'yõ�þ› 2#å}e�±ÚluØšïuùû.YÁ§Üi1a–ËáûÖÊ9+KnŽÉ,æßË?ŒðùGàÏqq\< 3y¯^êïî^¸»Ù™.Mœ¯®°³öYzý~6Ô®^Ë•pÙöÚ\xå¶ø©>ÊÉ+JVËo·›ß,~4Áåƒ�Ãű0lÌæ�}«¿¹:æïnd™r®»ÂÏÙfkð1ðëë—±å<:m®›9-º*O±²úÒ•²Ûíæ7ÌO…xÞ{|yy ÑXOÓ]“…År;?£lÆÃÕäî#ÇÉäœ3S£ÉÜM�…¨Êçôƒ'ŠåfVHë³}•éu=U§+»W�¨ íºÏžnú³±úÿ³¸ÎL¸|“®y¿çœ2«dØ›¾!¾À䜘¯¥m­’ÁŸ¯ŽëkJÒ´­Ó­ùÆã¬{�vW–ø7ý{Í8·8ÑÏõŽøwOyƒ¿ÖKeöý¶_n¾ÊÒ´ûiZ=Ÿ\s}¿YöìŽ?5øûþ¾æ|_›èò"¾±Éߊo0wÚÙ£¾Ÿm—Å›�eÔ¯îVŒéâÿqÓÇo%üwòíMÛûz/½:“¸îÑY%°ßº§Xóþ?Ík©²kî¶È®ØÓIôiukJ[[ýkU¶Z�®»{ªÖo5qgêw:ü-®¯:wÑÌ×lq£ËÂË‹ßm·},Œi­¾ßZR¾•ü‹Iõ»-¾¿m­È�3]´ÂÅØàeÅëX²°³`³'"*ÝKnúsÁ-·[ëJWÒ«F5»Æ»m¬É�3[´ÂÅØëòáõ¬YX9°G“‰“n¥·V9à–Û­õ¥+éU×� ý¯Ú+˜ùøýªþI~öt�ê­ùÉý¦ÞB~÷tçêG®ÐùÄý¦^@þöôïêS¯U„~*?ÚÝÎ?¼—Iÿc¹n79(äº8À #~ ?eG‰ß½�±úùí5ƒÿ ³'ÅÿÞîÐýwvZÁO…ïÙ›ãïog~ºû%n�Á—ì‘ðKûÉad\�Ö·R]Du|Å/ÄÕšþÈøÇí½ÞE’É´éî_Ö§Ç¬Ž—VßÒs ¾Û_7¶¾´ŠØ;;ý}+Ou´õô§çS�ß:�gƒØ�­¶ž çÚuO'ë®Ìãô²ž´�;–`p�¼÷ú}¶Ùæû;½}+öÒŸ’Ÿm9óŸÖx]…ñÏÚ»i¡¾mŸUrn»ìͶÓÖ–fãò¼¶šúÓí¶Èx‡6ÙÝëé_¶”¥})ëZqCñômýëñ妿[Çôû¾QÖ:.%Þ\g+meÕ“�YÔüß�r~È4óÙD˜»‹z~I…Ô¶”’,Ù"ºë,’ëí®Ñ„Õ?6KÃnÂÇêo.|_ììÚÉMwò§ùfÚØ¥¾æÓhùþƒ?s�õbƒ&HíËÕÃ4WV‘É_mõüÛ¿%v'Ä>sYy[ãWaeÉ$X/¾z—‘í+—Ãtš}W;Ñeî1ë$päIdyZ¸æŠÿHﯶúþmß’»â79‡¬¼ªñ³°²¯’<ÞÝOÈöŸNKáº]F«�hò÷ÿV8r/²Ì­drÅuiõöß_Í»òW ÿ|÷}Ö¿'>rŽ=º›A•–ýÅv{(kJWø%؉¡à<ë_-kmßê]Ï äÛ 9½)ëôr.ôûV³,ßY¢¸¤¯^\'?“×t~­ù+ù7ü[|„þñý±ý�oØCɯâÝä÷�íì~ù{Ïõ—qû×°þ”™Tb°ubÊ8À“ü;°ô¿P_æ¯hé#·´û×G÷N¥ÂØcåÙ™Â:["{'·yd6Á�ßµòqaÍŽkcš¶h ú í·?2Ð~|‹§ºšÿ/û'Qe;3»´vcõ†&dy4ÉáÝ9“4yQmí†k!‚=¯gäãC�l¶Û=mÒA…Xf³ï™�Öfÿ^EÓýQ—½‘§²�—ÝšKqúÇ6,ŠdðîžÈš<˜¶öÃ5°Á׳²1¡Í¶[mš¶i!ì3Y÷̸keáƒø¾ƒÄßãó3´´ÖÇä—|GO´â±O‘¨Í»€xѳ»]Ê8.«]u™2âî;jH°ù6Þ˳e¥0£ÒbË�…�ƒ›d’eH�"¤ÍüÜxƒþYçlø&žºþ‚ïËvÝ…ÕqãÇeºî/´·2áÿZcÒ,\8 ƒ‡îv0äkàŽËíƒG³ÀŠéeš9î¤~d¼‡Ã_(²öœ#Qú?£»Îݧ;ëH±¢Š=ÚÇ• 9ß]cY 8ÑA�ŶÛrp"²?§›e…Ô–X§º�ù�ðv/< ÌÚp�Gèþ�ïv|ë­bÆŽ;5ükkfT?î¼Ç¶(1¢‚-¶Ï‹##¬péöXQ}IeŠk©X?âOøÌÇðkÌ›»£ªxźü´—wÎøÆ&§ >=×}½�•lý­ÖXPcísïÀÕäågãò==·bêð#ÃÜˬ×At:Y¯§Ü�rIpz?ÿ”µèo;ð:¯{´¦ò‡�äu¦ty[ ajñûM÷ŽIÕÛi¡úWa´ÊØC�Çpa÷Gý’VïwÙí»·ßþJÝÒ~kâu†çe\NäŸÈëüȲ3£ÃÖÁÏôtÉä}oµÈ²èd¿7a‘•~‡ n�Ö~Aëë_m)^Ü| ù'wJù«‰Ö;��Ý8W’5oûîÓgƒmÑÝ[,¾;ªN�dÈüKSÅ$3GÐÍñK¶[$RÅ%µ²HäŽúVÛã¾ÚÖ•¥iZV•‰#²[/Š[,’9,º9#’Ú_d–_JÛ}—Ùu+mÖ]m}+Jý•£üIsG|RÙd±KeÑÉ–Û|rG}µ¶û/²êVÛì¾ÚÖ•¥iéZ œ<�êëº?È>ô鋲>÷ý©»ƒ²zÞÌÏOJfÅÂyŽç�Ûm=¶þf\:Ûd§ÙO²ïÉE_>Aõ­½5ß]ÙÔVMvL]]Û]‹×°e_ëîËÇἿoDZòýkm•­2 ×[%+éOZ]ê¬È¶§M÷Çuu³]“gWöÇaõô9WzúåÁùnß�ãåÓÖ–Ö¶åC¯¶JWÒž´¹L?Ÿ�5¨ñãÎ?/º;�és¸÷êÏ$û£…ðmFÆû¤ËÆëý/`ïñ¸òM})vDy|6Ü£–´§ÕŽKoÿLÃ,@ÄMGYðMÎ)Î>.6ütš|»²ökžðÌú_t—Ý‹N5Ù\¿M‰ï’”÷[û�m¥¾¶ÙmÔ¶ŸÎúRÛï�¾ÛÛ÷_Ä?‚üÇw…¿3MÔ¹ÝI äW&É5ÎùDh3k%ÐcÖÙvZ·ÆÉ¾?mi òÖÊ]}-÷Ý×wS�KuÈ\ÇÏÇíWòK÷³¤PÝh€ÎOí6ò÷»§?R=v€Î'í2ò÷·§R�z¬#ñQþÖîqýäºOûËq¹ÉG%ÑÆXÛðuá®'‰ ð]žïSflù söFTðãSe‹…¿À²þ¹âOn&>|8|c„ÍÒad]-q7;•m­´–¶ÒÀ�†Ÿ1|Wð»„fn5váö‡yÁƒÛ݉‘<ٱŇƒœŠK%q`σŒðù1î“{¤û®ß7auµ¶’ÖÚOïá»Ä¬o¼1á9›�]¸�ŸÞP`öçadO 6ì1 ß`Ç'â²I÷xs`Æã\BX/“k¤û¶Û7>¶ûi-m¥¶?¾ÇñññëÔ½qÈtóêû£´±ãï>þ·a‹•…µÀìž©ÕK ÏÂÊØlí×ÏÕÜCWÇ2"Æ’ÌL�†³+:È£“2ZW±n°:ºëø¬Ï�_ìñ_Í?$:#Mv‡„vvêN‡.M3&‡®ùU æ}qnFU,Š“åW‚rwÖ»Ûoô_u=)Zz+Móâ#ñ£Ì?!zSšýì}¼ÜC ëýæìnÊlÇæ]} ³ÒÛ).D|'�`RZúSú-.§¥?"µ¯;z*?¼Àò¥±0ÿGé8cmæâ8uš™ãð>QlÀE,ô²:K‘g ßàRZÒÚD¥ße?" /™µ^"|›ù…Òë‰ñ¬'¾šûåŽ;î¾MÖ“"µû+O·ìºêz][Ÿ<ì^GÛþ xaÚüËanטö_Š^<óÎ_³²—ÒÜþWË:“ˆïy&W¤’M%µŸu�=Õ¥×Ýu+_JÖµû[ÀÜfâ6Ð\ÇÏÇíWòK÷³¤PÝh€ÎOí6ò÷»§?R=v€Î'í2ò÷·§R�z¬#ñQþÖîqýäºOûËq¹ÉG%ÑÆYðYû*/þ÷v‡ë»²Ö |/~Ìß?{{;õ×Ù+t~ ¿d�‚_ÞK û"䵺’ê#«à EùãPó¼Çã’âýòý—ŒëL(=kJþ•Åë>K›¦šÏIa¥dÆÛcC-´ºêY[¬¥.üßZ5gÎN;o,ð¿Ë>;÷o½Í³ñ¿ºáÁƒ÷nÚG×<‹#S}ŸÑa¶²A³†+í¥×RÚÝm)wÙêÕ¿8xí¼³Ã,xõq¾÷.ÏÇé‹ Ý®Î>»ä9™,þ‰ +$8b’Ú]u-­ÖÒ—}ž­ióC¬v}Ûáß–3¥¾X·=¹ãO{uަH#Çšhö|û«¹OÀ¾²òð1%–Ì­µ•¶Ùg†;«ö]%”õº•`«=V–¥”ý"–H%Šx®­’Ã%’Å}==l’;©}—SÖ•§­·R•¸¤¾#š+«d‘_d‘ßOËeö]K¬ºŸèÛu=_î)d†XæŠêÙ,RY,wÓòÙ$wRë.§¯­=mºž¯¯Ç÷Û~-¾Òr~?›&³}Ç6úÝö“e ±_6¿o¨Ì‡a­ÍŠÉã–¤ÄÌDzKi}·[ZÛöÒ´ûêq.E‡Ì8¯åºêzkùGÓr,}X§ôÃÝë±¶xÔúð]tzA“oçÙZÙwå¥}¥ñ½æ/'ãºIƒÿœ\‡K«ÞaúIßê]¶ øÿÑ¡ºèeþƒ‘Oβµ¶ïËJú-;ã[Ì^OÇ4—ž˜\‡IªÞaÒ’Ç5)‹¶Áƒ?”šÝ¿Ð²)ùÖÖ¶Ýùiö/zÚ} ¯^\'?“×t~­ù+ù7ü[|„þñý±ý�oØCɯâÝä÷�íì~ù{Ïõ—qû×°þ”™Tb°ubÊ8Àø²ð«7Îï1ºï¨³1d“­xýÿÛ'ºócÈ®%qº³Šlu¶îu�ÏN.dyüÇi°ÂÑãÉ�[çÆ—eL¯ebÇ–ëwûã;ÃÌ¿6<³à}Y›‹$�u »ûa÷U“Ý‹Xz׌l5ÖíupÏ^ly¼»gŸ‡¥‚Lk®ÈÆ¿a÷ª[Xñä­»÷ñ¥áþ_š¾Xð>­ÍÅ’N»ÐÝý°»‡.ÉîÆ¬=mÆsõöí5‘OV&dy¼·g�‰¦‚ü{®Ÿý‡Þ}µŽ +odþ >:ëò3çŸàÜ¿M&àz‚8{—È)f‡6ÝnÛ†q­žz^µ“7Øc¦oiò©ñurÁnV.m4¥31n¬˜5¢Ímn·]¦×`j5Z­N« [«Õëq`Á×kuØ0G‹…��…‹XØxXxÑ[QGm¶Ge´¶ÚR”¥0```êðpõšÌ¿_��6câaáâcÙ>.&.møOÙM«»?µzú {g¦nÅÅ—+e“Îx†¿:Yx®8õ²|«¹÷ÈÍÓG n¬Væfcä]m×cÙJs“åGÄ8¼Çðï°¸6§]vofpxoí¢» 23çæüO:Jñ¬X¡’ò.ç ŒÝ=‘ßua³+2‹­ºì{=9Ïò�â4^bø}Ø\#S®»7²øD7öQÝ��&F|ü׊`fÉwÅŠã“"îo¡ÈÌÔY×V+2³ žënº =9Ÿòíàn·äcÁâñêLû/>Êè]®mðAf—»x6&Ã+‡Ù\ì¹,ÃÕàó,<Ìî3±Ì’Ù>éªÞåKeµ’Ë+JÑ+JÛZÛu+mÖÖ´ºÚÒ´­+JúV•¥~ÚV•WeZV•­+JÒ´¯¥i_²´­?-+Oæ«À­+JÖ•¥iZWÒ´¯ÙZVŸ–•§îVŠ�'‚|YæÆÉ†\|œydƒ#xï†x'†úÇ,3E%-’)b’ÚÛu·R•¶´ô¯Úÿƒþ?�ÕpNkÉ:Û›ðîÅá»9ô¼¿�r®?Í8®ãêÙ“ªä|_m‰»Òl±ï§ÛlØ;,¥¶¿ùU¯QÂ9—!ë®iÄ;ˆçɪå|”h9�ÚEÿ—u¼‡Œmq7z\øþÚDÃÙaG%?ѵéøG1äwÌø�`q,ù5\«ƒr}0ã;HkZM­ägk‰ºÓgÅZV•¤˜›(ä§Ûùme>ŒíîUãïuõ|ðkuòsN•ìþÚüN-¼åj'ä}yÊu|·K�¶Å‚|Y²õYLvdÅl‘ÖX.ºßu=}V¿ôŸjh;ϧz¯º8µ—ÅÇ;c¯8wbi±¦šòpµüÇ�ë÷øúüÉ þ…÷ýu™ÿC"Úz{&ŽëkJV•¥,ûêËÑw?Tu§nñ›d��vâ\ÿM ÷Ù~N.»—h°w¸¸yu�ó-Í‹:‘MoÙ[%²ëkJV•¢ÎΡì­suOZöç¶Høÿgp>'Ï´ÐM|rdâëùn‹{�‡—t_™LÜ(ó©Öý•¶[.¶´¥iZ.¤ëÇáýÇÖ�yÛ½{µý;À{Sƒq.Çàûºcdáþ˜áüãA¯äügk÷<Ø ÌÅý!¥ÚA7Ó–Ë%³ßíºÚ]Jѓّ^äZÏͧ Äà_(¾Zið)þ¥ÚrÎÍiu"’+oËì>¯àüëmZ[.NU×VÍÇ!Ȳ·Rê[}ÖÖëlŽÚÒ;kÍù�âx¼7ä§Ê=^¾˜ÛIùe.¤rEmù\߬xG0ÙÝm$Ÿ"ë½»]äöÝu.¥·]mkm¶R´²Úö~b¸–/ ù%ò‡W‡OLm�$áü¶—}9#¶ü®oÖ|+˜lëm%Ÿ"ë½»]ÜöÖê]KnºÚÖÛl§¥–ÕUø–4[7Ìï–Ùyšéð0¹&�ûÝÒÇôâÚë©ãgQi2ö8—ðØ?„\ì{®ÿçøòS÷­s%Ì× Îü1ûy¶?½…ƒ,”ºš*{ Y�¤Tº<\޳é­×äŽÛoºËòö³V—_î»×Ö”¯¶”¥&éøröù9Î òRë4LsÍ^ž‘R±bduçRîëOèvY}ÖÝ›·ž´­õºïZÖ”¯¶”¶Ùµ~-¼û¹Æ òRë4KsÍ^%ž‘R±âduïSîëOèvY}ÖÝ™·š´­õºï_ZR¾ÚRÛlÓü(¡±çÿ°ñLÛe·¤<‘î.¯ÔVKpí²ývÛ…wL·A\\h'¾*m»{*•®MÓMï¥Ô¥ôŠ‘GŠ�ôw¹%Ðs?µ_É/ÞΑýCu¢_9?´ÛÈOÞîœýHõÚÿ8Ÿ´ËÈÞÞ�ýJuê°�ÅGû[¹Ç÷’é?ìw-Æç%—G¾1Aå?t¦Û Ìî±æ1òÎÈ‚{s)‡‘×<#™òýNVFÑå`ÿ uz[´ðOmÖ}<½„_�OÊÞŽ/áò³Ìþ‹ê ®yÜ?3•ÙÊû ˆ²ïÂÈëþ �7-å:œÉp¤‹#ÎQ�©ý Ժߧ•°Š¾­ÜøæñÆ*¼ÍèΠÚaÙ›Ä39]œ«° ž<«°ò8Ç›–r�V\¸wÇ‘‰o'ÀÔ×Q Ժߧ“°Š¾´uÇà×Ã;|áù.ñ㬷š+w½_À7·÷ÇtÁ—¦Õr-ýoÔ“aïé¡åzM¾V66ÊöG9¿IÄsiKgº8¹Ô¬2YeÔZ ²!câÛð?PEüÔëÚ×i郭în�ÕŶÙÛï­7|ç­ùã�í¦¾µüß­ƒÂvó­Ûuç4Öl1¾¤Ðýã?�ì±ràúØòE<_V n·ÝeÖßo¯­+JúUü§†,˜&Çšß|9I ¶{®·ß¶VÉ-÷Y[n·ÝeÕ§­+JÓ÷,*ÙUÔ£Pjç„û6þx‘¶š;"—iãBìeŠ?ucŽLÞ«â™7Çn­n­–]-iOZúúQfÿˆ;97~&x½¹–+!—mã·Iìå†:ÝtqIŸÖ¼g*ø£ºïέ‘Ý-iJ×íô¢Í6Rn|Oñƒq,vC.×ÇŽ•ÙI uºèâ“;­¸ÖUñÙ[¿:¶Ytµ¥+_·Ò‹¹:[–OÏzs©¹ÖVZìžiÖ|–dkà–ù ÁŸ‘ñmVâ\8f’Û$–,i3+e·]JVê[ëZz¶q±-‡dÀ½yqüT|œþO]Ñú·ä¬äßñmòûÇöÇö¿a&¿‹w�ÞCµÿ°=ûåï?Ö]Çï^ÃúReQŠÁÕ‹(ãO»ðëxwgAøq/~r�M¸½‘å>ÊY�.N>L; _PqÛ³5�q¬º<¨édvræØò fǯÓÍÀÚàûýn‚ßlä¾^?´È‡°8ævDwßuñaÙ/ÈÖâý”³éë+m´üÊ­<ü4^HÏä?ÄÏKjöû�îû–xçÊ9×�Ü›e¼²:zcðÌüN]×}<±ÒÚdh¸·Ksþ3©Çº´¥Ö}Æèëî­žê÷�ÛGlôWeøƒp ÃùHî܈}ÿSkĺg?+Ýwºß¯Tq=]¿Nž”öY÷]lgÛùÞµýßD>x5øØ_$Ý·“/¤»~!Ô› ÏuÞëk“\qíUµŽž”öY÷MdTôÿʽkû¨|íëñðþHûg&_I6ÜC©v~ë½Ö×">¹ãú«k=)ì³îšØ¾Ïü«Ö¿º­;ñmñ½^‹å#Ší5ñËf_3ñ/©¹&òé&¾[&Úbó¾ÞáðÉ—~n4TÒqL;+e¿eo¶ëÿ-ÕqQÇg‘€Mgð²oþóÐÞUqoº{?Cvï ßýûëû¾óü%á¹Zïº}×èÛô~åü÷ýO©wÔûǧ¶ßg­óü4»Ï¼t?’œoî¾ÏÑ=¹Å÷Ÿ}úÞï¼8m¸uû¿Ñ·é}Óø3ï÷ýK¾§×ôöÛìõ¾`߆³wõú+É>9÷ogè®Úâû¿¾}ow×ý?Ãéƒ÷o»ý*}/º½þÿ}ßSëú{mözÝc§àúÉ÷üpwƳÓèy¹Ïò~§»×Ý÷¾ˆñÒ/g³Ûù¿Oî^¾¾µõ÷~Jz}²ŸIa%$¯Ì|ü~Õ$¿{:Gõ Öˆ|äþÓo!?{ºsõ#×hüâ~Ó/ {zwõ)תÂ?ínçÞK¤ÿ±Ü·œ”r]`» W@].ÏÉÏ)všÜ‹cÃÃã� Â6õ­Ÿsžléq{³°=µõ¿ï˜1`ñ;éwÙéUÔû}Õô•Wá¨èÊß›äw’»-t´³�t ÚûíúÉ“~?;ìŒÇü÷ׯ³‹_mßg¥³ÝOݪT?†¿£k&o‘žJlµÒÒÌl~?Ò<;iî·îòË•&7:ìlgóßxÅ�‹ßK¾ÏK'ºŸ»Tòÿ�x}{å÷˜;MÌÚrgÅ|kàûZãßw*ãx7G�Ú©¯·*ïèpñþk‘Îøl—Go­dÉÐR·zRË=f•òVé²€"Ãø¦ºÞ}§Bø­Û¶Ýgݸ7nó^¹šÊÒÏ«Y»S†ãrl{íõº’R;léÙ)w¥µ·Öë}kJûiXÑþ%¾¾»eÑž4v¥.³ÛÃ;c•õýÖWÛõ+wfpû9·[ëu/ö[N¦­.ô¶´õ­=kO³Ö5_‰S¯ïÙto�]§JÙìá½¯ÊøÖ×Ûõ+weq 9—[ëu/úvÓª.¥Þ”­=ko­iözÄgñ‡õßÚø1ãGkdj៘p�+pø—q÷k/Ê×q^Íê>ËßrÌ2½µ—k¹ê­òÛJÒÉ$Å‹Ýë[lô…*h~+µOWðÊl¥Îøøç8²Ml¶i¼¥ì]lRËm®½MÞ]××÷bÁ�HeÒWe»×ö§èÌÙ>”zÉ–Þ̦m$õ¿Ò¸ì÷~g¶Ù»Üï:GÀ æ>~?j¿’_½�#ú†ëD¾ri·�Ÿ½Ý9ú‘ë´þq?i—�?½½;ú”ëÕaŠ�ö·s�ï%ÒØî[�ÎJ9.Ž0Èß‚ÏÙQâwïgl~¾{M`ÿÂÇìÉñ÷»´?]Ý–°Sá{öføÁûÛÙß®¾É[£ðeû$|þòX_Ù u­Ô—Q_cþÙþå}—ýïù—ö9²xÎÇþçœóúÌåÔ<ç‹ì�îwϬ¾SýCÎ#ªÊU¶£<jg‚Ä{Ã_ä§ãÇꋈ,ÌðŸø™ø‘ü˜úõWÅ_øWüMüKþLÝú¬â«­üWþ,>8ÿxn ý^ñæÖ6q³,òׯ.?Š�“ŸÉëº?Vü•ƒü›þ-¾BxþØþÀ·ì!ä×ñnòûÈv¿ö¿|½çú˸ýëØJLª1X:±e` ÿâ¯Bo¼¢ò;¥ü}ãuɇaÚÝƒÇø¦NÇìét: œ»r9_(¿Û­¬ø¼S‹ãfl§§­? bßöÑ�¼bèÝß’ÞAôÿDh+•ogs½ËÏÃǦ\Ú>?6M2yW%®5×YIàâü_3c-¾´õ‹ætñ“£÷^IùÔ ®LY½›Î´\k+?™si43åS#”òJãVëi4cŒãeìe·Öž±c\ÜÿŽßóüãówÆ¿ñ+›»¶»3W¯æ™z½–·S¸ÕõÆÍæ½¹¼Ñæî!Ê×[¾ÐõÛæàÅ,S}ã.â¶9.¾–]j×âÜ{ƒqn5Âx–§CÅ8~ƒOŸÎ�Û¬ÁÓqî?®ÆÔéu8vßu÷Û‹®ÖâE t­Õ­,²žµªÍ~5Ç4|;Žh8�Öbé8×Òj¸çÓ`ÙXðµ= ÍN³:ÖêÇ‹��‹QÓÖ¾–YJ,Éã\sGø惈ñ�f.—�qm&«Žqí6µ³ S£ÑàÁ¬Ôë1,ºë®³8£¥kZÒË)ö®VÀÀÁÕ`ájõxXšÝf·]®ÀƇ1ñ0°±1ì�;cŽ8í¶Ë,¶–ÛJR”£ï¾Ûí¿X1ÿ‰×Ç?á·Œ�C䞟]<û®Œì)¸‡)ÈÃ×Áu‘uïlããbÛ´Ýì­ôÊ·KÏxæ£;½Ñ[6öjÓÛuõ÷Ç;ñtðÏÆî¬òS¯ž}¿IóÙxÏ$ÈÅÅ‹éÁÀ{Jl+¶;lºzOXuœçA§ÄIJï[,“q-ií­õ÷GSñtðËÇ­ò U�4û~•ç’ñŽG‘‹‰²Úãa×c¶Í·ÒzÁ«çF&%—{¬¶Mĵ§¶·×ßÏůâÔµñõÂ|”ÖàbÉÊ�:ë>3Ù8RK'·éfõ—&¿ŽdâbÙt–ÛtÛNѤ²R–Ý}lÁ¥}im•õ’Çá°íKµ=ÕägJÍ[>‡:ëN5ÙWË'¶±æõ§%»ŽäââÛt–ÛtÛ >Ϥ²[Kn¾ë0©_Z[e}f×ø6»û �ù¥âÖÓs¸—3’q.·ïî Ǿ…òqü n¸Úußkîk‘K~ž&ão/`pÈ)eÕõȃ ÖÚzCw¬×“ L–”þz•ö{©_JÑmoÀwDOãïÄ�…ügan’Mß;ëlŽ÷Ûlt˜ßB›H;÷‘î{w‰]¶’¶Y.fïM×ü¿QªÈ’ÿw¥uô²Êý;,¥;êó«nÁ€8[ø‹8ðÇã3›ò/O_íQÚ�GÏë_tvú~‘ä—uo¯¥÷ÙuÿoezzYK®û}}=´º´âïϧ³–|ss�õÖÒµë.Èê¾qk[)[oÏäñõµn§¾ën­~Ÿa]OÍ¥×zWòzzÖœbùîáò¿Ž®o¾­´­zϱú³›ÛZÖÊVÛ³ù5�qî·ß}·V¾ÞÀ­?6—]é_ÉéëZpkñ1qýNçá“ÊÍŽË,œÎ%ºñó�h'’Ú]~»m“äTqY³ ­iZÙ-ú.M›�ZÓÒ¾Éî§äª½”PDUh�‡á|ÏÓÁNæÕÙ‘Û ?-9Ž~V%+ý =—Ot†>D–ú}‘eOªÉ¶Êþía»ù‰¬~œÌ[ü)í½}“Çvn7”œ»3#•þ‹.wStÄ8“ßoîG‘.º{m¯îÖ+¿˜šwáÀÌÅ¿ÂÎÛ×Ù•Ó[lZ)ù »Œ™åŠÊÖ8¢‚ù$ôüÛkVºs_/üOëyò°ù÷“Ãöpý|�W!íÞ«ÜÒϧt¶Ò=6Vú=¦D²Çeke‘Ãuòz~m*×^iå÷Š=q6V/<ò_¡8Ž~_[#W¿íέÜRϧt¶Ò-6NúͦD²GekdqÃu÷ú~m*Á�¿äÿ�>=Ûƒ~ùÑ�fÎX ÖÝÛýµÀzÒÝ„óÅ•<àÝÍ9–™rÍ÷Ùl~êÝl7ÖŸe—zi§-ùÍø´á÷gA•å>«w›…lþÜ>%×=½ÊmΚ ÉoÝðvÚn•Çåºkâ­#’ìË º•¥ßR–ÝK«¨ü¯æ{ãSˆË™‹™äÖ£o™‰lÕ¦7à}§Ê¢Ë’ÉoÑÃÛh¸>v†K¦¾*ÒËî˶*Ò´»ßKkK«©<«ægã_‰I—�—ä¾§o™‰lÕ·ŠðNÒå1åÉ d·èâmt|#3E%Òßie÷eÙiZ]掠¥ÕÐÎÛùÖø�éLœ\^cç_LîeÌ­¶ÅwR_ÊûûÊÝ÷ŸO½ftGì|<Sî—úÖ{ã¥=lõôú‘ûµK‘þ&� &^N6³Œy1Ì!‚i£‹?Žu· ÅÄ̲,‹¡³#Î]Ú<[:Ør#¶“YI¡ŠJGZRûm“ÖÊk#üDžhò¦ÇÖh|†æ1E,ÑÙ�Ç:óŠââäÙõ†É¡³—v/Ͷ,ˆéõl¤�Ç}#¯¥öÛ­�eä_ˆ{À­&LÐk4^Brø¢–hìÍã½{Åq±²mŽzÃdÐÙË{‹æ[Dtú¶RH£¾‘×Òëm¿ókÚŠsâ3¯°/Ìâ]‰Ü½ç‘e–Ýn««:C–ê3å­sññ+’wez{WKíÇšìªû²m·èC})ZÍXá¿\ù‡â›èœ)$§ñO¶¹44®_Ò¿˜sŽÁä¾–IJ`}H´¸]‡luÉ‹Öé½/¿è]ù¶ýZ~sòÏķјu“ø ãGlr;i÷¯¥^YË8 ­õ²úSêSOg=¤_x�Ö²úVÿ£wÙoÕ§ÛLÊÿ§GáÝ'ðÆžÖäVÓï_J¼¯–quþËéL/©n¢>wH¾ñ­eô­ÿFï²ß«Oµ¬›ßÆñç�­Ë—Œø÷ænßof.Mø8;Þ#Òw[“�l~¸xù{]{rœ¬,YåüÙ&³"ø­üëb’¿šÂ›ÿÅ[¶– Çż!×`eRh®·7äNÚ ±ý•úÑWY®é­,–MY+Omÿ{ºÚ[oÛekwæá­ÏâmØÉÑñï 0±'¤‘ÖÜ­Ï}O±Šè}µú¶]¯Áê ]öI[ëOmÿyº”¥>ÛkëöaÍ×âfØÉÑñß ð±'¤±Öܽ×|O±Šè}•ú±Ý¯Áê-]öK[ëOmÿzº”¥>Ûkëöjo#üg\os™øðÝîøõŸwý´ä~R`qmÎW» òþù¡ÖxÿÌ0p~ŽuÒdzc“õb²Ù+ôî¾±Y¯û¯ÅåôóÝwñûÆÝV7×ȺØwXÝŸ¿ž˜×_JâEvF`q¸îžþÉ$úT¶Zý´²:}Œ¼üJSdM[¸ßCô§ëä]H·–ö/ š˜×_JâÅYð9Ÿ²³Ãg­$“éûe¯ÛK#üŒºüI>RO7»ŽôG@ê úÙR=Õ½‰¿›î÷]JâÅõðyŸ³ëCg­$“éûd¯ÛK,üŒIÌ?‡iæÛ‹Nào_ñ›ìŸ"ìÛ¹‡|ò>snF5Ô³î±bÛ¥ë.¼®ðÖ—}I/¬öËëOK#ôûq¦÷ñ4|„í­Ç·_Âü^âõ†ùn’M\öL™vßK)dy?Â^ÛäÛl>ÚÖÚÃlU­n¯ºµ§¥)޶ÿˆÇÎý•°Û‡ÂüjÐV+¯ºûõ<ŸÍ~M.¥´¶Éé½í}ÔvÛ§­>�±×Öµõ­~ÏLu·üE^vl­†Ü.ãg¬W_u÷ê8=žüš_Kim“þží]ÜvÛ§­>�±Öµ­}k_³ÓVïü`%5¾úÅѾ Ùoº±Ù[÷Ü—Ûek_e·Éo’±[}öÛöV´¶ÚV¿o¥?# m¿ŸÊ&Ëï_sî. ûÆEÓEú'¦úÊo¹Gtµ’˜˜¿§xæëß�e•út¬ßZ_e>Ûëwç0þßç›ä‹enM0»G„ñúÏ=ÓEv£©¸ ×aGYk%1q¿Oé·vß¶WéÒ³Ri}Ÿ–úÝùÌC¶ùàùØÛ“L>ÐáZ Ï=ÒÅv§©ø×aÇYk%1q¿Né·VݶWÙJÍIeöþ[ëwç<‡&ü[ÿ(»íf^¯†ø�Âò²eŠHw\gª; +g¯²<‹&¾ H¹�rrÍ5ñOµ†úω=ôŽêÖÛ­“Òúy�žÿ•Ù¯¥ÑùE%´¶–Ö<~ŽñÚë.­+u}õ®WSdÉî­+éö]K})O³×Öµñóüà|žM}.�É8qm¥´¶±ÁÓ=u•­+Zûë\ž®È“ÝZWÓ캔ô§äõõõò97Ÿ'_K£òF [im-¬xý5зYuiZ×ßZåu†LžêÒ¾ŸeÔ§¥?'¯¯®·òïÄÝó'É6Pgi¼–âÜ,ñ$Óq€èLÝnLöO“5Û9åç�uͶöçMöÅu±åG�Há²¶Åmõ’ù<wÍWÊ&Ã3':.ù´seM|òÙƒÅúÏY‡mòWÝu¸ÚýoÄÀÆ•¯æÇ VGm>Ê[JÆ=—äóäid–ÿ.»¶—K%ò]H¹vLRëî­Õ¤pÃdpÃ+_²Û-¶ÛiöR”£Ëòqò,’Kw—ÙK¤¾ù.¤\¿*éu÷VêÒ8a²8b²•¯Ùm¶Ûm´û)JQóoüG?3ò_|—y©²¥×Ýu÷RÎ�ñš+)[«[«K#�¥ìŽ;iZý–ÛJ[J}”§£Ïfü–|…gäß•?›QG,”²—[…ݽƒ­Æ§²Ël§³]½ÅÄŠµ¥¿mm²žêúÖ¾µ­jó™ÿ"^yl²¯ËÈóÉå’–Rë0;ƒœj±iH쥖û0µ{œ<8«Z[öÖØé[«ö×Öµ­^w;äCÎýŽMùy`ù²RÊ]fnóm^5)e–Ùo³ Y¸Äʵ¥¿mmŽ•º¾µ¯­kZ±¯$ùâùwå[Y·;?:;s.xáŠøxÞ'áÚ«mǎج¬:.!Ä´zvû¦~?ùÁ<ëÿ,'?Çcÿº'äÿœ ΟòÄògü6ö/û¡|/ùïþYÿËþõ=­ÿ­/Í™ò'çÎÇ+_Ÿæ—”¹¸9ØÓáæáåw¿eÏ�—‰•ÐdãdA'$º9 žî¶û.¥mºÚÖ•§£øeyõçv.Ng˜KåaæA6.V4ýÛØ²Á‘�‘ÑOÑ_Èkd‘Mõ¶ëkJÒ´¯¥_Ÿ+Ï�83±rp³|½òS+3l\¼\Žêì9`ÉÆÈŽèg‚h¯ä²HfŠúÛuµ¥iZV´«ùÍóuòË<2Áž]ûm“G|WÝ$ÃÇš–Émlº±dcë"ž )Jþmö]mö×í¥iZR­6jCRœ°Z™à‡ñð×ù)øñú¢â 3<'þ&~$&>„ýUñE—þÿÿ“7Cþ«8ªëÿ‹Ž?Þ¨?W¼yµ�œlË<€5ëË�â£äçòzî�Õ¿%`ÿ&ÿ‹o�ŸÞ?¶?°-ûy5ü[¼ƒþò¯ý�ïß/yþ²î?zöÒ“*ŒV¬YGK?†?Çë9ï–½«äÓ_V§Çþ±³O£Ê’K,Ÿ]Ø=Ó�£ÕæbÅu~¤ö¸ç%Æš¶ÓÒ?½Ùî­=öÒéþŽŠ�›yCÚ=ë²ÁÆÊÖtg\E§ÒÏ5m¦F¿�vÎVn§]°Â¥Õ÷_K8Gä8³VÚWÙLÛ=Õ§º”ºCŸ‡?£#æÞOözl°`ÊÖtw\ŨÒÏ-m¤øëµòsu:üìKn¯¾J[Â8ï!Æš¶Ò´²™–{«Ou´ºcÿƒÇƼn[䔞Vnqb—¥ú»Œu �eÆmÌÇ“•÷Nó3y½ä|o“dÒ±ê¹â�[~·*,ZW"ý,­²_Rû'œúh©ž,jGž�g“>y'Ññë/Ümù×Sr¨¸ŽºÉ-ŠéûC‡^SÖ÷{î¶ê{qùî�[-iö{©eiëO_Zjï›%g‘ž%yÒökíÚm9¿Wò|n-…}Ô²ÛùÞ£¼‡¯¦ºë©Z{pùÆ£_5'¯Óôõ§å¦¯y­Ò–yâg�]3f¾›]§6êþM�Ű«u¶RNu¨Ã¯!ëùkuÔ­=19¾£_5iözû==iùi­¾cxû�åwŠ>FxÙŸ~›½ÛÓ=‡×:­¦ÿWëYÇ97%ã#Ëä×IöÏ“ÃyL˜{\k¬­²Å“‡‘Ýl–Ûu*¾Vr­!K¬_Ý�‹ÕŸ'ž0ì6Y²áê9Žó•õŽ}±ÖOLÌ®Ä༗Œq|)mŽÛëtRslíeßm=´­”­kOOu:‹ðÇØÐõ·ÉŽy™ÙÒaj¹vוõÎÆÛ=õ¦lÜç„r-Á––[uk¼Ï#YwÛOm.²•­iJzÓ¨? =‹[üŽxë™�™&&¯–íyW]l-³ßé™79á<‡GÇ0䥖ßZÇ'1ÈÖÝöÓÛJÙJÖ´¥=i oÃۻαù~èÞ7­ÏÃÀÑ÷—æ•˧ۗ£³­·½­¤ÀÅ’·R‘fevVèée~ßu=lôõ»Ö–K,#X.´¨Š»½ºC§© Ý·Ü�UÕ¶äEõà¯cváž©l?Zòm¾²’Eõ¯¶Ïu¾´÷V”üµc^ÜÝ?ÕG?iv¿Zõ¬ÇI¢›Ÿó®/Ãb’Ëd4–99Ó]eñÖkí³ÝJÖžêÒŸ–¬mÏ»›§ú¦8åíÖën¶ŠX¾¼RsîuÅøtrCõl‡ëG"Úk­¾/­%¶{©ZÓÝu)ùkG™åü׆õöƒ?•óÞ[ÆxGÕÙ›>KË÷Ú®5 ×G,ÑcE~~ãs•…®Ä²\‰ìŽÚÉ%´ºûí¶ŸmiF•öÌ7ÆgZeבù‡Õ[)©um­ý#íœOZRë«éŸÕzeƒu¾–ý•¤ž•¯¥?-hÔyò»ñ××3VAå—Wl/¥kOwÈÝöœ>´¶ë¾ÌޱÓrüzÓÒßËK½=})ùkF ó¯•ŸŽî»š°oü°ë …ô­m­Ü}ßhCëJ]wÙ“Özn]�u=-¯ÛK½+_J~ZÓ×BùçËïÅ·\èfä|ƒäÄÝŽ¿ßõ1øvp~Óß]ôá’{¾�ë·/äù>¶EZ[ôðï÷_ZYO[î¶ÚêO;üFß|Fy!ãü—¸{F;=þÜ® Ôû=|ûn†ÚV;{7c×YTú”–ë©îŽß²;½}+[)v­ó_ŸßŽÎ+}öh¹GiöU¶{½²ð®®Ü`Y/¶èm¥,§bäð)路]Ou¶ý‘ÝëéZÙKµwš|ûüxqi$³GÉûO²-³Ýí—…õ~ß9}·Ãm>�;+�KO}$­Ô÷ÛoÙÞ¾•­´»K¹/âføgÑj²v:¿)7üÏ.ýñh¸×�¾Câísnú‘Yôq¦æ]Å4vIí’·úÍ™žÛ.üïw¶ÛµÃ™~(Ïð1ï¯_øùä_)̶:ûbåÖu¯þjOe´¶™šŽoØ‚ìzÝ}/®?»ßJ[ìô­n·_yOâNñOû¸WFyÈs)e}°òl~¹â—ËIl¶–ýïYÎ9¬öÃtºïuqýÞêRßg¥kuºýÊ¿?ŠØp_^Ñž@r,ºY_l\š»â—ËI¬·Û÷­g6æ³Û `­×ÒêÁî÷R–û=+[­Óí�âüøÒÄÈÎÇÂéŸ6v¶ãddA���ÖÝ!�¯ØÙ ·Çf=s¼‰ÆÙC‰—m´¾Ï­‹Ô²ê{ã²ï[i€¶?ŠÃS×Û©ðgc›�I.¤rì|ŽÆÕÍtTþrûàÆèÝÅ‘ÉwîÛI.¥?òª°^Ëñ7jbȾÝ?†ìZIu"›eßøÚ¬‹â¥)ì¾ül^šÜÇ—W×ÖÚKu)éüõ}~̲üLúˆ§¾Ý?†{ìZIu#›e߸ک§²ûñ±zosr]__[i-Ô§þU_ÜÖ>Êüe�/«Î¤}=àÏhs­oÞ¥²¹}•ÜÜSª3¾ãHbº Škø¿îx+•$õ¾ÛáûײË-¶êKun­–à.Kø¥¼�Êöÿüaé=ú–k.þò>wÊýsn÷} ‹EæpÏn,g¾¶ûý+é%ž¿fä_‰k½òmÿâKÆ®¤Ò]÷Y¬õä\£˜ò‹~ûu.û¾G·[^!_ºÅ_O|>¾ù=+é%ž¿fä?‰O½òiÿħ�½I¥»îÓYëÈy?1äôûåÔ»èOíÖßÄk÷hëZ{áõ÷ßé_I,õû0Æïñ�ï§×æEÇ>;5­­øY‘àfîü©Ìßëñ¶7Áuºü¬Íf�k'; &´¾h,ËǾ{)[-š×ßLW³üO~zåÁ‘¿ª,ÐG™gíœÌì9å²K-ÍÄ®Wv]�Y±ëu·GlØóEï·óí¾ÚÖÖ-Ø~$7raš¯ñ[Y±¤†Ì›xgieåâÍ%·Ûn^5r;ޏu’u.²Ù`–?u¿�mÖÖ¶±~Çñùµ“ ðàuŒ:ÚÍ�$6dÓ‡v†^^,Ò[}¶åãW#¸~åY ÷Rë-– c÷[ùÖÝmkkVywã óß7e¼Æÿ¸Þ¢ÜãÉÁåÚNææÛ)vTŸ&é²àÚ黃€bãàß‹t6[�v²Y$wßY®¤–Ùß~"ï“ÄÐK¯æ]OÅl†+£“CÔš ˆr¯­þêO=ÜžnG‘l¶ÓóiH¤ŽÏOËmkö±Nëñ|ˆme†LGÕjÈ£ºËàÒõ~¶x²n­ÞêM5yvþ{d¶Ÿ›JG|vz~[k_µŠw_??!ÛIa“‘õW²(ëeðizÃYÏo·ò×××ìôÆÙ?>¿+3Û}"ò_ ·_î¶ìn�èï_§gß:¿.ÚÙéö~u.»Ó÷}~Ö6ÊùÇù8È÷ý/"0p½òVû~ëÓ}Ò¶·V´ŠÏ¾õ¾_¬t¥}?;Ýw¥?/¯ÚÇ9_8_&™¿éy �ƒï¾·Û\^œé 뵺µúV}÷®s)[)Jú~wºïOÝõûZñ̇Ëß&ÂÏÅÒöÏUuäù™”ÉÇÙpîŒëìÜÝT4Éúõ×àGÏõ¼ç]&ÑA­ÙXù9Oí¤¿SóÞC{ó‰ò¡È¾ëúCË^A�÷?¯ôAuÏKñÞ>�Ôû×ðg­õ~öý }Ÿ_ê}/[½žß}þï/·ù�ù2Ýýßï¾Qî¡û¯Õú¢:ë§8ÿ»ë}/Þ?@õÞ·ï~ŸJžÏ«ïú~·{}=×zùmÇÌÇÉvóîÿ}ò‡uÝ~·Óý×�=ǽß_é{þñú¯5Ÿ{öý{>¯¿éúÝíô÷]늿ÎMù©ÿ,ÿü|OÿLm½ùnù(äy0ål<Éîœy`‚˜ö[¢ßãqlk£¤—Éàj0ò'÷IZ}Y,ºOo¥¾ïm)Jx-×ʯȆÿ",œï-;^ !‡èYn—c­ãxõ³ßtžéqxî¯W‹<Þëëýû.“ÛéOw¥)Jx-ÏʇÈnû",œï,»^ !†�Yn›e®ã˜õ²—ß'º\N=¬ÕâÏ7ºúÿD¾Ë¤öúSÝéJRŸ“'ñ üÑåVÚËæ–]µ²•¥>íО.áÒ¾¾•¯ºÜ>’‚—×ìû+w­hò›“Ï‘œ6Á“毒‘Yd¶ÍKµýµËõVûl¾ÊRì�VÏ "ø½$¯­—_[+_JÖž´¥iåv_$ž}m ³+Ëÿ bŽÉ­š—k{+’ig­öÙ$t¶ü­>nL�ûd¯¬w_XëZRµ§­¶Öž_eòCç¾Ö 1ò¼½ïø£²[f¥ÚÞÉäzyë}¶_e)~V£3&H½²WÖ;¯­•¯¥kO[m­<‡*üA?1\Ë_³oæÿ:ÃÆƒ6<û%⼦ø&Âéâƒ#Û&ÛðŽ·ãÛlœ*Ç•unÆ’k±ï¾–_u•¾8î·ãÿÎ?ò þ[~V‡ÎÏÿtωÿ8'�å�äçøpì÷Dø¿ó�yÓþXžLÿ†ÞÅÿt/ÿ=ÿË?ùxwÏþ§µ¿õ¤ÿœäü¶ü¬ÿ�Ÿþé�ùÁ<ëÿ,'?Çcÿº#þp/:ËÉŸðÛØ¿î„ÿžÿåŸü¼;çÿSÚßúÒÖ®Êí>Ëî^Y™Ï;oŸóÍæû|Lþ]Ï9Û•òLÜ]n4xZì|½Öï/7c‘VÅ ·ÉZG´¶ßJR”kÏaögböç'ÊæÝ§Îùwcóì|¶¹½òóÆ¥åŸÞUêâ†iîÅàÚnK[ ¾ï¶.θŸ1šk®È¶èë<:+¤’”üû£¶êYZ_[jç˧“•|pùS¬Š§»ƒé¹%l‚øl¾Øøw:✺i®®E·YXqáÑÝ$”§çÝ·RÊÒúÛW8>`¸F·°>-<ÿÐíi‘\\»—›ÅL[±,“ô—YðÝ—diëusqò¡®=»~)f¥¶Òk¢÷R+ã–¶ImiŠîâ©îMïð·Ÿ#ÿ¿þê댦]øk¿‹~¨ÿW¼M2?ÃiüY;óûûÃú¿â«¿ïìÓïåÍÙ¨/’}Hé#T¬€Ì|ü~Õ$¿{:Gõ Öˆ|äþÓo!?{ºsõ#×hüâ~Ó/ {zwõ)תÂ?ínçÞK¤ÿ±Ü·œ”r]`‘¿Ÿ²£ÄïÞÎØý|öšÁÿ…�Ù“âÿïwh~»»-`§Â÷ìÍñƒ÷·³¿]}’·GàËöHø%ýä°¿².@ë[©.¢:¾Ä¹äDô² Ïw÷'Xu».¹µù�Îx×®Ú˜6RL˜´°ovXY;¬¸éu´¤–M5÷Ým¶Û[®¶•Å�£Þ=1ÒÈw=ÇÛs՚̫2.ÁÊìgǸ•»*âÙKç‡Ufóa….×*Ú]JRke–뮥¶Û[«JWv�xôÇHëaÜwluÏV벣ȓ 'Ÿó>=Äé²û¥”¾xµQîöSmrm¥Ô¥!ƶYnºëm¶ÚÝu)\uÚ}ÁÔ�Är»»;K®º{�àåa`æónÓæüg¯¸ŽnË"ÌMvW$å»=F›+a—%±AóRùdº–ÛJÖ´£�=ÙøŒ~:zºé°¸.ë´;ÿkn>U#þÖœ +M ƒc d²=Žû´2ø ÕÅšK)Zåk±vqÒ;½ÖÒúþk“ýÃóóàZ]. Þv?ylí³*?o[ð|­f›: ÉdPgnû#+‚Ç.$ÒÙOõNº-”~˽ÖÒÿÈåpüúx Ö·M‡Ãw]�Þ;;#ʲ–õÏÊÖi`΂²Y>vï±ò¸=’bM%”ÿTëáØÇì»Ým/üŽ%yø™>%zý¾·QÜü»È®S¢äãû./ã¯^íùM•¬uºÜ�Σ°yÄýuÓœ§�Áu>Ì­O%Τԭ+ %§­iÉ.ÞüRÝÛ¶ºØ:#Åî°àpÛd±MŸÚœ»”vŽ^Mnú”�+ŠÃÔøÚ©,¶ë¡É.}¾ëkZÝZ]í§-;Wñ,öþÏÒ’ñ¯®¸]–Öû/Øvw+ä}‘>M•¬”²x5¼b°‡Y5-­¿™~Fu”ºÚúÖêWÒœ¸í_Ä£ÛÛ?H:OÆî»áv[I#¿aÙ¼«‘ö>FE+[éfDî3YA¬–Ûkoæ_>u´ºÚÖµº•ô§»ûñ“r<ˆ¹&£Å¿ ´š©£Ï†œC°;û³3¹YZ»l²ì‰9'OõÞ›ŒÝ�Ÿ,•ºÛ,Åç91Çm´º·_[«e¼»íœ“ŽÛ‡7?É�çÓeÏtñêú£�pþ·›—SÒ�arž5¢Äçt‚Ê~JK¶–¿»ZÖ¿k›}�ó7ò9Ú6gáåù´á:|Ùk%šž±ã•ßlT�ÒŽyöO|w‡ræWaÛÝÉÚ�©Ÿ_§ë™ØÝ…Ëy¶O¤1RiIù.ßg%¶Ã)e”¥}-²žÚzSìhOa÷töìôÊí~ÞìþÎÉ¥cº“ö>å|Îkk FÛ'#Ûl¯·éCù–úWómû)éF†öww?mä}ëµ{s³»3'Ö:ýã°9ï*æSR°Çôaô“‘mv7ÛôaüË=+ù¶}”ô§Øä÷kùÉæ§|qü®#ݾ]ù7ÛœC76» ž!Ù=ïÚ׉I™ý–Ïoä\£c¡Šè­žûc¥˜öÒ;.­¶Ò–ýŒRÅì`Õ ©žï ’Ÿ�ª. ³3ÂâgâGòcèOÕ_Yá_ñ7ñ/ù3t?곊®·ñ_ø°øãýẃõ{Ç›XÙÆÌ³È^¼¸þ*>N'®èý[òVòoø¶ù ýãûcûß°‡“_Å»È?ï!ÚÿØýò÷Ÿë.ã÷¯aý)2¨Å`êÅ”q€'ßølúf½yñõ“ÙY¸XVl»ï¸¹Ç/ÂÙEm>ÿ?á¿pë N·6ÿJWékù?ÞdAm}})�uÞ¿ŸöNSðöu-x�ÒvV>ß»¿¶y¿.ÄΊÊS6þ5ÄîÁë=v·2OËtx\‡‡íò!¶¿ÎÓ6êÿ¦N'ðùu/ðÁ){+ïýÝÚü×–âgGe)›âwau®·[—'ó×E…ÈxŽÞxm¯ó´Íº´þyháféôìÍÄÊ¥)=œ>>Y³ÈáÒ_í²;.ûÇÈÄ’—[m¶Ýmô­)JVŠÌ¼Òêì~–òßÉ.­ÀÀ—W¦á�ÓØš¾5ƒ5)I!â7rm†gº¾Û#²´Ÿ‹åbIJÛm-­·Ò´§§¢³ÿ3ºÃ¥üµòC«°0eÖéøgtv¯�áÍKi$\JîM°Ëâ7]í²;+IøÎN$”­¶ÒÚÒêV”ôôSãóÒ9Þ<ü yÉÖ9˜�V<~Có¾}Ç5Z(lÇÔê¸7ræÙÜ�{¬ÃÆŠÈáij_Áy²ÚYöVË6Ú5e¬�gstœ?ö×¶’S¬{¯«;H®ú•²x¸8Ñò ±ä¶*Û%ðäE¯ºËí¶¾·[uiû¬Óão?·ª<ˆènÐ’”º.¹î^±ç3Ù_©ì“Šs]&÷")i%ñM Ö_m¿mÖÖ´ýÖhñßSª¼†èŽÏº”¬}wÜ}eÎ&²ï©ì–+Í4»Éá’‘V’_ÐàÝeöÓíºÚÖŸºÝ�Žo[ü‡x-ÎswyœwSƼ¼ñÓcÈvø9XÒãq[{o‰EË"šì+­È›7�Ë•T÷[‘�,‘_mÖ_uµ¶!gúÏË “¾ß踶›eȹ>ëSÇ8þ›\ý¾÷}²ÃÔiµX0SÝ>nËi°› ~Ûä–ûl¶Ÿ–¯›¹Üéøî«?{È6ÚÝ“U‹.nÓq¹ÎÅÖjµ¸P[ï›/?a›,˜x°ÛO[¤’ûl¶Ÿ–¯›¹Üéøî«?{È6ÚÝ“U�.nÓq¹ÎÅÖjµ¸pÛî›/?a›,˜xÑ[öÝ$—Ûe´üµ›ï²+/–[ìŽ8ìºù$¾êYe–YJÝ}÷ßuim¶[m=kZý”£–½íóqñ¯Ð¿¤p¶>Ei;C‘àcE“èÝ~jI³úµ­>ïƒÌ8üWu”y‘úWêE•½Æ¾?Éu)_±Í~éùŠøòé/¾âl|�Ñv.ûܨ43…�Ù÷ì}õôû¶/)ã�Ïטù–¦‹+s�u¾Ÿo¥~Ç6»£æãפ뛉±ïýbo±qìȇAÓXYÝ�~Ãß_O»âòž;Ý{]Ÿé£ÊÜãÝoä­)_±Ê~üùÃø£ñºX0»ÍŽ Þmòi¶Ž-'Nfn;ÿg~–ë¢ËÔï-éO?ÄáûK²mú1Ǻ›]KäõûiKo­¼„î�Å7Å1¤ÚëüxñS¹Žèl·KË{›œëøÝaÈõ¥e¿e×Ü'”}ie±òh.¯­.­iéí¯*;wñ.q,i3ðzÆ>EºŽè-¦³“vß4Öñ›±ò6·Ý�Áøv,¦f=>ÚR‘ò {«öVµ§ó®VvçâUâ˜×çátGŒ¼ƒsÐÛM_&í®g®ãwA‘ëmo¿;„pü.S÷¸?-)HùWì­kOç\8îïÆ;ã?»|?îÞÕ¾é2¢Ûåw6ᇇlVÜL­-œFýÌÞÑu¶][2lÔÉm—W×ó©èå—kþ"o’ÎÅžZñnkÖ]%ƒ.=Ø×ëúëôYÞèï²±ß/éÖ¿³vØùwÛZ×êãÏl»ó£¥•¥=9¡Ùß>ß!üúK¿ƒ<·­ºs讆üN·ëm>udŽëkeo¿7´&ìŒørn¶¿ùsH+mßm”²¾žœÔìßžÿ�Î{-ÿÁžYÖý;‰|WAv'\õ¾Ÿ:ëãºÚÙu÷gvt½�°‡&ëkÿ— – ÛwÛe,¯§§{·ñe| öV³c¦ë=wŽþ:Ç.×ïz®M×ÝeŸÌ¹Ö®<›e‹Q�™ÜÜŸ²8ʲãÛô§È³�âɺ뢤5öûy¯Ù¾rù—ÜØ™ÚÞÑòŸ¿¹®“e}$Íã[®Öæ’q9¯¥¾ÚVœNÄßô\®íŽñßgryç�’ØÙ=}º¼ý?\ö^ï¥ø^N ±V ¡¿¯ºj^Á)vDWV“_Mm$ŸÖ¿Rëšµ$’K}òË}òÉ%Õ¾ù$ºëï¾ë«ëu×ßuku×V¿–µûZÉ}÷É}ÒI}ÒI}Õ¾ûﺷ_}÷WÖ뮺êÖ뮺µõ­köÕ¬×ß|·ß$—ß$’][ï’û«}÷ßu}nºû®­nºë«_ZÖ¿m\ëÛm¶»íž~ï{³Øn·;\¹óö›}¶nNÇg²ÏÊ’é²ssóó$›+3/&[«t’I}×ßukZÖµ‡ù—ÏLÛð­ÿs?1¯®¡þ s”¼ ï÷/ò³úýëOì{”%Ñøi?¹�•_×çZcü¡cWà÷ý�¾@-.gú�è$°’wIÁ,`s?µ_É/ÞΑýCu¢_9?´ÛÈOÞîœýHõÚÿ8Ÿ´ËÈÞÞ�ýJuê°�ÅGû[¹Ç÷’é?ìw-Æç%—G›üfàvö§’>>õ…ÖV[{»ú£�Ý.‚ÊÉo/çš=[)~U.Ʋ·Ócéë%+?Ó}ž¬Óãw ‹²¼‰èN¹ž?«?îž­á3Eë ¿R.UÎtZ)#÷dÛ~=¾û3ëOY)[)ëùÔôõf8T}“ä?CuÔ±ýX¹÷sõw –/t6ýHùO8Ñè¯�Ý‘K±í÷ÛŸZzÉJÙO_Îû=[¡ñ¿¢Õr��ÿøÖ÷ e¤ä^hx¹¢Üë²á‹#?U¶ï �°ÂÉÇžÉ`Ÿ+"øï²ûn²ën­+JÒ¾‹bV¬ñ\ÀÒß’ Fïãïͼ,ûd¾|Sï½¼tŽK¢º™¼¬y.û[un·í¬vlu±]u¿’ûi[köU¨ ZÜ]·‚¾câæ[}ÐÅãy쬥—Ö:Ó+KÖ¼“qƒun·í­–g`Ç[­ü—[JÒ¿eZ‰ò¬ÅÛx/æ..e·Ý ^2w†ÎÊY}cº™Z^·ä{œ«u>ÚÙfvuºßÉu´­+öU£?'ÿ³Oä;ù ùkú‚çê¶U©«bSj›ßánþ+>Gÿü/Õ×L»ð×üþÿQþ¯xšd†Óø²wç÷÷‡õÅV?~ßÙ§ÞË›²ÿP^3$ú‘ÒF©Y+˜ùøýªþI~öt�ê­ùÉý¦ÞB~÷tçêG®ÐùÄý¦^@þöôïêS¯U„~*?ÚÝÎ?¼—Iÿc¹n79(äº8À #~ ?eG‰ß½�±úùí5ƒÿ ³'ÅÿÞîÐýwvZÁO…ïÙ›ãïog~ºû%n�Á—ì‘ðKûÉad\�Ö·R]Du|Š;‡½ºcÇÎ)'8ïÒà½SÅ,ºhaÜó®Kªã¸û È1fÍ»Y¦�a“Nós&4Ý“åÍíôŽ;«ö1Ÿkw?Rt_—™÷'dð®±âñß$6ny·#ÕñìL̸ñåÊý«ý#“»}´°CubÄŶl©½=,ŽêýŒgÚ½ÍÔ½Æ%æ}ÇÙ/¬¸¼wÉ ›žkȵœ3.n6ؼóuL\›«I±§Ôêi%-ô�'Òï}±¨òëñcüô�é�|O±üÃæx±k䯨iq²zg§nžíÌøÍVw`sí_;ý)ª×c]—ºî´ÔçÒXl�:Ú]$�ÇÓÈŸ�Ÿ‘ï äÎÃÇî[z+‹f]Ññµ·õô˜·cSÛd°sÉ37«d“ÿ=5–o¬Ç’ê×Ò+mô¶œ&ïß›�>ö»; ¶"é>3™H©N9ÑzÛ¸<øõƒì²hyÌù{ŽÍ†yiöËH·QA}Õ¯¤VÓÒÚp¯¾þjþ@;ÒüÜ|£ùM¶�€ó®âŸÙaò=5ú>…â±CË󴛌¿v²m§jó¹ù�9Ôrý¶ÚAÛŠdq_|—_5¸ñ_Xé$7Ûý÷)Ûì9'Ýíùûm“.n×w¾Ùfmöû,Éî­óeì6[ ²3s2f¾µ­ÒI}×]ZúÖ®WïwÛÎQ·ØrKºÛr-öÛ&Lͦï{±ÌÛmöYr×Ö\­†Ë>lŒÌÌ™+öÝ|—Ýukùjåžó}¼äûlýÿ%Üíy÷k‘&fÓu¼Øæm¶Û,¹kî—+?eŸ6FffD—}·_%÷]ZþZ¸+Ù¡Ù�ÉÌ6�…Û݉λWŸo>íM×8ìŽ[¿ç<Ãq÷,h°°ÿJrnO°Ún¶tÂȢú³ßôâ²Ûmô¶”£ä>Kå<(ÔÏ?ˆ÷†¿ÉOÇ�ÕY™á?ñ3ñ#ù1ô'ꯊ,¿ð¯ø›ø—ü™ºõYÅW[ø¯üX|qþðÝAú½ãͬlãfYä¯^\'?“×t~­ù+ù7ü[|„þñý±ý�oØCɯâÝä÷�íì~ù{Ïõ—qû×°þ”™Tb°ubÊ8ÀŠüguÆ»ª>?<;áºÜ[°­³Çî·å¶GÑ­´Ìôö}I=¿“Ýwå­ä¸óÇ•�L5­bȆ)â­i[kXæ²Ù,­m¯ÛJÖÛ©ö2Û=³û?.vv¯3g³ÌÅ×kuعÛ †vD8˜888�ß‘—™™—‘|pbââÁ×É%÷[e–[ZÖ´¥+WçËËÄ×âegçåcá`ácÍ—››—4xؘ˜˜ÑÝ6FVVD×Y>><6Vûï¾´¶ËiZÖ´¥Ÿ33_‰•ŸŸ•�ƒ�ƒ�>fnndñcbaâcEtÙ9YY3]d8øØðÙu÷ß}Ô¶ËiZÖ´¥ü¥ùìøúñ¶M†‹A϶Fs¬?­ðw¢ ÁäÜ{/îVea_³ìÌüý_“[<ÒÛ ×ês·x·Û}/Å÷Ù[+Èß%>nüñîLí.›žf÷ï3Å÷Å^?Ò0arm&>EøtÈÄ¿aØ™¹ÚÎ~“]H§®·?i™‹}.¥øÞë}µä‡’6Þ xù&v›MÎó{ó™â}H« é80¹.“&ìJdb_°ì<Üío¿I¯¤S]­ÎÚeã_K©~7ºßmx§æâ ø¼ðÚLý&ÿ¾"ïnÃ×M�OYxÅ‹¬íÝü]¶ÈÓìáØòè·z>Ÿãû^;“‰-Ùú½Ÿ&ÂÛÃeŸ›‰}×YmÑÞòCñ,y£Úfêú‰uç�7D?égõûQdò»ñyùÙ8».;âgHu—Œš|Ýl˜6s~_™'{v¶Æ-½r1¹¿o¨â}Y¢¦^ž;1æÖí8Ï%²;ï–û2«_§XøaÜ>B÷¿�[˜ùywevÖßÛìÂÊìe¾åë ¾I%®.Ÿm�“‰¦Á¤²Ýu ÅŽm­Õô¶žµqo¶»÷»ûëmóº{k°ûGeo®Üã–î¹:Ë$¾I.ƒO…±ÌŸM‰Kå¾´‡8b·Ý_KiëW{c¾û·½öÖo;Ÿ¶{ ´6PVï¹ÍÎ9në‘E­²ûä¾°j0öY“áiñ)|·Ö�âÇ VÖêú[OZ£�äçžÞfù™±ËÎò{ÉnÝî<­ì|–#Éyn|i¤Þů¦ªÍŸê�-Ú¾³áyÀ÷G_Ñœ+kõ$º´­ÒI[°ë±#QÀ6ü+ÜÏÌ_ë먨å/Ã;ýËü¬þ¿zÓûå t~OîcåWõùÖŸØÿ(XÕø=ÿgo�ËK™þ£z ,$�ÒpK\ÇÏÇíWòK÷³¤PÝh€ÎOí6ò÷»§?R=v€Î'í2ò÷·§R�z¬#ñQþÖîqýäºOûËq¹ÉG%ÑÆo'ÆV®Ý¿Èw…8—KH©“]9´÷]&¥nÒs}FêØ½•¾?JÍv,¥þ¿Ðëw»Ò¾ž•Ý/Ž=M»¯=¼>ú[a¤>Du^Ú—Ý '¥nÐrÝnöȽ•’?mÓß®¥”¿×ún÷ú]íö×t>9µVî|ôñém†�ù Õ›Z_t4ž•»CË5»Ë"öVøý·O~º–Rÿ_èu»ßéw·Ò½}øà¸�‰òýàÖƒ6lh!×ö~÷�Y~Vº-¤Weõ[s~ËÀ†ÜigǶ,œŒþ%päÒê݇5Öä[m÷EK.´Qd²È…¶` QóÆËdðsÌË/¶Ûì¿Å"l¾ËéK­¾Ûº‡˜Rën¶´­.¶êWÒ´¯ÙZ5‡Í»,“Ã.£’Ûo²ÿ{òËì¾Ú]eö]Õ<²Û­ºÛ©Z]mÔ¯¥i_²´k'šöY'†ž[G%¶ßeþ2wÕ—Ù}´ºË컪ù]·[u·R´ºÛ©_JÒ¿eh׿-ð06¾)y7«Úablµ›/û£c®ÏƇ3?3­ù&>^n&E’cåbecÉtrG%·Y}—VÛ©ZV´Uj¬ÕZ•0Þÿ wñYò?ûÿá~®¸Êe߆»ø°wç÷ú�õ{ÄÓ#ü6ŸÅ“¿?¿¼?«þ*±ûð~þÍ>ðþ\Ý—ú‚ñ™'ÔŽ’5JÈ\ÇÏÇíWòK÷³¤PÝh€ÎOí6ò÷»§?R=v€Î'í2ò÷·§R�z¬#ñQþÖîqýäºOûËq¹ÉG%ÑÆYðYû*/þ÷v‡ë»²Ö |/~Ìß?{{;õ×Ù+t~ ¿d�‚_ÞK û"䵺’ê#«à äW”Þ>x™ÁîìO";S‹õ‡ºù ×ɻȟ#uÈ2ᬛ_Å8¶§aÊ9fÇÜ›/– nT°Å_«%¶ÇK¯¦ïÏ%:+Åî?ï¾Ìã]mÆk|�àͺȚm¶÷.*Gtºþ1Ƶ�gòNS²Š9m¾ü}v&TÑÅë%ÖÒËnº˜?¾¼’è¿8müû¾{/�u¿­òCƒ.ë"i¶Û̸iÒàqŽ7¬ƒ;‘ò�Œ6Mm÷ãëñ2fŽ/Y.¶–R·Sù äŸBø£Ö{~âò;¶8WNõ¾—ëÅ?%滈u±lvQjö[˜¸ïÖÓëny/Øë´ùR`éu8ù»m…`¾Ìlyo§µ4¿'gr¼�¿ ð‡€âu‡õ›.ãìýv·’ö6}´® ‘ìxǬ» Ãý²Å‘ýT¯#»'[$úXSÛ[m‹¿˜ˆ¯°¹$ûná§ÆëÍôlHûw²uøƒžfS×èö]ÓEuØÛ>1ÔºÔf»K·ûS»ùng<î.Ææ½¡ÌöGW&ç|—mÊ77ãÁm#ÆÃ�;q•—6>$VÒÈqã­�ô²Ëm¶”¥#£Ù�±ÙÝÏÊrù¿mö0ìž]�m±är.mÈvœ�k÷xý~†9[Lœ›ñuø¶×Û<^È ²”¶;-¶”¥#±Ù}¯Ù½ÍÊrù¿lóþaÙ»:ÛcÈä<×�í9Ö°GöA‡VÓ'&ü\ [?2x½�Ce)m–[m)JCóÈï+ü“ò÷œËØþMwobwW.¬ÛI5ù¼ç‘æí5Üks°¿i°Ôpž7Káã<�ÉŸ%d³U¥ÃÀÖÁöR(,¶”¥1Ó1û_¦x!üG¼5þJ~<~¨¸‚ÌÏ ÿ‰Ÿ‰É�¡?U|Qeÿ…ÄßÄ¿äÍÐÿªÎ*ºßÅâÃã�÷†êÕïmcg2Ï zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆþø¸÷åäãâÇ[m“&xqãºúÖ–Rù¤¶;k}m¶ë©m.»íô¥kéû�í�ùYcG[i&DÑAeo­ie/šûc¶·VÚ]u-¥×}¾”­}ßü¬œ|Xëm²dÏ=•¾µ¥”¾i-ŽÚß[iuÔ¶—]öúRµôzž Äv\ÿšðþ¦Ÿo͹Oâ:¬�œ“íÇÙrM¶&›}„ظٹQ`Å•›mÒÝ2Élt­m²êú[[vxî‡YŸþ‹Œiq鉦ãš}f‡S‹o§·Y¨Âƒ_��OJ[OHqqì·ì¥)ö-OÒiõü{M¨ãú˜-ÅÕhõx}f5¾žÜ}~³,,8-ô¥)í‹ m§ÙOÈ´÷G¦×ñÝ.Ÿ�ê`¦6«E«×鵘Öú{qõúÌHp°à§¥-§¤XÐ[oÙJSì^û¦ú€ ¿Š'MtgôG!­)ìÚxÁ§Ó[_I}Þíkv–uô­kýÒ”ätôöþwå÷}žÔ0?n’è<¶éGZÓÙµñÓ]¤¶ž’zû¸ÿeö&}õ­ÕþƒZVœšßJ[ùÔû}ßemC'ñ#inƒËN’ä^´öí~?j¿’_½�#ú†ëD¾ri·�Ÿ½Ý9ú‘ë´þq?i—�?½½;ú”ëÕaŠ�ö·s�ï%ÒØî[�ÎJ9.Ž0¦ Ø[“�±óñ¡Ë‚>ËËϲ)ì¶K,ÍÕðþO³×dÛmÔ­)6Ã)£»òÛ$vÝO¶ŽŽüEábgü�ø§n<9PÇÏv™¶E=–Ée¹zÞÊv8Ûu+JM‡�‹ÑÝùm’ËkO¶ŽŒüHábgü�ø©nÚ,ÅX–°ýk8Tüïþ#Þe%?!ÿT\½¬~l?-ÿ“}þªù[Y¼Ôþ&þZ&nøýVr¦ò£ø°ùýá»õ{ÈUY«3V€¥ 7¿ÂÝüV|�þÿø_«®2™wá®þ,ùýþ£ý^ñ4Èÿ §ñdïÏïïêÿЬ~ü¿³O¼?—7eþ ¼fIõ#¤�R²W1óñûUü’ýìéÔ7Z ó“ûM¼„ýîéÏÔ�] ó‰ûL¼�ýíéßÔ§^«üTµ»œy.“þÇrÜnrQÉtq€Fü~Ê�¿{;cõóÚkþ?fO‹ÿ½Ý¡úî쵂Ÿ ß³7ÆÞÞÎýuöJ݃/Ù#à—÷’Âþȹ­n¤ºˆêøüÝu¶[u÷Ým–Ymnºë«Km¶Ûiëu×]_J[m´§­k_Èÿ—]Ki[®­-¶ÚV뮺´¥-¥)ëZÖµû)JQÿ.ºÛm­×V–Ûm+u×]ZRÛm¥=kZÖ¿e)JÏ|‘þ!®®èÚïz‡ÂßàïvöÄpäë¶½»<¿¤ºs€çý»Éo®±ÿmMþ4É}’cM‡Kà’¹nF±Úùùæë>”»uÕ^!ÓAܽ¥ràìûBik°ê.™Y«¶idš+»3}�—ÖÛ±f‹K�$�ß\œÚÙ‘…Hð|‚üðõ¯K]ºê¿i î>Њ9°6�¡<µØu'ÌúßFKt·áÍ{/{� —ÝmØÓE¥‚Kâ¾¹µ²|:Eûå‡ñ0xùá^_%éñx¿“þNé³,ÕovµÏŸ;ǾªÏ·Y³ðùO'ã[\‡crý^EÐcdh´9xðaÍ~L9Ûl,ü5òûûï¹<’ì ¿iw§cr~Íç{¹e¿+{ɳîÉû¦<™eY©Ñëa¶ Oãørä_÷]nº \ K+ì†ìôµ>æï.Ýò�m;/ºû‘ö76Û_}r7\‹7ëSùæÉ³W¥ÖÁf>§Žè±fȾ¸úý|Ø8Ôº¶Å–ýˆŽw/xvß�¼ëiÙ]ÕÏùbó]µ÷×#sÈs~½1qïž\‹5š]tA©ãÚ/âÚëíǶMGáš\mgàÚi䯶i±5XóäÖü‰m¾y$’üDÅ,TÖp©žï ’Ÿ�ª. ³3ÂâgâGòcèOÕ_Yá_ñ7ñ/ù3t?곊®·ñ_ø°øãýẃõ{Ç›XÙÆÌ³È^¼¸þ*>N'®èý[òVòoø¶ù ýãûcûß°‡“_Å»È?ï!ÚÿØýò÷Ÿë.ã÷¯aý)2¨Å`êÅ”q€2×Ah/å]íÒ¼^<›p¤ä�µ×ó/гY‰~ã˜éµÖäß ·Çt¶ÁvGº¶Òëku)éëOÊË]¤»“w¯KqË2-Ä¿�v×\é,˾:Ín-Û^c¦À·"èi|u–Øk?º¶Òëku)éëOÊÊý ¥¿’w—LqØò-Ä¿Ûu¥³*øë-˜×íy~ŸÜ‹¢¶øë-°Öum¥Ô­Ô§§­¹ñ��÷ß‘ÿ�Ì?Óûß›ž)c}Oo¿éý~÷àQ{ýžë=þßw¯§­=š¶yhrÐÊ€lþ*�.\ÛÂÞC}ÐýÃiÅ{ÇK�m·Ý\Šeèvý_�›t±ÖÊYl7CÈñþ�Ôºµºê_JÒž”­Ñ*üMZ¼˜y_‡»»®‹î{=Ýš¸-¥×Vzdéö]a——t–VÊYlWE¼‡ÙZ]ZÖ´»Ö”ô¥kÄŪɇ–x�»¾±}ÏaÇ{«U´ºêÏL�>ˬ²òë%•²–ÛÑoaöV—Vµ­.õ¥=)ZÁŸñ¡î6¿ã‰ÿŠÊ%¨¶"êƒ8Ïï‹ ¦Ÿã—Âûç–Y¯·Ç¾»†Ûå’é.¤8ÚXqñâ¥×Öµ¤PcÅm–[ù-²Ú[OJR”Yñ¯,³xâ óI$·Ó zæ*]%÷_u"ƒ�âAtºêÖ´Žc¶Ë)ù-¶ÚRŸe(±÷ãfYfðÄ;æ’Io§BuäTºKî¾êE‹#¥×Vµ¤pöYOÉm¶Ò”û)EÉ_³OãÇù ø•ú‚à únãvÛÌæ×ÈÊOŒÿ¼Z_틺¯3}—è¾%™�1ä»Ý¿aÈrëNâæÓÛ~Ó>Êß$QO\lù໼ùó§äŸÇo¸Ì·vãøYÚû=L»Ò¼[/þ_È+}×ãàçîçºÙñxO›2Û©&Ï:ÚÖHáŸîXùÓÃv=yùç7É'Žþñ™nçû�ágjìõ2ì8gKñ|¸/åÛúßuøø9û¼‹¬Ÿ…qI³-­/Ùg[ZÉ3ýÏ6xnǯ7>F¾U×}ê,.?n¶ Ü>OÔ¾þm¹è绋€ ›~¿îgæ/õõÔ?Ôr—�á�þåþV_½iý�r„º? '÷1ò«úüëOì”,jüÿ³·Èå¥ÌÿQ½–Né8%Œ®cçãö«ù%ûÙÒ?¨n´@ ç'ö›y ûÝÓŸ©»@?çö™yûÛÓ¿©N½Vø¨ÿkw8þò]'ýŽå¸Ü䣒èã:}ðÃûO¼Aþø¯ì—ºOñûIûýUò¶³y©üMü´þLÝñú¬åL åGñaò;ûÃvÿê÷�ª³Vf­J@o…»ø¬ùýÿð¿W\e2ïÃ]üX;óûýGú½âi‘þOâÉߟßÞÕÿXýø?fŸx.nËýAxÌ“êGI¥d®cçãö«ù%ûÙÒ?¨n´@ ç'ö›y ûÝÓŸ©»@?çö™yûÛÓ¿©N½Vø¨ÿkw8þò]'ýŽå¸Ü䣒èã,�ø,ý•'~övÇëç´Öü,~ÌŸÿ{»CõÝÙk>¿foŒ½½�úë앺?_²GÁ/ï%…ý‘rZÝIuÕð…ìÞÎëî™à\£´{W—è¸_p½eû~OË9&tzýF§ ÙbÇŠ²Í'­óeææäE�‹�²dååMY$ÒYeÞ3°ûƒu7 ä}�Ù\«IÂx/×_µä|£�çE¯Ôêð­’8,ºiå­=ùYSG<Òùòreެ¾[ì²ïØ}‡Áº›…r>Æì®S¥á\ˆë¯Úò>OÈ3cÀÕjð¬’8lºi䯬™YSG<Òùòreެ¾[ì²ï5̹§ë®)Èyß`ò¾7Á¸GÔæï¹W1æ½gâÜkG®†ì�†ã¿Üäájµ¼,­óddKQÛJÖë©D¾V~q;3Ì|íçKxë�É:ŸÅË,ÈÖm¤²[ôÜÿºãš;ñófæ™9Í£à™É|Pqøf­¹q]t»+æºH±0a?òsó9ØžYfozwÇüÝÿXøÕôò5[\‹o¿SÎûŽ)£¿>^Y>$÷K£àÙQ_tPh¢’µÌ‚ë¥Øß%e³ &¿2݇å†fó§º7Ö>6ÒÉõ{Ií’ýO:ü|é9dØ“Ý.“ƒåE%ÑC¢ŠJýîÝ.Æék$x˜uØüÓþ$NÅòÞ^ÂñoÂ-¶ß¬|Oκ^/Ê{o-·í�!´‘Öè÷8øßz�_¹ë¡äòÿA®«éÁ¾Ýê¬ú{kñqsó´VGù§ BZ™à‡ñð×ù)øñú¢â 3<'þ&~$&>„ýUñE—þÿÿ“7Cþ«8ªëÿ‹Ž?Þ¨?W¼yµ�œlË<€5ëË�â£äçòzî�Õ¿%`ÿ&ÿ‹o�ŸÞ?¶?°-ûy5ü[¼ƒþò¯ý�ïß/yþ²î?zöÒ“*ŒV¬YG?øŸüi¼jþÿý7úÅãŒõâ·ñŸñÃûýtÿë Ž³¿‹_ÆoÇ?ïïÔ_¬<ÞoŒÚYñãü¹¼Jý~ð¯+;–p®JCÏñXmp&Úø3¤ŽvÏ_¯òCk™�ô¦§ÑÀÜdô^&º­ttÇ“ï<«}¶ßuö}/[©m.²·Dûñ8l0娸[ª²ov~�› ˜>œ´úX{9úSo«[) ÿ^mFE¾Ûn­Öý?[©J]mnŠ7âhØá˱ðÃSdÞí†�[œ§->–Ò~–ÆÁ›êÖÊCןQ‘o¶Û«u¿OÖêR—[[ ÏøÐ¿÷_ñÄÿÅe4V`AœgÏÅ_ìãðÃûÀp/ê]«">4ÿˆˆ_Þ¯¿¨˜ë~5ˆˆŸÞ#€ÿQ¡\•ñ�û4þ<�ω_¨.ßöð7}¼ÀØ|µüíñ¯ò¹o�þ$åêy¿�Ø6å蹯eßç‚ô¦Û×#?Q��-gÂæ]£¢¾ÏèØ²Y&£O—[cÌûÖL9zè£×ò“ó[ÆüpŸ”øûâÞ^«™wî2´|»°®�qÂzsiîŸ?[‹•› —v^’û+I1$²M^«.´³7ï3Ó®¶>?(¿5sÇ)ùG�þ.åê¹—}âS+IË»èñ6ü/§v~é±sõ¸�Iõ°ùod鯲´“K$Õ겫K3>ó<9:û#ógø†ºÛÁ o%ñÇÅþ7Û>fdV]>÷o[±7ýiãŒ~ìŒ}¦o2û¶MÐòŽÛÄ’*E¯âôôÇÀšë³77Ûf<:�´¹§6æ=�Ê·¼ë°9Nÿšó>O°ŸmȹW)Ûgoyïe“_Y³v{m”ù¹™úRžëï­im)JzR”¢Ë™òÞÃåÎoÏ9.ó˜óKŸ.Ó�r~K³ÌÜï7; ½)&^ÇeŸ,ùySVÛim+uÕöÙm-§¥´¥) ®c̹oar�ß6ç|—yÌ9%Ï›i¿äÜ“g—¸Þn6ú}\½†Ë>Yò²¦­-¥´­×WÛm´¶ž”¥)Jå»§»;kȾÏåýÑÞ]ƒÉûG´¹æÏô¯+æ¼»e.Ïq´È²pð±­¾ÿn>»Q§ÖãC‡¯ÀÅŽ n<8¸°Ã� QYåÞi昸3o·ýÌüÅþ¾º‡ú�ÎRñü3¿Ü¿ÊÏë÷­?±îP—Gá¤þæ>U_�iý�ò…�_ƒßövùü´¹Ÿê7 ’ÂIÝ'±€Ì|ü~Õ$¿{:Gõ Öˆ|äþÓo!?{ºsõ#×hüâ~Ó/ {zwõ)תÂ?ínçÞK¤ÿ±Ü·œ”r]`O¾i÷ˆ?ßuý€r÷Iþ ?i'Š¿×Ÿ!ý^óH¾!¿iŠÿצÿûåÎæ~/ÛSá‡üb¿äŸÞ«.– ¬6ZÈÕ?;ÿˆ÷™_ÉOÈÕ/k›ÄÏËäÇߪ¾VÖo5?‰¿–ŸÉ›¾?Uœ©�¼¨þ,>Gxnßý^òVjÌÕ )HMïð·Ÿ#ÿ¿þê댦]øk¿‹~¨ÿW¼M2?ÃiüY;óûûÃú¿â«¿ïìÓïåÍÙ¨/’}Hé#T¬€Ì|ü~Õ$¿{:Gõ Öˆ|äþÓo!?{ºsõ#×hüâ~Ó/ {zwõ)תÂ?ínçÞK¤ÿ±Ü·œ”r]`‘¿Ÿ²£ÄïÞÎØý|öšÁÿ…�Ù“âÿïwh~»»-`§Â÷ìÍñƒ÷·³¿]}’·GàËöHø%ýä°¿².@ë[©.¢:¾ð©Ú�wÒ=wË»c¶9vŸ‚u×ÓϽå\«{=ÐëõzønŽ+)K"Žl¼í†v\ÑãaáãG6fvdÑcãÅ,òÇÞ²»+�ô÷å=�ÙܧSÂø ÔϺäÜ›u=`ÀÖ`A[#¥kHì—'/3/&XàÅÅ‚9r³2¥Ž#’i,²ïÙ=“Á:‚rŽÍìÞQªá|…ê§Ýr^Kºž°`k0 ­‘Òµ¤vK“—™—“,pbâÁ¹Y™RÇÉ4–Yw“çœó…õw å=�ØÜ§EÂ8Ñl¹7.åÜ›e‹§ãüs�éñdÌÙíöû<É"ÆÃÂÃÆŠëï¾û©OJzSÖµ¥Û|ª|­v‡È‡cäi5Smx7Œ/w,½cÕŸx¤Sî²pé—…dö?ݯº §7ÚáäÉ÷|Z_&‡jââÖIoÌÎÎ�ɯÉçeyéØZ=Lû.ãgÜÉ\õµ³R)÷sáW+±;°V¶l¹†×"úÁ‹î¿ G‹/ݱþ¤ÕËÎÍ�_É�ÉÇdùãÏò´z©ö\7Æî%¹’Nºë{f¤SnçîV&?av`¯³eËö˜¹Ö _uøzLY~í�õ&®^nm`¿6ß8ý³òCØ|¯¦ú¯}±á>ñ>Sl|3…àE>³aܳñ}†e5¥Ù“e`àof‹m5lÎÖqÙé¦Ë1oš 6PW)È÷+œ²GÔšzËÆß";¦·ÿiî†î^Õ¤VY,ÒuÏXó^k<2_ôìŸ&n9¤ÙE��[þÏ©}ÖÙOÝ«1õ׎ÝÿÛö]'Sôwoöl6[mòÏÀ:Û™s xc¾ÿ§l³äqý6Â!÷ýžûî¶Ú~íY‡®üyïÞÞ¶ëú£¤;w³#²Û/’n××Á r_ôíš|�Ÿ> ­ÿg¾û­²Ÿ»Váuwǯž}Ù§ã<—©¹4—s´è.w¥÷]�Ê{�gdþdßJ–[üà¼Ã²KôK}e¥¾Ï²êÛwæ¶Ÿ‰~=ùT—s³èN_Kî®?)ì=övGæMôém¿ÀŽÌ1k$‘ÿD·Ö[möý—]mßšÝ�gàþù#ÉŸ ô§xxO¬Â—&3{Çc±Ãìô³&|l*xó‹…›“?­ñÅ\¸l’ïKn’?ZÝnÀqÂÙäæ\–Sšy+Ñ« ×KÔö.’Ì‹eôÇŠÈöºNl�Ëç_%n¶èïüÚY}?9�xÏá¨ò.±ÿ ¼Œé� + ×K^3¤æü¶¶d[7·(éµ×p¯« ¸ÿ�}õ­—GæÒËéùìéÆ¿ �™U�øaäWMh©X¦¬µãZ^oÊëdöËé�tÚk¸e%ŠX?:ûë[+ÿ›Ko§ç6ó‹~ Žc—«Å›šüƒñž?º¾ÌŠæëø·�Na«Ç¾Ü¹lÄ·q¶ï.—™dø4²I+~ Šk®ŽÚIm´–ìû…øS±é+²ó¦kæº8«Xð¼l²8¡—ÛZÍe%Ÿ¾%»":]_KnöE_JzÖß·Ò™³ðÇãÒÏ\ÿ4æ¾K¬Ž¾ÌOlŠÈ¯ô¯Õ·êMݳ]=žêÒ–Ý펾”õ­>ßJf¼?Ã-�Hé]‡š_-ÖGZÙ‡ãå‘GžÚý[i$ý×5ÓÙî­)mÞÈëéOZÓíô¦]á߃3®0Hÿl>¹¿'ú¿sýüñïCÁ~ãìû×ßÿHþ›í®Äý)÷Ÿ|?Gé}Ïè};ýßWê[ô²¦ƒð²tN=ù5åVvÖâ;튘vè8?ãwÁ}+Ö»&MŽo*·.Ù-­´¶–Û l­+ZÖïZR™SIøiz* ²+Èü–í­­—[1-Òq^Çî†êVÿ­\‹óëÉ©•mô­¾Ú[l5²´¯­nõ§¦RÒþ¾�‚üŠò/%{ckeÖÇL[t¼Sˆh/†úVÿ«vEù×rJdÛ}+o¶–Ûm­+ë[½})´¸ßƒëã‚Ïß;ãÍÉý}¿OîÜÿ¢1=¾žï¿êøé›õ=ßg§§·ÓÒ¿—×ìÌ…ÿÀè`Ãý#ÛþZgfE Î’mÔ89y6Gm2dƒ½—•‡‹<´º¶G\™oŽÚÒÚÉuiî®OÃü7žE/ß{[ÊL¼˜ãƒïwÃÌz£&{-·ëÝ5z_'#i)ZÛg×’û-¯§Ôº´÷W'áþ? â‹ïÝ«åfLqA÷»¡æ=S‡‡•=–[õî‹ûLädâãM%+[cûÄ—Ùm}>¥Õ§º¿kSøB~2µÛ< üÎÚó[}‹‡—NF“mÙý1³k 2[|˜òè¼yÒîcÄʶžÉ.ÅËÆž–Ö¾É,»Òêz,?Ã-ñí‹“óó_(¶1Yï÷aæv7^Ù�7º;¬·ê]¯ê, Ê};®¥ôöKgç[O_[}i_Aƒøt¼ÄÊ‹##–ù#´Š?¿;°84x³ûã¾Ë~­úÞ­×fÓé]u/·Ù5Ÿ�m=}mõ¶¾‡ ðìø!‰•Dü³ÈýœQûýøY¼ÿ„G‹?º;ì·êß­êý~m>�×Rû}“YùÖÓ×Öß[k�¸ÿáføŠÓmñ6[.µî[…�÷�­Çùxs;Ÿ\˜ìŠë)Kçºl—zÙ[©mÖý}/áþøðÕåI‘�Çûg‘Å~=ÐÛƒºìý„±It‘_L¸ïãÚýmr,¶:ÙJ]5Ñ{d»ÖÊÝíºß­¦øøóÖeI‘› ížEð] ¸[žÎσ).’+é•ü]¢Í¬ö[l¥.šè½·ÝëenöÝoèÇü5 °_[åñ72ÚÙ[iG�þP[eµ­m¯¾•Äî|Y=ô¥=>Û«o¥közúVž¯üÞ?Œ÷¬ìðÃÏÿë³ÓÌ9ñ·þõ\Ïü-vý|z_ùˆ>7ÿÞ·š…Žÿ^Ÿ³üÛ/…oò0ÿÂ+Ëñêòƒø~~3ô< ›ï5}cÏñöZn!ÉvºéîíÞu5°çk´Ù¹˜’Ö¶’C-#ȆÚûo¶ënôô­+G™æŸŸÚŽË6Úþ¯æPgêøÎûc…5{[ŸKHrðµyY8ÒÖ)·WÅ%#š+kíºÚÛ_OJÒ´yžkðcñ×§á¼·m¯ë.g~¯Œï¶83×µyÜ´‡3 U—“�-b›q$RR9¢¶¾Û­ºÚúzV•£þWðØü+Ö•¥<1­µ­+J]O"¼¯õ§¯îÓÝÞ•§­?Ñ¥h¯Ñ¤ÕN-LðCø�xkü”üxýQq™žÿ??“B~ªø¢Ëÿ ÿ‰¿‰É›¡ÿUœUu¿ŠÿŇÇï Ô«Þ<ÚÆÎ6ež@õåÇñQòsù=wGêß’°“Å·ÈOïÛØý„<šþ-ÞAÿy×þÀ÷ï—¼ÿYw½{éI•F+V,£Œëxßø?ÎøVú·Me4œ·Žmë~4ŸK"ßÑ»Œ<ßtû¬úS[ô}l»ÝOmÞ•õ£Öð ͼs�ð®Cuf¶Ý-ã{›®Æ’‘d[n¯s…�ZÁ-oŽ‘ÍJAù—{­ô»Ò¾´zÞ¸·�s¾¿º³[n�–ñÍÅ×cÉH²)n³q‡›ZÁ-n²‘ÍJAù·{©éw¥}hÚ�¹¦¿­üÛðë±6ßxýÀ¼¨ñïšlþç$eþ�âÝ·Äw™¿u—&|\h²>íƒw²é%ŽËnô­×[OZÒܵ§kJ×D!'ø¥y5Ù^IxÉÃÔ¾ÝGïù5=•¿ï¾î[Ï6:«¾ñmo¬tÅôáTú5¥´­oú¾µ­)J[ßijÉ)•ß~6ñ\_]PòNIée×}ö”å\Îme+‘mo­”ůð;ú im+[é'­kéOHzþ%.E÷žûñ¿‰zâÿÔN¡ä|‹ÒÊÝ÷Ê 9œúÏYí­õ²˜µþÿA­-¥k}$õ­})JA7ñ�ïuً㿌ř‰~ÛQ¥ò—{›�fE—gãk¹w�ºýffN-+õ!ÄÎÉâùvA%ií–üyiO¶;½"æ�J6(@€-$øÞÒâè>>ü&ÀÃúôŠ_ú#q-2.¥ò[›Èº×Žr �¾´²?lVì6rÒ;kO[,ô¶µ­ië[*¾?5xÚ<9ÃÅú¿N_:?g}&º—ßL­ß\qÝÎu)Z[o¤tÍÏ“Ùo§­¶zSÖ¾ž«'>?µXÚoü;ÃÅú´Ž_ºGg%&º—_L­ß]qíÖu)Z[g¤tÍØIì·ÓÖÛ=)Z×ÓÖ·'ücÁ>/ÆÏǾ6L2ãäãø=âtóÇ|3Á<= Àc–¢’–ɱImmºÛ©JÛZzWín£o›xÞQ¾n~jóºÏ3•xmá÷-“°ñ®›GÝ½ßÆ6–Û“Àfôú{¸ë­ÆºzÉ‹Ï`ºµ‡s´ŠûeÐßKñ1ënÊ“I¯Œ_̇Ìg[årË-iOÝfnžñÇÈo!³¶:¾�è~æïžž½mµÝ=Õüß³3µx¾èìûÎÇ…è÷yP{沞ùm¶ß[éO_¶�ãëï‰ÿ’Ìγ]Ç<3ï]nDŸÎÉØ<>~¥Á§ôX!üý§jËÃ5‘~~Eµüé©ù”ºïçl¾¶îwø½ù쌪añïûŸ]5kZRþ{Æ.ê¼_²Hb¯®whOÃðmüéí­=dûm¥×Sólº´ÜÎñƒò ØÙVáñÿ{›_5Õ­)<ãuf/Ù$Q~vwgOÄ0müé©_¶JzÛK®þvË«NŒõgÀoËÿqqØùGðo³uÙ%žq»OÖ�È©~=öÙ%dâÛÎzû–Å×]O§%ØVÇ-=ken¥+Zmÿü;'<³*<}÷]u§X÷Ý�Î;{‡gâÃë.,uú–u¶WagWÙfEòWÙ ß™ô§­ÕŠÙ6¿…|üŒò©£‹{Áúß­l¿ÓÝ“Í{SŒgì˜ÖWê[×RsìŠûmžûëíŽïÍ‚ÿôÕŽÙ6·…üü‹r‰£‹yÁúã®#¿ÓÝ“Í;OŒgC¬˜ÖWê[×’sÜŠûmžëëíŽï͆ÿO[«²o×ü%_)œÇC‰¸ä\ƒÅ.¨ØdÅd“q^Û|Ëc¾×ßt8òÖ ¼Ž­êžÊãËóÝkÊ{=ðßZ][+×잣ð»ù§5kú{½<^ÖÓéÝZWQ»í}Ýk-$¥,²´Ìê�)ÑzÝ[½kZ]ù¾ÚÓó›§ü6~]OußÂìñÃYoÓº¶×OµìÝåÕ›êR–Ùu¹½mÇilu‹ÖêßëZÒïÍöÖŸœØ-?áºòâz×ôÿuøå¬·éÝ[k§Úvfòµš’R–Ù[szß�RØë­Õ»Öµ¥ß›í­?9¶½cø6ü‹Û`Ù'sy£Ò¼e\izã�væ ¹vçÉ,lyVϤ²/Æ›WK&’ZâÛtyVG}–Ò{¶+AøT÷ÓE}ü§ÍÝF¶kr=,ÆÐxý™»ŠlJYek%ùÛâã÷ãdVJÝo²˜òÛJR—{«ZÖÚgÝ7á’ÜK×ò2µ¸Û=)n>›¡²¶ÑË�Kl­o»/7·´·cÏuÕºßoÐ’ÚR”»Ý_ZÛL÷¥ü3[‰cºþCæ>·[g¥-ÇÒôNVÚ9q©m•­÷fgvÞ–ìyî¾·SÛô%¶”¥+m¦zÔþ -T;< w¿# Žš<¸/Ú`j|NÆÒìó0-’Úäã`mó<�ßbëræ‹ÖØç“ .Èî­.¬WÒžÚäÍáYë¬y1«É<Êæ»hmÍŠLÈô}7¢ãÒO®¥ñV|\isû“[‹›$t¾–Ïu“Ye×[ZÃu-­.Èú?Ã7×8÷c×’ùeÍvÖ[™ùVèú«EÇ®›_K㬸Ø÷çó>OLlËì¥ô¶{­–Ëku+XnöÖ—d}'á¢ë¨.ǯ#òÚíl·2;ò­Òu^��Ý6.޲ãcߟ̹-1³/²—ÒÙî¶[-­Ô­b»ÛZ]´|Sðwø+‡�d|ãÉ�,ùÒ™wß~gÏéî�v ~ŸÓƳ[·êÞw‘f]ž—û¦®]Ö]VûkîÍš?Âïáf=™ä½éåÚK®Ž¸—h÷}QǬ‚ÊRÿ«nE™ýSÉîʺû«o¶¶Ý -¥+ëK½}i˜u†×ÃÈlž›Þçò[e}×Y\kµÞ®ÒY”¥ßRÙìÌêÝýr.ºµ§¶¶Ý-¥+ëJúúÓ/j? ç‡ðÙ=7ÝÍäžÊû®²¸×j7}_¤²+)K¾¥³Ù™Õü‚¹]ZÓÛ[nŠ–Ò•õ¥}}iìÿÌýøÓÿ9¿Â_Aí3=l_†/ãö8í²þÈòÂ{­§¥e—°º²’___Ëu!é(b¥ù[iG¤‹ðãx-vÙayA=ÖúúË/<ë:IÛZúÝHzr©ZR¾Ÿe´zh¿oƒÇm—ö“³Ým>Ùeç�kI/ûk_[©OC+O_O²Ú2¾‹ð üQê58:݆7‘ü£3*Ç>ÿ{Üx˜ûm�õ’ûé>t?¢†(2/Ž;,¿"nÈÕÛ4÷Ym-ºimÇáðAI%­=×RË,³Ö¿e´§¥ªÃÝà PÅówžEñÇe—äMØú»fžë-¥·M-¸ü>)$µ§ºêYe–z×ì¶”ô£9áþ¿†\lL\i¼XÞì&ÇÆ‚ só<‚ò.̼ébŠØäÌʳµ0p,ÉɺÚß} ‚iuÕöYe¾–ÓûYøk¾9mºÛ«•ä”¶êV¶_Ùúj[})ZV¶][85—ÒÛ©öWÒ´¯§ä­ÚÏÃÛñ÷mÖÝ[;ºJ[u+[/ì¬:[})ZV¶][8µ—ÒÛ©öWÒ´¯§ä­ÚßÃçñýmÖÝ[{ºúRêV¶]Ù8t¶êR¾µ¶êÙÅ­¿Ûw䯥i_æUú­ü4 ÝmÕñ'e}-º•­—y äÅ-¾”¯­mº¶w—ÒÛ¿%}+Jÿ2´d?óxþ0Þ³°?Ã?ÿ®Ïwÿ0çÆßûÕs?ðµØ_õñîæ øßÿzÞiþ9ÿýz}/ól¾¿ÈÃÿ¯,?Ç©þoÆûÖvøaçÿõØÿ˜sãoý깟øZì/úøÌAñ¿þõ¼Óü,sÿúô›eð­þFøEyaþ=Püùzñ�©tÖMŸ•}öÛ}õ¥”¯¶ž”¥(Š/Ê÷ŽU⟙¼Ó¦úcO°Ñp-?à;m~·gºÙr ¨s7Üc e³¾ížÞ|œél›2[®¶Û¯­,õô§¥=(ŠÊÏŽ]W⯙\ϧ:gQ°Ñð=?à;\ vÏu²ßåE™¾ã8[-•÷l¶ÓäæËd¹rÝu¶Ý}ie+éOJzQÏÄeá'ŒþyÙÁúwÅ>»—¬:Û�øÏÀ{aÆ$æ<çœYN]¹ì>Ûã[=„ŽÃ伯�Ç^³ˆáR°W2è,¾Ë®²Û}õ£™o9Âàpfß…oû™ù‹ý}uõœ¥ãøg¹•Ÿ×ïZcÜ¡.�ÃIýÌ|ªþ¿:Óûå ¿¿ìíòùis?ÔoA%„“ºN c+˜ùøýªþI~öt�ê­ùÉý¦ÞB~÷tçêG®ÐùÄý¦^@þöôïêS¯U„~*?ÚÝÎ?¼—Iÿc¹n79(äº8ÀŸ|0þÓï¾ëûåî“ü@~ÒO¯>Cú½æ‘|C~Òÿ¯MÿöË�Ìü6_¶§ÃøÅÉ?½V],AXlµ�ª~wÿï2¿’Ÿ�ÿª.^Ö?6?‰Ÿ–ÿÉ�¾ÿU|­¬Þj-?“7|~«9SyQüX|ŽþðÝ¿ú½ä*¬Õ™«@R�›ßánþ+>Gÿü/Õ×L»ð×üþÿQþ¯xšd†Óø²wç÷÷‡õÅV?~ßÙ§ÞË›²ÿP^3$ú‘ÒF©Y+�ùðȺo•ß(cºÛiLL~ŽÇ²´õõºÛ¼wêlºÝ­kOw¿*´û=)éJ+÷ùÀžù¾O<“Žê[JbÁÓ0G[i_[¬» ú»&µ¿Öµ¥n÷ä]O³Òž”§ý>o2/›äãÉ¦$5�mõõºËº¬2«[ýkZVï~MÔû=)éJÑU�øœ9v$ù‘òOM—�‰?_ñN�â:¹q©5&ÌÀÍ莽ç²dçÖY¤²ì»v|Û&k±Ùô"Ž•¶·Òë®ãË“îQ8²?àºÛ¬ø©ñ6—Ûuµ®«µ®¥.¥m­m¿½ûFû.ô¯¥}·Ùu+Jþí+ê°‹ánËãøÊñzÙ,¾Ë««ìÛémöÖÚÖÉ{¯²eŠúRêRµ²Hï¥Ö×òVÚÒ´û*°[á†ËãøÎñ~Ù,ºË««ì»émöÖÚÖÉ{£±äŽúRêRµ¶Hï¥Ö×òVÚÒ´ûì|båaüJø!^4ø²ßÑzŒ«"ɆH$»;qºÍÂɶÉm¶ë ÌÂÈŽh¯¥=²E}·ÛZÛu+^´ºŒê«üåü¤çù‰Û™þ=tþóÛãMrL¬Jl5;òp»›°µ7ϯÏçSd`É~g ÒËY±xìv_4yVM�×Ö¹PÉš’Ìï+ûG; º—yJxáÔ¼ƒ#M†§:ÌŒNß纩%ÂÎæ3eb_v&oÑÏõq´1Ç|°äÙõ67_Þ1ãă‡ÌçÉFo•Ý£�нO»öøçÔ܃'톯:ÌŒNÝçš»åÂÍæ3dâ_~&gÒÍõqô1Ç|±dYõ6_ur ‹¹ħó+³òŸµwþ xãͧ§‹½=É-Áíí÷ÉŸ]ß½ÁÆ3ã“#]•�m!Ÿ{ÕÝOÈ1+¿ßú—¶ä8·í©÷ر4Y�ðÂ÷ ‘>l�þ y?åFÓôW�}ØÝ§[6ú¬íÏãÙ_Á &~U¾üx9/9ØS†q«e+u.ØçâÙí¥nõô¥jØN‰ñ?É/&ö?£z¥¹ÿeÒÌøµy›�Ž{8–Ÿ>{),8܇›lkƒÃøÝòE_u+Ÿ��o·íõôû[Ñ~(ù!äÆÇôoDt¿>ìªY�³3q¡Ñä[Å5ÓÛI!ÇäÓc÷!Ç.’:û©\üìk}¿o¯§ÚÞO¾4üðóŠ¿xñoÆÎí?îÜC^}n¿†uM» ¸7í´wö÷alxŸWÙÉqcÙÁwèºíé±’Ù)u�ÝJV´îŸ@þ¯+yźýŸ�=¿Õý©Ëž|�'ƒ?¸9ö¿*ÚÓéàl5ÚÜ®)Ác¤Ôõ¥r19}#û+ôïþuÚŽŽü8~Lóp¶=ëÛ]mÒº¼œ_¯6›�cçö¿7ÁÉ¥Ôÿ©û<ž+à ú–úÒ³âïsí³òû/üŽÑôáÊòc˜Ó cÞ�±×-¬ÉÆúói¸þ>wjól,ŠVž˜ ,,ž-Ã!ú”õ¥gÅÞçÛgåö_ùLñÏðt÷ÿ!¿gåg–[ÕØeès+Å:GŠòNÝä=Dµ‡#�é¶\›˜Wª4|3�Á oƃ'”a[7ô_lÑÛK$ìŸK~Ÿ�޹ûŽoe_Û�ýµ�Û68¼ÏœIÃø”ùô²ê]™­Óu†'ä8PRúÒëqòwYöúÛé}×ÛZÒ½nêÃåào_ýÛ+°-í>òØÓ;s æ\Ún-ÆîͶÊÛ~N»UÖ¸¼?u‹d­/¶�¦u)Zz]uöúÒ½jê/Ãïà�û®W`S´»Çen5–æÁÌy¤¼[�_›K+K²µúž·Åâ;¬X=õ¥ÖÁ“µÎ¥+OK®¾ßZW¼=ø]¾$ºr ÝË:Ë´|�ÜE»ÂÝë·�çÛœŠÙ5uÁ¬2G¨³�ôÌ};·Z)²!÷Í�·Õì«=.º9.ºÖ7T:—Àß º*š™:£Å¾Œáû=¶Û¬äؽqÆvÚÙí­²KÎ÷8.e™=µ¶•ú“çI¯Ûëêé�VøUâ7J]¬Ÿ«|mé~´ÓÅlX“¯xÞG0ŽÛ=µ¶é¹¦Ç7–fKJÛJûæÍ’ú×í­}]-êï 7‘�ÅÇòeÅÜë²nÆÉꎻʛ+U›Î#ÉŠëraå{ÌœlŒÑ­—cKFuÒÇ~&/‘}Á È—s®È»'ªzó*l­^o6�&*Ó"U¼ÈÆÈÂÑRÛ~4°äg]$wâãÇ•ßÄ òÿoÆ¿�Ø]gÓ}G˜~@jv¸{NDYù};×÷S#U¾ïMž†+¯¬›lÚ_¯âQgý, Ýäså]fÃM°×OKï¾[ï’Kî’I.ºù$¾êß}÷ßZÝu÷Ýuku×]u}kZýµª ß|—Ý$—]|—Ýu÷ß}Õºûﺵºë®ºêÖ뮺êúÖµûkTï¾ù/¾I/ºI$ºëï¾û«u÷ßuku×ßuÕ­×]uÕõ­köÖª»ö{=–ëe°ÜîvÛm¾Û;/gµÚìòçÏÙlöYóÉ•�°ØgeI.VnvnT·I,²]t’Iunºµ­kWù—ù~ªá\›vO%Öpκáܧžó Üöãi¸¯ ãûnQÉ6ÙV”¶ n�I‰�³Îšµ­?6(®«ÔpÞÍ;�àq¾â£�r½¬ŸKWÆ8vƒkÉùÊ_³ú—K‰›²Ì“í§ÙwWíz~ÂyŸbr%À8�'ç<«k'ÑÕñž¡ÚònA²–µ¥>ž›K‰›±Ì“Öê}‘Çu~ÖQê�î� yoð¡ú�³;§œSY•º¿‡õGäý…É¢Òàω��¹›GÄõ{m”|Œø,Ÿ*øí‚¦²—ßoºß^Ìøïø|>D»ËuÊøwñãŒgcãæÁ�Ý<ŽìI“‰$÷E4pð#ƒÊùn«k¶VïºîñôõºžŸŸJ]J×®Ý ð=ç·rãámùWâ]Ç2ãÆÊ�/¶ù `äy8SKì›îÜ'‰aò~E¯ÙãÙJÝ÷]Ìz{®ô§çR•¥]rèo‚O=;“ oÊx¿è^;™>Ty}·È+�Ȳ0å—éÍH8OÃäÜ�_²‚ÚVï»n"ÔÝw¥?:”­+Yø»øP¾I;¢]NÛ½¶Mâ_›s��¸Ç漦Ìí;xôøfø×êÙ·¼?c%“MH+¯Üò¾=›d¶Iõ,²–Û[ûaÒŸ…ßÇ7dy�ùäkv¦ÂËñ'·Y×Ú~;Ôür•¶–Ý™�±ý'NÈäLi/õ²Ù±³5rû>ßKk_K{ Ó¿†ÛÇž;l9}ßÞ�¡Ú¥Çžšî«Ðuw¾ÚS×#au;�gÁuÕöÒ\lýt•¥=}-­})ØŸü7ž=ñÛaËîîòìîÎÏŽH&ýÂ5z¯ã—Ò”õÈÂϳ:œÿŸ×WÛl¸ùÚé+Jzú[ZúR@>8þ:×ø5·ò´ûÏÉžGª—6þC£‹g«é^£åQMõ,ŠN-âÛv®›îQßmÕ»�Ò²Ëoº¾Û+XÝfêOˆ��¾•¶êñ/z£u•$0E6ô5¹ÝÍ™t�Sÿ:ñëÛ;e�ªË–ÿ[¯» Óê¾³ý+¿üBÿµ ¶ÿ¬^œý\è�.ùëý£ý�ýauGö®@çç—ö�ö?õ‡Õ?Ø^¹[oâòý¦ýWü‹z¿õÅä ˆî48Ú‹fß…oû™ù‹ý}uõœ¥ãøg¹•Ÿ×ïZcÜ¡.�ÃIýÌ|ªþ¿:Óûå ¿¿ìíòùis?ÔoA%„“ºN c+˜ùøýªþI~öt�ê­ùÉý¦ÞB~÷tçêG®ÐùÄý¦^@þöôïêS¯U„~*?ÚÝÎ?¼—Iÿc¹n79(äº8ÀŸ|0þÓï¾ëûåî“ü@~ÒO¯>Cú½æ‘|C~Òÿ¯MÿöË�Ìü6_¶§ÃøÅÉ?½V],AXlµ�ª~wÿï2¿’Ÿ�ÿª.^Ö?6?‰Ÿ–ÿÉ�¾ÿU|­¬Þj-?“7|~«9SyQüX|ŽþðÝ¿ú½ä*¬Õ™«@R�›ßánþ+>Gÿü/Õ×L»ð×üþÿQþ¯xšd†Óø²wç÷÷‡õÅV?~ßÙ§ÞË›²ÿP^3$ú‘ÒF©Y+Qù¬ä•å_(¾]lë—…›\^iÅ8ßÖÀ¾) ²œ7¬x7¦$—C$–S7š?¡“mkî·&;éu-º•¶•ãüÅo¿„%>Sì>õ‰—÷~UÄt?W øä†Ïà·YpŽ1÷[îŠù-¦^è�£‘mkî¶xï¥Ô¶êV”¯Kæ}ü#ù%ò›a÷œL¿»ò¾'¡ú¸WÇ|6ºÓ„ñŠc_tWßm2ð©¨ú9­}ÖÏôº”º•¥*˜üIwÙ'ÍGšG}—ÛKüx²·Yu.¥/‹Å.ŒŠ[+[kZRøä²¶ÝOËK©ZWí£–®g¹¨á°Ì_†íNÃKñ�áöÏîÙ3u¦VÚ8þ¬{µûþaÉ·ºœ�~<’ÇO½ê¶0Ëì­i|~ÿmô¶ú]m,Zø�Õçj>:|NÄØÁ÷|‰zÎݤqýXe÷`îùóu«ŸÝ’ÙO½ksá“ÛZÒû=þÛémôºÚX›ñC¬ÎÔüvx¡‹°ƒîóËÖ”ÙÇÔ†_vë�ïw:ÉýÐI%”ûη>)=µ­/³ßí¾–ÝJÛKŽþ+¡—ã+ãÖÉmö]w…/ÍJ{­»Ö,ž–áyÝëmn§çÃ-µôü´õô¯¥}h雡΅·ÔÀ¿Äç®OŠÞ-EÒ{¸®sù?�»â�åàäcÓcÃú“ (1û‘û}2'ÃÎä�ìbÑk¯º8®ºÜÌÜœiìÈ×ÑÃïœÿ7²|bñ¦.Ÿà[k°{{Éw<_3 ";6K«ðá‚Èì­#È—?yÆ.Õ¤zfååcMløŽ!üåy·‘ã/�qtÿÛ]ƒÛ¾GC¸âøÙxYYŸÄúà çÜŠßK'—;yÂ-.Õ¶ëL̼œy­ŸÑ¯ÄaòY¸øûðžœSª÷™?#¼«Ìä]]ÕÛ�vvçQ»à\;YªÅ›¶ûoAµÕáÛn?!â:Ýö³U«­› ~vß‘blñë=ºéế´cU¨è�„^yë°³+§¸%š>¶‡*|M·tö$™Ük¬5ù¶ß\œ´ZíŽÓ–mâ’Ûb¿M‡°—Ic®WÝâ»êÓ~ü5øÔò«ÍüÛ2ú«†G¢ë¨²fÆÙ÷>»;�uÎØßVÜœ-vÊ, ý—+ÚÃ,_JümN.møÒßgÞ«�ßR›ñáÇÆÇ•nfÙ—Õ\2Í]Å“66Ó·ùíÙ¼®°æÆ÷Ó']±�;eʶ°ÉÒ¿S‹›~<—Ù÷šãÇwÔ§dþ7> ¼äù'ýÍ8Äê?ò²ý¹^CöÜ[-7Ûáàï)§ÞÃÕú-¥ÕñÎ3¦ÔñÞ=£ÀÆÕé4:-v£K§ÖaElzí^¯_>¿ )dPÑÇe)KiJQÚ�6›OÇu:ÝÕktZ->6·Q¥Ó`âë5:­vVÁ‰�­×aE,RÈâŠËc²ÊR–Ò”£´ºm6ŸŽjuÚ=©Öè´z|,}n£K¦ÁÅÕêuzüH­‡]®ÂŠ <,Xl¥‘Å–Ùe´¥)JR‰A8ÐÃ�� XøøñGA‘C1YH↣¥¶Gv[Km¶ÚR”¥=(úϤúOêÇý³ýÊû/ûßó/ìsdñ��ýÏ9çõ™Ê?¨yÏÙÜïžÿY|§ú‡œ*GU”«mFxÔÏ?ˆ÷†¿ÉOÇ�ÕY™á?ñ3ñ#ù1ô'ꯊ,¿ð¯ø›ø—ü™ºõYÅW[ø¯üX|qþðÝAú½ãͬlãfYä¯^\'?“×t~­ù+ù7ü[|„þñý±ý�oØCɯâÝä÷�íì~ù{Ïõ—qû×°þ”™Tb°ubÊ8ÀSü(v�ݳñ‘âÎ×+gfÇkÄ8–ë«6‘}ïïY«zÖï¸_Öeú×Ý�}œ/Q¬š(«éìÅš/OÍ­ü>öeý¥ñÍã6Û'6̽—â[N³ØE÷ªeO®³¬ùFï…h°²¿Ócß^)¦×ÏUûlÇš?OÍô­l.ø�ì»»Gã§Æ�®Fu™›.-Äö�i±‹ïTÊŸ]oZò}ß Ñàäÿ¦‚ÿণ_4QWùÌy£ôû=*·“á'¸µýçñ9ào5ÖjòtøúOø¿Nˉ•-“K.ÃÇŒ�‡@m¶–ße-¶˜ÛͧYM› ¿–Èr-¶¾µ¥jê‹¥®•:˜¬›åç´oíÿ’¯0yUÑÒõ¹±ë(#¶ZM`é�f¯¨lÈŠëm¶Ëlϯ®EiJ}—K_ZÝ_[«\÷ÊÏemo�ÿ+ù5±Öõ]£™×Gõ>¥žÎ¡Õêú¦éã­-¶ÛlÍ—†]?¥)éë-~Ú×Öµ®«åS²¶¯Èg•œ––V(õ]¡›×PÇõ>¥žÎ£Ök:®³EZ[m¶Ç™'ºJSÓÖZ×Öµ­n­Lÿˆ/¸5ÓòýævÿŽî¶;Ž?Ä9§éü+3çʾ6ߦz߆õŸ>Òêñò/ºÌv/fñ�ܞȩlRO,“zVénºîp9ðçãŒàKÂøÎ5æÏ•òM×C$ºù³·»L]^$V[ 9Öù22­¥)lwÝë_²Ú×ì®qñ�©+ßþIø÷ÑÏLk»¯¼zŸ©-É®UØTǯdsݤõ̳g~%"®çÝõi�‘Xý=ÔŠOOemЊ( Š8aŽ8a†;"Š(¬¶8¢Š;idqÇ”¥¶Ge´¥)JR”¥(µ8ìŠË"ŠË#Ž;-Ž8ã¶–Y–R–Ùe–[J[m–ÛOJRŸe(´Æ8ã†;"ŠË"Š+-Ž8ã¶Û#Ž;-¥¶Ye–Ò–Ùe–Ò”¥)OJQuûý¿Ûý€ 5ä?{p/:G³{ó³sdÂá]Å6ŸqLzãÓ?e$ÌmGÓÙ—>.,ûþQ»ÉÆÖëâ’Xì›7*++}´»Ö˜—¾;§ƒøéÓ½‰Ýý�›~ ën3ŸÉ6õ†ìzfçÝ�K Öhµ6eÏ‹�>÷’mò1õøß,vÍ›“uºßw­17{÷G ñÛ§»»»6ü.ÖÜg?’më ØôÍϾ Y³Eª³*|li÷œ�o‘�¯ÀŠù#¶l̘¬­Öû½iŠûǹúóÇN�ìÎ÷í�ä|o­ú“…r{̶÷ÛI¦ÇÒq½tû¨5ØtºÙ¶›­‡Ñ¦6>ìŒì٢džۥ’ËkV÷’¾Av•=ëÙž@v†m¹œË³y6^û:$È“_¤À¥±áh8¶–™rÏ“ƒ‰è11µ¸I}òY‰‹/ºë½n­k^D÷¿:òo»;½{#.ÜžYØÜ�+w—7Ë~—_m±áhxΣëß$öi8¶‡]…l—_%1±¬÷Ý}þë«[?�ýíÎ|™î®ÅïNÇË·'–v/"ÊÝåÁ ó_ƒ¦×ÒØðô\gQ÷‹äžÍ'Ñbãkðí¾ëä¦65žû®¿ÝuiÛó«Ì^ÌóÛÊžÞò—´æž ×erYò8ÿ®n6~^p e)­à=q§ÊÃÔèq35ü+‹cca]™L,iö™1ÍŸ•mÙ™Y_ƒXY†ŽË]'ÐýÅäw?ÔõwFõ×(ìÞw¹ºßºè8¾ºLÉ1±k‘4»mÎmÕ‹YÇ´RäÙ÷�Ž|ØØ8¶ÝK¦–Ë~ÖSé¾�í¿ ùÖ³­zW€rNÇæÛ_Kñô|oì›ñ±>¼ÒíwÒݳA¢Ä›&:dl3¦ÆÂÆ¥ô¬²ÙJú²—Nô—lùÎu½mÒü’v75ÚÖ—cé8æ Ù7ãbýxqåÚmódºn‡G‰.DtÈÏΛ —Ò²Ëe+êÙO<>ò[ÍŽÎÅêzƒ•ö÷:›ý†v‚ \M'ÔGî¶ýï4åû¬­gáÏK`³7m�‡�.\‘cÇ}ùEò¸ð›ðËê±,ÔsŸ;; í¶UÖãgS¢ú�g6&¶ÝLªkù×gß [ Û©mÙ¹˜Z1m²Km—q-•ô¬ž¼;ü::|+u\ÏÍnyvë*¶ãæS¥º¯c‘‡©†µ¦4ôÂæ��|8ûLû½—ËVŽ:E-”¾ ¬ÖWÒ²rð÷ðëê0­Õó/4ùÝÛœªÛe:c«63áê¡­ÔÆž˜\ϱ¯† –}ÞËåƒ+I%#–Ê_Ök+éYºøøG:[€Y¯ç!ý•|r�d×]ѽ9¸å<#§uõ¾œ‡¶rNÊŽÎ/Û<òù12u» ÑvpËuÛ i`š»\[ý×IÓ¢ñûàn¿°^é?Äí$ñWúóä?«Þ`éÄ7í ñ_úôßÿ`¹ÜÏÃeûj|0ÿŒWü“ûÕeÒĆËY§çñó+ù)ùú¢åícócø™ùoü˜ûïõWÊÚÍæ§ñ7òÓù3wÇ곕07•ŇÈïï Ûÿ«ÞBªÍYš´) ½þïâ³ä÷ÿÂý]q”Ë¿ wñ`ïÏïõê÷‰¦Gøm?‹'~xWüUc÷àýýš}áü¹»/õã2O©$j•�­¿’-½û¿�O63¯—ë{<§ï]lrRØì¶°i;'‘iq©oÒüËì·_m)wå¾”÷Wí­U­|„î%ÞyÛæ&tÒ}[£òW¹õqßK#²•ÇÑsýî�–Ò/̺ÛquÖR—=})î»ó«Um ûy7žuyƒ�,ŸV±ù'ܺ¸ï¥‘ÙJãè¹ö÷I‹Kiæ]m¸Úû)K¿-ô§º¿mj¨gæ×³îíß–?<ù]×O}u>Ar®°¥r0ñðd¥½#KÙm°âË,WÁeœ–Å5kI2"¥²Ém²_u´Ò¶ž4ùËP¥¿:<^9à…:¼Hk�m¾+ô6~LUšùÿê–ç¬xÖço%$¾ûëí›iŸ5ô¶•öYK½¶Ò–Ò”¥–^ê Ñø7áî»ÁK|dèì̈«-Ózgí:Û�ìöw}K¯“ì“c™-Ô¥+í¶•ô·ÒÚR‹)<ÓÁ¢ð{Ãýv<5‚–øÏÒ™Ö[¦ôÏÚuÇÚl_­²ls%º”¥}¶Ò¾–ú[JQsGÇOÜpÏ�Ÿ8!ƳÅ<7ñ‹�o1,ȃ*Ì]Æ‹¤øF¯g�fV,“cdÙn-öÒHïº;éO[kZV•n[m›hÜ€c*ž]Oæ—›ÝÅÛ8Yv]{¥Û]Ö�=g×È›°à™ZÍ.ÇYTPO‡�Í6wæò)`ºÚV­Ä¶þJQ\ŸÉÇ•ù}æ_mv~Òý—ÓlÿµÇSÒÙïŸ.¸áåkµ; gÕ†  ÄæI3yÑ_JÝûy,õô¶ž•Ð|šyM/—~dvÇg`ìïØð=6νuÕ¤òO…\ð™òµÚ�†¶’Å Øø¼¿i~nþ诧º)öÒYëéJ*cùéórï9~J»Ã˜è7¶nú“§3©ãÏJˉ>ƒ;S? êý†Ë {Ét›ž7Yp¹‹°{/7}Èu» eÈÉ“S´Ä†é)<1Eζ�´ưÐÔj6»ý®³E¡Öl7{½Ö~«O¦Ôadì¶»m¦Ã"«#c´Úí6â`kuºü8æËÎÏÎ˚ȡ†+.’Y.¥¶Òµ­(ýú­VÓ{´×i4šÜýÎëqŸ‰«Ôj5Xy ¦Óg°È�]®ÀÄŽl¼ìüÜ©lŠb²é$’ê[m+ZÒ�¿Å8§)ç|Ÿ�pžÆ·üË™òíÖ³�ñN#Å4ÛEÉù?"ÝæC®Óh8öƒQ�™µÝn¶Û ˆàÅÅÆŠYò&’Û,¶ë®¥+0?‹ÏÃÇ®ÃÄã=óò«®nÚIbÝq_£Ê²ºÜ,¥nÖæ÷VË K«²ÎšJÓ&œo kqá²Ø¬ÙO=dËÕÃ+ï�¿�mv¿�÷g�Êl6÷Mß‹øåU—êµÐÇO©ƒ™Ü ).¦×:i«lôãØ’ýÒ(ìŽÍŒÙ—']­~7¾5ØÜºüåÖS?m|°møÇŽ‘å[~³_”ú˜9}¿Ÿ‡%i´Í–_I©Çñ%û¬QÙ› §¬¹:ègÛñ ø]x7RæïÏ’ííÒ¤š]¿ñ† –'#êž½ŸH6’OÜ9ºér4}½ÊmËŽ<[ô°M•á‚<‹r.ÞÙ—pe¡¢Ðèø¶“QƸΛUÇxçÖái´: »Q¤Òiõ¸ÑáëµZ�V¾|n·_‰ ‘A1ÙQÛKm¶–Ò”J'M¦ÓñÍF¯�ñíV·E¡Òkðõ:]&›W¨Ôjµøñâ`k5zÜ( ÃÀ×àâÅlPÖGvÒÛiJR”J M¦ÓñÝF³AÇõZÝ‹I¯ÄÕi´šl]f£Q«×Á.·Y­ÂŠ < ~,VÇ 1YdqÇm-¶”¥)DÇ111p1q°pq±ð°°±áÄÃÃĆ<|\L\xí‡lŠ x"²–Ùe´¥¶ÛJR”¥(ú¯¤úOÐÁ~PòIxw�Drè$ņ~+Ñ}·É!›6ÚÝ…º>È6qÉ—m/Ž·bÙ~-+%)u¾¶R¿m?+ùȯâ?÷Ÿ,Ž\X$âý;Ù¼ŠÉ³iZáC~—…nöVË—J_k‹ØÞ²~u¿™Jý´ü¬;äG ¿‰xÿÞ|ª9q “ŒôïfòæÌ¥k‡ ún»ØÛ.])|u®4wczÉOu¿›Jý´|NM½Áâüo�r]¦n·YÇt›mîÇc²šÌ}v§#?/7?"Kâ² ,\|{¤–úÝm-²ÚÖµ§§ª§µ_*ÂTu-LðCø�xkü”üxýQq™žÿ??“B~ªø¢Ëÿ ÿ‰¿‰É›¡ÿUœUu¿ŠÿŇÇï Ô«Þ<ÚÆÎ6ež@õåÇñQòsù=wGêß’°“Å·ÈOïÛØý„<šþ-ÞAÿy×þÀ÷ï—¼ÿYw½{éI•F+V,£Œ4ïÂÝÝñî:kÉ_sgų3‚víÍräV» ÝWcè-â»øqq®¾´ý¡ÙuÖ—Ýe´¥³méö¦øk;’=¿Oy Й’Ån_ì hém—&µËÊÕvŠÞ7¸ÇÄÆºúûp4›.¾Ç–JÙm)lÛZ{«ë}ùü6}ÇÛ¨|„è\É¡·/„síhil—"µËÊÕöŽÞ7¸ƒëëé�¤Øð y$­¶Ò–͵§­}o¢ÄßÂä?ðó¿ ¼qÙíw»=ç�}ç‡Ëµ8ûÏÒqþ¶ïn5Lž=¡ãòúúÙëØ}m˶YpúzY6Î’zÖ²Ö”•RLÉ/¥Æð=«ØzN¢êîÈíŽKïþu‡æ‡È>•}²~„á\{cÉ6¿NêÛ})Ü5’zWÒ¿oîUâ»+�éz·®yÿfòKî�ŽõÏ å\ï}}µ¥·Y¥â,þA´¾Û«K©mÖàëä­+é_GŠì®w¦êîºçÝ›ÈîºÎ=×\+•s½íöÝKn·MÄtYüƒiu·V—RÛ­Á×ßZV´¯£Îòþ]Æxåóšïu¼_†ðž;»åÜ·’îr£ÁÓñÞ3Æõ¹;�ö÷m›5m‡[©ÕaK‘<·V–Çw]_²Š˜9/!ÛrîG¿å{üɶ;ÞO»ÚòÖÃ"KåÈÎÛn³§Ùl³'–K®’Y²s2o¾ë®­ku×Vµ¯ª®nIÈvÜ·‘où^ÿ.]†÷“n¶¼‡uŸ5×_6nÛu�>Ëc—-÷Ý}÷Ë“™“}÷Vµ­k[¾Úªö䜃kË9û•o²äÏÞrmÖÓ�ns¦ºëæÍÚîs§Øìrå¾û®¾ù232o¾êÖµ­k_¶ªO;£µy?{wlw‡6û—ðϹ;/�ö¯.ý øúïá?aò�¯.ßýÃIf’ /Ò»‰~•—_}ÖÙéJÝZÓÕññXпÿ�sŸÚ#>ñ]|äɇß'žeÁ$Uš94ýS›^ÒßRH©mÔ¾*hønEn÷SÙí¥}ß›êÞ¿Œ~¾Ììß�8Î –Ë&uq.s‘ñÖ[$ÖunD��¸²øéK©u—jx„ô»ÖžßOËöz·Ÿã3¯ó;3ÏïøÎvÍ~tñ.q“ñÖ[$Öu~Mý™¸¶Hý.¥Ñ×SÄg÷zÓÛíõõû=]qøêM7uü¼x/÷¹cai»o7¶à’ ¤¶û·=Áyw{ñÌ{«‘ÝXrù[âÅ%+_mc¾ê]K­­m­Ÿ+V8­¿Dƒñ>ùi—«Ót¯…œc>è)Éc§zvµ‘[‘™:]~~ÏŒu~޹QeÛ�>^óu°ÍÅš¯¶}v¶k.¶ŸeÑjüHRejô�Cáÿκ òxéÝ¡dväG$ú=v~Ã�u¶–¹1ÏL\œíöß?/Hî¾Éõºùm­´¯çEÃñùG•«Òõˆ\s:èkÉc§svu‘Û41]5c†+lÙV¶° çþ¶Rç|©ù‹$ÖËf›KÒzØ,¥–Û\x¥èþ¼ÜV«m¶ÖK®›k|ž·Vµô¾”õô¥)H|çí&Ø|™wÎ$²Û%š=7Ojñì¤vÙ\xeéÎ ºº+®¥¶Ö[®ÈÛß'ºµº¾—Òß_JR”€¿ÎVÎ\ÿ’îöÅ’kd³I¥éýdRËl®Æ+þIýê²éb Ãe¬€ Só¿ø�y•ü”ü‡ýQrö±ù±üLü·þL}÷ú«åmfóSø›ùiü™»ãõYʘÊ�âÃäw÷†íÿÕï!Uf¬ÍZ”€Þÿ wñYò?ûÿá~®¸Êe߆»ø°wç÷ú�õ{ÄÓ#ü6ŸÅ“¿?¿¼?«þ*±ûð~þÍ>ðþ\Ý—ú‚ñ™'ÔŽ’5JÈþnºÛ-ºûî¶Ë,¶·]uÕ¥¶Ûm´õºë®¯¥-¶ÚSÖµ¯äË®¥´­×V–Ûm+u×]ZR–Ò”õ­kZý”¥(ÿ—]m¶Öë«Km¶•ºë®­)m¶Òžµ­k_²”¥G��˳;°ùç<ØIõsù·3å»:_lV}LÎI¼ÎÜäÉìƒ =ófÝ_K!ŠÚzý–[OÍ¥Y]‹Ër¹÷`ó¾w�w»7šó.OËs.ôŽßvW#Ýçn2.öÃ�‡=f̯Ùl1[üË-§æÒ­žÄå™<÷°9×9ͻߙÌù�'å™wúGo»'‘nó·Ýí‹+}ÒæV¾–ôýË-§æÒ•�/»_Uß>YùCÞZ+}ºNæò'»;_Oo®M}º®Äì®MËõöúæk´ùuôÄÜYöˉ‹'þU WzÙoŒx׎k°Ú¾”àõëšêN¶ºÚÛw^õ—à÷[[±î­µâ|WU¡­µ»Û1.­+�ùb¥#¯úZzz-(ênN¼ê¾´à¥m§ëþÃémkÕ¶œgŽkt´¥nŶÌjÖŸrü±ÛHÿòšRž‹F:›ˆkÞ«ëNé[€ýÃx‡¶µ‚êÛüãšÝ/¥k�m˜Õ­>åùc¥,ÿÊiJz/#Öëñu:ì ^ VA…­ÂÅ×áÑÅ8¸pG��‘ÃdqGdqGJRÛm¶ÚRž”¥)ö2k 2ö€9ÑòËä<þ1ü|ù+Ù:¼ëð9^Ç‚ÍÖ¼|]¤š}¶7-í\¼n¯Üè³ ­2i¸â»ù÷q[i·Yui[iJÝMù?ï©üpðOÈŽÆÕæM…Ê2øL¼†da쫪Úâr¾ÌËÆàšÍÖ›*Ïèߤø�›éw6[çÖÝuÕ¥iéZÓB~O;æ|ò±5™’ár|¾/á™›êö˜¼«²òñ¸6·s¦Ê³ú7é>)ö]Å–Ùé}m×]ZVžž´ç¿Ê甾ütysäN—;q©åœ/¨öºN½Ýh±03¶<´»7a­êžªätÅÙ×î`ñÞÆæú¼ì¿©l”¦&<µ¤rV”ŽêǕǫ�S®ÿVY|·Ùv]$’]m‘Çeµ¾ûï¾´¶Û,¶ÚV뮺êúR”ûkWú²Ë侨ã¶ëä¾ël²Ë-­×ß}Õ¥¶Ûm¶Ò·]u×WÒ”§ÛZ¿Õ–_%öG—I$—[e–Ymn¾ûî­-¶Ë-¶•ºë®º¾”¥>ÚÕûµšÍ–ëe¯ÓiµùÛm¾Û;YªÕk1'ÏÙlöYóÇ‹ƒ¯×àâÇ.VnvnT¶ÇQÛt’Iu-¶•­iDõ¾þ 8ÿ‰ü'AäÇ�üOgå73ÖE³ãZ-þ /ÿƒ÷Úb]HµXTºÜnÐÜàäÕœëì·'[ÿ£qþ�)Ÿ&tß~~(ô^-ðý‘󯱶~Mrím›?§ÜáÒKz#Žm1.¶Ý>&E.¶ÉÛàäV›Œû¬¶|¯®·éÛLés¦åðýñO£ñ{ˆi<†ïŽ5�²ò_–ë¬Øh4ûœJIoEqÝž%ÖÛ¨ÁÄÈ÷[cíðr+M¾uÖR|ï®»éÛLÙs¬Þøø4â¿ý{¦òÈ®;ƹWœ<çW\é2/‡y‰ãOßi뇑ּ/odÙÚ쮕®ÎÈÇå‡]u"—ïêuóO­Žlí´ˆéw‘$@…ü£óÜ.¶øëó7’ç]K#Êñó±x^5õ¾øëfÓ²ôsõ¾–ûn²9kõ,Ür¸+m=)K®¥)ZÛJÖêi7É4Ãà^ùw¿Î¾‘Ç“Ð}‹Äñï÷ßlÙöƒ+�iî²û#’ï©M·%ƒÛOJR·zRµ¶•­ÔÒ�’i‰À¼ òç›w²<ž†ì>'�¾øëfËŸèrx¢ûn²É.ú–íy$5¶ž”¥nô¥km+[©¤_&<Ÿ'†|rùíÊ06÷èvº_ ¼˜ËÒn!›îù‚�5Ì£ãóbMÿÂó¿MI°z}¿Z¶ú}ª¾Õ¸+~SXµ3Áâ=á¯òSñãõEÄfxOüLüHþL} ú«â‹/ü+þ&þ%ÿ&n‡ýVqUÖþ+ÿ¼7P~¯xók8Ù–yk×—ÅGÉÏäõÝ«~JÁþMÿß!?¼l`[öòkø·yýä;_ûß¾^óýeÜ~õì?¥&U¬X²Ž0c¾<ŽÿƒÏÈçQâlrë‹Å{ëeãß$öâY™,¹<ÿ#]“À#Ƕû¬û´³v¦‹E “Û_tx“MùiuÖÝÖÏ„Ÿ -è�®ÃØå[‹Æ{ÃeÑúÝÞ/»/›O¯Ìà¶cÙZÛH²2{/G¦ÆºZWÝ6DÞž´º¶ÝÖ�„þÿ·¢<þêü=†U1¸×wâlº+ZÃL‹îÊæ³àeðk1캶Ò)ò;+I¦ÇºZV—G�<Þž´º¶Ý"ÏÂýåt9| ðþ½ä;Lmwò¿ƒò^ˆÙK¶ä2éôšþq}qy×Uí>á[oÃÞr}Ç.âVñMT2{$¤¼ªJEºúÇ%�Iþ'à´Ä¿Ä ß·t—Çaqý~LøÜ‹¿¹_é-DØ™6C>>»m6_0ærϺ٧Öl8O Øjæöþm.ÙGK¾Ë½+ÇŸ�ò»¦¾>ûKƒ“‘�¿ïGÆzgQ6,¶Ù$8[©rùG0»"?ZI~¿?„q-޾ZÓì¥ùÑÒïç½+Çßœ¾ð»§>?¹þ—&|mÿxr>3Óz™q¦¥’Ã…¸—+”rû§Ž—RI5ùÜ+‰ì5òÖŸ›Kó£¥ßeÞ•áÿâ'òŽ/~(<Œ— c‡ƒÌ|‚ÃÖø¹ÂqöŒ½¶Ú^ã³? ²u×ýÖø¬Öf[Ñ:ž[“‡—5öÅv4?d—ÝdW×v€ê*£À ü5ÝK;ùØvdyVãt‡IsÎ_®ÊŠ É�ü%å™Z>²Â×åMî¶Ìzåq®e¸ž?箾¸µ¥-ô÷]ox?WEÍ|êÚsÜÌ|ŠâôßMóNM¯Ì²~<\›•eéz÷ yku´‚ü¾7Ê7YùkwÝ®§§§­iݯÃÕÕñóO:6|ó.þëÓ�9Ìù6¿.È~¦=œ“”åéº÷ ykZR ²¸ç(ÛËeiëußv­===kIhþ>’Ïæ>w÷¿yeñÌ-§éO3xä;ìšRüŽ1Ù=½Î8Î7—_el­lÉÛðÌ1®’—R¶ÁY,ô­$¯¤úÓˆMùbø¯“!+å�¾Lw6ÃgǶ½•µã;Ù,ÁÌëî¼²Á¶8ÑI$•ŽíÇãXÙ³zV”¿'&Ký)îô¥mÿ#=ï_$|Úò/µqó±v:þÃÙñŽ—�tµÀÊà½d†ì1,–I>�7<{ŽãçMíô¶üœ™/¥)îôVùò+ÞÕòCÍ";W7a¢Ïì-Ÿá¹x5—î9\€ÙáÙø¶I$ŸOôÇãÐfÍíô¶üœ™/¥)îôT}ó§äÞw•_)þ\s_¿fäñn¿ìlîƒà’ï-ßjp8§EúõÄÙœ_&%ÃÆã¼×–höœš(`¯Ò¤ÛÉoõºûïºíi;Jœ�w£áOâVO:¹´ýáݸ¹^*õ‡"³[±ÕE““�µîŽm…��±»‚k2ñ/‹/UÄ5åcK¿ØÇ|YE—O­ƒ_«Õkp ²|x#Žb²Û,¶–Ò”NSC¡Ññm&ŸŒñ�6§Žq¾=¬ÀÒh8þ‡]‡¨ÒhôÚ¼X°µš�>§_>³Y®Â‚È`dž;"†+-²Ëim)Dâ´ZÒj8×Ój¸çãúÌ.‡�èµØz�&“M«Æ‹ [©Ôjµðãàkušì8lŠ!ŽÈ¡ŠËm²Ú[JQd“I¦ã:mGãš�_ãÜW�¤Ðèt˜š­6“MªÄ‹W¨Ôjð"ƒ[«ÖàÁ8øðÇdPÅe¶Ym-¥(û¬ú¯¦¬{åÞKØß%>dr ¤’KõÝϼàtºI¤žêEÕ˜šþ±‚:_%–]lqCÄ-¶Û)OlvÒ–[ZÛm+Zæ>T¹ÇöÂùòÓ}[ï“î³´àþëå¾jÓûXëõ½mK)|–Gu,ŠœOÛm¾žÛ-¥-¥kJRµ®“å;›×°>C<²ßVKä®lí8Gºùoš´§YàkzâØé|–Yu,ŠÞ)í¶ßOm–Ò–Òµ¥)ZÔÛø…ù¿ì™77Üc>–·—u¯ÉȂ뮲>CÖ}ÕÝo˰.­ÖGZMªå|S7JzzRHn¥+u=+^t9üÐ'@6ü+ÜÏÌ_ë먨å/Ã;ýËü¬þ¿zÓûå t~OîcåWõùÖŸØÿ(XÕø=ÿgo�ËK™þ£z ,$�ÒpK\ÇÏÇíWòK÷³¤PÝh€ÎOí6ò÷»§?R=v€Î'í2ò÷·§R�z¬#ñQþÖîqýäºOûËq¹ÉG%ÑÆtûá‡öŸxƒýð7_Ø/tŸâö’x«ýyòÕï0t‹âö�x¯ýzoÿ°\îgá²ýµ>Æ+þIýê²éb Ãe¬€ Só¿ø�y•ü”ü‡ýQrö±ù±üLü·þL}÷ú«åmfóSø›ùiü™»ãõYʘÊ�âÃäw÷†íÿÕï!Uf¬ÍZ”€Þÿ wñYò?ûÿá~®¸Êe߆»ø°wç÷ú�õ{ÄÓ#ü6ŸÅ“¿?¿¼?«þ*±ûð~þÍ>ðþ\Ý—ú‚ñ™'ÔŽ’5JȪžtö�Sáw•݇úJ-N_ñç·¶:lÙoº:S’ÝÁwx¼[;­­.¦N#ÈÅdž”­=e–ÚzÓòµ«ÌÎ{ý«üGòk°,΋]™Å:µvºŒ™¯¬vÿ"á[˜øæ5—ÛZ]IówÒcA)ZV²Im=iùZ׿_<§Xx“äÏ?·:-v_èžÔÚj2f¾±Ûü ³…îbã˜Ö_mmº“ço¥Æ‚/JÒµ’KiëOÊÁSw þ<øÇägE®®â^ŽèŽÞîõ4öúí$ë>¿ä<ÒÍu=òÃg®uÚZEöße?;í­?*«%eŠÎÔ£3޽}7myÑ}W�JÝ‘Ù}ÅÖ])íûeæ\×IÇcþ7[e?;cOË‘�OæÉe?:™wÇî wh÷×Hõ�´÷]Ø�»ÖÜÚzÒžµå¼ËK ¥=k™¯¥=k°ÿψ?ú%ŸÏS.xÿÁ¯ìþøé>µŽžëû·:߃Ùo­)ëw,æ:m )ë\½})ë\ÿÝž øË?ž¦âüwq-_>ùðoƒïq/ÏÑs0{Ù\³°³ä²_l¶ÃÖoC‰‰4Vý·cfäv•ÒÓÝöVLJVžµ·ìˆçãïx8g„Þ¿7“ë°-š<='=Ôß7ì--™2K“÷-'5Õgãcß%Õº\xã“í¥ô­lÄðûÈ]O•^3t×~j©‰ �‡Âõùü‡_�õ¾é¥æºë¥Òs½7Þ+\‹±t|ÃY�‹ ò~t°ÅlŸ’ê,¶ñÈ=W•4tß}êþ罃Ã5ù܇_�õ~饿ºêË¥çZo¯uùÅÒrýnn47I_t°Åeÿ’ê.oðëÉŽ!æG‹}åý‡ºú×�óitÚÍî?&‡ˆòLÜ;qù·_åïqq0!Øîzëšbì4[ é��u3õÓ[tQÝml·i›&Ù&É€ ¥ø—üœ³³|µà^8i3>·ñ·„S/‘GHlŽ¿Û;¶qõ—mÞ"È–Üü-Àã¿F·ÙøùY‘úzWÖèWþ"�#mìO(x/�zl¿«£ñó†ýû�Gm•޵ìnÒƒU¿ØA}öM$y¸ºî�¡¬]m—Á‘——g§¥k[¡qøˆ|Œ³±<ŸàÞ>é²þ®�ÇÞ÷ÞA¶R:×±{B VÿaòG<‘æãk¸FаÝu¶_FV]žž•­n¯“ñ|ù…o;ò7¡ü+â»Øòt= Ã2{c´°u\ƒ"lzö§lY O�ò¾7lVbboø7XiàÚk².¾Ik�Î$¥)·×êFÑt{�êM«ð¹ô¿ð{ǯ#;ï.ßL®ÏíN?׸¦ÅöË“©¸Ýû™¶8yw[ë~ÛsÙóãÉm•öÖ]_çzÖÛ}&9ølzƒø?Ð]ùÝùVIf_eöv“�k£ŸÛÿPú·�Ý´®Ç "êzß�±Ýv>V<”¶¾•—[ùßm´ô˜—á¸êÐ ß}Ý•eÖevWfé8º9±ý·SIÕÜ~í¥Û <‹©ëv6ËsØÙ8òRÚúV]wçzÖÚzX×øDw‘;~9Ÿ©ä^JwžN·I»ÈÌú˜<«©ºCMâùÚìd¾<_Ñ�§É¹ÎÒVÛ%šø)JÒ¶Gk(ä“FK¬ÞhvÜýâ?’ÝѲ‹Q¹ë¾�ì¾KÆs¥¤—[g1Ââ{JpÈ-¤T­ÿW7•I‡Ÿ’”¾JzÝm=n¦¼ùmÚSô—‹¾BöÖlZý·_ôßbòn=“7¿éÿ uÜ[g'Çþ‡K®÷æò:âÃoä¥.’žµ¥=kMzò×´fé?¼„íœ<èµÛ^Ó�‡É8öT¾ÿgð§‹ìïâ˜ôúvÝu$ÍäuņÏÉJ_%=kJzÖ˜7Éîä·Ç_¼†ò ýE܂ΊèÞÙî;´6ËHnÝÛÖ< Í«¨¶jß"»eM'Ñ¥Õºßmoõõ¢ª Öµ­kZÖµ­}kZýµ­kùkZÿ5Xê²Å(wß|·ß,·ß$’_uòI}Õ¾ûï¾µºûï¾êÖ믺êúÖµûkPä�übñÿ™ùQäSø÷Àl¥97jsoÇÎ’ rqt:»ë~o$å[qé\‰5Íåšþ=uðß“�£Õ×êgr>O��öM6³ŠqÌ<½–U±×ê]�‰},¥nô¥s_Ž=Ë|˜ïN¯èŽm-ä]™Ë5üzÛá¿#I¬»êfò.MŸW[4ºÎ+Çq2¶YVÙý¸ø·ÒÊVïJWn| ñ—ùÙåïCø£Â²ïÕl{›ãêw\ŠÈµùq¥ÂÍå=•Í£ÁÚmtx{|Ž×Ú-žÎÞà›e6-˜�ÝõçŽÚÚMÐÝ%ÀQy%ä¿S“¡ÀïNüîãÁÑf_™š\>Îì>Eͱµ9rA,Ðß“®‡wl2VËï¶·YZÒêÓíxG‡x†LÛð­ÿs?1¯®¡þ s”¼ ï÷/ò³úýëOì{”%Ñøi?¹�•_×çZcü¡cWà÷ý�¾@-.gú�è$°’wIÁ,`s?µ_É/ÞΑýCu¢_9?´ÛÈOÞîœýHõÚÿ8Ÿ´ËÈÞÞ�ýJuê°�ÅGû[¹Ç÷’é?ìw-Æç%—GÓï†Ú}â÷ÀÝ`½ÒˆÚIâ¯õçÈW¼ÁÒ/ˆoÚAâ¿õé¿þÀ9s¹Ÿ†ËöÔøaÿ¯ù'÷ªË¥ˆ+ –²5OÎÿâ=æWòSòõEËÚÇæÇñ3òßù1÷ßꯕµ›ÍOâoå§òfï�Õg*`o*?‹‘ßÞ·ÿW¼…Uš³5h R{ü-ßÅgÈÿïÿ…úºã)—~ïâÁߟßê?ÕïL�ðÚNüþþðþ¯øªÇïÁûû4ûÃùsv_ê ÆdŸR:HÕ+ î¿*|kñÃì®÷ïn«ê›ÿG_¶ÆÕó^m¡Ór-¦wßÓ踾Fm9' ­eŽëm³"Kî¶´¶ÚÖ•a.ßòSÇÞ€Åû×u÷OYu}o“a‹ƒÌù–‹IºÙâGuöÝ&—�åæÙ¼Þß[âºÛlÃÇžK®¶´¥µ­*Â]½ä§�Ý‹\¾êëÝ…~ǘó›w³ÄŽëíºM/ÊÌ·{¼¾·Åu¶Ù‡�<—Ýmimµ­*ĽÁß�ãÞƒ•÷çtu?Hq}Ž}5ZþGÛ½‹Ä:ßEŸ´º+ç·Yƒ¶æ;�6fÆècºúA—K[iZÒßJ8±ß߉[Á¾µ¦Ç[Òüo´¼ŒÞãÅúý†§I^³ëìÉ®÷Rll®GÏ"Ææ˜wãÖÚSßʆÿ_[/­>× ;Çñ øY×”ÎÀê=ewþî,{dÀÍÓèîëÎ ““öÒü<ÞAΣÂåØwGZåÈ8îdWSí¶êÓÓ×�½áø…|0ëÊg`u�²{÷u 6ß�™¨Ò]×¼'#í¤˜¹œƒœE‡Ëq.Ž´ÿË�ñܸ®ü¶ÝZzV¼ò;ñO|Yt¥gÖõ·#í�(¹–oñ~�NõÞn“‹k7z‹¤ÇÂÄÞsßÉël|�6ã2ÏH¶\{‘A÷Y¬¶JV;dõ2¼»äsÈïêžcÕC«5Ü_¯p;/¬µœwmºä{ËtZîS‡Æ9†/2äû+õÚíîTY<³I\I0´ú«mÿTVKo÷ÇHý7ÅßËw%ùïåë.UÖ<[¬°¸×Öö\êô[]Ç"Ý~†Àä�qÎa,ä¹ÿ£uû‰âÉäú_º]‰©ÖÒÏ\ŠßI=ÖR?Mñ}òÕɼÿïäë>UÖ\[¬ð¸××v]jô[MÇ!Ý~†Àä�qÞ]+ä™ÿ£µû‰áÈäÚ_º]‰ªÖÒß\Šßmþû)­øaùՃ囷¼‡ë­×NñóºÃ¯ºß›u߃°ö™Ì9~¯+�rímr�‡$—‰p-gè1ŸŸÃ±qñaÔE6<ÛY.—#"“Gf?~]Æw!pïnGÖ<û�[KéŸÝý‹Ö]Q¯ž)î†LO¥¼¿´v’ûl¥k<9z.³ÊÃ’ÚÖ–Öܯ¶¿’•ã?ÏGiÇ×?¼ï�ÛY£ÏîNy×]_®ž ¯†LzÛ»¯dmk},¶µ“3�õÞ^$–Ö¶Û[r}+_ÉJñ¯ç‹´ãë¯�uÇí¬¶g÷;ë¾°×Ï ×Å&?·uwcmn­,¶µ– ½^eâImkKknOÛ_ÉJð£ñ"÷Ì]ñäŒ|¶n%Ë»¯/¯º…×—ýã’ËÎyž§aØœJ;ì²ël‡wÑüs•Ó#ßémضIo¯ºëiZóP/@íU0§? =mj|›x‹Ç¦“èÁ¤ì\¾É’_·Ûd�GÅ9haGw¥’WýU²â0ÁO³ùéië[iùÔèÿÄW_CÙ_#~,h²k[qôüï?°d“ÓÖØæêþ'È»_Kÿ2OJO´âðEJú=%>Ú~Ztsâ?¯â쟑�´sÖ¶céùÖ`I'¥klrõ‡ä=‰�K½,¿ÓëìøÄR¾ŸÏIO¶Ÿ–�Îü7]w7a|Æø�tš 7ºN opv'!¾–G|(xçIö|c•õ+JÛaltÑÅ[iuÔÉš/ÉöÖ–_¬IXxµˆ¿ÅÌ&Úy«ÒÜ*=�ÞuüGÆ­&Úì 2¯–-nó–öOcÛ±¤˜¿Rè±srôüwY%õö[$�ý*ÝZÛK=!mø‘¹nFËËÞœávæÒ}oñçU¹¦™Éfë–v?‡eI1þ¥Ñcedê¸Öºû«í¶ûâúuºµ¶–zBÿñòÙö^]ô÷ ³6“븯�z­ÇÜìȺK07\¯°¹ì[I�õ+>VN«�믺¾Ûo¾/§[«[ig¥|Œw·3·>Vø…ÐòaY³­ü{幉²¥÷VL¼îìì�� Øá]d­–Ù¯ÇñÿKkKi[«“ZVµô¥-�Z<ó¡Ö²{áCǼ_þ8GÍz¯IÞ�‡““Æ1ø·"Ìç]é�gglµü»È¢ÎÎßp]_"Ââöäæúå×_¢ÆŠúGlVEV�6tÍÕÐ.¿—xøíŸüt×ÍJÓ‚pnSÛ|—èeY,Rçö&æ-ư³1ì­k‹Ÿ§×õþtöÒÿKîƒkeÞžÛ­­ÑüJ½ÏÓ²ütñÿzÿñù7jòHâžÉ —3�mbãg&+}kv§ „l%¥·V—V•·z{n¶µˆ·âNîh¶�—ã·@àOëüáü—µ9‘Ol‘K™Î¶±qž5�•¾¿wÍÕapŒùimÕ¥Õ‡emÞžÛ­­`ÍøÇü˜·×ÃÏ4»êûéNcäÇdñ‹õrÒ”¶êÏÕ½%¾ÇÝ_!¾·VÞÀÇ›+ëu¾‘I-)K¡­ÑLF!¦x!üG¼5þJ~<~¨¸‚ÌÏ ÿ‰Ÿ‰É�¡?U|Qeÿ…ÄßÄ¿äÍÐÿªÎ*ºßÅâÃã�÷†êÕïmcg2Ï zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆ–/á“ó>.=Ìû'ÁÞg´úx<þ¹Ý¹ÒÖäÉm,·˜èµ1CÙ\O”‚\‰§ßq ^6â +$xØöh³¯ô¬¹?l¡w—±hù_axaËöOš×7µ:v™2ÛKiÊ´úÈ¡ì^)‰î¤“Ë6ç‹kqöøñ[ôà‚Í>ÂJúÉ‘öÉ÷ðêùw�•ö†|¿gìÃæµÍí>ž·&[im¼«O­Ž.ÄâØ”­²O,Ûž/®ÇÛãÅJÇió䯬™lé¿·È4s`v¯Çdr*Ó' M¯zøÕúckZÒLîÅÇîn­ÑSqÉ)duÄË®/,Õi´úË®“ï£e•%)ªfiq¥¶œP ù Ýü7ƾŽíNùì ˜àâ}U·œÃg rñ0r6Òë1/»UÆõ“æÉ5ÛÞU¸¿[¯ŠêúÏ�—VÒ·_JW÷¯qq/:s²û³�d[ë.ºå»8þ󋉑²»Y‰|šý¶\É"Ç¿uÈöu‡_�×R³æäž·_JWw¯pñ/zs²»¯œä[ë>ºå›(þó�‹‘²»Y‰|šýº\É"Ç¿uÈöu‡+®õ›3&(éëuÔ£÷Gnðn€êÎïÍÚ_¦ëΡà\¯²9®Î{ór±x× Òfïö÷``Cþ¨ÙläÃÁ¾Ì\X©Y²².²(é[ï¶•ª¿·{G—wwiö/qsìÛ6<Ó´9¯%眣.(©5û®S·ËÜgÇ…‹oô<-~<ùu� =#‚ ,ŽÊRÛiJV]ÚÝ—Ê»›³yÿmsœË3ù‡dóC͹LVV,jí¹&Ó'k™=n¾˜zÜY2k4×Ù=–Ge)m´¢³ÎÕì®Uܽ™Ï»gœf[ŸËû#—òkÈòb²±cݶä{L�¦\XXþë­Ä×bÉ“X± ·ó ‚Ë#²”¶ÚQL¿”ÞEsß-üŒî�%»7&I¹¯tö#ç{LJìö»l>=‹·Î’í Ñeîòóö–qN Ç£ÅÓiñå–ÿºj°1à·ÒÈí¥1ÛÀ< €- øÉè übð/ÆžØÁ—‹È5g®äüÃa�.v·›öN^ods-.U‘Öï}x÷$å™:øïº¾û¡Å²µ¥+ù´²[㯢æñ¿Â_z“: ¬Mî›®µüƒ–açEdYšþiØY�ƒÌu3ÒÊ×ßM'%åXQÝZû«=ž´§ä¥�ÿ½/Ž>øçÔÙÐdâïuw¯ä<³6+!ÌÀæ|ÿ'/°9~ªzY[½ÿ¡9'ÉÂŽê×ÝXqìõ¥?�¥Ë?>/Y႞,øË6·UÈ:³¨8Î`bj¶“îµ7ö×"Š^cÜYú­¦DPK™«Ûv§"Üec]ôã¶�Ím-²ÛiKi½­Òn‹uÀ_ü@<ç†ü]wž²ùkwaò.¥àÚ«­šè®»&þÎâü·a¶ÛeÕš“qî%›eÖzÙOeÕ­k_OmÜŠùÍæ0ñO�~íÁ¬÷cæó�ßVðímÖKX¯¾i{/‹ò=„¥-º²Û‘ ãy–_g­´­—Vµ­}=µäwÎG1‡Š|m÷^fº Îq»êî­ºÉk×Í/dñŽG°†”¥—}[gÐñ¼Ë.·ÖÚVË«_Zú{kÅ¿Ä;Îy^ü7ù¯»âûKõ]§ë .DwÖÉ2x÷d÷¯Wu÷3ÕÛé}—_f߆rm†4–Ó×Ö)nõ¥iëEtè 0©Ì)OÂ÷ãî¿•÷¯ù%¹Äƒ"½AÁøÿ^pÚeàý_£É{W3i™ºÞê³nüÜ]–“‹p©p%¥¿�v.þê}”º¾²[ü6݃É{—½¼†ÛâY7ö­áÚ¿â5ʶX¬ä——±ÎÞî5™—Ò¿vÚi8çû”žßή.öúWì»í’Ÿá½è¼Kܽéä&ß9«Õ¼;CÀ8�Þ°í–8÷ý•—±ÎÝî5¹—SýM²ÓqÞ!\)=¿�\måô¯Ùu}féø9~?j¿’_½�#ú†ëD¾ri·�Ÿ½Ý9ú‘ë´þq?i—�?½½;ú”ëÕaŠ�ö·s�ï%ÒØî[�ÎJ9.Ž0§ß ?´ûÄï�ºþÀ9{¤ÿ´“Å_ëÏ�þ¯yƒ¤_ß´ƒÅëÓý€rçs? —í©ðÃþ1_òOïU—KV-djŸ�ÿÄ{̯ä§ä?ê‹—µ�Í�âgå¿òcï¿Õ_+k7šŸÄßËOäÍߪÎTÀÞT#¿¼7oþ¯y «5fjФ&÷ø[¿ŠÏ‘ÿßÿ õuÆS.ü5ßŃ¿?¿Ô«Þ&™á´þ,�ùýýáý_ñU�߃÷öi÷‡òæì¿ÔŒÉ>¤t‘ªV@›¹Üéøî«a½ä]n‹G¨ÄŸaµÜîs±uš­f5•—'7a±Í– L,Lxí­×É%öÙe´õ­iGàÚm5š=vvãu±ÀÔj5˜³fì¶›LÌ}~»_‡�edÈËÎÎË’,lLX#¶·_$—[e¶ÓÖµ¥ƒkµÕèõ¹Û�ÞËO¨ÖbÍ›²Úís1õúÝ~=•’|¼ìì¹!ÅÄÅ‚;kuòIu¶[JzÖ´£þV´¶•ºêÒ”¥+ZÖµô¥)O¶µ­köR”£–½ùóeñ·ã÷é<-§‘>ÐäÚì{'�Šôf_jäl¯¾Ûn®',ãô»¬ñ³¢¥Þ’E™½Åº;é[.ô¾ÚÛNk÷‡Ì/Ç¿E}ÿkßÚ>ÅäXXßx‹ŒôÆ.WgägßOo®?&ã¶K׸yöVïK¢ÍÝb]eÔ­·z]miNmwÌÇ×EýÿißzNÄäXXÿ^>5Ó8¹]›‘Ÿ¥µ®?&ãöÉ׸™Ö{½/‹3s‹u—R¶Ýéu·Rœ®ò;æÛâËÅœ‰u}Ÿæ_Tm94Sò=t¼C¨òö�ëɰ7¼ZJãmxß%ÀéÍg7‡�ï)°§Ýc‹�Ë©²ì‹o¶·Ò‘K[8¥ß¿Š[c|» o‹ž0àãÃ]¿¢¹§~rLŒÙ2ðiJ{íÏë.¼Ê×ۯʾï_mÑòÜ›m§å¶¾¿g{Ëñ,f]~Ã]ã_�øÐÇlÑ×WÌ;Ç�Ë“|øþŸÑmÏ뎓‰LY«_çn�•Mm?vÚþG»Ãñ(æ]&~»ÆßqaŽÉ쮳—÷‡ —&üŒjSú%¹ÝsÀò±)‰5küíÑò™­¥?-*��’ŸŒs®01¶¯ûÈžWÁôRÏ‘,s§é‹ÕX8�å[e’ëîÛðèµ¼Ãq¯­–zR=–Ï;ìºêzú]w¯»¯å¿ä¼îÈÇßyÊøNŽY²$ƒŽõ 1º¯ «l²\ öÜ:-o.Ü`VË=)Ëe›éK®§¯¥×zÇóÉ¿ÄmòÉäÅ7ºÊyO¸föÝEkÂüdãØ½U]DÚ›¡—ï.Ï®Fÿ¾õ·lò!¥ùqÓ—]�5.º/§H.¬Ndæfeì2²3³ò²s³ræ“#/32yrr²²%º·Ë>FD×_4ÓI}k[®ºµºµûkW8³32ö9y9û ¬œììÉäÉËÌÌž\œ¼¬‰¯¬“ddäMuóO<·Ý[®¾êÖë«_ZÕÎlÌ̽†VF~Ã+';;2i2ró3'—'++"k«|³ädMuóO4·ÝZÝuÕ­×V¾µ«Š¼¿˜òîÂäûÎoϹW$çÏ“ìr7——rýæÏ’ò~C¶Ë»ß•´Þo÷YY»]¶Ç&ÿ¶Iò%’[ëöÖê¿3ó?3Î:Uñ äD>2ü†øáÏ6¹·aq.GËÿµG7¾M�5šèøßkbÍÂ)´ÝdÉZAúŠovØ;©­“ó?êm+öV”º�ø£ï¸üuóÛÇîe±Ì“‹ò^S^¬æ5ûõšüÐ=Ÿ�'‡?q‘5ÖÃú#�ržâz__OMu+ùiJºñQßQøíç—@s=–eøœ_’rŸí]Ìn®mšüh;;N!Ão<—[èž5¾Ù`íæ¥ÿ›é®¥~ÊÒ•§nÿ÷”×ø³ò¹ã–N~vn'ò?;ÅÎk»[Ë/esM¯×uÖ'¤óã}ÃñÖqL¼ì«.­ð`cOZ[ÛeÖg,UX’µÔ ŸÅß1n»gÆßu™^èø ä�¹ÊãÆÎŠly6Ý…µ�Œql†R]v&×G©àùÙ6RZ[}qwV]J{/¥nˆoâRîë6Ý—ãÏ�Ü«ë â<‡µùL0eG.,Û.s³³Œñ\lÌhîºì}¦—[Ãv[I)K¾ï·²êSÛ}+XŠ~$¾ì�mÙ~=øõ®Êº±ð¾#È;W”E\rãK³ç+8×ÅÍÆŽû®ÆÙ鵜;>{i%)}qöÖ]J{o¥k?Æ=ä߬¾øk¦ÝIOd|·É¾ÈãÒk+H®ú·fu_Jn±w7}—IÑçðdcYù)t7ßùcE5”d�kHçðÈõ¬|§ÎnÆì,ÝlÙX=[ãï%›[±¤5»WËyŸ-áÜ{]I'¬WÙùÜR›Ë,²—Y}öÛ}iZÛmô¬‚O^üËìn}•®¿'­ú'}nÃéVèµ¼£˜ò¾'§ÖúÍX¯Ž9³8Î6êËm÷Y}ÖÒïO[m¾‰~½§#ó'±9î^¾üŒ·è½õ0³þ�k»”sUÅ4ÚïtµŽë#—3�cîl¶ßu·Ým.­=m¶ú& ø9ú‹s»òûÊþ÷Š|?àçZxã¦ê�–$±]vlœƒºû/�òÎ?›‰/þ[Š M_Cí¢›×óî®Mž•ô¥Þ³¿M}5e…` îÿ¦úmÇÊ'p륟2k8¯ é½¥ÿOìŽI|·SÒÏõD’×Óí÷]žmü›Ÿ’.Ñ×_6\¶ñNÔڬɺ•‡<ž¦å5‡_JI}mĺ^Kt—R´²¿^ù+ééZ]t ¾x7ÒmþGûC_|Ùr[ŸWSèb³&ê]ßuÕ¥þ-Ža‡É~R¸Ö—<ë'ëßú—‡ìnË�È%ÌÍæÝµÏ¬“Yt9I6 5üâ nº[b’™6Ëm,­–Ù}üKq½ÇD`Àßü¼œ|Xëm²dÏ`÷¯vaMõøß$æsê¸=ôŽh-¿¯øv&/áWbM$•ÃËÚq­6fTt¯¶™™ÖŸ•Pß͘PùÁòMäÇsèwtÝõ®«˜W©ºs'‘ÍɸæGWu46ð�#â9’ãaLJÇ{+c«ÍåÖbÅ#ƒ'�MùÒÝ[¦“JrÓÇ,À¦x!üG¼5þJ~<~¨¸‚ÌÏ ÿ‰Ÿ‰É�¡?U|Qeÿ…ÄßÄ¿äÍÐÿªÎ*ºßÅâÃã�÷†êÕïmcg2Ï zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆÈ=OÚ<פ»7�ö÷\n&Ðs¾·åZ^cŶÑRÙ)‹¸ÑgC�‹Lœ{ýaÍ×äÝÒÉÆ–—C“�%ñImÑßuµ÷½[ٜ˦{ƒö¿^í¥Ñón½äú~[ƶqÒ·ÛÓK™dåÁî¶<Ýv_Ó¬9XÒzÕ�%ñIK¬¾ëkï:»²¹�Mö7ín¿ÛK¤æ½{ɵ³�lã§¾Ø6šlȳ �* Ö‘æë²¾�aÊÇ“Ö,œy/ŠJ]e÷R¹×Æ?"»+ÄŸ º‹É> Ú]ªìN›æúni ºì;»qfÕ�uĹt;=6×;†ó�ùZmÞYPSa¨ÏÉžêY5ËE|3ò¯€ù§ã‡Zù××C‹�ÍtöÓ‘q¯¿Ù°ÏàÜßY_¹rî¶š�aÍ~^ƒq–C4˜øÿ~º Èã¤9V¶Mø“äÏò÷Çþ¼ï¾u˜úîg©¥w\~ì»s3¸o/×_\UÃörý IoÊÑn!’8æ¾i›‹Xr£³èÏkdw‰~Kð.ú¯{ç�ßþc©§é¾?\Ës3¸o/×]\.SÃö’}YnËÑíâ’8å¾i™‰Xr£²�Ïkqgˆ~Qu—š~5ôÿ”]?“—7î%"×bl£¶-¿Ûceåé9o ÞYoÆý?Â9~«;QŸv=óbß—…%ØòÍc–ý¢lƒc›‡¯â^ó²™9|GÀn¿ÛWéë¤Ñv—�àåÏ×fK�vYõÆÆ82cŠX"Ãʳ’çcäE-—_&šx¯¶ø¤¢(ˆ“Í{f›‹ø9Á6¾´Ä“IÙ=ë>\–×ëß sºë¯6Á“m·Ùn<ñò<Ì|ˆ®¶µ»Q4WRëo¢)_ˆ{ÍKfŸŒx?Ávž´Ä“IÙé6T–×ïÁ\î»ëìøážÛo²Ü|ˆùdÅuµ­ú™£º—Y}¤üZß#ºí´c¾ÆÊäœç'�öדWësµ¹”Òp­}›®¨êíÖ51³åÃÛò®U��Êòb¬ºý†›Q--ŸoTDQSEq ùücøÓÿ ?9üxéÌít;>%™Îp¹bãeÙ=p'뎾²Ng̵¹²ãÿDÇ·‘ê4·ê¡¿Ö”ûÖ|Tõ§«x~7üvÿ…š� Õºøvht?Tf`C±âÓó,N[Ïñòâ–L ¸¶î[ÊõÙ×Cét1òv¦º¸¯­iJdçEJþW^þ üAÊó;äóÆ¾ ™¨®Ó¯ºÃ”Cä7nÖ}6§�ébà=3›�ɱõŸK¸È‡aÆ{ŸSGÄó-ödVË7ÿR°Ée—Ñh*Èu�km@ÑüN™òáü~õ¾|?zÿ)&²=¯“ü&ئ¾;&‚>´î-¥±äYmiLˆi“­ŽúYwÙI-¶ïËm üF9óaø1À1â’k,Úù9Àð2-Š[ã²XcënàÚ[E–Ö”ždkcº–]öRKm»òÛDeO`åpÏŠ¼>9�vÊ‘vß“ÝA×ÙÔÁÏ“ì]n‡±;ZËw8öV–íu¿}ë(kn=þ¶Û™H'þzP5B= •g é~n)“ÀoÈk�™œÏÉ^y±®gЭ“äk´ü­8þ5gº;+>6&v».ë)JÝe—Í'¥iun¥&÷øu8µšOyfúèl¦O3òœíi“X+dÒ`j¸�^ñÌljÍ[-¬øøùšœ«ìô­ÖÛ|×Ò•¥}Ô¤Ü?×�IàÇ,ßV)“̼‚çO¼ýÙ4˜:®%×üw³VËk>>>^«&û=+u¶ß5ô§¥}Ô¥žŸ…k©1ºßâO…ó322¥ïþðî¾ÛÍ‚jzG®ÉÓo°z"Ì~®íþNF4¶Û[ñóðæ§æÝmkÿÆ#åÄ:þ%âïƒ;í�#Ûl<ží _»oqöØ: [þ³éÜx61ÍØèyFçaÌfÍþ̜¨s8þßÐ-­¿^#è°¢Ò�øfß…oû™ù‹ý}uõœ¥ãøg¹•Ÿ×ïZcÜ¡.�ÃIýÌ|ªþ¿:Óûå ¿¿ìíòùis?ÔoA%„“ºN c+˜ùøýªþI~öt�ê­ùÉý¦ÞB~÷tçêG®ÐùÄý¦^@þöôïêS¯U„~*?ÚÝÎ?¼—Iÿc¹n79(äº8ÀŸ|0þÓï¾ëûåî“ü@~ÒO¯>Cú½æ‘|C~Òÿ¯MÿöË�Ìü6_¶§ÃøÅÉ?½V],AXlµ�ª~wÿï2¿’Ÿ�ÿª.^Ö?6?‰Ÿ–ÿÉ�¾ÿU|­¬Þj-?“7|~«9SyQüX|ŽþðÝ¿ú½ä*¬Õ™«@R�›ßánþ+>Gÿü/Õ×L»ð×üþÿQþ¯xšd†Óø²wç÷÷‡õÅV?~ßÙ§ÞË›²ÿP^3$ú‘ÒF©Y8çâñóÃÎÒçݦénÚí~ßë}Ö.—‘áfß§ë>dÓë±öWýÓ–ìÿ„œ£.¶ãæÁtRGÇ.ÃÊŠO«EñÖ˯á÷š:â_esž’ÖuivluþËY¼ÂÈ®§¯8-¹zØ6~Èy^Îí÷$ž´ÆÍ‚øä‹�ɉ‘ŸR)ï³Û[¸…æoÎ_Dx�Ù<ã¥5�IÚ=�ÚÜg‹«Þáä×U×¼ÉòuÐl½°r��wÜ�"¿vÌ‚øä‹A&&DR}H§¾Ïmo�ïÈïâ/ðÏ㳸¹ç�›ÎÝ]ËäãúmŽãŒð�‹�p-û”q�72â¼o•ö1Þë3±¿Kñ~E��~f‹IÈàÆ‡&–_ë“d¸öp¿?Ÿ]£~VTCÖ^9ènÈÊû¬œ?‹có~i~DwEnדv=¼‡I,Ð[wºÌ�v—U5/üêVž”¥8eÞ?ˆCÍþÈ¿+«1zãÇýù݉7ã�sne÷cúrÙò.À³w È¾ÏZÖÙðtšÙ­»í¥iZQÃŽïüA>nöEÙX]]�×=¤¿"jâKÅøä<Ó˜× [+0ö\‹ŸÛ»ÑO$t¯­³àéu³Rï¶•¥iOHÆù;ø¹¼ñín4þ5u_Mø¯ÇsñutÖï2±²;Û¶ôYø·{ö“crŽg�¢ê¬Ü ­iKmƒ'‚K.<~´¤÷ßZ_o;‡ÈÞþògß¼û§´;o;²þ�¿°¹Ç#åXú˜æšYïÇÒàî6X:L?­5÷R Há†ÚÝ_Kiêä/myÞ½ó°‹iÝ=ÁÙ=§—�,ÒàS�s-ÿ$ÄÕ}y%–H´ºí¦vF¿I‹Y&¾¶Ã‰0Ùî­-¶”r+¶<�ï>÷ØE´îŽßìŽÒËÆ’[ð‡\ËÉ1utšI%’6¿i�‘�¥Åú“_ZC‰1[Úz£ûä§Ÿ>iù…•²“É�'»Ÿ·õ[>Co+»…ò~q¸·¬u|‚,²ßdvêu8QÇl—ûm§¾ï\0Ãì@ÔPý¶ë¬ºÛìºëo¶ê]mÖÖ¶ÝmÖ×ÖÛ­ºž•¥Ô­>Ê¿í·VÚÒëk[n¶´ºÛ­­i[kJúÒ´­>ÚV•ÛnºÛ©uµ­·[Z]mÖÖ´ºÛ©_ZV•§ÛJÒ¯Ó…›™­ÌÄØë²òp60fàçaO..ff,¶O�—‰“ñÏ�“�üñïÉÜi±iÉù§ ÆÔö¦¯ , 4]ÅÃ&—‰ö޾ F&ãy.ŸI—Ì59úX²g¦\Ú̉#޳ÒÊt½-ço¨óååâàbäçgdãá`ácÍ—™™—4xظ˜¸ÑÝ6FNND×Y ðCeo¾ûëKm¶•­kJQürrqðñçÌÌž\LXeÉÊÊÉ–È1ñ±à²égžyåºØ¡†­­×ßuim¶Òµ­}Ç'' #33" LLH%ÉÊÊÉ–81ñ±àŽégÈÈž[­Š!ŠÚÝ}÷V–Ûm+Z×ÐU©òä¼¾_y�ßÞAY,²h¹Ç;̃ƒÛ>%ºüˆ:ߊãbðþ¹‹3É%�g^¡Á¿2–Ýw»2ùo­k[«ZÖ§çg‘Wù[å¿yw¬3K.‹˜óL¬nI±nÁš.½âøØÜO€[“�uòSa/ÒaË™e+öæI-õõºêÖµ°yÓäLžVyiÞ=é²Ë£æÓ+…[>5pf‡¯¸Æ>7à6dàûä·a'ÒáË—m+_\É%¾µ­×Vµ§ÿæ ̨<ïùòO¿ô;köÝm‘Ìk×Ý14yûÌ­L½EÖ8Ñp®¾ÑàòMvÇ�aö>¢NS“®»î»-îU.³ß[î»Nš’ÔÇ3Àeü-�I6‹ÇÏ&»¾y­¥½•Û\W®0°î‚ûf�¨¸¤ûùvVOu¿N\MŽgoÉ)mkZK�»÷ü5}Y&“¡|‰îY¤­µìNÓã|ød²êauW—wúFn¥#—;;µ¦ƒó+_I0¯¥ß’‰†~.­“KÐþDw×ÖÚöiq¾‰‹|2[}0º³ŒKº®Æ)n¶‘Ë�››Ú“Aùµ¯¤˜WÒïÉE‹¿„¡¢àþ ÷·~lxÞËQÈ»ëÈ9øö·w›mñáòβéŽ%©×ñ�†¢ÚÝ[%ÄÖv77æX2Km)[²qä²¾¿N‰I¤¤’bZ` æ>~?j¿’_½�#ú†ëD¾ri·�Ÿ½Ý9ú‘ë´þq?i—�?½½;ú”ëÕaŠ�ö·s�ï%ÒØî[�ÎJ9.Ž0Öð Œ,Nw²öU²šì^[Ç23ë$WO0¡ÜaÉ•Y ²ÉošÊAmÞ¶RÛ«u>ÊR¿‘ëúû+žðŒÝ¥l·[‡ËøÖVÆé!¿";pq÷8RåÖH#Ž[粘ö]ëe¶Ý[©öR•õôzþ¾ÉÂÂç¼#3g[-Öârþ5“°ºH®ÈŽÜ(78RåÖø,²[æ²�Yw­”¶êÝO²”¯ämŸ€Û�ó¯ÂÎCÊ¥Š1¢òÓÇ-Ï#ž|9öC¡Õ÷ÎÜK66>^NtQëà’·CRß-)í¶Ë«ZR¶å­9ZR¹ôÅ¿�Ï2qüSðk˜ñ]ÚÌ>×òR=—Mð\hîÆ¿7�m0­·µ¹M¸ó]l¶âj8Veúë2aþ�‡´ÜàIo§§ºœ†ù¬òÞ|/å¼sGµ³´<„�aÔœ#+à®v.‡i‡m½›Éì†ù¢É�Oò¤Á�*Ypö»lééùiȯš- ñ�Ã.YÇt{K0û?È8öKÂ`Šø+�‹¡ÙáÛofrk!’Hò,ÆÔpü©0cʃÖ\M¦ÛJz~Zqkç¿Ï‹|øîíNGÅ·÷i»Ã¼ Ÿ¡º6¸9Ù˜[Ý_%æúÜØyW?Õejv8�>WZp(v[L œ^ø1¹š¸f§¦U”ºº´Tèµ3Áâ=á¯òSñãõEÄfxOüLüHþL} ú«â‹/ü+þ&þ%ÿ&n‡ýVqUÖþ+ÿ¼7P~¯xók8Ù–yk×—ÅGÉÏäõÝ«~JÁþMÿß!?¼l`[öòkø·yýä;_ûß¾^óýeÜ~õì?¥&U¬X²Ž0x~ ¾J­ð»¾$é®×ß݇ãg~î5ؼݦ×îšNªì«í‡YÇû.êfßú7_¤ÚAlZ¾G/»ýEn.lÓ]f®È$í¿Â§È|~!÷\½AÚ;ÊaxóÞ[l M¾~Ïev.Ÿ¬»ëaÖè» úå_]n“k‘k9×}ޔij2i¾ž²‘IÛ…¯�»øåzíŒ^JòL|½&ÞRhµ½KäF&‹E½ÎÚÅ=1õ+³µø8ºMÖFTA…��©Í“#_��´ž[Ó±N�eK>@<ÐàþxÉÎ;ó—ÇÓo…eœk­8uÓY üï³·˜ÙÁ~7mÕž ãÖÇ\I³ö“G[¦ÆÔ`åM’Ëe�ɨ~rùwÂü#ñÏšw—-Ž-žËË8ÿ^q+²#ÆŸœv6ç+ø5Æâ¾ùb¾<*W\í”±ûæÆÔáåOrɱ_¨žqùsÃ<&ñ×™÷‡,²-–Ë Ë8ÿ^ñ;§² ù·bî1ò¿ƒ\v;î–+ãÁ¶¸²æìe�ß.6«*hã–HíŠý0ùó{ª¾<¼Uìß(;^|,¼N­®àòo1ô[~Öìí¼9ð®³ãy2aí2©²ä9ð]&VF>}ú­>6nÎ\{ñpr+mb•ØÜÏ·»šöŸbo29';ì.Oºæ·}“Ë´ßò ùö[<ºcbE7ää]ôà‚8à‚:[v[e¶ÛJá{°ylsÎaÙ��ºÈä\Ûžò=¿+å;¼«!Š]–ïy›6~~E1ñcƒ Ï5iGEKcŠË#¶Ûi\§bv/ínwËû/Ÿî²95看mʹFï&Èb—c»ÝæÍŸŸ‘L|háÄÄ‚³ÍZE6GRØã²Û-¶ÚTä'~v�”}ÙÙžBwO%Ê彟Û«aËyfç&IëÞ³+d8:�F<óä~Šã\oS��­ÔàGwÝõÚÌH1a¥°Ãe´ñ/ñ¬6™GáñVm?ï29&²èr9|ðtWeM÷˜e“Ži2°y7fí!†H©‹™­Ûr8´¸Xù_uÑäé³b­)öú˃ðÞxÉ6ŸŠw'–¼‡]X²9„ðôçZdÍnDSÝÆô™x¼ƒ±vpÛ}´ÆËÖn9ZŒ8e²µº<�.Tuôõûe¯øpüf›OÅ;�Ë>C®¬Y¾xzw­rf¶x¦»Ži2±ybl¡¶û~ï•®Ûò(µ8pËm}Ñäi²£¯åX;øC¼+Ÿ¯|wîŸ99†—ç�ÜŠ>¬ê ÜÍfºMŽ?Ru^Ó:>o¾Òo±¶YYÑiù÷kß&³7_>>-öäðX'þ‹°Ýl´ˆJ10p¿ñê/åÀSþ�ÇâCþ#½Wü«x?ꇼÑþüG¿Äƒ«?•_ýQw’)¿Œ öiôòæëOÔ“("!<…B¸…ÿ‡o? 3ã®ññraž}_eööÆ(¯¶ûð³dæ;;1²m¥}bšý~Æ ©m~ß§5·~JÑ=/�LÜL¯Ž·ƒ"æÖóÞ×ÂÏŠ+í¾ü<¹9®Çcf>E´¯¬S]ƒŸ Ô¶¿oÓ–ÚþJÑ<_�œÜL¯ŽNºƒ")¦×sÞÖÂÏŽ;éuø™wó<ý…˜óÛO¶9nÁφZ[_·éËm%hµ‹ðØ_eÿ þÛeö]tWùd¶Ûu.¬w×ÊÞò––_JVµ²úÇ%·zWíöÝJþJÑÜ—e�“w8å9ç8âÝeÂ9‡có�¼Àx¾û™òÝæM“I�§ã\cW•ºÝìæ‹9²e�[…$•²;/’ÿo¥¶ÖêÒ•ó<Ó˜ñ®¼áü¯Ÿs-®>‹ˆpŽ7»åܧw•InÆÔqÞ9­ÉÛî¶sÛ ’Í|X:ì9%ºÛ-ºúÒßKiZúQæ¹§0ã}yÃùW>æ[X4|G„ñÍß-嬪Iv6§�qÝnNßs²žØl–kâÂ×bI%m²Û¯º–ú[J×Ò�…Ê9?ág‘s>c¾Óñ^!Ät[~Oʹ?!Øâiôs�è5ù]æûy¶Ï– ^ŸO¬Ä—#'&kìŠcºû֪±|ÀòG“ùuäÇryË-›?³¹–vßYªšh².ãœK 8t¼#Š[‘ ±å[Ÿ~·_õ¾�—dW²ÝO}÷Vµ¢ùeä/!ò¯ÈÎÛïþK¸¹��Ë2özÍTÒE5üŠàÅ›†qªÏPE“^9ĵ¸XWMK-¬÷AYn§ºûª­KÊÿ ù•>EvÏ~ò[%ÆÌì^Y—³Öêå’)®Ðq\ ÓpÞ5õ¡Š²+Çx¦» ³RËk=ÐV[©î¾å:ß%~fîüþós¿¼¤ÙS;CÏyŒØ]i¢ØE~&Oê>%�ë &f¶›}æ»{oÔâäîmÃÈ»#y“›“-¤õ£[î×–Š€&møVÿ¹Ÿ˜¿××PÿP9Ê^?†wû—ùYý~õ§ö=Êèü4ŸÜÇʯëó­?±þP±«ð{þÎß ?–—3ýFôXI;¤à–0¹�Ÿ�Ú¯ä—ïgHþ¡ºÑ/œŸÚmä'ïwN~¤zíÿœOÚeäïoNþ¥:õXGâ£ý­ÜãûÉtŸö;–ãs’ŽK£Œé÷Ãí>ñûàn¿°^é?Äí$ñWúóä?«Þ`éÄ7í ñ_úôßÿ`¹ÜÏÃeûj|0ÿŒWü“ûÕeÒĆËY§çñó+ù)ùú¢åícócø™ùoü˜ûïõWÊÚÍæ§ñ7òÓù3wÇ곕07•ŇÈïï Ûÿ«ÞBªÍYš´) ½þïâ³ä÷ÿÂý]q”Ë¿ wñ`ïÏïõê÷‰¦Gøm?‹'~xWüUc÷àýýš}áü¹»/õã2O©$j•�‰{ æë¿78‡wàë²¢ÐùÕz|œý¬µ¶¸Ù½ƒÕCƒò _oç[Mw§¾ú]þŸ&µ¥}>ÊB_ñô|œ ÌN!ܸZùaÒ÷ßYk&ÎÙ_u•�?žu�Ðpýî<6ÛJ]m¸&N3Zû½}nš¾•ôû) ÄAÒp?0¸�ra`M—¾zÏY6nÊúÙX³¹ßY]ÞãCKiK©L­}Þµ­ÓWÒ¾Ÿe+¼ü`7ÁÀ¼ÅñçÉ�F»A­ÕùÓNÈ¿FaÖ Ö뱺{‹Ï”r|‹,¤Y“fõçdñ}N—V²ýÛEXëù‘XŽr?î"$�áêóÂϼžŸÇ^{µû·QùG�¥ãÚéòçŸî|Sºqn“ �ìâ�ú48øÜÞ™—hs=‘ÙtÙSkeš[ û׼_^lÛãß‘óøûÍö•ÇêÏ$óu:Md¹SÏ÷N1ÜXµ¿…l"²²_6?6³&í_²/©>\šÛä’ÈqnõîçÁšöøùälýÍöŸwêß$suZMlÙsÍLN1ܵ¿…ì"²·ß>?4³&í&_²*_6Tšëä’Èqnõ–_áSù"ÆñãÉ>Aà×iò85�IåvÓiÕ¹[lì,WòW]��¬Õë­ÉÙmµxX–÷gÀ‹EKl�7?aȵ¼+~ñ5ÕŸJpɾ,epÏçëÌø¼_ð£uÖ\kg\NÕò›ôÇUñË ¾–åk:öÜLk»{’R’aäA|?ÁÍœ:*R’A“ûøòaºµÆ¿ÓŒ8þ^Gã_‡{޾ã»/ºö’¿¥zÇ�Ù öÛ—®àßuÇ»µ¹¶É�,wCØÅ¦¶¶ßD9[Ør"­~…ÞœgùÁòê?|>Üu÷ÙW³¼“ý)Ö|rÈ/¥¹zîL\{»S‘[møòÇt6qíŒZkkmñONòâ­kÞœ)üC? 0x'ñçÏðxŸ!ýß^MC³è~›‹_³³ ‘i±¹®K;G³uôÁäZO­‹¯øDñáî5ßy»Uʶº?­ÓŸÕ^šhª¥Y­ñÑÒxýñÍâÇ͇-æï®±ûK‘]%ø™_¥»ƒa�Ù”ÁÛ[,0Ï&Û�k9F6ªjßJû~ãK-­l¶Ú¬fø±é™z'À/¸VlVE¹Úuþ?cïé\K°ó,Ûö¶~ocɯÙÇ$PÍvËA‰ÉáÖI[éZÛ÷*[JÖÛmX±ñiÓRtW€¾4ð¼È£‹sµàý�¿ôÅ».Ý·jçfö-ø;;$Š®Ùhpù,:Ù+}+[~åKiZÛmµ\Eñ3ãtþ$ünxqÐû->÷Žòn1Òœs’sî9ÉoŽýßíÒ—3¶;WŽæÒ*Ý_ {œm1"Š—]Ha†Ë=×{}kÒ'@�t8]oâ ÕݯùKïL»¤ºúo8¿Kí,¶°Ö:Cl=EÃtµŽËë}Ôȶ·éë¾”¶”­Õ·Óó}k¯�ÍEÚß’®ã̺[¤¦ÿ‹uÞË+ b¤ÃÖ_EX¬¾·ßL›k~–·ûéKiJß[=?7ÝX|ëj®×|“w]ÒÝ%7¼[¨ö¶YXk ¶±âú:Åmõ¾ÿ¼[[ôÕ¿ßJ[é[ëo§æúÖ³ÏÅ™Ãîã?*ZýÕÙ×eÓ°ü^êaf=pë�M]¸\�²8pmž¹3þ‘¶Kø=r~µ-‡Û\ŠÅì¯Òú—ñeÇÇ ’¸¤’#š+ëd±_d‘ßoÙu’Gu.²ûkû•¶êR´¸ä¾+ì–;®²Hï¶Hï¶¾—Y}•¥ÖÝmiöÒën§­î9/ŠK%Šë¬’+í’;í¯¥Ö_eԺ˭­>Ú]mÔõ£ééw;^9¸Ôò~N¯w¢Ùàntû<9+^»k«Ê‹7_Ÿ‹->Øòq2à²K.ýË­¥VÚu/ak{oªºÏµ´±Ö?fõ÷ ì-LU’Ù«·šqÍo$À޲Ùëdµ³ee=Ôû.ôõ¢ÒÞ²çZÎÐën½ì½-=ºnÄàüO�jiõ-—ÓYËt8ü }[?2_L]…Ÿ�O²ïËE£½eÎu½ŸÖÝ{Ùzk}šŽÃàüOœê­ú–KíÖòÝÿß«kd¾˜» iî·ì»òÑx'ä:>]Ç´<³Œí0wœo“éµ|‡�îõ™1fkw=Ö -NÓ_™�|�eàì02cš),ºë$Žú]JÖ•£ ½ÃÜ>Èðm6šÍ³c»Ýìp4úm>fÓo·Úfcëõš½f¿L¼ýŽÇ?.Hqpp0qa¾Y¦–ûcŠ;kuÕ¥)Z¿Çc¯Ôkó¶Ûlì=^«W‡•±Ùìö9PakõÚü(/ÉÌÎÎÌɾ,lL¼æCƒvÕ»îvzW‘ò¡æÖG›ÞTr^c¡ØåÍÓ¼ÙøKàI÷¼|i¸®·.K¶Îí~TX’cìùþßêl/¬¸ñeÇ�÷„ýUñE—þÿÿ“7Cþ«8ªëÿ‹Ž?Þ¨?W¼yµ�œlË<€5ëË�â£äçòzî�Õ¿%`ÿ&ÿ‹o�ŸÞ?¶?°-ûy5ü[¼ƒþò¯ý�ïß/yþ²î?zöÒ“*ŒV¬YGkßÿ,0v�ãž ù ½öv_ÓÓ_ãÿ8ÚeYKyÿ ÑáCë=¼ÓÝmÿÃ>«Æ­uRÒ·[³ÔCXo¤y8Vߟ1/ƒ“øû?A¡ð»¾7vÛÙKQL.Žæ[,‹-þpí.­�}µ–[­ºþcÄ5˜õ®¾Z{¿Ija­—ÒÜœ:É›0�ƒß“È»7A¡ðý·t·±øž¦˜]ÌvY[ü;âl?[:ûi,×R빇ÖãÖºéiîý#©†±ßK21>¦mŽ¿†÷æ¦Ï.úÿUá”<ò<¿+:¿�ÉTòþS�{¿":·�k«-سlòd¶¼£¸zÛI‡uÛOukµÞh±ÿKÉ÷Éñ7™¶Ji%d”»yŽmÍxŸ\qMÏùÞÿYÅxg Ñìù/(䛜‹q5zMŸ\í–Ë;"ïZY.,7]_JVë½=-¥n­)_;˹wà<[‘s~i¼Öñž#Ä´ÛCÉyã&Ì=^—I¨Å—7e²ÎÉ’´¶,lLXn¾êþ_JzR•¯¥w—rî3Àx·"æüÓy®ã\K‰é¶<‡’r ¾E˜šÍ6—S‹.nÇc�‘æÅ�‹‹ ×Ý_¶µôô¥+_J>/$äœw†qÝÿ0æý/â\SKµäœ£”rM®‹Žñ¾;¢ÁŸi»ßï÷{Iñuš}.ŸY‹.FVVD±Á�wI%ÖÙmkJÝ>WþF¹È—‘ò�|{-Eõµ»>/Ñü3>[é““'*+¶üë�â[%ø˜üǞ˃�6TqzÙ…‡�‰…·dÏ_'ÊÈÿÏŽû“‘kãÎÒô§]SgÆúcŠf]}™Vj2ra»oÍ·øþû ‹•ó‰°`›"(éK0°àÅÃ÷M~=ù3×áò{çîÿÏ^ú—‘kìÎÒô¯]ÓeÇ:kŠf]%™6j22b»mÍ·ØßRø!å|Þ\(&ÈŽ:{0ðàÅÃ÷Mv=ù3Õsó¹òÙºù9òfý__n2àñ ¢ö�AhîÔäh§æYyŸuÄå=ÓÊq60Á¾—oÎ$×Gn§>Ìjé´0ãÅL,M†NÚü¾\¹¨æÃ…àaל •v§>á=eÁµrnù§aòÎ=Âx–ž+ãŠý§$å;lM&—“Mu�ÁL­ŽlvVKî¶Ë)_uÕ¥´­^»€pnMÙÜë†u¿ ×]·åüÿ”è8gÕÛ$p×aÈ96×M§Ã¬ÒÝl0[‘Ÿ›·I}ie”­nº´¥+W­à<’öo9á½qÃ5×mù>å:ÅõvÉdUØrK´ÅÓj1+4µ¶(-Ÿ?2;n’úÒË-­nº´¥+V\è.’çžI÷wSxÿÖPgvröëŽ'mslÕãî9nçO�³ÞdëðvYzþ9¦¦UröYvãËL<&žëkluZ�âŸ�|cÅ?ºsÇŽ!X&ÓõWÕq¹¶Xø·`ÛÈ9 m“cËùd˜WO“÷L®_Ësó¶“ÇI/¥“eÝJWÒ”Y�ã/Dñ¯:©º‰}týcÃu|zM„”Á¦ûyKoÎå\¦l:K=¸Ù¼³”fælò,¥÷RÙòïô¯¢Ë�ú+�øËÐ]OÐÜSè˨ë>¬ãÒgÁ‹÷*owžÛóùO(›’ÍLlÎWÊ3s6SÙKåßJWÑs'�?uÿŠž?ôïŽ[…n鎾ã\C%Ø:œ ýź-t8ÛUÈ#ÑëõZ¼®[̶ÿxÛnrãÇŠ¹û\ÜŒ›éõ%ºµØ&tg6pG+ñ<ãÝ7€=[%.¥´Äò߯ò.¥}}o¶î¢ï\Om¾Ÿ’´»*•ûr•GÿñAtÞ õ¤–ÖÚSÊn =ô¯¯­ÖÝÕ]×�J[éJþw¿&•ûr•pñãÝ7ƒ�m%·[JbyIÁr/¥}}n¶î¬îœZ[o¥?ž÷äÒ¿oîR¨½þ-î!—É~.¸~ç/¾òã©ù~|3Û-eÏÄÍëþãàVbaÖ;n¶Ü›3ù¼Ö·Ö–ýo§¯º¶Ò°®6Îú# ø„üèƒ�ñ-WƒÝw·‚NCÍ!Ôs.öÊÁÊÆš]Å̃k¸OÑúÓam9FÏ-¾dW]�‘·ì—eTbÿoœ°qÎ'ªð‡¯6ðÉÈ9”:ŽeÞ™X9XÒɤâX¹pm8_Éú7M6Ó“ì°âÛæE}qç‹[�‡öK�±»Ò ÿŠ·äâ.•èí_Ç·Qò/¥Ú¾Dê1ùzçê6œ§è\]¥•ÖñZæk~ã�M‡vousâfc×.þ&5ÙøÙØUÇÜáÍXd""ˆÚ¼pÍ¿ ß÷3óúúêê9KÇðÎÿrÿ+?¯Þ´þǹB]†“û˜ùUý~u§ö?Ê5~ÙÛäòÒæ¨Þ‚K 'tœÆW1óñûUü’ýìéÔ7Z ó“ûM¼„ýîéÏÔ�] ó‰ûL¼�ýíéßÔ§^«üTµ»œy.“þÇrÜnrQÉtq€>øaý§Þ ÿ| ×öËÝ'ø€ý¤ž*ÿ^|‡õ{Ì"ø†ý¤+ÿ^›ÿì—;™øl¿mO†ñŠÿ’z¬ºX‚°Ùk Tüïþ#Þe%?!ÿT\½¬~l?-ÿ“}þªù[Y¼Ôþ&þZ&nøýVr¦ò£ø°ùýá»õ{ÈUY«3V€¥ 7¿ÂÝüV|�þÿø_«®2™wá®þ,ùýþ£ý^ñ4Èÿ §ñdïÏïïêÿЬ~ü¿³O¼?—7eþ ¼fIõ#¤�R²pSñøÏwwø�Ú=M™ÜÏÆna«ì˜&ÇÂÌÍÛÉ×Û¿oìmf¸¶Éf>¿–¿}°–[idXœzëëu´¶¾î$|÷øísø;±ìM.³ï¼»Ç^W¬ì|y1°çËÚKÁöüKv¿èÛ}1õ¸¸{z¼w¿¹| Øv&›Y÷Þ[ã¿*Öö,cbdeí$àû?þ%û_�Hm¾Ø5øØ›,=Þt—Ò–Ç�¢­Õº”¥iw?׊9>NüWvÆûA®Øl¹¿‹ÜƒAäÇÅÖWU ù:.�´ãÝ­M®^ÊÛe·�èú‡—ow³C�$sdeéq©m$­´Žúü�NAiVPýY}ñ_d‘ßtrGu·Ç%—VËì¾ÊÒëo²ëkK­ºÛ©ëJÓí¥_êËïŽûdŽë¬’Ë­¾Ë캶ßeöÖ—[u·[Z]mÖÝOZVŸm*ÿV_|wÙ$wÝ‘Ýmö_eÕ¶û/¶´ºÛìºÚÒën¶êzÒ´ûiWîÖlöZ]–¿s¦ØgjvúœìMž«k¬ËŸe¬Ù`OVÃ_�‹$YXYØYQ[$RÇu²G%´ºÚÒ´¥V|$|™aùÉÐñuŸfoq.òƒ£´šýo6‡/>ë¶Ý�Â1k�ªÑvö,²É™�›4—ÃÉ/Žùíƒs|y7ýÞ=ž&=³ÕøtùÃóG¤¢ëÎÅÞb]ä§MiðuüÓ'*¶ìû‡c]³KÚØxùRß‘›“=÷ÇÈnŠélÇÛßd÷Û�Ë O'áãäKÌî“‹¯;w‹w’=7§Á×ó82rëMŸbpük Öi{W ©oÈÍÉžëáÃä7Et¶Á·ºÉï¦<[H)joÁ'ˇù4ñ£ EØ|�]™'©ÁÔw×ý…Ç®æšï¼I�Ç;·ˆëp$¦¯7CËñ£Š=ÜxQbY¦ä^ áb`dê/Ìíó².Æ»ªóü·•ñ¾Ź'7æ[½wâCÉ9'!Ùáit{A¥ÂŸe¸Þo7)ñµÚ�>§]�&FNND‘Ã1Ý}÷[mµ­+-ù7ó‹{çç•üÓ¹¤ûö^jc³‚ô¿Ί |Ž?Ö:ÌÙu2l Ç÷Ûnÿ“ì3²¶û_.EÐåg]�d×cãÁKk¤ùó;sç'“ü¿¶®®n_êl·„ôÿͲgÒuΓ/.MlùÐAe)nó“çåäm³é}ùÁ‘›\[f¿ []‡ÈÏ™›Ÿ8¼�åý³usp¸¦ÛxWPñìÛ"†m']i2òäÖÏ›6úY»äÙÙy\ú_|÷Ñ›\ke¾ x)mLß6Ÿ%[O“5¹bqí¶Òï:º¹½gãoÉÈÛC�üÖg]úO²dÑìð4²jù/oî!®ß.“àÁ²Å×~ŽÕå_?踤så¡írµ^xï™å‡–ýãî>>lø�ØúLW~¶H"ÏÀëýMòr>ÇÛáÉ“ýÜ�/Óìrã¥Ô¯ºøim)ukJWg<0è<Ÿ(<¨è¾ˆŠ ©ð;°4ø|¢¸3Å�›‰Àõ—‘vËiéX¬ËÕpm>Ã&*V•÷I-¥+ZÒ•Ù¯ ú'Éï)º3¢¢ƒ*|Ø|>Q\¢ÇÍÄàššÉÈ{e‡4þ±[—ªàú�†LT­+î’*[JVµ¥+ПŠ_ñü×ùñOÇ=¶Ó‡óÑ×o»/[³ÉÙ`alúŸ­ð³»/´´·l5]��›È¸Øk°d²è¿ê†\ýHýÞûmKŠ( Š8aŽ8a†;"Š(¬¶8¢Š;idqÇ”¥¶Ge´¥)JR”¥(³&8ìŠË"ŠË#Ž;-Ž8ã¶–Y–R–Ùe–[J[m–ÛOJRŸe(²î8ã†;"ŠË"Š+-Ž8ã¶Û#Ž;-¥¶Ye–Ò–Ùe–Ò”¥)OJQq;ý¿Ûý€þ'N/£Ï±äxðËd\»Æ.6\×Ý}ñͶÒö7jj§¤>ï͎ص1àÒë-ü—V·Wùä'¿ßýæ¿Zr¡’Ì~Q㇬ÓÝu×Y6×KØ]™ƒ•lT¯æÇHu—ázÛOÝ»×òÜ…?â4ã_¢üÔënC2Y�Ê>º<¾¿û¼IY®ÕE×MVS_m´“# "ÊzÖ+ª¶#ðøùKªòŸâ—Æ<èruÕå] Æìñkžjµ˜Û Ñmz;[Ǹ^6D¹ðEnÇwÓSñ}ÎT¸×K�L�¥ñÒê_‘ÙÙÇ\�nv Ä9ò{Ðm<èýüörÞS¯ÂŸÈþS§Ì¶ÐCc ™ºþ¡ÆÉ‚µÊý+Ëñ/‹+{KnŠÈô÷Å…ug·?.(#óÕò=Ðl¼é½Ü¶ò¾S¯Ä›È.IªÍú5ãüGc y¸=WØ×}ãô¯1Ã’,�Õ=ÑÙfšøñ.¤öl2,‚1¿<¿#qñ=ËÁîœÞKo+å“yÈõY”Šº%°†<Ü­‚|zýãô§/Ä’,�Í)tvG¨¾8ÿxn ý^ñæÖ6q³,òׯ.?Š�“ŸÉëº?Vü•ƒü›þ-¾BxþØþÀ·ì!ä×ñnòûÈv¿ö¿|½çú˸ýëØJLª1X:±e`§¥ÝnxÖçSȸîÛe¡ä–çG¼Ógdë6úm¾³&,Ýn×U²Â– Í~Ç_™’Á6LY,2Ùmö]K©J§ÉðÛó%ÅüÎâúoü€Üêx·–[S&¿a—66³OäŸY�lwòN7d—E�‹ÙX¸ñ}]Þ’/JdÒ—ìuÖ}×ïxš¹Ä|Iü¶qÏ/xæ§¢ûÓmªã^Qq­TxØY¹2ck5=ï©ÖcR’ò.;k6/aâãCYwZX©Kg¶—ì5ö}×ïxš¹¾üL|³ñÏ.øæ§£{Ëm«ã^Oñ­\xØY¹2cku=ë©ÖãRÙ9ŽêÅ�‹ØX¸ÑV]Κ*RÙí¥û }ŸuûÖ.²Î/ƒœþ ò;ÁuÞ›} ów…è©ÛS%uú]G�Z�.¾ù¶‹×ZølǓŇ&_!ãØ‘ÛL:[&n£þ¬Xw=ܤ‹/w»Òñ�.ß’rM¾¯�ñÞ?«Øn÷ÛíÞÃS¥Òiu8’çí6û}¦|¸ø:Ý^·I²2&’È¡ŠË¯¾ê[JÖŸ;q¸ÔñíNÓ¿Úk´z-»7qºÝn3qµš�>§Y�.nÇi´ØæË¿]¯Ã†ùgž[ìŠ(¬ºë®¥´­_;o·ÔñýNÓ}¾Úk´š-&»7oºÝmóqµº�F§[�.nÇi´ØæË»åžyo²(¢²ë®º–Òµ~|¼¼\ \œììœ|,,,y²ó32æ�;¦ÈÉÉÈšë"ƒ¬­×ßuim¶Òµ­iJ CóAó²ó_�fxõãî×k¤ñC‰íévÛkm2µ{/ y§2ð9÷k ÍÀë­~%³èµÛd™ÒÍ–Â:e[…‹¬„ËïËw˜Ì®„è�®ÓQãÛRýÆÒϼks;×�ê³!Ÿ{¸Ä–8sq8‹?“éu“RÛ²r)fÇ6:dY…¾Ÿ/+ÙÞ_o2º¢v›=OŒ¼_kïÜm,ûÆ»3¼ù¯.ð7›|Ic‡3€èó±i>›Y5-»'"–l3c¦E˜pkëœüD:>dí³ü,ñ•æÍâßÝ]ý·;U“ šŸ"ù~‹g��§ÀÑ]f?ß2z›ƒnuyÄÈúÔÇä;?fe±]�‡¯ÉÈ�˄΢t”7á§ðŸûcw'3óK›j¾·èÿ½p^ªûÔ^ìmŸnr]=¿Â-ÌÑ+d¿À. ¶¥•²X«oÞ·øÓÅ}%ᤓ÷‡ŸÃÞÙæ`s gÕâý9÷®Öx³Ö �ir-E¿Â-Ä^“ú_ü á;[böM Ö]‘½†h¯¤¸•ô’7áåðûøyÛ¿Ëîa¬ú¼_§~õÂ:ÇïúÁ±íC©·øC¸‡ÒomÿÀ®´¤~Éb­—do!š+©.-}&¹øF¾>¿„|ã³~FûIîÕuïé~“ñ×ïÐz}~q¾ÔCý·;_ï¬18^Þ9‡“g×ÃÌý?·†ïlø?›7$ÆÓOPÄŸÄ)Ámåÿµº÷zKÖœã¨yÔÒûí¬×dv›¯ç¶–ÙeöËô°ùܲÖÛ«m´¶:Ýë׎ß;ü>ÎOñ·ÛjÖ¿W€rþ¨æö[uô¬—ÏØ:>5=¶Yu$¤x|ÊY+K«m´¥•»×Ö”¥x÷óµÃíäÿý­¶õ¯Õ࿪¹„[uô¬·OÏôœ*j{l²êIHñ9Œ²V—VÛiK+w¯­)Jññè6‹á�ÌÜ]V»'g�¯Áé ý Ŷëåƒ_Ǽ‘éíÆûc}–Ö”®6¯�aåäMZúÒÈ¢ºïô´Wz�j£@Sü2¾FâõÏ–��ãÎë>Mg‘�wÃŽE~4³O�ØÝ;vß�êõÐdYK£À‚~¾äÙݶ٤ÂÏZßôéY~ÞÿÅà>PöWBî31ñ5ýýÀ`Øñë$ŠY23¹÷Rß¶Þëõ˜ÒYmÑâÅ‘ÁwÜ‹*k¯­¶Év VzÖêÙD†?§~âð/'û'¡öù°bëûó�A±ãöI²O›Ïºžý®ó_­Æ–Ûn�,Ž ½ä92Ýum¶Kð¢³Ö·{(˜�àûò›UÁ<–òGÄžG°‹ß :óŽv_]×eÈoÇÇ—œtŽVòAÅxß–Û Ïä<·�óÜ­¶TðÝdÖkø}}ô’Ë-¬S¥M)4ƒà ùóǯ~>ürä]»ÊeÀÚó­­™|s¦ºúi/»/žv ø—É�ØøóãåEÅ4­3wY”¾+qðìúQß\¼ŒHfÒ_>ü×à~ øÿÈ;c“߇´æ; 28ÿSðI%­2ù¿<ÈÅ’ý~,‘E49q­5=37tºÊcáG[#­ÙScC6“yóæ¯ðc 9kri0öœÇafNƒ©ø,²×ï|Û�Ï‹}øÒCÐdÅÆ´¾´ÌÛåÛu”Çò¶YuÙ3cC.�üŠùåÔÿ+sß&ûZÚmíÑÒ;×|k‹§Üö§hîñó/â\K›•]qnÏ® ù› ¸ñ³$Öépss©�‘LjÃ}g�«Ú<ï»;#›vßgòÎW؇Èö|«–r ÚE» ÆÛ"üŒ‹¢ÆÆŽ=~=.¤8¸˜ñÅ�‰�pÑGe–×YÙý—Íû—°ù—jvNÿ/”s¾{È6<›“ï3~�²gm6sÝ4¿G "ÅÀÀŲ¶Ã‹‹‘ãbcG0ÙdQÙeµÝvwesnãì.cÚ}�¾Ëäüëžò �&äûÌϧd™ÛMœ÷O7Ñǂȱp01­­°ââÁdxؘÑÙ 6Y–YJ‡|�ò ´¼¬ï>Ðò+º¹O)ìîÛå›[Ê6“O›.6<¹?OWÇôpì36¾'Ä´˜¸Ú­6¾“_·S…�‰¤PÙJx„xVLÛð­ÿs?1¯®¡þ s”¼ ï÷/ò³úýëOì{”%Ñøi?¹�•_×çZcü¡cWà÷ý�¾@-.gú�è$°’wIÁ,`s?µ_É/ÞΑýCu¢_9?´ÛÈOÞîœýHõÚÿ8Ÿ´ËÈÞÞ�ýJuê°�ÅGû[¹Ç÷’é?ìw-Æç%—GÓï†Ú}â÷ÀÝ`½ÒˆÚIâ¯õçÈW¼ÁÒ/ˆoÚAâ¿õé¿þÀ9s¹Ÿ†ËöÔøaÿ¯ù'÷ªË¥ˆ+ –²5OÎÿâ=æWòSòõEËÚÇæÇñ3òßù1÷ßꯕµ›ÍOâoå§òfï�Õg*`o*?‹‘ßÞ·ÿW¼…Uš³5h R{ü-ßÅgÈÿïÿ…úºã)—~ïâÁߟßê?ÕïL�ðÚNüþþðþ¯øªÇïÁûû4ûÃùsv_ê ÆdŸR:HÕ+ ”çœ'�v_æ}qÌõÖn8`qNEÂy^¢I%†=¯åZŒÍ÷]|°_ÑY�«Ï–*ÝeÖÝm.õ¥iW™æœ?Žö圗ë¢Üq>qÆwÜ?”j&ºû!ÚqÞM«ÊÒîõÒßÖIlYÚÜÙbº¶Ö—R—}•¥^kšq=Ø\;–p]®�oÅ9ÇÞñO©šëì‹iǹ.¯+Kº×K|wY%‘æëseŽêÛZ]J]öWÕñy'ãüËŽïø‡-Òj¹7åZ]¯äÜs{�‹µÑò?¼ÁŸY¹Ònuy±M‡²ÕmuÙRA‘�5—Å42]eÖÖÚÖŠªü¡èYâ×�½½ãç5²Zﺫ›îx½s¥Å›Íöš©•Æ9^<öÛ5š¾]Ær±6˜•º”­Ø¹q×÷U–ù+Ñœ›ÆŽûí~‡åÖÍ]×Ys=·¦lØ÷bÓy¦ŽJeñŽO��}k|ZþWÆrñ6XÔ»ó¾ï—g­)_±Y×’]ɼjïžÖèž]dÕÜõŸ2ÛqÊfË�~-7zxå¦WäøÐIý=*ãyX›,ow¥k�•ekJWìS]òâfûÁ�3¼ˆñ[{]„ñu'bìõœGk¶ÉÔåí9'YîáÆå]SËvrh¯¿UÓ•õ¶÷U±ÊÇ‹ÙvNL˜òG±_˜ƒØE§@ ÁÐ}ïÚ3vï ï›ä™W°¸Úݦ“g¿[{$†\=žŸo…uiÏC½ÕäM‡�‹'ædbÍ}•ôõõ¦Zè¾ðìŸ{W‡w?Rr ¸×;á:lu9öÛõ±ra–)16Z}¾×[ËG»×O..f5õ¥³cËu¾´¯¥ÔËÝÝ“ãŸjpþåê^A7ç\'gMާ>Ë~¶.LRE&.ÇO¶Â­ÖDzÑîõÓË‹™�æÍ�-ÖúÒµ¥i´žy�ÞÞù Á¼•ñß’Á çü+&Xr5Ûli¶|?�q]�Ó‹’u÷?Ð׃~÷†rŒ(éLQÏ�™‹-±f`dáìqq31ìŒøæùéÿ‘™Çç<&|~9Ùœg_�ÛýM—™d»¾ Èr!º”ËÃ÷{&Üð�ìÐK&«ge”²xíºi\9E`÷€^}u?žýE6ásCÇû ŽÃ��Úý[——dÛ¾È2!¿Û>=kHåÛðíܸòɪÙÙe,ÈŽË¢–ØráÉdžÁ?|÷ê�=:ŽkÃfƒAØ\v Öêì¼»%Ýp�þDWúdcúû%ÛðýܰK&«ge”³";.Š[bʇ"­wøÎù1ñÿåÇü>äé¼ÏпAú7MÝ]+¹Ùcfó^œæ¹¸ÓÍf¯i|0a~Ÿáûÿ¹dOǹ ðân±!–•‹a‹±×`ô1¾ ítTÿž—]_�y»_ m>Tµýå™¶ðÿÇ�ÜyýGÇwX÷vçcjsmŸ´9.�&ÜœN%Åòp²/ÆÎëÎ3µŠÉò3n÷SoµÆŽìon$ypüØ|§kûÃ3kâ�[¸óº“Žî±îí®ÄÔç[6gò]&M¹¼OŒdád_�Ÿ×¼ki“äf]î·mµÆŽìn&,sçÀŸñ,|ÞÉγyŸÆ×‰¼§ø «Î¦—ÊþÖãùxôç\ ð³kѼ/y�—•nÐm,örüˆ¾žnÃe‹úß;8v±‡FùT)À»ü0^*W�v'uy�Èõ–ɪëý]½/ÖY9XpϜӔÁƒ¿ì ¾».é)>׌pûuØ5­¶V“bòY­÷SÛZVO¿†÷Æ_Ó|ç¸ü´ßà[&»„ëíéλ›#xn弊AÏ6¸sßwÞ06œw‹S[‡mÖÛé67 žÚÖžÚÒ²uü8ž3×wÎ{‡Ë-þ²kxN¾Þžëɲ1`ž+ùo"‡Îö¸y]õðv|{‹S]‡K­¶´›�OmkOmiYÌþ«Jy9ç—!‚ßJýÛÅž¬ögßïû?ƒŸÜ»Žš}],ö]ëÃqµ9ðfV¾´ÛA$TüÛ«3ÔºÒãN\_ÅAÕù¹Ý}â7tb`Óôæ=›Öí•¶þuÙ|ëKÆyWšÿü¦8zós|tû>Ûïü¾´ô‹çâ`ë|¼Þ¿ñc·±±­¦æ=‰ÖûŒËm§¾ü¾s¥ãüŸ�ãIåöE^mn²ŸÍ¾ä`ÿÏ[åæð»wÚ`ñ®aØ}q¹Í¶ßο+›éxÿ&ãxÒÝù}±E×û[¬§óo»ý9÷Lcï¼\ðÿÈ:ìó#ÍêÎùæ=?’8£» 7½zþþc›¹Ë—é]4Sé²üzÅLJÒûlº›ýÔº´²¶ÃE¤HÕô€;Iðuçö„^W[¢ìMµ5½änØÙÙypbj¸w ÆØKw_ö^Æ\�§zî3Ÿ³ËÂØI|±E�ªÚåe]õ/ÆŽ:öá‹Îl<�¦‹ŸílÖt�|Á¨àüÿ?3/WÄwø¹³_Àûc6M–Ç»�çl²°³ï¾h!ÇÖm22¯­÷cGez÷ðÏç&‡^MÓE϶¶kzO½áÔðŽ}Ÿ™—&¯‰oñse¿‚öÂl‹mŽ=wÍØåagß|ÐÃ�­Úde_[îÆŽÊÉðÕü�i|òëaÒ�ËÊuükÆ¿,+¤â¼�’r”Ø<¬;oK\û:Þçd×a�«Ðor6“ñÍÔòW_…[,=žÇ2,==Ô­ŠVÝmöÛ}—[}—ÛK­ºÚÒën¶êzÛu·SÖ—[u+ëJÓò§ÏmÔº”ºÚÒën¥.¶ëkJÒêVž´­+O²´­ܶën¶—[Z]mÔ¥ÖÝmi[n¶´õ¥iZ}•¥h³­þŸõÿ@~b¾[8¿�ýy°ê®¦Ûêw¾[ó­-?ƒZºÃ�¸Àê]ËÒ+» ˜áÝ-1éµ®d»C­È¤–åeÒÌœ˜dÂŽè²8ÝòÅò�Ƽ#à9½oÖ{MVïÊ^k¨§ðcQt0m°ºËMŸ_¥w=娗KH-̦7Ô®—z_L¼ºY4ÑI‡–KÇ•ï”N5á7Íë~³Új÷žQs]E?ƒZ›¢‡k…ÖZlúý+¹ç.Ĭ´†Üß»}Jép&¥ôÊÊ¥“M˜‘ßdÜ@ù®ù“ë?‹^–»GÆó4\ÛÌ~ÒÑM/Hõ&G®ÃAª“2}^Wrö®6&f&N—®t¹8™0ëa­öfr�Ö5Ø8VýÛq°ÕWÅȹÿ—ò ç+å{½·%å›o±ßò>E¾Øeí·›íîã2m†Ûs¸ÚçË>nËi³ÎÈ’içšûåš[îºë«ukT·ûíç*Þîy?'Üm9$ä{]†÷�o÷yù[]Öów¶Ë›?k·Ûìó¥Ÿ7c³ÙgdI4óÍ}òË-÷]uÕºµª {íöó•o7<Ÿ“n6œ‡’r-®Ã{¿ßîóò¶›�Þëm—.~Óo¶ÙæË>fÃe±Îžù§ž[ï’Yoºë«[«Zª¹ç|ë˜ö4å}�Ø|—s̹ß:ä;~YÌ9g!Λe¼ä|“}�6Ëq¹Ûgä]|ÙYÛ ì‹å’û«öÝwóò_%å¦x!üG¼5þJ~<~¨¸‚ÌÏ ÿ‰Ÿ‰É�¡?U|Qeÿ…ÄßÄ¿äÍÐÿªÎ*ºßÅâÃã�÷†êÕïmcg2Ï zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆúz]Öç�nu<‹Ží¶ZA¡Ù`ît{Í6vN³o¦Ûë2bÍÖíu[,)`Ì×ìuù�Y,Å}’E%”ºÚÒêR¯£§ÜmøîßWÈ8þÓc£ßhö8[�&ïO›“¬ÛiöÚ̘³u»M^Ë Xs5û~d6Kñ_d±Ke·[u.¥*ú:}Æß�mõ{ýÓc£Þèö8[}.ëQ›“­Ûj6Úܘ³5Û=fÇXs06�Ù,3E}’E%”ºÚÒêR¯EÄyw*à«�ó® É7¼;šðíî«“ñ.[Æ6¹Ú.IÆy&‹: ž—}¡Ýk'ÆØêw�Ž4sãdÁ%’Ã-–ßeÔº”ªi??|s³qô>>ù×ȵ\C²ì» QÁûÿ6Üm?çô¿éâãê{6èíƒ[Ã9�%­+fÚ”‡M±²êÒzadEmÙÒÿøÂùÊãÝ™�¢|ÓßjøŸdR¸š¾Þ9Vãj8<º¿O_a]!×pþ_uþ—G²¥°éöÖëeûœöY÷Ùzüc|âñþʇGÑ^hoµ|S±©\M_ ï »qµ^¹·™ù¹ÝAÓ×núóÆM~e+“�,—kùgpfbK¸ÛNwn&L±áñ\˜i.»Ee÷ÇY©n^mÓd[‹r¿8ˆk�yÁ´åž2xºä}qá¾,¹Ü’røìØq®{äÔVÖ\<üíæ<”ÅÛñœÙÇî·ŽÍl; ¦ßxÝÙ“ÓO­á‰.'¢âɽ1Ô<ï¿{_¯º_¬´òoyïfr­Gã:ë}öCvÃm“d}Ød[d–àiµXõ¿+;.ú},L8ešJÒ8î­2?PuG6ï>Ðà}?×›÷\ß±y6¯ŠñÜ RZA÷ÝžE±]�±ž(¦®›U�ïÊÎʺÚlj‡ ³IédwV™¨z«š÷—gðN ë�Uû®mØœ›WŸî>¥°ý÷g‘lWfì'Ž9¾ã§Õcûò³²®·éâaÃ,ÒV–YuiŸüWñ¯³|Âò#¨|eéÜ3û¹9ž»ˆè¤Í³e~ŸI�5²çr_É/ÓëöÛL^#ÁøÖfçq‘�‹“.6¯"[b’¶{+h׈¾2ð_-øí¼QèNµèN·éxƒ3mtwËäü“*ëóùO-ØÅY²k&äY—ÇI/�’Ò½"Ž;m²OÅÏø_Š} ÖÝ Àã¶ý7ãð`æm®ƒîù|Ÿ’e]~(å›þ®EcÏä»üœŒ»ãú—Ù�Im†?H£²Ú\iâÏ�½gáÿŽýCã7OkåÀëΚáz΢“2-\[}äøÿS3Ì9-Ú]vŸS—ËùÇ$ÌÌÜîrqñ1£ËÚçäMH¬úžÚl‹`ýŸ@qòÔö÷‡ƒ^Wõ�šÜ½ÆË�ôObdñ­fº¹Y¼Ó�qüÎUÁa†Ë#–ù«^c¤Á­c¶ßt”¥m¥iZÒ´Õ;:¾½Íá§“½mº}¶Ï‘t§`]Ç5¸õ»ëeó /Ìä.+)drß}…š¬*ÖËm­×ÒžÚzV¾­Qó«¬?·/†ÞMõ¼Zùö».CÒ¼úþ;®Æ÷}|¾_¤ÐfrvÙÒI_á^§µ²Ûkuô¥m§Û_V³y§Ó»Ï!ü<ò³¡x½ÚË9WtxãÝ�YÅ&ÜÝušœ^SÏzß’qŽ7™±’8æ’L-ÞÎ o¾Û.¾Ël÷[JÖ”Uœ­Zª–€¾ê¾Ìæ1Ù| ¶úûiv—œu¯.ãüÛŠm)sÙ‰½ã{ÿ©ÎÈÆºêY-ËD|-ò×®¼Ûñϯ¼ƒë|Œh±9NºÌNYÅìØY±Øõ÷?×A�o,à›©iß~ÑgÍJÃ4˜øÿ×Í�›tƒ&*ÖÉ�<¢ëÿ1: ‚w¿^Ox\Ÿ_n?$ã”Í·7aÁ¹¶¾8cå+sÐÄ›ïÚ<ûÿ¡K|S; H3"³èdEuÖEx�åó  ø/{uìðÇ…ÉõöãòN;LÛsv›kã†>QÂ÷ýI~û¤Ï¿ú·Ã 3p¤ƒ.;>†DU­Ä~ùcÔþpøÙÕ^Oô¶Ã#/‚ö—³i¿cÝ÷¼O�àÏ6«–ð~K�OXáä\3“aek²î†éq2$Çúø³O‰,ɵ-–l«f€ùå”5áïMrŽôïNQá|j;`Çǂز¹-äYQdI¦á|/M&F5ûîY¾¿úcãÒøâŠ(åÉÉ—#",äw‘ÝKâ—Rò^èîŽKáüv+bŠ(­�'{É·¹1Í~§‰q-MóA~ë“ûqñí¾Ë,²É'žHqažx°o‘¾Fõ7Š�MÉ;Ÿ¹ù$\w‡ñØ­Š(¢¶<�ç&ÞdÇ5ú®'ÄõWÍû®Mº¾ í‚ o²Ë,²Iç’hgž,%äg‘�5âwMsŽýïÞqªëî®ëíUûNAÈ6—Ýuò_uÖÁ®ÒéuÐ[&~û’ï³ä�]®ÄŽ\¼Ü¹lŠ+.¾êQ[oÈž]·ò ß^ßì‹¿AñÝ}¹^­ë, üŒÞ=Ö¼2“[|¼9f¶Ò|ƒktVdî6—E ûß¶ØññbÅÄÆ¯kΟ7{OÎîìÙv¯`ß]/×Ó#OÖ]oƒ�>^ƒ®ø�Ö¶øµØ’Kd4Ùò ­b³#o´º(¤Øeÿ;±bâcWÍç7›]¥ç_uì»S°/®›�`S#QÖ�qƒ›>^‡¯8—Ö¶øµØ²Kl4Ùoö•ŠÌ�¾Î袓?/ùØàÅ‹§¯•ß”áùHòG7µùĹük©øeÛ~=ãÿNS7ëiúË„gecI••‘7}ÏaØ<Öín.W"Úú].\ÐcâÙu¸øú8Ó™9‚™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ æ>~?j¿’_½�#ú†ëD¾ri·�Ÿ½Ý9ú‘ë´þq?i—�?½½;ú”ëÕaŠ�ö·s�ï%ÒØî[�ÎJ9.Ž0§ß ?´ûÄï�ºþÀ9{¤ÿ´“Å_ëÏ�þ¯yƒ¤_ß´ƒÅëÓý€rçs? —í©ðÃþ1_òOïU—KV-djŸ�ÿÄ{̯ä§ä?ê‹—µ�Í�âgå¿òcï¿Õ_+k7šŸÄßËOäÍߪÎTÀÞT#¿¼7oþ¯y «5fjФ&÷ø[¿ŠÏ‘ÿßÿ õuÆS.ü5ßŃ¿?¿Ô«Þ&™á´þ,�ùýýáý_ñU�߃÷öi÷‡òæì¿ÔŒÉ>¤t‘ªV@{þ&ß «gYùÍÂ4–Ò,Ëpº¼å×âGe~ù“dõo5ÚSêÍ~F%¹zÌü©},·O‹góÖÑ_Ägâl›®|Ïáº[~œÖâõ?tM��JV“YIrzϘl­ÅÃ÷_Y"¦^“/;*oͤzŒ[)öÛJE3ñø‹['ë¯3xvšß§5¸½SÜòàãR•¤öR\ž´åÛqq=×ÖXi—¥ËÍÉ—ÒÚG©Æ³òÛD+?o�3rž¿ê/�þ¡ú»>°ûŸGy“‡Lé&»¯ù&êL®Ÿæ9ÐÒku8[žîsôyY?N¹¹“ò�\5ºèq-¤QEE¤€«Çß";�Å®ÒãÝÉÑ|ãmÀùï—Óe­’Ûðöšée†Lî?ȵ3ÒMw!ã›O¡e2prã—_m·Vß}–]ncènþí¯»3CÛ�+Ìv\+›h/­‘g`ßI0¶ºÙe†\í!ÕÍKð7Ü{g\{)‘‡“d�É[-¿Ò’YeöæˆïÎÙñ§²ô]·ÒüÇeÂù¶‚JÛv ô“i®–XdÍÐr\Þü÷Ùý )‘‡“d�É[-»Ò—Ùeöíg†iù à/}qo"|læ’ñNoǽúýƧ6Éö+±x~løòïzó²xÔyXpòŽ¿¦,wKd‡+.|ü Œ=–n<ú¾3~hºÏ^‡®ù¦N£¦ü¤û�ÓÙõ¦Ë2ü~3Ïspém2ö½C¼ÙMéxòã­2+¢É–»¼+>µ¶Ó;LûçñÕòñÑþnk4œ–djº“ÉZb]Ë­¶Y·C¡æÙxvRì�¯Tî³äÿ«0åAO¼]¦žOÓ8VÛ5)nf65ÙÒNã·åÓ¤<ÙÖix',ÈÕu/’TĬ[¸Ùf݇šåâYë“´ê½Î|Ÿõb,˜i÷‹´ÓÉúc ÚKJ[—��vuök|Qüâø±òyư8¶.~»¤ü¨Â�&>GãŸ-äÓíwÖ`kr6Ù<Ÿ§·Ùú¸»7‹SYƒ“>T8ðŹÓÓ{³°¢Äû¦voixuÑÚà“ç\ó…u�ßsîÅå|ƒðž/ƒvÏ‘r¾U¶ÂÑè4¸6É?yØí63A‰�d“ËdvRë©Y%¾Û-¥oºÚWÌó.iÄzï‹îù¿=äÚ.ÃøÞ ›-ÿ'äÛL=.‹O�ÖÙvVÇg°š LX«$–Ùmo¾žëî¶ÚzÝu)_3̹§ë¾/»æÜ÷“h¸oãxWìwüŸ“m0ôº=> —Yrv=„ÐbcGt²[e¾ë©[äºÛmõºêR¿’òn9Âøæÿ˜sA¤â|KŠé¶|�”ržKµÀÑqÎ7Ç´˜Sì·;íþïi>.³O¦Ôk±¤ÈÊÊÈ–81àŽëäºÛm­i ¯–Ïž=Ï‘ÚÞSãg‡Y»ÎÑ™òåé¹ßoÿÕ?Í»‹KKoÆÊÐqì)mÂÚð®³ÝÒë¾ùfE±í÷X•·&Ì,Y30ráÛò•óo¶ïíw"ñïÄœíßéœé2u|ß¶iMŽƒ™v¾ª”º ô<{[0ö¼;®öÔ­Õ̤öǶÜãVÜ|‹0ñnËÄ̇·Ê7Í–Û¿uü‹ÇÏó·\K¦³eÉÕónاé 1ímU)tè¸ö$–áí8w^m©[¾÷Ií�k¸Æ­¸óÙ‰‹v^&\ ¾nÿ¾Wnbn¼VøÜæ\‹Žu´Ñ[‡Ú^SjMðþ]Τ­ñË7ék¤ýÉx— ‚Û~–Ï}ŽŸQµäm^‡E®ÍÛî÷{-FŸS­Æ—3a´Úì²bÂ×ë°1 ¶ùò³sræ²(£²Úß}÷RÚRµ«ÕpNË{;›ðÞ¶à:þUÎû •qî¸ƪ;eÚr>[Ë6øz9¡ÖÅ}ñÙ&~ãqŸ<6ÖëiY$¥+Z~U¢~(ë¼+ðÿ¥ú±kÉxç�sÙ;,jâMMÏhr«îßó¼ºl110«³ÀÁÞgI¯ÖÍ-·Mn§ +î»éÒ«'<ñ�_âŠ}AÑPG�vû�ñÈö|ÿa�v<öíû“I~û›æY�.Û mît¸zù%¶³Y«ÅÆŠë®útªÈß¼e×ø…â¯Qtdã]¾ã¼v=§?ØcWkvý�Éd»{ͳ-̓í†6ï6L=|’ÛYmÖbãEu×}:UrG€Þ&qßü9ñ÷Å.5—Οà8zŽA½Å¦u˜œŸŸîós¹gfòÜ,}–Nfn¿–v&ÿi±Çľ[é…M�[_dvÒ›¦Û¶Ý6øÉÿ›_dòã{È ^·]Ç”õf«½x§Õž�}Ï#«r.Üòìˆ)ZWï“õŒ›ìh!§çK6E¶ÛëuiJóæ#¢¤ï¯�®÷Ôë°!Îä½q©ÃîŽ1Yn­.ÅÈë<ŸÓœžL[)JÝ>~_]þšÄ‚+:Irm¶”­kJW™0}'|ü~÷¦«]�%ë�Vsñ�©¶ìl޵Èý5ɥƲŸlù¹]y]Ö,Só¤—"Ûm¥n­)^WüÙxÕ±òÃâãÌ.§ãø³fs .²»µxV.†^G¼ÙrŽ‘Ýj{wŒñ­n5’g]¿çxü2n?�v=+7ýUºÚRú]u—VίE^ò¡ðÊ> >ct[^?Ãüò«•bi9‡[żuí-þ]Ðë¹^ª;îÄÔõ0ÛeÏX5¼ŸQ Ðâñ¼›þž6ËË5÷ÝtX”ÙKƒá[å§M¹Òq/ ü›ä¸º~I¥Ç×qŽ�ìÍÞUaÀåšØî®&«ªùnË*jÁ¯å:¸«/É¿Ù�´Å²Ì ëf|Xߤ¥¯ð·ò˦Üi8Ÿ†ÞLò\]G#Òãë¸ÏBv^ï*è°yV¶Ë뉪êÞY²Êš°àr}\U‡�äßìÇÙâÙf õ³:,oÒ6þïž¼.óÐpïüØçZÍxñÌ]ñÛ¹ùVÎ,;ÛQmÖj4_Ͷ9vÁ‰7t鬦6§c$¿yæ�ÝY“{™;¹h%” b`êü­|ìuߊ؜³¡¼XÙè»;Éxé›Ç÷¼º ãnúó£¶^ÊÁ›&|¾Ùõ|×±t×Ýtqê,¬¸Üû.¦Ò·_�.³#�ß'?5]ã/'é?¶:NÉò.Êçhw|– ±öü¥ó춸ù²l¥¥&×òë£�QÒbkóc»ô�Þü{õÙ<ù5ù¦à2âòn”ñ³a¤ì�",ûî‹uÉ`»oÀúg>Ûkd›m¤Úþ]ϵRÝtqêcºL=~e—~’º·Á~»"7ß1ÿˆ[£~>uÛÎ�ñó+‹÷ߘ¹8ûmvf‹eÓ®z 7ùõÖçvöÏU‘þ¯æxû8ïúOºæ|ß—í²·œ›”r,ùö{�ÖÛ6ÿ©‘™�›‘uòË%Õô¶Ú}–Ge¶Ùe-¶ÚR�²çç˜v_/ä\ûŸòM¿/æ|³i“ºä|—}›.Ãm·ÙåÝî›+/*k®ºêúR–Ùm=,Š;m²Ëm²Ûm¤/9¿7åý“˹=çÜ�o˹—+Údîy$ßfͰÛmöyw{¦ÊÌʞ믾ïJRÛ-§¥‘Çm¶YKl¶Ûi[/xw‡my'Û<ï½;מo»3¶{+y/!æœÓ‘Í›®}ÐA…‰�6$8ºÍ6�K«ÄƒW¬Áƒ[©ÖâÁ……�C žMå^YŠ€¦x!üG¼5þJ~<~¨¸‚ÌÏ ÿ‰Ÿ‰É�¡?U|Qeÿ…ÄßÄ¿äÍÐÿªÎ*ºßÅâÃã�÷†êÕïmcg2Ï zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆî‡ÆßÎo�^Ç¢êÞÓ�eß¾6áɇ…�Ŷû:ÿl.µÔÃq=�aÊv_eúŒ(>�Öh6u“]íÆ¶9uŸVi®íGÇ·Í/zx{�¬û>=�yxõ‡\ ^9³Øñ}×:˜-®7·®y&u÷Y‘©ÂÅ­•�E²ºìnû¬ŸS…�ì­š-�×àûqì‡mu/–[¥ñ;ø—ü€ð£_ƺ7Ë,SåŒz<ô¼kcà ~ýêMdYÖäbâñŽIȲñ1;3‰ë0¥ŸC¿ÎÆ› âAƒ¶Á×àǯ–n~%yÅã/›œ&Îkã×eê¹Mø¸¸³rn�uºnÅàù1ÇZárþ™%6šêG“}ÐY›25y’ÅÝ2²,·Þ˜ÿ‹žexéæ7 ·˜ô7bêùGݱñ¥ä\K*êjyï Èȶ”ûŸ,â9—Û´Öûr)|1åÙlÚìË⾸™9ÓßY‹x½æGŽÞbpëy‡Cö&¯“ýÛ^EIJëMO<á¹Ûë‡Êø–]öí5ÞÌ�|1åÙlÚì»ã¾¸¹3ÙO}lñ7Íüäë8{kÅžáâ½³Ãþ½Ø{{uåëyGÙÛ“›‹n£�p}ö.¯™p�®MÚùeƇkƒ‰vn³/ëbK òm«h[@Ú%>@>d|Pð7mÅ3·Vw'~ãEXñ:K¯ö˜sgé³eÅÊŸNÌå4³7Oט4’©,Û—¼úYPͶh.º[9uçGËOŒãm8ÎÃsgl÷–û&݆î–eå뤗#Uƒµ¿Lt|ßù ò[ÏÎy-ï[K8æžI)Â:«‹W7UÖœÿ©KäÓh%ÌË¿;{•dµ¦VÛ>\­–E¾Ø«51â‚¡Yægž¾DyÍͬäýÉÊ)ÕMuxgWñªåëzï…C[d²²ê´²ådߟ¼É²k©“µÎ“'a=·R/«n4pÁ-¼ÊóËÈo8ù­œŸ¸ù="ãš©o¯ ë5\½o^ð¸n¤–VM^–\¬›ó÷y6Ku2v™Òdì'¶´‹êÛ�0E\¯ÉŸËï–ß(ç#/·9,œ3¤4ÜŠ]×YøßÃ6�õ¿ úÙÝFÛwî¦>Obö>«.{'ßí,¬–K�™n»Yƒ“÷ôq¥í2r¸5¿Ã›ñÉ?Zð¼Ÿ;{{A./7ìÍ.N‡ ´»�tQäñβέŸ¦;*+2½ùX›>Ƭ_uÖKlXò[ ŽY£’|]½=³øøÿ›®¸nGšÝ©¤¿™öVš}/Gê6¸�'�u¶mÖWiØ6Y•ZÍ�±ì;¡¶ t¶Å ö裾hå›médÁ><›®øvGš�§¤¿™vFž}/Hê6¸Ç“Çúã6ë+µì ,Ê­Óãì{詺[b†Ktqß,rM�µô¶Æ/ÂÝñc?�]—ç¯qéé�ÝOðÜMQéòoÚÅ›×þ9ççáoq¶9øRWWv÷º¶Z­væË¾–mؼ Yt0IŸ³Ä¤§RZIQ,àVþtôþ.yƒäWC]ƒ™¯Öu÷ir\>%Âë/ÍŸ¯·Tä}o±É¾+lŽùvÜ s­Ê­m¶ÚzÍù)ùœù¯Ñ’ø×å—ô—Üg×k8/eòn+�•%’äÝÀ·9ä}y—<‘Ûe—K°à»�vEÞ”¥=eüŠÑ¼Ôèé|mò¿¿zOîYÞ Ù\ƒ‹A—}’dßÁ7ä]}™=ñÛe—K±àû�~EÞ”¥=eüŠz>\¼l³Ä�’�1º7 [ Òñ½sïùÒq{'�C¡ë>Ú‡¶úËŽ`G<÷Ewà\ã]®ÈŽÚ]9X²Çe×Ûe/®§µu¬s:·ñCòq̾9û¢\­–>ÖøõÙYZÍrðJÛ&ËY.‹°¸=™¸°ón/ ò©å’Ì]¾òbMtRW3 §¿#œ»À>Þ’m”Yü£ û+]ƒÛ|¶Ë�‹f=õ‡Ÿð»&ž x9�Škét_f6Û ëñf¬rS3§?#·ÀnÝ’m”YÜŸ¡{+]ƒÛ\#Û.v,x÷Ö,}Ã,šx ‡˜qȦ¾•‚KíÆÛa]~,ÕŽOºåá÷Ÿà¯æ;•|bw�ð+²6û½×†žƻ¸8v<[›úû“O‹‹¨Áîþ¨†ë²`ßê±11±·ØØv]&÷G�dwC‘—ƒ«®=‹=OÛ]mÞ�wÅ;g¨yž�°:ë›êãÛñžWDz¾õ®Øâ]|�O –ßlYzí¦·61s°r£‡72q²bŠx¤ŽÙ÷õ‡huïtp.3ÚUË´¼ë€su»N9ÉôTÊ×l1¾¤˜óÅw­,ÈÂØë³`—3"Ȳðrá—"8¦ŽHíŸ'Xö_w?ã=ŸÕ|»KÎx0×[´ã¼ŸA•L­~òIñ]ëK20¶:üÈdÆÌÃȲ,¼,¸dÇÈŽ9£’;më^ÌëÎäà¼g³úŸ›ñnÇë¾e®·mÅy· Ýëùßëë4¸×äë7¹òp²­ƒ/He¶ÛýðÏñ_Kd²ëi‘ñîÞࣞrü„xåà\IÍ{£”G‘Ê6˜òÓ€õ/ÈÃÌìnÁÏ·êÛm4úi'²ºî=‹,Uûîç2°ëpý)d¿*\|iô×Ìß;|ðk¯îæ=Åɬ“�lášÞÖ:qs{žgGlž¶éô×Íp´˜²Y陶ˬ:ìJÖØî–ì‰qà›M¼Ëó« |àóàä±ÉÈvPÍNÖ:)±s9ÿ;ÎŽÙ)íÔiïšÊái1¤�Ó3m—XuØ•­±Ý%ÙÁÚOçGÈWŠß=Qly=ظÜca�¼§àhàÝv�ln´X˜ù9|s­¸wÞðæÜç[6à ŒÜ™°´º¹3ñ¯ÙgaA-³+Ùóóä+½¾B»nþÂím…4ÜCCvfWu“7.NÖú<›¡¤Ö`Å=l®ã•n­ÆŠM¾æxíÉØMe–[lpbaã@ïÎ_<û³Ï.Ò“�övèn%¥¿+­º§M›•'ëÝ,÷GKìÄŽo¥]Ï(Û[wíwÇnNt¶Ûe–ãáɇ��8¼ðî¿;ûBNuÙ¹ÿ¡øž–ü¬^·ê½6nTœC¯´ÓÝ$·9¾�wŸmdß´ÜO¹ÒÛm–[$8¸˜Õ}|¤ü·ù)ò“Ú’rÊÏ›ƒtŸÛäåu�<{q——Âø./³'s»Èº sžÈÈÖåInû+«õ¦‹ ;é‰nˆ4•¤îV&møVÿ¹Ÿ˜¿××PÿP9Ê^?†wû—ùYý~õ§ö=Êèü4ŸÜÇʯëó­?±þP±«ð{þÎß ?–—3ýFôXI;¤à–0¹�Ÿ�Ú¯ä—ïgHþ¡ºÑ/œŸÚmä'ïwN~¤zíÿœOÚeäïoNþ¥:õXGâ£ý­ÜãûÉtŸö;–ãs’ŽK£Œé÷Ãí>ñûàn¿°^é?Äí$ñWúóä?«Þ`éÄ7í ñ_úôßÿ`¹ÜÏÃeûj|0ÿŒWü“ûÕeÒĆËY§çñó+ù)ùú¢åícócø™ùoü˜ûïõWÊÚÍæ§ñ7òÓù3wÇ곕07•ŇÈïï Ûÿ«ÞBªÍYš´) ½þïâ³ä÷ÿÂý]q”Ë¿ wñ`ïÏïõê÷‰¦Gøm?‹'~xWüUc÷àýýš}áü¹»/õã2O©$j•�ùÑüÉN•ìÞ‡ì½}»Ú\GkÄ÷VÒ ,Œ½us¡÷k9Ÿô†6f&7"âÛˆ±öZÌ‹¢¾¸» Hf¶žèèÅ]ãÓ|'ÈN¡ìN”ìm}›.Ù<[gÆ71ýI²°¾û»_¼Ô×;/Æö‘A°×d]õÅÏņkiWÞ9¼ƒê.Ãé^ÄÀ·cû'‹ì¸Æâ?£‰6Nßb÷k÷š¯¾ãåcco¸æÒ(6ìŠÇuqs±¡šÚ{¬£÷gMu×�ýEÙ=Û|zUÖ�±ÃwÜšh§¾H.ÍÑr-|Úü»ð³`­™z­¾%¦Fv=ñå`æEDÙ4V_m[^KøùÏüVï~Îñû³pþëÌ:Ë“æh32"†xpwšïHóx÷*ÓÓ&Èò/ÐòÞ?—‹²Á¾ûm¾ìL¨ëu¶Ýëm+Vò/¡ùÇŒ�ÝÙظ¿C•uÇ$ËÑääDZaîõµ¶<Þ?Ê5VÍé5tÜ«A•�±Ã­ô¤Ÿvɳßm·û­¥lžDôO7ñ›»;¢ûîü§®¹&^“'&8¥‡w­­,ÌÐr}U³Ò“WMÊt9XÛ :ßJ_\l›=ô¶ÿu´§gÎß{'À�+;‡ÅžÏ�''oÖœŸ'Žr©5øºÌ>ÅëÝ—ýRëþÉÕa`îy6â™8Ù×àÛŸ—6«*IµùWÓ3";0c °³Q@î9$†Hæ†Kâ–+í’)cºë$ŽK.¥ÖIöÖ—Y}—R•¥iZV•£ýÅ,�ÉÐÉ|SE}’Å,WÝ‘ÉÔºÉ#¾ÚÒë/²êR´­+JÒ´¸¥’#š/Šh¯²X¥Šû£’9#º—Y$wÛZ]eö]JV•¥iZV�¥¦ÜíøîßUÈ8þ×e¢ßh¶X;�&ïM�•¬Ûé¶ų́³u»]VË X3uû-~lMðßd±Ke·Ùu.¥*�ƒÿˆcËñu<¾1iåGUà[L\yùvê}Wqè1+t÷Yn»³/ÅÚßÊ1 ›&²]ÿc—%‘Ç�v6ÛK{©á§Ï?“þ=ãê¸WxãÂc¬ð©n,9\Ÿo6³·tXžë½¿qì;ñö5å0c]5òÝ÷72om�GŸ‹ ´¥½Îðßç‹Éï1õ\/»±©ä·Z`Ûn,<Ÿo.³¶ôxžëý´Àì+±ö5äøø÷Mt—C¼ÅÎËš–ÙyذÛJRUß¿ŠÇÊ¡ã]gæ¿“ËN¦×Ó[©·²12ñ8÷’\_Mxî³ïY[ÜŠGÄû›ô>‹Y™5°o#Öï÷LÚÍ�É}–ÒÄ¥<ðßKì¾ÊÖÛ­­+JÖ•t×'3 Ì9áÊÄʆ,œ\¬ilŸ'{-– àž+®ŠhfŠê]eöÖ¶ÝmiZWÑÓlœ|Ü||Ì<ˆ2ñ2à‹'+XçÇÉÇž;eƒ#x®º) š+©u—ÛZÛuµ¥i_GVß¡ýŸØbÎÕï—è½-œ‹º;g®:ŸG5'û®Ï±y§áØ™ÒcYI%Ç×I¿Øà~‘Ë¥µ§¤0}In­Ô¥¶Öµ¥+�û/¸ú“¦4ÖrÞìîÕú9k%�myÿ/Ðq,©b¶—]�…>÷?ÌܪÒêRØ¢÷ÉukJRÚÖ´¥q¿ewRôΞÞAÛ½�À:ÃI-d¶ §>åú%‡—$VÒëà›{ŸƒnnW¥Ô¥"‡ß%×V”¥µ­iJønÇìþ´éεì.Ýì> Õ|E÷jîùÇcòÝáú¾dÅ…‡úW“r}†¯K¯ûÖdöEÖžÏ|·Ûm¾·V”pcÊïÄ�âU`l´ž3è9G’Ü⸕¦¿w~Ó®zŸ]›-2 ­v[ŽQ­ÆæÛ™µsÇ·cáimÄÍŠïd{®­o·ˆþNþ!_ºÃ?OãÆ—’yÍ>í[p¶´ÁÙuÿXkó.¾|y)²Ýòmv?.Ú˯¾Ëfú8:{±3c¯¶Ìø«_}¼Lòsñx«Ö89ú�t¼�ÈŽg÷jÛ…µ¦Ë€uŽ¿2ë§‚ÿÒ;®K®ÇåÛiuòYdßGQ\LÈëí³>*×ßlw<ÑüSž7cn¸ÿBÏÉüÍívNvº_[Y“º“iªÜ`àæÃ½îŽY¥—7Y™­—'']±âzn]ƒ™~=¶],ÍnE±)óCäoË<¹ 6=ñØsIÄpv¤x×Rq 2¸çTñL‹)—f>F·ŠÛ›™~Óm‰|ðÇ´ÛdlvÔ‚[¢®Ubô²‘lò÷äÊ77–æ÷o<’þ)ƒŸM‡êÎ%Gë3“e™1C“¯ãtÌÌ—i´Ç‡6xãØírv;;ašø©“ô«K)O.þ@<žógynouóÉ/â¸Yߤ8÷Vñ8ò8ÿYq¬‹lÈŽ�¦f\»=¦49“GÇk‘°ÙÛ·EL�¥_e ùò%ó#çÉ~|Zîõçø\[¨µÙ¶l4>?u>6Lju¿:D7ÖË쾕¶ûkZV•¥_Ÿ3a‰•�Ÿ‹�����>nddâfbdÅt98¹XÓ[|9Ùßu—Ù}µ¶ûkZV•¥EY¾yøÅ³ðëËžòñï6,šjø?4Í—„feMTûn·äqÅÉzïk>TÛ�>~_Ûaýò–}�g[47z_Ô¥ižpxß±ñ/Êžç茨rlÕpþ_—7 ÊÊ›ïRí:ï�Y!à;)s-Ž8r³¦â›<[s=ž¶Å�dð×ó㺔­kÍßv>'yOÜÝ—Mš®!˲æáyY3S*]§^r #ä e.]–YNlüWg‹ng³Ö‘gY45ô¾;©J}~\ü*Ìðä Èo±tSé:æ]“Øu!äWjózG±%›’õþ&›sÉ£¦Ã“Û°²äã-…²dÇ&÷EŸ&’ø¯¹¨mRj£› +ZV•¥kJÒ¾´­>ÊÒ´ü•¥šõe÷Å}’Å}ñÉöß–][/²û+K¬¾Ëí­.¶ûn§­+O¶•I�ã3ñö@áh:OÌË9'põðë¸ïmâK]¯mõæ·éR0wñæKÝ™Åpe²ÚÛtÓǻ‚ù}“gÇf.R/øéùàç]%‹£éï/­ä­ÕØÒck´­‰}v�£ÀõÿN˜ñâò(ò¥²þÆã8WÛeô’ImÝâCY}²gÛLlH¤OñÙó³ÎzKIÓþ][È;[«±oÇ×è;Súí;C‚`{)Xœ‚<©l¿±xÖöÙu$’[7X�Ö_l™ÖÛ�‰Î~$ÿ×(êüN5Ð?%Ù“±¸‡Žq*tºìžGÙÜ['UK»×K�t›~ÒÒá鯦Dü�]O.ºìîÊÄßægÝ“�3^�ò¥|”àšþËè~Íâ=¥Âv6ÁKw\OkuuÙyXÛÐü‡YwÒÜqnE�‰™ÙÍ–>&÷ÒÙ¡Žï±.N›ï¡ò„ëû¥;‹vO ÙYÓÜñ�œY¿rÊ›:º�æ¾ï¥´ã{üll¸îÈ×l ÅÏÅ­ô¶hc»ìKw§;較áX‰Ò½‡Åû'‡lm‡éî8ÆÊ,ß¹d͉�›ú+y¯»ém8æû*:äk¶cgbÖê[46]ö'KÓ]ÙÔ^Cõ×í¾‹ìžÛiÊ ¾}4à›í"ÑfÝ•ƒ7 ü½|ÒÓ oªË²ü|ìŠE™ƒ•ðdEÖ_e¹�•YQ”EåÇÈ·ˆ>ê²gï®ßÑjy]ºúç꺯Ž_o*ímý$ÆÉÈ×ÙƒÂuRIŸ¬ÄÛÝ‹t8û­ÚíGÖô¶L¸éöÓN¼¥óßÅOuyáÚú=?%·ïúεÑIo$ìíí’Á“.08f®ùv8X›I1/††Çî:ªMél™QÓÖ´ÓÏ(üõñ[ÃÍfFGwö¶�QÉmÁûö·­´R[É;3yl°dK�L¬¾]Ž&ÎL[¢‡?a÷]%û$ÊŽžµ¦šyòá·�ÜcøMåG~pn®—'Ý–‹†egK½ìþ]‰&ÂÍM™<;«¸Ü;~{ɰ#ÙËl9˜šù0p}k&TÐCeò[ Ï�ŸŸ¯#ü´ÁÝõ‡Façx×Ñ{L|íNÞ .ãï�±Ø¬‰í¶ø¹O5Çø/§Î‚ÛeÔé)¾<Œœl¼íŽ4”²Ø”ùßó›ä”›ž¸é,\ïú_cV·gn£m÷®Ôç:Ùr+JÙȹ†x´âúœì8c¶]^›éÝ[eÈÇÊÏÏÆ—éÛ;~qûÿÉü=Ç\t®.o�]1±‡/[³³Q¶û×is�lÓûn�‘òü8ñmã:œì8¬¶]^ž‘Öëeȃ+;?JGlþLÿ¿’žNA¾ê� 5|‡ÄΕͬxÙ‘vÎ8üœæ±_²ú¿O’qÝ–N�¦õ{r±k~.†|íÔ9>ø÷ÿvÉ›œ­kZÖµ­kZ×Öµ¯ÛZÖ¿–µ¯ó\2páËï¾[ï–[ï’I/ºù$¾êß}÷ßZÝ}÷ßuku×Ýu}kZýµ¨ò´ëãÞµ»À.ºµºë¼Añª·]ZÖµ­kÓ.µ­k_¶µ­V^x!Z×Áï +ZÖµ¯Š^;Öµ¯ÛZÖ½EÃýkZÿ5ew‚u­| ðÞµ­kZø«ãÍkZýµ­kÔ|CÖµ¯îÖ«“þ1çŸ+ãgãß'&ir2r<ñ:|Œ‰ä¾içžn…à2K4ÒÉ[¤–Ydº·]uÕ­n­}kö¶ùµ�ªo^¼¸þ*>N'®èý[òVòoø¶ù ýãûcûß°‡“_Å»È?ï!ÚÿØýò÷Ÿë.ã÷¯aý)2¨Å`êÅ”q€=Ÿ_v?`u7.Ôsþ®æü¯®ùÆ‚YeÒòîÈ6œc‘êïÈ‚\Lªan4ÙXyøñåâO|3Ymô²hdº;éu—]JûØ\óªù^«�u§2äü™èä–]G*áûÍ—ß뮞1²mÅÚê²1s"‹/kášÊ_ìšî²ú]eÕ¥}‡ìwÕ¼«UÎzטò~ÌôrI.£”ðýÞÇ�oµ×O ˜Ùâí5Y¹‘E•‹-ñMe/öM÷Y}.²êÒ¹ƒ¢¼�îïûKÛ¾>öŸ6êÈÐ_Ü9gßfèöáÛ›‡Ÿ>“o4”ÃäÝo^óL¨¢’(`³î3[c�ß4¹ÝuÕ‘G‰ˆ·µx\ZÎ+åç_CÛúHn² »?¯!ÔqNɃß“$¹;n#}5¼—eÒ—Å VâÉÆédVVénžJÖêȃÄßÄMÚœ.-gòç€CÛÚHnŽ {;¯¢Ôq^ȃß<’äí¸•Ô×p^]•m/Ž(­Æ“ŽRØì÷I|òV·V`^~/>_ƵÚn òÒÙ=‹5¸—÷ï@bè´|×:˜ºÜ,|lžgÓ›¬íÜíö»kgÉÏØèöÜwí�K}Ö~|ž¼^ù.ð�Ì5¸}'ßœ;cÌv6cÒÞ²åy7ð^Í·6\K³2uø|3•Ù«ÚrI5–Ç}¹zlp,ºß[r.¶¶Ýt�¼où ðëÊëuø�5ÞVTðãbãC.FNND¶C��� —I4óÍ%ÖÇ1Gmnºë«Km¶•­kèþsM<2ädKAóO<×Û0õ¾Ie’úÛdqÇeµ­×V´¥)OZ¿œÓC� ¹Å<I4óÍ%±C 1[[å–Yo­¶Gv[ZÝukJR”õ¨äG”¿9Þ1E°ÖYÛ1÷¿;ÄöGè+pyõ>´°Ëu—l9Ý™ø=i®‡"ËbˇôÄ»,zÝ_LI.¶ëiÊï%~f|ñ¾,ì öœ=ÓÍqklvp¾Œ¦:—ë_I-¯ßùŒyø]y¬· {if\7í®ØCë_n,—[u”å�’Ÿ2¾ xãnöÑ‹º9¦-mŠÎÑÔÁç2Rk쾕®1³? ¯u¶aÏm,ʆíµûkZûqdºÛ­§!<ÏùÔøÐð~ ¾·°¼„Ñö‡eê2¶ºéz[Ç™5�½Ùn¸þóCȸ÷ »O¶Åà]qÈ´sæß,¸\³{ ÈšÚÆ£Ëÿ�-<�‹cÅ:ºhügë<Øë�6³€n2sûo�}¸µ’=×i_‡©ÙáÇ÷Œ{ëeš_™Y)—¥êMf�gß³åB·l§Æ–µºke“ëÕ6³õ—âWã·iç7yC·æš¬¸¼pêŒývßµ6Õ›'_-Î÷Û•§ê­.n-ðç_°ä´Š·ìæÅ¾+õÚ‹d¿ëÁ•>&ëÄÏdžÓÎ>ñ‡mÌõyQxçÕ9úí¿jmn—'.Y›ï·+QÕšlÌ[áÍ¿aÉioÙM�$Wëµ6É׃&|M!ßÃáñ “ò5ä=Ý·ÜÜsg‡>>o5ûw•v‘ê;�²°ë®Üè:#g›ý]l¸YPìùmq#ÉÈÅÑ_ÕÁŸu�� ŒZ½^³G¬×i4šì >›O�‡«Ôj5xxúýf¯Y¯Ç�]®ÀÄŽ\ XlŠb²ØâŽÚ[m)JR‰ûëµÚýF¿S©ÁÃÕêµxxºíf³]‹¿]¯Â‚Ìl<[Ç!ÍÝð-ÎÃ&?õN~Ó‘ñk³ð.¾J{aÆãØÖR¿�JR#ŸˆçÆ+´¼ë¨ü´ãºêY­æÚïíEÙâáÁ 1òÎ9në‚nv1×ïÛ>EÅïÎÀ­òSÛ7Dz•¯º”¤/…ySWÇo?øžDøøØÕñ›¹©žø°¢ûÖÿžtÇ$‹Y…£­ÑG>No(Ömöy»g¾º,Hb¥×Ý[¢ŠŒ1H2€:;ñïò{ägÇ2¿3®61ó¦ä\|þÁéS™•üäÕ¥�âemô¹RlŽÍ«®†ØâÛbG}/¬0[›�›� ˜î‚øòCßÞòÉrzÿ:.]Õ»ýž>;é®M™•oäÛlÙ[}&L4š~Ìï×Cl1í1c’Ù>”4ÌÇ͆à§@|ùïßùd¹<:.[Õûíž>w:éÎK—•N+Èn¶Øq²vÚ\ˆ©4ü?˜Ý¯†Ø£ÙâÇ}·ý(i™�™ 6ANÈüR|Öy?ñaÊ6ž)l]Åã—-̇3œxñÌ÷›[;r`’ngÖøbØËÖœÿ#;ñrr¢ÄÍÖm1¤·ôŽ¿.l]tø3·ðcåSÄ�<ôšœ^¸ç8|?·åÀŽ]÷DsÌÌM7ak³£ƒ2m…œn9¯‹±4ØÐëåÈûî–üšÃ‡tWæÃ…5õDzkÞü˜x³æî›[]óL^/ÚraRmßJsl¬M??×eCT™ß  –Kp¹ÞŸ,+çûîžLªC‹|WfG‡5õ‚ɪøcò[âç›Z}l{Ìñx¿iÉ…lÛΕæ¹XºŽ{®ÊŠ,™3©¢†Y,Âçz|xðïŸïº‹òi5ñÝ—$×Ö ,ˆðåW¿’N)úcÆþÒÅ“œ``äì9_Fs¿ÑüS¼8^.Ú›3söü»-…wsmî7o4™;mÞ²)�LÚåY,1ô‘ÐW@�c^ØîN¨è޲ìNæìNÖ<'SeÕÌäœ×}¯Ðkk5!–hð0äÏžö{|ËaºÜl,kfËÊ“Ò8c¾úÒÚãîÎ펲én#±ç½·ÏxŸ\pÝ]¿êÎEÌwšý²“]e÷Ã…�>|ðýûg™Xën>$“'&OK"ŽûëKk�»7µúÏ¥øŽÃžv×=â�sõv×˜o04ZÚM[/’,,i³ç†¹Û<¿§[qñ ¤™92zYwßZ[_Ø�“×]CÃw}�Û<û…õ^ñ˜±gäœï±9N�…pÞ?v~.« mß'ä™ÚÍ&ª,ͦ|Ñ]<ñÒL‰£Žß[ï¶•‹·ž‰_ŽiâÝõÏ€üfœŸk$9y غl¼>;®º|L¸+�×]m´‡kºÏ›"ñ³¹˜¸‘äcߺœØ/¶JÆÓÍÏÄ=Çunxƒürœ›k$3àÉßÿQ—…ǵ÷ω,UÍà={²‹mºÎÄ“"É ÌßY‰‹N=ÖɬÎǾ—Ö7žm~!Ž;¨‹sÀ<"ã´äÛKáŸN÷çÚœ¬>=¯¾|Y¢®oëí”8»]Ön$“Ç$›ë11bÈǺÉ5™°_Kë’?ʼnÒý[!êß�+|vvåk'ïîy¯Úé:OŒfY&®—epÎ'55\Ó¶3"Ží†7ÖɯÔcåÃ�—�.ëK¢º#ý¯Û�›Þœ÷�v�psŽGؽ�Êr뙼å<§c6Ëg—}´¥˜øÑÝ%i³ m‡ȱ0ñì²#Ž+-²‘cíÕì~ëçÞÊížiÈ9ÿ:ä¹Weîy/%ØK°ØdÝöÒ X}õ¤ý^^‘baãY&ÛÇ–ÙHµvjv7tó}ïdö¿3äûœòLªån9'%ØK°ØdÝöÒ hk}i³/H±pñì‹ mŠãŽÛl¤¼Šòg¿|¶í Ïsù%ÚÜø;+yõaÈ䜿c÷ŸÑºËö[¼ïÇܸaô·ÛFnrC•-+éî­ÓG� +ëZÒžÊzz}¾²Îü2y™7ñß1õ÷Ëupñw]™–ûcÉÎÁíˆ2å¥io¾·MºV•­iO§OJSíõ–?á›ÌÉ¿Žy‰�t·Wwѹ�Aéo¶<œìÖ‡*ZV”÷Vé¢×ÃJúÖ´§Ó§§§Ûë`çàâßm²ûýUò¶³y©üMü´þLÝñú¬åL åGñaò;ûÃvÿê÷�ª³Vf­J@o…»ø¬ùýÿð¿W\e2ïÃ]üX;óûýGú½âi‘þOâÉߟßÞÕÿXýø?fŸx.nËýAxÌ“êGI¥d�Ïâøâ›ÈŽžƒËÎ¥ÑS3¹:�dÅØZÜ;ï³/œôn¾» ÆÆ||ZE,Y¼‹¬ó2§ÙCm.Ǿ}DùöV鿇_ž�Ùûÿ©±ü¦êÝ-s;w£491s}n$•·+šô¾ÙÛm�øøÕŠK3wýu™“6ÇÚ_Óë'ϳ×"{0`GëçoÀ û÷©àò—«´¿|íÎŽÑdEÍõ¸—ÖÜ®iÓ8Wgm¶7ãã}),ÌßõÞ^DÛ-¥ð]6²|û=gš<(–üJß{9À»ƒ�ñî]®T•›/73�`ï#áÛ)3e¯ºzdàËlÕûo¥Íõá?7(ü _«Så�&Û`Ç}÷ÿñmÂz¯°6Öï©_I7ÜÛ‚ï¹ Ö[tž´·ït¶ž”§§¶”£x8—̯ÉW Á�[­òƒ²ÃŽë®ÿâ·„õw5ξ·{þÉ7\³ƒîw·Ûm×úÒŸyôû)OÉOFîñ/™’n…»[äîÿg‡×]éËx_Xó\Ûëw¾¾’nyg Üo.¶Û¤õ¥>óJSÒ”ôô§£¦Ücñ/üÌqí®~ÃÊ­?2×á[=²ñÞOÐ>;ÙªÙRlIñâûþgêÎ3ɽؓKlñÖ„5ºX­¤žøë|wå\Äò‘‰�l3÷_ÙÉm×Ö¹yÝ3ÕQä_K®­imÖëx¦¿–ÙJúSÛ+éO¶µ¯ÚÉøw>H°ñ­ƒ#¶8~Ò[n¾µÌÏê^»�&ú]ukKn·Y ×aÒØé_J{b¥})öÖµû[=ÇïÊ6“S�®Ùq9~d7Ot›þEÔüûm—Ig’Xìȇ‰wÑ[n,wÒ+>–U­–R·Öûý××àf|û|¬dÖJÃ䮿_ï–é-¦Hôô†ÊÝZÒþÿÖ9Õ¬VÒ¾”­þëý)öÝZúÖ¾{3çäã*é.ƒÈ| u/šé-³¦ú:ûa²ë®­1ã®Ã®3¯¬6R´¥+}n“Ò”õºµõ­|þgÎɦMd¬BkõÔ¾[¤¶Ü>œé é —]ui¤:ë:êÅe+éJß[¯ô¥=n­}k\;Î?Ëç,ƒ6mõ_XÉ•³»> ®Ñ�yŸ>·éf’š\+{'YØX×ë,²[l¥ùÏ™íŽßYëuoºícçŸ(Ÿ"=“$òr2¼€ŠÌ¨dÇÉÃâ]…ºëÍfF<Ö}9 ›S×óq�\°Ë­·[t5¶ëkZVž•¯®»sŸ’¿>»I$ä~[÷ŒÍ‘M�Ĺ¾Ï¯uóE-ŸNX¦Öð øÎ¾h¤³ìºÛ¢­µ¥kö}µõ×nqòOç¿aÉ-ü�Ë^ð†É£’)±x§7ÙõþÑKe#–)µ¼ þ5¯–),û.¶è«miZúÓí¯®¨vßÏËÇué±ô\ÇÎŽÛÓacMlñÏÔ˜\ 77_l‘ÉKr9Dq·äpÖèéJÇ.UñÖÚÖÚÛ[nº•Ñý¾çqÈ69;}þÛe»ÛfÉYs6›|ì­–Ç.[¾Û¥ÉÍÍ–l™ä­-×ÝZ´Ço¹Ür ŽVãµÙnöÙÒVlݦß;+e±Ì–¿–\¬ÜÙfÉÈ’¿ùU÷V­4Ûîvüƒc“·ßmv[½¶l•—3g·ÎÊÙlrå¯å“'72Y²g’¿Í¾êÕÌÎËínÑîŽ[�Ïû‹²y÷ls½¤Xðìù¯eóCÎùnÆHþ–$YÜ“”ìv»œ¸±büØí’k©e¿e=(ù¯šù¯öÝm×\Ë·»…uo]èò9/;ì>O¤áÜGC‹|Ë´ßò „Íf%r2¥ƒò²-ú³Ï$pA.’[ìŽÛ®§²ë¾¾åý¯Ï8Yõþ—#‘sn{ÈõS‹i1o†)v[½æl8ü{²2dƒ Ï=+,óÉñRé%¾Èíºê{.¼ëþ_ÚÜïˆu§Òär.kÏ9£Šq}&-ÐÅ.Çw»Í‡¹2C‹‰gš•–y¯Ž"¥ÒI}‘ÛuÔÌ>>ô7hyAÝ}gãïKñœÞ]ÙݳËuœ?‰épàÊ–?½ì$­Ù›�¼ø˜ÙWjx¿ÕÅ>Ëq±’Ï»jõX™yY_m x…Ãüñ­¼{â—bgæñÝ}Ûn}ʱ±cÅ“›öNöزù)È­°A‘.<ù¶Û‰¯·#ß>6£ëïú­lŠð£Ån'á—�ýwÐÜZìlìž;¯®Çšòh1lÅ—™öæ–eòÎO==–ä]VúŸÉî’l]^6.5oº�[U�~x±Å<5ñï:#‹Ý��•ǵõØó^K5¸Òs.ÀÜÒ<¾Yɧ§²Ì‹¡ÉÎÿSàÙ5d—Y���[î¤4ªâ|EëϼUé�úËÙ— êž%�¬Úr:âd`eóžm±šmÏ>ìŽ^×{>»7›ó†nʸTÌȃ]E˜x÷S-Ü&Ö6­µàÅ~$�²ûk¦ø·™½}§»3šôx·jãáC4ùÛŽ˜Ûm/ËÖï>�“_ï·­9^Îy¥¶(=i®ÜæeO-°àÒ”ŽOâð³'µ:‡�yqÁ57fs>‹Â»Žv\pM>vß§ö›+ò°öþÈî–ù-ëŽO°›"KlŠÚY®ÛæäÏ%#öˆçþ ¯ ²{K¨¸ß–ÜSv_2èÜ+¸ïeÁ‡³gmºƒi±»+oìŽé/’ÞºäÛ g’Øâ¥-×m³rf’‘bRˆ¬þ*ŽY¼—ñ7OæGYqè3{‹Ãü=®o;·_…éž_ã^ÚJfs+f“Žçí÷3ôîòË9&%™[ ^§C‘Ér}$Éš+.ƒâ¨k«ueΖïÎéñÏ™ãöEv4ê®cVcIºá›ÜÝ<» 2`Ì®§y‹Ÿpä9²±c¾\ è²0æ­–ýH®ô£+ôïzw�œºwÒ}“ËúË•Å�K¶â[œ­ev8VdE—ú/w…õ×oôÒdÁeòagC‘‰-ÖSßÞ”e^žï.áñû–ÃκW²9wYò¨£² 6ÜKs•¬»a…dñe~ŒÝaÇ}uûí<™Y|˜Y±dbËu”÷Çw¥;âÇ™þSøKÎîì�ûÃ�tÏ(Éûµ»›xÖ|ï[Ÿ&D¿^,š–ßI xÙø�|’ุ/&zƒ÷Î6=˜Òs>#›ý©;"[²¾¤Û]Ö.¯�p ̱c]XãÅÀÔèc»ÒÚÝ'­.­Ýçñçñ÷ÿ ÅÀÒyÔÜ;ºð± ƒ^cij?µ9žúäRì�¶ç [¼àÛœ›qî­¶c`ë´1]Z[ë}?:µï�ˆÓ¿øf.“Ⱦ¨áÝׇ�XÒs)™ý«ùÄ÷×"—Ï´Üaâëwœq‘dVÛ1°uº8®ô·ÖúWÝ[¥Wâãín1‰Çx§›~4è;K �>»oܺ·€óKtX:x16[ݯTr{vÜ/šó}ÆÚ ²å¦¿yÂõ}{£‡ ,¶•í'R~#�¾Æ¶èù—&íN‹Ì²+Xû3¬ö»|,¬©)é6>Räöe>”7úúK™·YéZÒÚúÛN¾uoÏÏǧ?Žëyg&쾖˶ÈiH;®6»L|œ‰+í’<<±á¶ïû~®]1)[=+Z[_[i׎®ùôøøçñÖÞWÉ{+¦2í²}Åë­®Ï'"OKd‹;«¦ìXm†;ÿ$¹TÄ¥lô­im}m§{ºCñ4|C÷»E~ï½¹wCò]öκ¸¸_wõ?9Õìu²]“v>>f÷˜uö³±º—O¬É¶ÚIL‰ù±ÃÔ¬ÕŠ´º”èOù ð±ðqs¸§˜þ8O÷É,‹]ºí¾ĹòKdÇmÜc—mt|Ž*ß÷›-§¿Ú}O[?Ÿ¶ëi¾\7Ïÿùö6.GòÓÇÌ«óoŽfdðââbÃ.NVVL¶A���—K<óÏ-ÖÅ 0Åmn¾û«Km¶•­kèþ998øXù™™bbbA.NVVL±Á���wK>FDòÝlPÁ VÖëﺴ¶ÛiZÖ¾ƒrß.|Qà3ɋμ�ñë…äÃôþ®?-îž·ã“Åõa¦D_R-Ç%Ã’Ï«�ZIo­)î²¾ê}ŸkòÏ(|gàRß9ò'¢ø\ÑÖ:_,í¾Ç%²²Åõâ¥ñî9öÖH+ï·ÖŸmŸm>Æå^PxÑÁ%¾oäGFpÙ£­”’.UÛ|�Ke†™Òøöüƒëk$¥öúÓí²¾´û>Ö8ì¾ãê.—ÐÏÊ»‡µ:ß©øÆ/º¹<�²ùÇàšzYÙWÖ}Ç(Újµñ{q±¤’¾é)éw]ù-­i¦™ógñ‰Õ³l°v¾Tq^S´×[/·¬øï7ì¨vSEm+L}o#áœkoçº__K$¿e=kùd£Q;æ㋬çÍÂÛy7Å9ÇÙ}˜}u¤æ‘t±[îú[îÇ·\Zû¥õô¶ûóㆵÿOF¢ö'ÌÇ7ZM�‡µòg‹rM޲û0úïIÌ;éc¶—} ï ãÛŽ-%Òzú[%ùñÃZÿ§£ŸÁóuñ5ÑØzìþiç—@îàÚÏv65�?É3<†ÌŠKl­õ»c®ègfl4ðV”û%ËŠ«_²—V¿c–ýÙø£ú#Gd¸~>xÝÙ½�™\lÈLö—!ã½]¦ÄÏô’ÌÌMgþÙ»mö¶Û½’I×éç¾”¬t¬u­$§5{‡ñ(t~’Ùq:+ÇÞÈì,ϧ— vÝ�½Ðužš šRK0ópðô_Ûm¹À÷{dº)骚û}lõŽ¿ŸNmwâIé%’ât_�ý�Ø9ŸK.ÒÝ�¼Ðu®š ªRK0óq0´Ûm¹À¥ÞÙ/Šjj¦¾ß[=c¯¥ôãÇ‘‹ãÁþ¾ý=ªñË¢ûÏÈÎA©Üãák7<Šþ?Ñ}YÊ5úwfnôü—mw:ìÜO£eõ¤8Ûƒ,ÒYZ_X­ô¾¼7ò[ç‹äSÈØ'Óáv†@q<ˆá²^?ãÞÂçÏ|ŸÛ•7?ÍÛïû2 çŽlÑbnqp¥¥¶Ö¸ô­bò#æçÏ®þ†}Nckú+‹Ï±K£èœ Þ°ž±ß-cÉ›�gí7½‰�•tRû%· k‡‰/¶•¬«ŒþC|Ùùí߰ϪÁì]Fqyã¶94}�›Ã³ç¬wKìÉ›œçì÷�‡MñKí–Ü=®$¾ÚV°R´¢;>]~(O“_$ð÷\[­y/ ñ'€íbäZ»°ú3M‘^ÈÍãûºYNÝæY|ƒ“i¹.�ÊÓqÄbâ9T–K¤¥¶Ö‘Ò>:çgfìó2¶;,Ì­†Ã?"l¼ìüì‰ró32ò$º\Œ¬¬©ï’|Œ‰åº·_}÷Vë®­kZÖ®Kçgfìórö[,Ì­†Ç?&|Ììüì‰róss2dºlœ¼¼©ï’|œœ‰¯­÷É}×]}Õ­kZÖ®Mæææì³2¶;¼­†Ã;"lÌìì܉r³32òdºlŒ¬¬©ï’|Œ‰å¾·_}÷Vë®­kZÖµG¯–òîWϹ>ÿ›s®OÈy¯3å›|þAÊyw-Ýl¹'ä»í®D™›=Þÿ¸ÉÌÚîvû¹n–|œ‰dši.­×Ý[«Z¿+ò¿+Ï=ÏYu¯8îNÂá�UÖœ{7•óîÀäZ¾+ĸîa³#i»ÜeY‰‡r2¥ÇÃÂÆ¶ûýóääI646ß,×Ù—_Ok×uÍ{sžñ±ëž?›ÊyÏ:ßë¸ÏÐ`}r6[}¦E˜ø°Ö|™`ÃÂŲ·Vùòr$‹ /–i,ŠËï§´ëž»æ}·Ï8—YuÞƒ3”sŽs¾×q®/ ÀúVäl¶ûL‹1ña¬ù2A‰‡�e×{çÈžH±ñ¡¶ùe¾Èìºúf/ºµü¢î޹ñû£øŽÇœö§iòLn3Äxæ¶;®ºl™"›3a´ÙdÒÛ¡ÔqÎ9¦ÄÉÙm¶9³Y«ÄÈËȾ8!’ûv—Ïÿy_Çÿ{aô/4æü{Ÿr zó‰s¬Ý×ÀÙkôøòrk¶pO¨Å·míÍ̳]—©’Û2oŽ ÏÖßX¢­ke»3çg†|“ÁNêÀé.YÍt|ïw‘×Ü_œeî8î»?[ªÆ—�͵şS�fÊK²ó#ÀÊÔIK2n² ÏÖÝX£¯­”Ù:<6äž ÷NIòÎg£ç[¹øæù{Ž;¯Ï×j±ää2í1¦ÕcÙ²¾¹yv`ejd¥¹Y gŽën¬QÖµ²›ãòõñ‘?Å'’<ÇŒ®é‡½gæ�Å»¦nW^_ÖÐê¥ä|ﲸD¼^=4œÛ�ß±�^º»"ÜúåcÖkréeq£¬^é4…¦-4r°h¿Æ~öîEñçáF}ØÖâׯ.˜ÑR+e¬ÔºÞ/Á4¼jÌŸ}c�ÒìË54š¶ú~edöúÝéî­”zîüðï2è)�X|méÍGÓ¤•–—S�ðM&†ÙýÕ²?Jå[­¤µ·Óó+·Ö¾žµ²KãËovïÁsn‚˜Õ‡ÆþžÔ}:IYiu¼ƒit6Ïî­–zW*Ým%­¾Ÿ™[ý¾µôõ­É_³OãÇù ø•ú‚à ánCq›Ìׯ.?Š�“ŸÉëº?Vü•ƒü›þ-¾BxþØþÀ·ì!ä×ñnòûÈv¿ö¿|½çú˸ýëØJLª1X:±e`÷|PüfõŸÈgŠmE>z¯!¸&w^Åн�•»äxÚ­ç/YÉ÷yêL�fÚíNÇ&Ü<ˆ2£ÃŸ†œÇˆr~¾åœ›‚smÇŒs¿ÛñnUÇ6ø÷bmt\‡CŸ>³q¨Øã_ùÐfköÒE%¿¹uµq_—q>IÀ¹W$áËM�Ç9oÞí¸Ï'Ðlâú.ÿG�>·m«Î‡Ö¿O+;H¯¥+Z{­¯¥kOµÅÞ[Å9'å<�„ó6åœGyµã\›A´†¸û.ûI�>·m«Î‚µ¯ÒÊÁÎÆ¾;éëZ{­¯¥kDX»¨û¡;GŸô·oq\þÙÝ_Ê÷\'œñMŒ¸YY:^G Í—c‹f~¯+?Q¶ÂºX½øÙ¸Y89Ø×Ù‘�4°I—yÇžyæ9ÚV¶Ö—[ZÛuµ¥mº•­+JÒ¾´­+O¶•¥JV´­+JÖ•¥}iZ}•¥iù+Jÿ5ÿiZÒ´­+ZV•õ¥iöV•§ä­+û•£úÁ<ø³Ã“�4¸ù8òÇ>>D_ ðO ô’)¡–:Û$RÅ%´ºÛ­­+miëOµ¾] ò}ç×�XÚýgRyGÙúÞ;ªÂ¦·WÃùVϲ¸N«]ï­õÂÓpîÉÂåœsE¾ë«Jáccßm×V¶ÝJÖµoI|“yÏãÖ>»¬<•ì|-»Ì åYø��ĵºû.÷S QÆ; ”é4pVµ¯þqA�}¾êÖÛ©Zú·s¤þH|ãñï_Ö>Iö6‹]‹n¿‹r�†/bñ-n·ûþå©âý‡‡Ê4šH+Z×ÿ8 Ç¾ÚÝZÛu+_W\|vùßù_ñ› W 󱹯³q¬Úåñ~ô³Kß8YxÚ¸1pãã˜›ÞØÖr¾oÅ8¾F%°ß…¡Ûjl¶•ºH«×VGY:³ñ=ù�ÆDaö·MôGië0cú[,íF'/ëŽa»û$­'ÈÛbòOÄð²½÷[ë\} q{-ôútºµ¹ÔN³üH^WqëµØÝ¡Óý)ÙZÜHénV’.Yל§kZRJý|�¤{¾UƱ²+}Öÿå�,qûmôöz×ÝN u§â;ò·�W]‹ÙýCÒÝ•¯ÄŽ‘æåé¡å}{Ê6µ¥$¯×ÈÙÅ»åk"·ÝoþXÒǶßOg­}ÎÖôçã!òcGnΞ@xoÑ��Yo�ô5Ý?Îù÷FW+bô–Ý­9œ>CSo,“~u·C÷m·ó}µ¯ç7Ÿ†~*.©ÎšÛ{ ÄÂâðV—ûäá�«ÆùäÖÖŸCÙíƒwÄ:æË©wºO_Y)íö[ù}õön‡üL=W�})Ø>*ö�Öÿ[¸wcñÎu})O¡ôý#Ýñ¾»¶µ»Ý'¯çSÛì·Ó×ß_fçpÿĵչ·ÒœûÅ~Æcõ¿Ýwì~;Î/¥)ô~Ÿ¤{®7×¶Ý[½Òzþu=¾Ë/¾¾Î‹õ§ãð³kÇ Éî/|¢àœ²êÿª´½i7TvdžŸCïX9?(ç]/²É¯ÞožÏKµCŽËÿ,—YÄcþ'&¶ë¤ë-1+K½)fG_u5×_OO_uµÄï,«)oî}µ¥Ðgx?çƒRêÉ×QâÖ•ô¥³ðN®º·ÓÓùë~ëÝ6Ò”ÿF´«;cþ#_¦¶êÉ×^PâV•ô¥¹¬.ºúzzû­û¯sdÛJSýÒ­„â‹CâÃ’áådîtþSuôøù4‚-w/ê>-›™�b²O¾âßÀ»;›ëìÆ¥÷VÏIç†o}µþ‡íôº¿£üç�¿÷¼ò³üu‡øéó�¼ÿÕ ÉÏö…×ã}ú?Î.ð[ÿT_&Ú]�×­ÿ:·âcÿV~ùÿÛ/úòó›ÏÅàî.4¿Áî˜ò£sŸd‘Û[./Ôú-t±V¿Ñdûô=¿ºÌ²û-þvÚâ~u~ÊÖÚ}¯…·üH¾ãÁ'è^£òki™l–[™Üc«µ2ÇZÿD¿ïqöÖ×*Ë­·ùÛk�ùÕû+Z~WÁÜ~#ï ± “ô'Ry1µÌ¶K-ŽÌî3Ö|c­¢I÷¸ûck•eÖÓùÛk�ùÕüµ§åb�÷âðøÆÔmóuºþ¯ó?”áâIm�r Wu>£gmÑG%f‡“÷÷ßG—ß[+Lœ,{ýÖ×ÒÚÛíºº­ÌLṽ€x[5r×íy�xÙLhn¤‘Ý%ùœKÖ—&—EK­¶Û6pûn­.­Õ¥+eu£–þ&Œ+cɃ‚xƒ•4·[oÜöœ·¹bÆŽ+é%•¾¹:-?\e]‘mÑRëiK61V—V—zÖ”­µÖž[ø˜pí‹& â&L³Ým¿sÙòÞ䊡¾—Ù[ë“£ÓõÖEù6Ý.¶”·ciuiw­iJÛ]`í�ÆQК|üXú3ÂNÞì]eô¯ßs;cµxgKçã×ÛëoÝuÜ?�÷Þ>e+Ù_~T”û~ßÈç÷kþ$¿‘{‹÷. gIt}–d],[N ×rr]ü�WÖ–âæÏÚûÎÁãÓ[m==o‡Y�}kû´§ØÑ^ÐüB¾zó|Y0xD=9Ó6S"éqö¼7�ÍÈù ÿK�—‘Ù»®qÇr)Oݾ=\WýŒöâóË›c_…ÂaéÞšŽ™IχpI¹ úéq²ò;3sÍøôþ”ü·Ç«‚ê×ùŽ`w¯âíùç¿Â­WHõ/ŽÝ ÛÅl}ž.‡”ó¾OÕçäÛm“äh¸TüG�Ý,v[miƒ…�o¶ÚSÓÒ”£–=×å§“>FÏ$�áÞ�™Ù8—ç_²‡CȹVÎN#¯Î’Ûl¿#MÂñfÆâZ+î²ÊR´Ã‚ž”§Øå§tùcä¿‘sÉ'ww—fvF%Ù²laÑr.U³—‰kóe¥¶É>›†cM�Äôw_m–ҴÂßKiOO²Žy;ò}ò æE7žFùiÜ��Æ÷øº¼M×^bòOà/RllÒÝõ5Óäôç]cñ.«¿>>ì¯ÐôÉ–JRù$ºêR´Öö¼µí¡à ®ð»ÃÞÚó�¾øŸCõ&¶K³öò~’弯#"~=×|x,ßsnO<4¶Ì}v¶Ì‹"‚;¯Žüìùñðá­gÈŽÚí‡þ&v‡š=áÆ:O«°¯·/g}v\¯•dbddè¸ Ú7\Ã�É l¶<<g²,x«$wggM,wRY¬l÷ˆ^'v‡™Ý߯zS«°oûÞÎÿÒ<«”äbÏ‘¢à<7h,ÝrþC$>ËcÂÁ¶{"‚*ßù¹ÓA‹~¬Ö:ñ™ñ¿Ý'^Kqþ‡êª8¾ݹrvþËY—±ãGבÏ[s÷¹Øø÷ãþšå¥~ÒÛ>5ûm�ÖÙ$øxQfì0ì¾ñGÅΧðߢøOAtÞ›ôoâU»7g•Häßó.O›lrrmÊó£²Êìy"Í·êKu)l8ÐÛ.4pâcãÁ‰^1øÛÖ%ô· èÞ¤ÔݯâÜO ¿yØeÖ9·¼³‘f{eßsO�qÓ?�r êVYn¶Û ‚:G��8°AvxËãwXø›Òü;£º—U]âxuûÎÃ+é˼å|‡3Û.û˜r|Øì�ïûýþm+,·RÛ!‚:G��8°A vÑø³ã7SxsãÿWøÓÑú\�Yõ?�A¡‹>xr÷;L™ò²v»þOÈó±ñpq¶£—r-†^Ïe0q_2|bíßy]Øø°öÈÇã»Éá¾oà¯8ÕKçƒò»-†ë2o³CÊp1gž(ï²¹X¶Ë�u}“_Jëw—~8qŸ-|rí^�åC‹=ãSãh÷3E$ßÁžc­–-¿ åG�äKn‡“`âäK —Ù÷¬{$‚êû%¾•×.|rã>YøéÚ�Ê.ƒyÆçÆÑîfŠI¿ƒ\Ç[$[nÉì²âÈ’Ý%ÁÆÈš+/³ï8öIÕöKu+­Þ`xÃ×þgøÇÝ~.öt~œ?¹¸.ÏŠdl¬Å³;+Œo)t^Î5xrÍ;¾̵ºýÖ’ÝHnÍÀŠ’R¶{©Z¸;+®y—Pö6êÎÃÒdqÎuל£wùn�&øe“Y¿ãÛ õ›<[r1¤›2 r±®¬SÃ|�Om’;î²ën­k}�×Ü¿©¹ï1ë.¦Ÿ�ó^É7O”i².Š[õû½tÚüø,È‚ùq³1þ¼ºá¾øgжÉ×Yu·V¶žÅëþ]Õó˜õŸ>ÓÏÇù¯ä›~'Ê4ÙG$šýÖ�:m~|Oòceãýx+tSÅ}ðÏm’;®²ën­8¾Côgx·Þ=£ãÏrñüž3Ù}EÌvü3•kfÇσ|�lßê-öŽ]–»'iÄùVªH6šm�!² ž§3.t3Çu|CÆvNFnñdâeâM&>V.LÛ,ùÝd°O –Òë/¶´ºÛ©JÒ¾¯Ñ‰—•�•��ƒ“‘…›‡‘Vf$ÒcåbecÉl¸ù8ÙÝd°dA-”ºËí­.¶êR´­+GèÄËÊÀÊÆÎÁÉÈÂÍÞ¬LÌI¤ÇÊÅÉÇ’Ù`ÉÆÈ†ë%‚x%²—Y}µ¥ÖÝJV•¥_wŒrŽMÂ9&‡˜ðÎE½â<¿Šî5܇Œr®1·Øh9'ßéòâÏÔï4;ÍVF&ÏO¸ÕçAdØÙ8òÇ4Ùmö]mÔ¥]¯ñkçïäÇ,}å\ÇOä—ÂŽÜk4Ýá��ºåx˜×fÙ—“.¿³µ9º¾o—³¾?|1I»ÉÝããŶÌK#¥½‹ñ§ç;În€ƒ_¡å|§Qä7 ²Ìjj»Ž ݧ,ÆÄ®U2r/Àì­^^/ËÙImnŽÉ·R;½¶Ãém”·°¾5|äyÇÐP`hyO)Ôù Ã0ì³ÝWqA›´åXصʦD÷àvF³/—elo·Ý’îfÜà w{m‡ÒÛ=²Ið¿ñP|†xÛ‡Çø{cpï2ºçO\Ø’ø¸·˜½C­¾Ïe+NÇØmún—Öû&’ÚEoj85“ÝéÔô²·V—VÛkù×ÙKº7ן*_ŸKÿƒ^Zu6»éúR´ìžÃ©.º·[5Ô¤vö¶·…Ý5} »ùÊ]éZÛOË}”»£{ò�ñçÙÞïàß–=Q®­žž´ì žÃ©®º·[5Ô¤vö¦·†VZúA_²ß_¶¶Óòße.ë7Y|¾ü]vçÔò~!çÇ‹8˜[ºÅn¿YÏ»ƒ‰u'/¾Y¤úPÁ•À{_a¹λ&Y>ËbÉ×E%kZ~oÛO]ÀáAô'fV;zß»ú‡°nšl|x©Â;+†rºË‘—t–bÁ4;¬úß6MÐßHí§­×ÖÊúR¾•m‡îþ—ì[c¿¯»{«ùÕ³IPÝùÿäöË.Md¦4q×I¶Î¥òdV+½”§­oö×Ó×Ò­­áýÝÒý‡HëÀ;{«ùÍ&’¡¯çüS“RYr«%1£Žº]¶o¾LŠÅ},¥=k}m¯§¯¥]ãü“ŽòÝV&ûŠïô¼›G°†ÌŒ ÏÚàîuYØòSÖ9ñ6:éòpòa¾Ÿm.²û­¯îU—™A”hŒ9§vôÇ[Ò{»·:Ã�[‹=˜¹7sN}Å8µ1òdÅ“:8ú«ÜžUæOEæÛuöY\~Ì1ûg>;¯úþ”ÈÖuT\Ïc�é÷{½ßR+}¾¶û½=ö{µcŸ|š|õ®/Þù'–ý%™gº–]æ8}�°ŽúýoÍ—UÖ”å»8koлÝÚÖÚWÓßo®­óÛï\“Ë^”Ë·ÝK.ƒ„ó >ÍÏŽêýoÍ—YÖÔå›(koл×ß=µ­¾¾žû}tœü¡ünõ½»»y—ž>"êóøä’úÐÁäWny^D­fÄ»‡èù6˔˛§¥`�ù}~Ïo­\ãîıàg³k‡Õ\{¹{ão�ߢ3t¼S€p�œô­¾–æn¹þv§˜êñï¶µ­/·ŽdßëOJÇO_V€vïâðƒÛ°ÄëM7m÷nÒ(o®¯3GÅ"áC7"ÚÛì�7mÏst¼«]ô­¢Y¡Éº•§Ûc@»sñ øEÂ-Øbu®›¶»³iW×W—¤âÐðŽ#�=µ·ÛffÛžfé¹Vº éZú_f‡&êVŸmŸºåouþ*o‰Ž®ÃÕÍÀ9—uy!—²ûå³auLò.9úH"÷ã_¿ÉïùºJ?»gK_e·ki³’ÏJÖøéOO^3ùø˜üÇìxöŽƒà=iã–—&èk‡»»îÛ쌧möÍ{ÎYƒ¯àãåûéZÛwºhëm=“Sí­Ü�ï¯ÄYå�?‹;WÑÜ ®ºS“l_vÜÍý©Ø7ÙKé/ÐÜr\gº Š]ë[dãRIeiOl¿—×’=ñø‰|°çÑgjúC�õßAjriÝ·2ÅivÖRûdú;ŽK…®à÷C‘î¥klœjI,­´öËù}xåãò³Ÿàgñÿçžr�¿%É×àß‘6WèÍE»<©àÒê"ÈȾøðñ,‡*Ý_dvú¸…Û]ÝÜ=óÉkÌ{£³y¿hrZG,û^mÈöœ‚mvÙåÝ­ÓŰțK©³"k®³Èq£­ÕöGmGíŽííþ÷䵿Íٜ۳¹%#’ }¯5ä{=üÚü9g“*ºíDyùcéµVO5×Y‰‰d8ÑÖêû,¢3~Oy�åW™üœçÊNùì~éÞcæí3ôøÜ»}5üW‰Kº¦ 6ðp.®·_Áº÷]²ý�Y±4zí~,—CekkJU‰Øµ‹ÚÈè§1øÜîºøþâ~~sí–7âý�ØÚn#Á:ßg¨Ì�”r!¾×í2tý•~]ù1ǬÒîrt¹pÇ›êfàý,诮6DIÐUñåÚœÁ7çG8ÚAÆ8ÿ7çš~5ºïc¦Ì�‘︎÷:ýGaß�&TVkµ;Œ�\÷`Á&5nÌÀú9±ÉX2!­ûûÊ~=»S‚x)Æüåæû<~1ǹ·;Ôq®×› >d|�{Ä÷¸Ù÷ê{LÙ2b³[©Ûäjç® cVìÌ¥™•ƒ"ßÜnIðyÛ�}ñŸò­ÚŸ‹ÁïËËá¾ã®W^m&ßïú«±;‡õßçÛ¾w›É4ñq¼þI7&¿u�®‡I³ÆÊãÕÂÊûý²æß�‰Î·?šáÈ`ÿ…;"—`ù׉í­.ƒ/ÆœŠßëOK©•}Åm´§å¥l®k_þY+ßÑK°üÕÅöÖ•‡'ÇlŠßëö]L˜»Â:[íôû+gÝ}}}~ßwú [~lŠ]…æ–/¶´¬9^<äVÿ_²êdÅÝÑÒßo§Ù[>éZúþï»ýõ?w0ƒ7¬|öàáKOç�Ì%ØÖk.ƒ*qÇûOK�…²’G.¾N¼–Kï­Õ¥ôɶ”¥+e}eâ•ZTI®+˜ùøýªþI~öt�ê­ùÉý¦ÞB~÷tçêG®ÐùÄý¦^@þöôïêS¯U„~*?ÚÝÎ?¼—Iÿc¹n79(äº8ÀŸ|0þÓï¾ëûåî“ü@~ÒO¯>Cú½æ‘|C~Òÿ¯MÿöË�Ìü6_¶§ÃøÅÉ?½V],AXlµ�ª~wÿï2¿’Ÿ�ÿª.^Ö?6?‰Ÿ–ÿÉ�¾ÿU|­¬Þj-?“7|~«9SyQüX|ŽþðÝ¿ú½ä*¬Õ™«@R�›ßánþ+>Gÿü/Õ×L»ð×üþÿQþ¯xšd†Óø²wç÷÷‡õÅV?~ßÙ§ÞË›²ÿP^3$ú‘ÒF©Y?ÍÖÛ}·Y}¶ßeöÖÛ­º”ºÛ­ºž—[uµõ¥ÖÝJúV•ü¯ùu´º•¶êRën¥mºÛ©JÒêVž•¥i_²´­òëmºÚÛu)u·R¶ÝmÔ¥mºÚÓÒ´­+öV• ¯çç'âæ ;v^ùéý-iãur,ɰõºìÉӽ‰°¦FÏcÀ%³:acq=Í#Ÿ3Ž]m"¤XöO�Xé÷(çÊ‚÷ÍƼþ#öœ½åÔÚ[lñ÷·Ù2Á­Öaý N¤çùÔ›aŸÂ$‡ÊaâñMײl½ÑÒ;!ŠÉ°+´Ä†l¨4|ÍümOâOiKÞO¦¥¾9vîû*X5ÚÜ?£‹ÔÜû;ëì3øL±cÇL<^+¹öM— ºÊGl0Ù6 c¶˜�Í•\âLøhÍñ#µw>oxÛÁlÅñG¸¹ 3öoãTÈŸ¡{›“fÏ~m÷iëd—ñÞ§ìí¬Ÿ_S,7Ý«Ôo2%ÓÛf»m&GÜ9pýÐÑ¿¿ÆŽ__èaóçºxþ^0æšlí_Ž|wq…f<úN½Â¦.ß¶®†\ì|þ}¯ž\ 5Õ¶] “äÛ÷Œ}¦=ñËëà/ã»#‚h!ó‡·´Y|»™ê2õÝ¡Û`Ûú> ¹Äû¾Ó´}™¹�góÌ äÃÔÝìƒþ¡_6E·OΣ—�À�ÇŽGÐÃæ÷nèò0ùo2ÔfkºCµÂ¶ôœs‰÷}§gÖ<�\¸3ùÖÒaênöCÿPï›"ÚÏΣ±ð»üKÓÇÞ¥‡ä'½øæöçïÎ+^?év8ْ캿¢w‘Ç•2–,»aÁÂåýÕ°äcËS͇Å,ǤY–~˜ÙàÇ+$›’gK´ò÷zM7&Òíøç"Õk·¼�jöMî“o‡�±ÔîtÛ\Ip6z­ž¿.9qsµÛ )ï†he¶èåŽû­º•¥kGÎÛê5\ƒS´Ðïu¸�&ï]›¨Üéö˜�gë6º­–4¸{nÇ*Éq³p3±&¾)¢’Û¬’;«mÔ­+Z>vßQªä�¦‡{­ÁÜé7zìÝFçQ´Åƒ;YµÕl±¥ÃØkv896K�™ƒ�‰5ñM–Ýd‘Ý[n¥iZÑü§‚˜fÆÉ†,Œ|ˆ¤‚x'ŽÉažl¬rÃ4RRë$ŠK.­·[u+JÒ¾•V»òÇñë¾øùòoqĵ˜Û ÞŒìi6\Ç¢yNLYÙ/¿*;¶\cŸ,¹6äò~¹ËÍ�*륬Ùxwáçßd?}¤6WŸò‹ànëÁ#6œgY�›—Ò�‡&Ë–tŸ"žÜ™)wûÌUÙð�ŽnD¹5Éä}}•›E÷K|¹xrbfßlUËú1×·ò�à†ëÁO"¶œg[�›—Ò݃&Ç•ô§"žÜ‰-»�W&:ì¸NÃ6yrk‘ȸVlxyÝ-Òåa߉›}±W/éGT÷ÏÅÛã7ʼÌÎ¥¾ß»ëa½å¾=ìãÚfînâu“'—tÇ ÈÚ].ê§^fíb¦®|¹³®ÙñìŒ)¯ÎÉØG³�—®l9¸á¨èÙ¸ÛGm¶Y´ØÙe–ÒË,³7&Ûl¶ÚR–Ûm´–”¶ÛiOJRŸe(ýömv‘Ùlqì³ì²Ëm²Ë,ÌȶË,¶”¶Ûm¶Ù)Km¶”ô¥)öR�¡fÛk–ÇÏae–[m–YfnM¶Ye´¥¶Ûm¶ÉJ[m´§¥)O²”f,"<€Õ`ajõ}çÜZÝf·]®ÀìÞk‡���‡ ˜ø˜XX˜û¸ññq1qã¶8ãŽÛl²Ëim´¥)J?ÓÍ‘%ÓdM,ó_éï–i/–Ký¶ÒË}×ß[®»Ûm´¥=köR�É,ÓdIt³Ë$Òßéî–[î’K½¶ÒÛ}×ß[®»Ûm´¥=köR�Ç4ÓdItÙË<·ú{åšK¥’ïm´¶ßu÷Öë®öÛm)OZý”£r>MÉ9†ç3‘òîA»å<‡c÷ÒîGµÏÝîs¾é‹'ß6›<Œ¬ì¯ºàâÅ ~ûîöE¶SÒÛiJ'ó7Ä)JÖ´¥)ZÖµô¥)öÖµ¯ä¥)üЫ,¾[ìŠ+/’I/¶Èã²Úß}÷ßZ[e–Ym+u×Ýu})J}µªuÿÙ2pÌ_.|�ãbyÙ:N¶â›œ_vÛ¦ºçw‹ý/?{}4ý‹ÏuóSïqû~ùªÔ_L).†|­–%“ZøFøÇŸÇ�å/zñ«q;ß±t~ÎÆ7Õý)Ô]}¸ƒÝ|ÙØ“Sþ¤ö7Â’ÚæYu¿|Õë.· úÁ>FÇ“PøKøËŸÇ.�åyñËq;×±4t·�q�¾5Ju'ÜAî¾lìY©ÿR¹÷6Â’ÚåÇ[~÷«Ö]nõ†l�Ž5,Çü:_ ÑxÓVyCß¼{;Ìúâ°E/ÞáW# ºŸe6&Û_ן£g¶™¸ý‡Ê¤ÄÆØr‰r©Ø7Ç�©�ü<ùö¾ÚaÒ;®ºûq¡æ}™¤ºÙéu–ÒÉ~ù§–´¥+u=•¶¾¾µ­)ÈïÄEªÉ×ùåÆòç¬U‹{ã·_mp©×]}¸ÐóÊÒ_Ié[-¥’ýóO-iJVê{+m}}kZR=Œ ö–tòëO×ï“(ö82á)  3>wy¼ƒã+ÃÜüúCIàêÛ´–} .ŽÏ¹q®QÈx溷[u÷Ö³W_ªŠ²]ëétžë©JR¾”±ƒâ“q—¼øìñ776�Òh:·Oe ²èìû§Üî4ºÛ¯¾µšìdu’¾¾—Iî­)JWÒ–)|Síò÷Þ'ææÒ*MWãê,ú6VË>éÇ÷[�km×ßZË\lu’¾¾—IëZR”¯¥.>ø±È¿+ã3ãÒY)e.·Â_1éK)ZSÙ‡Ò|'*×ÝuÕ÷Ý­ß¹[½})J}Ž–:è3|€õåÇñQòsù=wGêß’°“Å·ÈOïÛØý„<šþ-ÞAÿy×þÀ÷ï—¼ÿYw½{éI•F+V,£Œ3o·ýÌüÅþ¾º‡ú�ÎRñü3¿Ü¿ÊÏë÷­?±îP—Gá¤þæ>U_�iý�ò…�_ƒßövùü´¹Ÿê7 žóçëâ“3»´›?7¼|Ðß™Û<3A�åÁõXqÝ—ØüŽ`},N{§Ž mÈÍæüO‹n>^=i,›=$RÙ6pæû?œ�ŒLÎèÓl<Æèm s;Wˆi"³¹xf¯Êæö/ Ð`Ò,^i©Ž m›?šð­N-°dAu$—e¦†;!­³`Ã_²ùÃøÈËîm6ÃÌNˆÑW/´ø†’+{“†êñl®gað½ "Åæz¨á¶Ù³¹§ Õc[D¤’ì´ðÇl5¶l Ëú¿ˆÿáV_28_š>+õõvžZu†Ž8û3†q ®ä^Fõv‹ȱ酦Ķ¿Â®áë}n?¦¢Øl®ã}¥Žºˆë�‘‹¢Áލm¡Ê®°1xÅÜ~_wèÞ�âórniɦ¬’Ë%dÆÐñm4�Ù·åü¿od3Ç£âú8ç¶ìŒ‹­¾ûï¾8 Žl™ ‚Lëã�ý·åwmñ¾—é~7'"æŠK¥šin“CÆ48ÒCfÛ–òݵ�Ïf—ŒélžÊÏ=l¾I$¾8 Žl© ‚\éã�Ž=³åglñ¾˜éŽ7/!åü†ZË4ÒÖL}ÑcÉ ›^YË6¶C=š^5¥²{k<õ¶ù/’øà‚9²f‚ 6ÛÂO <€ùòŠxçãŸý?Ë÷þí–û}²»' …u· ÂÉÄÇßv/bï±ñ3@pýßb¶ûíŠl¼Ü¹ ÁÁƒ+a•‹‹=�?_ÝKñßÑøýkÁi$眎ü-Ïnv®f·}ØŸøâŽßoÕÈÖpÞ=÷‰¢Ój¾´±áG4²Ýt™Y9YXà7�ýYàOMA×\"‘òkÈ.ÃÛö§gækñð÷¼÷’cÁ|qSÒÊÍ6³‰h~ñ4Z}_Ö–<(¥–K®—+'+"{|ðK«| é¸:ó„Ò.AÍ7÷aí»O³²õøø›ÎyÉ ‚øâ·ÒÊÍ6³‰è~ñ4Z�_Ö–<8å–K®—+#+"{\¾5~7ú/ã#Ç-'Eu?ÂE›÷}ßowÛSƒ¬æ¿Î¾œ¿xßn"Ä¿'ôOÔýæL]–ÜŒ˜´úïHîŸ/.LÌì½ÿo+xÝEñümäsM<ûéÍù|Ÿ‰jõú?#ôz¬K&ÈÜp­V4xo¶+4·.|¾‡ ZÍÅþ“×ô-1'­ ÇÖäÉ|a¾~þ=¦æ:?øqõ&Š\žMÄõ˜Z ´Ú¬+eÉÝpÝfg Æ=~ÚúÛ=BÓzÖ }l÷ßߟo�™¹†“þM£—'’ñMfŸÈ 6¯Ùr7<;Y�ƒ³î² [“6_ Æ=~ÚúÛ=CSzÖ }l÷ßïÅ#ñ5oyud¿"½ÆõPöÇEqlˆ¼“ÒêuÔÝöŸJê#Å®¿°ï›]4˜�¢ðaÈ¿>lœ;gËâ2ItÛ !Ðkð¦†2"Œ«Ð~¬|üì:]n&f^-·Ö•¾Ü|‰ ¥õ¥=)[©öÒêÒŸÍ~œ|ÌÌZ]L\¼œj_ZVúcÏ,4¾¶úûku#ºßuië_OWêÇÍÍÄ¥ÔÅËÊÆ¥õ¥o¦>D°ÒúÛëí­ÔŽû}Õ§¯Ùê÷œ?´û;¯ ÍÆà�Ï8>6ÊXgØãðþ_È8Ì ñì¾8&Í‹K°Â�*X#’ëlºJ][iuiOJUÿr3ó²í¶Ì¼Ü¼«,»Ým¹3Mm·zz{­¶Kî¥.ô¯åÙósr©m¹YyY6Ù_u¶Ï‘,Ô¶µ§¥km$¾êRµ¡‘�›—m¶åfedÛe}ÖÛ‘‘4ÖÛuiéZÛI/º”­iüÇôåý±Ú}ƒ‡‹®ç½—Øß_ƒ“\Ü,_̹%ÃÃ̬WÁ\¼\mÎË6 |šÁ%Ö}K-¥þË«O_JÕù•ùXüÛ†‹��žýÅw`vv§c�â·Qnq$çÙ¶É‘­þÙ<ª8cØëz£E²ŽÛe¬yIO ›ûgÂÕKdvÉ�“�‰5½�ø„øÕÎó{µîçý‘­ÎÇñŸªw8—s\‹n› ½‹ÊaŠ-–Xióì¶—ÓXe‡#}6=ÔŸY-‘Y|æãOb>"¾6süÛíjóîÇÖæãøÓÕ[Œ[ù®M.› ûbrˆb‹c�Ö:|ë-¤ŸBh¤‡#{4Rl]d¶EeðO™�5’`ü< ~~vÔPwÞ§?ú+˜áÇ‘Çv¸Vfßä/gê,ÄÜcu¼1Iƒý­øí“âår̼š_nlSA¨Å†[²ó²õ2FüF¼jÜoŒØ0¸æ£CÃû§¨î®¯[¢Ðc`rn9®ƒ A66VË(a³Û·ÛJ[K-¯¤…>~¸ý øçŸE«ÆÄÓqnØê«îÃ×Ã�…�¦ÒAnçAƒ._F0ñò¶¸ñÅ ž‘Û}´¥´¶ÚÖ’ ùõÐRŽ©qtZ¼l=7펫º¸zø1°°4ÚX,ÜèpbÅÆ�Ã�…�“Ÿ��QYé·ÛJ[Ki_Izþ%˜$—á{Ë«ã·Ýf6�3M_[iìŠï':o—zV´­Þ³Oe¾”õ¯Ûëù)ZÒ/ Ôª¨-O­»Êƒšy©Ç,�¸;^/Ñ›¼‰n²JåY• Ûv–pßIi ¸òÃÉg¬´ºË®ºë#öÝm)u.”§á•ÚäCʼÃÑÛd5ÄØñþ‘ÚÏ%Öß\‹r4ÛÑÃŲ+é%"¶"ßMY)[.º·[g¥m¥.¥ÒŒü3›lˆyW˜;l‚¸›=Ò›iäºÙ+‘nF—eÙØx¶E}$¤VÁ$[é«%+e×Vëlô­´¥Ôºs‚÷ÿrKÿ¿üi“$KQ,ÄæÌ|ü~Õ$¿{:Gõ Öˆ|äþÓo!?{ºsõ#×hüâ~Ó/ {zwõ)תÂ?ínçÞK¤ÿ±Ü·œ”r]`O¾i÷ˆ?ßuý€r÷Iþ ?i'Š¿×Ÿ!ý^óH¾!¿iŠÿצÿûåÎæ~/ÛSá‡üb¿äŸÞ«.– ¬6ZÈÕ?;ÿˆ÷™_ÉOÈÕ/k›ÄÏËäÇߪ¾VÖo5?‰¿–ŸÉ›¾?Uœ©�¼¨þ,>Gxnßý^òVjÌÕ )HMïð·Ÿ#ÿ¿þê댦]øk¿‹~¨ÿW¼M2?ÃiüY;óûûÃú¿â«¿ïìÓïåÍÙ¨/’}Hé#T¬€Û¸:‡®{ó¬y§NößÖó^¹ì $úSÆö¶]v>n·Ç‘�‘�¹Ü“§¹žVÏ?§»{!­ÛÍ6$�ɑǹq"ƒšñ¨³!‹6Ëc†«n³* -Š_§~ÿ#_]�à?pËÇ6Îä}A̲¶yý?سY ×o4Ø’C~Fƒ�×(1°ù§‹2³¬¶8¢É¶ë2`²Ø¥úqÕ{óEðÝÚ�Íf¦]Çaø‘Ú{ŒútopeGÛMn\pß°Êê>Ù¦¿OÙ¼{ ÛäÆÊŽ5¼§W Û až®¯QÍG;ðq@Ýß…‰í�œ�“s÷6£a®ñS¬7X²láš ¼+»³–aK÷ˆú÷GŸKb¥¼_èm»’fÁ'ײ ,ÂÇ­™7eav×á÷âûcæ_`ÁÜ]½ªÍÀñ�®7ògA‘V-ýËÊp¥¬Öp]6]lŽÊqm|�Ûw!ÍŠÿ«H®³Û>D™8=¯ø‚øÂØy“Ø0÷nê³p66<6Ù ðCe,²Ë)Km¶”¥)JQ:Üll|pt'%é>ÀºíNNMmÝð>o‹…~ׯùÞ¿"=''ÁÅš\zgâÛL™1¶_Z çk²'‚Ù ’û'‹R¼×ðû®|ÝènKÒ½�uÚ¬œšÓwÁ9¶.9Û^ΰ1ò#Òòl,YdǦ~%´È“a…õ ûöºy ¶h$¾9âÓ¯=<%ê��;Æ>å×cWOËð~ÿÄ9}5�lwÝ]Øú¨rkû/‰]$Øsãï8ÞfEöËY8ÔÙêò2õ¹×7&9+GòGÇ>ÖñC¹y§Ew?¿�󞱿'éýytûÝdµºýG)ã ññk¶ãƒ Û‘‡“HìºèîöÉdrÙ$vWsä7�½¡âïnrî”íý Ú.gÄ3®Çš±Vi´ûÝ\µºíW'ã; ±ñkµã{ìJR|L�§õ²µ²["šÉ"²¼_!<ìÿ;o—t·ohnÑs.!Ÿv<Õ‹ëK¨Þë%­×j¹7Ï›»^7½Ä¥'ÄÈúqÝ[.­’YÖI•"ù±á_}øä/0ñ·È®/úšñŸ£²ÒîõÕËËá�‘Â6SåÃÇ;#®wÙXx?Â.Ⱦã5‘Mô¢ÉÃÍÆÉ×çA‹±ÂÌÄÇÁŒ(ÂÍNK/àŸáË+”æð¯9ü¦ã·cñ,)q97�=M½×ËO,Άøò´�ÁÌ0s#³èñ;í·'ŽaÝeoÛËôö7ÖÌñi²”oÂ�ÄÔü‡'‡y©ä¦‚°ñ¼[ñ¹Aõ†ïXò9\RY‘¦ížU‡™ŸK�ã]m¹<{ë.®Îÿ§±­mijïÒ†øWø�Ÿ�äðÿ4<“ÐÖ7‹&7"èn°Ý`KG!ˆK24ý¯Ê°ó#³éñ¼{­·#�âÝeÕÙ_ôöVÜK1¾ý<¯Ã{ðS‹Å1zûä{Ì>'_.ËŠWâÇKr½.TÀo§•õ8ÿ~s]NßçæSÇ3xn5ð˪ q÷–]]•uÒk&Z–ÚZI³ Áø¡õWÃæ¯Fnëlt�aâæ‹Umô¾êËuú~ØíŒ¹-¾:ÓÛlvÛ½¶¶ÖŸmÕ­Þ¿’ˆ]~$½L�ù}Ò»ÚÛÒÙxݧÔÙu/º²ÖM/gönd¶ßiì²;mßÙíºŸmÕ­Ô¯ó´Cñ"jd‡Ë¾–ÞVØþ–ÇÇ F¦Ë©}Õ–²i{7²ó$¶øëOe±ÛnúÏmÔûn­n¥%üþ2>·‡W䇆=¿n.™ç¤{ ­åÍ�'.ýŽD=UÏ0¹<¹XwÓî0aaßܲ_�,Ѧ¾y­“óc‰dwâCTd7ÁM2þ)öí ?²¿ùM~‡æÓ÷)éE�¿ Ù?{øÉñŠ_K©ìÄí|oϯ­Ô]éÙØ~´¯þS_¡ù´ýÊzQmGÀ5ƒŸ|=ø;¼Ç‹6ðzï–p«¬ØId³Ö~¶í~À묙cºÉ%¶˜SäñkïÆ·Ö•³èí­-­+m:äê{©nÄ5ëË�â£äçòzî�Õ¿%`ÿ&ÿ‹o�ŸÞ?¶?°-ûy5ü[¼ƒþò¯ý�ïß/yþ²î?zöÒ“*ŒV¬YGfß…oû™ù‹ý}uõœ¥ãøg¹•Ÿ×ïZcÜ¡.�ÃIýÌ|ªþ¿:Óûå ¿¿ìíòùis?ÔoA%„“ºN c!aóð¹‘Ö™|¿Ìïx�²uvMÙ|�¼úo�àÝõ:Ë&êß•¸ì¾ «Æ¥Þþ¶É¾·dn5�YOàÝõ¿* ?DV[5ù˜ø…Éëì¾Wå÷‹^Ù:ß#ï\ƒºú—�aV’õÞEk~NÛ²8^·•¤�{‘ZÝ>ã_ ”¯¿ß— µÕ]5º˜…üÉüEduþ_*òïÅ®/l�s‘\­ÿtõ7­%ëÌŠûòvÝ�Ã5¸Ô­$ëü‹«tû}|6R¼~ÿ~T6×WtÖê Ãø�~6¹;^sòáo ‡/.îWåIqMd”ÙY²¤™9üƒ¾x>§·ÇŸ|wÖ~U®Æ†É£šÉ6öÛ5&Ø_U‘”FmÐËx›âOwù§Üz’èŽ-&ÿ’íknfëq—õñxŸâñecãm9¯8ÝÅ�“n“Œê+•e/¾–K““=ñââC‘—<òìW‹¾,w7˜=³¥éî’ã2oyƶeîvùu›‹pŽ9fLû_Í71Á“nŸ�jþ½¾ë©d¹93]f6,9SC›âÿ‹]Ëå÷liz¥8Ì›ÎA±­™[�¾]fÅâÜ'ŽY“>×s=Ìpd[§ãúϯoºêY.NL·Y�‹FT°Á&óxñãä§ÉvatÏŽÜJüûp¯Õçv7bíé&_õ?ØgW¼Ÿšn½µ¶?u±Ov»“m6—cËf$Ö9ke‹Ÿ3ñÛÓöpN|£±9,xYݱÛÛt8|‡Ÿnñ¬¿èÁ ›.íÒÝ<–jõ1Ï,xÖ_|²ß>TÓäK?/üê/úžÎÁl³’sÎCfwiv¶Ã_ 盼häú1Ç ³eÝ¢âZk§’Í^ª9¥�Ëï’KçÊ›#&ióxà?QøÕpŽ œ��ò0ó{CµvboùÖïÉ>”QÃIr®Ññ-5ÓÉf³UÒG�e÷É%óåM‘‘5¦¿ßÞ?ü_øÿ‡Ó}7‡ú—ïÿFî{«º·:Ül.kÜ|× xlÚm,†|ßÐ?A÷܈8÷ƒ"lM.$ÒÖ²ål2¶;±½NŠ€?ÓW¬Þk6:MÞ»q¦Ü`fjöú�¦>ÃY´Öl1äÄÏ×lp2ã›;;kâšlº9cº¶ÝJÒµ£ñìuÚý¾¿;S¶ÁÃÚj¶˜yZíž³c‹n¿c¯Í‚ülÌì<›%ÆËÃËÆ–èåŠKn²K.­·R´­hü{v¿q¯ÏÔí°0öš­¦V»g¬ØâÁ›¯Øëó`¿7? &Éq²ðòñ¥º9b’Û¬’Ë«mÔ­+Z?åiK©[n¥+JÒ´­+OZV•û+JÒ¿eiZ+Êù™ø¯ÜxÛ5ì^°Ôì³üTímÞmü>Ûv*uw$šÛ³òz¯’íg¶{­ŽÈ¾¬¼'*k²6è$²ûæÈÃÊ–èüº|fm|"ìû»­5› ÿ;;s™˶Üí‡ö±äSÒìÜŽ´ä;9i5~íK+$ºœ™k‘›� ‘ItÙ“Í$¾[þ3ö¾öuֺ݅͆wŒ½™¸Ë¿ˆåÛnnÃûYòévnGZò”¶Íu1égÔ“C““-ÓæàC|wÝ.F$óIY×âøLÜøÙû?)|uã²l|1í®G>Ff—K­É­Þ3óÝÆTU¿ƒï,džì(:·”ls+^'±¶èíž·érãŠX5Ù{Ž%¸Þã¢2 “üj|jöçȧnÙÇ8å™|K§8ŽfGnöÞNÓkxÖ¶k©-º ²}87|ëw�eÔÃÂ¥ÞØlõÈÈ­�ÙùÝøíøîí/>ûJݎܾ)Ô]ù£÷äRí�ŽmiµÿRßn›³x%×Ío¾”®DuÞÒ‘WÛ_Kn“í§¯¥Ò8ü5Ûk!ò‹¾ôU¿"’lº›k#¶¿ê[¬ÒöÃ’ù©ï¥+‘Ûûiæ×ón“í§äºF_†×kd>PwÎŽ·äRM�BSkm–×ýKušnÂâ8r_5=ôÿTYvúÚGùµüÛ¯ûiù+6_Á™Êµø}‡çç’ìºm9 ñã•aÙež¸7kønó·õ+²dú”öeÙ‘Îñ) ¾Ë½Ö]-}möú]8´Ì&O,\ÇÏÇíWòK÷³¤PÝh€ÎOí6ò÷»§?R=v€Î'í2ò÷·§R�z¬#ñQþÖîqýäºOûËq¹ÉG%ÑÆtûá‡öŸxƒýð7_Ø/tŸâö’x«ýyòÕï0t‹âö�x¯ýzoÿ°\îgá²ýµ>Æ+þIýê²éb Ãe¬€ Só¿ø�y•ü”ü‡ýQrö±ù±üLü·þL}÷ú«åmfóSø›ùiü™»ãõYʘÊ�âÃäw÷†íÿÕï!Uf¬ÍZ”€Þÿ wñYò?ûÿá~®¸Êe߆»ø°wç÷ú�õ{ÄÓ#ü6ŸÅ“¿?¿¼?«þ*±ûð~þÍ>ðþ\Ý—ú‚ñ™'ÔŽ’5JȯžPx¿Ó^at×(è¾ôâñò^Éc¶||ˆ.‹‘q.E‹DzniÂ÷2cäß¡åšòo®>E,’)b’\l˜²0ò21åÁ~GxãÔ¾Võ/%é~èãQr.È¢¶X¥Šèñ·¼g{�ÖjyoÛ_ ÷éy>–ùï»"Û/²û/’ ã›ià—ùã—SyWÔÜ“¦;Ÿ�Åȸ"ŠÙb–+£ÆÞq�æ4sYªåœOk|3ߥäÚ[ç¾è'¶Ëì¾Ëä‚xæÆšx%Â^FxçÓ^Xô×8è.ýàú®ÁêîÁÕ_«äieÖßöÝlúíÖ—cÑçèy.‡>8òõÛI"ËÂËŠÉb¾Ûí¥UÐ|‹ük÷Ç_hSŒóÈ©Ëz·”æìnêžâÓáOæ:ÌY.’ÍvÛ맯ç:ü+ã»?U,²ÒË«[ñ¦ÊÆöÏt>@>;û“ÀNÇ·CÌãþu�'ÍϯYvΫht¼£K¤³Y¸Çº²ÿù®l¿3[$’[O¶üi² §Õ@[ÏÏ�>âð±íÐs(ÿ…=eɳsëÖ]±«Ä–/)ÀǾé-ÖmñëYƒœÓë.Ì×I$–Ò¾·ãK‘)*­¿˜o†nïøªìÜM†D™Ý™â·dosðzo¼±q+ZãgÛnv¬íˆqqqðø¿jàé0äʃÙmšÞG�ÙšÛ¾¦&×UΗ?šã¯ÿ‰ò#ØŸÂ.Eúc‚x»Á7AÙ=“ðä»mƒ2þµë[ó ›;—çbMÙ™—G6ƒkr2-–ip°³zÁññÏ<õçŸÂ>Gú[…ø×ÂöÐÃϹô0Ò þMŸ!Ê¿¯zöü¨eÆËä¹xÒÙ\ÌÊÙ..—[fšÙ&“+«Ÿ_\ïÏ.wNGÈéµá~6ð½¬PóÞ{ Tƒ?’çÁHr¯ë*q²ù&^4¶W32¶K‹¥Å–Ù¦¶I¤ÅÅÉ‘oÁÁ4ù#æ˜=éÞ˜;Þá/ÞÝÓiÙZ~AßœƒO•íÎë޽ηédáñ|<˜« äV•Æ­/ÁÁ¾¹õž}u‡]WÕ}wÒ=wÄzŸ©øŽŸ‚u×ÓÁ¢â¼WEÐëõzøn’[ë[å’l¼í†v\ÒdæfdÉ6fvdÒädK,òÉ%ÓÐë^µà}=Àø·Xõ�Ôð¾Âõ0ixÏÒÁX05˜Vù+JVKåÉËÌËÉ–Iò²§’\¬Ì©džy$šKïºx½mÖܧø'ë.²âú®Àø^ª /ãZX+³ ß%iJÉ|¹9y™y2É>VTòK•™•,“Ï$“I}÷Y¯Àø êîźç®x¶‹„p>¢Öñž#ÄxηOÇøçÓâLJ¬Ôj5˜qÅ�‡…‡�¶Ye–Òž”õ¯­kZ½ûÜ=»Ö9cò‘ñyÖ_#]UM®àþBp-vuÝCÚÒcËX#¬Õ»*^ϣÊ\Í·_ï3)î­öY.f—*úæaÛ%.ÌÃÏæ¯É7Æ÷\ùÿÖ1âÍ. ï>ƒ›UvuÐItX÷ÍZäËÃ9¤x±É•´àÛ¬«i[«e’ej²nûÖ-·úäâæscä‹ã‡®|üë(ñf—‡w— ÁÍ¿ªû6è$º<{æ­r%á¼Î°dC“‰“mdÁÛiöxRäë7Z]Œ4÷ãåâË,R•¥.÷[u´�Gxt_jøãÙ<ƒ©;›‡ìxO;ãrÇLýN}`žœL›~¦ÛQ³Â—#[ºÒìaüü|¼Ye‚ZzÒ—{­º”ªÓÌ <„ð3½¹/ŽþKp|ŽÏôø»}vD3SeÅ9×ÚK•‡Ÿõÿ$†Ë0¹G Þ݃b6%jØþÒ•º´¶ÚVë®­)m´¥kZÖµô¥)J}µ­jR•­iJRµ­kéJSí­k_ÉJSù¯ûJVµ¥)JÖµ¯¥)O¶µ­%)OÝ­_Ö'ÊžlheÈÉÈ–81ñàŽù§žy¯¤qC QÒé%–Y.¥¶Ûm+[«_J}©l|<|fî³8×”~upÊcñ¨cÆÞuwŽŸ [3wù~ÿ­¯å}ͧˎ?ºqì[m¶l>55·I²’ënÙÙ$wàçJkâ{á3+g“Ç|’ó[ˆ[Ž8ñ·=oãç$’ܽ¾MoúØ<—·ôùqÙLm>=–Û.7žÛ¯Í¾êWee�Y~\¤~(¾²¶™<{É4ø…°èã�qÖþ?rL9-ËÛäÖÿ­ƒÉ»sQ•ŸvÓãÛm²âñÙíºüÛî¥v6Y�eØysÖø%ü88Ü ‰y�ò1Àµû.s›‰�¿éïù޲ÌìN¿†{­ŸWÍûãE�ø9œû#–σÄò,šRÙ~ÖÏÓí~žc¶Ûm–Ûe–Ûe–[Km¶ÚRÛm¶Úz[m¶ÓÒ–Ûm)éJSò%›m´¶”¶ÚRÛm¥-¶ÛiJRÚRž”¥)O²”¥ŶÛm¶–ÛJ[m´¥¶Ûm)Km¶”ô¥)J}”¥(š«ý?ëþˆ~þ*¾1¹s8a›êÍ~q�ŽG¬—cÒCÚ<{«9/ŠÙ-†ì[l›[ÔzÝKä¶K«=–ÝJ_["Šj*È€,ü9¼ÂþKñ¡Å4·K4”ëÞÝíž’ç×2È,ÍÜáóêÅ��_³W oç_X)ö]%÷MÿÓÀøå_ÂŽž¨ú’_üìîÓâ¾Ûó~õl?|ßÙÍþœPõ¶:ÿ }õ‡ý5÷Ý7ÿ NÓà3”×�|vñMd’úpnÎí-KoÍûÕ°Ó/{6úqAÿÖØë^a[ëúk¯¬ßü5jŸá¤Úal>üGÄÅÈ‚iô{!µ{(âž)dÄ͛ɮâÝÇ�•wÝ~,÷k·òÒÉ)mõŽ[/¥=·ÛZ÷mÚ‡iÝk×—ÅGÉÏäõÝ«~JÁþMÿß!?¼l`[öòkø·yýä;_ûß¾^óýeÜ~õì?¥&U¬X²Ž0Í¿ ß÷3óúúêê9KÇðÎÿrÿ+?¯Þ´þǹB]†“û˜ùUý~u§ö?Ê5~ÙÛäòÒæ¨Þ‚K 'tœÆ›­¶ûn²ûm¾Ëí­·[u)u·[u=.¶ëkëK­º•ô­+ù_òëiu+mÔ¥ÖÝJÛu·R•¥Ô­=+JÒ¿eiZ?åÖÛuµ¶êRën¥mºÛ©JÛuµ§¥iZWì­+Aÿ—/€ÌŠdrß&¼ âÑ]‹tY\‡°üdÐaÖÉ¡š:R]¦ó£5¸‘VÉ¡žÏ~LÜVÚ[}—ÒKuú_�«Ž)Ÿ*’ß/(ò3Â>1#úYÞy㎇‘ßK㥲lw=-­Ä‹éßõ#¥ùñ‹)mÞê_n«Ý[±õ´ŠŸÊo ·KÉüŠð›ŒEô¾–Fóžxç¢ÃúwÒøél›×Lkq"ö_I,÷äKÆ­¥·Rê_n¯Ý[ ×[ �Ãw­ìünIåïÇ/Öèû;ß¶íßø¾·GÇ;_ýóšô~¯,-fƒ±ð¾—ÖÙq»i$Šëç×Ö Ä_sßD6heÇ–X'ŠH'‚Kᚬº9a–;«d‘Kô¶øäŽûkK­­)ZVž•EBhfÆš\|ˆ¥ÇÈÇ–Hg‚hïŠhfŠúÙ,RÅ}-¾9c¾ÚÒëkJV•§¥QY𱦗")qò1å’àš;⚢¾¶K±_KoŽXï¶´ºÚÒ•¥iéT÷º-ßÝîxÏ&Ómxï$ã»]†‹�qýî¿/Q»ÑnõsköÚmΧa>~³k¬ÏÇ’ Œyã²hf²ë/¶Û­­)üßÉüŸ(Ô¯ŽO‰ß">C¹/é.9�wZô>“e'2ïO¬Ê“MI-šÏ¾qþ«÷bIÏyŒ8ÕºKñàš,*{>û—�tØÖOÒÿ�Ï‹Îúó×�Û²ÒcI×}§Î�–w/"ÖäIª¾Hç¶Ì½ÖÖì[ù¿+Ž*_t‘C,X84¶Ÿ|É‚ùq£Ÿ¥~|`÷Ïž\†Ý–“þ¼é >|xܳ¹9»"ýUÒY5–åèx&¶·c_Íy\p{¯¾¥‹ ”·ï™0].=¶Elæ|Xñ/¤|9ë Uô��KªŽÜ|ŽCÈsëo0ç[È¡úSrNe½¶ y6»\ŠÝul²Ë!Ãò[dVÙgáï†þ=ø#Ñœsǯ¸&àò6» «ßùG8åû0ñ÷œ÷°9¶Y�ʹžò=|Ë•7¥˜øxØØ8‘ãkñ11`ÚvʶQ´ mÜ?Ö�ûÖœ¿§ûƒˆjyß\s½Lº^OÆ7Q_~.n-÷Ç>>F>DA�«Ûêó ‹+;Xsp3a‹#X§Š9-ǽ­Õ=wÞwÊú£µø¦«›u÷6ÕI§äœoq÷bæâÝ|sÁ<Á$9ºÝ®·6²°³qe‡32²1åŽhã’Ü}Ú½U×�ßל¯ª{[Šjù¯_ó]\šŽGÇ6ñßv.n-×Ç<Á<C™­Úks!�' 7Hs0s!�#HæŽËíÇ}µÔ½kÞýkÌú{¸xf‹°ºË°´Y|o™pÞI‰LÍFïQ™KktRÛK£Ÿ+xìŸ* âÊÃÊŠ9à’9£ŽKk½ùPø¤íŽÎÅ“o¬³sϼbæ{y!ë.Ø¿É2u9Y6äeÃ×=—\!ÂÔó­v&<•ƒ"ØàÁßâCvV%±Ifn ~L>0»/À~{.ÛYfß�xßË6—Å×]¡~4rdêr2)>L\±þã 8š®k¯Ç†ÿ¡“lpàïq£®N-±Knf >K~1»+À®{.ÛYfßœøãË6—Å×�Ÿ~5’dêr2>¾D\±~å 8š¾g¯Ç†ÿ£“lpáo1£®N-±In^aß4ß}½ñ¡Î7]§Ö˜ƒ³|(䛬zq.Ê­‘ì7}Y™¼Ë’^växvY&^>UlÅ×フZÍÇÕ‚ÏtÒ݇o$œ³rÝÀ@R~3þ+û³ä[�ß6¢ÜžÐ\Kk�‰Ù=ͱÁ¾L'¥1²²8wÅ–‘ÇÊ9ìúü‹eºn¦&² c›6Hþ¶,9]-øèøÍíÿ?9µóë­ÊàÝÅvpcvnga]~,sÒ˜ù3pþ�5-‹’sŒŒí–èé_ºë ’Éó/²²âA—ÒŸŽß�ßóçšß6¶Üž Ñœ[g7avÞv÷âG=´ÇÉ›ˆðœyil\“›ä`Ïl—GJý×[–M™}ŸW®åü7ü"woÊ'akùw$³�õ/†üSqìì®ì· -·-—[‘sºÏ¥qöÐdaîùÖÒË®†}´¸Ùz^3¿'2̼›põ+|vñÛ¨üVê>)Ò]%Å18�â8ŸK/lÛ-¾Êkl®Ï’rM�l³#wÉwy}\¼¹:K½-¶–GdqÙ=¾†èn­ñ§«x¿NôïÄ✊bR <8)IsvY²ÒÛ¶íöÂëi‘¸äŒŠV\¬©k[å¾¾”öÙm¶Û<þ‡è~®ñ««¸ÇOt÷Äâ¼#Šâ}LH}%ÎÙgKKnØo·Û ­·#qÈ7ý\¬©}o’úúSÛe¶Ym¡ÝѽIãORp>‰èž¢ëN¥ëM\w…ð¾;Ö`j°,š|̬ŒŒ¬Éòö›½îïi—>~ÓiŸ>VÏm³ÊŸ76yòçškós03+¹÷òµÀ­ì�Ž2øíÞ¿êˆæ|æÊRë-­óu~-�™výL\Ê]t“qm¥´²—_Zûm¾+«I,Ñ�“Ng=øÿòëE'¯¦Gs^ce)u–Öéºï_^ÁDzŸS.—]|übÚRÚYK®­}-¾;«I-Ñ¿“g=ðË}þ¿ê>�æœÂÊRë-­Óuæ{ -÷ãeÒ믛ŒÛJ[K)uÕ¯¥·Çui%º-òƒ£»’|l|€i¢×ϵÊËð¿ÉËõÚì\Jçeåí°úc™çiáÃĶäÈÌ»i�Ѷ;+-döû=/öÖ•…«~W¦à"/Ã-ɱ4 Ü×Q•|”¿™xÃØzlvÉ–_²ÁçÝQʽÒY,±Ý/³UÇrý)/¾•»×Ò–Rû­ï�áÎßcj|ìæšÌ›ï¥ü£ÆÞy§ÀŽ’Gm—çâsÞ¬ä^ëì’K+-lÖèò}=”¾úzúúRßuÔï_áÕßãj<êæz¼›ï¥ü§Æþy§ÀŽ’Gm—çáó¾®ä~ëì’K.–¶kty>”²—ßO__J[Âümœ?"ýù¢‹?.=6Çžq¶ÏÕÙ<–àælô½éãî§?'—R)²õ¸›ìØà’êVèìË––Ö”¾ïYë&î›jÆàs?µ_É/ÞΑýCu¢_9?´ÛÈOÞîœýHõÚÿ8Ÿ´ËÈÞÞ�ýJuê°�ÅGû[¹Ç÷’é?ìw-Æç%—GÓï†Ú}â÷ÀÝ`½ÒˆÚIâ¯õçÈW¼ÁÒ/ˆoÚAâ¿õé¿þÀ9s¹Ÿ†ËöÔøaÿ¯ù'÷ªË¥ˆ+ –²5OÎÿâ=æWòSòõEËÚÇæÇñ3òßù1÷ßꯕµ›ÍOâoå§òfï�Õg*`o*?‹‘ßÞ·ÿW¼…Uš³5h R{ü-ßÅgÈÿïÿ…úºã)—~ïâÁߟßê?ÕïL�ðÚNüþþðþ¯øªÇïÁûû4ûÃùsv_ê ÆdŸR:HÕ+ ,ê¿"zÏ”ô÷tð½G?ëžeƒL÷Ü[=°ÍlrY‘‰›ƒ›‡6.ËO¸ÖeÅdøyØsA™‡‘²Ã%’[K©�{§ºÓ¾úë“u?oñ W9ëþ_ƒ÷ ÷ÛÛ5!žË$³#/3lmާm­ËŠÉðóq&ƒ/";%†K$²Û©�»{§ú×¾ºï“uGnñ W8à¿ î;Þ=¶¶jC=–Idø¹xy˜’ãl5[mv\VO‰›‰49x™Ù,2Y%¶ÝLaÝ/Õ^Dõg6é>îàºÊê¾ÆÒË æ\/“b×+U¸×ß49xòR诇3[µÕl±`Í×çâK~·a�^$Ðäö@Oåák¸ü Øn{G®?M÷Š³ì®®'8‹Üž_Õøù¹Ù­Ôvî¯ X1þ¬ÖâCȱa‡U›‘ì¶Xõù8ø—ÁÓä§â¶<$ÍÙöW]þ›íŸ§Î¿èsðíÉå½iT´¦³µ0µ˜°bljuÒSþ40ë2²)m“Gƒ>F>4°~ù&ø…í ó¶�“ן¦»_Ɖó¯®?0�Ü®YÖÐåKKpu�§…¬Åƒ,Zß%1áßcC·*z[lÑàÏ>>4•ÀüÍþÎÞð7oß>-bsNùðöü-Žç‘Í÷n»GÇšk Ÿ?idâépá·�õ¤ZÈ/ËÆå¸¸°Ã�dSãn ì8™ûn#8ØãŠ3€ üQ|öG˜™º.ïò?�õW‹¶Y‡·Òaßšž}ÞQËu³bãq82,¦OàãR’äò £¥ù‘IZ»&ú²æàwsãá‹°<®ÌÐ÷?�˜[δñ®‘ân4ØÛ&¯�wL2ÖÉðñøÔ3YLž;À²ñÿ¢äoe¶’fA|që,“ë_�ƒÝoŒ†~Àò·/EܾAaï:ׯÚY‹·ÓàÝlš¾sÜñK[fÃ�C5”ÈãÜ*IgÞKm$̆øãÖÙ'Ö“7 X ÿ†ï°¼¶¿¯ü¤óƒO¹ë?v‡”ñ¥›'gÆû_È=çŽm/ÅŽÝ–§/Ý�™´ºÊãåÇ�—•ù·àoEyÝ×p®ÚÕM�É4Væduçfè-‚eÀv™”†¹ëçšË Úhv•ÆŽÝŽ¯*—âåÇe·Ûôr¢ÇʃK|ÙðC£<ìë›8Wlj¦×òM™“õïfèl‚.cÀ¶™”†³ß�<Ö]×C³»;v¼ª]�—–ÝoÑÉ‹& óßã§ÅŸ’.¥�©¼™áííÓ_²ÌëþÅãSaé{Kª÷;hq`Øíø+È×ìí×þ’·¹ºü¼lÝFʸ˜õÌÄÈ®<Ž>{ügy/ñóÌåÖv§¿‘uŽÓg÷. ÞW+'¯ùu³C6^&VEöß?åôÅÇ—ëéö?O#Ý�5ø·åâ[f\�`ó‡ã£ÈŸyl˜—¡»�u¶Ïgv în1‹•?å6˹Xx9’ÝKæâ|²¸�ÉYµ9õ²jß�5ø·åâÙnUðkó{ã³È9l˜•¡»�u¾Ïev î^3‹“?å6˹8˜9rÝKæâœ®¸°ÉYµ9õ²o|_‹~^-–å_Y¯Ê·—¼’M¯<ÔÓµ¼vÜmòõü#ɪ̷‰fzedzQØú:ϲÏê^m°ÅÊ‚û5û çÀÍšé¢Õìvtʾ.y´)¡®?€2�Mô§kù Ø|ª:W�ò.ÇìQ•n.£�q¬²ò¯·ÝoÞ6ùÖ, .“]ßW3a›.>Ý,òÇ·_L•Ô};ÚóÏt}aÓ¼�v;äYÁ«ãÜw¹95³ß“ì67Ý›K�I)&^~d¸øXpÒ²Ï,qÛuÔÉ=IÓÝŸÞüóGÖ=?Â7ýƒÎ¹E°jø÷îNMl÷YlùùùÝŸM�mÿS/?2X0°á¥džXã¶ë©°¾1ø£äG™]§­ézŸ•öïblqëŸ.Ÿ�bEL-–ÌÌ-vG%æ‹a6á|S?e�ÛM¶^SäÃÒÒIc¶éÁüWüuω“qNùòvÝly)®š ÷ã�Ò›>²é­¤~’krµPåCd|Ó°4÷úMnÛ"*aës}·ë¡úø°ì¥™—Æ�Âw^øµ/î¿"é¤í?!ð'ÇÝñí$v~�랢ÙCí—_‘§ƒ*-å¼ëW->µ»lˆíÆ×åÒ˰!¶lhöL§ãSáO¯¼]—�wW‘Vé{GÈ\±÷\I”Øu×Rl¢öË�‘¨ƒ*+-åœçU->­»lˆé�¯Ê¥—`CI±ãϖLjÿÐViû«¾dâÞFy}e5yúÞW.³#+«ú??‘åËNiwظÙ[>KÒ”¯ð»i‡�µ¤XðÓ_‹¨¤™¶æH‘ÞÇy’:Gñ9ðùƒ]cÎñõ•ÉÙõç‘|fÜ�•‘Rûµ|_—ð~{©ÚÛ$žßt8ùÜ�Kekëí¬–ÙJÒµ­+Hþ~#^ ]ÿ…}{ÌñuÕÉÍàÿÆdËØ[.»[Æù?çm…/¿ÓÝ¼Šš‹+öÒ•¾–R¾µôôàâ/àÔßø_×¼Ï_÷Œîßœjü­…±ÒëµÜo”pþm§ØRëý=ÑÙÈlÔY_·Ò·Ûe+J×ÓÒ3ß‹ ©÷½‹ñO^c¨’ð:É>ŸínSl¶Ré'ÐnµÜߣ ‡êÉl—øQܺÙ+ZRÿX㺞ßôÖÁ ¤)U˜'%ø]¹®ÇÃîûëÛn­Û^%ävW+É¥kw¥šžuÖ¼W«²Û~�¶S×?�gÝëI/º¾¿m¶Ò–ÖéŸþNY‡�â_vð›o­Û>5ä.w$ȶµ»Ò=g0뮯ÖÒ”úT²”»7†æ×ì¾êÖµûim=+tÍ ß-ÃÏñ;»8E/ºí—ò 7’Omkw¥šÎa×|_®¶Ú}+l§®o ͯÙ}×z×í¥´ô­ÖI~îE³Üüeöž§c°¿.%ægfé´X—Ò*~ŠÒguCrK±"úqÙ}ÐÏÈ7» �[ëuÞù®¥+í¥¶ÒLɤF”ð^¼¸þ*>N'®èý[òVòoø¶ù ýãûcûß°‡“_Å»È?ï!ÚÿØýò÷Ÿë.ã÷¯aý)2¨Å`êÅ”q€&møVÿ¹Ÿ˜¿××PÿP9Ê^?†wû—ùYý~õ§ö=Êèü4ŸÜÇʯëó­?±þP±«ð{þÎß ?–—3ýFôXI;¤à–0†_&¿Ýçé~Õê¹´ÝäÔ�gådr¬-gÓà=©±’—äÁjéux÷eE¹›3ó+É0#“edS_\¼}�"ÆŠ0üŒü6tךÚöo[Mªé#nÆËš^S‰¯öpŽËέ.ÈÆ‹³ôÚØ+‘nÒ\ŸXÿ„81ß±Ž)k\˜vVÃ�\eùøpé¿3Jög[K©é�"ïÇËžnQ‰®öpžÊλݑ]›§ÖÁ\‹v²äúÇü ÂŽý�‘ËuraØÛ41pƒå§àSÆ/“_þˆÖãj»¿�kmÈÒóË´0Aƒ©ÒwÇ»Îc‰fŠ kq·xòcr dá[õóp0"ÕKß&üPïï{#/ªü„ë­ÏåE÷ÍdÙVÇ›Ç9V¢êÒØ·¼?“à_‘¤äÚy.¯²éq&’¸óÛ|ÛDrÅd-<Žñ{¼üMì,®³ï޶áŠ;oÉÕää[fgå:ºVÊY»â<“ é´ü�SÔ¶Û䯖ûñ¦÷A‘l9É�¾ò3ÆòñC°2º×½øÛ„r(ì»'Y‘‘m™œ{”jým¤{¾%Èð®›Oȵ7Öê[t˜Òßv<Þè'¶)ã’++ˆó—ãËÊïŽÎÑ—«¼žëLþ/vvnæ ØšŠen:§µµšIñ¬Éßu·7ûž&ïÜ]†FF ñânµqçcÙ²ÁžJB×f`&’€=gà|ß³¹^—‚uÇäÜ÷šò<ªáh8—Ñì¹#ÜåÒ)'¾ f›Q�—°Ì’Vv]ñcC|·û,­,ŠË¯»ÒÛkZzŽÂ9—cò�? ëî+ȹ¿1ä5ÃÑñn)§Øo÷û|›b“"øuú�^>Vv]ñcÃ|—û,­,ŽË¯»ÒÛkZe™é>Ûò'²¸¿Otg]rÞÕìîg›vá\+M—»Ýì/†2³rîƒˬÁÔj0!“+?;&èpµøpË‘“,PE$–Ësã—ðáE‰&—¶þAf‡*[?ÕÚ�x®ïêbÇ%=”Å—¶y·+Û•ì¯Ô“ô6‡'éV´‚éöWÛ\�}e5ñÿø~!Á“MÚžvMfDwS;Sã¿ÜRlïúvSîÓæZl�nuñKu÷×Q¤È¬­�Ý6ÂhîÈÁJKÀÃÿšnÓó¦Xs2#º™ÚŸxÖâ’áG}-²˜÷ö—1Ód{snŽJß}uL�¡ZÙ Óì%²ìŒê~)ÿ ¿ ë¾'ß$Ùš^ÍçºÜÞ;Êx׋|[aÛ©¸îD:ù6ë{Ó}\:ÙÚ»]vë+Ùôz™câ¿[W,Y9|�[ŸtËSŒñŽ5Âøþ›‰pî=£â|WŽë±uŒñ�N‡�èµ8Q[¯M¦ÕãâëušìHm¥‘AvG´¥-¶”J3ŽñÎ?Ä4ZŽ/ÄôZn1Æxþ¿S¡ã¼wW…¥Ñi5X1[³Q¨ÖÁ�¯ÖëðಖE 1ÙvR”¶”¥…ãÜw�ñ§‹ñM›Œq�¿S¢ã¼{Y…¥ÑiuxQ[·S¨ÖÁ�¯Öëñ ²–E 1ÙvR”¶”¥$Òi4ÜgM¨ãœsQ«ãü{�êð4š“U¦ÒiµX‘`jõ�^P`ëuzÜ#‡ìЬ¶Ë-¥´¥qö_eôÀ;°z÷ƒö¿ ä�sÙ\SGÍø'0ÕͦäÜW’kàÚiw:Ùëm×cæaäÛ}—V9c²X¤§¤�Íe’Gu²YmÔò¼ãƒpîËâ<ƒ�vÒóʵ³ê9ä8lôû}vG§ÔÆÌÃɲø¤¥·Ûmö]éKã’Ûo²¶ßm·SÊóŽ Ã»/ˆòØkMÌxg*ÖÍ©ä\g�`A²ÓíõóúVü|Ì<›/Žÿl–[|wSÒø¥²Ûì­·ÛmÔø<§‹qŽsÆy æ¼sCÌ8o/ÑíxÇ,â\§Q¯äg“ñ½ö ú½çäZ¶>^«u£Üë2¥ÇËÄÉŠ\|ˆ$º9,ºË«JÂ/äûà³|Ëä½×á¶»Û½Y¤Úíº¯<½çmuf4Õ¾ü«5xÐÇ.odð�l”þ‡‘.Ýác_m2¡Ê�#et8~Gþ {£²ypx‹�½íN›»"]–Ó«¡³+uÚ=i‹7¾YìÔC²æö/×ËOlrÅK÷xx÷ÙL˜²ì‡#cXu|Ž|öGäòßñ#yÚ}7YåÙm:Âr·]¡Ö¸ÓVù'³S vK™Ø|?_%=#–*_ºÄǾÊdE•d9 ÀKåßðºsΧ›�yñ­ªä]©ÖW_ÈwÜÃÅüŒ©·}©×x‘RM´Úk7&ùv½½ÅáÆúø±hçºn_�t±ãÝ¿›.k°£[ZVÚÖÛ©[n¶µ¥ÖÖ•¥iZWÒ´­+öÒ´ª;õ¥iZÒ´­+JúV•û+JÓòÒ´þj<õ¥iZÒ´­+JúV•û+JÓòÒ´ýÊÑ ùàŸy±²a—'Y ÈÇž;áž á¾±Ë ÑIKdŠX¤¶¶ÝmÔ¥m­=+ö¿àÿ�äÿ´¥n­-¶•ºë«J[m)ZÖµ­})JRŸmkZ”¥kZR”­kZúR”ûkZ×òR”þkþÒ•­iJRµ­kéJSí­k_ÉJS÷kWõ‚ ò§‡r2r%Ž |x#¾içžkéPÃtºIe–K©m¶ÛJÖê×ÒŸjJ_?›ä_î¿2uÛþ¢èÚM×SÕyåèûk´ñ¡­—âÙ´Æš8³zۄ줯ôL‰énï7Ë©‹,yû+dAñÁð[Ø�ã“ÇûƒË¬ ïUôݹlµ}]5™Z^Ñì¼X}’ÁfÞ-‹7®¸vÂZûd–ZY»ÌDzúcE‰dØûHcã�àϰûÃ'�öÿ–øάéºOËWÖ[•¥íÊÆ†¶I›hd²,μáû +é$²ÒÍÖ^=—Ó,[&ÇØRd…ÓžvÄÜÈ’�W"ê¾²¶þ=¾áþ/ãåM¤íNÄÄ–‘í§þܹ¸×ŵê/67ÐÅ—GÐòü‹§Ê�"íØ�Ý›6î·ënÓüŒõŸWqà7Y£Œñ>5�·O¨Á²I'¾Ì|hm§¾|¬©äŸ"y+|ù92É4×ß,—ßtÆ8_ðž«á¼{¯:ã‹i8Wâzèõ\wŒqÜuº�V —ß-ÑãbÁm¶Ö\Œ‰ošin÷K‘<—Ë%×É}×Vb¯øOUðÞ=×�qÅô¼/„q=tz®;Æ8ö :íN« Ëï–èññ ¶Ûk.FD·Í<·û¥ÈžKå’ëä¾ë«>ž#Ä8Ÿâü{ƒðN1ǸW âZ}âœC‰iu¼s‹ñ�§<-V��è4øØz�6ŸY‡ ‘cãcE0Åm-²Ú[JQíÞÁëÞˆ�ûg€àö·UögWm/¶-odõÿ3à;.²É-�˜ñÍ—˾èäŽXä¶Ì}�Õ­·Yu+ù+JÓìxŽÌá8—×�×JÛMg`pŽWÂv5¾;%²˜«CŸ¢Ì­ñId±Ém1óîõ¶ën¶´û+JÓìxŽÌá8=•Öý�×;:Ûn·ŸðŽWÂv_e’Ùn*Ðçè²ë|RÇ,r[H3îõ¶ë.¶´û+JÓì|ÝÖ›UÈ´ûn?½×áí´›Ýf~›qªØcC™�³ÕlñeÂØkó±2,“+3{ã–9-ºË캶ÝJÒµ¢¤Üœiðò212cºœYåÆÈŠïOtSÁ%ÑKÞ•­=ÖImi_Oæ*ÕÉÆŸ'#*+àÉÅž\lˆo§¥ðÏ—E4WÓíô¾9-­+þ�råcO…“‘‡•ÐdâO.6L7ÓÒøg‚K¢š+éOZRèä¶´¯ú4R Ì8—#à·”ðNaªŸEËxW#Ýñ.S¤Êºò´Ü�Žlò´û½VMøÒÏ�|úýž°ßXï¾ÊÝe}·Vž•ðœvàK’ÅÇ>Süm³'*øaý§Þ ÿ| ×öËÝ'ø€ý¤ž*ÿ^|‡õ{Ì"ø†ý¤+ÿ^›ÿì—;™øl¿mO†ñŠÿ’z¬ºX‚°Ùk Tüïþ#Þe%?!ÿT\½¬~l?-ÿ“}þªù[Y¼Ôþ&þZ&nøýVr¦ò£ø°ùýá»õ{ÈUY«3V€¥ 7¿ÂÝüV|�þÿø_«®2™wá®þ,ùýþ£ý^ñ4Èÿ §ñdïÏïïêÿЬ~ü¿³O¼?—7eþ ¼fIõ#¤�R²~\ì¦f³g‡‹±Öìqr0vüìxrðs°rá¿/3"É ÊÅÊ‚K¬’;íºË캴­+JÖ�Ï—‰‰°ÄÊÀÏÅÇÍÁÍÇ›7 .òq2ñ2cºŒ\¬y­¾Œ|ˆo­—Ù}+möÖ´­+J¿>f&Ã+?;;|<Ü,È"ÉÄÌÄÉŠèrqr±¦¶ør1²!¾ë/²ûkmöÖ´­+Jˆµü�þÙy‡¸¼—GÖÜã2}¦ï}Ь›u}gÈòd‚üÛ¡êí¥!º.¸ÚægÇ}�ê²ëü÷åYd3iñqý—Ưäàˆv4ûÎØðž]'\sL©v[}×Fí禷­ùL–]›t}s±¶+ìëÍžVU²Y²zW�ÖéãŽ58ÐV—ÆËä à/‰v.Fóµü*—K×\Ï*]ŽßuÑûyé­ëŽA‘$wf]\ìl†û:ûe•—möE¬È¥tºxìŠMN4—Ä·åKð¸ô’Rnû›Ày¸‡ŒÝÕ<|Ütöf6^»ÇŽÅοeY³2õ8ú|]ŽoJòµ¹RRË589\w&ìs¥ë^¡àü‹°¹Ï ž�ë8ï×É�™užû-Ÿ?6Z{q5:| o¤™yÙrA…‡ +,òÇ·]Lýãg‹CùƒÙºî�ñ›¨¹—qö&Â3.Ñq}²ãéulp57ò>]È3¦Âã\#‰blö¸°dm÷˜:¼irb¶Y쬖úÌçã[ðñõÇKW�÷›tÐwjEÛMÒø´³iÔ|:’S.Ëy|³ÇHûS‘aÛlVIöYÇ —ïý-¥•ƒ.Ùw|y| uïNWCÛ^bSEÛ=Ÿ˜Û-ORcRÝŸTp|Ï«Lˆ«É®È†Úvg!ÄŠÈí’)#³C�-ÓÇH¶4¦>e²åøõøëîž®‹¶|ÁýÛ�˜»-ORãRÝ—TðŒÏ«L˜¿„×äCe;+�bÇdvÉ–Y¢‚[¦Ž‘lm¤vØ;ñ#øgºCÃ,Î5Þþcdñ$ü�Ógçì¸çÃÆ›oãÇTÏmÐáÚhô¼£E«ÚvO?ÖÙ¹qn7˜¸:ܬ¨¾ã¬³7]�¸žNÛm–Ûe–Ûe–[Km¶ÚRÛm¶Úz[m¶ÓÒ–Ûm)éJSò$em´¶”¶ÚRÛm¥-¶ÛiJRÚRž”¥)O²”¥,¶Ûm¶–ÛJ[m´¥¶Ûm)Km¶”ô¥)J}”¥(”«ý?ëþ€3̸_ìN/ºá<ÿŠñÎoÃyÚîAÅ9n—]ȸæóëì–ì=¶—o�—®Øc}Xí»Ù,wÛK­¥}=iJ¼÷,âV¿a‹uÖRµŽXï·Ö”¯§­(óܳˆñ^yÇ6ü?œq­1â|ƒý~÷Œr�F¿}Ç÷82]m÷áí4ûL|­~~5×ÙmÞÉcºßu´¯§­(ø¼—�qÞgÇwü?˜h4¼¯‰r½.Ó�òŽ/ÉuX;Þ;É8îó}fïA¿Òm ÊÖn4»�nT¸ùX¹ID]–ÝeÕ¥b�ç¿á«Òn®ÝöO€ü–5°¬SgÏãÇaíò§ÐfOY3K‹×=‘²›+?M6UcŠ,}vþ¹8×O-÷ߵĂÛ"¶2~p~=îý×aøAÈà㣛:n†ç›<™øî\ÖE$’cp�.VÃI.Mb²È07zÆ¿"k®»e‡�m±[¯7V“w~ç°¼#äPq�…ÑË�7Ds½–LÜ.{#–I1ø>Í—'?I.GÓ²È07_yƺynºí–$Û± ù,ü(=AÜ9œ�·><ùn£ ;c‘™·Øt:¿g—у?7?O~Mœ’`cíy_N},Zí2­À®' ÒÏ•.&:®’ØŸ÷�ÝÙã_9Íë~øëaÕœÓ ë_M7-ÔO¯¦Ë ÌŒÓ{eJI¨åz|¼Ilƒe­ÈÊÀÉöV±M}>Ô_»Ÿ¡»“Çng—×ÝßÖü¯­9v/Ö¾Ý_(ÕˇË ©°®Úñý�ŸWQÉtRåcId[ tùXSÖÊý9n¥=Q†înˆî?y–W_÷o\r¾µå¸¿Zûu|ŸW.›HrfîÓA³·êêy&Ž\œ{ì‹?_>Nõ¶¿N[©ö ÷å?†SøKÎíë*:?�tÏ(ÉûÍÚk¹.ÚÕþ¬²ù/²8ìºI$ºÛ,²Ëku÷ßuim¶Ym´­×]uÕô¥)ö֯ݬÖl·[-~›M¯ÎÛmöÙØšÍV«Y‰>~Ëg²Ïž<\~¿9r³s³r¥¶8¢ŽÛ¤’K©m´­kJ;çàoáüò¯Éü�9ïœ\ï:O&l|¹ååºËéÜ|¯Uë—Y"â�s—Hr8å2dĶ/¿r*àR(²£ËÆÄÙEJÇwr|%ø)òoÈÜ�'3ï,|Ïú{"l|¹ÿ„úû¿¶ï'Ö{笰ñ®•Håãy®/Òûîþ¸U†Ìˆò ÃØEJÙwpü(ø/ògÈÌ�/2ï3™³i©ÎÎÆû×&¦F÷S³Ã¥¹jh$¶TÓßçm¤Í»í�àÌìL™0|üêlîAq12üyìN-Åd®%“Ó'aoq®}È1)HíÏȊ赜o#Ý}ðÃZCí¥—Kuo™²\©n§ׯ.?Š�“ŸÉëº?Vü•ƒü›þ-¾BxþØþÀ·ì!ä×ñnòûÈv¿ö¿|½çú˸ýëØJLª1X:±e` ›~¿îgæ/õõÔ?Ôr—�á�þåþV_½iý�r„º? '÷1ò«úüëOì”,jüÿ³·Èå¥ÌÿQ½–Né8%ŒÃ�íãßJy7×۬ᄊã¡Á6RS"M%ú[°6ãäâC¹Ðm±%ÄÝñŽC‹‹™4plu¹8¹Ð[-ôŽ[}×zâ~éè® ò+�ìzÏ»ºûŽv?ÙÝõfÒòK¤®m1ò1bÛé6x²cn8æû.[ Økò1³±é%ßN[=ÕõÅÑÑ}CäOØõ§vuÿìn³»êË¥ä8—I\<ÚA>,[m&ÏLm¿ÞâãåKd ~F6l’ï§-¾êúâ®ëèîŸò;­y7N÷¿\qÖëa‡÷>AÃ9®Ÿs¨ËöWêaçcÙ‘egÕîõ9T·#c‰|úì¸ãÈÆš)ã²Kb-ç7á¦ì~%‘¶ç~ rË{3ŒÖérîé>ÆÜjô}‰«¥òb[L>ÍòíÕñ_‰fžJGµ¿K•�‹ ‘ÒmŽEþ·EƒÍûÏ8ÖF×›xUÊmì=uÒåÝÓ]ƒ¶×iùζ’K�é‡Äy¶Uºî-Ê1#úÒÝl[[µ8øÐ[oÞ3ç¿Ö±gó7ðòsÎ3>Ó›x[Ê-ì=[¥Ë»§;k®Ôs�u/’L>%Í2m×q~O‹Õ–ëbÚ]©ÉƒmûÆt÷zÖÿ#¿„“6yhühó›rñ¯»q¹Èñ{¹¹�åãÖI·»H4==ÜñÙ�“�’kõZÍ_3’),Ž)2³ù<×ÝìF°ºß°z—–í¸ip~Y×\ßE$Qî8�7ãû^1ÈõµÈ†Ìœk³4ûœ\<øbËÅ–Éa¾¶{&Šûo²·[u+Xàsî»çÝUʶ|³x_)ëþe¦¾;6¼[™h¶\s}�õâ¶|i25{ll\Ë ËǾÙa“ÙôæŠëo²·[u+XãóÞ¼ç½WÊv|²øg(àËM|vmx¿1Ñl¸î÷ëGlØ÷äk6¸Ø¹vC•öÉ žÏdÑ]Kì­ÖÖ•¬5ûßÇžòñƒ±v½Kä7Ts®�ì]7Õ“'ŠóÞ=Ÿ¡ÎË×Ù±Øj¡ßhåËŠÜ.KÅvYº¬‹pvúérµ› "¬˜ÓË¥ÕñoñÌ8û{Žò]½Ôq~)¢Üò~MÈ6º�ãÚÌÝÖ÷wµÎšÜ|-f£Q­ƒ'a²ØfO}¶E 1ß$—Ö”¶•­}c�ñíÿ,Ýê¸ÏÑî9/$Þçcjô|�ë3w;½ÎÏ2KaÄ×jµZè2sö9ÙS]Kc†ï’û«J[JÕõô}Ê÷z¾5Å´›~KÈ÷™ØúÍ&ƒA­ÍÜn÷,¹)&¿Wª×C“�°ÎÊ–ê[PÇ|—Ý_JRµ{.¾ë®Áí®c£ë¾«àœË³;“äO‰Æ¸/_q�ß3æ<‡+'a“�£ãSwÕuÏÉŒúx½Öy5ÇÌý³Â‡wݛ쾄ßG†ÒxõÜ™PÖHn—y‘fÇ kiuÚ©ì¯ÛÝÿ¾ü”îÊ깑» xß×YÇË®ƒ; ×qï0n®,ÿF-löë8E2ñï–*˹ž› )ì¥oÕÍe~Þíxyðä—u×U˼‹Ï§Žw‘\|ºès±aÝwó êãÍô`âÖÏn·„Ó* ¤Š²îg¦Ã{i[õrÙ_¶Wß„ÏÉNá—QÎüòæ±øÃ×rIMýSÂ38÷6ïÎCƒ[£ºès6ÐI»ëN®�7{eƒ"{ù&Æc¾ ½F5ß�I„øƒà/‹ ñjñßúÇYÇöÙØ0ár~ÅÜÖÞAÙÜΖY…YëÉ9¦l_¤+�•—¯�'ôf0ôØù5ºL|8kuh•ÿоxÑá—»�ô?[ë8þÇ7NGÎöž›ÎÅåÿJÌZÉw"åù¶Wc&ùX–äS]‹÷]N>E×_�‰º´JÛżjðÓ�] èޏÖqýŽnXœ��í=7�‹Ë©e¸µ’¼‡—æÙ]„˜Såb[‘Mv/Ýu8ùºü|X}Õ¢qž x%âo�œn¹ñS¥¸§Ui¶4Ä»“îp"ÊÛó¾u“ƒ—·ÎÀËçÝ…¿ÉÚsNg.¯+�÷öÓãêáÊ¿ <|ol6îCl›bÛp¾A>|Uóz}·?ãPÛãÏgß6VWepMOæ; œë32²û3¯lÉÓëù>Ï&’dzí0òµ›y'žÙ2²s"†¼fó"mŸ8ÑCN‰ïÚË‘‘ؼ'M‰‘¤åyÓeýê|žÅà¶äj°9.|ÿVvÇ#]µ’Y,»#'&(lÇq;äÛà{ÂÏ’¨÷\ïw¦—¢¼›ÍņÜO"ºÏY‰úW{“®Ôæjôøý·Â%È×ñîÕÔâÒLO©<²kù'Ýu¸¸xÛœ\8ë мó7â×Ì¿2³3»ƒ¬²7qE!Ãî~º»/—õv]²I ÷l7Pàâl¸~F^LôŠ}ö¯#"KnúKe=õˆO—>\ø[‘—�ÚÝu6ã¯ašØ±{{¯®Êå}m‘l—ãCvh°±v\FlŒ¬«a†æ²l™m»è[-”÷Ö"~\|jùmá†F^wjõÜû޾†zE‹Û½vW*ë|›d’`®Ãm.ˉO‘“‘Hbƒw‰­›"KnúËm=õ€È/ÁWÈÇ�é¾YÎú×ûntV«ëåoeÌx>¯U¤Ÿ|ìOÜ1y�WýÖ¡³''s�š¹’} M†eiï¯;Ú Ð—Ýá¿Æß—~tî±±º3¬3îátÏû†ë·ù�2¸¿SqË¢›,êfrÜŒIÿMl5¶æÅ$ÚÝ4=µ"»êS¶R·Sv¼Iøóò¯ÍM¶<=/׿Àêf݇¶íŽa÷ž3Õº+á’(³)‘Ê'ÄÈ»w��vDu›_§ÇÙìì²ú_÷og­ÔÝþ=¼©óGm�LõÆmxßk‡¶í~]÷ž5ÕÚ+¢–³)‘ÉçÄžíÖnr,ºl <-�–]Kþí[=n§X>?¾|ùùÈÖo:kª¤á}3›$ßxò#¸®Ùpn ú0W�ãÉwÙWW±ä�›=»®3‘«šÎ-¬ÜÛ­Ù_ {+ðb¾³Û5��?ƒ�<$›MØ¼ÎØ¼…òìlì^Áæ:l|n'Á¶xù2eC7Yp)gÙaéö8µú¦ßa>ÃklØß[L &“0Ÿ>|nðÚ]G>åVÇÞýóƒtYxÜÿ–j!Åã\7>,›²a—®x<™,-6ÖÁJmsfÏÚ[47I‹.sIŒ—ÿ�ÿ ^8xs.§žò«cïn÷Áº,¼n}Ë50ãq®ŸEÙË×\"L�ŽŸ?Û )µÍ›;il°ÖLYp¬šLu‚ÿž|iWQØx8Swÿ”8pìã“ÈnÅÔA…‘ÆìÛáak³°z“¯bØî8÷Yaɇ‹5Ÿ~¤Û>Mt[ìY6÷àdýÎ>Ù; ì¸ ¶¾Fú¢þ’óÃË^¶·KoÖé»Û°v|kKU† Ë÷¹<Ï‚G‹m³Û‡' ä7ÃéOJÅuµ¥kOJÖµ¿�n±—§|Þò›¯îÔÇ£ÂÖw_:ÛñýLÖ(0¸0ÜOÌøTXñÝm¾Ük¸� º?Jz{.§¥kOJ«hùë):{Í¿)8uQèðu�ÕÎvÜUwEåû‰ù� �;­³Û�wßá]¥==—SÒµ§¥kPÏͧMåôOË�ü3;a&ß¿y/nãˇmlƒ[ä&·¿5ºYX¡¥²éu]—”¥¾ÚIÞ•ºž—WKvÓç-@wà>Åêï8¼Fç»�º�?ò/¨29Îùc‚,.3•ÎtºþM‘<²úGf=š ÌŸ©î­´özúÝoå¦×x'Îaëo4|Uæ¹› 5ZÍ/u]w›$ްøî2Ôêù$ÓË/ô; ·E�‘õ+ZÒžÏ_¶Ÿ–›Wà·8ƒ­üÎñcšægÛ«Öi;ó«.Ýì$’8bÄãÙÜÇS¬äSM$¿Ðì‚ÝnG¾·VÚ{=~Û-:uð½Û›^’ùXð/šiáŽl�§‘¼ª²é6Lx‘äz;’f]4¸¹–z`qîÄÊŸÙí¶²}?e$Š·R[-=Yt²µo°¹�Ÿ�Ú¯ä—ïgHþ¡ºÑ/œŸÚmä'ïwN~¤zíÿœOÚeäïoNþ¥:õXGâ£ý­ÜãûÉtŸö;–ãs’ŽK£Œé÷Ãí>ñûàn¿°^é?Äí$ñWúóä?«Þ`éÄ7í ñ_úôßÿ`¹ÜÏÃeûj|0ÿŒWü“ûÕeÒĆËY§çñó+ù)ùú¢åícócø™ùoü˜ûïõWÊÚÍæ§ñ7òÓù3wÇ곕07•ŇÈïï Ûÿ«ÞBªÍYš´) ½þïâ³ä÷ÿÂý]q”Ë¿ wñ`ïÏïõê÷‰¦Gøm?‹'~xWüUc÷àýýš}áü¹»/õã2O©$j•�\ü”ñ#Ç//¸]¼ Ⱦ§ã™¢ÇºY5;Hr09/Éžüi'Ìâ\ÃM‘­å\W+*ì8­žì ̽EgÒš’E[¬®òÅÞ‚ò§‡ÿ»÷¬8ßch¡ºIu’í!ŸDzfº ¦ÌâÜ«S>¿“qŒ¼�»GlÒ`eã×"+~œ¾øëu•ÀžAø¿Ð~Tqà?~u�ìM7K&²]œ3âoøöL×CtÙœ_•jgÀä¼g/#îÖ[5ø9pVxíúrûã­l®òKÅŸüÀë=‡Oy3ÔYs#Ñs gÞ'ÑíåÕìt¶ò^¿Ã“’p~_‰©ÜeããntÙ˜\Hòe¤9ûîõ‰ç™†[´8¤Û.[ábcv§¥+<}GÚÛ-GìŒJÖºølÃãüòØ5=Êþ¤²äÏuvVq¯ºãEdt“2k«ucåÏáÐìN5.Ç•økϱû#GKožÎ¨ìýޝŽsìzÿ¨ã³�s›`ÖpŽMY¦–y=6VqêbãÇmŸ[.ZÖêÆ#Ëí؜j]�*ðãžãö>Ž”¾xú«³¶¾9Ïq«ë‰˜|œ[³„òZË,³IÿT¬ãôÆ‚;lúÙR×ÝXiy÷øDv�drÂøèíì|ì¬Ùí­ñ×¾¶?vÙA}´Ým)¤ë~ã×à× >Ù®» W©×r|JÃm—O°ärÖë®¶5ÝÃÑÑãç)»…w�UóÞ¨å?Jüœ}7<âû~5“°Á³"l_Òz‹¶x°AºÔK‘�}‘æb_6,µ¶¾É.ôG“¶ºG¸z’ׇwGYs~¯äµŽYñõ\ÛŽm8üÛ8r%Ä»e§—a�6ëS~D7[f^%ócI[kì’ê#ÓÛ%ÛýÉkÃû›¬ù·XòJÇ$øú®kÇ6z ¶qO&-v:‰3ñáÇÜꯞ­³/ù±¤­µö_TE<žðÇʯ 9…87”� Øý-¼ÈÍÚ`iòyv†k8¯-—KLíçà\ó]vÃv»[úK“eèö; XîšÊVJV´£±kµ�ë8?ç=›Éõ\'­øo*çüËy5Øú^'¸þÛ”ò]´öÙt·Å­Ñèñ3¶yÒY•ºêGÕ¥´­köR¯SÃ8?5ì~G®áÝ{Ä9G;åÛ‰/‹SŸnƒkÉùÎHãºY#×ét¸™»,ËãŠÊÝu#ŠêÛm+Zý”zŽÁù§cr=wëî#Éù×-ÜItZž/ô;^MÈvrÇeÒÉ¿K¥ÅÍØæ_VVë©wz[JÖ¿eC¨:G¹|‚æPußDõGcw/<ÉÂÈÙÅú»…ò.wÉEáÉ�vÚ]7×lóàÔkïÊ�ïrYn>=/¶²_m+J¤aáWáµò'¶gÔó/9>:ð /ÄÌ¿�é'Ôr¾çßà_÷ ªãËLYóøw^Y��“%¶Í›6Ëe…• cÈÔÛùißï¿W|ö|ÚÎYåw"‹ ø=÷ãå]Á´SêyGnîð®®4õ†Y1¦Øpþff÷{&Ê—i°Æž;£ÈÖY_¶�øñðø÷ÇgK¬åžUòº„Iv>UÜG>«“öæï ÿºÏXe¿lþ!ÀíÌÞïdÙRìóñ§Ž±äk,¯ÛIm|þîçç“êùßȇcEÑœVÙd’þˆéýÇæ½½´²+ù Ðr~ËÆþõ_Š™XºÜø+©§1¿a®É– nÕeÙK¬—çŠþxßáwþxíÖZ^ ¯Ë� ¼“}m$ÚsNm��lÿCgÍ9~Æé÷{ü˜¥Ìžø"’ZaàýâHñ!dž´Ž’³ñ§Ä¿¼Dá?ÀNƒë�? ÖäÛ‹~ÿqe%ÙrÞ_›‰IëÇ—r­�ù­öLReÍtË/ݰé5ñâÅ5¤t•_�^&xÿâ7 þt']iøV»&Ü[÷û‹)&Ç–òüÜJOô¶<»•l/ÈÝorc“*k¡ŽY~í‡I¯�(!­#¤Þ<^ñƯ zϨ<]éî#Ó¼ÉvYzÎ9fV×�m¥¶‘ß»æ<·{™¶æß}Lk,Ç·?qŸ�™f$1cÛ%!†(ìÚÇ65±À MÜÝÓ>Dp̾½ï.±á}©Ãrë<”ÒsMê Éð²µß¥ôy91]�Ç·øøy²ÙÇ\lìo}nŠ[.ûXǶº[©{çˆåp>æëžÙœG*²K]'1Ñàî±ðó$ÄȦÓS.TWåi7xØÙr[~�fcVúݶ]ö±—lô¿Sw¿Êà�Ë×\C²ø–Ue’º^a£ÁÜãáåË‹‘…M¦¦\¨¯ÊÒn±ñ²¤¶ ì9 ÌÇ­õº)l»ícîÐê^¬îþ³ëžæënÛ=¹¿M·ìž# çOe6 öe`ϛǹ.ËU‘‘ƒ—²Á%ÑVøe¶—Ù[n¥+HçyYøcú/›ß¶ä¾$v¶ÿ¤÷sS7/®;ÜÎÂëYr.¤pÔê¹-g�°¸–º;©}eÉÌ“”O_Z{c§¥}x äßáÊégvÇ�xµÙ›î™ÜÉL¬œ~ÎmÌì¼–o§e0µšÝü¹1sÞ/‡IiueÉË›’KZV”¶*z8äÏáÑé>evË�x»Ù›Þ›ÜI÷¬œ~ÎmÌçý{$Õ²Êak5»é2bç|c’{«.N\ÜŽZÒ´¥±SÑ?1¿ O„�É~G#ñ3°¹×ˆ|ªúâû¸¶M6=ßÓÙqccìïˬ.cÉuÝ•¢Ýnö8Þü»9V^³  Û¦µ¿Ö‘ëòáKäsÇL¼»¶~>ï»g�A—“�‰Ìº²öƳe-¶I&ÂÞ9¡Æ§ciµÕŽJV’í4x¯¥ÞŸÎÝéÁÞûøvùè,œ«óú/qÚ|v‰ Æå�|½¡¯Ï¶*[t™VqÍ>,}‰¬Â¶Ûé_«°ÒaYéëöþmÞœ!Ï?ú'*ý‡Fî;K�C‘,ܳ£®—³µùöCKn“.Î;§Å�°u˜T²úWêì4¸VÖž¿ùMÞ‘rò›ðÚ|­øÇËs�Òz¯%8f«_��•Êü`äWö6mgØç݃fŸ «·Î#Ýû½®k¹UÖñŒÜ(a“ß÷‹­²ZÇË-Ž·c¨ÍÉÖm°3u{)¯ÇÌ×ìqgÂÍÄž;«l�äâäÇðMeÔô­·[JÒ¿–Žgìu»>vV¯m�›«Ù`Í~>n»c‹>vDuô’ ¬L˜âÈÇš:ý•¶ûiu+ùhæžÇ]°Ôfäë6غ͎×ãækö8³áfâO}/‡'&ȧ‚k+öVÛí¥iüÇy÷^sþªå»ŽÚ˜u¿:ã¹ârϸÎë‡òÝUñY=˜ÛŽ9ÈpµÛ�fEÐËmô²xlº¶ÝJúzV�ÄüOÆñà ÑÓ^8÷÷‘‰t=Ó=™Û{eluø^1øÍÛý½ƒ°ÝÉÇ+Ì8ïØbõ–£w› õü«¶w–êzdžËLXýÞ»m¾•ºë-¥}÷ÙmÝõñoðÍùGØ×aï< ì㯺—_/Ðß…ÚÝŸ5ñM�_ºäÇ¢Úbõæ�;I>ž\{½¤ðË)&i_Wqükü:~HsÛð÷^Ióî)ÐÜ~ê[,Ü[�ߋٽ‘5ÑÏÔÃÊý°ÅàZHrñ/“éæE·ÛI¶Ò—áÖ•õw Æßóä=¿uä�=â� º”’^/ ¿³;[£š ߉“MFÃ�é Êžÿ§—ßi$RYé~%i_T›|0ü ÝíÌæÐrÏ:{׌t¿ÈÇÐmvIÒWAØ�³$Y°f_È8§!ç»\8:¿€r-,”Ƕ<ÝT|÷]“uÒÒßKl²I$ýáïÅ„~SmÔýQ�È»±INáíI0¹ßfY<e},�>Ó']…£áSý»£–¼{]¨¦M–Ùõé-Ö[u$ƒâÆ?‡^Y‰±êαÆÝóìjE[»c²oÄæ��Y¡ûŶdk6¹8šŽ!,‘dÖÉi¡ÁÕG‘m¶ýkd­´­$uâ�ÆW‡ž[‹±êî²ÆÝóÜjE[»_²oÄæ}‹Y¡úô³#Y´ÈÀÄÔqo�"¶KM®Ì‹m·êÛ}m¥i/oþ*<øïÕAo�=¢Óó¹4ñj7ýÕÌ/¿›÷O(²ývŸqvo=Þ[6gÖòYôpfgixüzn9vu+4:èk_Jtu¿ÍütHæy¯Ðö å¼ ”áÓaÆ9¿ßqG�um¥¹ÚKªËÓmðë[­¾ÚS'_›%Ÿm+OÎü•yþYÆ4¼ßŠò^Èñ-ÏãÜ»�îxÆûÿOfn—®ÉÕm1.÷[u¾Üœ¹,¯­+OK¿#ÏòÎ1¦æÜW’ðÎG‰nåÜsÆ7Ø7z{s4ÛývN«i‰w­·SÛ‘ƒ—}•õ¥iöþGçËÄÅÏÅÉÁÎÆÇÍÂÍÇ›3.ò1rñrcºŒlœy­¾)ñç†úÛ}—R¶ÝmkJÒ´ª ²~ÿ=©%ô‹·¼Cº*_u#ºNyÜÖIt~êû+}–ô–Ù}môõ¥.º”¯îÕ ÿ ß›ô¾úGÚÞ*].º–]6íË/ºÏZûn¾Ëz>Klº¶þZRë©Jþí*wþ/7)}ô‹µ|Vº:_u#ºþkÛv_užµö]}–ô„–Ù}mü´¥×R•ýÚþUs;oÁõò/×g‹¾|)ÌÑǰͳM—¶çÝé­Úåj­É’ÝvNÏ]‡ã®×a>%,ºh"ÊÉŽ)+[m–KiKëþ?Íóëý÷|@ÿoÝÏþ çüÛÏ8¿ßSÅ/öñÛßâ1þ?ÍÂóýôüTÿo»þ#_?üÏß’Ï÷ððgü%÷ïþÓ1þkÿŸ_ï»âû~îñ›yçûêx¥þÞ;{üFæáy¿þú~*·ŽÝÿ§ùŸ¿%ŸïáàÏøKïßý¦cü×ÿ>¿ßwÄöýÜÿâÿ6óÎ/÷ÔñKý¼v÷øŒ?ÍÂóýôüTÿo»þ#Oó?~K?ßÃÁŸð—ß¿ûLÇù¯þ}¾ïˆíû¹ÿÄþmçœ_ï©â—ûxíïñ›…æÿûéø©þÞ;wüFŸæ~ü–¿‡ƒ?á/¿ö™�ó_üúÿ}ß?Û÷sÿˆüÛÏ8¿ßSÅ/öñÛßâ0ÿ7 Íÿ÷ÓñSý¼vïø�?Ìýù,ÿÂ_~ÿí3æ¿ùõþû¾ ·îçÿù·žq¾§Š_íã·¿Äaþn›ÿï§â§ûxíßñ™ûòYþþ ÿ„¾ýÿÚft»âoá‹Í_�ï.µ�áÏ;+Ç ×\mzÿ›uç`iø,ì­·,ØéwØø;­,z¬.IÓü_S'ÝùÏÔO=oÏǺÜhd­¾û½"¿¢Ÿ~_ø+å>rs~Áñ÷u×û^Ì88ÕðŽQØ{>Q™©ÝE�ºÔ[«ÃäOÆõr}eÆõrÏ[ó ­¸ÑÉ[}÷R‘ßÑ‹ÿˆï/|ò›¸ù¿`øÿ¹àN Ì87ÕðŽQØ[>O›©ÝEƒ¹Ô[¬ÃäSÆõr}cÇ5rÍ[ó`­¸ñÉ[}÷R‘ßÙ¿‚ß‚5¾.¼Çå]ùÝ<ûžkÀ9ŸAóNœÙ`õw;í�·0Öæoù§[ó�fÛ YË:G…is1­ÎëË1§¤›®²,šÉe·Ýe,¬£RMI.0½yqüT|œþO]Ñú·ä¬äßñmòûÇöÇö¿a&¿‹w�ÞCµÿ°=ûåï?Ö]Çï^ÃúReQŠÁÕ‹(ãLÛð­ÿs?1¯®¡þ s”¼ ï÷/ò³úýëOì{”%Ñøi?¹�•_×çZcü¡cWà÷ý�¾@-.gú�è$°’wIÁ,`¿ù⯎žVqoàw�ý=Â{WKYkääzºS ûÝb®TÜW—kdÀåœG3&�ÛKçÕæâMu´ô­þŸc÷·�ä߯ﮨáÝ›¥²,˜ðkÈu”ý5£®e¶Y•‘ÅùFø\ŸŠgOe”¶ì�ff&Em§§¿Ñƒ»×Æž„ògŒ×ˆ÷ÇTðþÌÓYDx7r m?Mi+•K-ÉÈã<£ü.OÅsf¶ÊRì�nf,õ¶žžÿFòƾ�ò«¯3ú§Èî àÏÀ3þ÷-8÷>ãšýìZ�–fŸg¡¯"âùù~•áü¿ U¹Ê‹ u©Ÿ m¯¬÷_�‘Ÿ�HÔyQø_xÖÒüÞCá·yÏÆ2e¿êEÖ}ïû}—Ë6D’Û«ì¾%ª“}«ÀÅŠ±E6f�m=þ—].oî#ÉäÏáºãY3wþ%w>GÉ–ëå‹®{¦<�ǶYf–JǬì>3®»©×âCìŽ(2ôû|‰+ët™¹Xõy/øpxÎÒüÝ÷‰�Í‘Åò$ºùcë¾èŽ}¾†Ù%–Y+³°¸Îºíî«/dqA—§ÛOÛt™¹Xªù©øC<|ç×o¹‡ƒ=×Èú‘Mç?]Óýµndzú�'eL¸çÑó«g·µºûE÷ÜyäÏÏÙמf]÷šV-¶*E|u<�øÂó·Å;ö]½ã—>ÇâúèeËŸ°xf½‰×‘kãšè,ÏØs&óSÇcÈ­¾ë!Úß�—í­+t6þG<‚øÛógÆKó²;KÇþooÀ‹#*nwÃ0iØ=~<Õ†› ‡(á·îp8ì9”º8¶µÀÊöWÖè­ûiNùñ¿æ·Œ×çdv�@ó{8ÞsäÍθv 9ÿ�k¹ùü£‡_¹×ñø§ô÷YÒ¸9^ÚúÝ¿‘+~~R¼GÙç[Ë|Z潯ñów0`vgŽ8Y½ãÃöZÝûI™¯á˜Y]ƒÁxí¸ÓV±är­‚²Ö)=¶WéÝé¡-hû�à çñßãOΟ)é�—Ó>5öFçŽlñoÎÁç\“W_uÞf3Û�<úîyÏgã|[s|2V¾°aedå]í»ÙÕ¶´n§B|uù­ä·Ürº—Ç~ÁÙñýŽ-3ðù¯$ÖÇÀ¸f×·L­o5ç3ñî9»¬7Vµº Œ¬šÛm}±]éèÝ…øïóOÉ_¸åu7�]�³ãû o¾áó^G®��ð<¼Oläë¹§8Ÿ�ñÝÍa¾µ­ÐàädäÖ–×ÛÞžŽ³x¹ðqò�å®n¾îâgcpn%—/’~Çï}}ýÂq´\ž•—•êæì[4|‹žñØ0©÷‰ïâÚíöE�VÊÒ®’+o�WŒ?…Û6ù17žaù��²IYzëÇìY22&†ìh$Ä“3³ùÞ–p²"ʺHòq1øÖM—Ye+u+w­½Úñ¿ðÙäVL]×–]ë�Û$µ—€tN4’É4U�L92û'›ià·Kf¬‘äâAÇe¥m¥+m+_ZwSÇÃu“Yqw^XwœCl’V^ÑxÒM$ÑV($Ä“/²9®ž q¤¶jÉN$zjVÚR±fÒµõ¤œ<5ü\�O§å~xyŸØY°RœÎ�ñ×+‹pÏÒ[ȧ³qÛÜË[ü2å|gu¡†èr±µüwŠlqæžµƒaý —É%/¼ñ/Ã�eØ>;t‡à Œkñ6\¾˜ù<‡°wó] “âí»“dî9~fºYà¶Za×2˜Q_öÅ ”ûñËÂß¼LÖß�Ð]7ĸ&^LãlyDxù;Îs·Ç’K&“mιNÛ–çà]‘gÔ·üϺCwþZŠÊ}‰ øçá‹þ'koÀèNœâ|/" ±¶<¢È2wœço—Ù,¸Û^sȲvܳ7éìú–âÝ™÷H®¯ô(¬§Ø•¿‹>øŸá7“ƒø¯Ð½yÒú\ÌlMÞoÔVnaË¢Õemrõ7óÞÃÝ͵çýƒ“¨—y—LI·{=„رOtQ]d~–Sm[BÚÑ€?>^&.~.Nv6>nn<Ø™˜ypÇ“‹—‹“ÐäcdãÍmðÏ�<7ÖË쾕¶ëkZV•¥_Ç'3|<È!ÊÄʆ\l¬\˜¬Ÿ'{.Šx'‚[nŠhfŠêÛ}—R¶ÝmkJÓÑürq±óqò0ó1àËÄË‚\l¬\˜£Ÿ'xîŠ||ˆ%¶è¦‚h®­·Ùu+mÖÖ´­=¼·øð?É{¶œ‹ƒñ|ß»6É¥�Óø/?î¸Ø˜wï:—.?à…5øÔÇú—Å¢þdeM%òM‘}÷V®;yKðqá‘7l7ÜC‹åøçÏò¬’K7½=·‡åeÓ,\OÓ]]• ÜF˜V/«%šk4YYS]uòäÝuÕ­xõåÁï„ÞD]°ßq3—ã§>ʶY,Þu8î!—™÷h±±k¹ë ¨nâtÁ‚±}I,Ó[£Éɖ믗"ë«Z¸qæ¯ááøÊóF|ÎI•Ô3øçÙù¹TÍÊìŸeÓu¶FâjSq<ñòn>�yÕ;Ë·m¿Þö[ÐPrÉ1ã·ô�–{íº8¾Iþ;ºrí–ߨ.àÞLñY6cW…mááÝ…« ?­ffׂsl�~ ù¹QúÒ<-6ãy“|–ÖËm­kgº?~BþŸ5ºžìý§Sϼ‹âØòeÉ�üÚCÃyåºìH¾·Þö\/™eaëk“=”­#ÄÕn77ßOm¶Öµ·ÝÀ þ<Óꋳö�Q7 ò'‹ãß™&?ðOgçvk±#ú´ËÙp¾c“‡¯»*{=i&«o¸È¾úVÛm­koº*>Vþÿ;:Ÿïû¯;+ªü´ãxñéìÄã¹t7ofçgåKÖ�ñÎu¼ÚuM4º §-ÙSsx2ò­­ô�—Ûm—ñ+´:[¸zGyoî^«ì^¨äWÅIâÒv7 ä\/i>=Ô¥Ödãar-vº|œY-º•¶Xíº;­­+JÖ•qDzº{¶ºksgíÞ±ì¯ÞËeeƒQØò«Å=„ï$üuîn�»;mµÒi¶=“×|Ÿ‹qîM±Ò_m»Hø)Ùëaã\Ç—ÛÞ5Yy˜÷Å}²Y}Ö_mÕÆlpÇ-q}ž=Ç9.ÝkxßÐîy?"ÜeÁ�¨Ðqí^vëuµÎÉ’Øq°µº½l9ÙÙy_K,Ž(î¾û«JR•­_c�ñÞAË7:î9Åt[ŽKÈwPàêt<Y›¹Üí3r/¤paëµzè2s³r§’ê[dqYu÷V¾”¥jû=¿å{�âÚ=Ç%ä|¨°u:-³7s¹Ùæä_lpbkõšè2ssr¦’ê[dqÙu÷]ZR”­^û­:§´{§—`ð�ën}Û<óiDÚÎÖœ;‘s¾]±‡?«—.âÚí®ç.,X¿:K£†êYoÛ_J;ã/ÀÇȇ‘Wâì·=c‹ãÇ–Im›‘÷öNo ÛzcK¹0buÎ.ײ>ùt3Vü{³5x83ÖÊÛ÷«~ÚÓ¬>9ü!ùëß·blw]w‰Ð|Fk½eäã“•ÅvõŽ)á³* N½ÃÂÚv» `’·ÁLív¿zÛ[~ógÛZusÇ_„�<ûîì]Žç¯1:‰M¬»þïÉÊâ»jÙÐÛ“'_áám9ý3©µ¾¾ë°0ç­µ·ï6ýµ¤€üCü.%ÞFÉ£ßöÞ‡†x‰×; øæffÓ¸÷í»/#Œmæ¾»lî9Ô&ýÆâMÇðã÷ß§å™Ü:If¾È¾´~²_—>÷eºÀšÏ·îÖ]ö¥›àßá˜øäñ]7/ìÎ1³ó¶°pu—OÈ;ï S™ÕºíäZ�®¯}›Äú'ømtûÛ¶¿^W> |…óWÌEäwBöFqÍ:á\—ë{S’v�{o6âZšðù&ÖãqN³çéôRñ ŸÙ-ù0Ï\šOe`¥¶Y,±¨ù=øWï0¼­ÞyÑüߥ¸î›™ðÞ‹Ëp;/�ó½6îîgŵÒq[ò01øÇ]s< ´Òq-6¢Û$¿&«‘lÖÖ[m’Iÿ“Ÿ…Îöòÿʽçô‡7é~=¦æ|;†bòÌÊä<ëMº¯2âúÛø­ùüc®ù–Úi8ž›SK%¿"«‘lÖÖ[m’Ißœ_Ãßä¿È÷™ú¯%üeåÞ1õö‹cÒ|†ö,=¯Êû;ŽòÎOÙ\Oͱîä×bð¾¡ìVNºÞ»ÉãºÈf¿2,Šþ�ºË ¶Ë,’^mÿšÿç×ûîø�þß»Ÿü@9ëþmçœ_ï©â—ûxíïñçÇù¸^oÿ¾ŸŠŸíã·ÄkŽæ~ü–¿‡ƒ?á/¿ö™�ó_üúÿ}ß?Û÷sÿˆüÛÏ8¿ßSÅ/öñÛßâ0ÿ7 Íÿ÷ÓñSý¼vïø�?Ìýù,ÿÂ_~ÿí3æ¿ùõþû¾ ·îçÿù·žq¾§Š_íã·¿Äaþn›ÿï§â§ûxíßñ™ûòYþþ ÿ„¾ýÿÚf?Íóëý÷|@ÿoÝÏþ óo<âÿ}O¿ÛÇoˆÃüÜ/7ÿßOÅOöñÛ¿â4ÿ3÷ä³ýü<ÿ }ûÿ´Ìšÿç×ûîø�þß»Ÿü@æÞyÅþúž)·ŽÞÿ‡ù¸^oÿ¾ŸŠŸíã·ÄiþgïÉgûøx3þû÷ÿi˜ÿ5ÿϯ÷Ýñý¿w?ø€?ͼó‹ýõuÛöI±ÝKã’Ë­èÊVÛ캔­+O¶•£úEør<ä‚X§‡µüWŠhd²X¥�œöõ·Ç,wRøä²êtu+mö]JV•§ä­_�þ¿”n)¾ÒrŽ7ä„ÚNEÆöúÝþƒs­í.ÿÆØj7Z|Èv:½žL~3ÒL|Ü ìxåŠûkJÙ}”­>Ú'5Åÿ„•ã\v¼ÊÍ4|¾º-Oð®Î9>fW³’}Ãôåš�Ž&Ã#MnÓêÓùà†kàöÖøìºµ¶“FãõßWA¤¯)³U'®£[^GŠl¼�›êáCú^Í6F~6vFªÝ‡Ô¦=óC·Ãí­ö[ukm&wÇ«¾®ƒG^SnªÎO]>²¼ŽÍÙY;7ßrƒô½ºlŒül<éõVì>¥1ºmo²Û«[ic-•¾¶YYm²É+eµ’Ë/¬–[}iO}¶Iu‘]}–ÝöRµ¶ÚÖŸo¥?#î¾»ì?Ð'ÿ'þ\y§æ·mùÕ½‹ã–ƒƒóÜ>»ÇÓê¹ÿ.ìÍW+Æ¿ˆõŸá›*ì°xïPò­D6Ï´ÐM$K:jÝ�u—]K/­Ö[‘ß…O)ü¿ó ´ü‚ëN}ãþ�†s|N§YÎyWbë9>=üW®¸§ØWc… ê®MªŠÙ¶Z)¯‡éæË[ ºÊÝ캷Ylb~Fþ|¥ò÷ËþÑò­yï@èøo6ÄàXú­g9å=‰­äØ÷ñn¼âÜKa]†ƒªù6ª+fÙhå¾§›-n‚ë+w²êÖËaËóMøzüÐùó—‘ù3Ò=›ãà{~»ë¾#�©íNiÚÚN]fˉj§ÁÙO>é^q¦·yd¥`ºÜû¤ºß笲¿cA?Íóëý÷|@ÿoÝÏþ 9þmçœ_ï©â—ûxíïñÑïóp¼ßÿ}??ÛÇnÿˆ×&ÿÌýù,ÿÂ_~ÿí3æ¿ùõþû¾ ·îçÿù·žq¾§Š_íã·¿Äaþn›ÿï§â§ûxíßñ™ûòYþþ ÿ„¾ýÿÚf?Íóëý÷|@ÿoÝÏþ óo<âÿ}O¿ÛÇoˆÃüÜ/7ÿßOÅOöñÛ¿â4ÿ3÷ä³ýü<ÿ }ûÿ´Ìšÿç×ûîø�þß»Ÿü@æÞyÅþúž)·ŽÞÿ‡ù¸^oÿ¾ŸŠŸíã·ÄiþgïÉgûøx3þû÷ÿi˜ÿ5ÿϯ÷Ýñý¿w?ø€?ͼó‹ýõ¿ßwÄöýÜÿâÿ6óÎ/÷ÔñKý¼v÷øŒ?ÍÂóýôüTÿo»þ#Oó?~K?ßÃÁŸð—ß¿ûLÍÇøüøóÅ/1ú'È^Äì¯7¤–ÙòôJ¥*$×€SÉ^·Þw'Ž}ýÔ~Ò;ò/ƒ"k!¥Õ²).¥,®ò¯·=·ÐçÕ\s'W…È{7§{7¯´9›¹²ñ´Ø›žg·|sW“·ÈÀÂÙgA«ƒ;eùÃ�‘-±RêÙ÷R–×ù ×ûžÙè.ðê¾9“¬Âä=—Óý—×ú,ÍÜÙXÚlMÏ2á{®9«ÉÛd`ál³ ÖA�²²ü‹áÇž[b¥Õ²;î¥-®3îž´ìnœíž½ÒO�‹ºç�gÎøf£+k.D¼m§(âÛ]&¾}”ؘ¹ÙpàC—�eÓ]Ilt­m²úú[XWÿšÿç×ûîø�þß»Ÿü@!ÿþmçœ_ï©â—ûxíïñˆGù¸^oÿ¾ŸŠŸíã·Äj¼ßó?~K?ßÃÁŸð—ß¿ûLÇù¯þ}¾ïˆíû¹ÿÄþmçœ_ï©â—ûxíïñ›…æÿûéø©þÞ;wüFŸæ~ü–¿‡ƒ?á/¿ö™�ó_üúÿ}ß?Û÷sÿˆüÛÏ8¿ßSÅ/öñÛßâ0ÿ7 Íÿ÷ÓñSý¼vïø�?Ìýù,ÿÂ_~ÿí3æ¿ùõþû¾ ·îçÿù·žq¾§Š_íã·¿Äaþn›ÿï§â§ûxíßñ™ûòYþþ ÿ„¾ýÿÚf?Íóëý÷|@ÿoÝÏþ óo<âÿ}O¿ÛÇoˆÃüÜ/7ÿßOÅOöñÛ¿â4ÿ3÷ä³ýü<ÿ }ûÿ´Ìšÿç×ûîø�þß»Ÿü@æÞyÅþúž)·ŽÞÿ‡ù¸^oÿ¾ŸŠŸíã·ÄiþgïÉgûøx3þû÷ÿi˜ÿ5ÿϯ÷Ýñý¿w?ø€?ͼó‹ýõ=;£ã³¥ûk®»¯“õ(ÝóÎÏÇæºŒ®­Ýr½Þ«UÓè®ÇÙOË8Wˇa\½}÷RÈ š?§[kõ=Õ­´ïïÄg‚»à?Möw^w#ëŽIºæ�™o3Õåu®ß“î5xú»x¶�GX6rŽ!òâÏûÞ²K½±Á,N¶×ßëZÛNû|Hx'Ûžô÷gu÷pr.¸ä›žiÙVs-^O[mù6ãY�¬·‹ét•ƒa7(â;.,ÿ½ë¯»Û2Çôëm}þµ­´–À÷Æ÷xü^ø‡ØÝßÜ«ªy1åþHrþàÖìºyË÷üf3¿ëŸáxx9ٜӃuþÒ-ì[N¿Ì’X£Ã—ÜyaºÙ®¾ëì�±Î²:ÂíxÃv'Xõ·oqŒ®Úý»7‡fÍF_çü[GÌ8æNN?¿îÙ3iyÃ]&N7Ô»éÉXýñÖµ­µ£ÇsλëþÒã™\;³x?ìN%�$SfqŽsÆôܯ�åM�[«�6FŸ{‡Ÿ¯–l{®­c¾èëu•¯­µ¥^;�õßí.9•Ãû3ƒñÃây²E6gçoOÊøþLÐ{«óé÷¸yúùg‚·Ö¶_Xëu•¯­µ£Ëó^Âû'‰ïøbñ/ϸ7+ÖϦå3šè5<§‰òMFU)nV«Çw¸™ú}Æ·&ÚzID2E}?-µpÿÈÏÕà?pÛ±ÚõT|ûÆ®W‘�=pîà{é9W»k6Eeý!¸àüîýÎmøÑÇuc·O·ÑãÛJ[[iOJÒî6w÷Àƒ}µ÷ý§[cóy4ðd]�wÞ_ÈxT›9欔ÍÚðÎk]ÌßtŠÛ«e¸š�–– iJzzzWÝÇýøð¶>ÿ³ëh9¿�|šx'»î ¼¿�ð«öSMY>û´á¼Ò»™þë—VËq5;-4Ò–úRž•÷pƒÊ¿Ã3ñ]äÆv"ã�aÊü\æ{=¬û�†ïÆ®M�ÄøîÂkµ_£q5Wõg-Ôsn©ãœok#É»�é´Í=•­r)õ$÷q3¼¿ �˜ü»m�HvwP÷¶›¶~ŒÕfålú§°vô»×Ö–iy{ž ‡t_»õyE´¯î2œsîoÓå� ý%ŸÓ]™Õ=өIJ—`ëvm:¿œí/¯åŽ >âÍ÷ Æ­¿ùTÜ’:Wÿ¹Ç~åü:WðßÒYý7Ù]UÝœKm®·>mŸXó�¥nü¶E§ÜÙ¼áxÕ³÷k7#²•ýÏæR=}÷ø:¼¢ã7Õã_•Ý)Ûúúam2öZîÛâüÇ£¹ 0V²jôú x×öíÑrŒèiì»'73E szzÛK.­Ör¯µ~-~Cúc/+œx�Ý÷Å…Óeí¸GÊí>7�¾ž³OÊ:¾þaÇaŠžê}·å[éû¿k™}›ñ¡çÏQdË�̼PîIl‚;æŸc¸¼Ý¡¢Ç†ÊR·K“ÈzÊN]¢Æ²”ºŸl™¹ŸÙŸ¾zõLØüËÅ>â’LqMeiuµ§ÛJ}´«JvZ½ž›.]~ß]�ªÏ‚¾“al±20rᯭié.6TqM}iZ}¶Óò4³e«ÙéòäÀÛë³µYÐ×Òl-–&F\Uõ­=$ÆÊŽ)¬¯­+O¶Ú~G4;#ªûC¦ù4Ü+·ºßžõ_2ÆÇ‹3#‰vGä<“A‰<³ÃTÚO®ÕíbÇšli,¶û¢¥·]Ô¥}m¯§á~áx ý¶Û¯ºÛ,¶ë¯ºê[m¶Ò·]u×WÒÛm¶žµ­Õ­~Ê?í¶ÖêÒÛi[®º´¶Ûm¥k[«ZúR”¥>ÚÖµÛmºë©m´­×]Z[m¶Òµºë«_JR”§ÛZÖ¯Ó……™²ÌÄ×k±2söù0a`àáA.VfnfT¶A�‰‰�’O“““<–Ùv[[ï¾´¥)ZÕ³�[áW—ÝÛ6oSxÇÞÜûU—$qÃÈ8ïWsÎ1K¦ºëbúü§ôEœwËëeÞ—K•e¾–Ö¾¾”¯¦Éu¯†þYw X™]cãwvóMfuöY�¾Óu¯,—ŒÖ²Ö´Ž³r‰uqqìXïö×Òérl·Ò•û~ʶC­¼:ò¿¸"ÅÊë/;¯šks.ŽÈ7º^¶å“q¯YkZGY¹4š¸¸þ5—ûkét¹6[éJ××Ò•tK§¾!~OûÛw«Ñuï‚~K{÷zÛ¶Ú½÷<ëÿPpLÍ}) lÈ�²;wƒuõµÈ¶{n†Ë¶vÉ=¾·GmÔ¶êÓª=5øq¾Eû&úÍϵ]SÐZèîǾêö'aàr=Æ^4Õ¶²_¬Õõ>?acW&ëëXs²õõõüÚÝJþN˜uáùóë°n¤üï«ú7_d°ýZs®w‡È÷Y²VŸR}f³«ñùÖ “Åe}~Žnnk_²µ¥],êOÃÿç¿`ÝIùÖXô~’AY)ιÖ!ÜäcKZ{åÖë:ÇœáßøËÆ¬â]Óܪ4µÆÂÅͧã¸í®òÝ|_G'”ò/§'"å»8ãû+™³ÊËʾµ­n’êÖµw¦·"·],z¬®AªØejë[ï­ô»ø¯¶ÿζ´º”«öwDt—uãA‰Ü=AÖ=§$ãá[Ø\Œs µñMZÝ}5Òïõ™ók®÷Ýî¥Ð]ÖßùÔ­.¥*Å]›Ñ='Ý8ÐâwPõ—hÁ‹¸Øvöã<¾üf­×_nºmö³>m}Þû«u.‚èî¶ÿÎ¥iwÚÇ}¡ÔMÝüS'‚wOWu×op|Ù±ò38ohpž5Ïø¦^F$Ödbϓǹ^³m¨žliâ¶øî¾ÝeöÒ´­+JUË>Úü>ß]Ÿ —iº£˜tÞÒ|©ò²w=KÙ\Ÿik=m»èG£ç¹ÿ‡`bÃu+XìÅÖAKiuiöÛKio5;Gà¯ãŸ²"È¿UÕœŸ©¶™YdO¹êîÁä¸2Öé½µúXú.e“Íxv¾ é”§ºÚ_e—úWí¶•û»]ø~¾S³e¾<žƒãº‹-Ž·Û>ǺzjX¤º—[oѲšžs³ž’V—Vï[¬¶ÏKkùÞ¾”¯ÑÖ||–çÍ$y]'Ç4¶Y¾Ü�ŸouD°ËwºÛ~Œvéù†ÚzIZ][½n²Û=)_Îõô¥~†·à·ä§:kãÊé^9¦²Èë}¹.Ýê©a–ïu¶ý-Ôrí¬ô’´º·zÝe¶zR¿�ëéJún)øNþUù^N6Þï80cRx³ù_pís13e¬¶G÷ÔØËl¥Ô¯ÑÌǾµ­+K©éZWsºÛðØxÝ¥ŽÉ;_¿;�°3#¾—Òέáýg©š”ºÚý,¬=ždìîŠëiZWégCÛJÒêz}»™Öÿ†ßÇ 5–IÚÝ÷Ü\ÿ*;é},áºÎ!Öš©½.¶¿O+gƒÙ+â­¾´¯Ò͆ÿZÒ´ºžž•èÇR~Žž“ŶݣÚÞO÷.ËO%&äzùwàuËîúrG\\�7àÛK¯­×Òÿn,³#ße?£ûkuµéïLü0üiô|ØYúøW3Ýââ}ÖmÏpdîû{ï××××:~;Ø-ï ÄØS×ÖÙpõx¿N´¥l¥µû]êOˆ¿Žþš›7CãG å{x±i�6Óµ§ÜöÇÞï¥+þ­¿GÏö[î'…�ë_ZI‡®ÆöV”­´¶´õto©>#þ<ún\lÝ�\7•íáÆ¦<»^ÕŸqÚÕ˾”¯ú¶ý=Øïxžw­}i&&¿ÙZR¶ÒÚÓÕÖþŠø\ø®ñÊé&ëz2\û¶Úýî.ë³ôYýóÈ´û}T¶Ï®Îã|“½v��¾âÒâdGl¶S[“‹m%¶Ûý=öҴ醓G¤ã:�~ƒ�éõ\E©Æ�U¥Òkñ5Z�f_dXšýn8øxxÑÒ¿›v[m?rމju�· M¢Ök´º}n=˜šíV§ ]­ÀÅŠž‘ca`áÅ..KÉ�—Üþ0u?(Ýí3©²Úrýfƒø ϶y”Žèþ¦Ç°¸Gçö]eß�» 㺴¥kmkJVš…ÝÞøeä]ùù]Áã‡Xr}ÎÓ&ÌÍŸ,ÁÐÓ‡ó½„öYu–Ý™ÏøDÜsšäÙí»í²üúÙZúV´­iOMDîÏü4ò&üì®Þñˬy6çg•nfÇ•àè©ÃùÎ~E¶]e.Íç¼&n9Ìò¬öß_[/ϺÊ×Òµ§­)Zj?‘ÞøWåÔ[;¼“ñw¤û{qµÐÿ%末€è¯ì½~Š—_|xw´õø¸=�Å-ǾK®†MfÓXnºµ²ëkZÕÈ~Þü1žòè¶™�EÚ½áÓ»œÌŠË¯ÄØl8ßfp�D7I[«‹�m§ÐóÈì²¾Û.›‘Ý})O[«}}k^Vö·áÊñ–[³ÌêÎÇî^¤ÛeËï×ádl´=…Ã5VVû®¬6i·:�_.̲Ûn¥¶Ö^C[©JSÖ·WÖµå‡jþù]»,¾­ì~äêm®Tµ¿'c¡ì.«²é.¾°Ù§Üju\·2Ëmº–ÛYyn¥)Oun¯­kÄ®íü'_ý™³—oÖÙCxçuºzàáñ¾·í,n]ÃIÛYn�w³ÆîÎ5Ú<Û2{«}¶Ë>ÿ ¬²”²Øî­o¯9û#ð´y¬Ì­�AåKó}æVÜŽÈã<ã«s)ëºKk‰Æ1»† ý<�̶¿^žû?>´¶¿˜ç÷a~nøÖÏKz§É>£æx¾¶zËØ\g™uœô¥b­ÒzAÆãí¨î­“þm¿Ñ)î³ó«í¯æ4 ° o{ë²=½Uä�Rs,_[?¢öæ=k‘JV*Ý%>‡�¶#­lŸómþ‰O}Ÿ�_m1É~Òü=¡¬ÐæftŸ�¼ ›r¼O\;Ú]Èz¿C÷O£‘~539Ÿì>ߨW"“Û wÖͶ¶]|”¥+m±_¥üÏðï|�ñ|»ñ´�k×� ·RÛsøgoðœI)ZäSße�…ŸÀó©m>�µüèm¯ô[>ϲÿf¢ó€ï‘¾1=Ðé8]ö$t¾–Ó+‡v¯Á‚êWï·Ûo`dp\¯e>�¾¾±Òïè¶}ŸeþÍGåÿŸ#j{¡Óp.¼ìí¾–Ó+ˆv§‚úWëúßm¼û'„eRÊ}}}c¥ßÑmû>Ëýš?Î GÊWÖ[Ÿ äž(v~UÓR*éx7ms]~ÎË+�—5r.—²º›¯4Õ†Ù1¬Š´¦]dú™Ö–ÖÊK|xGwð‹ò—Çò£ÃÎñ#”O4¸ödÛ~“�õ%ŤwÉ,T¶Lî9Ø[\²)t5­a¾Ke¶ÚÛum¥·[ZὯÃOÉv›"Ì\ϹÒÉ ³Ûv«žu&û–]|‘ÒÛó4|ÿc‰Ôº*úÇuô’–Ö—VÚ[uµ®Û|7ü•ir,ÅÌñs�Í,�[‘mÚžuÔûìzGt’GKoÌÑóíŽ$sÒè«ë×ÒJ[Z][imÖÖº¡þm—ÍOùáâøõbi~+~Fá–Xoð¿Èß—Åubëý¼ñVèî­·V)àŽH&޵§æße×Yu>ÚV´ûXÊ_Œ?�Xe’+üEîÚÝ—Çubâ93Å[¬º¶Ö±Í òC4u­>ˬºën§ÛJÖŒi/Æ?ÈRIÞ#÷enŽû㺱q ©£­Ö][kXæ†ù!–ÊÖŸeÖÝu·Sí¥kGÍ¿ðã|ÏÇ}ñÝá^Ê·YuÖ][;ËÆil­m­m­l’>è¾9-­iö]mkmiöÒ¾�K¡ø‚ù/äxóekü:íÌxà›è_nû KŲ.¿Ùlžèq9>çQ—‘¶úDŽË£÷zÛîõ¥iOC¥ø�ùßãË“ƒâwfA3} íÝS�q¼Šßì¶OtX¼‹{«Êžmôþ‰e—Gîõ§»Ö•¥=›â‹äO}¹>(vdÃ/ѾÝÍš9=oö[¬X¼‡w«Éž/môþ‰e—GëëO_ZV”ýXÿ†ëæ—*ÊÉ…ÙÛKëei‘ä‹8wúÒ–ÝëH²û .³Òïç©OmkëO_ZU•õ?ß*ûŒl|ÈüY—*­ÛnáèMfM¶×òY‘­Êí6¸sWÿ(– .§îÒŒ‘­øJù8ÙÃMž4I‡D_V˶]µÑ¸[JþK'ÁŸ²íØãK_ü¦HmºŸ»J2F³á?äÓi6xÕ&}XîÙöÇH`Om+ù,Ÿ~É·e�-ò™!²ê~ï£Öq_Ã+ó+Èv•×íücã\îÓOús•yãþf®²Çtt³ �ðŽËæ[¯¼äRúÖÊýÏèÒ–WÝ%µöÒí‚à߆Óäs–SÝÈ+нcJ}Ou¼×³óöRÖ‘ýßÓÙo\q}×ÍI®öR·ÛOX®÷VÊVÊßžøáçóÿ’ÙK÷y׿ÏÖ±rÞÆÚgÍégÐô¥´à|?šÃ[¥ú·{i[éOXî÷Vß[+vxáÿ‡»ÏÎKo»w?Fõïóþ¶rÞÆÙçËégЭ=”à|GšE[¥ú·{i[í§¬W{«m+enÜ=Gáù%Ì—í·vxU§Ã›#›gawnËgƒ‰t¶S*X0áñò~v\0Ö묊¹±Y%ô¥µ–ÊWÝMÇë�½ٙðRnÜòï‚ñ\«}•®¿®:ÃÏà›Ö;+%”ÜrnQÖ²c{%­Ô¶ï¸Ëî¶ÚV¶Û[«KvÏ€~Þ™�Ý¥ågã™Vý:Ë®àiºæ¸òzÇefŽ=×"åH=²Öê[}p$÷[JV¶Òµ­-Û.øi9þd6MÚ^Tðþ9‘oÓ¬ºþÖÛ®i ¾±ÙYc³qȹ7¾lµº–ß\=ÖÛJÖÚVêÒÝõëŸÁ™ª‡;�göߟ{ †²‘Ã/0â}sãÖ6Ÿ:I¯Ã»ïk°y7no1á�au=™y\fJÍ •õÆŠëÿ¡ôS©¿ �\'#M²ìŽSÞ�Ó›…í»o§Ýó G á[¹)ëî²šÞ Ç´ÜÓ[‹wþS"ºZóÆýuáÜðw†äë6ÿ�÷OpebÛmvz�ï/Õñ.#´–ž¾ïnÐh¹vzÿ;föûééüû}ú¿ðñxAò5{È{Ÿ·ò±m¶»=Nó–ë8—ÚÉO_w·ƒètœ·ÿ_çlÞß}=?Ÿu§? ‡Ä×Ydlçæüs¾ü‰³aVbá÷sçèñtwÇJûåÕW 4½™%óÖ¿�Lé³m§¥=´·íõêßF|{xGãmÚ¬®˜ñ‡¨x†ïG—LíG0ŸŠâr®Á×åR¶Vɱ{™W�ó¸îŠë)[)úGÒ˾ÛiJÖ®�t¿‚žøõ]^GPxåÕ\Kq¥š³êùeü_‘sÌ)½m­/‹°y_éÎoZÛ[)[}v¥µûiéWMºcÁo|{»Y‘Ô>9õ_Üi§ûƯ•߯19;𕲶É?å§9­+eÖR¶úçÖ–×í§¥kWi|ð‡ÃÏ~„Þ8øÅÑ�1µ‹C�Ægå<¬øž‹œmô˜žÚÅ…É9î6²œÓ•{¯²—É&Ë?.i¤üù/ºúÖæã¶Áµí£|OÅxÇ6Ñì8Ç2ãš[ƶÐýßkǹ>Ÿ_¾ÑìñýÔ»èl5;\|¼ Ø}ÖÒ¾Ù#ºßZ~GÆäs�rÝ>åZ-7&ãûHk�³Ñr ^çO±Çº´­`ÏÖl`Ɉµ¥=m’Ë­û?#ãr9ǹnŸ;Žò­›“qý¤5ÇÙhù¯u§ØãÖ´­`ÎÖl Éˆµ¥+í’Ë­õ§ä)à‡&±²a‹#") ž ã²Xg†[+°Í”ºÉ"’Ë«mÖÝJÒ´¯¥\ßî†o�êÉÌØò/8'Üeb}Ö=ŸSdïú‚Ì;éo¥¹Ðñþ¸Ûñ¾#“°¥:é2µÙVï¶J_W>û[âC㻸2'Ïä>1pn7´—°GŸÕóo:žÜ{ýµ¥3-ÓuÖ×�ñ¬¼ÚV¾êß“ƒ?Ô»í¾—UÏîÕø”øñíùò3¹Œœ#�í&Ǭì:ÂmßTÓÿmiLË4Ý{´ã¼k+6•¯º·äàÏõ.ûo¥Õs7¼~>,|‰Šë{3Á¾…�.mÆVÿ;wÖ|bn‹å}¾tÒägfï¹oGgõ×'ßÉ›“=òÍnn^E’Éuoº•»ís?³¿ ljûÜR÷ï{õÎÎùro—øcìíqßú–=f‹®7So¶û¦Ùä]-ž–ÓÙ_ϯ;;ðÛx·»‚Y:¿»{»¯öRÉ‘·”[Ã;C�mñúbË­ÅÐðMÍ#Ç›í¾³læºK?6•¶¿œç‡bþßwpß'Xwgvp Œ²d_[yE¼7±4PY|~˜°âk±t|qm˜ó}·Ömœ÷IgæÒ¶WóÜ£íoÂ=ñ¡Íöœ�w×ÜëÊ.——g�54[�v åÜŒg}Þû1e³ °úç“vãÌŸl“C‘ɾ¬¶Ò¶Y<^´ºÝì?ÂÉÞzÙ©N¨ò³©ù–=mºµ¿°ø?0ëI¬¿ýM첑ñ¼ÞزK>o[½ö×úŸ›ý便üïðÐ÷¾êk/(:Ï–Û[.­‡|”õÝÖßë�í²”ãû.Ð¥ÖV—KëwÙZ{,üßϯÓÓuøj;‹_}?µŸ“½iË,­·Vµç\#”õíößë�í²”Ðl{:—[Z]/­Þ´¯æYù¿Ÿ_§ÎYø1ù>~GùÒíöt¬—êõ<³Ælî9ƒ}+<F þC§ïNS‘lƺêÝ,z˽ÒYJR;i}kf±sÃWò-Ƭšý&Çǎ¬xÑÏ\K³7øO$™7ÁvŸÃΠ³.­¤×Öù-ƒéÝJ[%ÒzÙMqäÿ‡Ï­rÉ©Ût6ºïð‡2ä—îÞþÆï\\¬ˆm¾´†ëà»Çq±ç’?JÝg×¾Ûk_O}Ô§­pÆÃðýü§áMlXÝÇvö]²W'_Ý]36_[ï¶°]n×�k2+-´²—V¶Ç[=.§¥Õ¯­)ˆv_?%Ø3Ù/IqÝÔwCl•ÊÖöÿSEwÖù-®=öî9ާ&³[m”ºµ¶;£ô¾ž—V¾êSì¾ ¾J°g²,^”㻈îŠÙ+•­íï­÷ÛX/·oÌ5Y5–Ûl¥Õ­±Ýg¥Ôôºµ÷R˜«•~=°‡ Qg�œï\(ò¯Ûñ^âØaëñ§¾|ˆ®ÖÍ7á7mvl1Ál·],˜õ²k)l·_I,³ñ[ð ò­uÖÛ_5VRë©JßwvôEm²•¯¥nº–vM÷ÖÛiö×Ò•¯ó)Wà³àÓäÖûì²ïµq[uöÛt·÷JVÈéuiJß}"ì$­–R¾µöÛuÞŸ’•¯Øü6|ü˜ß}–ÝÐ:¸­ºëmºKû‡¥ëdt­iJß}#çòI[l¥}kí¶ë½?%+_±ð1 ·ËVFV6<ÜG¢ðbžxa—7+º´÷âáÇ,–Ù~VM˜ZÌÌ۠Ƕêß}!†Ykm+ì²ë½-®AÑ~/“-¾LÐl8WTñh£‚²Ù™½í¾;‘�‘'ԲϻCo³‘æ[=mº·úÉ‘ûm¯çû½)\‡¥øùÚdKwêž7pý[2·]¡«Ÿ{ýöÛ÷x­ã¸{üªMíº·zß‘ûm¯çzúR¹Mðò´È–î3Õ|r8ᬶeî{CS><÷ûì·îñ[DZ7ÙTš¶Ý[½oŠÈý¶×ó½})\Ï�øA~L§­Ô—¸|$Ã¥´¥ivOgwuÔ¿××Ö–ýÏÇ<ªÒ¶þﺔ£aøïáxóK/+ÞWÞ~0èð/’ϼϡÝv§&΂®‹ßtx9Ý[ű§žË.¿ó>õeµºÚSßéun·:ñßÃiåÖV\ò¾ëñËI¯¾û~ñ‘ Úv_&Ά*ݾèð3úçŠcO-¶]}}µÊ²ÚÝm)îôº·[�x÷á»òã+*yWuxé¤À¾û~¼ú§er\Øb­Ñ{«f\ñli¥¶Ë¯üß¼Ùmn¶”÷z]ZÛ�z—ðpyE¹¾zw¯˜}×1Û4öã]Ô¼7°û¦ùqí·_\ig�˜ÃÐvãÍ5òeRK-º[c¤1V—ßõ¯¤“׿…o…beë²{[ÌNQÈ0=–ß¶ÒuïQêx†]/ºÏÇ×rŽIÍùÄ5²,�¶“K§¥d²žŸJÊ×Ö›qÀ¿ ß ÄËÂÈí,9O ÀöÛvÇQÀº»SÃòé}büøp¹!åüæ­ŽzúÒ[õt­öSÓéÛZúÓmxá£áx™XYŸåw(ßáVÛnØêxWêxŽU·Ö/χ �ò]Í¢ºØç¯­%¿WJße=>�µ¯­7˧ÿ÷ŒšHv÷÷™íÙ™äVíT½?Áúÿ£¡ÂÄú0Ûl9¥ÞC_³È¦E²_Yc“ÚÙu¶}:VÚßF:—ðõ|gu•¶É¾ëŽ}Ý9ñMøûÚìÍô—AtWÒJYv§­­ë~5� õ§¥Öe`ÏeÖý•§Û_]üêÏ�ÿŽ®·²÷]wÍ{c�=™m;O°÷³_eñß肋iøð‹çA_Éty8Yu>ÊÒ¿k~z»à“ã·®mŠý×^s^ÞØA5“A²íÃÞMu—Ǿ–˨à?À3› %Öd`ÍeÖý•¥~×WzðåüDôEÜOg‹ðvÿ.â‘Ö•å½ñÎ9§dÛɧ­nõËå�q•º×ôŽÚOmÞŸJÞ-5=)ZEîõ­zÅÕÑݬÈÓtŸOu‡Rk3-‚™ø�qÁxÏ �gv4tжµãÚÍ|›\«l·í›"²Ëu}k[«ZÖ® u�ItßJk'Óô÷TuÏVjòëÙ¸]{¸çƒa$6R8çØÛ ×`Wc•KiöË?Ô’êýµºµ­jéçYtŸNt®¶}GOõO\õn³+éW7¯¸_âçß ”²9¶4Ðë°+±É¥´ûež²Iu~ÚÝZÖµv¬:�ªzGˆâp—ë½ê.¯ÈËÌÀá=aÂøßâ8Yyò}lì¬N7Å5º�66Flߟ-öCmÒ]öÝZÕ–™=“Ùæ9o áœûS&ƒ�ñ1ÍtRßKåÒòÝ«‘êd¾–Ýe/“]¸ÄÌþúYuië[+_JÖ�?ÉxŸæzÉ4¼ÃŒñþY¦–ïtºžK¦×ou’ÝKn¶—I�´ÆÊžê[uië[+öV¯?Éx—æšËô¼ÇŒñþY§–ê_&§’éµÛÝd—Ò•¶—_�´ÆÊžê[uië[=}*ÿGd¶_¶Y$r[u’G%´¾Ë캕¶ë/²êVÛ­ºÚúV•û+FªrŽy„A¼ð¿Æ+ë|ÔÈ¿/UÒ}}Çv’IH¦‡ÒMÇÐê¶²EíȺ¿N³VÊßKoô÷ÙeÖë'!ðÁ¾Sön|BñºKåŸïåàt×Ò쥗éËk.ÓK¢×쥲¶ÍZÖÛ¥­µº–Ýéî¶ÚÓY¹€~r¨ï³uâ/Ž2_,ßx—/§8“e,¿NX«YvºM»e%•¶jÖ¶Ý-m­Ô¶ïOu¶Öš‰Ì~=üìNA�Ë;Áÿy×*ÙÝKö\›˜øÕÓŸ�l/§¯¥ùÛ�ß ÎØåÝOu~Ù$º¿k]v?ß;9l›'Ä®;öGH­¦»°û—Qm¥×_ë|žÆÂ‚I=o¯çÝmoôô§¯¥)J`=ŸÃgÆ–ÚhçÊñkŽE|qý+mÖsŽ×ÒÃ[iu×úÉ�§ç¸K'­Õüû­­þž”õô¥)L ²ørøÖÚÍdù^.qȯŽ:EmºÞoÚºhkm.ºÿ[ñõó dõº¿Ÿuµ¿ÓÒž¾”¥)‚9_ÃÅG3ËÆÍÛø ãFؘÕÅŠÎ)×®‰|U–ù}Ù8œw7'Ý}iõ¦ŽI©o¥¾ïm)Jyßù…~'¿ÉOÿ_Ÿ’_ã‰ç?æDø¿ÿ&?ý}B�‡œÿ™'ãü™õóùþ5ÚÑþm—·ùá凸õ?æøžÿ%?ý~~IŽ#þdO‹ÿòcÿ×Ñä'øØ?æIøÃÿ&_ý|þAÿ�süÛ/…oò0ÿÂ+Ëñêúx_Ÿc݉ÚÉ+�-³GLÞØï�•·_dŸV”ȳcÚYVeÅ[¾ÊÙ-/²ë6´­¿céa|,üdà_�$/kd»k&Š™½ŸÝ»+.¾9~­¶äE±ì¬¨ó!­Ôôº9i|w[ùµ¥mûO áã7ü{àñƒY%qe²hé›Ù½Õ²²ëì—êÛLˆö=“•\U»ì¬rÒû.·ókJÛö2÷ ø ø‰à8ÚL]‚ÝC�Í·?îi?5윙ç·c~Ò–nó;•òœ¾M…÷™+mq¶Wåã×Ó¶}ÞÛb¦oá¿ÿ¼Ë,Ñøaãöm#Žxí»˜õö£±$öäMlò]t½�'–ùl¾ÚR;î­oŠÏ[,­¶Ö´fž+ñ©àŽÈµ!t&]¶Y4v×”õî�œÉ[g–“_[ææØü†Y$¶úzYu×Vè¬üÛ+mµ­›Šükø ì²=Gˆ�—K,šËkÊzÿKÎdôž[f¾·KÍ äß%·Ûée×V·GoæÙ[m­hØl_‹ï�<:EL�_-º ýñKŠ}6E—ÒúÉmôÉ›‚I‘[ì¾¾¶Ö·zÛéJSÒ”£iú÷¢zC¨ë%ÝQÓ}UÖ7KgÒ–î½ëÎ#Âë,~²]ôä¯Ôkk}žé¯¯¥}ië}›VËpNšê­¶ë:˪zÛ®l¾Ï§}œƒq~#mñû¤»éÝoÕëéužén¯¥~Ï[«üÚ¶S‚ô×Puu.§YõO[uÝ/³éßN Á¸Ç¥ñû¤»éÝM¯_K¬÷M}}+özÝ_æÕ¹|o‹qލÄãüCŽhx®‡ˬÁÒq½F¿G¨Ã²ëï–ë15º¼|\,{.’K®­,²”­×V¿–¬ªÉ,’û  zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆ™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆ™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆ™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆ™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆ™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆ™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆ™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆ™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆ™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆ™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆ™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆ™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆ™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆ™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆ™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆ™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆ™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆ™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆ™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ zòãø¨ù9üž»£õoÉX?É¿âÛä'÷�í�ì ~ÂMï ÿ¼‡kÿ`{÷ËÞ¬»�Þ½‡ô¤Ê£ƒ«QÆ™·á[þæ~bÿ_]Cý@ç)xþßî_ågõûÖŸØ÷(K£ðÒs*¿¯Î´þÇùBƯÁïû;|€þZ\ÏõÐIa$î“‚XÀ=˸¦ƒ�ñNOÁù^é^/̸öëŠrM_Þ³pIh9·'Q¸Àûî·#c‡÷Ívd‘ýX&ŠhýÞë/¶êRê|>OÆô¼Ë�r!É0¿HñÞU£Ûq½ö¿ïxÒï025›L/½àO‹�‹÷¬©,ú�˶{½Ö]mÔ¥iðù7Òó7Èx�$Ãý#ǹV�mÆ÷Úÿ¼eáýÿK¼ÀÈÖm0þ÷�>.v/Þ°r¤³êC,rÙî÷Yu·R•§òž²`›k}ðäE$2Ùîºß|RÙ[$·Ýemºßu—Vž´­+OÜr‹þa_‰ïòSÿ×çä—øâsþdO‹ÿòcÿ×Ñä'øØsþdŸŒ?òeÿ×Ïäø×pãüÛ/…oò0ÿÂ+ËñêÌ+ñ=þJúüü’ÿGüÈŸÿäÇÿ¯£ÈOñ°Ì“ñ‡þL¿úùüƒÿçù¶_ ßäaÿ„W–ãÔÿ˜Wâ{ü”ÿõùù%þ8�ù‘>/ÿÉ�ÿ_G�Ÿã`ÿ™'ãü™õóùþ5Ïól¾¿ÈÃÿ¯,?Ç©ÿ0¯Ä÷ù)ÿëóòKüqó"|_ÿ“þ¾�!?ÆÁÿ2OÆù2ÿëçòükŸæÙ|+‘‡þ^X�Sþa_‰ïòSÿ×çä—øâ?æDø¿ÿ&?ý}B�ƒþdŸŒ?òeÿ×Ïäø×?ͲøVÿ#ü"¼°ÿ§ü¿ßä§ÿ¯ÏÉ/ñÄ̉ñþLúú<„ÿüÉ?äËÿ¯ŸÈ?ñ®›eð­þFøEyaþ=Où…~'¿ÉOÿ_Ÿ’_ãˆÿ™âÿü˜ÿõôy þ6ù’~0ÿÉ—ÿ_?�ã\ÿ6Ëá[üŒ?ðŠòÃüzŸó üO’Ÿþ¿?$¿Çÿ2'Åÿù1ÿëèòüló$üaÿ“/þ¾ ÿƹþm—·ùá凸õnwŠÞø¹á6³˜éücëíg­çùú}Ÿ.Æþö'3ý-›¡ÇÎÅÔÏõ»–ò¹ð>éÊ{}¸×Cdžÿ[éuimi·ž3xkão‡š¾W¥ñË®?µÖ·›çë6œ£ø_Ï9wé<í>>V&º­Î¹G'ÈÂû¾>l¶ûqïŠËýÞ·RêÒ•¦ÝxÑá¿�þjù^›ÇN¹þ×zÞoŸ¬Ùò|oá<忤ó´øùXºéþ·:åž|/»ãæËo·ø¬¿Ýëu.­)ZtoÃß<[ð®¹Sø™ÕÿÚ£€ržk›Ø{Ýð×±9×ß¹ŽÃG ãy›�Ò½•Ëy–ëëix¾ ?w‡&<[~‡¾Øé%ò]~Ø6y³�±ÿÙaxios-axios-aa54a5e/docs/public/sponsors/herodevs.svg000066400000000000000000000200161526660636600231200ustar00rootroot00000000000000 axios-axios-aa54a5e/docs/public/words-light.svg000066400000000000000000000126161526660636600216650ustar00rootroot00000000000000axios-axios-aa54a5e/docs/public/words.svg000066400000000000000000000126161526660636600205600ustar00rootroot00000000000000axios-axios-aa54a5e/docs/scripts/000077500000000000000000000000001526660636600171045ustar00rootroot00000000000000axios-axios-aa54a5e/docs/scripts/process-sponsors.js000066400000000000000000000215471526660636600230150ustar00rootroot00000000000000import fs from 'node:fs'; import axios from 'axios'; import { printSuccessMessage, printErrorMessage, printInfoMessage } from './utils.js'; import { selectLatestSponsorsBySlug } from './selectLatestSponsorsBySlug.js'; /** * Special configuration for processing sponsor data. */ const config = { legacyAgreements: { Stytch: 'gold', Airbnb: 'silver', Descope: 'gold', 'Principal Financial Group': 'gold', }, sponsorsToIgnore: ['axios'], additionalSponsors: [ // Permanent gold sponsorship, managed independently of Open Collective. { name: 'HeroDevs', imageUrl: '/sponsors/herodevs.svg', description: 'Security and long-term support for end-of-life open source software.', tier: 'gold', slug: 'herodevs', website: 'https://www.herodevs.com/', twitter: null, active: true, }, { name: 'Code for Japan', imageUrl: '/sponsors/code-for-japan.jpg', description: null, tier: 'silver', slug: 'code-for-japan', website: 'https://www.code4japan.org/', twitter: 'https://x.com/CodeforJapan', active: true, }, { name: 'superluxuryreps', imageUrl: 'https://images.opencollective.com/superluxuryreps/378b62f/avatar.png', description: 'super luxury reps', tier: 'silver', slug: 'superluxuryreps', website: 'https://superluxuryreps.com/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship', twitter: null, active: true, }, ], }; /** * The GraphQL query to get all sponsors. * * @type {string} */ const getAllSponsorsQuery = ` query Account { account(githubHandle: "https://github.com/axios") { name slug members(role: BACKER, limit: 1000) { totalCount nodes { account { name slug socialLinks { type url createdAt updatedAt } website imageUrl legalName description } tier { name } totalDonations { value currency } since } } } } `; /** * The GraphQL query to get all active sponsors. * * @type {string} */ const getActiveSponsorsQuery = ` query Account { account(githubHandle: "https://github.com/axios") { orders(onlyActiveSubscriptions: true, onlySubscriptions: true, frequency: MONTHLY, status: ACTIVE, limit: 1000) { totalCount nodes { tier { name } fromAccount { id name socialLinks { type url } imageUrl legalName description website slug } amount { currency value } totalDonations { value currency } } } } } `; /** * Gets all sponsors. * * @returns {Promise} The sponsors. */ const getAllSponsors = async () => { printInfoMessage('getting all sponsors...'); const response = await axios.post('https://api.opencollective.com/graphql/v2', { query: getAllSponsorsQuery, }); return response.data.data; }; /** * Gets all active sponsors. * * @returns {Promise} The active sponsors. */ const getActiveSponsors = async () => { printInfoMessage('getting active sponsors...'); const response = await axios.post('https://api.opencollective.com/graphql/v2', { query: getActiveSponsorsQuery, }); return response.data.data; }; /** * Builds a URL with UTM parameters. * * @param {string} url - The URL to build. * @param {boolean} bypass - Whether to bypass UTM parameters. * @returns {string} The URL with UTM parameters. */ const buildLinks = (url, bypass = false) => { if (bypass) { return url; } try { const urlObject = new URL(url); const { searchParams } = urlObject; searchParams.set('utm_source', 'axios_docs_website'); searchParams.set('utm_medium', 'website'); searchParams.set('utm_campaign', 'axios_open_collective_sponsorship'); return urlObject.toString(); } catch { return url; } }; /** * Formats all active sponsors. * * @param {any} sponsorsData - The sponsors data. * @returns {any} The formatted sponsor data. */ const formatActiveSponsorData = (sponsorsData) => { const getSponsorTier = (sponsor) => { if (config.legacyAgreements[sponsor.fromAccount.name]) { return config.legacyAgreements[sponsor.fromAccount.name]; } if (sponsor.tier?.name.toLowerCase() === 'silver sponsor') { return 'silver'; } return sponsor.tier?.name.toLowerCase() || 'backer'; }; const processedData = sponsorsData .map((sponsor) => ({ name: sponsor.fromAccount.name ?? 'Backer', imageUrl: sponsor.fromAccount.imageUrl ?? null, description: sponsor.fromAccount.description ?? null, tier: getSponsorTier(sponsor), slug: sponsor.fromAccount.slug, website: sponsor.fromAccount.website ? buildLinks(sponsor.fromAccount.website) : (buildLinks( sponsor.fromAccount.socialLinks.find((link) => link.type === 'WEBSITE')?.url ) ?? null), twitter: buildLinks(sponsor.fromAccount.socialLinks.find((link) => link.type === 'TWITTER')?.url) ?? null, })) .filter((sponsor) => !config.sponsorsToIgnore.includes(sponsor.name)); return processedData; }; /** * Formats all sponsors irrespective state. * * @param {any} sponsorsData - The sponsors data. * @returns {any} The formatted sponsor data. */ const formatAllSponsorData = (sponsorsData) => { const getSponsorTier = (sponsor) => { if (config.legacyAgreements[sponsor.account.name]) { return config.legacyAgreements[sponsor.account.name]; } if (sponsor.tier?.name.toLowerCase() === 'silver sponsor') { return 'silver'; } return sponsor.tier?.name.toLowerCase() || 'backer'; }; const latestSponsorsBySlug = selectLatestSponsorsBySlug(sponsorsData); const processedData = [...latestSponsorsBySlug.values()] .map((sponsor) => ({ name: sponsor.account.name ?? 'Backer', imageUrl: sponsor.account.imageUrl ?? null, description: sponsor.account.description ?? null, tier: getSponsorTier(sponsor), slug: sponsor.account.slug, website: sponsor.account.website ? buildLinks(sponsor.account.website) : (buildLinks(sponsor.account.socialLinks.find((link) => link.type === 'WEBSITE')?.url) ?? null), twitter: buildLinks(sponsor.account.socialLinks.find((link) => link.type === 'TWITTER')?.url) ?? null, })) .filter((sponsor) => !config.sponsorsToIgnore.includes(sponsor.name)); return processedData; }; /** * The main process. */ const mainProcess = async () => { try { const allSponsors = await getAllSponsors(); const activeSponsors = await getActiveSponsors(); const additionalSponsorSlugs = new Set( config.additionalSponsors.map((sponsor) => sponsor.slug) ); const allSponsorsProcessedData = formatAllSponsorData( allSponsors.account.members.nodes ).filter((sponsor) => !additionalSponsorSlugs.has(sponsor.slug)); const activeSponsorsProcessedData = formatActiveSponsorData( activeSponsors.account.orders.nodes ).filter((sponsor) => !additionalSponsorSlugs.has(sponsor.slug)); const sponsorsByTier = {}; for (const sponsor of allSponsorsProcessedData) { sponsorsByTier[sponsor.tier] ||= []; const isActiveSponsor = activeSponsorsProcessedData.find( (activeSponsor) => activeSponsor.slug === sponsor.slug ); if (isActiveSponsor) { sponsorsByTier[sponsor.tier].push({ ...sponsor, active: true, }); } else { sponsorsByTier[sponsor.tier].push({ ...sponsor, active: false, }); } } for (const sponsor of activeSponsorsProcessedData) { const isSponsorInAllSponsors = allSponsorsProcessedData.find( (allSponsor) => allSponsor.slug === sponsor.slug ); if (!isSponsorInAllSponsors) { if (!sponsorsByTier[sponsor.tier]) { sponsorsByTier[sponsor.tier] ||= []; } sponsorsByTier[sponsor.tier].push({ ...sponsor, active: true, }); } } for (const additionalSponsor of config.additionalSponsors) { if (!sponsorsByTier[additionalSponsor.tier]) { sponsorsByTier[additionalSponsor.tier] ||= []; } sponsorsByTier[additionalSponsor.tier].push({ ...additionalSponsor, active: true, }); } fs.writeFileSync('./data/sponsors.json', JSON.stringify(sponsorsByTier, null, 2)); printSuccessMessage('processed sponsors successfully!'); } catch (_) { printErrorMessage('failed to process sponsors!'); console.error(_); } }; await mainProcess(); axios-axios-aa54a5e/docs/scripts/selectLatestSponsorsBySlug.js000066400000000000000000000021601526660636600247720ustar00rootroot00000000000000/** * Selects the most recent membership for each sponsor slug. * A valid timestamp takes precedence over a missing or invalid timestamp. * When neither timestamp is valid, the first membership is retained. * * @param {any[]} sponsorsData - The sponsor memberships. * @returns {Map} The latest membership keyed by sponsor slug. */ export const selectLatestSponsorsBySlug = (sponsorsData) => sponsorsData.reduce((sponsorsBySlug, sponsor) => { const slug = sponsor.account.slug; const existingSponsor = sponsorsBySlug.get(slug); if (!existingSponsor) { sponsorsBySlug.set(slug, sponsor); return sponsorsBySlug; } const sponsorSince = Date.parse(sponsor.since); const existingSponsorSince = Date.parse(existingSponsor.since); const sponsorSinceIsValid = !Number.isNaN(sponsorSince); const existingSponsorSinceIsValid = !Number.isNaN(existingSponsorSince); if ( sponsorSinceIsValid && (!existingSponsorSinceIsValid || sponsorSince > existingSponsorSince) ) { sponsorsBySlug.set(slug, sponsor); } return sponsorsBySlug; }, new Map()); axios-axios-aa54a5e/docs/scripts/utils.js000066400000000000000000000005221526660636600206010ustar00rootroot00000000000000import chalk from 'chalk'; export const printSuccessMessage = (message) => { console.log(chalk.green('Success:'), `${message}`); }; export const printInfoMessage = (message) => { console.log(chalk.blue('Info:'), `${message}`); }; export const printErrorMessage = (message) => { console.log(chalk.red('Error:'), `${message}`); }; axios-axios-aa54a5e/docs/site.webmanifest000066400000000000000000000005621526660636600206120ustar00rootroot00000000000000{ "name": "axios", "short_name": "axios", "icons": [ { "src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" } ], "theme_color": "#ffffff", "background_color": "#ffffff", "display": "standalone" } axios-axios-aa54a5e/docs/zh/000077500000000000000000000000001526660636600160365ustar00rootroot00000000000000axios-axios-aa54a5e/docs/zh/index.md000066400000000000000000000234531526660636600174760ustar00rootroot00000000000000--- # https://vitepress.dev/reference/default-theme-home-page layout: home search: false hero: name: 'axios 文档' text: 'axios 是一个基于 Promise 的简å�• HTTP 客户端,适用于æµ�览器和 Node.js' image: dark: /logo.svg light: /logo-light.svg alt: axios actions: - theme: brand text: 快速开始 link: /zh/pages/getting-started/first-steps - theme: alt text: API å�‚考 link: /zh/pages/advanced/api-reference features: - title: 简å�•易用 details: axios 上手æž�为简å�•,一行代ç �å�³å�¯å®Œæˆ�åˆ�始化。最基本的 API 请求仅需 2 行代ç �。 - title: 强大的拦截器 details: 创新的拦截器系统让您å�¯ä»¥å®Œå…¨æŽŒæŽ§è¯·æ±‚å’Œå“�应的生命周期,支æŒ�修改请求ã€�å“�应å�Šé”™è¯¯ã€‚ - title: TypeScript 支æŒ� details: axios æ��供完整的类型声明,全é�¢æ”¯æŒ� TypeScript,让您å�¯ä»¥åœ¨ TypeScript 项目中放心使用。 ---

赞助商

{{ sponsor.name }}

{{ capitalizeFirstLetter(sponsor.tier) }}
axios-axios-aa54a5e/docs/zh/pages/000077500000000000000000000000001526660636600171355ustar00rootroot00000000000000axios-axios-aa54a5e/docs/zh/pages/advanced/000077500000000000000000000000001526660636600207025ustar00rootroot00000000000000axios-axios-aa54a5e/docs/zh/pages/advanced/adapters.md000066400000000000000000000070211526660636600230270ustar00rootroot00000000000000# 适�器 适�器�许你自定义 axios 处�请求数�的方�。默认情况下,axios 使用 `['xhr', 'http', 'fetch']` 的有�优先级列表,并选择当�环境支�的第一个适�器。实际上,这�味�在�览器中使用 `xhr`,在 Node.js 中使用 `http`,在两者���用的环境(如 Cloudflare Workers 或 Deno)中使用 `fetch`。 编写自定义适�器�以让你完全掌控 axios 如何�起请求和处��应,适用于测试�自定义传输或�标准环境等场景。 ## 内置适�器 �以通过 `adapter` �置选项按�称选择内置适�器: ```js // 使用 fetch 适�器 const instance = axios.create({ adapter: "fetch" }); // 使用 XHR 适�器(�览器默认) const instance = axios.create({ adapter: "xhr" }); // 使用 HTTP 适�器(Node.js 默认) const instance = axios.create({ adapter: "http" }); ``` 你也�以传入一个适�器�称数组,axios 将使用当�环境支�的第一个: ```js const instance = axios.create({ adapter: ["fetch", "xhr", "http"] }); ``` 关于 `fetch` 适�器的更多详情,请�阅 [Fetch 适�器](/pages/advanced/fetch-adapter)页�。 ## 创建自定义适�器 �创建自定义适�器,需�编写一个接� `config` 对象并返回 Promise 的函数,该 Promise 需解�为有效的 axios �应对象。 ```js import axios from "axios"; import { settle } from "axios/unsafe/core/settle.js"; function myAdapter(config) { /** * 到此时: * - config 已与默认�置�并 * - 请求转�器已执行 * - 请求拦截器已执行 * * 适�器现在负责�起请求 * 并返回有效的�应对象。 */ return new Promise((resolve, reject) => { // 在此执行自定义请求逻辑。 // 本示例以原生 fetch API 为起点。 fetch(config.url, { method: config.method?.toUpperCase() ?? "GET", headers: config.headers?.toJSON() ?? {}, body: config.data, signal: config.signal, }) .then(async (fetchResponse) => { const responseData = await fetchResponse.text(); const response = { data: responseData, status: fetchResponse.status, statusText: fetchResponse.statusText, headers: Object.fromEntries(fetchResponse.headers.entries()), config, request: null, }; // settle 根� HTTP 状��决定是 resolve 还是 reject settle(resolve, reject, response); /** * 到此�: * - �应转�器将执行 * - �应拦截器将执行 */ }) .catch(reject); }); } const instance = axios.create({ adapter: myAdapter }); ``` TypeScript 适�器�以使用相应的泛型,在�应�置中�时�留请求数�和查询�数: ```ts import type { AxiosPromise, InternalAxiosRequestConfig, } from "axios"; interface RequestBody { includeArchived: boolean; } interface SearchParams { query: string; } interface SearchResponse { results: string[]; } const searchAdapter = ( config: InternalAxiosRequestConfig ): AxiosPromise => Promise.resolve({ data: { results: [] }, status: 200, statusText: "OK", headers: {}, config, }); ``` ::: tip `settle` 辅助函数对 2xx 状�� resolve Promise,对其他状�� reject Promise,与 axios 的默认行为一致。如果需�自定义状��验�,请改用 `validateStatus` �置选项。 ::: axios-axios-aa54a5e/docs/zh/pages/advanced/api-reference.md000066400000000000000000000274221526660636600237400ustar00rootroot00000000000000# API �考 以下是 axios 包中所有�用函数和类的列表。这些函数�在你的项目中使用和导入。所有函数和类��我们�循语义化版本的承诺�护,�在未�布主版本�更的情况下,这些 API 将��稳定��。 ## 实例 `axios` 实例是你用于�起 HTTP 请求的主�对象,它是一个创建 `Axios` 类新实例的工厂函数。`axios` 实例�供了多个请求方法,详�文档的[请求别�](/pages/advanced/request-method-aliases)章节。 ## TypeScript 请求类型 公共请求类型使用��的泛型分别表示请求数�和查询�数: ```ts AxiosRequestConfig RawAxiosRequestConfig InternalAxiosRequestConfig AxiosDefaults CreateAxiosDefaults AxiosResponse AxiosPromise AxiosError CanceledError ``` `D` 是请求体类型,`P` 是查询�数类型。`AxiosResponse`�`AxiosPromise`�错误�默认�置��调用实例�请求别��适�器和 `mergeConfig()` 都会在请求�置中�留这两�类型。自定义�数�列化器会接收�一个 `P`。 请求方法使用 `` 的泛型顺�,将 `P` 添加在最�,因此现有的显�泛型�数��兼容。未�供自定义�应类型 `R` 时,默认 `AxiosResponse` 会在 `response.config` 中�留 `D` 和 `P`;显��供的 `R` �然控制最终返回值。为����兼容,请求数�和�数泛型�默认为 `any`。 ## 类 ### `Axios` `Axios` 类是�起 HTTP 请求的核心类,是一个创建 `Axios` 类新实例的工厂函数。该类�供多个 HTTP 请求方法,详�文档的[请求别�](/pages/advanced/request-method-aliases)章节。 #### `constructor` 创建一个新的 `Axios` 实例,构造函数接�一个�选的�置对象作为�数。 ```ts constructor(instanceConfig?: AxiosRequestConfig); ``` #### `request` 处�请求调用和�应解�,是�起 HTTP 请求的核心方法。接�一个�置对象作为�数,返回一个解�为�应对象的 Promise。 ```ts request(config: AxiosRequestConfig): Promise; ``` ### `CancelToken` `CancelToken` 类基于 `tc39/proposal-cancelable-promises` �案,用于创建��消 HTTP 请求的令牌。该类现已废弃,推�使用 `AbortController` API。 从 0.22.0 版本起,`CancelToken` 类已废弃,将在未�版本中移除。建议改用 `AbortController` API。 该类主�为了��兼容而�留导出,未�将被移除。我们强烈�建议在新项目中使用;下�的旧版互�作辅助方法仅为已有代�列出。 这些旧版方法�为现有集��供类型: ```ts subscribe(listener: (cancel: Cancel | any) => void): void; unsubscribe(listener: (cancel: Cancel | any) => void): void; toAbortSignal(): AbortSignal; ``` ## 函数 ### `AxiosError` `AxiosError` 类是 HTTP 请求失败时抛出的错误类,继承自 `Error` 类并添加了�外属性。 #### `constructor` 创建一个新的 `AxiosError` 实例,构造函数接��选的 message�code�config�request 和 response 作为�数。 ```ts constructor(message?: string, code?: string, config?: InternalAxiosRequestConfig, request?: any, response?: AxiosResponse); ``` #### `properties` `AxiosError` 类�供以下属性: ```ts // �置实例。 config?: InternalAxiosRequestConfig; // 错误代�。 code?: string; // 请求实例。 request?: any; // �应实例。 response?: AxiosResponse; // 表示该错误是�为 AxiosError 的布尔值。 isAxiosError: boolean; // 错误状��。 status?: number; // 将错误转�为 JSON 对象的辅助方法。 toJSON: () => object; // 错误原因。 cause?: Error; ``` ### `AxiosHeaders` `AxiosHeaders` 类是用于管� HTTP 请求头的工具类,�供添加�删除和获�请求头等�作方法。 此处仅列出主�方法,完整方法列表请�阅类型声明文件。 #### `constructor` 创建一个新的 `AxiosHeaders` 实例,构造函数接�一个�选的请求头对象作为�数。 ```ts constructor(headers?: RawAxiosHeaders | AxiosHeaders | string); ``` #### `set` �请求头对象添加一个请求头。 空字符串或仅包�空白字符的请求头�称会被忽略。 ```ts set(headerName?: string, value?: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders; set(headers?: RawAxiosHeaders | AxiosHeaders | string, rewrite?: boolean): AxiosHeaders; set(headers?: Iterable<[string, AxiosHeaderValue]>, rewrite?: boolean): AxiosHeaders; ``` #### `get` 从请求头对象获�一个请求头。 ```ts get(headerName: string, parser: typeof AxiosHeaders.parseParameters): AxiosHeaderParameters; get(headerName: string, parser: RegExp): RegExpExecArray | null; get(headerName: string, matcher?: true | AxiosHeaderParser): AxiosHeaderValue; ``` 传入 `AxiosHeaders.parseParameters` �将规范化的 HTTP �数解�为安全的�原型为 null 的映射: ```js const headers = new AxiosHeaders({ "Content-Type": 'multipart/form-data; boundary="a,b"', }); console.log({ ...headers.get("Content-Type", AxiosHeaders.parseParameters), }); // { boundary: "a,b" } ``` �数�称�区分大�写。解�器会移除带引�字符串的定界引�,解�转义的引�和�斜�,�留带引�值中的逗�和分�,并且�移除�带引�值两侧的 RFC �选空白。它会忽略 `__proto__`�`constructor` 和 `prototype`。`get(name, true)` �是旧版分�器。 #### `has` 检查请求头对象中是�存在�个请求头。 ```ts has(header: string, matcher?: AxiosHeaderMatcher): boolean; ``` #### `delete` 从请求头对象移除一个请求头。 ```ts delete(header: string | string[], matcher?: AxiosHeaderMatcher): boolean; ``` #### `clear` 从请求头对象移除所有请求头。 ```ts clear(matcher?: AxiosHeaderMatcher): boolean; ``` #### `normalize` 规范化请求头对象。 ```ts normalize(format: boolean): AxiosHeaders; ``` #### `concat` �并多个请求头对象。 ```ts concat(...targets: Array): AxiosHeaders; ``` #### `toJSON` 将请求头对象转�为 JSON 对象。 ```ts toJSON(asStrings: true): Record; toJSON(asStrings?: false): Record; ``` #### `toString` 将请求头返回为�� CRLF 的 HTTP 请求头�,�行一个 `name: value` 键值对。 ```ts toString(): string; ``` ### `CanceledError` `CanceledError` 类是 HTTP 请求被�消时抛出的错误类,继承自 `AxiosError` 类。 ```ts constructor(message?: string, config?: InternalAxiosRequestConfig, request?: any); __CANCEL__?: boolean; ``` ### `Cancel` `Cancel` 类是 `CanceledError` 类的别�,为��兼容而�留导出,将在未�版本中移除。 ```ts Cancel: typeof CanceledError; ``` ### `isCancel` 检查�个错误是�为 `CanceledError` 的函数,�用于区分主动�消和�外错误。 ```ts isCancel(value: any): value is CanceledError; ``` ```js import axios from 'axios'; const controller = new AbortController(); axios.get('/api/data', { signal: controller.signal }).catch((error) => { if (axios.isCancel(error)) { console.log('Request was cancelled:', error.message); } else { console.error('Unexpected error:', error); } }); controller.abort('User navigated away'); ``` ### `isAxiosError` 检查�个错误是�为 `AxiosError` 的函数。在 `catch` �中使用此函数,�安全访问 axios 特有的错误属性,如 `error.response` 和 `error.config`。 ```ts isAxiosError(value: any): value is AxiosError; ``` ```js import axios from 'axios'; try { await axios.get('/api/resource'); } catch (error) { if (axios.isAxiosError(error)) { // error.response�error.config�error.code ��使用 console.error('HTTP error', error.response?.status, error.message); } else { // � axios 错误(例如编程错误) throw error; } } ``` ### `all` `all` 函数接�一组 Promise 并返回一个在所有 Promise 都完���完�的�一 Promise,现已废弃,推�使用 `Promise.all` 方法。 从 0.22.0 版本起,`all` 函数已废弃,将在未�版本中移除。建议改用 `Promise.all` 方法。 ### `spread` `spread` 函数�将一个�数数组展开为函数调用的多个�数,在你需�将数组�数传递给接收多个�数的函数时�常实用。 ```ts spread(callback: (...args: T[]) => R): (array: T[]) => R; ``` ### `toFormData` 将普通 JavaScript 对象(包括嵌套对象)转�为 `FormData` 实例,在需�从对象中以编程方�构建 multipart 表�数�时�常实用。 ```ts toFormData(sourceObj: object, formData?: FormData, options?: FormSerializerOptions): FormData; ``` ```js import { toFormData } from 'axios'; const data = { name: 'Jay', avatar: fileBlob }; const form = toFormData(data); // form 现在是一个�直接��的 FormData 实例 await axios.post('/api/users', form); ``` ### `formToJSON` 将 `FormData` 实例转�回普通 JavaScript 对象,在需�以结构化格�读�表�数�时�常实用。 �有点�和方括�表示法具有结构�义:`.`�`[` 和 `]` 会分隔路径,而 `-`�空格�`+`�`*` 和 `&` 会�留在字�键中。`foo.bar` 和 `foo[bar]` 会创建嵌套对象,`foo[]` 会创建数组。 ```ts formToJSON(form: FormData): object; ``` ```js import { formToJSON } from 'axios'; const form = new FormData(); form.append('user-name', 'johndoe'); form.append('user.name', 'john'); const obj = formToJSON(form); console.log(obj); // { "user-name": "johndoe", user: { name: "john" } } ``` ### `getAdapter` 通过�称或�称数组解�并返回一个适�器函数。axios 在内部使用此函数为当�环境选择最�适的适�器。 ```ts getAdapter(adapters: string | string[]): AxiosAdapter; ``` ```js import { getAdapter } from 'axios'; // 显�获� fetch 适�器 const fetchAdapter = getAdapter('fetch'); // 按优先级列表获�最�适的适�器 const adapter = getAdapter(['fetch', 'xhr', 'http']); ``` ### `mergeConfig` �并两个 axios �置对象,使用与 axios 内部�并默认�置和请求级选项相�的深度�并策略。�者的值优先级更高。 ```ts mergeConfig( config1: AxiosRequestConfig, config2: AxiosRequestConfig ): AxiosRequestConfig; ``` ```js import { mergeConfig } from 'axios'; const base = { baseURL: 'https://api.example.com', timeout: 5000 }; const override = { timeout: 10000, headers: { 'X-Custom': 'value' } }; const merged = mergeConfig(base, override); // { baseURL: "https://api.example.com", timeout: 10000, headers: { "X-Custom": "value" } } ``` ## 常� ### `HttpStatusCode` 包� HTTP 状��命�常�的对象,�用于编写更具�读性的�件判断,��直接使用数字字��。 ```js import axios, { HttpStatusCode } from 'axios'; try { const response = await axios.get('/api/resource'); } catch (error) { if (axios.isAxiosError(error)) { if (error.response?.status === HttpStatusCode.NotFound) { console.error('Resource not found'); } else if (error.response?.status === HttpStatusCode.Unauthorized) { console.error('Authentication required'); } } } ``` ## 其他 ### `VERSION` `axios` 包的当�版本�字符串,��次�布更新。 axios-axios-aa54a5e/docs/zh/pages/advanced/authentication.md000066400000000000000000000103541526660636600242460ustar00rootroot00000000000000# 认� 大多数 API 都需���形�的认�。本页介�在 axios 请求中附加凭�的最常�模�。 ## Bearer 令牌(JWT) 最常�的方�是在 `Authorization` 请求头中附加 JWT。最简�的�法是通过 axios 实例上的请求拦截器实现,这样令牌会在�次请求时实时读�: ```js import axios from "axios"; const api = axios.create({ baseURL: "https://api.example.com" }); api.interceptors.request.use((config) => { const token = localStorage.getItem("access_token"); if (token) { config.headers.set("Authorization", `Bearer ${token}`); } return config; }); ``` ## HTTP Basic 认� 对于使用 HTTP Basic 认�的 API,传入 `auth` 选项��。axios 会自动对凭�进行编�并设置 `Authorization` 请求头: ```js const response = await axios.get("https://api.example.com/data", { auth: { username: "myUser", password: "myPassword", }, }); ``` 如果未�供 `auth`,Node.js HTTP 和 fetch 适�器也�以从请求 URL 中�� Basic 认�凭�,例如 `https://myUser:myPassword@api.example.com/data`。URL 中�过百分�编�的凭�会在生� `Authorization` 请求头�解�。新代�建议优先使用显�的 `auth` 选项;它会优先于 URL 中的凭�。 ::: tip 对于 Bearer 令牌和 API 密钥,请使用自定义 `Authorization` 请求头,而� `auth` 选项——`auth` 仅适用于 HTTP Basic 认�。 ::: ## API 密钥 API 密钥通常作为请求头或查询�数传递,具体�决于 API 的�求: ```js // 作为请求头 const api = axios.create({ baseURL: "https://api.example.com", headers: { "X-API-Key": "your-api-key-here" }, }); // 作为查询�数 const response = await axios.get("https://api.example.com/data", { params: { apiKey: "your-api-key-here" }, }); ``` ## 令牌刷新 当访问令牌过期时,你需��默刷新它并�新��失败的请求。�应拦截器是实现此逻辑的�适�置: ```js import axios from "axios"; const api = axios.create({ baseURL: "https://api.example.com" }); // 跟踪是�已有刷新正在进行,以��并行�起多个刷新请求 let isRefreshing = false; let failedQueue = []; const processQueue = (error, token = null) => { failedQueue.forEach((prom) => { if (error) { prom.reject(error); } else { prom.resolve(token); } }); failedQueue = []; }; api.interceptors.response.use( (response) => response, async (error) => { const originalRequest = error.config; if (error.response?.status === 401 && !originalRequest._retry) { if (isRefreshing) { // 将请求加入队列,等待刷新完� return new Promise((resolve, reject) => { failedQueue.push({ resolve, reject }); }) .then((token) => { originalRequest.headers["Authorization"] = `Bearer ${token}`; return api(originalRequest); }) .catch((err) => Promise.reject(err)); } originalRequest._retry = true; isRefreshing = true; try { const { data } = await axios.post("/auth/refresh", { refreshToken: localStorage.getItem("refresh_token"), }); const newToken = data.access_token; localStorage.setItem("access_token", newToken); api.defaults.headers.common["Authorization"] = `Bearer ${newToken}`; processQueue(null, newToken); return api(originalRequest); } catch (refreshError) { processQueue(refreshError, null); // 跳转到登录页或触�事件 localStorage.removeItem("access_token"); window.location.href = "/login"; return Promise.reject(refreshError); } finally { isRefreshing = false; } } return Promise.reject(error); } ); ``` ## 基于 Cookie 的认� 对于�赖 Cookie 的会� API,设置 `withCredentials: true` 以在跨域请求中�带 Cookie: ```js const api = axios.create({ baseURL: "https://api.example.com", withCredentials: true, // �次请求��带 Cookie }); ``` ::: warning `withCredentials: true` �求�务器�应时�带 `Access-Control-Allow-Credentials: true`,且 `Access-Control-Allow-Origin` 必须为具体域�(�能是通�符)。 ::: axios-axios-aa54a5e/docs/zh/pages/advanced/cancellation.md000066400000000000000000000060251526660636600236630ustar00rootroot00000000000000# �消请求 从 v0.22.0 起,axios 支�使用 AbortController 以简�的方��消请求。该功能在�览器和 Node.js(使用支� AbortController 的 axios 版本)中��使用。��消请求,需�创建一个 `AbortController` 实例,并将其 `signal` 传入请求的 `signal` 选项。 ```js const controller = new AbortController(); axios .get('/foo/bar', { signal: controller.signal, }) .then(function (response) { //... }); // �消请求 controller.abort(); ``` ## CancelToken 你也�以使用 `CancelToken` API ��消请求。该 API 已废弃,将在下一个主版本中移除,建议改用 `AbortController`。�以使用 `CancelToken.source` 工厂方法创建�消令牌,如下所示: ```js const CancelToken = axios.CancelToken; const source = CancelToken.source(); axios .get('/user/12345', { cancelToken: source.token, }) .catch(function (thrown) { if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // 处�错误 } }); axios.post( '/user/12345', { name: 'new name', }, { cancelToken: source.token, } ); // �消请求(message �数�选) source.cancel('Operation canceled by the user.'); ``` 也�以通过� `CancelToken` 构造函数传入执行函数�创建�消令牌: ```js const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { // 执行函数接收一个 cancel 函数作为�数 cancel = c; }), }); // �消请求 cancel(); ``` `CancelToken` 还为旧版集�暴露了一些底层辅助方法: ```js const source = axios.CancelToken.source(); const listener = (cancel) => { console.log(cancel.message); }; source.token.subscribe(listener); const signal = source.token.toAbortSignal(); // 将 `signal` 传给接� AbortSignal 的 API。 source.cancel('Operation canceled by the user.'); source.token.unsubscribe(listener); ``` 被�消的请求会以 `axios.CanceledError` 拒�。旧版导出 `axios.Cancel` 是 `axios.CanceledError` 的别�,�消错误还包� `__CANCEL__`,用于兼容 `axios.isCancel`。 在 TypeScript 中,`isCancel()` 在收窄 `unknown` 错误类型时,会�留�应数��请求数�和查询�数的类型: ```ts interface SearchResponse { results: string[]; } interface RequestBody { includeArchived: boolean; } interface SearchParams { query: string; } try { await axios.get("/search"); } catch (error) { if (axios.isCancel(error)) { error.response?.data; // SearchResponse | undefined error.config?.data; // RequestBody | undefined error.config?.params; // SearchParams | undefined } } ``` 你�以使用�一个�消令牌或 AbortController �消多个请求。如果在 axios 请求开始时�消令牌已处于已�消状�,则请求会立�被�消,�会�试�起实际的网络请求。 axios-axios-aa54a5e/docs/zh/pages/advanced/config-defaults.md000066400000000000000000000043541526660636600243040ustar00rootroot00000000000000# �置默认值 axios �许你指定应用于�个请求的�置默认值,包括 `baseURL`�`headers`�`timeout` 等属性。下�是使用�置默认值的示例: ```js axios.defaults.baseURL = "https://jsonplaceholder.typicode.com/posts"; axios.defaults.headers.common["Authorization"] = AUTH_TOKEN; axios.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded"; ``` ::: warning 全局请求头会被��到所有主机 如果你的应用会与多个域�通信,设置 `axios.defaults.headers.common["Authorization"]` 会将令牌��到**所有**域�,包括你�能无法控制的第三方 API。对任何�带凭�的客户端,请使用带有�定 `baseURL` 的[自定义实例](#自定义实例默认值)。 ::: ## 自定义实例默认值 axios 实例在创建时会有自己的默认�置,这些默认�置�以通过修改实例的 `defaults` 属性�覆盖。下�是使用自定义实例默认值的示例: ```js var instance = axios.create({ baseURL: "https://jsonplaceholder.typicode.com/posts", timeout: 1000, headers: { Authorization: "foobar" }, }); instance.defaults.headers.common["Authorization"] = AUTH_TOKEN; ``` ## �置优先级 �置将按照优先级顺��并,�次为:库的默认值�实例的默认属性,最�是请求时传入的�置�数。下�通过示例说明优先级顺�。 �些选项是请求专属的,�会从请求�置中读�。`data` 就属于这类选项:axios �会从全局或实例默认值继承请求体,也�会深度�并请求体。如果�个请求都需�共享的请求体字段,请使用请求拦截器或 `transformRequest` 添加,并谨慎�定作用范围,��把�感值��到错误的端点。 首先,创建一个使用库�供的默认值的实例。此时 timeout �置值为 `0`,这是库的默认值。 ```js const instance = axios.create(); ``` 接下�,将实例的 timeout 默认值覆盖为 `2500` 毫秒。此�,使用该实例的所有请求都将在 2.5 秒�超时。 ```js instance.defaults.timeout = 2500; ``` 最�,�起一个 timeout 为 `5000` 毫秒的请求,该请求将在 5 秒�超时。 ```js instance.get("/longRequest", { timeout: 5000, }); ``` axios-axios-aa54a5e/docs/zh/pages/advanced/create-an-instance.md000066400000000000000000000050511526660636600246660ustar00rootroot00000000000000# 创建实例 `axios.create()` �许你创建一个预�置的 axios 实例。该实例与默认 `axios` 对象拥有相�的请求和�应 API,但会将你�供的�置作为�次请求的基础�置。对于任何超过�文件规模的应用,这是使用 axios 的推�方�。 ```ts import axios from "axios"; const instance = axios.create({ baseURL: "https://api.example.com", timeout: 5000, headers: { "X-Custom-Header": "foobar" }, }); ``` `create` 方法接�完整的[请求�置](/pages/advanced/request-config)对象。之��以�使用默认 axios 对象一样使用该实例: ```js const response = await instance.get("/users/1"); ``` ## 为什么�使用实例? ### 按�务设置 baseURL 在大多数应用中,你需�与多个 API 通信。为�个�务创建独立的实例,�以��在�次调用时��指定 baseURL: ```js const githubApi = axios.create({ baseURL: "https://api.github.com" }); const internalApi = axios.create({ baseURL: "https://api.internal.example.com" }); const { data: repos } = await githubApi.get("/users/axios/repos"); const { data: users } = await internalApi.get("/users"); ``` ### 共享认�请求头 在一个实例上附加认�令牌,让该实例的所有请求自动�带,而�影�其他实例: ```js const authApi = axios.create({ baseURL: "https://api.example.com", headers: { Authorization: `Bearer ${getToken()}`, }, }); ``` ### 按�务设置超时与�试 ���务有��的��性特�。�以为实时�务设置较短的超时,为批处�任务设置较长的超时: ```js const realtimeApi = axios.create({ baseURL: "https://realtime.example.com", timeout: 2000 }); const batchApi = axios.create({ baseURL: "https://batch.example.com", timeout: 60000 }); ``` ### 隔离的拦截器 添加到实例上的拦截器�对该实例生效,有助于��关注点分离: ```js const loggingApi = axios.create({ baseURL: "https://api.example.com" }); loggingApi.interceptors.request.use((config) => { console.log(`→ ${config.method?.toUpperCase()} ${config.url}`); return config; }); ``` ## 按请求覆盖默认�置 在请求时传入的�置始终会覆盖实例默认�置: ```js const api = axios.create({ timeout: 5000 }); // 这个特定请求使用 30 秒超时 await api.get("/slow-endpoint", { timeout: 30000 }); ``` ::: tip 实例默认�置也�以在创建�通过修改 `instance.defaults` �更改: ```js instance.defaults.headers.common["Authorization"] = `Bearer ${newToken}`; ``` ::: axios-axios-aa54a5e/docs/zh/pages/advanced/error-handling.md000066400000000000000000000174011526660636600241420ustar00rootroot00000000000000# 错误处� axios �能会抛出多���类型的错误,有些�自 axios 本身,有些�自�务器或客户端。下表列出了所抛出错误的基本结构: | 属性 | 说明 | | ------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | message | 错误信�的简�摘�,以�失败时的状��。 | | name | 定义错误的��,对于 axios �说始终是 `AxiosError`。 | | stack | �供错误的堆栈跟踪。 | | config | 包�用户在�起请求时定义的特定实例�置的 axios �置对象。 | | code | 表示 axios 内部识别的错误,下表列出了 axios 内部错误的具体说明。 | | status | HTTP �应状��。常� HTTP �应状��的�义请�阅[此处](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes)。 | 以下是 axios 内部�能出现的错误列表: | 错误代� | 说明 | | ------------------------- | --------------------------------------------------------------------------------------- | | ERR_BAD_OPTION_VALUE | axios �置中�供了无效或�支�的值。 | | ERR_BAD_OPTION | axios �置中�供了无效选项。 | | ECONNABORTED | 通常表示请求已超时(除�设置了 `transitional.clarifyTimeoutError`)或被�览器或其�件中止。 | | ETIMEDOUT | 请求因超过 axios 默认时�而超时。必须将 `transitional.clarifyTimeoutError` 设置为 `true`,�则会抛出通用的 `ECONNABORTED` 错误。 | | ERR_NETWORK | 网络相关问题。在�览器中,此错误也�能由 [CORS](https://developer.mozilla.org/ru/docs/Web/HTTP/Guides/CORS) 或[混�内容](https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content)策略�规引起。出于安全考虑,�览器��许 JS 代�获知错误的真实原因,请检查控制�。 | | ERR_FR_TOO_MANY_REDIRECTS | 请求�定�次数过多,超过了 axios �置中指定的最大�定�次数。 | | ERR_DEPRECATED | 使用了 axios 中已废弃的功能或方法。 | | ERR_BAD_RESPONSE | �应无法正确解�或格�异常,通常与 `5xx` 状��的�应有关。 | | ERR_BAD_REQUEST | 请求格�异常或缺少必��数,通常与 `4xx` 状��的�应有关。 | | ERR_CANCELED | 功能或方法被用户通过 AbortSignal(或 CancelToken)显��消。 | | ERR_NOT_SUPPORT | 当� axios 环境�支�该功能或方法。 | | ERR_INVALID_URL | axios 请求�供了无效的 URL。 | | ERR_FORM_DATA_DEPTH_EXCEEDED | 在�列化 `params` 或表�数�时,对象超过了�置的 `maxDepth`,默认上�为 100 层。详� [`paramsSerializer`](/pages/advanced/request-config#paramsserializer) 和 [`formSerializer`](/pages/advanced/request-config#formserializer)。 | ## 处�错误 axios 的默认行为是在请求失败时 reject Promise。�过,你也�以�获错误并按需处�。以下是�获错误的示例: ```js axios.get("/user/12345").catch(function (error) { if (error.response) { // 请求已�出,�务器返回了�在 2xx 范围内的状�� console.log(error.response.data); console.log(error.response.status); console.log(error.response.headers); } else if (error.request) { // 请求已�出,但未收到�应 // `error.request` 在�览器中是 XMLHttpRequest 实例,在 node.js 中是 http.ClientRequest 实例 console.log(error.request); } else { // 在设置请求时触�了错误 console.log("Error", error.message); } console.log(error.config); }); ``` ## 格�错误的 HTTP(S) URL 如果 `http:` 或 `https:` 请求的 `url` 或 `baseURL` 在�议�缺少 `//`,axios 会拒�请求。例如,`https:example.com` 和 `https:/example.com` 会以代�为 `ERR_INVALID_URL` 的 `AxiosError` 拒�,而�会由�览器或 Node.js URL 解�器�默规范化。请使用 `https://example.com` 等格�正确的 URL。 错误消�会指出有问题的 URL,例如: ```text Invalid URL "https:example.com": missing "//" after protocol ``` 此行为�防止格�错误的 URL 绕过 `baseURL` 或 URL �许列表。报告的 URL 会先规范化控制字符,并��凭��查询�数值和片段内容;�议�主机�路径和查询�数�称会�留,以便识别请求。消�级脱�始终生效,因为 `AxiosError.message` 一定会包�在 `toJSON()` 中;请求�置的 `redact` 选项无法清�已创建的消�。 使用 `validateStatus` �置选项,�以覆盖默认�件(`status >= 200 && status < 300`),自定义应当抛出错误的 HTTP 状��。 ```js axios.get("/user/12345", { validateStatus: function (status) { return status < 500; // 仅在状���于 500 时 resolve }, }); ``` 使用 `toJSON` 方法,�以获�包�更多错误信�的对象。 ```js axios.get("/user/12345").catch(function (error) { console.log(error.toJSON()); }); ``` 为��从 `error.config` 中泄露�感信�,�在请求�置中传入 `redact` 数组。当调用 `AxiosError#toJSON()` 时,匹�的�置键会在任�深度上以�区分大�写的方�被脱�。 ```js axios.get("/user/12345", { headers: { Authorization: "Bearer token" }, redact: ["authorization"] }).catch(function (error) { console.log(error.toJSON().config.headers.Authorization); // [REDACTED ****] }); ``` ## 处�超时 当请求超过其�置的 `timeout` 时,axios 默认会以 `ECONNABORTED` 拒�。设置 `transitional.clarifyTimeoutError: true` �改为收到 `ETIMEDOUT`,便于将超时错误与其他中止情况区分开。 ```js async function fetchWithTimeout() { try { const response = await axios.get("https://example.com/data", { timeout: 5000, // 5 秒 transitional: { // 如果你更倾�于使用 ETIMEDOUT 而� ECONNABORTED,请设置为 true clarifyTimeoutError: true, }, }); console.log("Response:", response.data); } catch (error) { if (axios.isAxiosError(error)) { if (error.code === "ECONNABORTED" || error.code === "ETIMEDOUT") { console.error("请求超时,请�试。"); return; } console.error("Axios 错误:", error.message); return; } console.error("�外错误:", error); } } ``` ::: tip 在生产环境中始终设置 `timeout` 若�设置超时,�滞的请求�能会无�挂起。匹�的�置选项请�阅 [`timeout`](/pages/advanced/request-config#timeout) 与 [`transitional.clarifyTimeoutError`](/pages/advanced/request-config#transitional)。 ::: axios-axios-aa54a5e/docs/zh/pages/advanced/fetch-adapter.md000066400000000000000000000062441526660636600237410ustar00rootroot00000000000000# Fetch 适�器 `fetch` 适�器是我们在 1.7.0 版本中引入的新适�器,使 axios 能够使用 `fetch` API,兼顾两者的优势。默认情况下,当构建中 `xhr` 和 `http` 适�器��用,或当�环境�支�时,会使用 `fetch`。若�将其作为默认适�器,必须在创建 axios 实例时通过 `adapter` 选项显�指定。 ```js import axios from 'axios'; const instance = axios.create({ adapter: 'fetch', }); ``` 该适�器支�与 `xhr` 适�器相�的功能,包括上传和下载进度�获,还支��外的�应类型,如 `stream` 和 `formdata`(如果环境支�)。 当�略 `auth` 时,fetch 适�器�以从请求 URL 中读� HTTP Basic 认�凭�,例如 `https://user:pass@example.com`。生� `Authorization` 请求头�会先解� URL 中�过百分�编�的凭�,并且 `auth` 优先于 URL 中的凭�。 ## 自定义 fetch 从 `v1.12.0` 起,你�以自定义 fetch 适�器,使用自定义的 `fetch` 函数代替环境全局的 `fetch`。�以通过 `env` �置选项传入自定义的 `fetch` 函数�`Request` 和 `Response` 构造函数。这在使用�供了自己 `fetch` 实现的自定义环境或应用框架时�常实用。 ::: info 使用自定义 `fetch` 函数时,�能还需��供匹�的 `Request` 和 `Response` 构造函数。如果�略,将使用全局构造函数。如果你的自定义 `fetch` 与全局构造函数�兼容,�以传入 `null` ��用它们。 **注�:** 将 `Request` 和 `Response` 设置为 `null` �,fetch 适�器将无法�获上传和下载进度。 ::: ### 基本示例 ```js import customFetchFunction from 'customFetchModule'; const instance = axios.create({ adapter: 'fetch', onDownloadProgress(e) { console.log('downloadProgress', e); }, env: { fetch: customFetchFunction, Request: null, // null -> �用该构造函数 Response: null, }, }); ``` ### 与 Tauri 一起使用 [Tauri](https://tauri.app/plugin/http-client/) �供了一个平� `fetch` 函数,�绕过�览器对原生层请求的 CORS �制。以下示例展示了在 Tauri 应用中使用该自定义 fetch �置 axios 的最简设置。 ```js import { fetch } from '@tauri-apps/plugin-http'; import axios from 'axios'; const instance = axios.create({ adapter: 'fetch', onDownloadProgress(e) { console.log('downloadProgress', e); }, env: { fetch, }, }); const { data } = await instance.get('https://google.com'); ``` ### 与 SvelteKit 一起使用 [SvelteKit](https://svelte.dev/docs/kit/web-standards#Fetch-APIs) 为�务端 `load` 函数�供了自定义的 `fetch` 实现,用于处� Cookie 转�和相对 URL。由于其 `fetch` 与标准 `URL` API �兼容,必须明确�置 axios 使用它,并�用全局 `Request` 和 `Response` 构造函数。 ```js export async function load({ fetch }) { const { data: post } = await axios.get('https://jsonplaceholder.typicode.com/posts/1', { adapter: 'fetch', env: { fetch, Request: null, Response: null, }, }); return { post }; } ``` axios-axios-aa54a5e/docs/zh/pages/advanced/file-posting.md000066400000000000000000000055571526660636600236400ustar00rootroot00000000000000# 文件上传 axios 让文件上传�得简�。需� `multipart/form-data` 上传时,使用 `postForm` 或 `FormData` ��。 ## �文件上传(�览器) 直接将 `File` 对象作为字段值传入——axios 会自动检测并使用正确的内容类型: ```js await axios.postForm("https://httpbin.org/post", { description: "My profile photo", file: document.querySelector("#fileInput").files[0], }); ``` ## 多文件上传(�览器) 传入 `FileList` �一次性上传所有选中的文件,所有文件将使用相�的字段�(`files[]`)��: ```js await axios.postForm( "https://httpbin.org/post", document.querySelector("#fileInput").files ); ``` 也�以通过在键�末尾追加 `[]` �显�指定一个自定义字段�,传入 `FileList`(或 `File` 对象数组): ```js await axios.postForm("https://httpbin.org/post", { "files[]": document.querySelector("#fileInput").files, }); ``` 如需为�个文件使用��的字段�,请手动构建 `FormData` 对象: ```js const formData = new FormData(); formData.append("avatar", avatarFile); formData.append("cover", coverFile); await axios.post("https://httpbin.org/post", formData); ``` ## 跟踪上传进度(�览器) 使用 `onUploadProgress` 回调�用户显示进度�或百分比: ```js await axios.postForm("https://httpbin.org/post", { file: document.querySelector("#fileInput").files[0], }, { onUploadProgress: (progressEvent) => { const percent = Math.round( (progressEvent.loaded * 100) / progressEvent.total ); console.log(`Upload progress: ${percent}%`); }, }); ``` 进度事件对象上�用的完整字段列表,请�阅[进度�获](/pages/advanced/progress-capturing)。 ## Node.js 中的文件上传 在 Node.js 中,使用 `fs.createReadStream` 上传文件系统中的文件,无需将整个文件加载到内存: ```js import fs from "fs"; import FormData from "form-data"; import axios from "axios"; const form = new FormData(); form.append("file", fs.createReadStream("/path/to/file.jpg")); form.append("description", "My uploaded file"); await axios.post("https://httpbin.org/post", form); ``` ::: tip 在 Node.js 环境中创建 `FormData` 对象需� `form-data` npm 包。在现代 Node.js(v18+)中,全局 `FormData` 已原生�用。 ::: ## 上传 Buffer(Node.js) 也�以直接上传内存中的 `Buffer`: ```js const buffer = Buffer.from("Hello, world!"); const form = new FormData(); form.append("file", buffer, { filename: "hello.txt", contentType: "text/plain", knownLength: buffer.length, }); await axios.post("https://httpbin.org/post", form); ``` ::: warning Node.js 环境目��支��获 `FormData` 上传进度。 ::: ::: danger 在 Node.js 中上传�读�时,请设置 `maxRedirects: 0`,以防止 `follow-redirects` 包将整个�缓冲到内存中。 ::: axios-axios-aa54a5e/docs/zh/pages/advanced/header-methods.md000066400000000000000000000172231526660636600241220ustar00rootroot00000000000000# 请求头方法 ��新 `AxiosHeaders` 类的引入,axios �供了一组�作请求头的方法,比直接�作请求头对象更加方便。 ## 构造函数 `new AxiosHeaders(headers?)` `AxiosHeaders` 类的构造函数接�一个�选的请求头对象��始化实例,对象�以包�任�数�的请求头,且键��区分大�写。 ```js constructor(headers?: RawAxiosHeaders | AxiosHeaders | string); ``` 为方便起�,你�以传入一个以�行符分隔的请求头字符串,它们会被解�并添加到实例中。 ```js const headers = new AxiosHeaders(` Host: www.bing.com User-Agent: curl/7.54.0 Accept: */*`); console.log(headers); // Object [AxiosHeaders] { // host: 'www.bing.com', // 'user-agent': 'curl/7.54.0', // accept: '*/*' // } ``` ## Set `set` 方法用于在 `AxiosHeaders` 实例上设置请求头,�以传入�个请求头�称和值�包�多个请求头的对象,或以�行符分隔的请求头字符串。该方法还接�一个�选的 `rewrite` �数,用于控制设置请求头时的覆盖行为。 ```js set(headerName, value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher); set(headerName, value, rewrite?: (this: AxiosHeaders, value: string, name: string) => boolean); set(headers?: RawAxiosHeaders | AxiosHeaders | string, rewrite?: boolean); set(headers?: Iterable<[string, AxiosHeaderValue]>, rewrite?: boolean); ``` `rewrite` �数控制覆盖行为: - `false` - 如果请求头的值已设置(�为 undefined),则�覆盖 - `undefined`(默认)- 覆盖请求头,除�其值被设置为 false - `true` - 始终覆盖 该�数也�以接�一个用户自定义函数,用于决定是�应覆盖该值,函数接收当�值�请求头�称和请求头对象作为�数。 空字符串或仅包�空白字符的请求头�称会被忽略。 也�以传入�迭代的键值对,例如 `Map`: ```js const headers = new AxiosHeaders(); headers.set( new Map([ ['X-Trace-Id', 'abc123'], ['Accept', 'application/json'], ]) ); ``` `AxiosHeaders` 会�留第一个匹�键的大�写形�。你�以利用这一特性,先以 `undefined` 值预设一个键�,之��设置值,从而�留特定的请求头大�写。详�[�留特定请求头大�写](/pages/advanced/headers#preserving-a-specific-header-case)。 ## Get `get` 方法用于获�请求头的值,�以传入�个请求头�称��选的匹�器或解�器。匹�器默认为 `true`,解�器�以是用于从请求头中��值的正则表达�。 ```js get(headerName: string, parser: typeof AxiosHeaders.parseParameters): AxiosHeaderParameters; get(headerName: string, parser: RegExp): RegExpExecArray | null; get(headerName: string, matcher?: true | AxiosHeaderParser): AxiosHeaderValue; ``` 以下是 `get` 方法的一些使用示例: ```js const headers = new AxiosHeaders({ 'Content-Type': 'multipart/form-data; boundary=Asrf456BGe4h', }); console.log(headers.get('Content-Type')); // multipart/form-data; boundary=Asrf456BGe4h console.log(headers.get('Content-Type', true)); // 解�以 \s,;= 为分隔符的键值对字符串: // [Object: null prototype] { // 'multipart/form-data': undefined, // boundary: 'Asrf456BGe4h' // } const quotedHeaders = new AxiosHeaders({ 'Content-Type': 'multipart/form-data; boundary="a,b"', }); console.log({ ...quotedHeaders.get('Content-Type', AxiosHeaders.parseParameters), }); // { boundary: 'a,b' } console.log( headers.get('Content-Type', (value, name, headers) => { return String(value).replace(/a/g, 'ZZZ'); }) ); // multipZZZrt/form-dZZZtZZZ; boundZZZry=Asrf456BGe4h console.log(headers.get('Content-Type', /boundary=(\w+)/)?.[0]); // boundary=Asrf456BGe4h ``` `AxiosHeaders.parseParameters` 是用于规范化 HTTP �数值的�选解�器。它返回一个原型为 null 的映射,�数�称�区分大�写。它会移除带引�字符串的定界引�,解�转义的�引�和�斜�,并�留带引�值中的逗�和分�。对于�带引�的值,�会移除其两侧的 RFC �选空白(空格和水平制表符)。 解�器会忽略会导致对象安全问题的键(`__proto__`�`constructor` 和 `prototype`)。传入 `true` �会使用旧版分�器,其输出����以实现��兼容。 ## Has `has` 方法用于检查 `AxiosHeaders` 实例中是�存在�个请求头,�以传入�个请求头�称和�选的匹�器。 ```js has(header: string, matcher?: AxiosHeaderMatcher): boolean; ``` ::: info 如果请求头已设置(值�为 undefined),则返回 true。 ::: ## Delete `delete` 方法用于从 `AxiosHeaders` 实例中删除�个请求头,�以传入�个请求头�称和�选的匹�器。 ```js delete(header: string | string[], matcher?: AxiosHeaderMatcher): boolean; ``` ::: info 如果至少有一个请求头被移除,则返回 true。 ::: ## Clear `clear` 方法用于在�传入任何�数时删除 `AxiosHeaders` 实例中的所有请求头。如果传入匹�器,则�移除与匹�器匹�的请求头,此时匹�器用于匹�请求头�称而�值。 ```js clear(matcher?: AxiosHeaderMatcher): boolean; ``` ::: info 如果至少有一个请求头被清除,则返回 true。 ::: ## Normalize 如果直接修改了请求头对象,�能导致相��称但大�写��的��项。此方法通过将��键�并为一个�规范化请求头对象。axios 在�个拦截器调用�内部使用此方法。将 format 设置为 true �将请求头�称转�为首字�大写的格�(cOntEnt-type => Content-Type),设置为 false 则�留原始格�。 ```js const headers = new AxiosHeaders({ foo: '1', }); headers.Foo = '2'; headers.FOO = '3'; console.log(headers.toJSON()); // [Object: null prototype] { foo: '1', Foo: '2', FOO: '3' } console.log(headers.normalize().toJSON()); // [Object: null prototype] { foo: '3' } console.log(headers.normalize(true).toJSON()); // [Object: null prototype] { Foo: '3' } ``` ::: info 返回 `this` 以支�链�调用。 ::: ## Concat 将实例与目标对象�并到一个新的 AxiosHeaders 实例中。如果目标是字符串,它将被解�为原始 HTTP 请求头;如果目标是 AxiosHeaders 实例,它将与当�实例�并。 这对于组�请求头时的大�写预设�常有用,例如: ```js const headers = AxiosHeaders.concat( { 'content-type': undefined }, { 'Content-Type': 'application/octet-stream' } ); ``` ```js concat(...targets: Array): AxiosHeaders; ``` ::: info 返回一个新的 AxiosHeaders 实例。 ::: ## toJSON 将所有内部请求头值解�到一个新的 null 原型对象中。将 `asStrings` 设置为 true �将数组解�为以逗�分隔的字符串。 ```js toJSON(asStrings: true): Record; toJSON(asStrings?: false): Record; ``` ## toString 将请求头返回为�� CRLF 的 HTTP 请求头�,�行一个 `name: value` 键值对。 ```js toString(): string; ``` ## From 从传入的原始请求头创建并返回一个新的 `AxiosHeaders` 实例;如果传入的已�是 `AxiosHeaders` 实例,则直接返回该实例。 ```js from(thing?: AxiosHeaders | RawAxiosHeaders | string): AxiosHeaders; ``` ## 快�方法 以下快�方法�供使用: - `setContentType`�`getContentType`�`hasContentType` - `setContentLength`�`getContentLength`�`hasContentLength` - `setAccept`�`getAccept`�`hasAccept` - `setUserAgent`�`getUserAgent`�`hasUserAgent` - `setContentEncoding`�`getContentEncoding`�`hasContentEncoding` axios-axios-aa54a5e/docs/zh/pages/advanced/headers.md000066400000000000000000000110001526660636600226270ustar00rootroot00000000000000# 请求头 axios 暴露了自己的 AxiosHeaders 类,通过类 Map 的 API ��作请求头,��键��区分大�写。axios 内部使用该类管�请求头,�时也将其暴露给用户以�供便利。尽管 HTTP 请求头本身�区分大�写,axios �会�留原始请求头的大�写形�,以满足风格需求,并在�务器错误地将请求头大�写视为有效区分时�供兼容。直接�作请求头对象的旧方��然�用,但已废弃,�建议在新代�中使用。 ## 使用请求头 AxiosHeaders 对象实例�以包���类型的内部值,用于控制设置和�并逻辑。axios 在将最终请求头对象���会调用 `toJSON` 方法。AxiosHeaders 对象也是�迭代的,�以在循环中使用,或转�为数组或对象。 请求头值�以是以下类型之一: - `string` - 正常的字符串值,将被��到�务器 - `null` - 转�为 JSON 时跳过该请求头 - `false` - 转�为 JSON 时跳过该请求头,并�外表示调用 `set` 方法时必须将 `rewrite` 选项设置为 true �能覆盖此值(axios 内部使用此机制�许用户选择�安装�些请求头,如 User-Agent 或 Content-Type) - `undefined` - 值未设置 ::: warning 如果请求头值�是 undefined,则视为已设置。 ::: 请求头对象始终在拦截器和转�器中�始化,如以下示例所示: ```js axios.interceptors.request.use((request: InternalAxiosRequestConfig) => { request.headers.set("My-header", "value"); request.headers.set({ "My-set-header1": "my-set-value1", "My-set-header2": "my-set-value2", }); // �止 axios �续设置此请求头 request.headers.set("User-Agent", false); request.headers.setContentType("text/plain"); // 直接访问的方�已废弃 request.headers["My-set-header2"] = "newValue"; return request; }); ``` 你�以使用任何�迭代方法�历 AxiosHeaders,如 for-of 循环�forEach 或展开�算符: ```js const headers = new AxiosHeaders({ foo: '1', bar: '2', baz: '3', }); for (const [header, value] of headers) { console.log(header, value); } // foo 1 // bar 2 // baz 3 ``` ## 在请求中设置请求头 最常�的设置请求头的方�是在请求�置或实例�置的 `headers` 选项中设置: ```js // 针对�个请求 await axios.get('/api/data', { headers: { 'Accept-Language': 'en-US', 'X-Request-ID': 'abc123', }, }); // 针对实例(应用于�个请求) const api = axios.create({ headers: { 'X-App-Version': '2.0.0', }, }); ``` ## �留特定请求头大�写 axios 请求头�称�区分大�写,但 `AxiosHeaders` 会�留第一个匹�键的大�写形�。如果你需�为大�写�感的�标准�务器�留特定大�写,�以在 `defaults` 中预设键�,之��按常规方�设置值。 ```js const api = axios.create(); api.defaults.headers.common = { 'content-type': undefined, accept: undefined, }; await api.put(url, data, { headers: { 'Content-Type': 'application/octet-stream', Accept: 'application/json', }, }); ``` 也�以在组�请求头时直接使用 `AxiosHeaders` 实现: ```js import axios, { AxiosHeaders } from 'axios'; const headers = AxiosHeaders.concat( { 'content-type': undefined }, { 'Content-Type': 'application/octet-stream' } ); await axios.put(url, data, { headers }); ``` ## 在拦截器中设置请求头 拦截器是附加动�请求头(如认�令牌)的�适�置,因为令牌�能在实例首次创建时还��用: ```js api.interceptors.request.use((config) => { const token = getAuthToken(); // 在请求时读� config.headers.set('Authorization', `Bearer ${token}`); return config; }); ``` ## 读��应头 �应头以 `AxiosHeaders` 实例的形�在 `response.headers` 上�用,所有头�称�为�写: ```js const response = await axios.get('/api/data'); console.log(response.headers['content-type']); // application/json; charset=utf-8 console.log(response.headers.get('x-request-id')); // abc123 ``` ## 移除默认请求头 如需�消 axios 默认设置的请求头(如 `Content-Type` 或 `User-Agent`),将其值设置为 `false`: ```js await axios.post('/api/data', payload, { headers: { 'Content-Type': false, // 让�览器自动设置(例如针对 FormData) }, }); ``` 关于完整 `AxiosHeaders` 方法 API 的详细说明,请�阅[请求头方法](/pages/advanced/header-methods)页�。 axios-axios-aa54a5e/docs/zh/pages/advanced/html-form-processing.md000066400000000000000000000033011526660636600253000ustar00rootroot00000000000000# HTML 表��交(�览器) 你也�以直接从 HTML 表�元素�交数�,无需任何�外的 JavaScript 代����交页�中的表�。 ```js await axios.postForm('https://httpbin.org/post', document.querySelector('#htmlForm')); ``` `FormData` 和 `HTMLForm` 对象也�以通过显�将 `Content-Type` 请求头设置为 `application/json` �以 `JSON` 格���: ```js await axios.post('https://httpbin.org/post', document.querySelector('#htmlForm'), { headers: { 'Content-Type': 'application/json', }, }); ``` 以下是一个有效的��被上述代��交的表�示例: ```html
``` 上述表�将以如下格��交: ```json { "foo": "1", "deep": { "prop": "2" }, "deep prop spaced": "3", "baz": ["4", "5"], "user": { "age": "value2" } } ``` ::: tip 字段�路径表示法 �有点�和方括�表示法会创建属性路径。其他字符(包括空格�`-`�`+`�`*` 和 `&`)都会�留为字�字段�的一部分。例如,`deep.prop` 会创建嵌套路径,而 `deep prop spaced` �是顶层键。 ::: ::: warning 目��支�将 Blob/File 以 JSON(base64)格���。 ::: axios-axios-aa54a5e/docs/zh/pages/advanced/http2.md000066400000000000000000000044621526660636600222730ustar00rootroot00000000000000# HTTP2 在 `1.13.0` 版本中,`http` 适�器新增了实验性的 HTTP/2 支�,仅在 Node.js 环境中�用。 ## 基本用法 使用 `httpVersion` 选项�选择请求使用的�议版本,将其设置为 `2` ��用 HTTP/2。 ```js const { data, headers, status } = await axios.post( "https://httpbin.org/post", form, { httpVersion: 2, }, ); ``` ## `http2Options` �以通过 `http2Options` �置对象传入内部 `session.request()` 调用的原生�外选项,其中还包括自定义的 `sessionTimeout` �数,用于控制空闲 HTTP/2 会�在关闭���存活的时间(毫秒),默认值为 `1000ms`。 ```js { httpVersion: 2, http2Options: { rejectUnauthorized: false, // 接�自签��书(仅用于开�环境) sessionTimeout: 5000, // 空闲会��� 5 秒 }, } ``` ::: warning HTTP/2 支�目��为实验性功能,API �能在未�的次�版本或补�版本中�生�化。 ::: ::: warning HTTP/2 �支��定� HTTP/2 适�器目��会自动跟��定�。如果使用 `httpVersion: 2` �起的请求收到 `3xx` �应,�定��会被自动跟�。请手动处�这类�应,或对�赖�定�的端点继续使用 HTTP/1.x。 ::: ## 完整示例 以下示例通过 HTTP/2 �� `multipart/form-data` POST 请求,并�时跟踪上传和下载进度。 ```js const form = new FormData(); form.append("foo", "123"); const { data, headers, status } = await axios.post( "https://httpbin.org/post", form, { httpVersion: 2, http2Options: { // rejectUnauthorized: false, // sessionTimeout: 1000 }, onUploadProgress(e) { console.log("upload progress", e); }, onDownloadProgress(e) { console.log("download progress", e); }, responseType: "arraybuffer", }, ); ``` ## �置�考 | 选项 | 类型 | 默认值 | 说明 | |---|---|---|---| | `httpVersion` | `number` | `1` | 使用的 HTTP �议版本,设置为 `2` ��用 HTTP/2。 | | `http2Options.sessionTimeout` | `number` | `1000` | 空闲 HTTP/2 会�关闭�等待的时间(毫秒)。 | Node.js 内置 `http2` 模�支�的其他原生 `session.request()` 选项也�以通过 `http2Options` 传入。 axios-axios-aa54a5e/docs/zh/pages/advanced/interceptors.md000066400000000000000000000142651526660636600237550ustar00rootroot00000000000000# 拦截器 拦截器是一�强大的机制,�用于拦截和修改 HTTP 请求与�应,与 Express.js 中的中间件�常相似。拦截器是在请求���和�应接收�执行的函数,适用于日志记录�修改请求头�修改�应等多�场景。 拦截器的基本用法如下: ```js // 添加请求拦截器 axios.interceptors.request.use( function (config) { // 在请求��之�执行�些�作 return config; }, function (error) { // 处�请求错误 return Promise.reject(error); } ); // 添加�应拦截器 axios.interceptors.response.use( function (response) { // 状��在 2xx 范围内的�应会触�此函数 // 处��应数� return response; }, function (error) { // 状���在 2xx 范围内的�应会触�此函数 // 处��应错误 return Promise.reject(error); } ); ``` ## 移除拦截器 �以通过对�移除的拦截器调用 `eject` 方法�移除特定拦截器。也�以通过在 `axios.interceptors` 对象上调用 `clear` 方法�移除所有拦截器。以下是移除拦截器的示例: ```js // 移除请求拦截器 const myInterceptor = axios.interceptors.request.use(function () { /*...*/ }); axios.interceptors.request.eject(myInterceptor); // 移除�应拦截器 const myInterceptor = axios.interceptors.response.use(function () { /*...*/ }); axios.interceptors.response.eject(myInterceptor); ``` 以下是移除所有拦截器的示例: ```js const instance = axios.create(); instance.interceptors.request.use(function () { /*...*/ }); instance.interceptors.request.clear(); // 移除所有请求拦截器 instance.interceptors.response.use(function () { /*...*/ }); instance.interceptors.response.clear(); // 移除所有�应拦截器 ``` ## 拦截器的默认行为 添加请求拦截器时,默认被视为异步执行。当主线程被阻塞时(拦截器底层会创建一个 Promise,你的请求会被放到调用栈底部),这�能导致 axios 请求的执行出现延迟。如果你的请求拦截器是�步的,�以在选项对象中添加一个标志,告知 axios �步�行该代�,从而��请求执行的延迟。 ```js axios.interceptors.request.use( function (config) { config.headers.test = "I am only a header!"; return config; }, null, { synchronous: true } ); ``` ### �步拦截器错误 �步请求拦截器抛出错误时,axios 会调用与该拦截器�对的 `onRejected` 处�器,并�止�行其余请求拦截器。如果处�器正常返回(包括返回 `undefined` 或已兑现的 Promise),该错误会被视为已处�,axios 将使用最�一个有效�置��请求。处�器的返回值�会替�该�置。 若�阻止��请求,请�略拒�处�器,或让它抛出错误或返回已拒�的 Promise。最终错误��会继续传递给�应拒�拦截器。 ```js axios.interceptors.request.use( function validate(config) { if (!config.headers.has("Authorization")) { throw new Error("Authorization is required"); } return config; }, function rejectInvalidRequest(error) { return Promise.reject(error); }, { synchronous: true } ); ``` 仅记录错误的拒�处�器�以正常返回,以�留现有的继续请求行为: ```js axios.interceptors.request.use( function prepare(config) { throw new Error("Optional preparation failed"); }, function logPreparationFailure(error) { console.warn(error); // 正常返回将使用最�一个有效�置��请求。 }, { synchronous: true } ); ``` ## 使用 `runWhen` 的拦截器 如果你希望根��行时�件决定是�执行�个拦截器,�以在选项对象中添加 `runWhen` 函数。仅当 `runWhen` 返回 `false` 时,拦截器�会执行。该函数会以 config 对象作为�数调用(你也�以为其绑定自定义�数)。这对于�需在特定时机�行的异步请求拦截器�常实用。 ```js function onGetCall(config) { return config.method === "get"; } axios.interceptors.request.use( function (config) { config.headers.test = "special get headers"; return config; }, null, { runWhen: onGetCall } ); ``` ## 拦截器执行顺� ::: warning 请求拦截器与�应拦截器的执行顺�**相�** 请求拦截器按**逆�**执行(LIFO——�进先出)。**最�**添加的请求拦截器**最先**执行。 �应拦截器按**添加顺�**执行(FIFO——先进先出)。**最先**添加的�应拦截器**最先**执行。 ::: 下�的示例展示了三个请求拦截器和三个�应拦截器的完整执行顺�: ```js const instance = axios.create(); const interceptor = (id) => (base) => { console.log(id); return base; }; instance.interceptors.request.use(interceptor("Request Interceptor 1")); instance.interceptors.request.use(interceptor("Request Interceptor 2")); instance.interceptors.request.use(interceptor("Request Interceptor 3")); instance.interceptors.response.use(interceptor("Response Interceptor 1")); instance.interceptors.response.use(interceptor("Response Interceptor 2")); instance.interceptors.response.use(interceptor("Response Interceptor 3")); // 控制�输出: // Request Interceptor 3 // Request Interceptor 2 // Request Interceptor 1 // [�起 HTTP 请求] // Response Interceptor 1 // Response Interceptor 2 // Response Interceptor 3 ``` ## 多个拦截器 你�以在�一个请求或�应上添加多个拦截器,�一拦截器链中的多个拦截器�循以下规则: - �个拦截器都会执行 - 请求拦截器按照�进先出(LIFO)的顺�执行 - �应拦截器按照添加顺�(FIFO)执行 - �返回最�一个拦截器的结果 - �个拦截器接收其�驱拦截器的结果 - 当�功回调中的拦截器抛出错误时 - �续的�功回调拦截器�会被调用 - �续的错误回调拦截器会被调用 - 一旦被�获,�续的�功回调拦截器将�次被调用(与 Promise 链的行为一致) ::: tip �深入了解拦截器的工作原�,�以查阅[这里](https://github.com/axios/axios/blob/v1.x/tests/browser/interceptors.browser.test.js)的测试用例。 ::: axios-axios-aa54a5e/docs/zh/pages/advanced/multipart-form-data-format.md000066400000000000000000000150161526660636600264060ustar00rootroot00000000000000# multipart/form-data 格� axios 支�以 `multipart/form-data` 格���请求,这�格�常用于文件上传。�以该格���请求,需�创建一个 `FormData` 对象并�其追加数�,然�将 `FormData` 对象传入 axios 请求�置的 `data` 属性。 ```js const formData = new FormData(); formData.append('foo', 'bar'); axios.post('https://httpbin.org/post', formData); ``` 对于�览器�Web Worker 或 React Native 的 `FormData`,��手动设置 `Content-Type` 请求头;这些�行时会自行添加 multipart boundary。 在 Node.js 中,�以使用 `form-data` 库,如下所示: ```js const FormData = require('form-data'); const form = new FormData(); form.append('my_field', 'my value'); form.append('my_buffer', Buffer.alloc(10)); form.append('my_file', fs.createReadStream('/foo/bar.jpg')); axios.post('https://example.com', form); ``` ## 自动�列化为 FormData 从 v0.27.0 起,如果请求的 Content-Type 请求头设置为 `multipart/form-data`,axios 支�自动将对象�列化为 FormData 对象。这�味�你�以直接将 JavaScript 对象传入 axios 请求�置的 `data` 属性。例如,� POST 请求传递数�时: ```js import axios from 'axios'; axios .post( 'https://httpbin.org/post', { x: 1 }, { headers: { 'Content-Type': 'multipart/form-data', }, } ) .then(({ data }) => console.log(data)); ``` 在 Node.js 构建中,默认使用 ([`form-data`](https://github.com/form-data/form-data)) 作为 polyfill。你�以通过设置 `env.FormData` �置���覆盖 FormData 类,但大多数情况下�需�这样�: ```js const axios = require('axios'); var FormData = require('form-data'); axios .post( 'https://httpbin.org/post', { x: 1, buf: Buffer.alloc(10) }, { headers: { 'Content-Type': 'multipart/form-data', }, } ) .then(({ data }) => console.log(data)); ``` ## Node.js `FormData` 的请求头策略 当你传入一个暴露 `getHeaders()` 的 Node.js `FormData` 对象(例如 [`form-data`](https://github.com/form-data/form-data) 包)时,axios 默认会将它返回的所有请求头�制到请求上。这�留了 v1 的兼容性,但如果 `FormData` 对象�自��信��,�能会出问题——`getHeaders()` �能覆盖 `Authorization` 等请求头或注入任�请求头。 设置 `formDataHeaderPolicy: 'content-only'` �**�**从 `getHeaders()` �制 `Content-Type` 和 `Content-Length`,�通过请求的 `headers` �置显�设置其他请求头: ```js await axios.post('https://example.com/upload', form, { formDataHeaderPolicy: 'content-only', headers: { Authorization: 'Bearer my-token', }, }); ``` 默认值为 `'legacy'`。详�请求�置�考中的 [`formDataHeaderPolicy`](/pages/advanced/request-config#formdataheaderpolicy)。 ## 支�的特殊结尾 axios FormData �列化器支�以下特殊结尾,用于执行对应�作: - `{}` - 使用 JSON.stringify �列化该值 - `[]` - 将类数组对象展开为具有相�键的独立字段 ::: warning 注�:展开/扩展�作默认应用于数组和 FileList 对象 ::: ## �置 FormData �列化器 FormData �列化器通过 `config.formSerializer` 对象属性支�以下�外选项,用于处�特殊情况: - `visitor: Function` - 用户自定义的访问者函数,将递归调用以按照自定义规则将数�对象�列化为 FormData 对象。 - `dots: boolean = false` - 使用点�表示法代替方括���列化数组和对象; - `metaTokens: boolean = true` - 在 FormData 键中添加特殊结尾(如 `user{}: '{"name": "John"}'`)。�端 body 解�器�以利用此元信�自动将值解�为 JSON。 - `indexes: null|false|true = false` - 控制如何为�平类数组对象的展开键添加索引 - `null` - �添加方括�(`arr: 1`,`arr: 2`,`arr: 3`) - `false`(默认)- 添加空方括�(`arr[]: 1`,`arr[]: 2`,`arr[]: 3`) - `true` - 添加带索引的方括�(`arr[0]: 1`,`arr[1]: 2`,`arr[2]: 3`) - `maxDepth: number = 100` - �列化器递归的最大对象嵌套深度。如果输入超过此深度,将抛出 `code: 'ERR_FORM_DATA_DEPTH_EXCEEDED'` 的 `AxiosError`。这�以�护�务端应用��深层嵌套载�的 DoS 攻击。设置为 `Infinity` ��用此�制。 - `Blob: typeof Blob` - 在符�规范的 `FormData` 中转�类 ArrayBuffer 值时使用的 Blob 构造函数。�有当�行时以其他标识符�供兼容的 `Blob` 构造函数时,�需�覆盖它。 ```js // 当 schema 确实需�超过 100 层嵌套时,��高�制: axios.postForm('/api', data, { formSerializer: { maxDepth: 200 } }); ``` ::: warning 安全�示 默认�制 100 是有�为之。将客户端控制的 JSON 作为 `data` 转�给 axios 的�务端代�,如果没有此�护,容易�生调用栈溢出。除�你的 schema 确实需�,�则���高 `maxDepth`。 ::: 例如,对于以下对象: ```js const obj = { x: 1, arr: [1, 2, 3], arr2: [1, [2], 3], users: [ { name: 'Peter', surname: 'Griffin' }, { name: 'Thomas', surname: 'Anderson' }, ], 'obj2{}': [{ x: 1 }], }; ``` axios �列化器内部将执行以下步骤: ```js const formData = new FormData(); formData.append('x', '1'); formData.append('arr[]', '1'); formData.append('arr[]', '2'); formData.append('arr[]', '3'); formData.append('arr2[0]', '1'); formData.append('arr2[1][0]', '2'); formData.append('arr2[2]', '3'); formData.append('users[0][name]', 'Peter'); formData.append('users[0][surname]', 'Griffin'); formData.append('users[1][name]', 'Thomas'); formData.append('users[1][surname]', 'Anderson'); formData.append('obj2{}', '[{"x":1}]'); ``` ## 将 FormData 转回 JSON `axios.formToJSON()` 会将字段�称中的点�和方括�表示法转�为嵌套对象和数组。�有 `.`�`[` 和 `]` 是结构分隔符。`-`�空格�`+`�`*` 和 `&` 等其他字符会�留为字�键的一部分。 ```js const form = new FormData(); form.append('user-name', 'johndoe'); form.append('user.name', 'john'); console.log(axios.formToJSON(form)); // { // 'user-name': 'johndoe', // user: { name: 'john' } // } ``` `user[name]` �样会创建嵌套对象路径,而 `items[]` 会创建数组。 axios 支�以下快�方法:`postForm`�`putForm`�`patchForm`,它们分别对应相应的 HTTP 方法,并预设 `Content-Type` 请求头为 `multipart/form-data`。 axios-axios-aa54a5e/docs/zh/pages/advanced/progress-capturing.md000066400000000000000000000032711526660636600250650ustar00rootroot00000000000000# 进度�获 axios �时支�在�览器和 Node.js 环境中�获请求的上传/下载进度。进度事件的触�频率被�制为�秒最多 3 次,以���览器被过多的进度事件压垮。以下是�获进度事件的示例: ```js await axios.post(url, data, { onUploadProgress: function (axiosProgressEvent) { /*{ loaded: number; total?: number; progress?: number; // 范围 [0..1] bytes: number; // 自上次触�以�传输的字节数(增�) estimated?: number; // 预计剩余时间(秒) rate?: number; // 上传速度(字节/秒) upload: true; // 上传标识 }*/ }, onDownloadProgress: function (axiosProgressEvent) { /*{ loaded: number; total?: number; progress?: number; bytes: number; estimated?: number; rate?: number; // 下载速度(字节/秒) download: true; // 下载标识 }*/ }, }); ``` 你也�以在 Node.js 中将上传和下载进度事件��传输到�读�,以便以自定义方�显示进度。以下是��传输进度事件的示例: ```js const { data } = await axios.post(SERVER_URL, readableStream, { onUploadProgress: ({ progress }) => { console.log((progress * 100).toFixed(2)); }, headers: { "Content-Length": contentLength, }, maxRedirects: 0, // ��缓冲整个� }); ``` ::: warning Node.js 环境目��支��获 FormData 上传进度 ::: ::: danger 建议通过设置 `maxRedirects: 0` ��用�定�,以便在 Node.js 环境中上传�,因为 `follow-redirects` 包会��循"背压"算法而将整个�缓冲到内存中 ::: axios-axios-aa54a5e/docs/zh/pages/advanced/promises.md000066400000000000000000000047511526660636600230740ustar00rootroot00000000000000# Promise axios 基于原生 ES6 Promise API 构建。�个 axios 请求都返回一个 Promise,该 Promise 解�为�应对象或以错误拒�。如果你的环境�支� ES6 Promise,需�使用 polyfill,例如 [es6-promise](https://github.com/stefanpenner/es6-promise)。 对于 TypeScript 集�,`AxiosPromise` 是 `AxiosResponse` 的 Promise。它会在 `response.config` 中�留请求数�和查询�数: ```ts declare const search: AxiosPromise; search.then((response) => { response.data; // SearchResponse response.config.data; // RequestBody | undefined response.config.params; // SearchParams | undefined }); ``` ## then / catch / finally 由于 axios 返回的是标准 Promise,你�以使用 `.then()`�`.catch()` 和 `.finally()` �处�结果: ```js axios.get("/api/users") .then((response) => { console.log(response.data); }) .catch((error) => { console.error("Request failed:", error.message); }) .finally(() => { console.log("Request finished"); }); ``` ## async / await 大多数代�库推�使用 `async/await`,它使异步代�读起���步代�: ```js async function fetchUser(id) { try { const response = await axios.get(`/api/users/${id}`); return response.data; } catch (error) { console.error("Failed to fetch user:", error.message); throw error; } } ``` ## 并行请求 由于 axios 返回标准 Promise,你�以使用 `Promise.all` �时�起多个请求,并等待所有请求完�: ```js const [users, posts] = await Promise.all([ axios.get("/api/users"), axios.get("/api/posts"), ]); console.log(users.data, posts.data); ``` ::: tip `Promise.all` 会在任何一个请求失败时立� reject。如果你希望处�部分失败的情况,请改用 `Promise.allSettled`。 ::: ```js const results = await Promise.allSettled([ axios.get("/api/users"), axios.get("/api/posts"), ]); results.forEach((result) => { if (result.status === "fulfilled") { console.log(result.value.data); } else { console.error("Request failed:", result.reason.message); } }); ``` ## 链�请求 �以链�调用 `.then()` �顺�执行请求,将上一个请求的数�传递给下一个: ```js axios.get("/api/user/1") .then(({ data: user }) => axios.get(`/api/posts?userId=${user.id}`)) .then(({ data: posts }) => { console.log("Posts for user:", posts); }) .catch(console.error); ``` axios-axios-aa54a5e/docs/zh/pages/advanced/rate-limiting.md000066400000000000000000000036321526660636600237750ustar00rootroot00000000000000# 速率�制 axios 通过 HTTP 适�器在 Node.js 环境中支�带宽�制。你�以�制数�的上传或下载速度,适用于批��作���任务或�希望�满带宽的礼貌抓�等场景。 ## `maxRate` `maxRate` 选项接�一个数字(字节/秒)或一个数组,数组第一个值为上传�制,第二个值为下载�制。使用 `[uploadRate]` 仅�制上传,使用 `[uploadRate, downloadRate]` �时�制两个方�。传入�个数字时,该�制�时应用于上传和下载。 ```js // 将上传和下载速度��制为 100 KB/s await axios.get(URL, { maxRate: 100 * 1024 }); // 上传�制 100 KB/s,下载�制 500 KB/s await axios.get(URL, { maxRate: [100 * 1024, 500 * 1024] }); ``` ::: warning `maxRate` 仅支� Node.js HTTP 适�器,在�览器环境中无效。 ::: ## 上传速率�制 �制上传速度的�时记录进度: ```js const { data } = await axios.post(SERVER_URL, myBuffer, { onUploadProgress: ({ progress, rate }) => { const percent = (progress * 100).toFixed(1); const kbps = (rate / 1024).toFixed(1); console.log(`Upload [${percent}%] at ${kbps} KB/s`); }, maxRate: [100 * 1024], // 上传�制 100 KB/s }); ``` ## 下载速率�制 �制大�应体的下载速度: ```js const { data } = await axios.get(FILE_URL, { onDownloadProgress: ({ progress, rate }) => { const percent = (progress * 100).toFixed(1); const kbps = (rate / 1024).toFixed(1); console.log(`Download [${percent}%] at ${kbps} KB/s`); }, maxRate: [Infinity, 200 * 1024], // ��制上传,下载�制 200 KB/s responseType: "arraybuffer", }); ``` ## �时�制上传和下载 将两个�制作为数组传入,��时控制两个方�: ```js await axios.post(SERVER_URL, largeBuffer, { maxRate: [50 * 1024, 500 * 1024], // 上传 50 KB/s,下载 500 KB/s }); ``` axios-axios-aa54a5e/docs/zh/pages/advanced/request-config.md000066400000000000000000000675101526660636600241700ustar00rootroot00000000000000# 请求�置 请求�置用于�置 HTTP 请求的�项�数。虽然有大��用选项,但唯一必填的选项是 `url`。如果�置对象中没有 `method` 字段,默认使用 `GET` 方法。 ::: warning 安全�示:解压炸弹防护是�选的 默认情况下 `maxContentLength` 和 `maxBodyLength` �为 `-1`(��制)。��或被攻陷的�务器�能返回一个很�的 gzip/deflate/brotli/zstd �应,解压��达数 GB,从而耗尽 Node.js 进程的内存。 如果你��完全�信的�务器�起请求,**请设置上�**: ```js axios.defaults.maxContentLength = 10 * 1024 * 1024; // 10 MB axios.defaults.maxBodyLength = 10 * 1024 * 1024; ``` 详�[安全指�](/pages/misc/security)。 ::: ### `url` `url` 是请求的目标 URL,�以是字符串或 `URL` 实例。 `http:` 或 `https:` URL 必须在�议�包� `//`。`https:example.com` 和 `https:/example.com` 等格�错误的值会以 `ERR_INVALID_URL` 拒�;请使用 `https://example.com` 等格�正确的 URL。 ### `method` `method` 是请求使用的 HTTP 方法,默认为 `GET`。 ### `baseURL` `baseURL` 是拼接在 `url` ��的基础 URL,除� `url` 是�对 URL。这对于��一域��起请求�常实用,无需在�次请求时��写域�和 API 版本�缀。 `baseURL` �是构造 URL 的便利选项,并�是安全边界。如果请求的 `url` �自��信输入,应先校验�传给 axios。相对 `url` �以包� `..` 路径段;axios 将其与 `baseURL` 拼接�,�行平�的 URL 解�器会规范化路径,�能把请求解�到预期路径�缀之外。`allowAbsoluteUrls: false` �以阻止�对 URL 替� `baseURL`,但�会校验或�制相对路径。 �样的格�规则也适用于 `baseURL`:`http:` 和 `https:` 值必须在�议�包� `//`。 ### `allowAbsoluteUrls` `allowAbsoluteUrls` 决定�对 URL 是��以覆盖已�置的 `baseUrl`。设置为 `true`(默认值)时,�对 `url` 会覆盖 `baseUrl`;设置为 `false` 时,�对 `url` 始终会拼接在 `baseUrl` 之�。 ### `transformRequest` `transformRequest` 函数�许你在数���到�务器之�对其进行修改,仅适用于 `PUT`�`POST`�`PATCH` 和 `DELETE` 请求方法。数组中的最�一个函数必须返回字符串�Buffer�ArrayBuffer�FormData 或 Stream 实例。 ### `transformResponse` `transformResponse` 函数�许你在数�传递给 `then` 或 `catch` 函数之�对�应数�进行修改,函数以�应数�为唯一�数。 ### `parseReviver` `parseReviver` 函数�许你�默认 `transformResponse` 所使用的原生 `JSON.parse()` 调用直接�供一个自定义的 "reviver" 函数。 这对于执行高性能的类型水�(例如将 ISO 字符串转�为 `Temporal` 或 `Date` 对象)或防止解�过程中的精度丢失尤为有用。 在支� `JSON.parse` reviver `context` �数的环境中,reviver 函数会接收第三个 `context` �数,用于访问原始 JSON `source`,从而能够精确转�那些以标准 JavaScript 数字解�时会丢失精度的大整数(BigInt)。 > 注�:`Temporal` 尚未在所有环境中�用,必�时请考虑使用 polyfill。 ```js const client = axios.create({ parseReviver: (key, value, context) => { // 示例:精度安全的 BigInt 解� if (typeof value === 'number' && context?.source) { const isInteger = Number.isInteger(value); const isUnsafe = !Number.isSafeInteger(value); const isValidIntegerString = /^-?\d+$/.test(context.source); if (isInteger && isUnsafe && isValidIntegerString) { try { return BigInt(context.source); } catch { // 兜底:如果解�失败则返回原始值 } } } // 示例:将日期水�为 Temporal 对象 if ( typeof value === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(value) && typeof Temporal !== 'undefined' && Temporal?.PlainDate ) { return Temporal.PlainDate.from(value); } return value; }, }); ``` ### `headers` `headers` 是�请求��的 HTTP 请求头,默认将 `Content-Type` 设置为 `application/json`。 ### `params` `params` 是�请求��的 URL 查询�数,必须是普通对象或 URLSearchParams 对象。如果 `url` 中已包�查询�数,它们将与 `params` 对象�并。 ### `paramsSerializer` `paramsSerializer` 函数�许你在�数��到�务器之�自定义 `params` 对象的�列化方�,有多个�用选项,详�本页末尾的完整请求�置示例。 在 TypeScript 中,`AxiosRequestConfig` 使用 `P` 表示 `params`,自定义 `paramsSerializer` 会接收�一个类型: ```ts interface SearchParams { query: string; page?: number; } const config: AxiosRequestConfig = { params: { query: "axios", page: 1 }, paramsSerializer: (params) => `${params.query}:${params.page ?? 1}`, }; ``` #### 严格的 RFC 3986 百分�编� axios 默认会将 `%3A`�`%24`�`%2C` 和 `%20` 解�回 `:`�`$`�`,` 和 `+`,以���读性(其中 `+` �循查询字符串中表示空格的 `application/x-www-form-urlencoded` 约定)。这些字符在 [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986#section-3.4) 中对查询组件而言都是�法的,因此默认输出是正确的。但部分�端�求严格的百分�编�,会拒�这��读形�。 �通过 `encode` 选项覆盖默认编�器: ```js // �次请求:对查询值使用严格的 RFC 3986 百分�编� axios.get('/foo', { params: { filter: JSON.stringify({ startedAt: '2026-01-23' }) }, paramsSerializer: { encode: encodeURIComponent }, }); // 也�在实例默认值中设置 const client = axios.create({ paramsSerializer: { encode: encodeURIComponent }, }); ``` ### `data` `data` 是作为请求体��的数�,�以是字符串�普通对象�Buffer�ArrayBuffer�FormData�Stream 或 URLSearchParams,仅适用于 `PUT`�`POST`�`DELETE` 和 `PATCH` 请求方法。在未设置 `transformRequest` 的情况下,必须是以下类型之一: - string�普通对象�ArrayBuffer�ArrayBufferView�URLSearchParams - 仅�览器:FormData�File�Blob - React Native:FormData - 仅 Node.js:Stream�Buffer�FormData(form-data 包) 对于�览器�Web Worker 和 React Native 的 `FormData`,��手动设置 `Content-Type`;�行时会自行添加 multipart boundary。 对于�供了 `getHeaders()` 方法的 Node.js `FormData` 对象,axios 默认会�制其返回的所有请求头,以�� v1 兼容性。如果 `FormData` 对象是自定义的或�完全�信,�设置 `formDataHeaderPolicy: 'content-only'`,仅�制 `Content-Type` 和 `Content-Length`,其他请求头则通过请求 `headers` �置显�设置。 ### `formDataHeaderPolicy` 控制 axios 如何�制 Node.js `FormData#getHeaders()` 返回的请求头。默认值为 `'legacy'`,��制所有返回的请求头以�留现有的 v1 行为。设置为 `'content-only'` 时,仅从 `getHeaders()` �制 `Content-Type` 和 `Content-Length`。 ### 使用 Symbol 键的自定义选项 �置�并会�留自身的��枚举的 Symbol 属性。TypeScript 应用�以通过模�扩充为 `AxiosRequestConfig` 添加特定 Symbol 键,然�在请求拦截器或适�器的 `InternalAxiosRequestConfig` 中读�该选项。继承的或��枚举的 Symbol 属性�会被�制。示例请�阅 [TypeScript 指�](/pages/advanced/type-script#使用-symbol-键的自定义请求�置)。 ### `timeout` `timeout` 是请求超时�等待的毫秒数。如果请求耗时超过 `timeout`,请求将被中止。 ### `withCredentials` `withCredentials` 属性指示跨域 Access-Control 请求是�应�带 cookie�授�请求头或 TLS 客户端�书等凭�。该设置对��请求无效。 ### `adapter` `adapter` �许自定义请求处�方�,便于测试。返回一个 Promise 并�供有效的�应,详�[适�器](/pages/advanced/adapters)文档。我们还�供了多个内置适�器,Node.js 默认使用 `http`,�览器默认使用 `xhr`。内置适�器列表如下: - fetch - http - xhr 你也�以传入一个适�器数组,axios 将使用当�环境支�的第一个适�器。 ### `auth` `auth` 表示使用 HTTP Basic 认�,并�供凭�。这将设置 `Authorization` 请求头,覆盖任何通过 `headers` 自定义的 `Authorization` 请求头。如果�略 `auth`,Node.js HTTP 和 fetch 适�器�以从请求 URL 中�� Basic 认�凭�,例如 `https://user:pass@example.com`;URL 中�过百分�编�的凭�会先解�,且 `auth` 始终优先于 URL 中的凭�。在 Node.js HTTP 适�器中,Basic 认�会在���定�时�留,并在跨��定�时剥离。请注�,仅 HTTP Basic 认��通过此�数�置,Bearer 令牌等请改用自定义 `Authorization` 请求头。 ### `responseType` `responseType` 指示�务器�应的数�类型,�以是以下之一: - arraybuffer - document - json - text - stream - blob(仅�览器) - formdata(仅 fetch 适�器) ### `responseEncoding` `responseEncoding` 指示解��应时使用的编�,支�以下选项: - ascii - ASCII - ansi - ANSI - binary - BINARY - base64 - BASE64 - base64url - BASE64URL - hex - HEX - latin1 - LATIN1 - ucs-2 - UCS-2 - ucs2 - UCS2 - utf-8 - UTF-8 - utf8 - UTF8 - utf16le - UTF16LE ::: tip 注�:当 `responseType` 为 `stream` 或客户端请求时,此选项将被忽略 ::: ### `xsrfCookieName` `xsrfCookieName` 是用作 `XSRF` 令牌值的 cookie �称。 ### `xsrfHeaderName` `xsrfHeaderName` 是用作 `XSRF` 令牌值的请求头�称。 ### `withXSRFToken` `withXSRFToken` 控制 axios 在�览器请求中是�读� XSRF cookie 并设置 XSRF 请求头。�选值如下: - `undefined` _(默认)_ — 仅在��请求时设置 XSRF 请求头。 - `true` — 始终设置 XSRF 请求头,包括跨域请求。 - `false` — 永�设置 XSRF 请求头。 - `(config: InternalAxiosRequestConfig) => boolean | undefined` — 回调函数,按请求决定是�设置,会接收内部 config 对象。 ```ts withXSRFToken: boolean | undefined | ((config: InternalAxiosRequestConfig) => boolean | undefined); ``` ::: warning 跨域 XSRF 与 `withCredentials` `withCredentials` 控制跨站请求是��带凭�(cookie�HTTP 认�)。在较旧版本的 axios 中,设置 `withCredentials: true` 会��地让 axios 在跨域请求中设置 XSRF 请求头。新版本 axios 将这两个关注点分开:�在跨域请求中�� XSRF 请求头,必须**�时**设置 `withCredentials: true` 和 `withXSRFToken: true`。 ```js axios.get('/user', { withCredentials: true, withXSRFToken: true }); ``` ::: ### `onUploadProgress` `onUploadProgress` 函数�许你监�上传进度。 ### `onDownloadProgress` `onDownloadProgress` 函数�许你监�下载进度。 ### `maxContentLength` `maxContentLength` 属性定义�应内容�许的最大字节数。Node.js HTTP 适�器会对缓冲�应和���应执行该�制。fetch 适�器会在�应声明了长度��应��跟踪,或�应大��确定时执行该�制。 > ⚠� **安全�示:** 默认值为 `-1`(��制)。�应�加�制�加上 gzip/deflate/brotli/zstd 解压,会带�解压炸弹导致的拒��务风险。 > 在访问�完全�信的�务器时,请显�设置该�制。 ### `maxBodyLength` `maxBodyLength` 属性定义请求体�许的最大字节数。Node.js HTTP 适�器会执行该�制;fetch 适�器会在请求体长度�确定时执行该�制。 ### `redact` `redact` 属性是一个�选的�置键�数组,用于在 `AxiosError` 通过 `toJSON()` �列化时对匹�的键进行脱�。匹��区分大�写,并会在�列化�的请求�置中递归进行,命中的值会被替�为 `[REDACTED ****]`。 `redact` 仅影�错误�列化,�会修改请求数��请求头或原始�置对象。 ```js axios .get('/user/12345', { headers: { Authorization: 'Bearer token' }, auth: { username: 'me', password: 'secret' }, redact: ['authorization', 'password'], }) .catch((error) => { console.log(error.toJSON().config); }); ``` ### `validateStatus` `validateStatus` 函数�许你覆盖默认的状��验�逻辑。默认情况下,axios 会在状���在 200-299 范围内时拒� Promise。你�以�供自定义的 `validateStatus` 函数�覆盖此行为,该函数应在状��在你希望接�的范围内时返回 `true`。 默认情况下,显�设置 `validateStatus: undefined` 会�留旧行为并 resolve 所有�应状��,因为 `transitional.validateStatusUndefinedResolves` 默认值为 `true`。如果希望显�的 `validateStatus: undefined` 表现得�未设置 `validateStatus` 一样,请将 `transitional.validateStatusUndefinedResolves` 设置为 `false`;这样 axios 会使用已�置/默认的验�器,并默认拒�� 2xx �应。 `validateStatus: null` �会接�所有�应状��。如果你�用了该过渡行为,但确实希望所有状��都 resolve,请使用 `validateStatus: null` 或返回 `true` 的验�器。 ```js axios.get('/user/12345', { validateStatus: undefined, transitional: { validateStatusUndefinedResolves: false, }, }); ``` ### `maxRedirects` `maxRedirects` 属性定义最大�定�次数,设置为 0 时�跟�任何�定�。 ### `sensitiveHeaders` `sensitiveHeaders` 属性是一个�选数组,用于列出承载密钥的自定义请求头�称(例如 `X-API-Key`)。Node.js HTTP 适�器在跟��定�到���时会移除这些请求头。匹��区分大�写。���定�会�留这些请求头。如果 `maxRedirects` 为 `0`,axios �会跟��定�,`sensitiveHeaders` 也�会使用。 ```js axios.get('https://api.example.com/users', { headers: { 'X-API-Key': 'secret' }, sensitiveHeaders: ['X-API-Key'], }); ``` ### `beforeRedirect` `beforeRedirect` 函数�许你在请求�定��对其进行修改,�用于调整�定�时的请求选项�检查最新的�应头或通过抛出错误��消请求。当 `maxRedirects` 设置为 0 时,�会使用 `beforeRedirect`。 ```js beforeRedirect: (options, { headers }) => { if (options.hostname === 'example.com' && options.protocol === 'https:') { options.auth = 'user:password'; } }; ``` ::: warning 安全�示:在�定�时�新注入凭� `beforeRedirect` 钩�在�定�过程中**�感请求头被剥离之�**�行。出于安全考虑,`follow-redirects` 库会在�议�级(HTTPS → HTTP)时移除凭�。由于 `beforeRedirect` 在此之��行,如果�检查目标�议就�新注入凭�,�能会泄露�感数�。仅对�信的 HTTPS 目标�新添加凭�,��在被�级的�定�上�新添加凭�。 ::: ### `socketPath` `socketPath` 属性定义用于替代 TCP 连接的 UNIX 套接字路径,例如 `/var/run/docker.sock`,用于� Docker 守护进程��请求。`socketPath` 和 `proxy` �能指定其中一个,如果两者都指定,则使用 `socketPath`。 :::warning 安全�示 设置 `socketPath` �,请求 URL 中的主机�和端�将被忽略,axios 会直接与指定的 Unix 域套接字通信。如果请求�置中有任何部分�自用户输入(例如在转�或�并请求选项的代�/Webhook 处�程�中),攻击者�以注入 `socketPath` 将���定�到特�本地套接字,如 `/var/run/docker.sock`�`/run/containerd/containerd.sock` 或 `/run/systemd/private`,从而完全绕过基于主机�的 SSRF 防护(CWE-918)。应对�自��信输入的�置进行过滤或仅�许特定键,并/或使用 `allowedSocketPaths`(�下文)�制接�的套接字路径。 ::: ### `allowedSocketPaths` �制�通过 `socketPath` 使用的套接字路径。接�一个字符串或字符串数组。设置�,axios 会解� `socketPath` 并与�个�目(�样解��)比较;若无匹�,请求将以 `ERR_BAD_OPTION_VALUE` 错误�的 `AxiosError` 被拒�。未设置(默认)时,`socketPath` 行为与以往一致。 ```js const client = axios.create({ allowedSocketPaths: ['/var/run/docker.sock'], }); // �许 await client.get('http://localhost/v1.45/info', { socketPath: '/var/run/docker.sock' }); // 拒� — �在白��中 await client.get('http://localhost/pods', { socketPath: '/var/run/kubelet.sock' }); ``` 空数组 (`allowedSocketPaths: []`) 会阻止所有套接字路径。 ### `transport` `transport` 属性定义请求使用的传输方�,适用于通过���议(如 `http2`)�起请求的场景。 ### `httpAgent` 和 `httpsAgent` `httpAgent` 和 `httpsAgent` 分别定义在 Node.js 中执行 HTTP 和 HTTPS 请求时使用的自定义代�,�用于添加 `keepAlive` 等默认未�用的选项。 ### `proxy` `proxy` 定义代��务器的主机��端�和�议,也�以通过常规的 `http_proxy` 和 `https_proxy` 环境���定义代�。 如果你使用环境���置代�,还�以定义 `no_proxy` 环境��,以逗�分隔的方�列出�需�代�的域�。 在支�原生环境��代�的 Node.js 版本中,当所选 `httpAgent` 或 `httpsAgent` �用了 `proxyEnv` 时,axios 会将环境��代�处�交给 Node,包括使用 `NODE_USE_ENV_PROXY=1`�`--use-env-proxy` 或 `NODE_OPTIONS=--use-env-proxy` �动的进程。没有 `proxyEnv` 的自定义 agent �继续使用 axios 的环境��代�解�。显�的 `proxy` �置�由 axios 处�。 设置为 `false` ��用代�,忽略环境��。`auth` 表示使用 HTTP Basic 认�连接代�并�供凭�,这将设置 `Proxy-Authorization` 请求头,覆盖任何通过 `headers` 自定义的 `Proxy-Authorization` 请求头。如果代��务器使用 HTTPS,则必须将�议设置为 `https`。 通过代�转�时,如果用户在 `headers` 中�供了 `Host` 请求头,axios 会�留它(�区分大�写匹� `host` / `Host` / `HOST`)。这样你就�以指�一个与请求 URL ��的虚拟主机——例如,访问 `127.0.0.1:4000`,但让代�将请求当作 `example.com` 处�。如果未�供 `Host` 请求头,axios �会�以�一样将其默认设为请求 URL 的 `hostname:port`。 对于 `https://` 目标,axios 会通过代�建立 CONNECT 隧�,并与�站执行端到端 TLS。`Proxy-Authorization` �会��在 CONNECT 请求上,�会��到被 TLS 包裹的�站请求中。`httpsAgent` 的 TLS 选项(如 `ca`�`cert`�`key` 和 `rejectUnauthorized`)会转�给生�的隧�代�,因此�会应用到�站 TLS 连接。如果你�供的是 `HttpsProxyAgent`,axios 会让该代�自行处�隧�。 ```js proxy: { protocol: "https", host: "127.0.0.1", hostname: "localhost", // 如果�时定义了 "host" 和 "hostname",则优先使用 "hostname" port: 9000, auth: { username: "mikeymike", password: "rapunz3l" } }, ``` ### `cancelToken` `cancelToken` 属性�许你创建一个�消令牌,用于�消请求。详�[�消请求](/pages/advanced/cancellation)文档。 ### `signal` `signal` 属性�许你�请求传入一个 `AbortSignal` 实例,从而通过 `AbortController` API �消请求。 ### `decompress` `decompress` 属性指示是�自动解压�应数�,默认值为 `true`。当当� Node.js �行时�供对应的 zlib 解压器时,Node.js HTTP 适�器支� gzip�deflate�brotli 和 zstd。 ### `insecureHTTPParser` 指示是�使用接�无效 HTTP 请求头的�安全 HTTP 解�器,�用于与�符�规范的 HTTP 实现互通。�建议使用�安全解�器。 请注�,`insecureHTTPParser` 选项仅在 Node.js 12.10.0 �更高版本中�用。请阅读 [Node.js 文档](https://nodejs.org/en/blog/vulnerability/february-2020-security-releases/#strict-http-header-parsing-none)以获�更多信�。完整选项列表�[此处](https://nodejs.org/dist/latest-v12.x/docs/api/http.html#http_http_request_url_options_callback)。 ### `transitional` `transitional` 属性�许你�用或�用�些过渡性功能,�用选项如下: - `silentJSONParsing`:若设置为 `true` _(默认)_,axios 会在 JSON 解�失败时�默忽略错误,并�留原始�应字符串。设置为 `false` 则会抛出 `SyntaxError`。 ::: tip ��说明 此选项仅在 `responseType` **显�**设置为 `'json'` 时生效。当未指定 `responseType` 时,axios 会通过 `forcedJSONParsing` �试解�为 JSON,若失败则�论此设置如何,都会�默返回原始字符串。如果希望无效 JSON 抛出错误,请�时设置: ```js { responseType: 'json', transitional: { silentJSONParsing: false } } ``` ::: - `forcedJSONParsing`:强制 axios 将�应解�为 JSON,�使�应�是有效的 JSON。适用于返回无效 JSON 的 API。 - `clarifyTimeoutError`:在请求超时时�供更清晰的错误信�,适用于调试超时问题。 - `validateStatusUndefinedResolves`:若设置为 `true` _(默认)_,显�的 `validateStatus: undefined` 会出于兼容性 resolve 所有�应状��。设置为 `false` �,显�的 `undefined` 会�未设置 `validateStatus` 一样处�,axios 将使用已�置/默认的验�器。如果你确实希望所有状��都 resolve,请使用 `validateStatus: null` 或返回 `true` 的验�器。 - `advertiseZstdAcceptEncoding`:设为 `true` 时,如果当� Node.js �行时支� zstd 解压,axios 会在默认 `Accept-Encoding` 请求头中加入 `zstd`。在�支�且 `decompress` 为 `true` 时,zstd �应�会自动解压。 - `legacyInterceptorReqResOrdering`:设置为 true 时使用旧版拦截器请求/�应排�。 ### `env` `env` 属性�许你设置一些�置选项,例如用于自动将数��列化为 FormData 对象的 FormData 类。 - FormData: window?.FormData || global?.FormData ### `formSerializer` `formSerializer` 选项�许你�置普通对象作为请求 `data` 时如何�列化为 `multipart/form-data`。�用选项: - `visitor` — 对�个值递归调用的自定义访问者函数 - `dots` — 使用点�表示法代替方括�表示法 - `metaTokens` — �留特殊的键�缀(如 `{}`) - `indexes` — 控制数组键的方括�格�(`null` / `false` / `true`) - `maxDepth` _(默认:`100`)_ — 抛出 `AxiosError`(错误� `ERR_FORM_DATA_DEPTH_EXCEEDED`)�的最大嵌套深度。设置为 `Infinity` ��用。 - `Blob` — 在符�规范的 `FormData` 中转�类 ArrayBuffer 值时使用的 Blob 构造函数。 详� [multipart/form-data](/pages/advanced/multipart-form-data-format) 页�以�本页末尾的完整请求�置示例。 ### `maxRate` `maxRate` 属性定义上传和/或下载的最大**带宽**(字节/秒)。接��个数字(�时适用于两个方�)或两元素数组 `[uploadRate, downloadRate]`,�个元素为字节/秒�制。例如,`100 * 1024` 表示 100 KB/s。详�[速率�制](/pages/advanced/rate-limiting)中的示例。 ## 完整请求�置示例 ```js { url: "/posts", method: "get", baseURL: "https://jsonplaceholder.typicode.com", allowAbsoluteUrls: true, transformRequest: [function (data, headers) { return data; }], transformResponse: [function (data) { return data; }], headers: {"X-Requested-With": "XMLHttpRequest"}, params: { postId: 5 }, paramsSerializer: { // 自定义编�函数,以迭代方��个�列化键值对。 encode?: (param: string): string => { /* 在此执行自定义�作并返回转��的字符串 */ }, // 对整个�数进行自定义�列化的函数,�许用户模拟 1.x 之�的行为。 serialize?: (params: Record, options?: ParamsSerializerOptions ), // �置数组索引在�数中的格�。 // 三��用选项: // (1) indexes: null(�添加方括�) // (2)(默认)indexes: false(添加空方括�) // (3) indexes: true(添加带索引的方括�) indexes: false, // �列化�数时的最大对象嵌套深度。超过时抛出 AxiosError // (ERR_FORM_DATA_DEPTH_EXCEEDED)。默认:100。设置为 Infinity ��用。 maxDepth: 100 }, data: { firstName: "Fred" }, // `data` 是请求专属的:axios �会从默认值继承或深度�并它。 // 如需添加共享请求体字段,请使用请求拦截器或 transformRequest。 formDataHeaderPolicy: "legacy", // �一�将数���到请求体的语法,仅适用于 POST 方法,���值,���键 data: "Country=Brasil&City=Belo Horizonte", timeout: 1000, withCredentials: false, adapter: function (config) { // 在此执行自定义逻辑 }, adapter: "xhr", auth: { username: "janedoe", password: "s00pers3cret" }, responseType: "json", responseEncoding: "utf8", xsrfCookieName: "XSRF-TOKEN", xsrfHeaderName: "X-XSRF-TOKEN", withXSRFToken: boolean | undefined | ((config: InternalAxiosRequestConfig) => boolean | undefined), onUploadProgress: function ({loaded, total, progress, bytes, estimated, rate, upload = true}) { // 在此处� axios 进度事件 }, onDownloadProgress: function ({loaded, total, progress, bytes, estimated, rate, download = true}) { // 在此处� axios 进度事件 }, maxContentLength: 2000, maxBodyLength: 2000, redact: ['authorization', 'password'], validateStatus: function (status) { return status >= 200 && status < 300; }, maxRedirects: 21, sensitiveHeaders: ['X-API-Key'], beforeRedirect: (options, { headers }) => { if (options.hostname === "typicode.com") { options.auth = "user:password"; } }, socketPath: null, allowedSocketPaths: null, transport: undefined, httpAgent: new http.Agent({ keepAlive: true }), httpsAgent: new https.Agent({ keepAlive: true }), proxy: { protocol: "https", host: "127.0.0.1", // hostname: "127.0.0.1" // 如果�时定义了 "host" 和 "hostname",则优先使用 "hostname" port: 9000, auth: { username: "mikeymike", password: "rapunz3l" } }, cancelToken: new CancelToken(function (cancel) { cancel("Operation has been canceled."); }), signal: new AbortController().signal, decompress: true, insecureHTTPParser: undefined, transitional: { silentJSONParsing: true, forcedJSONParsing: true, clarifyTimeoutError: false, validateStatusUndefinedResolves: true, advertiseZstdAcceptEncoding: false, legacyInterceptorReqResOrdering: true, }, env: { FormData: window?.FormData || global?.FormData }, formSerializer: { // 自定义访问者函数,用于�列化表�值 visitor: (value, key, path, helpers) => {}; // 使用点�表示法代替方括�格� dots: boolean; // 在�数键中�留特殊结尾(如 {}) metaTokens: boolean; // 使用数组索引格�: // null - �添加方括� // false - 添加空方括� // true - 添加带索引的方括� indexes: boolean; // 最大对象嵌套深度。超过时抛出 AxiosError (ERR_FORM_DATA_DEPTH_EXCEEDED)。 // 默认:100。设置为 Infinity ��用。 maxDepth: 100; }, maxRate: [ 100 * 1024, // 上传�制 100KB/s 100 * 1024 // 下载�制 100KB/s ] } ``` axios-axios-aa54a5e/docs/zh/pages/advanced/request-method-aliases.md000066400000000000000000000134721526660636600256200ustar00rootroot00000000000000# 请求别� axios �供了一组�起 HTTP 请求的别�方法,这些别�是 `request` 方法的快�方�,设计简��使用方便。 axios 尽��循 RFC 7231 和 RFC 5789 规范,别�方法与这些规范中定义的 HTTP 方法��一致。 ### `axios` axios �以通过仅传入�置对象��起 HTTP 请求,完整的�置对象文档�[此处](/pages/advanced/request-config)。 ```ts axios( url: string | AxiosRequestConfig, config?: AxiosRequestConfig ): Promise; ``` 请求方法的泛型�数顺�为 ``:�应数��自定义�应�请求数�和查询�数。`P` 添加在最�,因此现有的显� `T`�`R` 和 `D` �数�义����。未�供自定义 `R` 时,默认 `AxiosResponse` 会在 `response.config` 中�留 `D` 和 `P`。 ## 方法别� 以下是�用的请求别�方法: ### `request` `request` 方法是�起 HTTP 请求的主方法,接�一个�置对象并返回解�为�应对象的 Promise,�用于�起任�类型的 HTTP 请求。 ```ts axios.request(config: AxiosRequestConfig): Promise; ``` ### `get` `get` 方法用于�起 GET 请求,接� URL 和�选�置对象,返回解�为�应对象的 Promise。 ```ts axios.get(url: string, config?: AxiosRequestConfig): Promise; ``` ### `delete` `delete` 方法用于�起 DELETE 请求,接� URL 和�选�置对象,返回解�为�应对象的 Promise。 ```ts axios.delete(url: string, config?: AxiosRequestConfig): Promise; ``` ### `head` `head` 方法用于�起 HEAD 请求,接� URL 和�选�置对象,返回解�为�应对象的 Promise。 ```ts axios.head(url: string, config?: AxiosRequestConfig): Promise; ``` ### `options` `options` 方法用于�起 OPTIONS 请求,接� URL 和�选�置对象,返回解�为�应对象的 Promise。 ```ts axios.options(url: string, config?: AxiosRequestConfig): Promise; ``` ### `post` `post` 方法用于�起 POST 请求,接� URL��选数�对象和�选�置对象,返回解�为�应对象的 Promise。 ```ts axios.post(url: string, data?: D, config?: AxiosRequestConfig): Promise; ``` ### `put` `put` 方法用于�起 PUT 请求,接� URL��选数�对象和�选�置对象,返回解�为�应对象的 Promise。 ```ts axios.put(url: string, data?: D, config?: AxiosRequestConfig): Promise; ``` ### `patch` `patch` 方法用于�起 PATCH 请求,接� URL��选数�对象和�选�置对象,返回解�为�应对象的 Promise。 ```ts axios.patch(url: string, data?: D, config?: AxiosRequestConfig): Promise; ``` ### `query` `query` 方法用于�起 QUERY 请求,这是一�安全且幂等的��以�带请求体的方法。它接� URL��选数�对象和�选�置对象,返回解�为�应对象的 Promise。当读�类�作的�数过于��或�感��适�放在 URL 中时,�以使用该方法。 ```ts axios.query(url: string, data?: D, config?: AxiosRequestConfig): Promise; ``` ```js // 将��的�索�件作为请求体�� const { data } = await axios.query("/api/search", { selector: ["name", "email"], filter: { active: true, role: "admin" }, }); ``` ::: warning �案规范 QUERY 方法目�由 IETF 的 [Internet-Draft](https://datatracker.ietf.org/doc/draft-ietf-httpbis-safe-method-w-body/) 定义,尚未�为正�标准。其语义乃至方法�称都�能在最终�布��生�化,并且�务器�代�和 CDN 的支�情况�差��。在用于生产环境之�,请确认你的整个链路都能够正确处� `QUERY` 请求。 ::: ### `getUri` `getUri` 方法返回给定�置在�实际�起请求的情况下会��的 URL。它会应用 `baseURL`�`paramsSerializer` 和 `params`,因此你拿到的字符串与 axios 实际�出的 URL 相�。�用于构建链接�调试�列化逻辑,或在�一个请求中�用解��的 URL。 ```ts axios.getUri(config?: AxiosRequestConfig): string; ``` ```js const url = axios.getUri({ url: "/users", baseURL: "https://api.example.com", params: { active: true, role: "admin" }, }); // "https://api.example.com/users?active=true&role=admin" ``` ::: tip 在实例上调用 `getUri`(`instance.getUri(config)`)会继承该实例的 `baseURL`�`params` 和 `paramsSerializer` 默认值。 ::: ## 表�数�快�方法 这些方法与上述对应方法等价,但会预设 `Content-Type` 为 `multipart/form-data`,是上传文件或�交 HTML 表�的推�方�。 ### `postForm` ```ts axios.postForm(url: string, data?: D, config?: AxiosRequestConfig): Promise; ``` ```js // 从�览器文件输入框上传文件 await axios.postForm("/api/upload", { file: document.querySelector("#fileInput").files[0], description: "Profile photo", }); ``` ### `putForm` ```ts axios.putForm(url: string, data?: D, config?: AxiosRequestConfig): Promise; ``` ```js // 用表�数�替�资� await axios.putForm("/api/users/1/avatar", { avatar: document.querySelector("#avatarInput").files[0], }); ``` ### `patchForm` ```ts axios.patchForm(url: string, data?: D, config?: AxiosRequestConfig): Promise; ``` ```js // 使用表�数�更新特定字段 await axios.patchForm("/api/users/1", { displayName: "New Name", avatar: document.querySelector("#avatarInput").files[0], }); ``` ::: tip `postForm`�`putForm` 和 `patchForm` 接�与基础方法相�的所有数�类型——普通对象�`FormData`�`FileList` 以� `HTMLFormElement`。更多示例请�阅[文件上传](/pages/advanced/file-posting)。 ::: axios-axios-aa54a5e/docs/zh/pages/advanced/response-schema.md000066400000000000000000000044511526660636600243240ustar00rootroot00000000000000# �应结构 �个 axios 请求都会解�为具有以下结构的�应对象,在�览器和 Node.js 环境中��一致。 ```js { // �务器�供的�应数�。 // 使用 `transformResponse` 时,这将是最�一次转�的结果。 data: {}, // �务器�应的 HTTP 状��(如 200�404�500)。 status: 200, // 与状��对应的 HTTP 状�消�(如 "OK"�"Not Found")。 statusText: "OK", // �务器��的�应头。 // �应头�称�为�写,�通过方括�或点�表示法访问。 headers: {}, // 本次请求使用的 axios �置,包括 baseURL�headers�timeout�params �其他选项。 config: {}, // 底层请求对象。 // 在 Node.js 中:最�一个 `http.ClientRequest` 实例(�过任何�定��)。 // 在�览器中:`XMLHttpRequest` 实例。 request: {}, } ``` 在 TypeScript 中,`AxiosResponse` 分别表示�应数�(`T`)�请求数�(`D`)��应头(`H`)和查询�数(`P`)的类型。�置会�留两�请求侧类型: ```ts interface AxiosResponse { data: T; status: number; statusText: string; headers: (H & RawAxiosResponseHeaders) | AxiosResponseHeaders; config: InternalAxiosRequestConfig; request?: any; } ``` ## 访问�应字段 实际使用中,你通常�需�解构出所需的部分: ```js const { data, status, headers } = await axios.get("/api/users/1"); console.log(status); // 200 console.log(headers["content-type"]); // "application/json; charset=utf-8" console.log(data); // { id: 1, name: "Jay", email: "jay@example.com" } ``` ## 检查状�� axios 默认对任何 2xx �应 resolve Promise,对超出该范围的�应 reject Promise。�以通过 `validateStatus` �置选项自定义此行为: ```js const response = await axios.get("/api/resource", { validateStatus: (status) => status < 500, // 500 以下的所有状��� resolve }); ``` ## 访问�应头 无论�务器如何��,所有�应头�称�为�写: ```js const response = await axios.get("/api/resource"); // 以下两�写法等价 const contentType = response.headers["content-type"]; const contentType2 = response.headers.get("content-type"); ``` axios-axios-aa54a5e/docs/zh/pages/advanced/retry.md000066400000000000000000000070711526660636600223760ustar00rootroot00000000000000# �试与错误�� 网络请求�能因瞬时原因而失败——�务器抖动�短暂的网络中断或���应。在拦截器中实现�试策略,�以让你�明地处�这些失败,无需在业务代�中添加��的�试逻辑。 ## 基本�试(使用�应拦截器) 最简�的方�是�获特定错误状��,并在有�次数内立��新��原始请求: ```js import axios from "axios"; const api = axios.create({ baseURL: "https://api.example.com" }); const MAX_RETRIES = 3; api.interceptors.response.use( (response) => response, async (error) => { const config = error.config; // 仅在网络错误或 5xx �务器错误时�试 const shouldRetry = !error.response || (error.response.status >= 500 && error.response.status < 600); if (!shouldRetry) { return Promise.reject(error); } config._retryCount = config._retryCount ?? 0; if (config._retryCount >= MAX_RETRIES) { return Promise.reject(error); } config._retryCount += 1; return api(config); } ); ``` ## 指数退� 失败�立��试�能会使本已压力过大的�务器雪上加霜。指数退�策略会在�次�试之间等待��增长的时间: ```js const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); api.interceptors.response.use( (response) => response, async (error) => { const config = error.config; const shouldRetry = !error.response || (error.response.status >= 500 && error.response.status < 600); if (!shouldRetry) return Promise.reject(error); config._retryCount = config._retryCount ?? 0; if (config._retryCount >= 3) return Promise.reject(error); config._retryCount += 1; // �次�试�分别等待 200ms�400ms�800ms…… const backoff = 100 * 2 ** config._retryCount; await delay(backoff); return api(config); } ); ``` ## �应 429(��)时使用 Retry-After 当�务器返回 `429 Too Many Requests` 时,通常会在�应头中包� `Retry-After` 字段,明确告知你需�等待多长时间: ```js api.interceptors.response.use( (response) => response, async (error) => { const config = error.config; if (error.response?.status !== 429) return Promise.reject(error); config._retryCount = config._retryCount ?? 0; if (config._retryCount >= 3) return Promise.reject(error); config._retryCount += 1; const retryAfterHeader = error.response.headers["retry-after"]; const waitMs = retryAfterHeader ? parseFloat(retryAfterHeader) * 1000 // 请求头��为秒 : 1000; // 默认等待 1 秒 await new Promise((resolve) => setTimeout(resolve, waitMs)); return api(config); } ); ``` ## 针对特定请求�用�试 如果�些请求�应被�试(例如�幂等的�更�作,�希望��执行),�以在请求�置中添加一个标志: ```js // 在�试逻辑之�,在拦截器中添加以下判断: if (config._noRetry) return Promise.reject(error); // 然�在特定调用中�用�试: await api.post("/payments/charge", body, { _noRetry: true }); ``` ## 结��试与�消 使用 `AbortController` �以�消正在等待退�延迟的请求: ```js const controller = new AbortController(); try { await api.get("/api/data", { signal: controller.signal }); } catch (error) { if (axios.isCancel(error)) { console.log("Request aborted by user"); } } // 从其他地方�消请求(以�任何待处�的�试延迟): controller.abort(); ``` axios-axios-aa54a5e/docs/zh/pages/advanced/testing.md000066400000000000000000000076411526660636600227110ustar00rootroot00000000000000# 测试 测试使用 axios �起 HTTP 请求的代��常简�。推�的方�是对 axios 本身进行 mock,让测试在�触�真实网络的情况下�行,从而完全控制代�收到的�应内容。 ## 使用 Vitest 或 Jest 进行 Mock Vitest 和 Jest 都支�通过 `vi.mock` / `jest.mock` 进行模�级 mock。你�以 mock 整个 axios 模�,并控制�个方法的返回值: ```js // user-service.js import axios from "axios"; export async function getUser(id) { const { data } = await axios.get(`/api/users/${id}`); return data; } ``` ```js // user-service.test.js import { describe, it, expect, vi } from "vitest"; import axios from "axios"; import { getUser } from "./user-service"; vi.mock("axios"); describe("getUser", () => { it("returns user data on success", async () => { const mockUser = { id: 1, name: "Jay" }; // 让 axios.get 返回我们的��应 axios.get.mockResolvedValueOnce({ data: mockUser }); const result = await getUser(1); expect(result).toEqual(mockUser); expect(axios.get).toHaveBeenCalledWith("/api/users/1"); }); it("throws when the request fails", async () => { axios.get.mockRejectedValueOnce(new Error("Network error")); await expect(getUser(1)).rejects.toThrow("Network error"); }); }); ``` ## Mock AxiosError �测试检查 `error.response` 的错误处�路径,�以直接创建一个 `AxiosError` 实例: ```js import axios, { AxiosError } from "axios"; import { vi } from "vitest"; const mockError = new AxiosError( "Not Found", "ERR_BAD_REQUEST", {}, // config {}, // request { // response status: 404, statusText: "Not Found", data: { message: "User not found" }, headers: {}, config: {}, } ); axios.get.mockRejectedValueOnce(mockError); ``` ## 使用 axios-mock-adapter [axios-mock-adapter](https://github.com/ctimmerm/axios-mock-adapter) 是一个在 axios 实例上安装自定义适�器的库,在适�器层�拦截请求。这�味�你的拦截器�然会执行,因此更适�集�测试。 ```bash npm install --save-dev axios-mock-adapter ``` ```js import axios from "axios"; import MockAdapter from "axios-mock-adapter"; const mock = new MockAdapter(axios); // Mock GET 请求 mock.onGet("/api/users/1").reply(200, { id: 1, name: "Jay" }); // Mock POST 请求 mock.onPost("/api/users").reply(201, { id: 2, name: "New User" }); // Mock 网络错误 mock.onGet("/api/failing").networkError(); // Mock 超时 mock.onGet("/api/slow").timeout(); ``` 在�个测试之间�置 mock: ```js afterEach(() => { mock.reset(); // 清除所有已注册的处�器 }); ``` ## 测试拦截器 ��独测试拦截器,在测试中创建一个全新的 axios 实例: ```js import axios from "axios"; import MockAdapter from "axios-mock-adapter"; describe("auth interceptor", () => { it("attaches a Bearer token to every request", async () => { const instance = axios.create(); const mock = new MockAdapter(instance); // 添加你的拦截器 instance.interceptors.request.use((config) => { config.headers.set("Authorization", "Bearer test-token"); return config; }); // 通过检查 mock 收到的内容��获请求�置 let capturedConfig; mock.onGet("/api/data").reply((config) => { capturedConfig = config; return [200, {}]; }); await instance.get("/api/data"); expect(capturedConfig.headers["Authorization"]).toBe("Bearer test-token"); }); }); ``` ## 最佳实践 - 始终在模�级别进行 mock(或使用 `MockAdapter`)——��在共享实例的�个方法上进行 mock,因为状��能在测试之间泄�。 - 优先使用 `mockResolvedValueOnce` / `mockRejectedValueOnce`,而�是 `mockResolvedValue`,以确�测试相互隔离,互�影�。 - 测试�试逻辑时,使用 `MockAdapter`,以便被测拦截器在�次�试时都能真正执行。 axios-axios-aa54a5e/docs/zh/pages/advanced/type-script.md000066400000000000000000000131631526660636600235130ustar00rootroot00000000000000# TypeScript `axios` 在 npm 包中通过 `index.d.ts`(ESM)和 `index.d.cts`(CJS)�包�供 TypeScript 类型定义,因此两�模�格�下的类型检查与编辑器支�都开箱�用。 ## 模�解�注�事项 由于 axios �时以 ESM 默认导出和 CJS `module.exports` 两�方��布,存在以下�置注�事项: - 推�使用 `"moduleResolution": "node16"`(由 `"module": "node16"` ��指定),需� TypeScript 4.7 或更高版本。 - 如果你使用 ESM,现有�置应该没有问题。 - 如果你将 TypeScript 编译为 CJS 且无法使用 `"moduleResolution": "node16"`,则必须�用 `esModuleInterop`。 - 如果你使用 TypeScript 对 CJS JavaScript 代�进行类型检查,则�能使用 `"moduleResolution": "node16"`。 ## axios 错误的类型守� 使用 `axios.isAxiosError` 类型守��以在 `catch` �中安全地收窄 `unknown` 错误。收窄之�,你便�以在完整的类型支�下访问 `error.response`�`error.config` 和 `error.code` 等 axios 专有属性。 ```ts import axios from "axios"; let user: User | null = null; try { const { data } = await axios.get("/user?ID=12345"); user = data.userDetails; } catch (error) { if (axios.isAxiosError(error)) { handleAxiosError(error); } else { handleUnexpectedError(error); } } ``` 使用 `axios.isCancel()` �以将�消错误收窄为 `CanceledError`: ```ts const controller = new AbortController(); try { await axios.get("/user?ID=12345", { signal: controller.signal }); } catch (error) { if (axios.isCancel(error)) { handleCancellation(error); } } ``` ## 为请求数�和查询�数添加类型 `AxiosRequestConfig` 使用 `D` 表示请求数�,使用 `P` 表示查询�数。自定义�数�列化器会接收�一个 `P` 类型: ```ts import axios, { type AxiosPromise, type AxiosRequestConfig, type InternalAxiosRequestConfig, } from "axios"; interface RequestBody { includeArchived: boolean; } interface SearchParams { query: string; page?: number; } interface SearchResponse { results: string[]; } const searchConfig: AxiosRequestConfig = { data: { includeArchived: false }, params: { query: "axios", page: 1 }, paramsSerializer: (params) => `${params.query}:${params.page ?? 1}`, }; const response = await axios.get("/search", searchConfig); response.config.data; // RequestBody | undefined response.config.params; // SearchParams | undefined const invalidConfig: AxiosRequestConfig = { // @ts-expect-error `query` 必须是字符串 params: { query: 123 }, }; ``` 默认请求结果会在 `response.config` 上�留 `D` 和 `P`,包括请求别�从带类型的请求�置中推断出这些类型的情况。`RawAxiosRequestConfig`�`InternalAxiosRequestConfig`�`AxiosDefaults`�`CreateAxiosDefaults`�`AxiosResponse`�`AxiosPromise`�`AxiosError`�`CanceledError`��调用实例�适�器和 `mergeConfig()` 也会�留查询�数类型。 请求方法将 `P` 添加为最�一个泛型�数,� ``,因此现有的�应数�(`T`)�自定义�应(`R`)和请求数�(`D`)�数�置����。显��供的自定义�应类型�然控制最终返回值。为����兼容,`P` 默认为 `any`。 适�器或其他显�添加类型的 Promise �以�时�留两�请求类型: ```ts const searchAdapter = ( config: InternalAxiosRequestConfig ): AxiosPromise => Promise.resolve({ data: { results: [] }, status: 200, statusText: "OK", headers: {}, config, }); declare const error: unknown; if (axios.isCancel(error)) { error.config?.data; // RequestBody | undefined error.config?.params; // SearchParams | undefined } ``` ## 带类型的实例与拦截器 将 `axios.create` 的结果标注为 `AxiosInstance`,并将请求拦截器标注为 `InternalAxiosRequestConfig`,��对自定义客户端实现端到端的类型检查: ```ts import axios, { AxiosInstance, InternalAxiosRequestConfig } from "axios"; const apiClient: AxiosInstance = axios.create({ baseURL: "https://api.example.com", timeout: 10000, }); apiClient.interceptors.request.use((config: InternalAxiosRequestConfig) => { // 添加认�令牌�记录日志等 return config; }); ``` ## 使用 Symbol 键的自定义请求�置 axios �并默认�置和�次请求�置时,会�留自身的��枚举的 Symbol 属性。应用�以通过模�扩充为 `AxiosRequestConfig` 添加特定的 Symbol 键,然�在拦截器或适�器的 `InternalAxiosRequestConfig` 中读�该选项: ```ts import axios from "axios"; export const someFlag: unique symbol = Symbol( "some flag used in request interceptor" ); declare module "axios" { interface AxiosRequestConfig { [someFlag]?: boolean; } } axios.interceptors.request.use((config) => { if (config[someFlag]) { config.headers.set("X-Some-Flag", "enabled"); } return config; }); await axios.get("/users", { [someFlag]: true }); ``` �有自身的��枚举的 Symbol 属性会被�制;��枚举或继承的 Symbol 属性�会被�制。 ## 为�应数�添加类型 axios 的请求方法对�应数�类型是泛型的。� `axios.get`(以�其他别�)传入类型�数��为 `response.data` 添加类型: ```ts interface User { id: number; name: string; } const { data } = await apiClient.get("/users/1"); // `data` 的类型为 `User` ``` axios-axios-aa54a5e/docs/zh/pages/advanced/x-www-form-urlencoded-format.md000066400000000000000000000104261526660636600266710ustar00rootroot00000000000000# x-www-form-urlencoded 格� ## URLSearchParams 默认情况下,axios 会将 JavaScript 对象�列化为 `JSON`。如果需�以 [`application/x-www-form-urlencoded` 格�](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST)��数�,�以使用 [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) API,�大多数�览器[已支�](http://www.caniuse.com/#feat=urlsearchparams)该 API,Node.js 从 v10(2018 年�布)开始也[支�](https://nodejs.org/api/url.html#url_class_urlsearchparams)。 ```js const params = new URLSearchParams({ foo: 'bar' }); params.append('extraparam', 'value'); axios.post('/foo', params); ``` ## 查询字符串 对于�支� `URLSearchParams` 的旧版�览器或环境,�以使用 [`qs`](https://github.com/ljharb/qs) 库将对象�列化为 `application/x-www-form-urlencoded` 格�。 ```js const qs = require('qs'); axios.post('/foo', qs.stringify({ bar: 123 })); ``` 如需完全控制请求头和方法,�将 `qs.stringify` 的输出作为请求 `data` 传入,并显�设置 `Content-Type`: ```js import qs from 'qs'; const data = { bar: 123 }; const options = { method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, data: qs.stringify(data), url: '/foo', }; axios(options); ``` 在�常旧的 Node.js 版本中,�以使用 Node.js 内置的 `querystring` 模�。注�该模�在 Node.js v16 中已废弃——新代�请优先使用 `URLSearchParams` 或 `qs`。 ```js const querystring = require('querystring'); axios.post('https://something.com/', querystring.stringify({ foo: 'bar' })); ``` ::: tip 嵌套对象优先使用 `qs` 如果你需��列化嵌套对象,建议使用 `qs` 库,因为 `querystring` 方法在该场景下存在[已知问题](https://github.com/nodejs/node-v0.x-archive/issues/1665)。 ::: ## 自动�列化为 URLSearchParams 从 v0.21.0 起,如果将 `Content-Type` 请求头设置为 `application/x-www-form-urlencoded`,axios 会自动将 JavaScript 对象�列化为 `URLSearchParams`。这�味�你�以直接将 JavaScript 对象传入 axios 请求�置的 `data` 属性。例如,� `POST` 请求传递数�时: ```js const data = { x: 1, arr: [1, 2, 3], arr2: [1, [2], 3], users: [ { name: 'Peter', surname: 'Griffin' }, { name: 'Thomas', surname: 'Anderson' }, ], }; await axios.postForm('https://postman-echo.com/post', data, { headers: { 'content-type': 'application/x-www-form-urlencoded' }, }); ``` `data` 对象将被自动�列化为 `URLSearchParams` 并以 `application/x-www-form-urlencoded` 格���。�务器将收到以下数�: ```json { "x": "1", "arr[]": ["1", "2", "3"], "arr2[0]": "1", "arr2[1][0]": "2", "arr2[2]": "3", "users[0][name]": "Peter", "users[0][surname]": "Griffin", "users[1][name]": "Thomas", "users[1][surname]": "Anderson" } ``` 如果你的�端 body 解�器(如 `express.js` 的 `body-parser`)支�嵌套对象解�,�务器端将自动还原为相�的对象结构: ## �数�列化的深度�制 当 axios 通过 `AxiosURLSearchParams` �列化 `params` 对象时,会调用与 FormData �列化器相�的递归�历器。`maxDepth` 选项(默认 `100`)�制递归的最大深度。超过�制的载�会抛出 `code: 'ERR_FORM_DATA_DEPTH_EXCEEDED'` 的 `AxiosError`,而�是导致调用栈溢出。 共享类型�员 `SerializerOptions.Blob` �影��列化到符�规范的 `FormData`;�列化到 `URLSearchParams` 时没有效果。 ```js // 如果你的 params 对象确实需�超过 100 层嵌套,��高�制: axios.get('/api', { params: deepObject, paramsSerializer: { maxDepth: 200 } }); ``` ::: warning 安全�示 除�你的 schema 确实需�,�则���高 `maxDepth`。默认值 100 ��护将客户端控制的数�作为 `params` 转�给 axios 的�务端代���深层嵌套对象的 DoS 攻击。 ::: ```js var app = express(); app.use(bodyParser.urlencoded({ extended: true })); // 支�编��的请求体 app.post('/', function (req, res, next) { // 将请求体以 JSON 格�回传 res.send(JSON.stringify(req.body)); }); server = app.listen(3000); ``` axios-axios-aa54a5e/docs/zh/pages/getting-started/000077500000000000000000000000001526660636600222425ustar00rootroot00000000000000axios-axios-aa54a5e/docs/zh/pages/getting-started/examples/000077500000000000000000000000001526660636600240605ustar00rootroot00000000000000axios-axios-aa54a5e/docs/zh/pages/getting-started/examples/commonjs.md000066400000000000000000000102161526660636600262270ustar00rootroot00000000000000# JavaScript 示例 ## 导入库 在 CommonJS 环境中,�以使用 `require` 函数导入库;如果使用 Webpack 或 Rollup 等打包工具,则�以使用 `import` 语�。 #### �使用打包工具 ```js const axios = require("axios"); ``` #### 使用打包工具(webpack�rollup�vite 等) ```js import axios from "axios"; ``` ## 使用 then/catch/finally axios 的核心返回的是一个 Promise,你�以使用 `then`�`catch` 和 `finally` 回调�处��应数��错误以�请求完�的逻辑。 ### GET 请求 ```js axios .get("https://jsonplaceholder.typicode.com/posts", { params: { postId: 5, }, }) .then((response) => { console.log(response.data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Request completed"); }); ``` ### POST 请求 ```js axios .post("https://jsonplaceholder.typicode.com/posts", { title: "foo", body: "bar", userId: 1, }) .then((response) => { console.log(response.data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Request completed"); }); ``` ### PUT 请求 ```js axios .put("https://jsonplaceholder.typicode.com/posts/1", { title: "foo", body: "bar", userId: 1, }) .then((response) => { console.log(response.data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Request completed"); }); ``` ### PATCH 请求 ```js axios .patch("https://jsonplaceholder.typicode.com/posts/1", { title: "foo", }) .then((response) => { console.log(response.data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Request completed"); }); ``` ### DELETE 请求 ```js axios .delete("https://jsonplaceholder.typicode.com/posts/1") .then((response) => { console.log(response.data); }) .catch((error) => { console.error(error); }) .finally(() => { console.log("Request completed"); }); ``` ## 使用 async/await 处� Promise 的�一�方�是使用 `async` 和 `await`,�� try/catch/finally ��处�错误和完�逻辑。这�写法�以让代�更加清晰易读,�时有效��所谓的"回调地狱"。 ::: tip 注�:async/await 属于 ECMAScript 2017 规范,Internet Explorer �部分旧版�览器�支�,使用时请注�兼容性。 ::: ### GET 请求 ```js const getPosts = async () => { try { const response = await axios.get( "https://jsonplaceholder.typicode.com/posts", { params: { postId: 5, }, } ); console.log(response.data); } catch (error) { console.error(error); } finally { console.log("Request completed"); } }; ``` ### POST 请求 ```js const createPost = async () => { try { const response = await axios.post( "https://jsonplaceholder.typicode.com/posts", { title: "foo", body: "bar", userId: 1, } ); console.log(response.data); } catch (error) { console.error(error); } finally { console.log("Request completed"); } }; ``` ### PUT 请求 ```js const updatePost = async () => { try { const response = await axios.put( "https://jsonplaceholder.typicode.com/posts/1", { title: "foo", body: "bar", userId: 1, } ); console.log(response.data); } catch (error) { console.error(error); } finally { console.log("Request completed"); } }; ``` ### PATCH 请求 ```js const updatePost = async () => { try { const response = await axios.patch( "https://jsonplaceholder.typicode.com/posts/1", { title: "foo", } ); console.log(response.data); } catch (error) { console.error(error); } finally { console.log("Request completed"); } }; ``` ### DELETE 请求 ```js const deletePost = async () => { try { const response = await axios.delete( "https://jsonplaceholder.typicode.com/posts/1" ); console.log(response.data); } catch (error) { console.error(error); } finally { console.log("Request completed"); } }; ``` axios-axios-aa54a5e/docs/zh/pages/getting-started/examples/typescript.md000066400000000000000000000064701526660636600266170ustar00rootroot00000000000000# TypeScript 示例 ## 导入类型 axios 内置了 TypeScript 类型定义,你�以直接从 `"axios"` 导入所需的类型: ```ts import axios from "axios"; import type { AxiosRequestConfig, AxiosResponse, AxiosError } from "axios"; ``` ## 为请求标注类型 在�应上使用泛型�数,告知 TypeScript 数�的具体结构: ```ts import axios from "axios"; type Post = { userId: number; id: number; title: string; body: string; }; const response = await axios.get("https://jsonplaceholder.typicode.com/posts/1"); console.log(response.data.title); // TypeScript 知�这是一个字符串 ``` ## 为函数标注类型 将请求�装在函数中,并明确声明返回类型,以获得最佳的类型安全性: ```ts import axios, { AxiosResponse } from "axios"; type Post = { userId: number; id: number; title: string; body: string; }; const getPost = async (id: number): Promise => { const response = await axios.get( `https://jsonplaceholder.typicode.com/posts/${id}` ); return response.data; }; ``` ## 为 POST 请求标注类型 你�以�时为请求体和预期�应标注类型: ```ts type CreatePostBody = { title: string; body: string; userId: number; }; type CreatePostResponse = CreatePostBody & { id: number }; const createPost = async (data: CreatePostBody): Promise => { const response = await axios.post( "https://jsonplaceholder.typicode.com/posts", data ); return response.data; }; ``` ## 带类型的 axios 实例 创建一个带类型的实例,将 baseURL 和请求头内置其中: ```ts import axios from "axios"; import type { AxiosInstance } from "axios"; const api: AxiosInstance = axios.create({ baseURL: "https://api.example.com", timeout: 5000, }); ``` ## 带类型的拦截器 在 v1.x 中,请求拦截器应使用 `InternalAxiosRequestConfig`(而� `AxiosRequestConfig`): ```ts import axios from "axios"; import type { InternalAxiosRequestConfig, AxiosResponse } from "axios"; api.interceptors.request.use((config: InternalAxiosRequestConfig) => { config.headers.set("Authorization", `Bearer ${getToken()}`); return config; }); api.interceptors.response.use( (response: AxiosResponse) => response, (error) => Promise.reject(error) ); ``` ## 为错误标注类型 使用 `axios.isAxiosError()` 对�获的错误进行类型收窄: ```ts import axios, { AxiosError } from "axios"; type ApiError = { message: string; code: number; }; try { await axios.get("/api/protected-resource"); } catch (error) { if (axios.isAxiosError(error)) { // error.response?.data 的类型为 ApiError console.error(error.response?.data.message); console.error(error.response?.status); } else { throw error; } } ``` ## TypeScript �置说明 由于 axios �时�布了 ESM 和 CJS 版本,根�你的�置��,�能存在以下注�事项: - 推�设置为 `"moduleResolution": "node16"`(由 `"module": "node16"` ��指定),需� TypeScript 4.7 或更高版本。 - 如果你将 TypeScript 编译为 CJS 且无法使用 `"moduleResolution": "node16"`,请�用 `"esModuleInterop": true`。 - 如果你使用 TypeScript 对 CJS JavaScript 代�进行类型检查,则�能使用 `"moduleResolution": "node16"`。 axios-axios-aa54a5e/docs/zh/pages/getting-started/features.md000066400000000000000000000044001526660636600244000ustar00rootroot00000000000000# 功能特性 axios 是一个功能强大的 HTTP 客户端,�供简�易用的 API ��起 HTTP 请求。它支�所有主��览器,在 JavaScript 社区中被广泛使用。以下是 axios 的核心特性,这些特性使其�为你下一个项目的优选方案。 ## �构(Isomorphic) axios 是一个通用 HTTP 客户端,�在�览器和 Node.js 中�时使用。这�味�你既�以在�端代�中�起 API 请求,也�以在�端代�中使用 axios,�常适�构建�进� Web 应用��页应用以��务端渲染应用。 对于�时负责��端开�的团队,axios 也是�佳选择。统一使用 axios �起 HTTP 请求,�以�低代�库的��度,����端 API 的一致性。 ## Fetch 支� axios 对 Fetch API �供了一�的支�。Fetch API 是 XHR API 的现代替代方案,�通过�置选项�用该适�器。XHR 适�器和 Fetch 适�器使用完全相�的 API,因此无需修改现有代���平滑�移到 Fetch API。 ## �览器支� axios 支�所有主��览器�部分较旧的�览器,包括 Chrome�Firefox�Safari 和 Edge,是构建需�兼容多��览器的 Web 应用的�想选择。 ## Node.js 支� axios �样支�多个 Node.js 版本,兼容性�过测试,最早�追溯至 v12.x,在无法或�便�级到最新 Node.js 版本的环境中�样�以放心使用。 此外,axios 还有针对 Bun 和 Deno 的冒烟测试,用于验�关键的�行时行为,增强跨�行时兼容性的�信度。 ## 其他特性 - 支� Promise API - 拦截请求和�应 - 转�请求和�应数� - AbortController 支� - 超时设置 - 支�嵌套�数的查询字符串�列化 - 自动将请求体�列化为: - JSON(application/json) - Multipart / FormData(multipart/form-data) - URL 编�表�(application/x-www-form-urlencoded) - 将 HTML 表�数�以 JSON 格��� - 自动处��应中的 JSON 数� - 支��览器和 Node.js 的进度�获,并�供�外信�(传输速率�剩余时间) - 支�在 Node.js 中设置带宽�制 - 兼容规范的 FormData 和 Blob(包括 Node.js 环境) - 客户端 XSRF 防护支� axios-axios-aa54a5e/docs/zh/pages/getting-started/first-steps.md000066400000000000000000000064551526660636600250610ustar00rootroot00000000000000# 入门指� 欢迎阅读 axios 文档�本指�将帮助你快速上手 axios,并�起第一个 API 请求。如果你是 axios 新手,建议从这里开始。 ## 安装 你�以通过多�方�在项目中使用 axios。最常�的方�是通过 npm 安装,也支� jsDelivr�unpkg 等 CDN。 #### 使用 npm ```bash npm install axios ``` #### 使用 pnpm ```bash pnpm install axios ``` #### 使用 yarn ```bash yarn add axios ``` #### 使用 bun ```bash bun add axios ``` #### 使用 deno ```bash deno install npm:axios ``` #### 使用 jsDelivr 使用 jsDelivr 时,建议使用压缩版本并固定版本�,以���外更新。如需使用最新版本,�以去掉版本�,但强烈�建议在生产环境这样�,因为�能导致应用出现�外�化。 ```html ``` #### 使用 unpkg 使用 unpkg 时,建议使用压缩版本并固定版本�,以���外更新。如需使用最新版本,�以去掉版本�,但强烈�建议在生产环境这样�,因为�能导致应用出现�外�化。 ```html ``` ## 导入 axios 安装完��,你�以使用 `import` 或 `require` �导入此库: ```js import axios, { isCancel, AxiosError } from "axios"; ``` 也�以使用默认导出,因为命�导出�是从 axios 工厂的�导出: ```js import axios from "axios"; console.log(axios.isCancel("something")); ``` 如果使用 `require` 导入,**�有默认导出�用**: ```js const axios = require("axios"); console.log(axios.isCancel("something")); ``` �些打包器和 ES6 lint 规则�能需�: ```js import { default as axios } from "axios"; ``` 对于自定义或较旧的环境,如果模�解�行为�正常,�以直接导入预构建包: ```js const axios = require("axios/dist/browser/axios.cjs"); // �览器 CommonJS 包(ES2017) // const axios = require("axios/dist/node/axios.cjs"); // node CommonJS 包(ES2017) ``` ## �起第一个请求 使用 axios �起请求最少�需�两行代�。你�以通过�供 URL 和请求方法�任� API ��请求。例如,� JSONPlaceholder API �起一个 GET 请求: ```js import axios from "axios"; const response = await axios.get( "https://jsonplaceholder.typicode.com/posts/1" ); console.log(response.data); ``` axios �供了简�的请求 API。你�以使用 `axios.get` �起 GET 请求,使用 `axios.post` �起 POST 请求,�此类推。也�以使用 `axios.request` 方法�起任�类型的请求。 ::: tip 在生产环境中设置 `timeout` 如果�设置 `timeout`,�滞的请求�能会无�挂起。�通过请求�置传入: ```js const response = await axios.get("https://example.com/data", { timeout: 5000, // 5 秒 }); ``` 匹�的 `ECONNABORTED` / `ETIMEDOUT` 错误�请�阅[请求�置中的 `timeout`](/pages/advanced/request-config#timeout) 与[错误处�](/pages/advanced/error-handling)。 ::: ## 下一步 现在你已�用 axios 完�了第一个请求,�以继续探索 axios 文档的其余内容。了解更多关于�起请求�处��应以�在项目中使用 axios 的知识,请查阅文档其他章节。 axios-axios-aa54a5e/docs/zh/pages/getting-started/upgrade-guide.md000066400000000000000000000107111526660636600253060ustar00rootroot00000000000000# �级指� 本指�旨在帮助你将项目从旧版本�级到新版本。建议阅读�个主�版本的�布说明,其中�能包�关于破�性�更的��信�。 ## �级到 v1.19.0 ### 格�错误的 HTTP(S) URL 将被拒� 现在,如果 `http:` 或 `https:` 的 `url` 或 `baseURL` 在�议�缺少 `//`,请求会被拒�。请将 `https:example.com` 或 `https:/example.com` 等值替�为 `https://example.com` 等格�正确的 URL。生�的 `AxiosError` 使用 `ERR_INVALID_URL` 代�,并在安全���感信��指出有问题的 URL。此项安全行为�更旨在防止格�错误 URL 的规范化过程绕过 `baseURL` 或 URL �许列表。 ### �步请求拦截器错误 �步请求拦截器抛出错误时,axios 会调用其�对的拒�处�器,并�止�行其余请求拦截器。正常返回的拒�处�器会将错误视为已处�,并使用最�一个有效�置��请求;它的返回值�会�为请求�置。如果验�必须阻止请求��,请�略拒�处�器,或让它抛出错误或返回已拒�的 Promise。最终错误会继续传递给�应拒�拦截器。 ## 从 v0.x �级到 v1.x ### import 语��更 在 v1.x 中,import 语�改为使用 `default` 导出,你需�将 import 语�更新为如下形�: ```diff - import { axios } from "axios"; + import axios from "axios"; ``` ### 拦截器系统�更 在 v1.x 中,你需�使用 `InternalAxiosRequestConfig` 类型�为 `request` 拦截器的 `config` �数标注类型。这是因为该�数现在的类型是 `InternalAxiosRequestConfig`,而��是公开的 `AxiosRequestConfig`。 ```diff - axios.interceptors.request.use((config: AxiosRequestConfig) => { + axios.interceptors.request.use((config: InternalAxiosRequestConfig) => { return config; }); ``` ### 请求头结构�更 在 v1.x 中,请求头的结构已去除 `common` 属性,你需�按如下方�更新相关代�: ```diff - if (request.headers?.common?.Authorization) { - request.headers.common.Authorization = ... + if (request.headers?.Authorization) { + request.headers.Authorization = ... ``` 原本�于 `common`�`get`�`post` 等属性下的默认请求头,现在直接设置在 `axios.defaults.headers` 上: ```diff - axios.defaults.headers.common["Accept"] = "application/json"; + axios.defaults.headers["Accept"] = "application/json"; ``` ### Multipart 表�数� 如果请求包� `FormData` 数�,`Content-Type: multipart/form-data` 请求头现在会被自动设置,请移除手动设置以����: ```diff - axios.post("/upload", formData, { - headers: { "Content-Type": "multipart/form-data" }, - }); + axios.post("/upload", formData); ``` 如果你明确设置了 `Content-Type: application/json`,axios 现在会自动将数��列化为 JSON。 ### �数�列化 v1.x 对 URL �数的�列化方�进行了若干破�性�更,主�包括: **`params` 默认会进行百分�编�。** 如果你的�端期望接收 qs 风格的原始方括�编�,�能需��置自定义�列化器: ```js import qs from 'qs'; axios.create({ paramsSerializer: { serialize: (params) => qs.stringify(params, { arrayFormat: 'brackets' }), }, }); ``` **`params` 中的嵌套对象现在使用方括�表示法**(`foo[bar]=1`)�列化,而��使用点�表示法。如果你的�端期望点�表示法,请使用自定义�列化器。 **`null` 和 `undefined` �数**的处�方�现在已统一:`null` 值�列化为空字符串,而 `undefined` 值则被完全忽略。 关于�数�列化�置的完整选项,请�阅[请求�置](/pages/advanced/request-config)页�。 ### 内部模���导出 我们决定��导出 axios 的内部模�,你需�将代�更新为仅使用 axios 的公开 API。此�更旨在简化 API,缩� axios 的接�范围,使我们能够在�声明破�性�更的情况下修改内部实现。 请查阅本站的 [API �考](/pages/advanced/api-reference),获�最新的公开 API 信�。 ### 请求�置 我们对请求�置对象进行了调整,请查阅本站的[�置�考](/pages/advanced/request-config)获�最新信�。 ### ��的破�性�更 本指�并�详尽,�能未涵盖所有破�性�更。如果你�到任何问题,欢迎在 [docs GitHub 仓库](https://github.com/axios/docs)�交 issue,并添加 `breaking change` 标签。 axios-axios-aa54a5e/docs/zh/pages/misc/000077500000000000000000000000001526660636600200705ustar00rootroot00000000000000axios-axios-aa54a5e/docs/zh/pages/misc/security.md000066400000000000000000000241471526660636600222710ustar00rootroot00000000000000# 安全政策 ## ⚠� 解压炸弹 / �应无�缓冲 默认情况下,`maxContentLength` 与 `maxBodyLength` �为 `-1`(��制)。��或被攻陷的�务器�以返回一个很�的 gzip/deflate/brotli/zstd 压缩�应,解压�体积�达数 GB,耗尽 Node.js 进程的内存。 **如果你��完全�信的�务器�起请求,必须根�你的业务场景设置�适的 `maxContentLength`(以� `maxBodyLength`)。** 在��解压过程中会按分�强制执行该�制,因此�需设置该值��抵御解压炸弹攻击。 ```js axios.get('https://example.com/data', { maxContentLength: 10 * 1024 * 1024, // 10 MB maxBodyLength: 10 * 1024 * 1024, }); // 或全局设置: axios.defaults.maxContentLength = 10 * 1024 * 1024; axios.defaults.maxBodyLength = 10 * 1024 * 1024; ``` 默认值未被调低是因为调低会�默地影�所有�法的大文件下载。为��信��选择��的上�,应由应用自行负责。 ## 其他安全�感的选项 以下请求�置选项具有直接的安全影�。它们在[请求�置](/pages/advanced/request-config)中�有完整说明,这里集中列出以便统一查阅。 | 选项 | 风险 | 缓解措施 | | --- | --- | --- | | [`baseURL`](/pages/advanced/request-config#baseurl) | 有些应用会把 `baseURL` 中的路径�缀(例如 `https://api.example.com/v1/`)当作请求边界。用户�控的相对 `url` �能包� `..` 路径段,最终 URL 解�器会对其进行规范化,并�能把请求解�到该路径�缀之外。 | ���赖 `baseURL` �路径隔离。将��信请求路径传给 axios �先校验;拒��对 URL��议相对 URL 和 `..` 路径段,或根� allowlist 检查解�� URL 的 origin 与 pathname。 | | [`socketPath`](/pages/advanced/request-config#socketpath) | 如果�自��信输入,攻击者�将���定�到 `/var/run/docker.sock` 等特�本地套接字,绕过基于主机�的 SSRF 防护(CWE-918)。 | 对�自��信输入的�置进行过滤或仅�许特定键。使用 [`allowedSocketPaths`](/pages/advanced/request-config#allowedsocketpaths) �制�接�的套接字路径。 | | [`beforeRedirect`](/pages/advanced/request-config#beforeredirect) | 在 `follow-redirects` 因�议�级剥离凭�**之�**�行。如果�检查目标�议就�新注入凭�,�能在明文 HTTP 上泄露凭�。 | 仅对�信的 HTTPS 目标�新添加凭�。在为 `auth` 赋值�检查 `options.protocol === "https:"`。 | | [`sensitiveHeaders`](/pages/advanced/request-config#sensitiveheaders) | `X-API-Key` 等自定义密钥请求头在 Node.js HTTP 适�器跟��定�到���时�能被转�。 | 在 `sensitiveHeaders` 中列出这些自定义密钥请求头�称;axios 会在跨��定�时�区分大�写地移除匹�项。���定�会�留它们。 | | [`withXSRFToken`](/pages/advanced/request-config#withxsrftoken) | 设置为 `true` 会在跨域请求中强制设置 XSRF 请求头。较旧的 axios 版本会在 `withCredentials: true` 时���用此行为;新版本�求两个标志�时设置。 | ��为 `undefined`(仅��),除�你的�端确实在跨域请求中校验 XSRF。 | | [`redact`](/pages/advanced/request-config#redact) | `AxiosError#toJSON()` 默认会包�请求�置,�能将 `Authorization` 请求头或 `auth` 凭�泄露到错误日志和�测中。 | 通过 `redact` 数组传入需��蔽的�置键�。匹��区分大�写,且会递归处�。 | | [`formDataHeaderPolicy`](/pages/advanced/request-config#formdataheaderpolicy) | 自定义 `FormData` 的 `getHeaders()` 若返回攻击者�控的值,�能在 Node.js 中覆盖 `Authorization` 等请求头或注入任�请求头。 | 设置为 `'content-only'` 仅�制 `Content-Type` 与 `Content-Length`,�通过请求 `headers` �置显�设置其他请求头。 | ## 供应链加固:`ignore-scripts` 与生命周期脚本 仓库附带项目级的 `.npmrc`,其中设置了 `ignore-scripts=true`。这会在仓库内�行 `npm install` 或 `npm ci` 时阻止任何直接或传递�赖的 npm 生命周期脚本(`preinstall`�`install`�`postinstall`�`prepare`)执行。详� [THREATMODEL.md](https://github.com/axios/axios/blob/v1.x/THREATMODEL.md)(�� T-S2)了解原因。 由此带�的一个影�:仓库自身的 `prepare` 钩�(用于安装 Husky 的 git 钩�)**�会**自动�行。在你首次安装�,请手动�用 git 钩�: ```bash npm ci npm rebuild husky && npx husky ``` �次全新检出仓库��需�行一次这两�命令。�续�次 `npm install` �**�**需��新执行。 ::: danger ��移除 `ignore-scripts=true` 为了"修�"husky 设置而从 `.npmrc` 中移除 `ignore-scripts=true`,会�新打开�赖树中所有其他包的生命周期脚本攻击�。所有 CI 工作�都已使用 `--ignore-scripts` 调用 npm,因此本地行为与 CI ��一致。 ::: 我们建议在任何将 axios(或其他�赖)引入到处�机密的构建环境的下游项目中,也使用相�的 `ignore-scripts=true` 设置。 ## 验��布版本 自 `axios` 在 npm 上�布的�一个 tarball �通过 GitHub Actions �布,并附带 [npm provenance �明](https://docs.npmjs.com/generating-provenance-statements),以密�学方�将软件包与生�它的工作���交 SHA 绑定。 使用者�在本地验�该�明: ```bash # 验�你 lockfile 中的所有包(包括 axios) npm audit signatures ``` 验�通过仅�明该 tarball 是在 `axios/axios` 的 GitHub Actions 环境中�基于已知 commit 构建的,且在构建至 registry 之间未被篡改。它**�**�明该 commit 中的代�没有 bug。 如果 `npm audit signatures` 针对较新版本的 `axios` 报告缺失或无效的�明,请视为潜在的供应链事件,并通过下方的�密渠�上报。 ## 报告�洞 如果你认为在本项目中�现了安全�洞,请按照以下说明�我们报告。我们对所有安全�洞报告都认真对待。如果你�现的是第三方库中的�洞,请�该库的维护者报告。 ## 报告�程 请勿通过公开的 GitHub issue 报告安全�洞。请使用 GitHub 官方安全渠�,�交 [security advisory(安全公告)](https://github.com/axios/axios/security/advisories/new)。 ## 披露政策 收到安全�洞报告�,我们将指定一�主�负责人。该负责人负责确认问题�确定�影�版本�评估严�程度�开�并�布修�,并与报告人�调公开披露。 ### 60 天解决与披露承诺 我们承诺 **在收到�次报告� 60 个自然日内解决并公开披露�一个有效的安全公告**,计时从通过 [GitHub 安全公告渠�](https://github.com/axios/axios/security/advisories/new) 接到报告的那一刻起算。 这 60 天是对报告人和下游使用者的硬性承诺,是底线而�目标。如果我们无法在期�内�布修�,也会在第 60 天公开�布公告,�供当��行的最佳缓解指引,让使用者能够采�行动。 **60 天窗�内的�里程碑:** | 天 | 里程碑 | | ----- | ---------------------------------------------------------------------------------------------------- | | 0 | 收到报告。在 GitHub 上开��密公告。 | | ≤ 3 | �报告人��确认收到。分�决定:在范围内 / �在范围内 / �� / 需补充信�。 | | ≤ 10 | 评估严�程度(在适用时使用 CVSS v4)。确认�影�版本。如有必�,�由 GitHub 申请 CVE。 | | ≤ 45 | 修�已开��评审�测试完毕。在�有分支上准备候选版本。�报告人�供预览以便验�。 | | ≤ 60 | 已修�版本�布至 npm。公开公告 + CVE �布。除�报告人�有�求,�则给予署�。更新 CHANGELOG。 | **例外与延期。** - 如果报告人�求缩短��期(例如计划在会议上披露研究�果),我们会尽���。 - 如果修�需�引入破�性�更�需与主�下游使用者�调�或�赖 `follow-redirects` / `form-data` / `proxy-from-env` 的上游�布,我们�能将期�延长至 60 天以上。任何延期都会在第 60 天通过公告公开披露,并说明修订�的预期时间与原因。 - 如果报告**�在范围内**(例如属于项目 [��模型](https://github.com/axios/axios/blob/v1.x/THREATMODEL.md) 中明确列出的 non-goal),我们会在分�窗�内(≤ 3 天)�报告人说明并关闭。�在范围内的报告�会进入 60 天队列。 - **正在被实际利用的�洞** 按事件处�:修�与公告在补�验�通过�立��布,而�按 60 天时间表。 **对报告人的期望。** 在报告处于��期时,我们请报告人在以下较早�生的时间点之���公开披露:(a) �调披露的公告�布,或 (b) 第 60 天。如果我们未在 60 天内行动,报告人��自行披露——我们会将其视为我们的失败,而�报告人的失败。 ## 安全更新 安全更新将在补�开�和测试完��尽快�布。我们将通过项目的 GitHub 仓库通知用户,并在项目的 GitHub Releases 页��布�版说明和安全公告。我们还将弃用所有包�该安全�洞的版本。 ## 维护者侧事件�应 对于影�维护者账��工作站或�布基础设施的入侵场景(钓鱼�硬件密钥丢失�异常 tag/�布),项目在 [THREATMODEL.md §3.7](https://github.com/axios/axios/blob/v1.x/THREATMODEL.md#37-incident-response-runbook) 中维护了一份内部事件�应手册。内容涵盖会��销�密钥轮��下游通知�以��消�布/弃用�程。 ## 安全�作伙伴与致谢 感谢以下安全研究人员与我们�作,共��障项目的安全: - [Socket Dev](https://socket.dev/) - [GitHub Security Lab](https://securitylab.github.com/) axios-axios-aa54a5e/docs/zh/pages/misc/semver.md000066400000000000000000000034241526660636600217160ustar00rootroot00000000000000# 语义化版本 语义化版本是一�版本控制方案,用于传达软件包中�更的性质。它是一套简�的规则和�求,规定了版本�的分�和递增方�。 ## axios 的版本控制 axios �循语义化版本方案。这�味��个 axios 版本都由三个部分组�:主版本��次版本�和补�版本�,版本�根��版中�更的性质递增。 过去,axios 有时�能未严格�循语义化版本,但未�将更严格地�循语义化版本方案,以确�用户�以�赖版本��了解库中�更的性质。 以下是版本方案的简�概述。 ## 版本格� 语义化版本�由三个部分组�: 1. 主版本�(Major) 2. 次版本�(Minor) 3. 补�版本�(Patch) 版本�写作 `MAJOR.MINOR.PATCH`,�个部分有其特定�义: - **主版本�**:进行�兼容的 API �更时递增。 - **次版本�**:以��兼容的方�添加新功能时递增。 - **补�版本�**:进行��兼容的缺陷修�时递增。 ## 预�布版本 除版本�的三个部分外,还�以附加预�布版本标识。方�是在补�版本��紧跟一个连字符和一系列点�分隔的标识符,例如 `1.0.0-alpha.1`。 预�布版本表示该版本�稳定,�能�满足版本�所指示的预期兼容性�求。预�布版本按标识符的顺�排列,例如 `1.0.0-alpha.1` 早于 `1.0.0-alpha.2`。 ## 版本范围 指定包的版本范围时,�以使用多��算符�表示�接�的版本范围,�用�算符如下: - `>`:大于 - `<`:�于 - `>=`:大于或等于 - `<=`:�于或等于 - `~`:近似等于 - `^`:兼容 例如,`^1.0.0` 表示接�任何大于或等于 `1.0.0` 且�于 `2.0.0` 的版本。 axios-axios-aa54a5e/docs/zh/pages/misc/sponsors.md000066400000000000000000000115321526660636600223020ustar00rootroot00000000000000--- layout: page search: false ---

赞助商

axios 由以下组织�供支�。如果你希望赞助 axios,请访问我们的 open collective 页�了解详情。

{{ capitalizeFirstLetter(sponsor.tier) }}
{{ sponsor.name }}
axios-axios-aa54a5e/eslint.config.js000066400000000000000000000023421526660636600175660ustar00rootroot00000000000000import js from '@eslint/js'; import globals from 'globals'; export default [ { ...js.configs.recommended, files: ['lib/**/*.js'], linterOptions: { reportUnusedDisableDirectives: 'off' }, languageOptions: { ecmaVersion: 2018, sourceType: 'module' }, rules: { ...js.configs.recommended.rules, 'no-cond-assign': 0, 'no-useless-assignment': 'off', 'no-unused-vars': [ 'error', { args: 'none', caughtErrors: 'none', varsIgnorePattern: '^_' } ] } }, { files: ['lib/**/*.js'], ignores: [ 'lib/adapters/http.js', 'lib/adapters/xhr.js', 'lib/platform/node/**/*.js', 'lib/platform/browser/**/*.js' ], languageOptions: { globals: { ...globals.browser, ...globals.node, globalThis: 'readonly' } } }, { files: ['lib/adapters/http.js', 'lib/platform/node/**/*.js'], languageOptions: { globals: { ...globals.node, globalThis: 'readonly' } } }, { files: ['lib/adapters/xhr.js', 'lib/platform/browser/**/*.js'], languageOptions: { globals: { ...globals.browser, globalThis: 'readonly' } } } ]; axios-axios-aa54a5e/examples/000077500000000000000000000000001526660636600163035ustar00rootroot00000000000000axios-axios-aa54a5e/examples/README.md000066400000000000000000000003241526660636600175610ustar00rootroot00000000000000# axios examples To run the examples: 1. `git clone https://github.com/axios/axios.git` 2. `cd axios` 3. `npm install` 4. `npm run build` 5. `npm run examples` 6. [http://localhost:3000](http://localhost:3000) axios-axios-aa54a5e/examples/abort-controller/000077500000000000000000000000001526660636600215735ustar00rootroot00000000000000axios-axios-aa54a5e/examples/abort-controller/index.html000066400000000000000000000110711526660636600235700ustar00rootroot00000000000000 axios - abort controller example

axios.AbortController

1. Single Request Cancellation

Click "Start Request" to begin a 3-second request. Click "Cancel Request" to abort it.


2. Search-as-you-type (Race Condition Handling)

Type quickly. Previous pending requests will be cancelled automatically.

    axios-axios-aa54a5e/examples/abort-controller/server.js000066400000000000000000000010331526660636600234340ustar00rootroot00000000000000export default function (req, res) { let parsedUrl; try { parsedUrl = new URL(req.url, 'http://localhost'); } catch { res.writeHead(400, { 'Content-Type': 'text/plain' }); res.end('Invalid URL'); return; } const delay = parsedUrl.searchParams.get('delay') || 3000; setTimeout(() => { res.writeHead(200, { 'Content-Type': 'text/json', }); res.write( JSON.stringify({ message: 'Response completed successfully after ' + delay + 'ms', }) ); res.end(); }, delay); } axios-axios-aa54a5e/examples/all/000077500000000000000000000000001526660636600170535ustar00rootroot00000000000000axios-axios-aa54a5e/examples/all/index.html000066400000000000000000000024111526660636600210460ustar00rootroot00000000000000 axios - all example

    axios.all

    User


    Orgs

      axios-axios-aa54a5e/examples/amd/000077500000000000000000000000001526660636600170445ustar00rootroot00000000000000axios-axios-aa54a5e/examples/amd/index.html000066400000000000000000000016631526660636600210470ustar00rootroot00000000000000 AMD

      AMD

      User

      axios-axios-aa54a5e/examples/get/000077500000000000000000000000001526660636600170625ustar00rootroot00000000000000axios-axios-aa54a5e/examples/get/index.html000066400000000000000000000024551526660636600210650ustar00rootroot00000000000000 axios - get example

      axios.get

        axios-axios-aa54a5e/examples/get/server.js000066400000000000000000000011651526660636600207310ustar00rootroot00000000000000const people = [ { name: 'Matt Zabriskie', github: 'mzabriskie', twitter: 'mzabriskie', avatar: '199035', }, { name: 'Ryan Florence', github: 'rpflorence', twitter: 'ryanflorence', avatar: '100200', }, { name: 'Kent C. Dodds', github: 'kentcdodds', twitter: 'kentcdodds', avatar: '1500684', }, { name: 'Chris Esplin', github: 'deltaepsilon', twitter: 'chrisesplin', avatar: '878947', }, ]; export default function (req, res) { res.writeHead(200, { 'Content-Type': 'application/json', }); res.write(JSON.stringify(people)); res.end(); } axios-axios-aa54a5e/examples/improved-network-errors.md000066400000000000000000000037741526660636600234660ustar00rootroot00000000000000# Overview of this document: This document explains a simple approach to make Axios network errors more helpful and human-readable. By default, Axios shows a generic `"Network Error"` message for many failures. This can be confusing because it doesn't explain "what actually went wrong" (e.g., no internet, a timeout, a CORS issue, etc.). Our approach adds clear, categorised error messages for different network issues. --- ==> Problem Axios currently throws the same `Network Error` message for many different cases: - The Internet is disconnected - DNS lookup fails - Server is down or refusing connections - CORS blocked requests - Request timed out These cases all look the same to developers and users, making debugging harder. --> Our Approach — Wrapper / Middleware We created a small wrapper function called `enhanceNetworkError()` that: - Detects Common network problems using `error.code`, `error.message`, and response status. - Adds a new field `error.detailedMessage` with a short, clear explanation. - Assigns a new `error.code` (like `ERR_TIMEOUT`, `ERR_DNS_FAILURE`, etc.). - Works for both browser and Node.js environments. The wrapper is used inside an Axios instance via a Response interceptor. -> How It Works 1. When Axios throws an error, the interceptor catches it. 2. The `enhanceNetworkError()` function checks what type of error it is: - Offline → `ERR_NO_INTERNET` - DNS failure → `ERR_DNS_FAILURE` - Timeout → `ERR_TIMEOUT` - CORS blocked → `ERR_CORS_BLOCKED` - Server-side → `ERR_SERVER` - Client-side → `ERR_CLIENT` - Other → `ERR_NETWORK_GENERIC` 3. It returns a more descriptive error with both `code` and `detailedMessage`. -> Example Usage ```javascript const api = createEnhancedClient({ baseURL: 'https://example.com' }); api .get('/data') .then((res) => console.log(res.data)) .catch((err) => { console.error(err.code); // e.g., ERR_TIMEOUT console.error(err.detailedMessage); // e.g., "The request took too long to respond." }); ``` axios-axios-aa54a5e/examples/network_enhanced.js000066400000000000000000000043511526660636600221620ustar00rootroot00000000000000import axios from 'axios'; function enhanceNetworkError(error) { // when Offline (no internet) if (typeof navigator !== 'undefined' && !navigator.onLine) { error.code = 'ERR_NO_INTERNET'; error.detailedMessage = 'No internet connection detected. Please check your connection and try again.'; } // when DNS failure occurs (invalid domain) else if (error.code === 'ENOTFOUND' || /dns/i.test(error.message)) { error.code = 'ERR_DNS_FAILURE'; error.detailedMessage = 'Unable to reach the requested domain. Please verify the URL or your network settings.'; } // when Connection refused by server else if (error.code === 'ECONNREFUSED' || /refused/i.test(error.message)) { error.code = 'ERR_CONNECTION_REFUSED'; error.detailedMessage = 'Connection was refused by the server. It may be temporarily unavailable.'; } // when Request timeout happens else if (error.code === 'ETIMEDOUT' || /timeout/i.test(error.message)) { error.code = 'ERR_TIMEOUT'; error.detailedMessage = 'The request took too long to respond. Please try again later.'; } // when CORS restriction happens (for browser only) else if (/CORS/i.test(error.message)) { error.code = 'ERR_CORS_BLOCKED'; error.detailedMessage = 'The request was blocked due to cross-origin restrictions.'; } // when Server-side error occurs else if (error.response && error.response.status >= 500) { error.code = 'ERR_SERVER'; error.detailedMessage = 'A server-side issue occurred. Please try again later.'; } // when Client-side error occurs else if (error.response && error.response.status >= 400) { error.code = 'ERR_CLIENT'; error.detailedMessage = 'A client-side error occurred. Please check your request.'; } // when unknown network issue occurs else { error.code = 'ERR_NETWORK_GENERIC'; error.detailedMessage = 'A network issue occurred. Please check your connection or try again later.'; } return error; } export function createEnhancedClient(config = {}) { const client = axios.create(config); client.interceptors.response.use( (response) => response, (error) => { throw enhanceNetworkError(error); } ); return client; } export default enhanceNetworkError; axios-axios-aa54a5e/examples/post/000077500000000000000000000000001526660636600172705ustar00rootroot00000000000000axios-axios-aa54a5e/examples/post/index.html000066400000000000000000000022571526660636600212730ustar00rootroot00000000000000 axios - post example

        axios.post

        axios-axios-aa54a5e/examples/post/server.js000066400000000000000000000005011526660636600211300ustar00rootroot00000000000000export default function (req, res) { let data = ''; req.on('data', function (chunk) { data += chunk; }); req.on('end', function () { console.log('POST data received'); res.writeHead(200, { 'Content-Type': 'application/json', }); res.write(JSON.stringify(data)); res.end(); }); } axios-axios-aa54a5e/examples/postMultipartFormData/000077500000000000000000000000001526660636600226105ustar00rootroot00000000000000axios-axios-aa54a5e/examples/postMultipartFormData/index.html000066400000000000000000000344231526660636600246130ustar00rootroot00000000000000 Axios - Multipart Form Example

        Multipart Form Data

        Response Data
        Submit the form to see the response data here
        This form demonstrates how to submit multipart/form-data with both text fields and files.
        axios-axios-aa54a5e/examples/postMultipartFormData/server.js000066400000000000000000000003601526660636600244530ustar00rootroot00000000000000export default function (req, res) { req.on('data', function (chunk) {}); req.on('end', function () { console.log('POST received'); res.writeHead(200, { 'Content-Type': 'application/json', }); res.end(); }); } axios-axios-aa54a5e/examples/server.js000066400000000000000000000110501526660636600201440ustar00rootroot00000000000000import fs from 'fs'; import path from 'path'; import http from 'http'; import minimist from 'minimist'; import url from 'url'; const argv = minimist(process.argv.slice(2)); let server; let dirs; const __filename = url.fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); function listDirs(root) { const files = fs.readdirSync(root); const dirs = []; for (let i = 0, l = files.length; i < l; i++) { const file = files[i]; if (file[0] !== '.') { const stat = fs.statSync(path.join(root, file)); if (stat.isDirectory()) { dirs.push(file); } } } return dirs; } function getIndexTemplate() { const links = dirs.map(function (dir) { const url = '/' + dir; return ( '
      • ' + url + '
      • ' ); }); return ( '' + '' + '' + 'axios examples' + '' + '' + '
          ' + links.join('') + '
        ' ); } function sendResponse(res, statusCode, body) { res.writeHead(statusCode); res.write(body); res.end(); } function send200(res, body) { sendResponse(res, 200, body || '

        OK

        '); } function send404(res, body) { sendResponse(res, 404, body || '

        Not Found

        '); } function pipeFileToResponse(res, file, type) { try { // Validate file path - prevent directory traversal const safeBasePath = path.join(__dirname, 'examples'); const resolvedPath = path.resolve(path.join(safeBasePath, file)); // Ensure the resolved path is within intended directory if (!resolvedPath.startsWith(safeBasePath)) { res.writeHead(400); res.end('Invalid file path'); return; } // Check if file exists if (!fs.existsSync(resolvedPath)) { res.writeHead(404); res.end('File not found'); return; } if (type) { res.writeHead(200, { 'Content-Type': type, }); } else { res.writeHead(200); } const stream = fs.createReadStream(resolvedPath); stream.on('error', (err) => { console.error('Error while reading file:', err.message); if (!res.headersSent) { res.writeHead(500, { 'Content-Type': 'text/plain' }); } res.end('File read error'); }); stream.pipe(res); } catch (err) { console.error('Unexpected error:', err.message); if (!res.headersSent) { res.writeHead(500, { 'Content-Type': 'text/plain' }); } res.end('Internal server error'); } } dirs = listDirs(__dirname); server = http.createServer(function (req, res) { let url = req.url; // Process axios itself if (/axios\.min\.js$/.test(url)) { pipeFileToResponse(res, '../dist/axios.min.js', 'text/javascript'); return; } if (/axios\.min\.map$/.test(url)) { pipeFileToResponse(res, '../dist/axios.min.map', 'text/javascript'); return; } if (/axios\.amd\.min\.js$/.test(url)) { pipeFileToResponse(res, '../dist/axios.amd.min.js', 'text/javascript'); return; } if (/axios\.amd\.min\.map$/.test(url)) { pipeFileToResponse(res, '../dist/axios.amd.min.map', 'text/javascript'); return; } // Process / if (url === '/' || url === '/index.html') { send200(res, getIndexTemplate()); return; } // Format request */ -> */index.html if (/\/$/.test(url)) { url += 'index.html'; } // Format request /get -> /get/index.html const parts = url.split('/'); if (dirs.indexOf(parts[parts.length - 1]) > -1) { url += '/index.html'; } // Process index.html request if (/index\.html$/.test(url)) { if (fs.existsSync(path.join(__dirname, url))) { pipeFileToResponse(res, url, 'text/html'); } else { send404(res); } return; } // Process server request else if (new RegExp('(' + dirs.join('|') + ')\/server').test(url)) { if (fs.existsSync(path.join(__dirname, url + '.js'))) { import('file://' + path.join(__dirname, url + '.js')) .then((server) => { server.default(req, res); }) .catch((err) => { console.error('Error importing server:', err); send404(res); }); } else { send404(res); } return; } else { send404(res); } }); const PORT = argv.p || 3000; server.listen(PORT, () => { console.log(`Examples running on ${PORT}`); }); axios-axios-aa54a5e/examples/transform-response/000077500000000000000000000000001526660636600221525ustar00rootroot00000000000000axios-axios-aa54a5e/examples/transform-response/index.html000066400000000000000000000027141526660636600241530ustar00rootroot00000000000000 axios - transform response example

        transformResponse


        Created:
        Updated:
        axios-axios-aa54a5e/examples/upload/000077500000000000000000000000001526660636600175675ustar00rootroot00000000000000axios-axios-aa54a5e/examples/upload/index.html000066400000000000000000000031141526660636600215630ustar00rootroot00000000000000 axios - file upload example

        file upload

        axios-axios-aa54a5e/examples/upload/server.js000066400000000000000000000003461526660636600214360ustar00rootroot00000000000000export default function (req, res) { let _data = ''; req.on('data', function (chunk) { _data += chunk; }); req.on('end', function () { console.log('File uploaded'); res.writeHead(200); res.end(); }); } axios-axios-aa54a5e/gulpfile.js000066400000000000000000000042051526660636600166330ustar00rootroot00000000000000import gulp from 'gulp'; import fs from 'fs-extra'; import axios from './scripts/axios-build-instance.js'; import minimist from 'minimist'; const argv = minimist(process.argv.slice(2)); gulp.task('default', async function () { console.log('hello!'); }); const clear = gulp.task('clear', async function () { await fs.emptyDir('./dist/'); }); async function getContributors(user, repo, maxCount = 1) { const contributors = ( await axios.get( `https://api.github.com/repos/${encodeURIComponent(user)}/${encodeURIComponent(repo)}/contributors`, { params: { per_page: maxCount } } ) ).data; return Promise.all( contributors.map(async (contributor) => { return { ...contributor, ...( await axios.get(`https://api.github.com/users/${encodeURIComponent(contributor.login)}`) ).data, }; }) ); } const packageJSON = gulp.task('package', async function () { const CONTRIBUTION_THRESHOLD = 3; const npm = JSON.parse(await fs.readFile('package.json')); try { const contributors = await getContributors('axios', 'axios', 15); npm.contributors = contributors .filter( ({ type, contributions }) => type.toLowerCase() === 'user' && contributions >= CONTRIBUTION_THRESHOLD ) .map(({ login, name, _ }) => `${name || login} (https://github.com/${login})`); await fs.writeFile('package.json', JSON.stringify(npm, null, 2)); } catch (err) { if (axios.isAxiosError(err) && err.response && err.response.status === 403) { throw Error(`GitHub API Error: ${err.response.data && err.response.data.message}`); } throw err; } }); const env = gulp.task('env', async function () { var npm = JSON.parse(await fs.readFile('package.json')); const envFilePath = './lib/env/data.js'; await fs.writeFile( envFilePath, Object.entries({ VERSION: (argv.bump || npm.version).replace(/^v/, ''), }) .map(([key, value]) => { return `export const ${key} = ${JSON.stringify(value)};`; }) .join('\n') ); }); const version = gulp.series('env', 'package'); export { env, clear, version, packageJSON }; axios-axios-aa54a5e/index.d.cts000066400000000000000000000600431526660636600165340ustar00rootroot00000000000000type MethodsHeaders = Partial< { [Key in axios.Method as Lowercase]: AxiosHeaders; } & { common: AxiosHeaders } >; type AxiosHeaderMatcher = | string | RegExp | ((this: AxiosHeaders, value: string, name: string) => boolean); type AxiosHeaderParser = (this: AxiosHeaders, value: axios.AxiosHeaderValue, header: string) => any; type CommonRequestHeadersList = | 'Accept' | 'Content-Length' | 'User-Agent' | 'Content-Encoding' | 'Authorization' | 'Location'; type ContentType = | axios.AxiosHeaderValue | 'text/html' | 'text/plain' | 'multipart/form-data' | 'application/json' | 'application/x-www-form-urlencoded' | 'application/octet-stream'; type CommonResponseHeadersList = | 'Server' | 'Content-Type' | 'Content-Length' | 'Cache-Control' | 'Content-Encoding'; type CommonResponseHeaderKey = CommonResponseHeadersList | Lowercase; type BrowserProgressEvent = any; declare class AxiosHeaders { constructor(headers?: axios.RawAxiosHeaders | AxiosHeaders | string); [key: string]: any; set( headerName?: string, value?: axios.AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher ): AxiosHeaders; set(headers?: axios.RawAxiosHeaders | AxiosHeaders | string, rewrite?: boolean): AxiosHeaders; set(headers?: Iterable<[string, axios.AxiosHeaderValue]>, rewrite?: boolean): AxiosHeaders; get( headerName: string, parser: typeof AxiosHeaders.parseParameters ): axios.AxiosHeaderParameters; get(headerName: string, parser: RegExp): RegExpExecArray | null; get(headerName: string, matcher?: true | AxiosHeaderParser): axios.AxiosHeaderValue; has(header: string, matcher?: AxiosHeaderMatcher): boolean; delete(header: string | string[], matcher?: AxiosHeaderMatcher): boolean; clear(matcher?: AxiosHeaderMatcher): boolean; normalize(format: boolean): AxiosHeaders; concat( ...targets: Array ): AxiosHeaders; toJSON(asStrings: true): Record; toJSON(asStrings?: false): Record; toJSON(asStrings?: boolean): Record; static from(thing?: AxiosHeaders | axios.RawAxiosHeaders | string): AxiosHeaders; static parseParameters(value: axios.AxiosHeaderValue): axios.AxiosHeaderParameters; static accessor(header: string | string[]): AxiosHeaders; static concat( ...targets: Array ): AxiosHeaders; setContentType(value: ContentType, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders; getContentType(parser?: RegExp): RegExpExecArray | null; getContentType(matcher?: AxiosHeaderMatcher): axios.AxiosHeaderValue; hasContentType(matcher?: AxiosHeaderMatcher): boolean; setContentLength( value: axios.AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher ): AxiosHeaders; getContentLength(parser?: RegExp): RegExpExecArray | null; getContentLength(matcher?: AxiosHeaderMatcher): axios.AxiosHeaderValue; hasContentLength(matcher?: AxiosHeaderMatcher): boolean; setAccept(value: axios.AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders; getAccept(parser?: RegExp): RegExpExecArray | null; getAccept(matcher?: AxiosHeaderMatcher): axios.AxiosHeaderValue; hasAccept(matcher?: AxiosHeaderMatcher): boolean; setUserAgent(value: axios.AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders; getUserAgent(parser?: RegExp): RegExpExecArray | null; getUserAgent(matcher?: AxiosHeaderMatcher): axios.AxiosHeaderValue; hasUserAgent(matcher?: AxiosHeaderMatcher): boolean; setContentEncoding( value: axios.AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher ): AxiosHeaders; getContentEncoding(parser?: RegExp): RegExpExecArray | null; getContentEncoding(matcher?: AxiosHeaderMatcher): axios.AxiosHeaderValue; hasContentEncoding(matcher?: AxiosHeaderMatcher): boolean; setAuthorization( value: axios.AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher ): AxiosHeaders; getAuthorization(parser?: RegExp): RegExpExecArray | null; getAuthorization(matcher?: AxiosHeaderMatcher): axios.AxiosHeaderValue; hasAuthorization(matcher?: AxiosHeaderMatcher): boolean; getSetCookie(): string[]; toString(): string; [Symbol.iterator](): IterableIterator<[string, axios.AxiosHeaderValue]>; } declare class AxiosError extends Error { constructor( message?: string, code?: string, config?: axios.InternalAxiosRequestConfig, request?: any, response?: axios.AxiosResponse ); config?: axios.InternalAxiosRequestConfig; code?: string; request?: any; response?: axios.AxiosResponse; isAxiosError: boolean; status?: number; toJSON: () => object; cause?: Error; event?: BrowserProgressEvent; static from( error: Error | unknown, code?: string, config?: axios.InternalAxiosRequestConfig, request?: any, response?: axios.AxiosResponse, customProps?: object ): AxiosError; static readonly ERR_FR_TOO_MANY_REDIRECTS = 'ERR_FR_TOO_MANY_REDIRECTS'; static readonly ERR_BAD_OPTION_VALUE = 'ERR_BAD_OPTION_VALUE'; static readonly ERR_BAD_OPTION = 'ERR_BAD_OPTION'; static readonly ERR_NETWORK = 'ERR_NETWORK'; static readonly ERR_DEPRECATED = 'ERR_DEPRECATED'; static readonly ERR_BAD_RESPONSE = 'ERR_BAD_RESPONSE'; static readonly ERR_BAD_REQUEST = 'ERR_BAD_REQUEST'; static readonly ERR_NOT_SUPPORT = 'ERR_NOT_SUPPORT'; static readonly ERR_INVALID_URL = 'ERR_INVALID_URL'; static readonly ERR_CANCELED = 'ERR_CANCELED'; static readonly ERR_FORM_DATA_DEPTH_EXCEEDED = 'ERR_FORM_DATA_DEPTH_EXCEEDED'; static readonly ECONNABORTED = 'ECONNABORTED'; static readonly ECONNREFUSED = 'ECONNREFUSED'; static readonly ETIMEDOUT = 'ETIMEDOUT'; } declare class CanceledError extends AxiosError { constructor(message?: string, config?: axios.InternalAxiosRequestConfig, request?: any); readonly name: 'CanceledError'; __CANCEL__?: boolean; } declare const axiosResponseDefault: unique symbol; type AxiosResponseDefault = axios.AxiosResponseDefault; type AxiosResponseResult = R extends AxiosResponseDefault ? axios.AxiosResponse : R; declare class Axios { constructor(config?: axios.AxiosRequestConfig); defaults: axios.AxiosDefaults; interceptors: { request: axios.AxiosInterceptorManager; response: axios.AxiosInterceptorManager; }; getUri(config?: axios.AxiosRequestConfig): string; request( config: axios.AxiosRequestConfig ): Promise>; get( url: string, config?: axios.AxiosRequestConfig ): Promise>; delete( url: string, config?: axios.AxiosRequestConfig ): Promise>; head( url: string, config?: axios.AxiosRequestConfig ): Promise>; options( url: string, config?: axios.AxiosRequestConfig ): Promise>; post( url: string, data?: D, config?: axios.AxiosRequestConfig ): Promise>; put( url: string, data?: D, config?: axios.AxiosRequestConfig ): Promise>; patch( url: string, data?: D, config?: axios.AxiosRequestConfig ): Promise>; postForm( url: string, data?: D, config?: axios.AxiosRequestConfig ): Promise>; putForm( url: string, data?: D, config?: axios.AxiosRequestConfig ): Promise>; patchForm( url: string, data?: D, config?: axios.AxiosRequestConfig ): Promise>; query( url: string, data?: D, config?: axios.AxiosRequestConfig ): Promise>; } declare enum HttpStatusCode { Continue = 100, SwitchingProtocols = 101, Processing = 102, EarlyHints = 103, Ok = 200, Created = 201, Accepted = 202, NonAuthoritativeInformation = 203, NoContent = 204, ResetContent = 205, PartialContent = 206, MultiStatus = 207, AlreadyReported = 208, ImUsed = 226, MultipleChoices = 300, MovedPermanently = 301, Found = 302, SeeOther = 303, NotModified = 304, UseProxy = 305, Unused = 306, TemporaryRedirect = 307, PermanentRedirect = 308, BadRequest = 400, Unauthorized = 401, PaymentRequired = 402, Forbidden = 403, NotFound = 404, MethodNotAllowed = 405, NotAcceptable = 406, ProxyAuthenticationRequired = 407, RequestTimeout = 408, Conflict = 409, Gone = 410, LengthRequired = 411, PreconditionFailed = 412, /** @deprecated Use `ContentTooLarge` instead. */ PayloadTooLarge = 413, ContentTooLarge = 413, UriTooLong = 414, UnsupportedMediaType = 415, RangeNotSatisfiable = 416, ExpectationFailed = 417, ImATeapot = 418, MisdirectedRequest = 421, /** @deprecated Use `UnprocessableContent` instead. */ UnprocessableEntity = 422, UnprocessableContent = 422, Locked = 423, FailedDependency = 424, TooEarly = 425, UpgradeRequired = 426, PreconditionRequired = 428, TooManyRequests = 429, RequestHeaderFieldsTooLarge = 431, UnavailableForLegalReasons = 451, InternalServerError = 500, NotImplemented = 501, BadGateway = 502, ServiceUnavailable = 503, GatewayTimeout = 504, HttpVersionNotSupported = 505, VariantAlsoNegotiates = 506, InsufficientStorage = 507, LoopDetected = 508, NotExtended = 510, NetworkAuthenticationRequired = 511, WebServerReturnsAnUnknownError = 520, WebServerIsDown = 521, ConnectionTimedOut = 522, OriginIsUnreachable = 523, TimeoutOccurred = 524, SslHandshakeFailed = 525, InvalidSslCertificate = 526, } type InternalAxiosError = AxiosError; declare namespace axios { interface AxiosResponseDefault { readonly [axiosResponseDefault]: true; } type AxiosHeaderParameters = Record; type AxiosError = InternalAxiosError; interface RawAxiosHeaders { [key: string]: AxiosHeaderValue; } type RawAxiosRequestHeaders = Partial< RawAxiosHeaders & { [Key in CommonRequestHeadersList]: AxiosHeaderValue; } & { 'Content-Type': ContentType; } >; type AxiosRequestHeaders = RawAxiosRequestHeaders & AxiosHeaders; type AxiosHeaderValue = AxiosHeaders | string | string[] | number | boolean | null; type RawCommonResponseHeaders = { [Key in CommonResponseHeaderKey]: AxiosHeaderValue; } & { 'set-cookie': string[]; }; type RawAxiosResponseHeaders = Partial; type AxiosResponseHeaders = RawAxiosResponseHeaders & AxiosHeaders; interface AxiosRequestTransformer { (this: InternalAxiosRequestConfig, data: any, headers: AxiosRequestHeaders): any; } interface AxiosResponseTransformer { ( this: InternalAxiosRequestConfig, data: any, headers: AxiosResponseHeaders, status?: number ): any; } interface AxiosAdapter { (config: InternalAxiosRequestConfig): AxiosPromise; } interface AxiosBasicCredentials { username: string; password: string; } interface AxiosProxyConfig { host: string; port: number; auth?: AxiosBasicCredentials; protocol?: string; } type UppercaseMethod = | 'GET' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'POST' | 'PUT' | 'PATCH' | 'PURGE' | 'LINK' | 'UNLINK' | 'QUERY'; type Method = (UppercaseMethod | Lowercase) & {}; type ResponseType = 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream' | 'formdata'; type UppercaseResponseEncoding = | 'ASCII' | 'ANSI' | 'BINARY' | 'BASE64' | 'BASE64URL' | 'HEX' | 'LATIN1' | 'UCS-2' | 'UCS2' | 'UTF-8' | 'UTF8' | 'UTF16LE'; type responseEncoding = (UppercaseResponseEncoding | Lowercase) & {}; interface TransitionalOptions { silentJSONParsing?: boolean; forcedJSONParsing?: boolean; clarifyTimeoutError?: boolean; legacyInterceptorReqResOrdering?: boolean; advertiseZstdAcceptEncoding?: boolean; validateStatusUndefinedResolves?: boolean; /** Opt in to UTF-8 Basic auth usernames in XHR/Fetch config resolution. Defaults to false. */ encodeBasicAuthUsername?: boolean; } interface GenericAbortSignal { readonly aborted: boolean; onabort?: ((...args: any) => any) | null; addEventListener?: (...args: any) => any; removeEventListener?: (...args: any) => any; } interface FormDataVisitorHelpers { defaultVisitor: SerializerVisitor; convertValue: (value: any) => any; isVisitable: (value: any) => boolean; } interface SerializerVisitor { ( this: GenericFormData, value: any, key: string | number, path: null | Array, helpers: FormDataVisitorHelpers ): boolean; } interface SerializerOptions { visitor?: SerializerVisitor; dots?: boolean; metaTokens?: boolean; indexes?: boolean | null; maxDepth?: number; Blob?: { new (...args: any[]): any }; } // tslint:disable-next-line interface FormSerializerOptions extends SerializerOptions {} interface ParamEncoder { (value: any, defaultEncoder: (value: any) => any): any; } interface CustomParamsSerializer

        > { (params: P, options?: ParamsSerializerOptions

        ): string; } interface ParamsSerializerOptions

        > extends SerializerOptions { encode?: ParamEncoder; serialize?: CustomParamsSerializer

        ; } type MaxUploadRate = number; type MaxDownloadRate = number; interface AxiosProgressEvent { loaded: number; total?: number; progress?: number; bytes: number; rate?: number; estimated?: number; upload?: boolean; download?: boolean; event?: BrowserProgressEvent; lengthComputable: boolean; } type Milliseconds = number; type AxiosAdapterName = 'fetch' | 'xhr' | 'http' | (string & {}); type AxiosAdapterConfig = AxiosAdapter | AxiosAdapterName; type AddressFamily = 4 | 6 | undefined; interface LookupAddressEntry { address: string; family?: AddressFamily; } type LookupAddress = string | LookupAddressEntry; interface AxiosRequestConfig { url?: string; method?: Method | string; baseURL?: string; allowAbsoluteUrls?: boolean; transformRequest?: AxiosRequestTransformer | AxiosRequestTransformer[]; transformResponse?: AxiosResponseTransformer | AxiosResponseTransformer[]; headers?: (RawAxiosRequestHeaders & MethodsHeaders) | AxiosHeaders; params?: P; paramsSerializer?: | ParamsSerializerOptions : P> | CustomParamsSerializer : P>; data?: D; timeout?: Milliseconds; timeoutErrorMessage?: string; withCredentials?: boolean; adapter?: AxiosAdapterConfig | AxiosAdapterConfig[]; auth?: AxiosBasicCredentials; responseType?: ResponseType; responseEncoding?: responseEncoding | string; xsrfCookieName?: string; xsrfHeaderName?: string; onUploadProgress?: (progressEvent: AxiosProgressEvent) => void; onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void; maxContentLength?: number; validateStatus?: ((status: number) => boolean) | null; maxBodyLength?: number; maxRedirects?: number; maxRate?: number | [MaxUploadRate, MaxDownloadRate]; beforeRedirect?: ( options: Record, responseDetails: { headers: Record; statusCode: HttpStatusCode }, requestDetails: { headers: Record; url: string; method: string }, ) => void; socketPath?: string | null; allowedSocketPaths?: string | string[] | null; transport?: any; httpAgent?: any; httpsAgent?: any; proxy?: AxiosProxyConfig | false; cancelToken?: CancelToken | undefined; decompress?: boolean; transitional?: TransitionalOptions; signal?: GenericAbortSignal; insecureHTTPParser?: boolean; env?: { FormData?: new (...args: any[]) => object; fetch?: (input: URL | Request | string, init?: RequestInit) => Promise; Request?: new (input: URL | Request | string, init?: RequestInit) => Request; /** * Custom Response constructor for the fetch adapter. * When response-body streaming is supported, must also accept * `ReadableStream` bodies at runtime for `onDownloadProgress`, * enabled `maxContentLength` limits, or cancellation/timeout tracking with * `responseType: 'stream'` or `'response'`. * The body parameter retains its legacy type for compatibility. */ Response?: new ( body?: ArrayBuffer | ArrayBufferView | Blob | FormData | URLSearchParams | string | null, init?: ResponseInit ) => Response; }; formSerializer?: FormSerializerOptions; family?: AddressFamily; lookup?: | (( hostname: string, options: object, cb: ( err: Error | null, address: LookupAddress | LookupAddress[], family?: AddressFamily ) => void ) => void) | (( hostname: string, options: object ) => Promise< | [address: LookupAddressEntry | LookupAddressEntry[], family?: AddressFamily] | LookupAddress >); withXSRFToken?: boolean | ((config: InternalAxiosRequestConfig) => boolean | undefined); parseReviver?: (this: any, key: string, value: any, context?: { source?: string }) => any; fetchOptions?: | Omit | Record; httpVersion?: 1 | 2; http2Options?: Record & { sessionTimeout?: number; }; formDataHeaderPolicy?: 'legacy' | 'content-only'; redact?: string[]; sensitiveHeaders?: string[]; } // Alias type RawAxiosRequestConfig = AxiosRequestConfig; interface InternalAxiosRequestConfig extends AxiosRequestConfig { headers: AxiosRequestHeaders; } interface HeadersDefaults { common: RawAxiosRequestHeaders; delete: RawAxiosRequestHeaders; get: RawAxiosRequestHeaders; head: RawAxiosRequestHeaders; post: RawAxiosRequestHeaders; put: RawAxiosRequestHeaders; patch: RawAxiosRequestHeaders; options?: RawAxiosRequestHeaders; purge?: RawAxiosRequestHeaders; link?: RawAxiosRequestHeaders; unlink?: RawAxiosRequestHeaders; query?: RawAxiosRequestHeaders; } interface AxiosDefaults extends Omit, 'headers'> { headers: HeadersDefaults; } interface CreateAxiosDefaults extends Omit< AxiosRequestConfig, 'headers' > { headers?: RawAxiosRequestHeaders | AxiosHeaders | Partial; } interface AxiosResponse { data: T; status: number; statusText: string; /** * Wall-clock milliseconds from adapter invocation to settlement, clamped at zero. * Excludes interceptors and request/response transforms. Stream consumption is not included. */ duration?: number; headers: (H & RawAxiosResponseHeaders) | AxiosResponseHeaders; config: InternalAxiosRequestConfig; request?: any; } type AxiosPromise = Promise>; interface CancelStatic { new (message?: string): Cancel; } interface Cancel { message: string | undefined; } interface Canceler { (message?: string, config?: AxiosRequestConfig, request?: any): void; } interface CancelTokenStatic { new (executor: (cancel: Canceler) => void): CancelToken; source(): CancelTokenSource; } interface CancelToken { promise: Promise; reason?: Cancel; throwIfRequested(): void; subscribe(listener: (cancel: Cancel | any) => void): void; unsubscribe(listener: (cancel: Cancel | any) => void): void; toAbortSignal(): AbortSignal; } interface CancelTokenSource { token: CancelToken; cancel: Canceler; } interface AxiosInterceptorOptions { synchronous?: boolean; runWhen?: ((config: InternalAxiosRequestConfig) => boolean) | null; } type AxiosInterceptorFulfilled = (value: T) => T | Promise; type AxiosInterceptorRejected = (error: any) => any; type AxiosRequestInterceptorUse = ( onFulfilled?: AxiosInterceptorFulfilled | null, onRejected?: AxiosInterceptorRejected | null, options?: AxiosInterceptorOptions ) => number; type AxiosResponseInterceptorUse = ( onFulfilled?: AxiosInterceptorFulfilled | null, onRejected?: AxiosInterceptorRejected | null ) => number; interface AxiosInterceptorHandler { fulfilled: AxiosInterceptorFulfilled; rejected?: AxiosInterceptorRejected; synchronous: boolean; runWhen?: ((config: InternalAxiosRequestConfig) => boolean) | null; } interface AxiosInterceptorManager { use: V extends AxiosResponse ? AxiosResponseInterceptorUse : AxiosRequestInterceptorUse; eject(id: number): void; clear(): void; handlers?: Array>; } interface AxiosInstance extends Axios { ( config: AxiosRequestConfig ): Promise>; ( url: string, config?: AxiosRequestConfig ): Promise>; create(config?: CreateAxiosDefaults): AxiosInstance; defaults: Omit & { headers: HeadersDefaults & { [key: string]: AxiosHeaderValue; }; }; } interface GenericFormData { append(name: string, value: any, options?: any): any; } interface GenericHTMLFormElement { name: string; method: string; submit(): void; } interface AxiosStatic extends AxiosInstance { Cancel: typeof CanceledError; CancelToken: CancelTokenStatic; Axios: typeof Axios; AxiosError: typeof AxiosError; CanceledError: typeof CanceledError; HttpStatusCode: typeof HttpStatusCode; readonly VERSION: string; isCancel(value: any): value is CanceledError; all(values: Array>): Promise; spread(callback: (...args: T[]) => R): (array: T[]) => R; isAxiosError(payload: any): payload is AxiosError; toFormData( sourceObj: object, targetFormData?: GenericFormData, options?: FormSerializerOptions ): GenericFormData; formToJSON(form: GenericFormData | GenericHTMLFormElement): object; getAdapter(adapters: AxiosAdapterConfig | AxiosAdapterConfig[] | undefined): AxiosAdapter; AxiosHeaders: typeof AxiosHeaders; mergeConfig( config1: AxiosRequestConfig, config2: AxiosRequestConfig ): AxiosRequestConfig; } } declare const axios: axios.AxiosStatic; export = axios; axios-axios-aa54a5e/index.d.ts000066400000000000000000000571751526660636600164050ustar00rootroot00000000000000// TypeScript Version: 4.7 type StringLiteralsOrString = Literals | (string & {}); export type AxiosHeaderValue = AxiosHeaders | string | string[] | number | boolean | null; export type AxiosHeaderParameters = Record; export interface RawAxiosHeaders { [key: string]: AxiosHeaderValue; } type MethodsHeaders = Partial< { [Key in Method as Lowercase]: AxiosHeaders; } & { common: AxiosHeaders } >; type AxiosHeaderMatcher = | string | RegExp | ((this: AxiosHeaders, value: string, name: string) => boolean); type AxiosHeaderParser = (this: AxiosHeaders, value: AxiosHeaderValue, header: string) => any; export class AxiosHeaders { constructor(headers?: RawAxiosHeaders | AxiosHeaders | string); [key: string]: any; set( headerName?: string, value?: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher ): AxiosHeaders; set(headers?: RawAxiosHeaders | AxiosHeaders | string, rewrite?: boolean): AxiosHeaders; set(headers?: Iterable<[string, AxiosHeaderValue]>, rewrite?: boolean): AxiosHeaders; get(headerName: string, parser: typeof AxiosHeaders.parseParameters): AxiosHeaderParameters; get(headerName: string, parser: RegExp): RegExpExecArray | null; get(headerName: string, matcher?: true | AxiosHeaderParser): AxiosHeaderValue; has(header: string, matcher?: AxiosHeaderMatcher): boolean; delete(header: string | string[], matcher?: AxiosHeaderMatcher): boolean; clear(matcher?: AxiosHeaderMatcher): boolean; normalize(format: boolean): AxiosHeaders; concat( ...targets: Array ): AxiosHeaders; toJSON(asStrings: true): Record; toJSON(asStrings?: false): Record; toJSON(asStrings?: boolean): Record; static from(thing?: AxiosHeaders | RawAxiosHeaders | string): AxiosHeaders; static parseParameters(value: AxiosHeaderValue): AxiosHeaderParameters; static accessor(header: string | string[]): AxiosHeaders; static concat( ...targets: Array ): AxiosHeaders; setContentType(value: ContentType, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders; getContentType(parser?: RegExp): RegExpExecArray | null; getContentType(matcher?: AxiosHeaderMatcher): AxiosHeaderValue; hasContentType(matcher?: AxiosHeaderMatcher): boolean; setContentLength(value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders; getContentLength(parser?: RegExp): RegExpExecArray | null; getContentLength(matcher?: AxiosHeaderMatcher): AxiosHeaderValue; hasContentLength(matcher?: AxiosHeaderMatcher): boolean; setAccept(value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders; getAccept(parser?: RegExp): RegExpExecArray | null; getAccept(matcher?: AxiosHeaderMatcher): AxiosHeaderValue; hasAccept(matcher?: AxiosHeaderMatcher): boolean; setUserAgent(value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders; getUserAgent(parser?: RegExp): RegExpExecArray | null; getUserAgent(matcher?: AxiosHeaderMatcher): AxiosHeaderValue; hasUserAgent(matcher?: AxiosHeaderMatcher): boolean; setContentEncoding(value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders; getContentEncoding(parser?: RegExp): RegExpExecArray | null; getContentEncoding(matcher?: AxiosHeaderMatcher): AxiosHeaderValue; hasContentEncoding(matcher?: AxiosHeaderMatcher): boolean; setAuthorization(value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders; getAuthorization(parser?: RegExp): RegExpExecArray | null; getAuthorization(matcher?: AxiosHeaderMatcher): AxiosHeaderValue; hasAuthorization(matcher?: AxiosHeaderMatcher): boolean; getSetCookie(): string[]; toString(): string; [Symbol.iterator](): IterableIterator<[string, AxiosHeaderValue]>; } type CommonRequestHeadersList = | 'Accept' | 'Content-Length' | 'User-Agent' | 'Content-Encoding' | 'Authorization' | 'Location'; type ContentType = | AxiosHeaderValue | 'text/html' | 'text/plain' | 'multipart/form-data' | 'application/json' | 'application/x-www-form-urlencoded' | 'application/octet-stream'; export type RawAxiosRequestHeaders = Partial< RawAxiosHeaders & { [Key in CommonRequestHeadersList]: AxiosHeaderValue; } & { 'Content-Type': ContentType; } >; export type AxiosRequestHeaders = RawAxiosRequestHeaders & AxiosHeaders; type CommonResponseHeadersList = | 'Server' | 'Content-Type' | 'Content-Length' | 'Cache-Control' | 'Content-Encoding'; type CommonResponseHeaderKey = CommonResponseHeadersList | Lowercase; type RawCommonResponseHeaders = { [Key in CommonResponseHeaderKey]: AxiosHeaderValue; } & { 'set-cookie': string[]; }; export type RawAxiosResponseHeaders = Partial; export type AxiosResponseHeaders = RawAxiosResponseHeaders & AxiosHeaders; export interface AxiosRequestTransformer { (this: InternalAxiosRequestConfig, data: any, headers: AxiosRequestHeaders): any; } export interface AxiosResponseTransformer { ( this: InternalAxiosRequestConfig, data: any, headers: AxiosResponseHeaders, status?: number ): any; } export interface AxiosAdapter { (config: InternalAxiosRequestConfig): AxiosPromise; } export interface AxiosBasicCredentials { username: string; password: string; } export interface AxiosProxyConfig { host: string; port: number; auth?: AxiosBasicCredentials; protocol?: string; } export enum HttpStatusCode { Continue = 100, SwitchingProtocols = 101, Processing = 102, EarlyHints = 103, Ok = 200, Created = 201, Accepted = 202, NonAuthoritativeInformation = 203, NoContent = 204, ResetContent = 205, PartialContent = 206, MultiStatus = 207, AlreadyReported = 208, ImUsed = 226, MultipleChoices = 300, MovedPermanently = 301, Found = 302, SeeOther = 303, NotModified = 304, UseProxy = 305, Unused = 306, TemporaryRedirect = 307, PermanentRedirect = 308, BadRequest = 400, Unauthorized = 401, PaymentRequired = 402, Forbidden = 403, NotFound = 404, MethodNotAllowed = 405, NotAcceptable = 406, ProxyAuthenticationRequired = 407, RequestTimeout = 408, Conflict = 409, Gone = 410, LengthRequired = 411, PreconditionFailed = 412, /** @deprecated Use `ContentTooLarge` instead. */ PayloadTooLarge = 413, ContentTooLarge = 413, UriTooLong = 414, UnsupportedMediaType = 415, RangeNotSatisfiable = 416, ExpectationFailed = 417, ImATeapot = 418, MisdirectedRequest = 421, /** @deprecated Use `UnprocessableContent` instead. */ UnprocessableEntity = 422, UnprocessableContent = 422, Locked = 423, FailedDependency = 424, TooEarly = 425, UpgradeRequired = 426, PreconditionRequired = 428, TooManyRequests = 429, RequestHeaderFieldsTooLarge = 431, UnavailableForLegalReasons = 451, InternalServerError = 500, NotImplemented = 501, BadGateway = 502, ServiceUnavailable = 503, GatewayTimeout = 504, HttpVersionNotSupported = 505, VariantAlsoNegotiates = 506, InsufficientStorage = 507, LoopDetected = 508, NotExtended = 510, NetworkAuthenticationRequired = 511, WebServerReturnsAnUnknownError = 520, WebServerIsDown = 521, ConnectionTimedOut = 522, OriginIsUnreachable = 523, TimeoutOccurred = 524, SslHandshakeFailed = 525, InvalidSslCertificate = 526, } type UppercaseMethod = | 'GET' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'POST' | 'PUT' | 'PATCH' | 'PURGE' | 'LINK' | 'UNLINK' | 'QUERY'; export type Method = (UppercaseMethod | Lowercase) & {}; export type ResponseType = | 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream' | 'formdata'; type UppercaseResponseEncoding = | 'ASCII' | 'ANSI' | 'BINARY' | 'BASE64' | 'BASE64URL' | 'HEX' | 'LATIN1' | 'UCS-2' | 'UCS2' | 'UTF-8' | 'UTF8' | 'UTF16LE'; export type responseEncoding = ( | UppercaseResponseEncoding | Lowercase ) & {}; export interface TransitionalOptions { silentJSONParsing?: boolean; forcedJSONParsing?: boolean; clarifyTimeoutError?: boolean; legacyInterceptorReqResOrdering?: boolean; advertiseZstdAcceptEncoding?: boolean; validateStatusUndefinedResolves?: boolean; /** Opt in to UTF-8 Basic auth usernames in XHR/Fetch config resolution. Defaults to false. */ encodeBasicAuthUsername?: boolean; } export interface GenericAbortSignal { readonly aborted: boolean; onabort?: ((...args: any) => any) | null; addEventListener?: (...args: any) => any; removeEventListener?: (...args: any) => any; } export interface FormDataVisitorHelpers { defaultVisitor: SerializerVisitor; convertValue: (value: any) => any; isVisitable: (value: any) => boolean; } export interface SerializerVisitor { ( this: GenericFormData, value: any, key: string | number, path: null | Array, helpers: FormDataVisitorHelpers ): boolean; } export interface SerializerOptions { visitor?: SerializerVisitor; dots?: boolean; metaTokens?: boolean; indexes?: boolean | null; maxDepth?: number; Blob?: { new (...args: any[]): any }; } // tslint:disable-next-line export interface FormSerializerOptions extends SerializerOptions {} export interface ParamEncoder { (value: any, defaultEncoder: (value: any) => any): any; } export interface CustomParamsSerializer

        > { (params: P, options?: ParamsSerializerOptions

        ): string; } export interface ParamsSerializerOptions

        > extends SerializerOptions { encode?: ParamEncoder; serialize?: CustomParamsSerializer

        ; } type MaxUploadRate = number; type MaxDownloadRate = number; type BrowserProgressEvent = any; export interface AxiosProgressEvent { loaded: number; total?: number; progress?: number; bytes: number; rate?: number; estimated?: number; upload?: boolean; download?: boolean; event?: BrowserProgressEvent; lengthComputable: boolean; } type Milliseconds = number; type AxiosAdapterName = StringLiteralsOrString<'xhr' | 'http' | 'fetch'>; type AxiosAdapterConfig = AxiosAdapter | AxiosAdapterName; export type AddressFamily = 4 | 6 | undefined; export interface LookupAddressEntry { address: string; family?: AddressFamily; } export type LookupAddress = string | LookupAddressEntry; export interface AxiosRequestConfig { url?: string; method?: StringLiteralsOrString; baseURL?: string; allowAbsoluteUrls?: boolean; transformRequest?: AxiosRequestTransformer | AxiosRequestTransformer[]; transformResponse?: AxiosResponseTransformer | AxiosResponseTransformer[]; headers?: (RawAxiosRequestHeaders & MethodsHeaders) | AxiosHeaders; params?: P; paramsSerializer?: | ParamsSerializerOptions : P> | CustomParamsSerializer : P>; data?: D; timeout?: Milliseconds; timeoutErrorMessage?: string; withCredentials?: boolean; adapter?: AxiosAdapterConfig | AxiosAdapterConfig[]; auth?: AxiosBasicCredentials; responseType?: ResponseType; responseEncoding?: StringLiteralsOrString; xsrfCookieName?: string; xsrfHeaderName?: string; onUploadProgress?: (progressEvent: AxiosProgressEvent) => void; onDownloadProgress?: (progressEvent: AxiosProgressEvent) => void; maxContentLength?: number; validateStatus?: ((status: number) => boolean) | null; maxBodyLength?: number; maxRedirects?: number; maxRate?: number | [MaxUploadRate, MaxDownloadRate]; beforeRedirect?: ( options: Record, responseDetails: { headers: Record; statusCode: HttpStatusCode; }, requestDetails: { headers: Record; url: string; method: string; }, ) => void; socketPath?: string | null; allowedSocketPaths?: string | string[] | null; transport?: any; httpAgent?: any; httpsAgent?: any; proxy?: AxiosProxyConfig | false; cancelToken?: CancelToken | undefined; decompress?: boolean; transitional?: TransitionalOptions; signal?: GenericAbortSignal; insecureHTTPParser?: boolean; env?: { FormData?: new (...args: any[]) => object; fetch?: (input: URL | Request | string, init?: RequestInit) => Promise; Request?: new (input: URL | Request | string, init?: RequestInit) => Request; /** * Custom Response constructor for the fetch adapter. * When response-body streaming is supported, must also accept * `ReadableStream` bodies at runtime for `onDownloadProgress`, * enabled `maxContentLength` limits, or cancellation/timeout tracking with * `responseType: 'stream'` or `'response'`. * The body parameter retains its legacy type for compatibility. */ Response?: new ( body?: ArrayBuffer | ArrayBufferView | Blob | FormData | URLSearchParams | string | null, init?: ResponseInit ) => Response; }; formSerializer?: FormSerializerOptions; family?: AddressFamily; lookup?: | (( hostname: string, options: object, cb: ( err: Error | null, address: LookupAddress | LookupAddress[], family?: AddressFamily ) => void ) => void) | (( hostname: string, options: object ) => Promise< [address: LookupAddressEntry | LookupAddressEntry[], family?: AddressFamily] | LookupAddress >); withXSRFToken?: boolean | ((config: InternalAxiosRequestConfig) => boolean | undefined); parseReviver?: (this: any, key: string, value: any, context?: { source?: string }) => any; fetchOptions?: Omit | Record; httpVersion?: 1 | 2; http2Options?: Record & { sessionTimeout?: number; }; formDataHeaderPolicy?: 'legacy' | 'content-only'; redact?: string[]; sensitiveHeaders?: string[]; } // Alias export type RawAxiosRequestConfig = AxiosRequestConfig; export interface InternalAxiosRequestConfig extends AxiosRequestConfig { headers: AxiosRequestHeaders; } export interface HeadersDefaults { common: RawAxiosRequestHeaders; delete: RawAxiosRequestHeaders; get: RawAxiosRequestHeaders; head: RawAxiosRequestHeaders; post: RawAxiosRequestHeaders; put: RawAxiosRequestHeaders; patch: RawAxiosRequestHeaders; options?: RawAxiosRequestHeaders; purge?: RawAxiosRequestHeaders; link?: RawAxiosRequestHeaders; unlink?: RawAxiosRequestHeaders; query?: RawAxiosRequestHeaders; } export interface AxiosDefaults extends Omit, 'headers'> { headers: HeadersDefaults; } export interface CreateAxiosDefaults extends Omit< AxiosRequestConfig, 'headers' > { headers?: RawAxiosRequestHeaders | AxiosHeaders | Partial; } export interface AxiosResponse { data: T; status: number; statusText: string; /** * Wall-clock milliseconds from adapter invocation to settlement, clamped at zero. * Excludes interceptors and request/response transforms. Stream consumption is not included. */ duration?: number; headers: (H & RawAxiosResponseHeaders) | AxiosResponseHeaders; config: InternalAxiosRequestConfig; request?: any; } export class AxiosError extends Error { constructor( message?: string, code?: string, config?: InternalAxiosRequestConfig, request?: any, response?: AxiosResponse ); config?: InternalAxiosRequestConfig; code?: string; request?: any; response?: AxiosResponse; isAxiosError: boolean; status?: number; toJSON: () => object; cause?: Error; event?: BrowserProgressEvent; static from( error: Error | unknown, code?: string, config?: InternalAxiosRequestConfig, request?: any, response?: AxiosResponse, customProps?: object ): AxiosError; static readonly ERR_FR_TOO_MANY_REDIRECTS = 'ERR_FR_TOO_MANY_REDIRECTS'; static readonly ERR_BAD_OPTION_VALUE = 'ERR_BAD_OPTION_VALUE'; static readonly ERR_BAD_OPTION = 'ERR_BAD_OPTION'; static readonly ERR_NETWORK = 'ERR_NETWORK'; static readonly ERR_DEPRECATED = 'ERR_DEPRECATED'; static readonly ERR_BAD_RESPONSE = 'ERR_BAD_RESPONSE'; static readonly ERR_BAD_REQUEST = 'ERR_BAD_REQUEST'; static readonly ERR_NOT_SUPPORT = 'ERR_NOT_SUPPORT'; static readonly ERR_INVALID_URL = 'ERR_INVALID_URL'; static readonly ERR_CANCELED = 'ERR_CANCELED'; static readonly ERR_FORM_DATA_DEPTH_EXCEEDED = 'ERR_FORM_DATA_DEPTH_EXCEEDED'; static readonly ECONNABORTED = 'ECONNABORTED'; static readonly ECONNREFUSED = 'ECONNREFUSED'; static readonly ETIMEDOUT = 'ETIMEDOUT'; } export class CanceledError extends AxiosError { constructor(message?: string, config?: InternalAxiosRequestConfig, request?: any); readonly name: 'CanceledError'; __CANCEL__?: boolean; } declare const axiosResponseDefault: unique symbol; export interface AxiosResponseDefault { readonly [axiosResponseDefault]: true; } type AxiosResponseResult = R extends AxiosResponseDefault ? AxiosResponse : R; export type AxiosPromise = Promise>; export interface CancelStatic { new (message?: string): Cancel; } export interface Cancel { message: string | undefined; } export interface Canceler { (message?: string, config?: AxiosRequestConfig, request?: any): void; } export interface CancelTokenStatic { new (executor: (cancel: Canceler) => void): CancelToken; source(): CancelTokenSource; } export interface CancelToken { promise: Promise; reason?: Cancel; throwIfRequested(): void; subscribe(listener: (cancel: Cancel | any) => void): void; unsubscribe(listener: (cancel: Cancel | any) => void): void; toAbortSignal(): AbortSignal; } export interface CancelTokenSource { token: CancelToken; cancel: Canceler; } export interface AxiosInterceptorOptions { synchronous?: boolean; runWhen?: ((config: InternalAxiosRequestConfig) => boolean) | null; } type AxiosInterceptorFulfilled = (value: T) => T | Promise; type AxiosInterceptorRejected = (error: any) => any; type AxiosRequestInterceptorUse = ( onFulfilled?: AxiosInterceptorFulfilled | null, onRejected?: AxiosInterceptorRejected | null, options?: AxiosInterceptorOptions ) => number; type AxiosResponseInterceptorUse = ( onFulfilled?: AxiosInterceptorFulfilled | null, onRejected?: AxiosInterceptorRejected | null ) => number; interface AxiosInterceptorHandler { fulfilled: AxiosInterceptorFulfilled; rejected?: AxiosInterceptorRejected; synchronous: boolean; runWhen?: ((config: InternalAxiosRequestConfig) => boolean) | null; } export interface AxiosInterceptorManager { use: V extends AxiosResponse ? AxiosResponseInterceptorUse : AxiosRequestInterceptorUse; eject(id: number): void; clear(): void; handlers?: Array>; } export class Axios { constructor(config?: AxiosRequestConfig); defaults: AxiosDefaults; interceptors: { request: AxiosInterceptorManager; response: AxiosInterceptorManager; }; getUri(config?: AxiosRequestConfig): string; request( config: AxiosRequestConfig ): Promise>; get( url: string, config?: AxiosRequestConfig ): Promise>; delete( url: string, config?: AxiosRequestConfig ): Promise>; head( url: string, config?: AxiosRequestConfig ): Promise>; options( url: string, config?: AxiosRequestConfig ): Promise>; post( url: string, data?: D, config?: AxiosRequestConfig ): Promise>; put( url: string, data?: D, config?: AxiosRequestConfig ): Promise>; patch( url: string, data?: D, config?: AxiosRequestConfig ): Promise>; postForm( url: string, data?: D, config?: AxiosRequestConfig ): Promise>; putForm( url: string, data?: D, config?: AxiosRequestConfig ): Promise>; patchForm( url: string, data?: D, config?: AxiosRequestConfig ): Promise>; query( url: string, data?: D, config?: AxiosRequestConfig ): Promise>; } export interface AxiosInstance extends Axios { ( config: AxiosRequestConfig ): Promise>; ( url: string, config?: AxiosRequestConfig ): Promise>; create(config?: CreateAxiosDefaults): AxiosInstance; defaults: Omit & { headers: HeadersDefaults & { [key: string]: AxiosHeaderValue; }; }; } export interface GenericFormData { append(name: string, value: any, options?: any): any; } export interface GenericHTMLFormElement { name: string; method: string; submit(): void; } export function getAdapter( adapters: AxiosAdapterConfig | AxiosAdapterConfig[] | undefined ): AxiosAdapter; export function toFormData( sourceObj: object, targetFormData?: GenericFormData, options?: FormSerializerOptions ): GenericFormData; export function formToJSON(form: GenericFormData | GenericHTMLFormElement): object; export function isAxiosError( payload: any ): payload is AxiosError; export function spread(callback: (...args: T[]) => R): (array: T[]) => R; export function isCancel(value: any): value is CanceledError; export function all(values: Array>): Promise; export function mergeConfig( config1: AxiosRequestConfig, config2: AxiosRequestConfig ): AxiosRequestConfig; export function create(config?: CreateAxiosDefaults): AxiosInstance; export interface AxiosStatic extends AxiosInstance { Cancel: typeof CanceledError; CancelToken: CancelTokenStatic; Axios: typeof Axios; AxiosError: typeof AxiosError; HttpStatusCode: typeof HttpStatusCode; readonly VERSION: string; isCancel: typeof isCancel; all: typeof all; spread: typeof spread; isAxiosError: typeof isAxiosError; toFormData: typeof toFormData; formToJSON: typeof formToJSON; getAdapter: typeof getAdapter; CanceledError: typeof CanceledError; AxiosHeaders: typeof AxiosHeaders; mergeConfig: typeof mergeConfig; } declare const axios: AxiosStatic; export default axios; axios-axios-aa54a5e/index.js000066400000000000000000000013001526660636600161240ustar00rootroot00000000000000import axios from './lib/axios.js'; // This module is intended to unwrap Axios default export as named. // Keep top-level export same with static properties // so that it can keep same with es module or cjs const { Axios, AxiosError, CanceledError, isCancel, CancelToken, VERSION, all, Cancel, isAxiosError, spread, toFormData, AxiosHeaders, HttpStatusCode, formToJSON, getAdapter, mergeConfig, create, } = axios; export { axios as default, create, Axios, AxiosError, CanceledError, isCancel, CancelToken, VERSION, all, Cancel, isAxiosError, spread, toFormData, AxiosHeaders, HttpStatusCode, formToJSON, getAdapter, mergeConfig, }; axios-axios-aa54a5e/lib/000077500000000000000000000000001526660636600152335ustar00rootroot00000000000000axios-axios-aa54a5e/lib/adapters/000077500000000000000000000000001526660636600170365ustar00rootroot00000000000000axios-axios-aa54a5e/lib/adapters/README.md000066400000000000000000000016171526660636600203220ustar00rootroot00000000000000# axios // adapters The modules under `adapters/` are modules that handle dispatching a request and settling a returned `Promise` once a response is received. ## Example ```js var settle = require('../core/settle'); module.exports = function myAdapter(config) { // At this point: // - config has been merged with defaults // - request transformers have already run // - request interceptors have already run // Make the request using config provided // Upon response settle the Promise return new Promise(function (resolve, reject) { var response = { data: responseData, status: request.status, statusText: request.statusText, headers: responseHeaders, config: config, request: request, }; settle(resolve, reject, response); // From here: // - response transformers will run // - response interceptors will run }); }; ``` axios-axios-aa54a5e/lib/adapters/adapters.js000066400000000000000000000066611526660636600212100ustar00rootroot00000000000000import utils from '../utils.js'; import httpAdapter from './http.js'; import xhrAdapter from './xhr.js'; import * as fetchAdapter from './fetch.js'; import AxiosError from '../core/AxiosError.js'; /** * Known adapters mapping. * Provides environment-specific adapters for Axios: * - `http` for Node.js * - `xhr` for browsers * - `fetch` for fetch API-based requests * * @type {Object} */ const knownAdapters = { http: httpAdapter, xhr: xhrAdapter, fetch: { get: fetchAdapter.getFetch, }, }; // Assign adapter names for easier debugging and identification utils.forEach(knownAdapters, (fn, value) => { if (fn) { try { // Null-proto descriptors so a polluted Object.prototype.get cannot turn // these data descriptors into accessor descriptors on the way in. Object.defineProperty(fn, 'name', { __proto__: null, value }); } catch (e) { // eslint-disable-next-line no-empty } Object.defineProperty(fn, 'adapterName', { __proto__: null, value }); } }); /** * Render a rejection reason string for unknown or unsupported adapters * * @param {string} reason * @returns {string} */ const renderReason = (reason) => `- ${reason}`; /** * Check if the adapter is resolved (function, null, or false) * * @param {Function|null|false} adapter * @returns {boolean} */ const isResolvedHandle = (adapter) => utils.isFunction(adapter) || adapter === null || adapter === false; /** * Get the first suitable adapter from the provided list. * Tries each adapter in order until a supported one is found. * Throws an AxiosError if no adapter is suitable. * * @param {Array|string|Function} adapters - Adapter(s) by name or function. * @param {Object} config - Axios request configuration * @throws {AxiosError} If no suitable adapter is available * @returns {Function} The resolved adapter function */ function getAdapter(adapters, config) { adapters = utils.isArray(adapters) ? adapters : [adapters]; const { length } = adapters; let nameOrAdapter; let adapter; const rejectedReasons = {}; for (let i = 0; i < length; i++) { nameOrAdapter = adapters[i]; let id; adapter = nameOrAdapter; if (!isResolvedHandle(nameOrAdapter)) { adapter = knownAdapters[(id = String(nameOrAdapter)).toLowerCase()]; if (adapter === undefined) { throw new AxiosError(`Unknown adapter '${id}'`); } } if (adapter && (utils.isFunction(adapter) || (adapter = adapter.get(config)))) { break; } rejectedReasons[id || '#' + i] = adapter; } if (!adapter) { const reasons = Object.entries(rejectedReasons).map( ([id, state]) => `adapter ${id} ` + (state === false ? 'is not supported by the environment' : 'is not available in the build') ); let s = length ? reasons.length > 1 ? 'since :\n' + reasons.map(renderReason).join('\n') : ' ' + renderReason(reasons[0]) : 'as no adapter specified'; throw new AxiosError( `There is no suitable adapter to dispatch the request ` + s, AxiosError.ERR_NOT_SUPPORT ); } return adapter; } /** * Exports Axios adapters and utility to resolve an adapter */ export default { /** * Resolve an adapter from a list of adapter names or functions. * @type {Function} */ getAdapter, /** * Exposes all known adapters * @type {Object} */ adapters: knownAdapters, }; axios-axios-aa54a5e/lib/adapters/fetch.js000066400000000000000000000570171526660636600204770ustar00rootroot00000000000000import platform from '../platform/index.js'; import utils from '../utils.js'; import AxiosError from '../core/AxiosError.js'; import composeSignals from '../helpers/composeSignals.js'; import { trackStream } from '../helpers/trackStream.js'; import createMultipartBody from '../core/createMultipartBody.js'; import AxiosHeaders from '../core/AxiosHeaders.js'; import { progressEventReducer, progressEventDecorator, asyncDecorator, } from '../helpers/progressEventReducer.js'; import resolveConfig from '../helpers/resolveConfig.js'; import settle from '../core/settle.js'; import estimateDataURLDecodedBytes from '../helpers/estimateDataURLDecodedBytes.js'; import { VERSION } from '../env/data.js'; import { toByteStringHeaderObject } from '../helpers/sanitizeHeaderValue.js'; const DEFAULT_CHUNK_SIZE = 64 * 1024; const DEFAULT_REQUEST_OPTIONS = { cache: 'default', redirect: 'follow', referrer: 'about:client', referrerPolicy: '', mode: 'cors', integrity: '', keepalive: false, priority: 'auto', window: null, }; const { isFunction } = utils; /** * Encode a UTF-8 string to a Latin-1 byte string for use with btoa(). * This is a modern replacement for the deprecated unescape(encodeURIComponent(str)) pattern. * * @param {string} str The string to encode * * @returns {string} UTF-8 bytes as a Latin-1 string */ const encodeUTF8 = (str) => encodeURIComponent(str).replace(/%([0-9A-F]{2})/gi, (_, hex) => String.fromCharCode(parseInt(hex, 16)) ); // Node's WHATWG URL parser returns `username` and `password` percent-encoded. // Decode before composing the `auth` option so credentials such as // `my%40email.com:pass` are sent as `my@email.com:pass`. Falls back to the // original value for malformed input so a bad encoding never throws. const decodeURIComponentSafe = (value) => { if (!utils.isString(value)) { return value; } try { return decodeURIComponent(value); } catch (error) { return value; } }; const test = (fn, ...args) => { try { return !!fn(...args); } catch (e) { return false; } }; const maybeWithAuthCredentials = (url) => { const protocolIndex = url.indexOf('://'); let urlToCheck = url; if (protocolIndex !== -1) { urlToCheck = urlToCheck.slice(protocolIndex + 3); } return urlToCheck.includes('@') || urlToCheck.includes(':'); }; const factory = (env) => { const globalObject = utils.global !== undefined && utils.global !== null ? utils.global : globalThis; const { ReadableStream, TextEncoder } = globalObject; env = utils.merge.call( { skipUndefined: true, }, { Request: globalObject.Request, Response: globalObject.Response, Blob: globalObject.Blob, }, env ); const envFetch = utils.hasOwnProp(env, 'fetch') ? env.fetch : undefined; const { Request, Response, Blob } = env; const isFetchSupported = envFetch ? isFunction(envFetch) : typeof fetch === 'function'; const isRequestSupported = isFunction(Request); const isResponseSupported = isFunction(Response); if (!isFetchSupported) { return false; } const isReadableStreamSupported = isFetchSupported && isFunction(ReadableStream); const encodeText = isFetchSupported && (typeof TextEncoder === 'function' ? ( (encoder) => (str) => encoder.encode(str) )(new TextEncoder()) : async (str) => new Uint8Array(await new Request(str).arrayBuffer())); const supportsRequestStream = isRequestSupported && isReadableStreamSupported && test(() => { let duplexAccessed = false; const request = new Request(platform.origin, { body: new ReadableStream(), method: 'POST', get duplex() { duplexAccessed = true; return 'half'; }, }); const hasContentType = request.headers.has('Content-Type'); if (request.body != null) { request.body.cancel(); } return duplexAccessed && !hasContentType; }); const supportsResponseStream = isResponseSupported && isReadableStreamSupported && test(() => utils.isReadableStream(new Response('').body)); // Some runtimes reject every explicit cache mode. Use own defaults for both // probes so unrelated polluted options cannot affect cache-mode detection. const requestDefaults = Object.assign(Object.create(null), DEFAULT_REQUEST_OPTIONS); if (isRequestSupported && !test(() => new Request(platform.origin, requestDefaults))) { delete requestDefaults.cache; // Only omit cache when that makes Request construction succeed. Otherwise // retain the explicit default that protects supporting runtimes from pollution. if (!test(() => new Request(platform.origin, requestDefaults))) { requestDefaults.cache = DEFAULT_REQUEST_OPTIONS.cache; } } const resolvers = { stream: supportsResponseStream && ((res) => res.body), }; isFetchSupported && (() => { ['text', 'arrayBuffer', 'blob', 'formData', 'stream'].forEach((type) => { !resolvers[type] && (resolvers[type] = (res, config) => { let method = res && res[type]; if (method) { return method.call(res); } throw new AxiosError( `Response type '${type}' is not supported`, AxiosError.ERR_NOT_SUPPORT, config ); }); }); })(); const getBodyLength = async (body) => { if (body == null) { return 0; } if (utils.isBlob(body)) { return body.size; } if (utils.isArrayBufferView(body) || utils.isArrayBuffer(body)) { return body.byteLength; } if (utils.isURLSearchParams(body)) { body = body + ''; } if (utils.isString(body)) { return (await encodeText(body)).byteLength; } }; const resolveBodyLength = async (headers, body) => { const length = utils.toFiniteNumber(headers.getContentLength()); return length == null ? getBodyLength(body) : length; }; return async (config) => { let { url, method, data, signal, cancelToken, timeout, onDownloadProgress, onUploadProgress, responseType, headers, withCredentials = 'same-origin', fetchOptions, maxContentLength, maxBodyLength, maxRedirects, } = resolveConfig(config); const hasMaxContentLength = utils.isNumber(maxContentLength) && maxContentLength > -1; const hasMaxBodyLength = utils.isNumber(maxBodyLength) && maxBodyLength > -1; const own = (key) => (utils.hasOwnProp(config, key) ? config[key] : undefined); let _fetch = envFetch || fetch; responseType = responseType ? (responseType + '').toLowerCase() : 'text'; let composedSignal = composeSignals( [signal, cancelToken && cancelToken.toAbortSignal()], timeout ); let request = null; const unsubscribe = composedSignal && composedSignal.unsubscribe && (() => { composedSignal.unsubscribe(); }); let requestContentLength; // AxiosErrors we raise while request or response bodies are being streamed. Captured // by identity so the catch block can surface it directly, regardless of // how the runtime wraps the resulting fetch rejection (undici exposes it // as `err.cause`; some browsers drop the original error entirely). let pendingBodyError = null; let pendingResponseError = null; const maxBodyLengthError = () => new AxiosError( 'Request body larger than maxBodyLength limit', AxiosError.ERR_BAD_REQUEST, config, request ); try { // HTTP basic authentication let auth = undefined; const configAuth = own('auth'); if (configAuth) { const username = utils.getSafeProp(configAuth, 'username') || ''; const password = utils.getSafeProp(configAuth, 'password') || ''; auth = { username, password, }; } let parsedURL; if (maybeWithAuthCredentials(url)) { parsedURL = new URL(url, platform.origin); if (!auth && (parsedURL.username || parsedURL.password)) { const urlUsername = decodeURIComponentSafe(parsedURL.username); const urlPassword = decodeURIComponentSafe(parsedURL.password); auth = { username: urlUsername, password: urlPassword, }; } if (parsedURL.username || parsedURL.password) { parsedURL.username = ''; parsedURL.password = ''; url = parsedURL.href; } } if (auth) { headers.delete('authorization'); headers.set( 'Authorization', 'Basic ' + btoa(encodeUTF8((auth.username || '') + ':' + (auth.password || ''))) ); } // Enforce maxContentLength for data: URLs up-front so we never materialize // an oversized payload. The HTTP adapter applies the same check (see http.js // "if (protocol === 'data:')" branch). if (hasMaxContentLength && parsedURL && parsedURL.protocol === 'data:') { const estimated = estimateDataURLDecodedBytes(parsedURL.href); if (estimated > maxContentLength) { throw new AxiosError( 'maxContentLength size of ' + maxContentLength + ' exceeded', AxiosError.ERR_BAD_RESPONSE, config, request ); } } const formContentType = utils.isSpecCompliantForm(data) && AxiosHeaders.from(own('headers')).getContentType(); let formBoundary; if ( utils.isString(formContentType) && /^multipart\/form-data(?:\s*;|$)/i.test(formContentType.trim()) ) { const parameters = AxiosHeaders.parseParameters(formContentType); if (utils.hasOwnProp(parameters, 'boundary')) formBoundary = parameters.boundary; } if ( utils.isSpecCompliantForm(data) && method !== 'get' && method !== 'head' && (hasMaxBodyLength || (onUploadProgress && supportsRequestStream) || formBoundary !== undefined) ) { data = createMultipartBody( data, Blob, hasMaxBodyLength ? maxBodyLength : Infinity, maxBodyLengthError, formBoundary ); // Blob.type lowercases its value, including a case-sensitive boundary. // Retain the caller's header when an explicit boundary was serialized. headers.setContentType(formBoundary === undefined ? data.type : formContentType); requestContentLength = data.size; } // Enforce maxBodyLength against known-size bodies before dispatch using // the body's *actual* size — never a caller-declared Content-Length, // which could under-report to slip an oversized body past the check. // Unknown-size streams return undefined here and are counted per-chunk // below as fetch consumes them. if (hasMaxBodyLength && method !== 'get' && method !== 'head') { const outboundLength = await getBodyLength(data); if (typeof outboundLength === 'number' && isFinite(outboundLength)) { requestContentLength = outboundLength; if (outboundLength > maxBodyLength) { throw maxBodyLengthError(); } } } // A streamed body under maxBodyLength must be counted as fetch consumes // it; its size is never trusted from a caller-declared Content-Length. const mustEnforceStreamBody = hasMaxBodyLength && (utils.isReadableStream(data) || utils.isStream(data)); const trackRequestStream = (stream, onProgress, flush) => trackStream( stream, DEFAULT_CHUNK_SIZE, (loadedBytes) => { if (hasMaxBodyLength && loadedBytes > maxBodyLength) { throw (pendingBodyError = maxBodyLengthError()); } onProgress && onProgress(loadedBytes); }, flush ); if ( supportsRequestStream && method !== 'get' && method !== 'head' && (onUploadProgress || mustEnforceStreamBody) ) { requestContentLength = requestContentLength == null ? await resolveBodyLength(headers, data) : requestContentLength; // A declared length of 0 is only trusted to skip the wrap when we are // not enforcing a stream limit (which must not rely on that header). if (requestContentLength !== 0 || mustEnforceStreamBody) { let _request = new Request(url, { method: 'POST', body: data, duplex: 'half', }); let contentTypeHeader; if ( utils.isFormData(data) && (contentTypeHeader = _request.headers.get('content-type')) ) { headers.setContentType(contentTypeHeader); } if (_request.body) { const [onProgress, flush] = (onUploadProgress && progressEventDecorator( requestContentLength, progressEventReducer(asyncDecorator(onUploadProgress)) )) || []; data = trackRequestStream(_request.body, onProgress, flush); } } } else if ( mustEnforceStreamBody && !isRequestSupported && isReadableStreamSupported && method !== 'get' && method !== 'head' ) { data = trackRequestStream(data); } else if ( mustEnforceStreamBody && isRequestSupported && !supportsRequestStream && method !== 'get' && method !== 'head' ) { throw new AxiosError( 'Stream request bodies are not supported by the current fetch implementation', AxiosError.ERR_NOT_SUPPORT, config, request ); } if (!utils.isString(withCredentials)) { withCredentials = withCredentials ? 'include' : 'omit'; } // Cloudflare Workers throws when credentials are defined // see https://github.com/cloudflare/workerd/issues/902 const isCredentialsSupported = isRequestSupported && 'credentials' in Request.prototype; // If data is FormData and Content-Type is multipart/form-data without boundary, // delete it so fetch can set it correctly with the boundary if (utils.isFormData(data)) { const contentType = headers.getContentType(); if ( contentType && /^multipart\/form-data/i.test(contentType) && !/boundary=/i.test(contentType) ) { headers.delete('content-type'); } } // Set User-Agent header if not already set (fetch defaults to 'node' in Node.js) headers.set('User-Agent', 'axios/' + VERSION, false); const safeFetchOptions = fetchOptions == null ? fetchOptions : Object.assign(Object.create(null), fetchOptions); if (safeFetchOptions) { // These options are owned by Axios and are already reflected in the // resolved Request passed to fetch. delete safeFetchOptions.body; delete safeFetchOptions.headers; delete safeFetchOptions.method; delete safeFetchOptions.signal; delete safeFetchOptions.duplex; delete safeFetchOptions.credentials; } const resolvedOptions = Object.assign(Object.create(null), safeFetchOptions, { signal: composedSignal, method: method.toUpperCase(), headers: toByteStringHeaderObject(headers.normalize()), body: data, duplex: 'half', credentials: isCredentialsSupported ? withCredentials : undefined, }); if (isRequestSupported) { utils.forEach(requestDefaults, (value, key) => { if (resolvedOptions[key] === undefined) { resolvedOptions[key] = value; } }); if (resolvedOptions.signal === undefined) { resolvedOptions.signal = null; } if (resolvedOptions.body === undefined) { resolvedOptions.body = null; } } if (maxRedirects === 0) { resolvedOptions.redirect = 'manual'; if (safeFetchOptions) { safeFetchOptions.redirect = 'manual'; } } request = isRequestSupported && new Request(url, resolvedOptions); let response = await (isRequestSupported ? _fetch(request, safeFetchOptions) : _fetch(url, resolvedOptions)); const responseHeaders = AxiosHeaders.from(response.headers); // Cheap pre-check: if the server honestly declares a content-length that // already exceeds the cap, reject before we start streaming. if (hasMaxContentLength) { const declaredLength = utils.toFiniteNumber(responseHeaders.getContentLength()); if (declaredLength != null && declaredLength > maxContentLength) { throw new AxiosError( 'maxContentLength size of ' + maxContentLength + ' exceeded', AxiosError.ERR_BAD_RESPONSE, config, request ); } } const isStreamResponse = supportsResponseStream && (responseType === 'stream' || responseType === 'response'); if ( supportsResponseStream && response.body && (onDownloadProgress || hasMaxContentLength || (isStreamResponse && unsubscribe)) ) { const options = {}; ['status', 'statusText', 'headers'].forEach((prop) => { options[prop] = response[prop]; }); const responseContentLength = utils.toFiniteNumber(responseHeaders.getContentLength()); const [onProgress, flush] = (onDownloadProgress && progressEventDecorator( responseContentLength, progressEventReducer(asyncDecorator(onDownloadProgress), true) )) || []; let bytesRead = 0; const onChunkProgress = (loadedBytes) => { if (hasMaxContentLength) { bytesRead = loadedBytes; if (bytesRead > maxContentLength) { throw (pendingResponseError = new AxiosError( 'maxContentLength size of ' + maxContentLength + ' exceeded', AxiosError.ERR_BAD_RESPONSE, config, request )); } } onProgress && onProgress(loadedBytes); }; response = new Response( trackStream(response.body, DEFAULT_CHUNK_SIZE, onChunkProgress, () => { flush && flush(); unsubscribe && unsubscribe(); }), options ); } responseType = responseType || 'text'; let responseData = await resolvers[utils.findKey(resolvers, responseType) || 'text']( response, config ); // Fallback enforcement for environments without ReadableStream support // (legacy runtimes). Detect materialized size from typed output; skip // streams/Response passthrough since the user will read those themselves. if (hasMaxContentLength && !supportsResponseStream && !isStreamResponse) { let materializedSize; if (responseData != null) { if (typeof responseData.byteLength === 'number') { materializedSize = responseData.byteLength; } else if (typeof responseData.size === 'number') { materializedSize = responseData.size; } else if (typeof responseData === 'string') { materializedSize = typeof TextEncoder === 'function' ? new TextEncoder().encode(responseData).byteLength : responseData.length; } } if (typeof materializedSize === 'number' && materializedSize > maxContentLength) { throw new AxiosError( 'maxContentLength size of ' + maxContentLength + ' exceeded', AxiosError.ERR_BAD_RESPONSE, config, request ); } } !isStreamResponse && unsubscribe && unsubscribe(); return await new Promise((resolve, reject) => { settle(resolve, reject, { data: responseData, headers: AxiosHeaders.from(response.headers), status: response.status, statusText: response.statusText, config, request, }); }); } catch (err) { unsubscribe && unsubscribe(); // Safari can surface fetch aborts as a DOMException-like object whose // branded getters throw. Prefer our composed signal reason before reading // the caught error, preserving timeout vs cancellation semantics. if (composedSignal && composedSignal.aborted && composedSignal.reason instanceof AxiosError) { const canceledError = composedSignal.reason; canceledError.config = config; request && (canceledError.request = request); if (err !== canceledError) { // Non-enumerable to match native Error `cause` semantics so loggers // don't recurse into circular fetch internals (see #7205). Object.defineProperty(canceledError, 'cause', { __proto__: null, value: err, writable: true, enumerable: false, configurable: true, }); } throw canceledError; } // Surface a maxBodyLength violation we raised while the request body was // being streamed. Keeping the original error (rather than reading // `err.cause.isAxiosError`) keeps the error deterministic across runtimes // and avoids both prototype-pollution reads and mis-attributing a foreign // AxiosError that merely happened to land in `err.cause`. if (pendingBodyError) { request && !pendingBodyError.request && (pendingBodyError.request = request); throw pendingBodyError; } if (pendingResponseError) { throw pendingResponseError; } // Re-throw AxiosErrors we raised synchronously (data: URL / content-length // pre-checks, response size enforcement) without re-wrapping them. if (err instanceof AxiosError) { request && !err.request && (err.request = request); throw err; } if (err && err.name === 'TypeError' && /Load failed|fetch/i.test(err.message)) { const networkError = new AxiosError( 'Network Error', AxiosError.ERR_NETWORK, config, request, err && err.response ); // Non-enumerable to match native Error `cause` semantics so loggers // don't recurse into circular fetch internals (see #7205). Object.defineProperty(networkError, 'cause', { __proto__: null, value: err.cause || err, writable: true, enumerable: false, configurable: true, }); throw networkError; } throw AxiosError.from(err, err && err.code, config, request, err && err.response); } }; }; const seedCache = new Map(); export const getFetch = (config) => { const env = Object.create(null); const configuredEnv = config && utils.hasOwnProp(config, 'env') && config.env; ['fetch', 'Request', 'Response', 'Blob'].forEach((key) => { if (configuredEnv && utils.hasOwnProp(configuredEnv, key)) { env[key] = configuredEnv[key]; } }); const { fetch, Request, Response, Blob } = env; const seeds = [Request, Response, fetch, Blob]; let len = seeds.length, i = len, seed, target, map = seedCache; while (i--) { seed = seeds[i]; target = map.get(seed); target === undefined && map.set(seed, (target = i ? new Map() : factory(env))); map = target; } return target; }; const adapter = getFetch(); export default adapter; axios-axios-aa54a5e/lib/adapters/http.js000077500000000000000000001554341526660636600203720ustar00rootroot00000000000000import utils from '../utils.js'; import settle from '../core/settle.js'; import buildFullPath from '../core/buildFullPath.js'; import buildURL from '../helpers/buildURL.js'; import { getProxyForUrl } from 'proxy-from-env'; import HttpsProxyAgent from 'https-proxy-agent'; import http from 'http'; import https from 'https'; import http2 from 'http2'; import util from 'util'; import { resolve as resolvePath } from 'path'; import followRedirects from 'follow-redirects'; import zlib from 'zlib'; import { VERSION } from '../env/data.js'; import transitionalDefaults from '../defaults/transitional.js'; import AxiosError from '../core/AxiosError.js'; import CanceledError from '../cancel/CanceledError.js'; import platform from '../platform/index.js'; import fromDataURI from '../helpers/fromDataURI.js'; import stream from 'stream'; import AxiosHeaders from '../core/AxiosHeaders.js'; import setFormDataHeaders from '../core/setFormDataHeaders.js'; import AxiosTransformStream from '../helpers/AxiosTransformStream.js'; import { EventEmitter } from 'events'; import formDataToStream from '../helpers/formDataToStream.js'; import readBlob from '../helpers/readBlob.js'; import ZlibHeaderTransformStream from '../helpers/ZlibHeaderTransformStream.js'; import Http2Sessions from '../helpers/Http2Sessions.js'; import callbackify from '../helpers/callbackify.js'; import shouldBypassProxy from '../helpers/shouldBypassProxy.js'; import bind from '../helpers/bind.js'; import { toByteStringHeaderObject } from '../helpers/sanitizeHeaderValue.js'; import { progressEventReducer, progressEventDecorator, asyncDecorator, } from '../helpers/progressEventReducer.js'; import { estimateDataURLBufferAllocation } from '../helpers/estimateDataURLDecodedBytes.js'; const zlibOptions = { flush: zlib.constants.Z_SYNC_FLUSH, finishFlush: zlib.constants.Z_SYNC_FLUSH, }; const brotliOptions = { flush: zlib.constants.BROTLI_OPERATION_FLUSH, finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH, }; const zstdOptions = { flush: zlib.constants.ZSTD_e_flush, finishFlush: zlib.constants.ZSTD_e_flush, }; const isBrotliSupported = utils.isFunction(zlib.createBrotliDecompress); const isZstdSupported = utils.isFunction(zlib.createZstdDecompress); const ACCEPT_ENCODING = 'gzip, compress, deflate' + (isBrotliSupported ? ', br' : ''); const ACCEPT_ENCODING_WITH_ZSTD = ACCEPT_ENCODING + (isZstdSupported ? ', zstd' : ''); const scheduleProgress = typeof process !== 'undefined' && process.nextTick ? process.nextTick.bind(process) : utils.asap; const { http: httpFollow, https: httpsFollow } = followRedirects; const isHttps = /https:?/; // Symbols used to bind a single 'error' listener to a pooled socket and track // the request currently owning that socket across keep-alive reuse (issue #10780). const kAxiosSocketListener = Symbol('axios.http.socketListener'); const kAxiosCurrentReq = Symbol('axios.http.currentReq'); // A shared listener avoids retaining an adapter context for the lifetime of a // pooled socket. EventEmitter invokes listeners with `this` set to the emitter. function handleSocketError(err) { const current = this[kAxiosCurrentReq]; if (current && !current.destroyed) { current.destroy(err); } } // Tags HttpsProxyAgent instances installed by setProxy() so the redirect path // can strip them without clobbering a user-supplied agent that happens to be // an HttpsProxyAgent. const kAxiosInstalledTunnel = Symbol('axios.http.installedTunnel'); // Cache of CONNECT-tunneling agents keyed by proxy config so repeat requests // through the same proxy reuse a single agent (and its socket pool). The // keyspace is bounded by the set of distinct proxy configs the process uses, // so unbounded growth is not a concern in practice. const tunnelingAgentCache = new Map(); const tunnelingAgentCacheUser = new WeakMap(); const directAgents = new WeakMap(); const directAgentHooks = ['createConnection', 'getName']; const nativeAgentPoolHooks = [ 'addRequest', 'createSocket', 'removeSocket', 'keepSocketAlive', 'reuseSocket', '_getSession', '_cacheSession', '_evictSession', ]; // Minimum minor versions where Node's HTTP Agent supports native proxyEnv // handling. Checking the selected agent below also covers startup modes such // as NODE_OPTIONS=--use-env-proxy and --no-use-env-proxy precedence. const NODE_NATIVE_ENV_PROXY_SUPPORT = { 22: 21, 24: 5, }; function isNodeNativeEnvProxySupported(nodeVersion = process.versions && process.versions.node) { if (!nodeVersion) { return false; } const [major, minor] = nodeVersion.split('.').map((part) => Number(part)); if (!Number.isInteger(major) || !Number.isInteger(minor)) { return false; } if (major > 24) { return true; } return ( NODE_NATIVE_ENV_PROXY_SUPPORT[major] != null && minor >= NODE_NATIVE_ENV_PROXY_SUPPORT[major] ); } function isNodeEnvProxyEnabled(agent, nodeVersion = process.versions && process.versions.node) { if (!isNodeNativeEnvProxySupported(nodeVersion)) { return false; } const agentOptions = agent && agent.options; return Boolean( agentOptions && utils.hasOwnProp(agentOptions, 'proxyEnv') && agentOptions.proxyEnv != null ); } function getProxyEnvAgent(options, configHttpAgent, configHttpsAgent) { return isHttps.test(options.protocol) ? configHttpsAgent || https.globalAgent : configHttpAgent || http.globalAgent; } function getDirectAgent(configuredAgent, transport) { if ( !process.allowedNodeEnvironmentFlags || !process.allowedNodeEnvironmentFlags.has('--use-env-proxy') ) { return configuredAgent; } const agent = configuredAgent || transport.globalAgent; const options = agent && agent.options; if ( !(agent instanceof transport.Agent) || agent.protocol !== transport.globalAgent.protocol || !options || !utils.hasOwnProp(options, 'proxyEnv') || typeof options.proxyEnv !== 'object' || options.proxyEnv === null ) { return configuredAgent; } // These hooks own pool queues or TLS session state. Neither binding them to // the source agent nor copying them without subclass state preserves their // behavior on a separate direct pool. for (const hook of nativeAgentPoolHooks) { if ( utils.isFunction(transport.Agent.prototype[hook]) && agent[hook] !== transport.Agent.prototype[hook] ) { throw new AxiosError( 'proxy: false cannot adapt an agent overriding ' + hook + '; use an agent constructed without proxyEnv', AxiosError.ERR_BAD_OPTION_VALUE ); } } // Node snapshots proxyEnv when constructing the agent. Keep this check // conservative even when the caller later removes protocol-specific entries. let direct = directAgents.get(agent); if (!direct) { direct = new transport.Agent({ ...options, proxyEnv: undefined }); // Agent.destroy() does not emit an event. Keep the associated pool under // the configured agent's lifetime, including agents reused after destroy(). const destroy = agent.destroy; agent.destroy = function destroyWithDirectAgent() { try { return destroy.apply(this, arguments); } finally { direct.destroy(); } }; directAgents.set(agent, direct); } // Connections and pool keys do not own the direct pool's queues. Preserve // their configured receiver and refresh hooks that change between requests. for (const hook of directAgentHooks) { direct[hook] = agent[hook] === transport.Agent.prototype[hook] ? transport.Agent.prototype[hook] : bind(agent[hook], agent); } return direct; } function setDirectAgent(options, configHttpAgent, configHttpsAgent) { const secure = isHttps.test(options.protocol); const agent = getDirectAgent(secure ? configHttpsAgent : configHttpAgent, secure ? https : http); options.agent = agent; if (options.agents) { options.agents[secure ? 'https' : 'http'] = agent; } } function getTunnelingAgent(agentOptions, userHttpsAgent) { const key = agentOptions.protocol + '//' + agentOptions.hostname + ':' + (agentOptions.port || '') + '#' + (agentOptions.auth || ''); const cache = userHttpsAgent ? tunnelingAgentCacheUser.get(userHttpsAgent) || tunnelingAgentCacheUser.set(userHttpsAgent, new Map()).get(userHttpsAgent) : tunnelingAgentCache; let agent = cache.get(key); if (agent) return agent; // Preserve per-request TLS options for the proxy connection, with our proxy // fields taking precedence. Global-agent options belong only to the origin // TLS upgrade in the callback below. const merged = userHttpsAgent && userHttpsAgent.options ? { ...userHttpsAgent.options, ...agentOptions } : agentOptions; agent = new HttpsProxyAgent(merged); const callback = agent.callback; agent.callback = function axiosTunnelingAgentCallback(req, opts) { // Without a proxy the origin TLS connection would be made by https.globalAgent, // so its options have to reach the tunneled origin too. Resolved per request // because the tunneling agent outlives any one of them. const originTLSAgent = userHttpsAgent || https.globalAgent; const originTLSOptions = originTLSAgent && originTLSAgent.options; // HttpsProxyAgent v5 reads callback opts for the post-CONNECT origin TLS upgrade. return callback.call(this, req, { ...originTLSOptions, ...opts }); }; agent[kAxiosInstalledTunnel] = true; cache.set(key, agent); return agent; } const supportedProtocols = platform.protocols.map((protocol) => { return protocol + ':'; }); // Node's WHATWG URL parser returns `username` and `password` percent-encoded. // Decode before composing the `auth` option so credentials such as // `my%40email.com:pass` are sent as `my@email.com:pass`. Falls back to the // original value for malformed input so a bad encoding never throws. const decodeURIComponentSafe = (value) => { if (!utils.isString(value)) { return value; } try { return decodeURIComponent(value); } catch (error) { return value; } }; const flushOnFinish = (stream, [throttled, flush]) => { stream.on('end', flush).on('error', flush); return throttled; }; const http2Sessions = new Http2Sessions(); /** * If the proxy, auth, sensitive header, or config beforeRedirects functions are defined, * call them with the options object. * * @param {Object} options - The options object that was passed to the request. * * @returns {Object} */ function dispatchBeforeRedirect(options, responseDetails, requestDetails) { if (!isSameOriginRedirect(options, requestDetails)) { stripMatchingHeaders(options.headers, new Set(['authorization', 'cookie'])); delete options.auth; } if (options.beforeRedirects.proxy) { options.beforeRedirects.proxy(options); } if (options.beforeRedirects.auth) { options.beforeRedirects.auth(options); } if (options.beforeRedirects.sensitiveHeaders) { options.beforeRedirects.sensitiveHeaders(options, requestDetails); } if (options.beforeRedirects.direct) { options.beforeRedirects.direct(options); } if (options.beforeRedirects.config) { options.beforeRedirects.config(options, responseDetails, requestDetails); } } function stripMatchingHeaders(headers, sensitiveSet) { if (!headers) { return; } Object.keys(headers).forEach((header) => { if (sensitiveSet.has(header.toLowerCase())) { delete headers[header]; } }); } function isSameOriginRedirect(redirectOptions, requestDetails) { if (!requestDetails) { return false; } try { return new URL(requestDetails.url).origin === new URL(redirectOptions.href).origin; } catch (e) { // If origin comparison fails, treat the redirect as unsafe. return false; } } /** * If the proxy or config afterRedirects functions are defined, call them with the options * * @param {http.ClientRequestArgs} options * @param {AxiosProxyConfig} configProxy configuration from Axios options object * @param {string} location * @param {boolean} [allowEnvProxy=true] whether environment proxy configuration can be used * * @returns {boolean} whether a proxy applies to the selected transport */ function setProxy( options, configProxy, location, isRedirect, configHttpsAgent, configHttpAgent, allowEnvProxy = true ) { let proxy = configProxy; const proxyEnvAgent = getProxyEnvAgent(options, configHttpAgent, configHttpsAgent); if (!proxy && proxy !== false && allowEnvProxy && !isNodeEnvProxyEnabled(proxyEnvAgent)) { const proxyUrl = getProxyForUrl(location); if (proxyUrl) { if (!shouldBypassProxy(location)) { proxy = new URL(proxyUrl); } } } // On redirect re-invocation, strip any stale Proxy-Authorization header carried // over from the prior request (e.g. new target no longer uses a proxy, or uses // a different proxy). Skip on the initial request so user-supplied headers are // preserved. Header names are case-insensitive, so remove every case variant. if (isRedirect && options.headers) { for (const name of Object.keys(options.headers)) { if (name.toLowerCase() === 'proxy-authorization') { delete options.headers[name]; } } } // Strip any tunneling agent we installed for the previous hop so a redirect // that drops the proxy or crosses an HTTPS↔HTTP boundary doesn't reuse a // stale one. Match on our Symbol marker so a user-supplied HttpsProxyAgent // (which won't carry the marker) is left alone. if (isRedirect && options.agent && options.agent[kAxiosInstalledTunnel]) { options.agent = undefined; } if ( isRedirect && options.agents && options.agents.https && options.agents.https[kAxiosInstalledTunnel] ) { options.agents.https = configHttpsAgent; } if (proxy) { // Read proxy fields without traversing the prototype chain. URL instances expose // username/password/hostname/host/port/protocol via getters on URL.prototype (so // direct reads are shielded), but plain object proxies — and the `auth` field // (which URL does not expose) — must be guarded so a polluted Object.prototype // (e.g. Object.prototype.auth = { username, password }) cannot inject // attacker-controlled credentials into the Proxy-Authorization header or // redirect proxying to an attacker-controlled host. const isProxyURL = proxy instanceof URL; const readProxyField = (key) => isProxyURL || utils.hasOwnProp(proxy, key) ? proxy[key] : undefined; const proxyUsername = readProxyField('username'); const proxyPassword = readProxyField('password'); let proxyAuth = utils.hasOwnProp(proxy, 'auth') ? proxy.auth : undefined; // Basic proxy authorization if (proxyUsername) { proxyAuth = (proxyUsername || '') + ':' + (proxyPassword || ''); } if (proxyAuth) { // Support proxy auth object form. Read sub-fields via own-prop checks so a // plain object inheriting from polluted Object.prototype cannot leak creds. const authIsObject = typeof proxyAuth === 'object'; const authUsername = authIsObject && utils.hasOwnProp(proxyAuth, 'username') ? proxyAuth.username : undefined; const authPassword = authIsObject && utils.hasOwnProp(proxyAuth, 'password') ? proxyAuth.password : undefined; const validProxyAuth = Boolean(authUsername || authPassword); if (validProxyAuth) { proxyAuth = (authUsername || '') + ':' + (authPassword || ''); } else if (authIsObject) { throw new AxiosError('Invalid proxy authorization', AxiosError.ERR_BAD_OPTION, { proxy }); } } const targetIsHttps = isHttps.test(options.protocol); if (targetIsHttps) { // CONNECT-tunneling path for HTTPS targets. Preserves end-to-end TLS to // the origin so the proxy cannot inspect the URL, headers, or body — the // behavior already promised by THREATMODEL.md (T-R9). HttpsProxyAgent // sends Proxy-Authorization on the CONNECT request only, never on the // wrapped TLS request, which is why we don't stamp it onto // options.headers here. If the user already supplied an HttpsProxyAgent, // they own tunneling end-to-end and we leave them alone; otherwise we // install our own tunneling agent and forward their TLS options (if any) // so a custom httpsAgent for cert pinning / rejectUnauthorized still // applies to the origin TLS upgrade. if (!(configHttpsAgent instanceof HttpsProxyAgent)) { const proxyHost = readProxyField('hostname') || readProxyField('host'); const proxyPort = readProxyField('port'); const rawProxyProtocol = readProxyField('protocol'); const normalizedProtocol = rawProxyProtocol ? rawProxyProtocol.includes(':') ? rawProxyProtocol : `${rawProxyProtocol}:` : 'http:'; // Bracket IPv6 literals for URL parsing; URL.hostname strips the // brackets again on read so the agent receives the raw form. const proxyHostForURL = proxyHost && proxyHost.includes(':') && !proxyHost.startsWith('[') ? `[${proxyHost}]` : proxyHost; const proxyURL = new URL( `${normalizedProtocol}//${proxyHostForURL}${proxyPort ? ':' + proxyPort : ''}` ); const agentOptions = { protocol: proxyURL.protocol, hostname: proxyURL.hostname.replace(/^\[|\]$/g, ''), port: proxyURL.port, auth: proxyAuth && typeof proxyAuth === 'string' ? proxyAuth : undefined, }; if (proxyURL.protocol === 'https:') { agentOptions.ALPNProtocols = ['http/1.1']; } const tunnelingAgent = getTunnelingAgent(agentOptions, configHttpsAgent); // Set both: `options.agent` is consumed by the native https.request path // (maxRedirects === 0); `options.agents.https` is consumed by // follow-redirects, which ignores `options.agent` when `options.agents` // is present. options.agent = tunnelingAgent; if (options.agents) { options.agents.https = tunnelingAgent; } } } else { // Forward-proxy mode for plaintext HTTP targets. The request line carries // the absolute URL and the proxy sees everything — acceptable for plain // HTTP since the wire was already plaintext. if (proxyAuth) { const base64 = Buffer.from(proxyAuth, 'utf8').toString('base64'); options.headers['Proxy-Authorization'] = 'Basic ' + base64; } // Preserve a user-supplied Host header (case-insensitive) so callers can override // the value forwarded to the proxy; otherwise default to the request URL's host. let hasUserHostHeader = false; for (const name of Object.keys(options.headers)) { if (name.toLowerCase() === 'host') { hasUserHostHeader = true; break; } } if (!hasUserHostHeader) { options.headers.host = options.hostname + (options.port ? ':' + options.port : ''); } const proxyHost = readProxyField('hostname') || readProxyField('host'); options.hostname = proxyHost; // Replace 'host' since options is not a URL object options.host = proxyHost; options.port = readProxyField('port'); options.path = location; const proxyProtocol = readProxyField('protocol'); if (proxyProtocol) { options.protocol = proxyProtocol.includes(':') ? proxyProtocol : `${proxyProtocol}:`; } } } options.beforeRedirects.proxy = function beforeRedirect(redirectOptions) { // Configure proxy for redirected request, passing the original config proxy to apply // the exact same logic as if the redirected request was performed by axios directly. setProxy( redirectOptions, configProxy, redirectOptions.href, true, configHttpsAgent, configHttpAgent, allowEnvProxy ); }; return Boolean( proxy || (configProxy !== false && allowEnvProxy && isNodeEnvProxyEnabled(proxyEnvAgent)) ); } const isHttpAdapterSupported = typeof process !== 'undefined' && utils.kindOf(process) === 'process'; // temporary hotfix const wrapAsync = (asyncExecutor) => { return new Promise((resolve, reject) => { let onDone; let isDone; const done = (value, isRejected) => { if (isDone) return; isDone = true; onDone && onDone(value, isRejected); }; const _resolve = (value) => { done(value); resolve(value); }; const _reject = (reason) => { done(reason, true); reject(reason); }; asyncExecutor(_resolve, _reject, (onDoneHandler) => (onDone = onDoneHandler)).catch(_reject); }); }; const resolveFamily = ({ address, family }) => { if (!utils.isString(address)) { throw new AxiosError('address must be a string', AxiosError.ERR_BAD_OPTION_VALUE); } return { address, family: family || (address.indexOf('.') < 0 ? 6 : 4), }; }; const buildAddressEntry = (address, family) => resolveFamily(utils.isObject(address) ? address : { address, family }); const normalizedLookupCache = new WeakMap(); const normalizeLookup = (lookup) => { let normalized = normalizedLookupCache.get(lookup); if (normalized) { return normalized; } const callbackLookup = callbackify(lookup, (value) => (utils.isArray(value) ? value : [value])); // Support opt.all, which is required by current Node.js releases. normalized = (hostname, opt, cb) => { callbackLookup(hostname, opt, (err, arg0, arg1) => { if (err) { return cb(err); } let addresses; try { addresses = utils.isArray(arg0) ? arg0.map((addr) => buildAddressEntry(addr)) : [buildAddressEntry(arg0, arg1)]; } catch (error) { return cb(error); } opt.all ? cb(err, addresses) : cb(err, addresses[0].address, addresses[0].family); }); }; normalizedLookupCache.set(lookup, normalized); return normalized; }; const http2Transport = { request(options, cb) { const authority = options.protocol + '//' + options.hostname + ':' + (options.port || (options.protocol === 'https:' ? 443 : 80)); const { http2Options, headers } = options; const session = http2Sessions.getSession(authority, http2Options); const { HTTP2_HEADER_SCHEME, HTTP2_HEADER_METHOD, HTTP2_HEADER_PATH, HTTP2_HEADER_STATUS } = http2.constants; const http2Headers = { [HTTP2_HEADER_SCHEME]: options.protocol.replace(':', ''), [HTTP2_HEADER_METHOD]: options.method, [HTTP2_HEADER_PATH]: options.path, }; utils.forEach(headers, (header, name) => { name.charAt(0) !== ':' && (http2Headers[name] = header); }); const req = session.request(http2Headers); req.once('response', (responseHeaders) => { const response = req; //duplex responseHeaders = Object.assign({}, responseHeaders); const status = responseHeaders[HTTP2_HEADER_STATUS]; delete responseHeaders[HTTP2_HEADER_STATUS]; response.headers = responseHeaders; response.statusCode = +status; cb(response); }); return req; }, }; /*eslint consistent-return:0*/ export default isHttpAdapterSupported && function httpAdapter(config) { return wrapAsync(async function dispatchHttpRequest(resolve, reject, onDone) { // Read config pollution-safely: own properties and members inherited from // a non-Object.prototype source (e.g. an Object.create(defaults) template) // are honored, but values injected onto a polluted Object.prototype are // ignored. All behavior-affecting reads in this adapter go through own() // so the protection boundary stays consistent. const own = (key) => utils.getSafeProp(config, key); const transitional = own('transitional') || transitionalDefaults; let data = own('data'); let lookup = own('lookup'); let family = own('family'); let httpVersion = own('httpVersion'); if (httpVersion === undefined) httpVersion = 1; const rawHttpVersion = httpVersion; let http2Options = own('http2Options'); const configProxy = own('proxy'); const httpAgent = own('httpAgent'); const httpsAgent = own('httpsAgent'); const responseType = own('responseType'); const responseEncoding = own('responseEncoding'); const socketPath = own('socketPath'); const method = own('method').toUpperCase(); const maxRedirects = own('maxRedirects'); const maxBodyLength = own('maxBodyLength'); const maxContentLength = own('maxContentLength'); const decompress = own('decompress'); let isDone; let rejected = false; let req; let connectPhaseTimer; try { httpVersion = +httpVersion; } catch (err) { throw new AxiosError( 'Invalid protocol version: value is not a number', AxiosError.ERR_BAD_OPTION_VALUE, config ); } if (Number.isNaN(httpVersion)) { throw new AxiosError( `Invalid protocol version: '${rawHttpVersion}' is not a number`, AxiosError.ERR_BAD_OPTION_VALUE, config ); } if (httpVersion !== 1 && httpVersion !== 2) { throw new AxiosError( `Unsupported protocol version '${httpVersion}'`, AxiosError.ERR_BAD_OPTION_VALUE, config ); } const isHttp2 = httpVersion === 2; if (lookup) { lookup = normalizeLookup(lookup); } const abortEmitter = new EventEmitter(); function abort(reason) { try { abortEmitter.emit( 'abort', !reason || reason.type ? new CanceledError(null, config, req) : reason ); } catch (err) { // ignore emit errors } } function clearConnectPhaseTimer() { if (connectPhaseTimer) { clearTimeout(connectPhaseTimer); connectPhaseTimer = null; } } function createTimeoutError() { const configTimeout = own('timeout'); let timeoutErrorMessage = configTimeout ? 'timeout of ' + configTimeout + 'ms exceeded' : 'timeout exceeded'; const configTimeoutErrorMessage = own('timeoutErrorMessage'); if (configTimeoutErrorMessage) { timeoutErrorMessage = configTimeoutErrorMessage; } return new AxiosError( timeoutErrorMessage, transitional.clarifyTimeoutError ? AxiosError.ETIMEDOUT : AxiosError.ECONNABORTED, config, req ); } abortEmitter.once('abort', reject); const onFinished = () => { clearConnectPhaseTimer(); if (config.cancelToken) { config.cancelToken.unsubscribe(abort); } if (config.signal) { config.signal.removeEventListener('abort', abort); } abortEmitter.removeAllListeners(); }; if (config.cancelToken || config.signal) { config.cancelToken && config.cancelToken.subscribe(abort); if (config.signal) { config.signal.aborted ? abort() : config.signal.addEventListener('abort', abort); } } onDone((response, isRejected) => { isDone = true; clearConnectPhaseTimer(); if (isRejected) { rejected = true; onFinished(); return; } const { data } = response; if (data instanceof stream.Readable || data instanceof stream.Duplex) { const offListeners = stream.finished(data, () => { offListeners(); onFinished(); }); } else { onFinished(); } }); // Parse url const fullPath = buildFullPath(own('baseURL'), own('url'), own('allowAbsoluteUrls'), config); // Unix-socket requests (own socketPath) commonly pass a path-only url // like '/foo'; supply a synthetic base so new URL() can still parse it. // Use the own-property value (not config.socketPath) so a polluted // prototype cannot influence URL base selection. const urlBase = socketPath ? 'http://localhost' : platform.hasBrowserEnv ? platform.origin : undefined; const parsed = new URL(fullPath, urlBase); const protocol = parsed.protocol || supportedProtocols[0]; if (protocol === 'data:') { // Apply the same semantics as HTTP: only enforce if a finite, non-negative cap is set. if (maxContentLength > -1) { // Use the exact string passed to fromDataURI (the configured url); fall back to fullPath if needed. const dataUrl = String(own('url') || fullPath || ''); const estimated = estimateDataURLBufferAllocation(dataUrl); if (estimated > maxContentLength) { return reject( new AxiosError( 'maxContentLength size of ' + maxContentLength + ' exceeded', AxiosError.ERR_BAD_RESPONSE, config ) ); } } let convertedData; if (method !== 'GET') { return settle(resolve, reject, { status: 405, statusText: 'method not allowed', headers: {}, config, }); } try { convertedData = fromDataURI(own('url'), responseType === 'blob', { Blob: config.env && config.env.Blob, }); } catch (err) { throw AxiosError.from(err, AxiosError.ERR_BAD_REQUEST, config); } // Check bytes before converting the result to text or a stream. if ( maxContentLength > -1 && (responseType === 'blob' ? convertedData.size : convertedData.length) > maxContentLength ) { return reject( new AxiosError( 'maxContentLength size of ' + maxContentLength + ' exceeded', AxiosError.ERR_BAD_RESPONSE, config ) ); } if (responseType === 'text') { convertedData = convertedData.toString(responseEncoding); if (!responseEncoding || responseEncoding === 'utf8') { convertedData = utils.stripBOM(convertedData); } } else if (responseType === 'stream') { convertedData = stream.Readable.from(convertedData); } return settle(resolve, reject, { data: convertedData, status: 200, statusText: 'OK', headers: new AxiosHeaders(), config, }); } if (supportedProtocols.indexOf(protocol) === -1) { return reject( new AxiosError('Unsupported protocol ' + protocol, AxiosError.ERR_BAD_REQUEST, config) ); } const headers = AxiosHeaders.from(config.headers).normalize(); // Set User-Agent (required by some servers) // See https://github.com/axios/axios/issues/69 // User-Agent is specified; handle case where no UA header is desired // Only set header if it hasn't been set in config headers.set('User-Agent', 'axios/' + VERSION, false); const { onUploadProgress, onDownloadProgress } = config; const maxRate = config.maxRate; let maxUploadRate = undefined; let maxDownloadRate = undefined; // support for spec compliant FormData objects if (utils.isSpecCompliantForm(data)) { const userBoundary = headers.getContentType(/boundary=([-_\w\d]{10,70})/i); data = formDataToStream( data, (formHeaders) => { headers.set(formHeaders); }, { tag: `axios-${VERSION}-boundary`, boundary: (userBoundary && userBoundary[1]) || undefined, } ); // support for https://www.npmjs.com/package/form-data api } else if ( utils.isFormData(data) && utils.isFunction(data.getHeaders) && data.getHeaders !== Object.prototype.getHeaders ) { setFormDataHeaders(headers, data.getHeaders(), own('formDataHeaderPolicy')); if (!headers.hasContentLength()) { try { const knownLength = await util.promisify(data.getLength).call(data); Number.isFinite(knownLength) && knownLength >= 0 && headers.setContentLength(knownLength); /*eslint no-empty:0*/ } catch (e) {} } } else if (utils.isBlob(data) || utils.isFile(data)) { data.size && headers.setContentType(data.type || 'application/octet-stream'); headers.setContentLength(data.size || 0); data = stream.Readable.from(readBlob(data)); } else if (data && !utils.isStream(data)) { if (Buffer.isBuffer(data)) { // Nothing to do... } else if (utils.isArrayBuffer(data)) { data = Buffer.from(new Uint8Array(data)); } else if (utils.isString(data)) { data = Buffer.from(data, 'utf-8'); } else { return reject( new AxiosError( 'Data after transformation must be a string, an ArrayBuffer, a Buffer, or a Stream', AxiosError.ERR_BAD_REQUEST, config ) ); } // Add Content-Length header if data exists headers.setContentLength(data.length, false); if (maxBodyLength > -1 && data.length > maxBodyLength) { return reject( new AxiosError( 'Request body larger than maxBodyLength limit', AxiosError.ERR_BAD_REQUEST, config ) ); } } const contentLength = utils.toFiniteNumber(headers.getContentLength()); if (utils.isArray(maxRate)) { maxUploadRate = maxRate[0]; maxDownloadRate = maxRate[1]; } else { maxUploadRate = maxDownloadRate = maxRate; } if (data && (onUploadProgress || maxUploadRate)) { if (!utils.isStream(data)) { data = stream.Readable.from(data, { objectMode: false }); } data = stream.pipeline( [ data, new AxiosTransformStream({ maxRate: utils.toFiniteNumber(maxUploadRate), }), ], utils.noop ); onUploadProgress && data.on( 'progress', flushOnFinish( data, progressEventDecorator( contentLength, progressEventReducer(asyncDecorator(onUploadProgress, scheduleProgress), false, 3) ) ) ); } // HTTP basic authentication let auth = undefined; const configAuth = own('auth'); if (configAuth) { const username = utils.getSafeProp(configAuth, 'username') || ''; const password = utils.getSafeProp(configAuth, 'password') || ''; auth = username + ':' + password; } if (!auth && (parsed.username || parsed.password)) { const urlUsername = decodeURIComponentSafe(parsed.username); const urlPassword = decodeURIComponentSafe(parsed.password); auth = urlUsername + ':' + urlPassword; } auth && headers.delete('authorization'); let path; try { path = buildURL( parsed.pathname + parsed.search, own('params'), own('paramsSerializer') ).replace(/^\?/, ''); } catch (err) { return reject( AxiosError.from(err, AxiosError.ERR_BAD_REQUEST, config, null, null, { url: own('url'), exists: true, }) ); } headers.set( 'Accept-Encoding', utils.hasOwnProp(transitional, 'advertiseZstdAcceptEncoding') && transitional.advertiseZstdAcceptEncoding === true ? ACCEPT_ENCODING_WITH_ZSTD : ACCEPT_ENCODING, false ); if (isHttp2 && lookup) { http2Options = Object.assign(Object.create(null), http2Options, { lookup }); } // Null-prototype to block prototype pollution gadgets on properties read // directly by Node's http.request (e.g. insecureHTTPParser, lookup). const options = Object.assign(Object.create(null), { path, method: method, headers: toByteStringHeaderObject(headers), agents: { http: httpAgent, https: httpsAgent }, auth, protocol, family, beforeRedirect: dispatchBeforeRedirect, beforeRedirects: Object.create(null), http2Options, createConnection: undefined, }); // cacheable-lookup integration hotfix !utils.isUndefined(lookup) && (options.lookup = lookup); let proxyApplied = false; if (socketPath) { if (typeof socketPath !== 'string') { return reject( new AxiosError('socketPath must be a string', AxiosError.ERR_BAD_OPTION_VALUE, config) ); } const allowedSocketPaths = own('allowedSocketPaths'); if (allowedSocketPaths != null) { const allowed = Array.isArray(allowedSocketPaths) ? allowedSocketPaths : [allowedSocketPaths]; const resolvedSocket = resolvePath(socketPath); const isAllowed = allowed.some( (entry) => typeof entry === 'string' && resolvePath(entry) === resolvedSocket ); if (!isAllowed) { return reject( new AxiosError( `socketPath "${socketPath}" is not permitted by allowedSocketPaths`, AxiosError.ERR_BAD_OPTION_VALUE, config ) ); } } options.socketPath = socketPath; } else { options.hostname = parsed.hostname.startsWith('[') ? parsed.hostname.slice(1, -1) : parsed.hostname; options.port = parsed.port; proxyApplied = setProxy( options, configProxy, protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path, false, httpsAgent, httpAgent, // The HTTP/2 transport connects independently of HTTP/1 agents, so it // cannot apply either axios-resolved or agent-local environment proxies. // Explicit proxy config is still processed and rejected below. !isHttp2 ); } let transport; let isNativeTransport = false; const isHttpsRequest = isHttps.test(options.protocol); // Don't clobber a CONNECT-tunneling agent installed by setProxy() for an // HTTPS target. if (!isHttp2 && configProxy === false) { setDirectAgent(options, httpAgent, httpsAgent); options.beforeRedirects.direct = (redirectOptions) => { setDirectAgent(redirectOptions, httpAgent, httpsAgent); }; } else if (options.agent == null) { options.agent = isHttpsRequest ? httpsAgent : httpAgent; } if (isHttp2) { if (proxyApplied) { return reject( new AxiosError( 'HTTP/2 requests with a proxy are not supported', AxiosError.ERR_NOT_SUPPORT, config ) ); } transport = http2Transport; } else { const configTransport = own('transport'); if (configTransport) { transport = configTransport; } else if (maxRedirects === 0) { transport = isHttpsRequest ? https : http; isNativeTransport = true; } else { options.sensitiveHeaders = []; if (maxRedirects) { options.maxRedirects = maxRedirects; } const configBeforeRedirect = own('beforeRedirect'); if (configBeforeRedirect) { options.beforeRedirects.config = configBeforeRedirect; } if (auth) { // Restore HTTP Basic credentials on same-origin redirects only. // follow-redirects >= 1.15.8 strips Authorization on every redirect (see #6929); // cross-origin stripping is the documented mitigation for T-R2 in THREATMODEL.md // and is preserved by deliberately not restoring on origin change. const requestOrigin = parsed.origin; const authToRestore = auth; options.beforeRedirects.auth = function beforeRedirectAuth(redirectOptions) { try { if (new URL(redirectOptions.href).origin === requestOrigin) { redirectOptions.auth = authToRestore; } } catch (e) { // ignore malformed URL: leaving auth stripped is fail-safe } }; } const sensitiveHeaders = own('sensitiveHeaders'); if (sensitiveHeaders != null) { if (!utils.isArray(sensitiveHeaders)) { return reject( new AxiosError( 'sensitiveHeaders must be an array of strings', AxiosError.ERR_BAD_OPTION_VALUE, config ) ); } const sensitiveSet = new Set(); for (const header of sensitiveHeaders) { if (!utils.isString(header)) { return reject( new AxiosError( 'sensitiveHeaders must be an array of strings', AxiosError.ERR_BAD_OPTION_VALUE, config ) ); } sensitiveSet.add(header.toLowerCase()); } if (sensitiveSet.size) { options.sensitiveHeaders = Array.from(sensitiveSet); options.beforeRedirects.sensitiveHeaders = function beforeRedirectSensitiveHeaders( redirectOptions, requestDetails ) { if (!isSameOriginRedirect(redirectOptions, requestDetails)) { stripMatchingHeaders(redirectOptions.headers, sensitiveSet); } }; } } transport = isHttpsRequest ? httpsFollow : httpFollow; } } // Set an explicit maxBodyLength option for transports that inspect it. // When maxBodyLength is -1 (default/unlimited), use Infinity so // follow-redirects does not fall back to its own 10MB default. if (maxBodyLength > -1) { options.maxBodyLength = maxBodyLength; } else { options.maxBodyLength = Infinity; } // Always set an explicit own value so a polluted // Object.prototype.insecureHTTPParser cannot enable the lenient parser // through Node's internal options copy options.insecureHTTPParser = Boolean(own('insecureHTTPParser')); // Create the request req = transport.request(options, function handleResponse(res) { clearConnectPhaseTimer(); if (req.destroyed) return; const streams = [res]; const responseLength = utils.toFiniteNumber(res.headers['content-length']); if (onDownloadProgress || maxDownloadRate) { const transformStream = new AxiosTransformStream({ maxRate: utils.toFiniteNumber(maxDownloadRate), }); onDownloadProgress && transformStream.on( 'progress', flushOnFinish( transformStream, progressEventDecorator( responseLength, progressEventReducer( asyncDecorator(onDownloadProgress, scheduleProgress), true, 3 ) ) ) ); streams.push(transformStream); } // decompress the response body transparently if required let responseStream = res; // return the last request in case of redirects const lastRequest = res.req || req; // if decompress disabled we should not decompress if (decompress !== false && res.headers['content-encoding']) { // if no content, but headers still say that it is encoded, // remove the header not confuse downstream operations if (method === 'HEAD' || res.statusCode === 204) { delete res.headers['content-encoding']; } switch ((res.headers['content-encoding'] || '').toLowerCase()) { /*eslint default-case:0*/ case 'gzip': case 'x-gzip': case 'compress': case 'x-compress': // add the unzipper to the body stream processing pipeline streams.push(zlib.createUnzip(zlibOptions)); // remove the content-encoding in order to not confuse downstream operations delete res.headers['content-encoding']; break; case 'deflate': streams.push(new ZlibHeaderTransformStream()); // add the unzipper to the body stream processing pipeline streams.push(zlib.createUnzip(zlibOptions)); // remove the content-encoding in order to not confuse downstream operations delete res.headers['content-encoding']; break; case 'br': if (isBrotliSupported) { streams.push(zlib.createBrotliDecompress(brotliOptions)); delete res.headers['content-encoding']; } break; case 'zstd': if (isZstdSupported) { streams.push(zlib.createZstdDecompress(zstdOptions)); delete res.headers['content-encoding']; } break; } } responseStream = streams.length > 1 ? stream.pipeline(streams, utils.noop) : streams[0]; const response = { status: res.statusCode, statusText: res.statusMessage, headers: new AxiosHeaders(res.headers), config, request: lastRequest, }; if (responseType === 'stream') { // Enforce maxContentLength on streamed responses; previously this // was applied only to buffered responses. if (maxContentLength > -1) { const limit = maxContentLength; const source = responseStream; async function* enforceMaxContentLength() { let totalResponseBytes = 0; for await (const chunk of source) { totalResponseBytes += chunk.length; if (totalResponseBytes > limit) { throw new AxiosError( 'maxContentLength size of ' + limit + ' exceeded', AxiosError.ERR_BAD_RESPONSE, config, lastRequest ); } yield chunk; } } responseStream = stream.Readable.from(enforceMaxContentLength(), { objectMode: false, }); } response.data = responseStream; settle(resolve, reject, response); } else { const responseBuffer = []; let totalResponseBytes = 0; responseStream.on('data', function handleStreamData(chunk) { responseBuffer.push(chunk); totalResponseBytes += chunk.length; // make sure the content length is not over the maxContentLength if specified if (maxContentLength > -1 && totalResponseBytes > maxContentLength) { // stream.destroy() emit aborted event before calling reject() on Node.js v16 rejected = true; responseStream.destroy(); abort( new AxiosError( 'maxContentLength size of ' + maxContentLength + ' exceeded', AxiosError.ERR_BAD_RESPONSE, config, lastRequest ) ); } }); responseStream.on('aborted', function handlerStreamAborted() { if (rejected) { return; } const err = new AxiosError( 'stream has been aborted', AxiosError.ERR_BAD_RESPONSE, config, lastRequest, response ); responseStream.destroy(err); reject(err); }); responseStream.on('error', function handleStreamError(err) { if (rejected) return; reject(AxiosError.from(err, null, config, lastRequest, response)); }); responseStream.on('end', function handleStreamEnd() { try { let responseData = responseBuffer.length === 1 ? responseBuffer[0] : Buffer.concat(responseBuffer); if (responseType !== 'arraybuffer') { responseData = responseData.toString(responseEncoding); if (!responseEncoding || responseEncoding === 'utf8') { responseData = utils.stripBOM(responseData); } } response.data = responseData; } catch (err) { return reject(AxiosError.from(err, null, config, response.request, response)); } settle(resolve, reject, response); }); } abortEmitter.once('abort', (err) => { if (!responseStream.destroyed) { responseStream.emit('error', err); responseStream.destroy(); } }); }); abortEmitter.once('abort', (err) => { if (req.close) { req.close(); } else { req.destroy(err); } }); // Handle errors req.on('error', function handleRequestError(err) { reject(AxiosError.from(err, null, config, req)); }); // set tcp keep alive to prevent drop connection by peer // Track every socket bound to this outer RedirectableRequest so a single // 'close' listener can release ownership on all of them. follow-redirects // re-emits the 'socket' event for each hop's native request onto the same // outer request, so attaching per-request listeners inside this handler // would accumulate across hops and trigger MaxListenersExceededWarning at // >= 11 redirects. Clearing only the last-bound socket would leave stale // kAxiosCurrentReq refs on earlier hop sockets returned to the keep-alive // pool, causing an idle-pool 'error' to be attributed to a closed req. const boundSockets = new Set(); req.on('socket', function handleRequestSocket(socket) { // default interval of sending ack packet is 1 minute // proxy agents (e.g. agent-base) may return a generic Duplex stream // that doesn't have setKeepAlive, so guard before calling if (typeof socket.setKeepAlive === 'function') { socket.setKeepAlive(true, 1000 * 60); } // Install one shared 'error' listener per socket. The symbol follows the // currently-active request as pooled sockets are reassigned (issue #10780). if (!socket[kAxiosSocketListener]) { socket.on('error', handleSocketError); socket[kAxiosSocketListener] = true; } socket[kAxiosCurrentReq] = req; boundSockets.add(socket); }); req.once('close', function clearCurrentReq() { clearConnectPhaseTimer(); for (const socket of boundSockets) { if (socket[kAxiosCurrentReq] === req) { socket[kAxiosCurrentReq] = null; } } boundSockets.clear(); }); // Handle request timeout if (own('timeout')) { // This is forcing a int timeout to avoid problems if the `req` interface doesn't handle other types. const timeout = parseInt(own('timeout'), 10); if (Number.isNaN(timeout)) { abort( new AxiosError( 'error trying to parse `config.timeout` to int', AxiosError.ERR_BAD_OPTION_VALUE, config, req ) ); return; } const handleTimeout = function handleTimeout() { if (isDone) return; abort(createTimeoutError()); }; if (isNativeTransport && timeout > 0) { // Native ClientRequest#setTimeout starts from the socket lifecycle and // may not fire while TCP connect is still pending. Mirror the // follow-redirects wall-clock timer for the maxRedirects === 0 path. connectPhaseTimer = setTimeout(handleTimeout, timeout); } // Sometime, the response will be very slow, and does not respond, the connect event will be block by event loop system. // And timer callback will be fired, and abort() will be invoked before connection, then get "socket hang up" and code ECONNRESET. // At this time, if we have a large number of request, nodejs will hang up some socket on background. and the number will up and up. // And then these socket which be hang up will devouring CPU little by little. // ClientRequest.setTimeout will be fired on the specify milliseconds, and can make sure that abort() will be fired after connect. req.setTimeout(timeout, handleTimeout); } else { // explicitly reset the socket timeout value for a possible `keep-alive` request req.setTimeout(0); } // Send the request if (utils.isStream(data)) { let ended = false; let errored = false; data.on('end', () => { ended = true; }); data.once('error', (err) => { errored = true; req.destroy(err); }); data.on('close', () => { if (!ended && !errored) { abort(new CanceledError('Request stream has been aborted', config, req)); } }); // Transform decodes string chunks to Buffers before counting, so every // transport receives at most maxBodyLength encoded bytes. let uploadStream = data; if (maxBodyLength > -1) { const limit = maxBodyLength; let bytesSent = 0; uploadStream = stream.pipeline( [ data, new stream.Transform({ transform(chunk, _enc, cb) { bytesSent += chunk.length; if (bytesSent > limit) { return cb( new AxiosError( 'Request body larger than maxBodyLength limit', AxiosError.ERR_BAD_REQUEST, config, req ) ); } cb(null, chunk); }, }), ], utils.noop ); uploadStream.on('error', (err) => { if (!req.destroyed) req.destroy(err); }); } uploadStream.pipe(req); } else { data && req.write(data); req.end(); } }); }; export const __setProxy = setProxy; export const __getDirectAgent = getDirectAgent; export const __isNodeNativeEnvProxySupported = isNodeNativeEnvProxySupported; export const __isNodeEnvProxyEnabled = isNodeEnvProxyEnabled; export const __isSameOriginRedirect = isSameOriginRedirect; axios-axios-aa54a5e/lib/adapters/xhr.js000066400000000000000000000322451526660636600202030ustar00rootroot00000000000000import utils from '../utils.js'; import settle from '../core/settle.js'; import transitionalDefaults from '../defaults/transitional.js'; import AxiosError from '../core/AxiosError.js'; import CanceledError from '../cancel/CanceledError.js'; import normalizeURLForProtocolCheck from '../helpers/normalizeURLForProtocolCheck.js'; import parseProtocol from '../helpers/parseProtocol.js'; import platform from '../platform/index.js'; import AxiosHeaders from '../core/AxiosHeaders.js'; import cookies from '../helpers/cookies.js'; import { progressEventReducer } from '../helpers/progressEventReducer.js'; import resolveConfig from '../helpers/resolveConfig.js'; import { toByteStringHeaderObject } from '../helpers/sanitizeHeaderValue.js'; const isXHRAdapterSupported = typeof XMLHttpRequest !== 'undefined'; export default isXHRAdapterSupported && function (config) { return new Promise(function dispatchXhrRequest(resolve, reject) { let useAsyncXsrf; try { const store = platform.hasStandardBrowserEnv && typeof window !== 'undefined' && window.cookieStore; useAsyncXsrf = store && utils.isFunction(store.get); } catch (e) { // A restricted Cookie Store getter must retain the synchronous fallback. } let xsrfCookieName; const _config = resolveConfig( config, useAsyncXsrf ? (name) => { xsrfCookieName = name; return null; } : undefined ); let requestData = _config.data; const requestHeaders = AxiosHeaders.from(_config.headers).normalize(); let { responseType, onUploadProgress, onDownloadProgress } = _config; let onCanceled; let timeoutId; let uploadThrottled, downloadThrottled; let flushUpload, flushDownload, flushDownloadWithEvent; function done() { clearTimeout(timeoutId); // A final progress callback must not prevent the remaining cleanup. // Report errors asynchronously, as onloadend and the throttle timer do. try { flushUpload && flushUpload(); // flush events } catch (err) { setTimeout(() => { throw err; }); } try { flushDownload && flushDownload(); // flush events } catch (err) { setTimeout(() => { throw err; }); } // A flush can synchronously cancel the request and reenter done(). // In that case the inner call has already detached these listeners. if (request) { downloadThrottled && request.removeEventListener('progress', downloadThrottled); if (request.upload) { uploadThrottled && request.upload.removeEventListener('progress', uploadThrottled); flushUpload && request.upload.removeEventListener('loadend', flushUpload); } } _config.cancelToken && _config.cancelToken.unsubscribe(onCanceled); _config.signal && _config.signal.removeEventListener('abort', onCanceled); } let request = new XMLHttpRequest(); request.open(_config.method.toUpperCase(), _config.url, true); // Set the request timeout in MS request.timeout = _config.timeout; function onloadend(event) { if (!request) { return; } // Status 0 means no response was received, which onerror and onabort normally // reject before this runs. Firefox 152 fires only readystatechange and loadend for // navigation-canceled requests (https://bugzilla.mozilla.org/show_bug.cgi?id=1505389), // leaving settle() to resolve them as an empty success. ECONNABORTED is the error // onabort raised on Firefox 151. Reads over file:, which some environments report as // status 0 on success, are excluded by the request URL's scheme after browser-style // preprocessing, by the page origin's scheme for relative URLs (which inherit it), or // by responseURL where implemented. if ( request.status === 0 && (parseProtocol(normalizeURLForProtocolCheck(_config.url)) || parseProtocol(platform.origin)) !== 'file' && !(request.responseURL && request.responseURL.startsWith('file:')) ) { reject(new AxiosError('Request aborted', AxiosError.ECONNABORTED, config, request)); done(); // Clean up request request = null; return; } // When loadend is still dispatching, flushing with it gives progress // listeners a final delivery whose event has a live target. The legacy // ready-state fallback has no event, so replay its pending progress. // A throwing listener must not block settlement; rethrow asynchronously, // matching how listener errors surface on the throttle timer path. try { if (event) { flushDownloadWithEvent && flushDownloadWithEvent(event); } else { flushDownload && flushDownload(); } } catch (err) { setTimeout(() => { throw err; }); } // A final progress callback can cancel the request synchronously. if (!request) { return; } // Prepare the response const responseHeaders = AxiosHeaders.from( 'getAllResponseHeaders' in request && request.getAllResponseHeaders() ); const responseData = !responseType || responseType === 'text' || responseType === 'json' ? request.responseText : request.response; const response = { data: responseData, status: request.status, statusText: request.statusText, headers: responseHeaders, config, request, }; settle( function _resolve(value) { resolve(value); done(); }, function _reject(err) { reject(err); done(); }, response ); // Clean up request request = null; } if ('onloadend' in request) { // Use onloadend if available request.onloadend = onloadend; } else { // Listen for ready state to emulate onloadend request.onreadystatechange = function handleLoad() { if (!request || request.readyState !== 4) { return; } // The request errored out and we didn't get a response, this will be // handled by onerror instead // With one exception: request that using file: protocol, most browsers // will return status as 0 even though it's a successful request if ( request.status === 0 && !(request.responseURL && request.responseURL.startsWith('file:')) ) { return; } // readystate handler is calling before onerror or ontimeout handlers, // so we should call onloadend on the next 'tick' setTimeout(onloadend); }; } // Handle browser request cancellation (as opposed to a manual cancellation) request.onabort = function handleAbort() { if (!request) { return; } reject(new AxiosError('Request aborted', AxiosError.ECONNABORTED, config, request)); done(); // Clean up request request = null; }; // Handle low level network errors request.onerror = function handleError(event) { // Browsers deliver a ProgressEvent in XHR onerror // (message may be empty; when present, surface it) // See https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/error_event const msg = event && event.message ? event.message : 'Network Error'; const err = new AxiosError(msg, AxiosError.ERR_NETWORK, config, request); // attach the underlying event for consumers who want details err.event = event || null; reject(err); done(); request = null; }; // Handle timeout request.ontimeout = function handleTimeout() { if (!request) return; let timeoutErrorMessage = _config.timeout ? 'timeout of ' + _config.timeout + 'ms exceeded' : 'timeout exceeded'; const transitional = _config.transitional || transitionalDefaults; if (_config.timeoutErrorMessage) { timeoutErrorMessage = _config.timeoutErrorMessage; } reject( new AxiosError( timeoutErrorMessage, transitional.clarifyTimeoutError ? AxiosError.ETIMEDOUT : AxiosError.ECONNABORTED, config, request ) ); done(); // Clean up request request = null; }; // Remove Content-Type if data is undefined requestData === undefined && requestHeaders.setContentType(null); // Add withCredentials to request if needed if (!utils.isUndefined(_config.withCredentials)) { request.withCredentials = !!_config.withCredentials; } // Add responseType to request if needed if (responseType && responseType !== 'json') { request.responseType = _config.responseType; } // Handle progress if needed if (onDownloadProgress) { [downloadThrottled, flushDownload, flushDownloadWithEvent] = progressEventReducer( onDownloadProgress, true ); request.addEventListener('progress', downloadThrottled); } // Not all browsers support upload events if (onUploadProgress && request.upload) { [uploadThrottled, flushUpload] = progressEventReducer(onUploadProgress); request.upload.addEventListener('progress', uploadThrottled); request.upload.addEventListener('loadend', flushUpload); } if (_config.cancelToken || _config.signal) { // Handle cancellation // eslint-disable-next-line func-names onCanceled = (cancel) => { if (!request) { return; } reject(!cancel || cancel.type ? new CanceledError(null, config, request) : cancel); const xhr = request; done(); request = null; xhr.abort(); }; _config.cancelToken && _config.cancelToken.subscribe(onCanceled); if (!request) return; if (_config.signal) { _config.signal.aborted ? onCanceled() : _config.signal.addEventListener('abort', onCanceled); } } if (!request) return; const protocol = parseProtocol(_config.url); if (protocol && !platform.protocols.includes(protocol)) { reject( new AxiosError( 'Unsupported protocol ' + protocol + ':', AxiosError.ERR_BAD_REQUEST, config ) ); done(); return; } const sendRequest = () => { if (!request) return; // Apply headers after an asynchronous XSRF read so existing headers are // replaced through AxiosHeaders rather than appended by the native XHR. if ('setRequestHeader' in request) { utils.forEach( toByteStringHeaderObject(requestHeaders), function setRequestHeader(val, key) { request.setRequestHeader(key, val); } ); } request.send(requestData || null); }; if (xsrfCookieName) { // Native XHR timeout starts at send(). Cover cookie preparation and the // subsequent request with the same budget and existing timeout handler. const now = typeof performance !== 'undefined' && utils.isFunction(performance.now) ? () => performance.now() : Date.now; const deadline = request.timeout > 0 ? now() + Number(request.timeout) : undefined; const abortOnTimeout = () => { if (!request) return; const xhr = request; xhr.ontimeout(); xhr.abort(); }; if (deadline !== undefined) { const scheduleTimeout = () => { const remaining = deadline - now(); if (remaining > 0) { // Browser timers overflow above the signed 32-bit delay limit. timeoutId = setTimeout(scheduleTimeout, Math.min(remaining, 0x7fffffff)); } else { abortOnTimeout(); } }; scheduleTimeout(); } cookies .readAsync(xsrfCookieName) .then((value) => { if (!request) return; // Promise callbacks may run before an overdue timer after a busy task. if (deadline !== undefined && now() >= deadline) { abortOnTimeout(); return; } if (value) { const headerName = utils.hasOwnProp(_config, 'xsrfHeaderName') && _config.xsrfHeaderName; requestHeaders.set(headerName, value); } sendRequest(); }) .catch((error) => { if (!request) return; reject(AxiosError.from(error, null, config, request)); done(); request = null; }); } else { sendRequest(); } }); }; axios-axios-aa54a5e/lib/axios.js000066400000000000000000000047761526660636600167320ustar00rootroot00000000000000'use strict'; import utils from './utils.js'; import bind from './helpers/bind.js'; import Axios from './core/Axios.js'; import mergeConfig from './core/mergeConfig.js'; import defaults from './defaults/index.js'; import formDataToJSON from './helpers/formDataToJSON.js'; import CanceledError from './cancel/CanceledError.js'; import CancelToken from './cancel/CancelToken.js'; import isCancel from './cancel/isCancel.js'; import { VERSION } from './env/data.js'; import toFormData from './helpers/toFormData.js'; import AxiosError from './core/AxiosError.js'; import spread from './helpers/spread.js'; import isAxiosError from './helpers/isAxiosError.js'; import AxiosHeaders from './core/AxiosHeaders.js'; import adapters from './adapters/adapters.js'; import HttpStatusCode from './helpers/HttpStatusCode.js'; /** * Create an instance of Axios * * @param {Object} defaultConfig The default config for the instance * * @returns {Axios} A new instance of Axios */ function createInstance(defaultConfig) { const context = new Axios(defaultConfig); const instance = bind(Axios.prototype.request, context); // Copy axios.prototype to instance utils.extend(instance, Axios.prototype, context, { allOwnKeys: true }); // Copy context to instance utils.extend(instance, context, null, { allOwnKeys: true }); // Factory for creating new instances instance.create = function create(instanceConfig) { return createInstance(mergeConfig(defaultConfig, instanceConfig)); }; return instance; } // Create the default instance to be exported const axios = createInstance(defaults); // Expose Axios class to allow class inheritance axios.Axios = Axios; // Expose Cancel & CancelToken axios.CanceledError = CanceledError; axios.CancelToken = CancelToken; axios.isCancel = isCancel; axios.VERSION = VERSION; axios.toFormData = toFormData; // Expose AxiosError class axios.AxiosError = AxiosError; // alias for CanceledError for backward compatibility axios.Cancel = axios.CanceledError; // Expose all/spread axios.all = function all(promises) { return Promise.all(promises); }; axios.spread = spread; // Expose isAxiosError axios.isAxiosError = isAxiosError; // Expose mergeConfig axios.mergeConfig = mergeConfig; axios.AxiosHeaders = AxiosHeaders; axios.formToJSON = (thing) => formDataToJSON(utils.isHTMLForm(thing) ? new FormData(thing) : thing); axios.getAdapter = adapters.getAdapter; axios.HttpStatusCode = HttpStatusCode; axios.default = axios; // this module should only have a default export export default axios; axios-axios-aa54a5e/lib/cancel/000077500000000000000000000000001526660636600164605ustar00rootroot00000000000000axios-axios-aa54a5e/lib/cancel/CancelToken.js000066400000000000000000000053541526660636600212130ustar00rootroot00000000000000'use strict'; import CanceledError from './CanceledError.js'; /** * A `CancelToken` is an object that can be used to request cancellation of an operation. * * @param {Function} executor The executor function. * * @returns {CancelToken} */ class CancelToken { constructor(executor) { if (typeof executor !== 'function') { throw new TypeError('executor must be a function.'); } let resolvePromise; this.promise = new Promise(function promiseExecutor(resolve) { resolvePromise = resolve; }); const token = this; // eslint-disable-next-line func-names this.promise.then((cancel) => { if (!token._listeners) return; let i = token._listeners.length; while (i-- > 0) { token._listeners[i](cancel); } token._listeners = null; }); // eslint-disable-next-line func-names this.promise.then = (onfulfilled) => { let _resolve; // eslint-disable-next-line func-names const promise = new Promise((resolve) => { token.subscribe(resolve); _resolve = resolve; }).then(onfulfilled); promise.cancel = function reject() { token.unsubscribe(_resolve); }; return promise; }; executor(function cancel(message, config, request) { if (token.reason) { // Cancellation has already been requested return; } token.reason = new CanceledError(message, config, request); resolvePromise(token.reason); }); } /** * Throws a `CanceledError` if cancellation has been requested. */ throwIfRequested() { if (this.reason) { throw this.reason; } } /** * Subscribe to the cancel signal */ subscribe(listener) { if (this.reason) { listener(this.reason); return; } if (this._listeners) { this._listeners.push(listener); } else { this._listeners = [listener]; } } /** * Unsubscribe from the cancel signal */ unsubscribe(listener) { if (!this._listeners) { return; } const index = this._listeners.indexOf(listener); if (index !== -1) { this._listeners.splice(index, 1); } } toAbortSignal() { const controller = new AbortController(); const abort = (err) => { controller.abort(err); }; this.subscribe(abort); controller.signal.unsubscribe = () => this.unsubscribe(abort); return controller.signal; } /** * Returns an object that contains a new `CancelToken` and a function that, when called, * cancels the `CancelToken`. */ static source() { let cancel; const token = new CancelToken(function executor(c) { cancel = c; }); return { token, cancel, }; } } export default CancelToken; axios-axios-aa54a5e/lib/cancel/CanceledError.js000066400000000000000000000011521526660636600215250ustar00rootroot00000000000000'use strict'; import AxiosError from '../core/AxiosError.js'; class CanceledError extends AxiosError { /** * A `CanceledError` is an object that is thrown when an operation is canceled. * * @param {string=} message The message. * @param {Object=} config The config. * @param {Object=} request The request. * * @returns {CanceledError} The created error. */ constructor(message, config, request) { super(message == null ? 'canceled' : message, AxiosError.ERR_CANCELED, config, request); this.name = 'CanceledError'; this.__CANCEL__ = true; } } export default CanceledError; axios-axios-aa54a5e/lib/cancel/isCancel.js000066400000000000000000000001431526660636600205350ustar00rootroot00000000000000'use strict'; export default function isCancel(value) { return !!(value && value.__CANCEL__); } axios-axios-aa54a5e/lib/core/000077500000000000000000000000001526660636600161635ustar00rootroot00000000000000axios-axios-aa54a5e/lib/core/Axios.js000066400000000000000000000257761526660636600176250ustar00rootroot00000000000000'use strict'; import utils from '../utils.js'; import buildURL from '../helpers/buildURL.js'; import InterceptorManager from './InterceptorManager.js'; import dispatchRequest from './dispatchRequest.js'; import mergeConfig from './mergeConfig.js'; import buildFullPath from './buildFullPath.js'; import methodList from './methodList.js'; import validator from '../helpers/validator.js'; import AxiosHeaders from './AxiosHeaders.js'; import transitionalDefaults from '../defaults/transitional.js'; import { getCauseStackIndex } from './AxiosError.js'; const validators = validator.validators; // Recognize header maps without consuming objects intended as literal values. // AxiosHeaders from other builds share a brand and header-container interface, // but not constructor identity. A tag and toJSON alone can describe a literal. const isHeadersBucket = (value) => { if (value == null || utils.isPlainObject(value) || value instanceof AxiosHeaders) { return true; } try { return ( utils.isObject(value) && utils.getSafeProp(value, Symbol.toStringTag) === 'AxiosHeaders' && [ 'toJSON', 'set', 'get', 'has', 'delete', 'clear', 'normalize', 'concat', 'toString', Symbol.iterator, ].every((method) => utils.isFunction(utils.getSafeProp(value, method))) ); } catch (e) { // A failed optional brand probe must not prevent literal header serialization. return false; } }; /** * Create a new instance of Axios * * @param {Object} instanceConfig The default config for the instance * * @return {Axios} A new instance of Axios */ class Axios { constructor(instanceConfig) { this.defaults = instanceConfig || {}; this.interceptors = { request: new InterceptorManager(), response: new InterceptorManager(), }; } /** * Dispatch a request * * @param {String|Object} configOrUrl The config specific for this request (merged with this.defaults) * @param {?Object} config * * @returns {Promise} The Promise to be fulfilled */ async request(configOrUrl, config) { try { return await this._request(configOrUrl, config); } catch (err) { if (err instanceof Error) { try { let dummy = {}; Error.captureStackTrace ? Error.captureStackTrace(dummy) : (dummy = new Error()); const dummyStack = dummy.stack; let stack = ''; // slice off the Error: ... line if (typeof dummyStack === 'string') { const firstNewlineIndex = dummyStack.indexOf('\n'); stack = firstNewlineIndex === -1 ? '' : dummyStack.slice(firstNewlineIndex + 1); } if (!err.stack) { err.stack = stack; } else if (stack) { // Match a common suffix after the 2 top frames when available, // otherwise compare the full reconstructed stack. const firstNewlineIndex = stack.indexOf('\n'); const secondNewlineIndex = firstNewlineIndex === -1 ? -1 : stack.indexOf('\n', firstNewlineIndex + 1); const duplicateCheckSuffix = secondNewlineIndex === -1 ? stack : stack.slice(secondNewlineIndex + 1); // `AxiosError.from()` may have already inlined the wrapped error's // stack under a `Caused by:` marker. Keep the reconstructed caller // frames ahead of that marker so stack-only reporters do not read // them as part of the low-level cause. const currentStack = String(err.stack); const causeIndex = getCauseStackIndex(err, currentStack); const stackHead = causeIndex === -1 ? currentStack : currentStack.slice(0, causeIndex); const causeSection = causeIndex === -1 ? '' : currentStack.slice(causeIndex); if (!stackHead.endsWith(duplicateCheckSuffix)) { err.stack = stackHead + '\n' + stack + causeSection; } } } catch (e) { // Ignore failures from custom stack hooks or un-writable stack properties. } } throw err; } } _request(configOrUrl, config) { /*eslint no-param-reassign:0*/ // Allow for axios('example/url'[, config]) a la fetch API if (typeof configOrUrl === 'string') { config = config || {}; config.url = configOrUrl; } else { config = configOrUrl || {}; } config = mergeConfig(this.defaults, config); const { transitional, paramsSerializer, headers } = config; if (transitional !== undefined) { validator.assertOptions( transitional, { silentJSONParsing: validators.transitional(validators.boolean), forcedJSONParsing: validators.transitional(validators.boolean), clarifyTimeoutError: validators.transitional(validators.boolean), legacyInterceptorReqResOrdering: validators.transitional(validators.boolean), advertiseZstdAcceptEncoding: validators.transitional(validators.boolean), validateStatusUndefinedResolves: validators.transitional(validators.boolean), encodeBasicAuthUsername: validators.transitional(validators.boolean), }, false ); } if (paramsSerializer != null) { if (utils.isFunction(paramsSerializer)) { config.paramsSerializer = { serialize: paramsSerializer, }; } else { validator.assertOptions( paramsSerializer, { encode: validators.function, serialize: validators.function, }, true ); } } // Set config.allowAbsoluteUrls if (config.allowAbsoluteUrls !== undefined) { // do nothing } else if ( utils.hasOwnProp(this.defaults, 'allowAbsoluteUrls') && this.defaults.allowAbsoluteUrls !== undefined ) { config.allowAbsoluteUrls = this.defaults.allowAbsoluteUrls; } else { config.allowAbsoluteUrls = true; } validator.assertOptions( config, { baseUrl: validators.spelling('baseURL'), withXsrfToken: validators.spelling('withXSRFToken'), }, true ); // Set config.method config.method = ( utils.getSafeProp(config, 'method') || utils.getSafeProp(this.defaults, 'method') || 'get' ).toLowerCase(); // Flatten header defaults without consuming literal method-named headers. const commonHeaders = utils.getSafeProp(headers, 'common'); const methodHeaders = utils.getSafeProp(headers, config.method); const contextHeaders = headers && utils.merge( isHeadersBucket(commonHeaders) ? commonHeaders : undefined, isHeadersBucket(methodHeaders) ? methodHeaders : undefined ); headers && utils.forEach(methodList.concat('common'), (method) => { if (isHeadersBucket(utils.getSafeProp(headers, method))) { delete headers[method]; } }); config.headers = AxiosHeaders.concat(contextHeaders, headers); // filter out skipped interceptors const requestInterceptorChain = []; let synchronousRequestInterceptors = true; this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) { if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) { return; } synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous; const transitional = config.transitional || transitionalDefaults; const legacyInterceptorReqResOrdering = transitional && transitional.legacyInterceptorReqResOrdering; if (legacyInterceptorReqResOrdering) { requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected); } else { requestInterceptorChain.push(interceptor.fulfilled, interceptor.rejected); } }); const responseInterceptorChain = []; this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) { responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected); }); let promise; let i = 0; let len; if (!synchronousRequestInterceptors) { const chain = [dispatchRequest.bind(this), undefined]; chain.unshift(...requestInterceptorChain); chain.push(...responseInterceptorChain); len = chain.length; promise = Promise.resolve(config); while (i < len) { promise = promise.then(chain[i++], chain[i++]); } return promise; } len = requestInterceptorChain.length; let newConfig = config; while (i < len) { const onFulfilled = requestInterceptorChain[i++]; const onRejected = requestInterceptorChain[i++]; try { newConfig = onFulfilled ? onFulfilled(newConfig) : newConfig; } catch (error) { if (!onRejected) { promise = Promise.reject(error); break; } try { const rejectedResult = onRejected.call(this, error); if (utils.isThenable(rejectedResult)) { promise = Promise.resolve(rejectedResult).then(() => dispatchRequest.call(this, newConfig) ); } } catch (rejectedError) { promise = Promise.reject(rejectedError); } break; } } if (!promise) { try { promise = dispatchRequest.call(this, newConfig); } catch (error) { promise = Promise.reject(error); } } i = 0; len = responseInterceptorChain.length; while (i < len) { promise = promise.then(responseInterceptorChain[i++], responseInterceptorChain[i++]); } return promise; } getUri(config) { config = mergeConfig(this.defaults, config); const fullPath = buildFullPath(config.baseURL, config.url, config.allowAbsoluteUrls, config); return buildURL(fullPath, config.params, config.paramsSerializer); } } // Provide aliases for supported request methods utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) { /*eslint func-names:0*/ Axios.prototype[method] = async function (url, config) { return this.request( mergeConfig(config || {}, { method, url, data: config && utils.hasOwnProp(config, 'data') ? config.data : undefined, }) ); }; }); utils.forEach(['post', 'put', 'patch', 'query'], function forEachMethodWithData(method) { function generateHTTPMethod(isForm) { return async function httpMethod(url, data, config) { return this.request( mergeConfig(config || {}, { method, headers: isForm ? { 'Content-Type': 'multipart/form-data', } : {}, url, data, }) ); }; } Axios.prototype[method] = generateHTTPMethod(); // QUERY is a safe/idempotent read method; multipart form bodies don't fit // its semantics, so no queryForm shorthand is generated. if (method !== 'query') { Axios.prototype[method + 'Form'] = generateHTTPMethod(true); } }); export default Axios; axios-axios-aa54a5e/lib/core/AxiosError.js000066400000000000000000000221031526660636600206140ustar00rootroot00000000000000'use strict'; import utils from '../utils.js'; import AxiosHeaders from './AxiosHeaders.js'; export const REDACTED = '[REDACTED ****]'; function hasOwnOrPrototypeToJSON(source) { if (utils.hasOwnProp(source, 'toJSON')) { return true; } let prototype = Object.getPrototypeOf(source); while (prototype && prototype !== Object.prototype) { if (utils.hasOwnProp(prototype, 'toJSON')) { return true; } prototype = Object.getPrototypeOf(prototype); } return false; } // Build a plain-object snapshot of `config` and replace the value of any key // (case-insensitive) listed in `redactKeys` with REDACTED. Walks through arrays // and AxiosHeaders, and short-circuits on circular references. function redactConfig(config, redactKeys) { const lowerKeys = new Set(redactKeys.map((k) => String(k).toLowerCase())); const seen = []; const visit = (source) => { if (source === null || typeof source !== 'object') return source; if (utils.isBuffer(source)) return source; if (seen.indexOf(source) !== -1) return undefined; if (source instanceof AxiosHeaders) { source = source.toJSON(); } seen.push(source); let result; if (utils.isArray(source)) { result = []; source.forEach((v, i) => { const reducedValue = visit(v); if (!utils.isUndefined(reducedValue)) { result[i] = reducedValue; } }); } else { if (!utils.isPlainObject(source) && hasOwnOrPrototypeToJSON(source)) { seen.pop(); return source; } result = Object.create(null); for (const [key, value] of Object.entries(source)) { const reducedValue = lowerKeys.has(key.toLowerCase()) ? REDACTED : visit(value); if (!utils.isUndefined(reducedValue)) { result[key] = reducedValue; } } } seen.pop(); return result; }; return visit(config); } function stringifySafely(value) { try { return String(value); } catch (err) { return ''; } } function aggregateErrorMessage(error) { const message = error.errors .map((entry) => { try { return entry && entry.message ? stringifySafely(entry.message) : stringifySafely(entry); } catch (err) { return ''; } }) .filter(Boolean) .join('; '); return message || error.name || 'AggregateError'; } // Shared with `Axios.prototype.request`, which splices the reconstructed // caller frames in ahead of this marker. export const CAUSED_BY_SEPARATOR = '\nCaused by: '; const $causeStack = Symbol('causeStack'); // Match the section we appended, not marker-like text in an error message or // a replacement stack supplied by the caller. Do not re-read the cause's stack. export function getCauseStackIndex(error, stack) { const causeStack = utils.hasOwnProp(error, $causeStack) && error[$causeStack]; return causeStack && stack.endsWith(causeStack) ? stack.length - causeStack.length : -1; } /** * Append the stack of a wrapped error to `target.stack`. * * `cause` already exposes the original error for tooling that walks the standard * cause chain, but many error reporters read `stack` and nothing else. Inlining * the wrapped stack there means those reporters surface the underlying failure * instead of the axios frames that normalized it (see #6670). * * Every read is defensive: a custom `Error.prepareStackTrace` hook, a runtime * without stack support, or a replaced `stack` property can leave the value * missing, non-string, or read-only, and normalizing an error must not throw * because of that. * * @param {Error} target The AxiosError being built. * @param {Error} source The error being wrapped. * * @returns {void} */ function appendCauseStack(target, source) { try { const sourceStack = source && source.stack; if (typeof sourceStack !== 'string' || !sourceStack) { return; } const targetStack = target.stack; if (typeof targetStack !== 'string' || !targetStack) { return; } // Each call to `from()` creates a fresh target. Re-wrapping includes the // previous stack verbatim, adding one cause section per wrap. const causeStack = CAUSED_BY_SEPARATOR + sourceStack; target.stack = targetStack + causeStack; Object.defineProperty(target, $causeStack, { __proto__: null, value: causeStack }); } catch (e) { // Ignore failures from custom stack hooks or un-writable stack properties. } } class AxiosError extends Error { static from(error, code, config, request, response, customProps) { // `AggregateError` (thrown by Node on dual-stack/Happy-Eyeballs connection // failures) has an empty `message`; its detail lives in `errors[]`. Without // this, the wrapped error surfaces with a blank message (see #6721). let message = error.message; if (!message && utils.isArray(error.errors) && error.errors.length) { message = aggregateErrorMessage(error); } const axiosError = new AxiosError(message, code || error.code, config, request, response); // Match native `Error` `cause` semantics: non-enumerable. The wrapped // error often carries circular internals (sockets, requests, agents), so // an enumerable `cause` makes structured loggers (pino/winston) and any // own-property walk throw "Converting circular structure to JSON". // Regression from #6982; see #7205. `__proto__: null` mirrors the // `message` descriptor below (prototype-pollution-safe descriptor). Object.defineProperty(axiosError, 'cause', { __proto__: null, value: error, writable: true, enumerable: false, configurable: true, }); axiosError.name = error.name; // Inline the wrapped stack for reporters that read only `stack`; the `cause` // property above already covers cause-aware tooling. appendCauseStack(axiosError, error); // Preserve status from the original error if not already set from response if (error.status != null && axiosError.status == null) { axiosError.status = error.status; } customProps && Object.assign(axiosError, customProps); return axiosError; } /** * Create an Error with the specified message, config, error code, request and response. * * @param {string} message The error message. * @param {string} [code] The error code (for example, 'ECONNABORTED'). * @param {Object} [config] The config. * @param {Object} [request] The request. * @param {Object} [response] The response. * * @returns {Error} The created error. */ constructor(message, code, config, request, response) { super(message); // Make message enumerable to maintain backward compatibility // The native Error constructor sets message as non-enumerable, // but axios < v1.13.3 had it as enumerable Object.defineProperty(this, 'message', { // Null-proto descriptor so a polluted Object.prototype.get cannot turn // this data descriptor into an accessor descriptor on the way in. __proto__: null, value: message, enumerable: true, writable: true, configurable: true, }); this.name = 'AxiosError'; this.isAxiosError = true; code && (this.code = code); config && (this.config = config); request && (this.request = request); if (response) { this.response = response; this.status = response.status; } } toJSON() { // Opt-in redaction: when the request config carries a `redact` array, the // value of any matching key (case-insensitive, at any depth) is replaced // with REDACTED in the serialized snapshot. Undefined or empty leaves the // existing serialization behavior unchanged. const config = this.config; const redactKeys = config && utils.hasOwnProp(config, 'redact') ? config.redact : undefined; const serializedConfig = utils.isArray(redactKeys) && redactKeys.length > 0 ? redactConfig(config, redactKeys) : utils.toJSONObject(config); return { // Standard message: this.message, name: this.name, // Microsoft description: this.description, number: this.number, // Mozilla fileName: this.fileName, lineNumber: this.lineNumber, columnNumber: this.columnNumber, stack: this.stack, // Axios config: serializedConfig, code: this.code, status: this.status, }; } } // This can be changed to static properties as soon as the parser options in .eslint.cjs are updated. AxiosError.ERR_BAD_OPTION_VALUE = 'ERR_BAD_OPTION_VALUE'; AxiosError.ERR_BAD_OPTION = 'ERR_BAD_OPTION'; AxiosError.ECONNABORTED = 'ECONNABORTED'; AxiosError.ETIMEDOUT = 'ETIMEDOUT'; AxiosError.ECONNREFUSED = 'ECONNREFUSED'; AxiosError.ERR_NETWORK = 'ERR_NETWORK'; AxiosError.ERR_FR_TOO_MANY_REDIRECTS = 'ERR_FR_TOO_MANY_REDIRECTS'; AxiosError.ERR_DEPRECATED = 'ERR_DEPRECATED'; AxiosError.ERR_BAD_RESPONSE = 'ERR_BAD_RESPONSE'; AxiosError.ERR_BAD_REQUEST = 'ERR_BAD_REQUEST'; AxiosError.ERR_CANCELED = 'ERR_CANCELED'; AxiosError.ERR_NOT_SUPPORT = 'ERR_NOT_SUPPORT'; AxiosError.ERR_INVALID_URL = 'ERR_INVALID_URL'; AxiosError.ERR_FORM_DATA_DEPTH_EXCEEDED = 'ERR_FORM_DATA_DEPTH_EXCEEDED'; export default AxiosError; axios-axios-aa54a5e/lib/core/AxiosHeaders.js000066400000000000000000000256631526660636600211140ustar00rootroot00000000000000'use strict'; import utils from '../utils.js'; import parseHeaders from '../helpers/parseHeaders.js'; import { sanitizeHeaderValue } from '../helpers/sanitizeHeaderValue.js'; const $internals = Symbol('internals'); function normalizeHeader(header) { return header && String(header).trim().toLowerCase(); } function normalizeValue(value) { if (value === false || value == null) { return value; } return utils.isArray(value) ? value.map(normalizeValue) : sanitizeHeaderValue(String(value)); } function parseTokens(str) { const tokens = Object.create(null); const tokensRE = /([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g; let match; while ((match = tokensRE.exec(str))) { tokens[match[1]] = match[2]; } return tokens; } const parameterNameRE = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/; function trimOWS(value) { let start = 0; let end = value.length; while (start < end) { const code = value.charCodeAt(start); if (code !== 0x09 && code !== 0x20) { break; } start += 1; } while (end > start) { const code = value.charCodeAt(end - 1); if (code !== 0x09 && code !== 0x20) { break; } end -= 1; } return start === 0 && end === value.length ? value : value.slice(start, end); } function decodeQuotedString(value) { const last = value.length - 1; if (last < 1 || value.charCodeAt(0) !== 0x22 || value.charCodeAt(last) !== 0x22) { return value; } let decoded = ''; for (let i = 1; i < last; i++) { const code = value.charCodeAt(i); if (code === 0x22) { return value; } if (code === 0x5c) { i += 1; if (i >= last) { return value; } } decoded += value[i]; } return decoded; } function parseParameters(value) { const parameters = Object.create(null); const str = String(value); let start = 0; let quoted = false; let escaped = false; function parseParameter(end) { const part = trimOWS(str.slice(start, end)); const equals = part.indexOf('='); if (equals < 1) { return; } const name = trimOWS(part.slice(0, equals)); if (!parameterNameRE.test(name)) { return; } const normalizedName = name.toLowerCase(); if ( normalizedName === '__proto__' || normalizedName === 'constructor' || normalizedName === 'prototype' ) { return; } const parameterValue = trimOWS(part.slice(equals + 1)); parameters[normalizedName] = decodeQuotedString(parameterValue); } for (let i = 0; i < str.length; i++) { const code = str.charCodeAt(i); if (quoted) { if (escaped) { escaped = false; } else if (code === 0x5c) { escaped = true; } else if (code === 0x22) { quoted = false; } } else if (code === 0x22) { quoted = true; } else if (code === 0x2c || code === 0x3b) { parseParameter(i); start = i + 1; } } parseParameter(str.length); return parameters; } const isValidHeaderName = (str) => /^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(str.trim()); // `__proto__` is a valid field name, and the one name the uppercase mapping at // the bottom of this file cannot reach: it is inherited from Object.prototype // instead of being declared on AxiosHeaders.prototype, so `self.__proto__ = v` // runs the inherited setter. That silently discards the header, and an array // value replaces the instance prototype, taking every method with it. // Store it under the same kind of case variant that mapping uses for `set` and // `constructor`. Header lookup is caseless, so it reads back under either // spelling, and no own `__proto__` slot is ever created for a later // `Object.assign` to copy onto a plain object through the same setter. const PROTO_HEADER_SLOT = '__Proto__'; const headerSlot = (name) => (name === '__proto__' ? PROTO_HEADER_SLOT : name); function matchHeaderValue(context, value, header, filter, isHeaderNameFilter) { if (utils.isFunction(filter)) { return filter.call(context, value, header); } if (isHeaderNameFilter) { value = header; } if (!utils.isString(value)) return; if (utils.isString(filter)) { return value.indexOf(filter) !== -1; } if (utils.isRegExp(filter)) { return filter.test(value); } } function formatHeader(header) { return header .trim() .toLowerCase() .replace(/([a-z\d])(\w*)/g, (w, char, str) => { return char.toUpperCase() + str; }); } function buildAccessors(obj, header) { const accessorName = utils.toCamelCase(' ' + header); ['get', 'set', 'has'].forEach((methodName) => { Object.defineProperty(obj, methodName + accessorName, { // Null-proto descriptor so a polluted Object.prototype.get cannot turn // this data descriptor into an accessor descriptor on the way in. __proto__: null, value: function (arg1, arg2, arg3) { return this[methodName].call(this, header, arg1, arg2, arg3); }, configurable: true, }); }); } class AxiosHeaders { constructor(headers) { headers && this.set(headers); } set(header, valueOrRewrite, rewrite) { const self = this; function setHeader(_value, _header, _rewrite) { const lHeader = normalizeHeader(_header); if (!lHeader) { return; } const key = utils.findKey(self, lHeader); if ( !key || self[key] === undefined || _rewrite === true || (_rewrite === undefined && self[key] !== false) ) { self[headerSlot(key || _header)] = normalizeValue(_value); } } const setHeaders = (headers, _rewrite) => utils.forEach(headers, (_value, _header) => setHeader(_value, _header, _rewrite)); if (utils.isPlainObject(header) || header instanceof this.constructor) { setHeaders(header, valueOrRewrite); } else if (utils.isString(header) && (header = header.trim()) && !isValidHeaderName(header)) { setHeaders(parseHeaders(header), valueOrRewrite); } else if (utils.isObject(header) && utils.isSafeIterable(header)) { let obj = Object.create(null), dest, key; for (const entry of header) { if (!utils.isArray(entry)) { throw new TypeError('Object iterator must return a key-value pair'); } key = entry[0]; if (utils.hasOwnProp(obj, key)) { dest = obj[key]; obj[key] = utils.isArray(dest) ? [...dest, entry[1]] : [dest, entry[1]]; } else { obj[key] = entry[1]; } } setHeaders(obj, valueOrRewrite); } else { header != null && setHeader(valueOrRewrite, header, rewrite); } return this; } get(header, parser) { header = normalizeHeader(header); if (header) { const key = utils.findKey(this, header); if (key) { const value = this[key]; if (!parser) { return value; } if (parser === true) { return parseTokens(value); } if (utils.isFunction(parser)) { return parser.call(this, value, key); } if (utils.isRegExp(parser)) { return parser.exec(value); } throw new TypeError('parser must be boolean|regexp|function'); } } } has(header, matcher) { header = normalizeHeader(header); if (header) { const key = utils.findKey(this, header); return !!( key && this[key] !== undefined && (!matcher || matchHeaderValue(this, this[key], key, matcher)) ); } return false; } delete(header, matcher) { const self = this; let deleted = false; function deleteHeader(_header) { _header = normalizeHeader(_header); if (_header) { const key = utils.findKey(self, _header); if (key && (!matcher || matchHeaderValue(self, self[key], key, matcher))) { delete self[key]; deleted = true; } } } if (utils.isArray(header)) { header.forEach(deleteHeader); } else { deleteHeader(header); } return deleted; } clear(matcher) { const keys = Object.keys(this); let i = keys.length; let deleted = false; while (i--) { const key = keys[i]; if (!matcher || matchHeaderValue(this, this[key], key, matcher, true)) { delete this[key]; deleted = true; } } return deleted; } normalize(format) { const self = this; const headers = Object.create(null); utils.forEach(this, (value, header) => { const key = utils.findKey(headers, header); if (key) { self[key] = normalizeValue(value); delete self[header]; return; } const normalized = headerSlot(format ? formatHeader(header) : String(header).trim()); if (normalized !== header) { delete self[header]; } self[normalized] = normalizeValue(value); headers[normalized] = true; }); return this; } concat(...targets) { return this.constructor.concat(this, ...targets); } toJSON(asStrings) { const obj = Object.create(null); utils.forEach(this, (value, header) => { value != null && value !== false && (obj[header] = asStrings && utils.isArray(value) ? value.join(', ') : value); }); return obj; } [Symbol.iterator]() { return Object.entries(this.toJSON())[Symbol.iterator](); } toString() { return Object.entries(this.toJSON()) .map(([header, value]) => header + ': ' + value) .join('\n'); } getSetCookie() { const value = this.get('set-cookie'); return utils.isArray(value) ? value : value == null || value === false ? [] : [value]; } get [Symbol.toStringTag]() { return 'AxiosHeaders'; } static from(thing) { return thing instanceof this ? thing : new this(thing); } static parseParameters(value) { return parseParameters(value); } static concat(first, ...targets) { const computed = new this(first); targets.forEach((target) => computed.set(target)); return computed; } static accessor(header) { const internals = (this[$internals] = this[$internals] = { accessors: {}, }); const accessors = internals.accessors; const prototype = this.prototype; function defineAccessor(_header) { const lHeader = normalizeHeader(_header); if (!accessors[lHeader]) { buildAccessors(prototype, _header); accessors[lHeader] = true; } } utils.isArray(header) ? header.forEach(defineAccessor) : defineAccessor(header); return this; } } AxiosHeaders.accessor([ 'Content-Type', 'Content-Length', 'Accept', 'Accept-Encoding', 'User-Agent', 'Authorization', ]); // reserved names hotfix utils.reduceDescriptors(AxiosHeaders.prototype, ({ value }, key) => { let mapped = key[0].toUpperCase() + key.slice(1); // map `set` => `Set` return { get: () => value, set(headerValue) { this[mapped] = headerValue; }, }; }); utils.freezeMethods(AxiosHeaders); export default AxiosHeaders; axios-axios-aa54a5e/lib/core/InterceptorManager.js000066400000000000000000000107021526660636600223120ustar00rootroot00000000000000'use strict'; import utils from '../utils.js'; const $internals = Symbol('internals'); // `handlers` is public and may be replaced with a nullish value by user code; // `clear()` has always tolerated that. Treat it as an empty stack rather than // dereferencing it. function countHandlers(handlers) { return handlers ? handlers.length : 0; } function trimHandlers(handlers) { if (!handlers) { return; } while (handlers.length && handlers[handlers.length - 1] === null) { handlers.pop(); } } // Preserve the array-key coercion used before IDs were stored in a Map, without // treating null, empty strings, or noncanonical numeric strings as ID zero. function normalizeHandlerId(id) { if (typeof id === 'string' && id !== '' && String(Number(id)) === id) { return Number(id); } return id; } function syncHandlerEntries(manager, internals) { const handlers = manager.handlers; const length = countHandlers(handlers); if (handlers !== internals.handlersRef) { internals.handlersRef = handlers; internals.handlerEntries.clear(); } else if (length !== internals.handlersLength) { if (!length) { internals.handlerEntries.clear(); } else { internals.handlerEntries.forEach(function removeStaleEntry(entry, id) { if (handlers[entry.index] !== entry.handler) { internals.handlerEntries.delete(id); } }); } } internals.handlersLength = length; } class InterceptorManager { constructor() { this.handlers = []; this[$internals] = { handlersRef: this.handlers, handlersLength: this.handlers.length, handlerEntries: new Map(), iterationDepth: 0, nextId: 0, }; } /** * Add a new interceptor to the stack * * @param {Function} fulfilled The function to handle `then` for a `Promise` * @param {Function} rejected The function to handle `reject` for a `Promise` * @param {Object} options The options for the interceptor, synchronous and runWhen * * @return {Number} An ID used to remove interceptor later */ use(fulfilled, rejected, options) { const handler = { fulfilled, rejected, synchronous: options ? options.synchronous : false, runWhen: options ? options.runWhen : null, }; const internals = this[$internals]; if (this.handlers == null) { this.handlers = []; } syncHandlerEntries(this, internals); const id = internals.nextId++; this.handlers.push(handler); internals.handlerEntries.set(id, { handler, index: this.handlers.length - 1, }); internals.handlersLength = this.handlers.length; return id; } /** * Remove an interceptor from the stack * * @param {Number} id The ID that was returned by `use` * * @returns {void} */ eject(id) { const internals = this[$internals]; syncHandlerEntries(this, internals); const handlerId = normalizeHandlerId(id); const entry = internals.handlerEntries.get(handlerId); if (entry) { internals.handlerEntries.delete(handlerId); // Ignore IDs invalidated by clear or direct replacement of handlers. if (this.handlers[entry.index] !== entry.handler) { return; } this.handlers[entry.index] = null; // Do not reuse an index while forEach is walking its length snapshot. if (!internals.iterationDepth) { trimHandlers(this.handlers); internals.handlersLength = this.handlers.length; } } } /** * Clear all interceptors from the stack * * @returns {void} */ clear() { if (this.handlers) { this.handlers = []; syncHandlerEntries(this, this[$internals]); } } /** * Iterate over all the registered interceptors * * This method is particularly useful for skipping over any * interceptors that may have become `null` calling `eject`. * * @param {Function} fn The function to call for each interceptor * * @returns {void} */ forEach(fn) { const internals = this[$internals]; syncHandlerEntries(this, internals); internals.iterationDepth++; try { utils.forEach(this.handlers, function forEachHandler(h) { if (h !== null) { fn(h); } }); } finally { if (!--internals.iterationDepth) { syncHandlerEntries(this, internals); trimHandlers(this.handlers); internals.handlersLength = countHandlers(this.handlers); } } } } export default InterceptorManager; axios-axios-aa54a5e/lib/core/README.md000066400000000000000000000006171526660636600174460ustar00rootroot00000000000000# axios // core The modules found in `core/` should be modules that are specific to the domain logic of axios. These modules would most likely not make sense to be consumed outside of the axios module, as their logic is too specific. Some examples of core modules are: - Dispatching requests - Requests sent via `adapters/` (see lib/adapters/README.md) - Managing interceptors - Handling config axios-axios-aa54a5e/lib/core/assertParamsAcyclic.js000066400000000000000000000031261526660636600224600ustar00rootroot00000000000000'use strict'; import utils from '../utils.js'; import AxiosError from './AxiosError.js'; export default function assertParamsAcyclic(params) { const pending = [{ value: params }]; const active = new WeakSet(); const visited = new WeakSet(); while (pending.length) { const { value, done } = pending.pop(); if (!utils.isPlainObject(value) && !utils.isArray(value)) continue; if (done) { active.delete(value); continue; } if (active.has(value)) { throw new AxiosError( 'Circular reference detected in params', AxiosError.ERR_BAD_OPTION_VALUE ); } if (visited.has(value)) continue; visited.add(value); active.add(value); pending.push({ value, done: true }); const keys = Object.keys(value); if (Object.getOwnPropertySymbols && Object.getOwnPropertyDescriptor) { Object.getOwnPropertySymbols(value).forEach((key) => { const descriptor = Object.getOwnPropertyDescriptor(value, key); if (descriptor && descriptor.enumerable) keys.push(key); }); } // Shallow array elements retain fields filtered during plain-object merges. // Inspect reserved data properties without evaluating their accessors. keys.forEach((key) => { if (key === '__proto__' || key === 'constructor' || key === 'prototype') { const descriptor = Object.getOwnPropertyDescriptor(value, key); if (descriptor && utils.hasOwnProp(descriptor, 'value')) { pending.push({ value: descriptor.value }); } } else { pending.push({ value: value[key] }); } }); } } axios-axios-aa54a5e/lib/core/buildFullPath.js000066400000000000000000000055231526660636600212650ustar00rootroot00000000000000'use strict'; import AxiosError, { REDACTED } from './AxiosError.js'; import isAbsoluteURL from '../helpers/isAbsoluteURL.js'; import combineURLs from '../helpers/combineURLs.js'; import normalizeURLForProtocolCheck from '../helpers/normalizeURLForProtocolCheck.js'; const malformedHttpProtocol = /^https?:(?!\/\/)/i; // Redact the parts of a URL that can carry secrets before it is embedded in an // error message. AxiosError.toJSON() serializes `message` verbatim and errors // are commonly logged, while the opt-in `config.redact` model only cleans // config keys — it cannot reach the message. Redact only the genuinely // sensitive substrings — userinfo (credentials), query parameter values and // fragment contents — with the same REDACTED marker the config redaction uses, // while keeping the scheme, host, path and parameter names so the offending // request stays accurately identifiable. function redactFragment(fragment) { if (!fragment) { return fragment; } return fragment.replace(/(^|&)([^=&]*=)?[^&]+/g, (match, separator, parameterName = '') => { return `${separator}${parameterName}${REDACTED}`; }); } function redactSensitiveURLParts(url) { const redactedURL = url.replace(/^(https?:\/{0,2})[^/?#]*@/i, `$1${REDACTED}@`); const fragmentIndex = redactedURL.indexOf('#'); const urlWithoutFragment = fragmentIndex === -1 ? redactedURL : redactedURL.slice(0, fragmentIndex); const redactedURLWithoutFragment = urlWithoutFragment.replace( /([?&][^=&#]*=)[^&#]*/g, `$1${REDACTED}` ); if (fragmentIndex === -1) { return redactedURLWithoutFragment; } return `${redactedURLWithoutFragment}#${redactFragment(redactedURL.slice(fragmentIndex + 1))}`; } function assertValidHttpProtocolURL(url, config) { if (typeof url === 'string') { const normalizedURL = normalizeURLForProtocolCheck(url); if (malformedHttpProtocol.test(normalizedURL)) { throw new AxiosError( `Invalid URL ${JSON.stringify(redactSensitiveURLParts(normalizedURL))}: missing "//" after protocol`, AxiosError.ERR_INVALID_URL, config ); } } } /** * Creates a new URL by combining the baseURL with the requestedURL, * only when the requestedURL is not already an absolute URL. * If the requestURL is absolute, this function returns the requestedURL untouched. * * @param {string} baseURL The base URL * @param {string} requestedURL Absolute or relative URL to combine * * @returns {string} The combined full path */ export default function buildFullPath(baseURL, requestedURL, allowAbsoluteUrls, config) { assertValidHttpProtocolURL(requestedURL, config); let isRelativeUrl = !isAbsoluteURL(requestedURL); if (baseURL && (isRelativeUrl || allowAbsoluteUrls === false)) { assertValidHttpProtocolURL(baseURL, config); return combineURLs(baseURL, requestedURL); } return requestedURL; } axios-axios-aa54a5e/lib/core/createMultipartBody.js000066400000000000000000000026001526660636600225020ustar00rootroot00000000000000import utils from '../utils.js'; import platform from '../platform/index.js'; import AxiosError from './AxiosError.js'; import formDataToBlob, { FormDataError } from '../helpers/formDataToBlob.js'; export default function createMultipartBody(form, Blob, limit, limitError, boundary) { // Report missing Blob support before trying to select a random source. if (!utils.isFunction(Blob)) { throw new AxiosError('Blob is not supported', AxiosError.ERR_NOT_SUPPORT); } if (boundary === undefined) { if (utils.isFunction(platform.generateString)) { boundary = 'axios-' + platform.generateString(36, '0123456789abcdef'); } else { const crypto = utils.global.crypto; if (!crypto || !utils.isFunction(crypto.getRandomValues)) { throw new AxiosError('Random byte generation is not supported', AxiosError.ERR_NOT_SUPPORT); } const random = new Uint8Array(18); crypto.getRandomValues(random); boundary = 'axios-' + Array.from(random, (value) => ('0' + value.toString(16)).slice(-2)).join(''); } } try { return formDataToBlob(form, Blob, limit, limitError, boundary); } catch (error) { if (error instanceof FormDataError) { const code = error.reason === 'boundary' ? AxiosError.ERR_BAD_OPTION_VALUE : AxiosError.ERR_BAD_REQUEST; throw new AxiosError(error.message, code); } throw error; } } axios-axios-aa54a5e/lib/core/dispatchRequest.js000066400000000000000000000070151526660636600216740ustar00rootroot00000000000000'use strict'; import transformData from './transformData.js'; import isCancel from '../cancel/isCancel.js'; import defaults from '../defaults/index.js'; import CanceledError from '../cancel/CanceledError.js'; import AxiosHeaders from '../core/AxiosHeaders.js'; import adapters from '../adapters/adapters.js'; import utils from '../utils.js'; /** * Throws a `CanceledError` if cancellation has been requested. * * @param {Object} config The config that is to be used for the request * * @returns {void} */ function throwIfCancellationRequested(config) { if (config.cancelToken) { config.cancelToken.throwIfRequested(); } if (config.signal && config.signal.aborted) { throw new CanceledError(null, config); } } /** * Milliseconds elapsed since `start`, floored at zero. * * Wall-clock, to match the rest of the codebase (speedometer, throttle, * AxiosTransformStream). A backwards clock adjustment mid-request would * otherwise produce a negative duration, so the result is clamped. * * @param {number} start Timestamp from `Date.now()` * * @returns {number} Non-negative elapsed milliseconds */ function elapsedSince(start) { return Math.max(0, Date.now() - start); } /** * Dispatch a request to the server using the configured adapter. * * @param {object} config The config that is to be used for the request * * @returns {Promise} The Promise to be fulfilled */ export default function dispatchRequest(_config) { // Interceptors may replace the merged config with an ordinary object. Flatten // it at the dispatch boundary so shared prototype members cannot become // request behavior, while preserving intentional template/class members. const config = utils.toSafeFlatObject(_config); throwIfCancellationRequested(config); config.headers = AxiosHeaders.from(utils.getSafeProp(config, 'headers')); // Transform request data config.data = transformData.call(config, config.transformRequest); if (['post', 'put', 'patch'].indexOf(config.method) !== -1) { config.headers.setContentType('application/x-www-form-urlencoded', false); } const adapter = adapters.getAdapter(config.adapter || defaults.adapter, config); const start = Date.now(); return adapter(config).then( function onAdapterResolution(response) { throwIfCancellationRequested(config); response.duration = elapsedSince(start); // Expose the current response on config so that transformResponse can // attach it to any AxiosError it throws (e.g. on JSON parse failure). // We clean it up afterwards to avoid polluting the config object. config.response = response; try { response.data = transformData.call(config, config.transformResponse, response); } finally { delete config.response; } response.headers = AxiosHeaders.from(response.headers); return response; }, function onAdapterRejection(reason) { if (!isCancel(reason)) { throwIfCancellationRequested(config); // Transform response data if (reason && reason.response) { reason.response.duration = elapsedSince(start); config.response = reason.response; try { reason.response.data = transformData.call( config, config.transformResponse, reason.response ); } finally { delete config.response; } reason.response.headers = AxiosHeaders.from(reason.response.headers); } } return Promise.reject(reason); } ); } axios-axios-aa54a5e/lib/core/mergeConfig.js000066400000000000000000000172771526660636600207640ustar00rootroot00000000000000'use strict'; import utils from '../utils.js'; import AxiosHeaders from './AxiosHeaders.js'; import assertParamsAcyclic from './assertParamsAcyclic.js'; const headersToObject = (thing) => (thing instanceof AxiosHeaders ? { ...thing } : thing); const ownEnumerableKeys = (thing) => { if (Object.getOwnPropertySymbols && Object.getOwnPropertyDescriptor) { return Object.keys(thing).concat( Object.getOwnPropertySymbols(thing).filter((symbol) => { const descriptor = Object.getOwnPropertyDescriptor(thing, symbol); return descriptor && descriptor.enumerable; }) ); } return Object.keys(thing); }; // Copy params without recursive merges so the serializer remains responsible // for its depth policy. Only visit defaults that contribute to the result. function mergeParams(a, b) { const pending = []; const copies = new WeakMap(); const empty = {}; function copy(target, source) { if (!utils.isPlainObject(source)) { return utils.isArray(source) ? source.slice() : source; } const base = utils.isPlainObject(target) ? target : empty; let bases = copies.get(source); if (!bases) { bases = new WeakMap(); copies.set(source, bases); } if (bases.has(base)) return bases.get(base); const result = {}; bases.set(base, result); pending.push({ base, source, result }); return result; } const result = utils.isUndefined(b) ? copy(undefined, a) : copy(a, b); while (pending.length) { const { base, source, result: target } = pending.pop(); const baseKeys = new Set(ownEnumerableKeys(base)); const sourceKeys = new Set(ownEnumerableKeys(source)); const keys = new Set([...baseKeys, ...sourceKeys]); keys.forEach((key) => { if (key === '__proto__' || key === 'constructor' || key === 'prototype') return; const hasSource = sourceKeys.has(key); const value = hasSource ? source[key] : undefined; const previous = (!hasSource || utils.isPlainObject(value)) && baseKeys.has(key) ? base[key] : undefined; target[key] = hasSource ? copy(previous, value) : copy(undefined, previous); }); } assertParamsAcyclic(result); return result; } function getMergedValue(target, source, prop, caseless) { if (utils.isPlainObject(target) && utils.isPlainObject(source)) { return utils.merge.call({ caseless }, target, source); } else if (utils.isPlainObject(source)) { return utils.merge({}, source); } else if (utils.isArray(source)) { return source.slice(); } return source; } function mergeDeepProperties(a, b, prop, caseless) { if (!utils.isUndefined(b)) { return getMergedValue(a, b, prop, caseless); } else if (!utils.isUndefined(a)) { return getMergedValue(undefined, a, prop, caseless); } } // eslint-disable-next-line consistent-return function valueFromConfig2(a, b) { if (!utils.isUndefined(b)) { return getMergedValue(undefined, b); } } // eslint-disable-next-line consistent-return function defaultToConfig2(a, b) { if (!utils.isUndefined(b)) { return getMergedValue(undefined, b); } else if (!utils.isUndefined(a)) { return getMergedValue(undefined, a); } } function getMergedTransitionalOption(config1, config2, prop) { const transitional2 = utils.hasOwnProp(config2, 'transitional') ? config2.transitional : undefined; if (!utils.isUndefined(transitional2)) { if (utils.isPlainObject(transitional2)) { if (utils.hasOwnProp(transitional2, prop)) { return transitional2[prop]; } } else { return undefined; } } const transitional1 = utils.hasOwnProp(config1, 'transitional') ? config1.transitional : undefined; if (utils.isPlainObject(transitional1) && utils.hasOwnProp(transitional1, prop)) { return transitional1[prop]; } return undefined; } // eslint-disable-next-line consistent-return function mergeDirectKeys(a, b, prop, config1, config2) { if (utils.hasOwnProp(config2, prop)) { return getMergedValue(a, b); } else if (utils.hasOwnProp(config1, prop)) { return getMergedValue(undefined, a); } } function mergeHeaders(a, b, prop) { return mergeDeepProperties(headersToObject(a), headersToObject(b), prop, true); } // Null-proto, frozen descriptor so a polluted Object.prototype.get cannot turn // this data descriptor into an accessor descriptor on the way in. // defineProperty only reads it, so one shared instance serves every call. const hasOwnPropertyDescriptor = Object.freeze({ __proto__: null, value: Object.prototype.hasOwnProperty, enumerable: false, writable: true, configurable: true, }); // The strategy table and the strategies above are stateless, so they are // built once at module load instead of on every call: mergeConfig runs at // most twice per request (once from a method alias, once from `_request`). const mergeMap = { url: valueFromConfig2, method: valueFromConfig2, data: valueFromConfig2, baseURL: defaultToConfig2, transformRequest: defaultToConfig2, transformResponse: defaultToConfig2, params: mergeParams, paramsSerializer: defaultToConfig2, timeout: defaultToConfig2, timeoutErrorMessage: defaultToConfig2, withCredentials: defaultToConfig2, withXSRFToken: defaultToConfig2, adapter: defaultToConfig2, responseType: defaultToConfig2, xsrfCookieName: defaultToConfig2, xsrfHeaderName: defaultToConfig2, onUploadProgress: defaultToConfig2, onDownloadProgress: defaultToConfig2, decompress: defaultToConfig2, maxContentLength: defaultToConfig2, maxBodyLength: defaultToConfig2, beforeRedirect: defaultToConfig2, transport: defaultToConfig2, httpAgent: defaultToConfig2, httpsAgent: defaultToConfig2, cancelToken: defaultToConfig2, socketPath: defaultToConfig2, allowedSocketPaths: defaultToConfig2, responseEncoding: defaultToConfig2, validateStatus: mergeDirectKeys, headers: mergeHeaders, }; /** * Config-specific merge-function which creates a new config-object * by merging two configuration objects together. * * @param {Object} config1 * @param {Object} config2 * * @returns {Object} New object resulting from merging config2 to config1 */ export default function mergeConfig(config1, config2) { // eslint-disable-next-line no-param-reassign config1 = config1 || {}; config2 = config2 || {}; // Use a null-prototype object so that downstream reads such as `config.auth` // or `config.baseURL` cannot inherit polluted values from Object.prototype. // `hasOwnProperty` is restored as a non-enumerable own slot to preserve // ergonomics for user code that relies on it. const config = Object.create(null); Object.defineProperty(config, 'hasOwnProperty', hasOwnPropertyDescriptor); const keys = ownEnumerableKeys({ ...config1, ...config2 }); for (let i = 0, len = keys.length; i < len; i++) { const prop = keys[i]; if (prop === '__proto__' || prop === 'constructor' || prop === 'prototype') continue; const merge = utils.hasOwnProp(mergeMap, prop) ? mergeMap[prop] : mergeDeepProperties; const a = utils.hasOwnProp(config1, prop) ? config1[prop] : undefined; const b = utils.hasOwnProp(config2, prop) ? config2[prop] : undefined; const configValue = merge === mergeDirectKeys ? merge(a, b, prop, config1, config2) : merge(a, b, prop); (utils.isUndefined(configValue) && merge !== mergeDirectKeys) || (config[prop] = configValue); } if ( utils.hasOwnProp(config2, 'validateStatus') && utils.isUndefined(config2.validateStatus) && getMergedTransitionalOption(config1, config2, 'validateStatusUndefinedResolves') === false ) { if (utils.hasOwnProp(config1, 'validateStatus')) { config.validateStatus = getMergedValue(undefined, config1.validateStatus); } else { delete config.validateStatus; } } return config; } axios-axios-aa54a5e/lib/core/methodList.js000066400000000000000000000003101526660636600206270ustar00rootroot00000000000000'use strict'; const methodList = Object.freeze([ 'get', 'delete', 'head', 'options', 'post', 'put', 'patch', 'purge', 'link', 'unlink', 'query', ]); export default methodList; axios-axios-aa54a5e/lib/core/setFormDataHeaders.js000066400000000000000000000016131526660636600222270ustar00rootroot00000000000000'use strict'; const FORM_DATA_CONTENT_HEADERS = ['content-type', 'content-length']; /** * Apply the headers generated by a FormData implementation to the request headers, * honoring the `formDataHeaderPolicy` option: with 'content-only', copy only the * content-* headers; otherwise merge all of them. * * @param {AxiosHeaders} headers - the request headers to mutate * @param {Object | null | undefined} formHeaders - headers produced by the FormData implementation * @param {String} [policy] - the resolved `formDataHeaderPolicy` config value * * @returns {void} */ export default function setFormDataHeaders(headers, formHeaders, policy) { if (policy !== 'content-only') { headers.set(formHeaders); return; } Object.entries(formHeaders || {}).forEach(([key, val]) => { if (FORM_DATA_CONTENT_HEADERS.includes(key.toLowerCase())) { headers.set(key, val); } }); } axios-axios-aa54a5e/lib/core/settle.js000066400000000000000000000015161526660636600200240ustar00rootroot00000000000000'use strict'; import AxiosError from './AxiosError.js'; /** * Resolve or reject a Promise based on response status. * * @param {Function} resolve A function that resolves the promise. * @param {Function} reject A function that rejects the promise. * @param {object} response The response. * * @returns {object} The response. */ export default function settle(resolve, reject, response) { const validateStatus = response.config.validateStatus; if (!response.status || !validateStatus || validateStatus(response.status)) { resolve(response); } else { reject(new AxiosError( 'Request failed with status code ' + response.status, response.status >= 400 && response.status < 500 ? AxiosError.ERR_BAD_REQUEST : AxiosError.ERR_BAD_RESPONSE, response.config, response.request, response )); } } axios-axios-aa54a5e/lib/core/transformData.js000066400000000000000000000014101526660636600213220ustar00rootroot00000000000000'use strict'; import utils from '../utils.js'; import defaults from '../defaults/index.js'; import AxiosHeaders from '../core/AxiosHeaders.js'; /** * Transform the data for a request or a response * * @param {Array|Function} fns A single function or Array of functions * @param {?Object} response The response object * * @returns {*} The resulting transformed data */ export default function transformData(fns, response) { const config = this || defaults; const context = response || config; const headers = AxiosHeaders.from(context.headers); let data = context.data; utils.forEach(fns, function transform(fn) { data = fn.call(config, data, headers.normalize(), response ? response.status : undefined); }); headers.normalize(); return data; } axios-axios-aa54a5e/lib/defaults/000077500000000000000000000000001526660636600170425ustar00rootroot00000000000000axios-axios-aa54a5e/lib/defaults/index.js000066400000000000000000000115611526660636600205130ustar00rootroot00000000000000'use strict'; import utils from '../utils.js'; import AxiosError from '../core/AxiosError.js'; import transitionalDefaults from './transitional.js'; import toFormData from '../helpers/toFormData.js'; import toURLEncodedForm from '../helpers/toURLEncodedForm.js'; import platform from '../platform/index.js'; import formDataToJSON from '../helpers/formDataToJSON.js'; import methodList from '../core/methodList.js'; const own = (obj, key) => (obj != null && utils.hasOwnProp(obj, key) ? obj[key] : undefined); /** * It takes a string, tries to parse it, and if it fails, it returns the stringified version * of the input * * @param {any} rawValue - The value to be stringified. * @param {Function} parser - A function that parses a string into a JavaScript object. * @param {Function} encoder - A function that takes a value and returns a string. * * @returns {string} A stringified version of the rawValue. */ function stringifySafely(rawValue, parser, encoder) { if (utils.isString(rawValue)) { try { (parser || JSON.parse)(rawValue); return utils.trim(rawValue); } catch (e) { if (e.name !== 'SyntaxError') { throw e; } } } return (encoder || JSON.stringify)(rawValue); } const defaults = { transitional: transitionalDefaults, adapter: ['xhr', 'http', 'fetch'], transformRequest: [ function transformRequest(data, headers) { const contentType = headers.getContentType() || ''; const hasJSONContentType = contentType.indexOf('application/json') > -1; const isObjectPayload = utils.isObject(data); if (isObjectPayload && utils.isHTMLForm(data)) { data = new FormData(data); } const isFormData = utils.isFormData(data); if (isFormData) { return hasJSONContentType ? JSON.stringify(formDataToJSON(data)) : data; } if ( utils.isArrayBuffer(data) || utils.isBuffer(data) || utils.isStream(data) || utils.isFile(data) || utils.isBlob(data) || utils.isReadableStream(data) ) { return data; } if (utils.isArrayBufferView(data)) { return data.buffer; } if (utils.isURLSearchParams(data)) { headers.setContentType('application/x-www-form-urlencoded;charset=utf-8', false); return data.toString(); } let isFileList; if (isObjectPayload) { const formSerializer = own(this, 'formSerializer'); if (contentType.indexOf('application/x-www-form-urlencoded') > -1) { return toURLEncodedForm(data, formSerializer).toString(); } if ( (isFileList = utils.isFileList(data)) || contentType.indexOf('multipart/form-data') > -1 ) { const env = own(this, 'env'); const _FormData = env && env.FormData; return toFormData( isFileList ? { 'files[]': data } : data, _FormData && new _FormData(), formSerializer ); } } if (isObjectPayload || hasJSONContentType) { headers.setContentType('application/json', false); return stringifySafely(data); } return data; }, ], transformResponse: [ function transformResponse(data) { const transitional = own(this, 'transitional') || defaults.transitional; const forcedJSONParsing = transitional && transitional.forcedJSONParsing; const responseType = own(this, 'responseType'); const JSONRequested = responseType === 'json'; if (utils.isResponse(data) || utils.isReadableStream(data)) { return data; } if ( data && utils.isString(data) && ((forcedJSONParsing && !responseType) || JSONRequested) ) { const silentJSONParsing = transitional && transitional.silentJSONParsing; const strictJSONParsing = !silentJSONParsing && JSONRequested; try { return JSON.parse(data, own(this, 'parseReviver')); } catch (e) { if (strictJSONParsing) { if (e.name === 'SyntaxError') { throw AxiosError.from(e, AxiosError.ERR_BAD_RESPONSE, this, null, own(this, 'response')); } throw e; } } } return data; }, ], /** * A timeout in milliseconds to abort a request. If set to 0 (default) a * timeout is not created. */ timeout: 0, xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN', maxContentLength: -1, maxBodyLength: -1, env: { FormData: platform.classes.FormData, Blob: platform.classes.Blob, }, validateStatus: function validateStatus(status) { return status >= 200 && status < 300; }, headers: { common: { Accept: 'application/json, text/plain, */*', 'Content-Type': undefined, }, }, }; utils.forEach(methodList, (method) => { defaults.headers[method] = {}; }); export default defaults; axios-axios-aa54a5e/lib/defaults/transitional.js000066400000000000000000000004211526660636600221040ustar00rootroot00000000000000'use strict'; export default { silentJSONParsing: true, forcedJSONParsing: true, clarifyTimeoutError: false, legacyInterceptorReqResOrdering: true, advertiseZstdAcceptEncoding: false, validateStatusUndefinedResolves: true, encodeBasicAuthUsername: false, }; axios-axios-aa54a5e/lib/env/000077500000000000000000000000001526660636600160235ustar00rootroot00000000000000axios-axios-aa54a5e/lib/env/README.md000066400000000000000000000002031526660636600172750ustar00rootroot00000000000000# axios // env The `data.js` file is updated automatically when the package version is upgrading. Please do not edit it manually. axios-axios-aa54a5e/lib/env/classes/000077500000000000000000000000001526660636600174605ustar00rootroot00000000000000axios-axios-aa54a5e/lib/env/classes/FormData.js000066400000000000000000000001521526660636600215110ustar00rootroot00000000000000import _FormData from 'form-data'; export default typeof FormData !== 'undefined' ? FormData : _FormData; axios-axios-aa54a5e/lib/env/data.js000066400000000000000000000000401526660636600172640ustar00rootroot00000000000000export const VERSION = "1.21.0";axios-axios-aa54a5e/lib/helpers/000077500000000000000000000000001526660636600166755ustar00rootroot00000000000000axios-axios-aa54a5e/lib/helpers/AxiosTransformStream.js000066400000000000000000000100411526660636600233620ustar00rootroot00000000000000'use strict'; import stream from 'stream'; import utils from '../utils.js'; const kInternals = Symbol('internals'); class AxiosTransformStream extends stream.Transform { constructor(options) { options = utils.toFlatObject( options, { maxRate: 0, chunkSize: 64 * 1024, minChunkSize: 100, timeWindow: 500, ticksRate: 2, samplesCount: 15, }, null, (prop, source) => { return !utils.isUndefined(source[prop]); } ); super({ readableHighWaterMark: options.chunkSize, }); const internals = (this[kInternals] = { timeWindow: options.timeWindow, chunkSize: options.chunkSize, maxRate: options.maxRate, minChunkSize: options.minChunkSize, bytesSeen: 0, isCaptured: false, notifiedBytesLoaded: 0, ts: Date.now(), bytes: 0, onReadCallback: null, }); this.on('newListener', (event) => { if (event === 'progress') { if (!internals.isCaptured) { internals.isCaptured = true; } } }); } _read(size) { const internals = this[kInternals]; if (internals.onReadCallback) { internals.onReadCallback(); } return super._read(size); } _transform(chunk, encoding, callback) { const internals = this[kInternals]; const maxRate = internals.maxRate; const readableHighWaterMark = this.readableHighWaterMark; const timeWindow = internals.timeWindow; const divider = 1000 / timeWindow; const bytesThreshold = maxRate / divider; let minChunkSize = internals.minChunkSize !== false ? Math.max(internals.minChunkSize, bytesThreshold * 0.01) : 0; if (maxRate) { minChunkSize = Math.min(bytesThreshold, minChunkSize); } const pushChunk = (_chunk, _callback) => { const bytes = Buffer.byteLength(_chunk); internals.bytesSeen += bytes; internals.bytes += bytes; internals.isCaptured && this.emit('progress', internals.bytesSeen); if (this.push(_chunk)) { process.nextTick(_callback); } else { internals.onReadCallback = () => { internals.onReadCallback = null; process.nextTick(_callback); }; } }; const transformChunk = (_chunk, _callback) => { if (this.destroyed) { return _callback(); } const chunkSize = Buffer.byteLength(_chunk); let chunkRemainder = null; let maxChunkSize = readableHighWaterMark; let bytesLeft; let passed = 0; if (maxRate) { const now = Date.now(); if (!internals.ts || (passed = now - internals.ts) >= timeWindow) { internals.ts = now; bytesLeft = bytesThreshold - internals.bytes; internals.bytes = bytesLeft < 0 ? -bytesLeft : 0; passed = 0; } bytesLeft = bytesThreshold - internals.bytes; } if (maxRate) { if (bytesLeft <= 0) { // next time window return setTimeout(() => { _callback(null, _chunk); }, timeWindow - passed); } if (bytesLeft < maxChunkSize) { // A buffer boundary must consume at least one byte. Carry any // fractional allowance forward in the next accounting window. maxChunkSize = Math.max(1, Math.floor(bytesLeft)); } } if (maxChunkSize && chunkSize > maxChunkSize && chunkSize - maxChunkSize > minChunkSize) { chunkRemainder = _chunk.subarray(maxChunkSize); _chunk = _chunk.subarray(0, maxChunkSize); } pushChunk( _chunk, chunkRemainder ? () => { process.nextTick(_callback, null, chunkRemainder); } : _callback ); }; transformChunk(chunk, function transformNextChunk(err, _chunk) { if (err) { return callback(err); } if (_chunk) { transformChunk(_chunk, transformNextChunk); } else { callback(null); } }); } } export default AxiosTransformStream; axios-axios-aa54a5e/lib/helpers/AxiosURLSearchParams.js000066400000000000000000000026101526660636600231720ustar00rootroot00000000000000'use strict'; import toFormData from './toFormData.js'; /** * It encodes a string by replacing all characters that are not in the unreserved set with * their percent-encoded equivalents * * @param {string} str - The string to encode. * * @returns {string} The encoded string. */ function encode(str) { const charMap = { '!': '%21', "'": '%27', '(': '%28', ')': '%29', '~': '%7E', '%20': '+', }; return encodeURIComponent(str).replace(/[!'()~]|%20/g, function replacer(match) { return charMap[match]; }); } /** * It takes a params object and converts it to a FormData object * * @param {Object} params - The parameters to be converted to a FormData object. * @param {Object} options - The options object passed to the Axios constructor. * * @returns {void} */ function AxiosURLSearchParams(params, options) { this._pairs = []; params && toFormData(params, this, options); } const prototype = AxiosURLSearchParams.prototype; prototype.append = function append(name, value) { this._pairs.push([name, value]); }; prototype.toString = function toString(encoder) { const _encode = encoder ? (value) => encoder.call(this, value, encode) : encode; return this._pairs .map(function each(pair) { return _encode(pair[0]) + '=' + _encode(pair[1]); }, '') .join('&'); }; export default AxiosURLSearchParams; axios-axios-aa54a5e/lib/helpers/Http2Sessions.js000066400000000000000000000052721526660636600217710ustar00rootroot00000000000000'use strict'; // Node-only: relies on the built-in `http2` module. Browser/react-native // builds replace `lib/adapters/http.js` (the sole importer) with `lib/helpers/null.js` // via the `browser` package.json field, so this module is never reached in // those environments. Do not import it from any browser-reachable code path. import http2 from 'http2'; import util from 'util'; class Http2Sessions { constructor() { this.sessions = Object.create(null); } getSession(authority, options) { options = Object.assign( Object.create(null), { sessionTimeout: 1000, }, options ); let authoritySessions = this.sessions[authority]; if (authoritySessions) { let len = authoritySessions.length; for (let i = 0; i < len; i++) { const [sessionHandle, sessionOptions] = authoritySessions[i]; if ( !sessionHandle.destroyed && !sessionHandle.closed && util.isDeepStrictEqual(sessionOptions, options) ) { return sessionHandle; } } } const session = http2.connect(authority, options); let removed; let timer; const removeSession = () => { if (removed) { return; } removed = true; if (timer) { clearTimeout(timer); timer = null; } let entries = authoritySessions, len = entries.length, i = len; while (i--) { if (entries[i][0] === session) { if (len === 1) { delete this.sessions[authority]; } else { entries.splice(i, 1); } if (!session.closed) { session.close(); } return; } } }; const originalRequestFn = session.request; const { sessionTimeout } = options; if (sessionTimeout != null) { let streamsCount = 0; session.request = function () { const stream = originalRequestFn.apply(this, arguments); streamsCount++; if (timer) { clearTimeout(timer); timer = null; } stream.once('close', () => { if (!--streamsCount && !removed) { timer = setTimeout(() => { timer = null; removeSession(); }, sessionTimeout); } }); return stream; }; } session.once('close', removeSession); session.once('error', removeSession); session.once('goaway', removeSession); let entry = [session, options]; authoritySessions ? authoritySessions.push(entry) : (authoritySessions = this.sessions[authority] = [entry]); return session; } } export default Http2Sessions; axios-axios-aa54a5e/lib/helpers/HttpStatusCode.js000066400000000000000000000037541526660636600221620ustar00rootroot00000000000000const HttpStatusCode = { Continue: 100, SwitchingProtocols: 101, Processing: 102, EarlyHints: 103, Ok: 200, Created: 201, Accepted: 202, NonAuthoritativeInformation: 203, NoContent: 204, ResetContent: 205, PartialContent: 206, MultiStatus: 207, AlreadyReported: 208, ImUsed: 226, MultipleChoices: 300, MovedPermanently: 301, Found: 302, SeeOther: 303, NotModified: 304, UseProxy: 305, Unused: 306, TemporaryRedirect: 307, PermanentRedirect: 308, BadRequest: 400, Unauthorized: 401, PaymentRequired: 402, Forbidden: 403, NotFound: 404, MethodNotAllowed: 405, NotAcceptable: 406, ProxyAuthenticationRequired: 407, RequestTimeout: 408, Conflict: 409, Gone: 410, LengthRequired: 411, PreconditionFailed: 412, /** * @deprecated Use `ContentTooLarge` instead. */ PayloadTooLarge: 413, ContentTooLarge: 413, UriTooLong: 414, UnsupportedMediaType: 415, RangeNotSatisfiable: 416, ExpectationFailed: 417, ImATeapot: 418, MisdirectedRequest: 421, /** * @deprecated Use `UnprocessableContent` instead. */ UnprocessableEntity: 422, UnprocessableContent: 422, Locked: 423, FailedDependency: 424, TooEarly: 425, UpgradeRequired: 426, PreconditionRequired: 428, TooManyRequests: 429, RequestHeaderFieldsTooLarge: 431, UnavailableForLegalReasons: 451, InternalServerError: 500, NotImplemented: 501, BadGateway: 502, ServiceUnavailable: 503, GatewayTimeout: 504, HttpVersionNotSupported: 505, VariantAlsoNegotiates: 506, InsufficientStorage: 507, LoopDetected: 508, NotExtended: 510, NetworkAuthenticationRequired: 511, WebServerReturnsAnUnknownError: 520, WebServerIsDown: 521, ConnectionTimedOut: 522, OriginIsUnreachable: 523, TimeoutOccurred: 524, SslHandshakeFailed: 525, InvalidSslCertificate: 526, }; Object.entries(HttpStatusCode).forEach(([key, value]) => { if (HttpStatusCode[value] === undefined) { HttpStatusCode[value] = key; } }); export default HttpStatusCode; axios-axios-aa54a5e/lib/helpers/README.md000066400000000000000000000005371526660636600201610ustar00rootroot00000000000000# axios // helpers The modules found in `helpers/` should be generic modules that are _not_ specific to the domain logic of axios. These modules could theoretically be published to npm on their own and consumed by other modules or apps. Some examples of generic modules are things like: - Browser polyfills - Managing cookies - Parsing HTTP headers axios-axios-aa54a5e/lib/helpers/ZlibHeaderTransformStream.js000066400000000000000000000012601526660636600243130ustar00rootroot00000000000000'use strict'; import stream from 'stream'; class ZlibHeaderTransformStream extends stream.Transform { __transform(chunk, encoding, callback) { this.push(chunk); callback(); } _transform(chunk, encoding, callback) { if (chunk.length !== 0) { this._transform = this.__transform; // Add Default Compression headers if no zlib headers are present if (chunk[0] !== 120) { // Hex: 78 const header = Buffer.alloc(2); header[0] = 120; // Hex: 78 header[1] = 156; // Hex: 9C this.push(header, encoding); } } this.__transform(chunk, encoding, callback); } } export default ZlibHeaderTransformStream; axios-axios-aa54a5e/lib/helpers/bind.js000066400000000000000000000006741526660636600201560ustar00rootroot00000000000000'use strict'; /** * Create a bound version of a function with a specified `this` context * * @param {Function} fn - The function to bind * @param {*} thisArg - The value to be passed as the `this` parameter * @returns {Function} A new function that will call the original function with the specified `this` context */ export default function bind(fn, thisArg) { return function wrap() { return fn.apply(thisArg, arguments); }; } axios-axios-aa54a5e/lib/helpers/buildURL.js000066400000000000000000000034331526660636600207200ustar00rootroot00000000000000'use strict'; import utils from '../utils.js'; import AxiosURLSearchParams from './AxiosURLSearchParams.js'; /** * It replaces URL-encoded forms of `:`, `$`, `,`, and spaces with * their plain counterparts (`:`, `$`, `,`, `+`). * * @param {string} val The value to be encoded. * * @returns {string} The encoded value. */ export function encode(val) { return encodeURIComponent(val) .replace(/%3A/gi, ':') .replace(/%24/g, '$') .replace(/%2C/gi, ',') .replace(/%20/g, '+'); } /** * Build a URL by appending params to the end * * @param {string} url The base of the url (e.g., http://www.google.com) * @param {object} [params] The params to be appended * @param {?(object|Function)} options * * @returns {string} The formatted url */ export default function buildURL(url, params, options) { if (!params) { return url; } url = url || ''; const _options = utils.isFunction(options) ? { serialize: options, } : options; // Read serializer options pollution-safely: own properties and methods on a // class/template prototype are honored, but values injected onto a polluted // Object.prototype are ignored. const _encode = utils.getSafeProp(_options, 'encode') || encode; const serializeFn = utils.getSafeProp(_options, 'serialize'); let serializedParams; if (serializeFn) { serializedParams = serializeFn(params, _options); } else { serializedParams = utils.isURLSearchParams(params) ? params.toString() : new AxiosURLSearchParams(params, _options).toString(_encode); } if (serializedParams) { const hashmarkIndex = url.indexOf('#'); if (hashmarkIndex !== -1) { url = url.slice(0, hashmarkIndex); } url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams; } return url; } axios-axios-aa54a5e/lib/helpers/callbackify.js000066400000000000000000000006411526660636600215000ustar00rootroot00000000000000import utils from '../utils.js'; const callbackify = (fn, reducer) => { return utils.isAsyncFn(fn) ? function (...args) { const cb = args.pop(); fn.apply(this, args).then((value) => { try { reducer ? cb(null, ...reducer(value)) : cb(null, value); } catch (err) { cb(err); } }, cb); } : fn; }; export default callbackify; axios-axios-aa54a5e/lib/helpers/combineURLs.js000066400000000000000000000007541526660636600214230ustar00rootroot00000000000000'use strict'; /** * Creates a new URL by combining the specified URLs * * @param {string} baseURL The base URL * @param {string} relativeURL The relative URL * * @returns {string} The combined URL */ export default function combineURLs(baseURL, relativeURL) { if (!relativeURL) { return baseURL; } let end = baseURL.length; while (end > 0 && baseURL.charCodeAt(end - 1) === 47) { end--; } return baseURL.slice(0, end) + '/' + relativeURL.replace(/^\/+/, ''); } axios-axios-aa54a5e/lib/helpers/composeSignals.js000066400000000000000000000031541526660636600222240ustar00rootroot00000000000000import CanceledError from '../cancel/CanceledError.js'; import AxiosError from '../core/AxiosError.js'; import utils from '../utils.js'; const composeSignals = (signals, timeout) => { signals = signals ? signals.filter(Boolean) : []; if (!timeout && !signals.length) { return; } const controller = new AbortController(); let aborted = false; const onabort = function (reason) { if (!aborted) { aborted = true; unsubscribe(); const err = reason instanceof Error ? reason : this.reason; controller.abort( err instanceof AxiosError ? err : new CanceledError(err instanceof Error ? err.message : err) ); } }; let timer = timeout && setTimeout(() => { timer = null; onabort(new AxiosError(`timeout of ${timeout}ms exceeded`, AxiosError.ETIMEDOUT)); }, timeout); const unsubscribe = () => { if (!signals) { return; } timer && clearTimeout(timer); timer = null; signals.forEach((signal) => { const unsubscribeSignal = utils.getSafeProp(signal, 'unsubscribe'); utils.isFunction(unsubscribeSignal) ? unsubscribeSignal.call(signal, onabort) : signal.removeEventListener('abort', onabort); }); signals = null; }; signals.forEach((signal) => { if (aborted) { return; } if (signal.aborted) { onabort.call(signal); return; } signal.addEventListener('abort', onabort, { once: true }); }); const { signal } = controller; signal.unsubscribe = () => utils.asap(unsubscribe); return signal; }; export default composeSignals; axios-axios-aa54a5e/lib/helpers/cookies.js000066400000000000000000000054551526660636600207000ustar00rootroot00000000000000import utils from '../utils.js'; import platform from '../platform/index.js'; const decodeCookieValue = (value) => { try { return decodeURIComponent(value); } catch (e) { return value; } }; export default platform.hasStandardBrowserEnv ? // Standard browser envs support document.cookie { write(name, value, expires, path, domain, secure, sameSite) { if (typeof document === 'undefined') return; const cookie = [`${name}=${encodeURIComponent(value)}`]; if (utils.isNumber(expires)) { cookie.push(`expires=${new Date(expires).toUTCString()}`); } if (utils.isString(path)) { cookie.push(`path=${path}`); } if (utils.isString(domain)) { cookie.push(`domain=${domain}`); } if (secure === true) { cookie.push('secure'); } if (utils.isString(sameSite)) { cookie.push(`SameSite=${sameSite}`); } document.cookie = cookie.join('; '); }, read(name) { if (typeof document === 'undefined') return null; const cookieString = document.cookie; if (!cookieString) return null; // Match name=value by splitting on the semicolon separator instead of building a // RegExp from `name` — interpolating an unescaped string into a RegExp would let // metacharacters (e.g. `.+?` in an attacker-influenced cookie name) cause ReDoS or // match the wrong cookie. Browsers may serialize cookie pairs as either ";" or // "; ", so ignore optional whitespace before each cookie name. const cookies = cookieString.split(';'); for (let i = 0; i < cookies.length; i++) { const cookie = cookies[i].replace(/^\s+/, ''); const eq = cookie.indexOf('='); if (eq !== -1 && cookie.slice(0, eq) === name) { return decodeCookieValue(cookie.slice(eq + 1)); } } return null; }, async readAsync(name) { try { const store = typeof window !== 'undefined' && window.cookieStore; if (store && utils.isFunction(store.get)) { const cookie = await store.get(name); // Both APIs expose stored bytes; preserve Axios's existing URI decoding. return cookie ? decodeCookieValue(cookie.value) : null; } } catch (e) { // Restricted or unavailable storage retains the synchronous fallback. } return this.read(name); }, remove(name) { this.write(name, '', Date.now() - 86400000, '/'); }, } : // Non-standard browser env (web workers, react-native) lack needed support. { write() {}, read() { return null; }, async readAsync() { return null; }, remove() {}, }; axios-axios-aa54a5e/lib/helpers/deprecatedMethod.js000066400000000000000000000014111526660636600224710ustar00rootroot00000000000000'use strict'; /*eslint no-console:0*/ /** * Supply a warning to the developer that a method they are using * has been deprecated. * * @param {string} method The name of the deprecated method * @param {string} [instead] The alternate method to use if applicable * @param {string} [docs] The documentation URL to get further details * * @returns {void} */ export default function deprecatedMethod(method, instead, docs) { try { console.warn( 'DEPRECATED method `' + method + '`.' + (instead ? ' Use `' + instead + '` instead.' : '') + ' This method will be removed in a future release.' ); if (docs) { console.warn('For more information about usage see ' + docs); } } catch (e) { /* Ignore */ } } axios-axios-aa54a5e/lib/helpers/estimateDataURLDecodedBytes.js000066400000000000000000000132011526660636600244770ustar00rootroot00000000000000/** * Estimate data: URL byte lengths *without* allocating large buffers. * - Fetch percent-decodes a base64 body before decoding it. * - Node's Buffer.from(body, 'base64') sizes its backing allocation from the * percent-decoded body, including ignored characters and content after padding. * - Non-base64 data is percent-decoded and then encoded as UTF-8. */ const isHexDigit = (charCode) => (charCode >= 48 && charCode <= 57) || (charCode >= 65 && charCode <= 70) || (charCode >= 97 && charCode <= 102); const isPercentEncodedByte = (str, i, len) => i + 2 < len && isHexDigit(str.charCodeAt(i + 1)) && isHexDigit(str.charCodeAt(i + 2)); const hexValue = (charCode) => (charCode <= 57 ? charCode - 48 : (charCode & 0xdf) - 55); const isBase64Char = (charCode) => (charCode >= 65 && charCode <= 90) || // A-Z (charCode >= 97 && charCode <= 122) || // a-z (charCode >= 48 && charCode <= 57) || // 0-9 charCode === 43 || // + charCode === 47 || // / charCode === 45 || // - (base64url) charCode === 95; // _ (base64url) const isBase64Whitespace = (charCode) => charCode === 9 || charCode === 10 || charCode === 12 || charCode === 13 || charCode === 32; const base64Bytes = (significant) => { const groups = Math.floor(significant / 4); const remainder = significant % 4; return groups * 3 + (remainder === 2 ? 1 : remainder === 3 ? 2 : 0); }; // Buffer.byteLength(body, 'base64') uses the raw string length as an allocation // upper bound even when Buffer.from later ignores characters or stops at '='. const estimateBase64BufferAllocation = (body) => { const len = body.length; let padding = 0; if (len > 0 && body.charCodeAt(len - 1) === 61 /* '=' */) { padding++; if (len > 1 && body.charCodeAt(len - 2) === 61 /* '=' */) { padding++; } } return Math.floor(((len - padding) * 3) / 4); }; const estimatePercentDecodedBase64BufferAllocation = (body) => { // Ordinary Base64 needs only its length and trailing padding. if (body.indexOf('%') === -1) { return estimateBase64BufferAllocation(body); } let length = 0; let previous = 0; let last = 0; for (let i = 0, len = body.length; i < len; i++) { let code = body.charCodeAt(i); if (code === 37 /* '%' */ && isPercentEncodedByte(body, i, len)) { code = hexValue(body.charCodeAt(i + 1)) * 16 + hexValue(body.charCodeAt(i + 2)); i += 2; } length++; previous = last; last = code; } const padding = last === 61 ? (previous === 61 ? 2 : 1) : 0; return Math.floor(((length - padding) * 3) / 4); }; const estimatePercentDecodedBase64Bytes = (body) => { const len = body.length; let significant = 0; let padding = 0; let invalid = false; for (let i = 0; i < len; i++) { let code = body.charCodeAt(i); if (code === 37 /* '%' */ && isPercentEncodedByte(body, i, len)) { code = hexValue(body.charCodeAt(i + 1)) * 16 + hexValue(body.charCodeAt(i + 2)); i += 2; } if (isBase64Whitespace(code)) { continue; } if (code === 61 /* '=' */) { padding++; continue; } if (!isBase64Char(code) || padding > 0) { invalid = true; continue; } significant++; } // Fetch rejects malformed forgiving-base64 input. Returning the raw-size // allocation bound keeps that invalid input from becoming a pre-check bypass. if ( invalid || padding > 2 || (padding > 0 && (significant + padding) % 4 !== 0) || significant % 4 === 1 ) { return estimateBase64BufferAllocation(body); } return base64Bytes(significant); }; const estimateDataURLBytes = (url, estimateBase64, base64Pattern) => { if (!url || typeof url !== 'string') return 0; if (!/^data:/i.test(url)) return 0; const comma = url.indexOf(','); if (comma < 0) return 0; const meta = url.slice(5, comma); const body = url.slice(comma + 1); const isBase64 = base64Pattern.test(meta); if (isBase64) { return estimateBase64(body); } // Compute UTF-8 byte length directly from UTF-16 code units without allocating // a byte buffer (TextEncoder.encode would defeat the DoS guard on large bodies). // Valid %XX triplets count as one decoded byte; this matches the bytes that // decodeURIComponent(body) would produce before Buffer re-encodes the string. let bytes = 0; for (let i = 0, len = body.length; i < len; i++) { const c = body.charCodeAt(i); if (c === 37 /* '%' */ && isPercentEncodedByte(body, i, len)) { bytes += 1; i += 2; } else if (c < 0x80) { bytes += 1; } else if (c < 0x800) { bytes += 2; } else if (c >= 0xd800 && c <= 0xdbff && i + 1 < len) { const next = body.charCodeAt(i + 1); if (next >= 0xdc00 && next <= 0xdfff) { bytes += 4; i++; } else { bytes += 3; } } else { bytes += 3; } } return bytes; }; /** * Estimate the percent-decoded payload size used by Fetch data: URLs. * * @param {string} url * @returns {number} */ export default function estimateDataURLDecodedBytes(url) { // Fetch removes URL fragments before processing a data: URL. const fragmentIndex = typeof url === 'string' ? url.indexOf('#') : -1; return estimateDataURLBytes( fragmentIndex === -1 ? url : url.slice(0, fragmentIndex), estimatePercentDecodedBase64Bytes, /; *base64[\t\n\f\r ]*$/i ); } /** * Estimate the Buffer backing allocation used after Node's percent-decoding step. * * @param {string} url * @returns {number} */ export function estimateDataURLBufferAllocation(url) { // Match fromDataURI's case-insensitive protocol and encoding suffix. return estimateDataURLBytes(url, estimatePercentDecodedBase64BufferAllocation, /;base64$/i); } axios-axios-aa54a5e/lib/helpers/formDataToBlob.js000066400000000000000000000070531526660636600220770ustar00rootroot00000000000000import utils from '../utils.js'; const CRLF = '\r\n'; const normalizeLines = (value) => String(value).replace(/\r\n|\r|\n/g, CRLF); const escapeName = (value) => String(value).replace(/[\r\n"]/g, (char) => char === '\r' ? '%0D' : char === '\n' ? '%0A' : '%22' ); // Count UTF-8 bytes without allocating an encoded copy of a field. FormData // string values normalize line endings; Blob/File contents remain unchanged. const stringBytes = (value, normalize) => { let length = 0; for (let i = 0; i < value.length; i++) { const code = value.charCodeAt(i); if (normalize && (code === 13 || code === 10)) { if (code === 13 && value.charCodeAt(i + 1) === 10) i++; length += 2; } else if (code < 0x80) { length++; } else if (code < 0x800) { length += 2; } else if ( code >= 0xd800 && code <= 0xdbff && value.charCodeAt(i + 1) >= 0xdc00 && value.charCodeAt(i + 1) <= 0xdfff ) { length += 4; i++; } else { length += 3; } } return length; }; // Describe serialization failures independently of any request library. export class FormDataError extends TypeError { constructor(message, reason) { super(message); this.name = 'FormDataError'; this.reason = reason; } } // The caller supplies the boundary, Blob implementation, and limit error. export default function formDataToBlob(form, Blob, limit, limitError, boundary) { if (!utils.isFunction(Blob)) { throw new FormDataError('Blob is not supported', 'blob'); } if ( !utils.isString(boundary) || !/^[A-Za-z0-9'()+_,./:=? -]{1,70}$/.test(boundary) || / $/.test(boundary) ) { throw new FormDataError('Invalid multipart boundary', 'boundary'); } const footer = '--' + boundary + '--' + CRLF; let length = footer.length; const parts = []; const checkLength = (extra) => { if (length + extra > limit) throw limitError(); }; checkLength(0); for (const [name, value] of form) { const isString = utils.isString(value); const valueLength = isString ? stringBytes(value, true) : value.size; if (!Number.isFinite(valueLength) || valueLength < 0) { throw new FormDataError('Unsupported FormData part size', 'part'); } // Reject large values and names before building normalized header strings. checkLength( valueLength + stringBytes(name, true) + (!isString && value.name ? stringBytes(value.name, false) : 0) + (!isString && value.type ? stringBytes(value.type, false) : 0) ); let header = '--' + boundary + CRLF + 'Content-Disposition: form-data; name="' + escapeName(normalizeLines(name)) + '"'; if (!isString) { header += '; filename="' + escapeName(utils.isString(value.name) ? value.name : 'blob') + '"' + CRLF + 'Content-Type: ' + String(value.type || 'application/octet-stream').replace(/[\r\n]/g, ''); } header += CRLF + CRLF; const partLength = stringBytes(header, false) + valueLength + CRLF.length; checkLength(partLength); length += partLength; parts.push({ header, value, isString }); } // Native Blob composition retains existing Blob/File data. No file is read // and no complete multipart ArrayBuffer is created to determine its length. const blobParts = []; for (const part of parts) { blobParts.push(part.header, part.isString ? normalizeLines(part.value) : part.value, CRLF); } blobParts.push(footer); return new Blob(blobParts, { type: 'multipart/form-data; boundary=' + boundary }); } axios-axios-aa54a5e/lib/helpers/formDataToJSON.js000066400000000000000000000101011526660636600217560ustar00rootroot00000000000000'use strict'; import utils from '../utils.js'; import AxiosError from '../core/AxiosError.js'; import { DEFAULT_FORM_DATA_MAX_DEPTH } from './toFormData.js'; const MAX_DEPTH = DEFAULT_FORM_DATA_MAX_DEPTH; function throwIfDepthExceeded(index) { if (index > MAX_DEPTH) { throw new AxiosError( 'FormData field is too deeply nested (' + index + ' levels). Max depth: ' + MAX_DEPTH, AxiosError.ERR_FORM_DATA_DEPTH_EXCEEDED ); } } /** * It takes a string like `foo[x][y][z]` and returns an array like `['foo', 'x', 'y', 'z'] * * @param {string} name - The name of the property to get. * * @returns An array of strings. */ function parsePropPath(name) { // foo[x][y][z] -> ['foo', 'x', 'y', 'z'] // foo.x.y.z -> ['foo', 'x', 'y', 'z'] // A path is split on `.` and on `[...]` groups. A segment — whether written // in dot notation or captured inside brackets — may contain any character // except `.`, `[` and `]`, so a key like `user-name` or `user name` is kept // literal instead of being split (#5402). `.`, `[` and `]` keep their existing // meaning, e.g. `foo[bar.baz]` -> ['foo', 'bar', 'baz'] and `[]` is an array push. // Excluding `[` from the bracket group also makes the match fail fast at the // next `[`, so a malformed name cannot rescan to the end of the string from // every unmatched `[` — parsing stays linear in the length of the name. const path = []; const pattern = /[^.[\]]+|\[([^.[\]]*)]/g; let match; while ((match = pattern.exec(name)) !== null) { throwIfDepthExceeded(path.length); path.push(match[0] === '[]' ? '' : match[1] || match[0]); } return path; } /** * Convert an array to an object. * * @param {Array} arr - The array to convert to an object. * * @returns An object with the same keys and values as the array. */ function arrayToObject(arr) { const obj = {}; const keys = Object.keys(arr); let i; const len = keys.length; let key; for (i = 0; i < len; i++) { key = keys[i]; obj[key] = arr[key]; } return obj; } function normalizeArray(value) { if (!utils.isArray(value)) return value; const keys = Object.keys(value); return keys.length !== value.length || keys.some((key, index) => key !== String(index)) ? arrayToObject(value) : value; } function normalizeArrays(obj) { const pending = [obj]; while (pending.length) { const target = pending.pop(); Object.keys(target).forEach((key) => { const value = (target[key] = normalizeArray(target[key])); if (utils.isArray(value) || utils.isPlainObject(value)) { pending.push(value); } }); } return obj; } /** * It takes a FormData object and returns a JavaScript object * * @param {string} formData The FormData object to convert to JSON. * * @returns {Object | null} The converted object. */ function formDataToJSON(formData) { function buildPath(path, value, target, index) { throwIfDepthExceeded(index); let name = path[index++]; if (name === '__proto__') return true; const isNumericKey = Number.isFinite(+name); const isLast = index >= path.length; name = !name && utils.isArray(target) ? target.length : name; if (isLast) { if (utils.hasOwnProp(target, name)) { target[name] = normalizeArray(target[name]); target[name] = utils.isArray(target[name]) ? target[name].concat(value) : [target[name], value]; } else { target[name] = value; } return !isNumericKey; } if (!utils.hasOwnProp(target, name) || !utils.isObject(target[name])) { target[name] = []; } const result = buildPath(path, value, target[name], index); if (result && utils.isArray(target[name])) { target[name] = arrayToObject(target[name]); } return !isNumericKey; } if (utils.isFormData(formData) && utils.isFunction(formData.entries)) { const obj = {}; utils.forEachEntry(formData, (name, value) => { buildPath(parsePropPath(name), value, obj, 0); }); return normalizeArrays(obj); } return null; } export default formDataToJSON; axios-axios-aa54a5e/lib/helpers/formDataToStream.js000066400000000000000000000060341526660636600224520ustar00rootroot00000000000000import util from 'util'; import { Readable } from 'stream'; import utils from '../utils.js'; import readBlob from './readBlob.js'; import platform from '../platform/index.js'; const BOUNDARY_ALPHABET = platform.ALPHABET.ALPHA_DIGIT + '-_'; const textEncoder = typeof TextEncoder === 'function' ? new TextEncoder() : new util.TextEncoder(); const CRLF = '\r\n'; const CRLF_BYTES = textEncoder.encode(CRLF); const CRLF_BYTES_COUNT = 2; class FormDataPart { constructor(name, value) { const { escapeName } = this.constructor; const isStringValue = utils.isString(value); let headers = `Content-Disposition: form-data; name="${escapeName(name)}"${ !isStringValue && value.name ? `; filename="${escapeName(value.name)}"` : '' }${CRLF}`; if (isStringValue) { value = textEncoder.encode(String(value).replace(/\r?\n|\r\n?/g, CRLF)); } else { const safeType = String(value.type || 'application/octet-stream').replace(/[\r\n]/g, ''); headers += `Content-Type: ${safeType}${CRLF}`; } this.headers = textEncoder.encode(headers + CRLF); this.contentLength = isStringValue ? value.byteLength : value.size; this.size = this.headers.byteLength + this.contentLength + CRLF_BYTES_COUNT; this.name = name; this.value = value; } async *encode() { yield this.headers; const { value } = this; if (utils.isTypedArray(value)) { yield value; } else { yield* readBlob(value); } yield CRLF_BYTES; } static escapeName(name) { return String(name).replace( /[\r\n"]/g, (match) => ({ '\r': '%0D', '\n': '%0A', '"': '%22', })[match] ); } } const formDataToStream = (form, headersHandler, options) => { const { tag = 'form-data-boundary', size = 25, boundary = tag + '-' + platform.generateString(size, BOUNDARY_ALPHABET), } = options || {}; if (!utils.isFormData(form)) { throw new TypeError('FormData instance required'); } if (boundary.length < 1 || boundary.length > 70) { throw new Error('boundary must be 1-70 characters long'); } const boundaryBytes = textEncoder.encode('--' + boundary + CRLF); const footerBytes = textEncoder.encode('--' + boundary + '--' + CRLF); let contentLength = footerBytes.byteLength; const parts = Array.from(form.entries()).map(([name, value]) => { const part = new FormDataPart(name, value); contentLength += part.size; return part; }); contentLength += boundaryBytes.byteLength * parts.length; contentLength = utils.toFiniteNumber(contentLength); const computedHeaders = { 'Content-Type': `multipart/form-data; boundary=${boundary}`, }; if (Number.isFinite(contentLength)) { computedHeaders['Content-Length'] = contentLength; } headersHandler && headersHandler(computedHeaders); return Readable.from( (async function* () { for (const part of parts) { yield boundaryBytes; yield* part.encode(); } yield footerBytes; })() ); }; export default formDataToStream; axios-axios-aa54a5e/lib/helpers/fromDataURI.js000066400000000000000000000043221526660636600213510ustar00rootroot00000000000000'use strict'; import AxiosError from '../core/AxiosError.js'; import parseProtocol from './parseProtocol.js'; import platform from '../platform/index.js'; // RFC 2397: data:[][;base64], // mediatype = type/subtype followed by optional ;name=value parameters // The `i` flag makes the `;base64` token case-insensitive, matching the WHATWG // URL/Fetch data URL processor (and therefore the fetch adapter and browsers). // The remaining character classes do not depend on letter case. const DATA_URL_PATTERN = /^([^,;/]+\/[^,;/]+)?((?:;[^,;=]+=[^,;]+)*)(;base64)?,([\s\S]*)$/i; /** * Parse data uri to a Buffer or Blob * * @param {String} uri * @param {?Boolean} asBlob * @param {?Object} options * @param {?Function} options.Blob * * @returns {Buffer|Blob} */ export default function fromDataURI(uri, asBlob, options) { const _Blob = (options && options.Blob) || platform.classes.Blob; const protocol = parseProtocol(uri); if (asBlob === undefined && _Blob) { asBlob = true; } if (protocol === 'data') { uri = protocol.length ? uri.slice(protocol.length + 1) : uri; const match = DATA_URL_PATTERN.exec(uri); if (!match) { throw new AxiosError('Invalid URL', AxiosError.ERR_INVALID_URL); } const type = match[1]; const params = match[2]; const encoding = match[3] ? 'base64' : 'utf8'; const body = match[4]; // RFC 2397 section 3: default mediatype is text/plain;charset=US-ASCII // Bare `data:,` leaves mime undefined; Blob normalises that to "" per spec. let mime = ''; if (type) { mime = params ? type + params : type; } else if (params) { mime = 'text/plain' + params; } const buffer = encoding === 'base64' ? Buffer.from( body.replace(/%([\da-f]{2})/gi, (_, hex) => String.fromCharCode(parseInt(hex, 16))), 'base64' ) : Buffer.from(decodeURIComponent(body), encoding); if (asBlob) { if (!_Blob) { throw new AxiosError('Blob is not supported', AxiosError.ERR_NOT_SUPPORT); } return new _Blob([buffer], { type: mime }); } return buffer; } throw new AxiosError('Unsupported protocol ' + protocol, AxiosError.ERR_NOT_SUPPORT); } axios-axios-aa54a5e/lib/helpers/isAbsoluteURL.js000066400000000000000000000011511526660636600217260ustar00rootroot00000000000000'use strict'; /** * Determines whether the specified URL is absolute * * @param {string} url The URL to test * * @returns {boolean} True if the specified URL is absolute, otherwise false */ export default function isAbsoluteURL(url) { // A URL is considered absolute if it begins with "://" or "//" (protocol-relative URL). // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed // by any combination of letters, digits, plus, period, or hyphen. if (typeof url !== 'string') { return false; } return /^([a-z][a-z\d+\-.]*:)?\/\//i.test(url); } axios-axios-aa54a5e/lib/helpers/isAxiosError.js000066400000000000000000000005611526660636600216660ustar00rootroot00000000000000'use strict'; import utils from '../utils.js'; /** * Determines whether the payload is an error thrown by Axios * * @param {*} payload The value to test * * @returns {boolean} True if the payload is an error thrown by Axios, otherwise false */ export default function isAxiosError(payload) { return utils.isObject(payload) && payload.isAxiosError === true; } axios-axios-aa54a5e/lib/helpers/isURLSameOrigin.js000066400000000000000000000005421526660636600222100ustar00rootroot00000000000000import platform from '../platform/index.js'; export default platform.hasStandardBrowserEnv ? ((origin) => (url) => { url = new URL(url, document.baseURI || platform.origin); return ( origin.protocol === url.protocol && origin.host === url.host && origin.port === url.port ); })(new URL(platform.origin)) : () => true; axios-axios-aa54a5e/lib/helpers/normalizeURLForProtocolCheck.js000066400000000000000000000007311526660636600247460ustar00rootroot00000000000000'use strict'; const urlParserControlCharacters = /[\t\n\r]/g; /** * Match WHATWG URL preprocessing before checking a URL's protocol. * * @param {string} url * * @returns {string} */ export default function normalizeURLForProtocolCheck(url) { if (typeof url !== 'string') { return url; } let start = 0; while (start < url.length && url.charCodeAt(start) <= 0x20) { start++; } return url.slice(start).replace(urlParserControlCharacters, ''); } axios-axios-aa54a5e/lib/helpers/null.js000066400000000000000000000000701526660636600202020ustar00rootroot00000000000000// eslint-disable-next-line strict export default null; axios-axios-aa54a5e/lib/helpers/parseHeaders.js000066400000000000000000000030701526660636600216410ustar00rootroot00000000000000'use strict'; import utils from '../utils.js'; // RawAxiosHeaders whose duplicates are ignored by node // c.f. https://nodejs.org/api/http.html#http_message_headers const ignoreDuplicateOf = utils.toObjectSet([ 'age', 'authorization', 'content-length', 'content-type', 'etag', 'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since', 'last-modified', 'location', 'max-forwards', 'proxy-authorization', 'referer', 'retry-after', 'user-agent', ]); /** * Parse headers into an object * * ``` * Date: Wed, 27 Aug 2014 08:58:49 GMT * Content-Type: application/json * Connection: keep-alive * Transfer-Encoding: chunked * ``` * * @param {String} rawHeaders Headers needing to be parsed * * @returns {Object} Headers parsed into an object */ export default (rawHeaders) => { // Header names are data, including __proto__; avoid inherited setters. const parsed = Object.create(null); let key; let val; let i; rawHeaders && rawHeaders.split('\n').forEach(function parser(line) { i = line.indexOf(':'); key = line.substring(0, i).trim().toLowerCase(); val = line.substring(i + 1).trim(); const hasKey = utils.hasOwnProp(parsed, key); if (!key || (hasKey && utils.hasOwnProp(ignoreDuplicateOf, key))) { return; } if (key === 'set-cookie') { if (hasKey) { parsed[key].push(val); } else { parsed[key] = [val]; } } else { parsed[key] = hasKey ? parsed[key] + ', ' + val : val; } }); return parsed; }; axios-axios-aa54a5e/lib/helpers/parseProtocol.js000066400000000000000000000002471526660636600220720ustar00rootroot00000000000000'use strict'; export default function parseProtocol(url) { const match = /^([-+\w]{1,25}):(?:\/\/)?/.exec(url); return (match && match[1].toLowerCase()) || ''; } axios-axios-aa54a5e/lib/helpers/progressEventReducer.js000066400000000000000000000026511526660636600234170ustar00rootroot00000000000000import speedometer from './speedometer.js'; import throttle from './throttle.js'; import utils from '../utils.js'; export const progressEventReducer = (listener, isDownloadStream, freq = 3) => { let bytesNotified = 0; const _speedometer = speedometer(50, 250); return throttle((e) => { if (!e || !utils.isNumber(e.loaded)) { return; } const rawLoaded = e.loaded; const total = e.lengthComputable ? e.total : undefined; const loaded = Math.max(0, total != null ? Math.min(rawLoaded, total) : rawLoaded); const progressBytes = Math.max(0, loaded - bytesNotified); const rate = _speedometer(progressBytes); bytesNotified = Math.max(bytesNotified, loaded); const data = { loaded, total, progress: total ? loaded / total : undefined, bytes: progressBytes, rate: rate ? rate : undefined, estimated: rate && total ? (total - loaded) / rate : undefined, event: e, lengthComputable: total != null, [isDownloadStream ? 'download' : 'upload']: true, }; listener(data); }, freq); }; export const progressEventDecorator = (total, throttled) => { const lengthComputable = total != null; return [ (loaded) => throttled[0]({ lengthComputable, total, loaded, }), throttled[1], ]; }; export const asyncDecorator = (fn, scheduler = utils.asap) => (...args) => scheduler(() => fn(...args)); axios-axios-aa54a5e/lib/helpers/readBlob.js000066400000000000000000000005031526660636600207430ustar00rootroot00000000000000const { asyncIterator } = Symbol; const readBlob = async function* (blob) { if (blob.stream) { yield* blob.stream(); } else if (blob.arrayBuffer) { yield await blob.arrayBuffer(); } else if (blob[asyncIterator]) { yield* blob[asyncIterator](); } else { yield blob; } }; export default readBlob; axios-axios-aa54a5e/lib/helpers/resolveConfig.js000066400000000000000000000077301526660636600220470ustar00rootroot00000000000000import platform from '../platform/index.js'; import utils from '../utils.js'; import AxiosError from '../core/AxiosError.js'; import isURLSameOrigin from './isURLSameOrigin.js'; import cookies from './cookies.js'; import buildFullPath from '../core/buildFullPath.js'; import mergeConfig from '../core/mergeConfig.js'; import AxiosHeaders from '../core/AxiosHeaders.js'; import setFormDataHeaders from '../core/setFormDataHeaders.js'; import buildURL from './buildURL.js'; /** * Encode a UTF-8 string to a Latin-1 byte string for use with btoa(). * This is a modern replacement for the deprecated unescape(encodeURIComponent(str)) pattern. * * @param {string} str The string to encode * * @returns {string} UTF-8 bytes as a Latin-1 string */ const encodeUTF8 = (str) => encodeURIComponent(str).replace(/%([0-9A-F]{2})/gi, (_, hex) => String.fromCharCode(parseInt(hex, 16)) ); const readCookie = (name) => cookies.read(name); function resolveConfig(config, readXsrfCookie = readCookie) { const newConfig = mergeConfig({}, config); // Read only own properties to prevent prototype pollution gadgets // (e.g. Object.prototype.baseURL = 'https://evil.com'). const own = (key) => (utils.hasOwnProp(newConfig, key) ? newConfig[key] : undefined); const data = own('data'); let withXSRFToken = own('withXSRFToken'); const xsrfHeaderName = own('xsrfHeaderName'); const xsrfCookieName = own('xsrfCookieName'); let headers = own('headers'); const auth = own('auth'); const baseURL = own('baseURL'); const allowAbsoluteUrls = own('allowAbsoluteUrls'); const url = own('url'); newConfig.headers = headers = AxiosHeaders.from(headers); newConfig.url = buildURL( buildFullPath(baseURL, url, allowAbsoluteUrls, newConfig), own('params'), own('paramsSerializer') ); // HTTP basic authentication if (auth) { const username = utils.getSafeProp(auth, 'username') || ''; const password = utils.getSafeProp(auth, 'password') || ''; const transitional = own('transitional'); const encodeUsername = transitional && utils.hasOwnProp(transitional, 'encodeBasicAuthUsername') && transitional.encodeBasicAuthUsername === true; try { headers.set( 'Authorization', // Preserve legacy username bytes unless the caller opts in to UTF-8. 'Basic ' + btoa( (encodeUsername ? encodeUTF8(username) : username) + ':' + (password ? encodeUTF8(password) : '') ) ); } catch (e) { throw AxiosError.from(e, AxiosError.ERR_BAD_OPTION_VALUE, config); } } if (utils.isFormData(data)) { const getHeaders = utils.getSafeProp(data, 'getHeaders'); if ( platform.hasStandardBrowserEnv || platform.hasStandardBrowserWebWorkerEnv || utils.isReactNative(data) ) { headers.setContentType(undefined); // browser/web worker/RN handles it } else if (utils.isFunction(getHeaders)) { // Node.js FormData (like form-data package) setFormDataHeaders(headers, getHeaders.call(data), own('formDataHeaderPolicy')); } } // Add xsrf header // This is only done if running in a standard browser environment. // Specifically not if we're in a web worker, or react-native. if (platform.hasStandardBrowserEnv) { if (utils.isFunction(withXSRFToken)) { withXSRFToken = withXSRFToken(newConfig); } // Strict boolean check — prevents proto-pollution gadgets (e.g. Object.prototype.withXSRFToken = 1) // and misconfigurations (e.g. "false") from short-circuiting the same-origin check and leaking // the XSRF token cross-origin. const shouldSendXSRF = withXSRFToken === true || (withXSRFToken == null && isURLSameOrigin(newConfig.url)); if (shouldSendXSRF) { const xsrfValue = xsrfHeaderName && xsrfCookieName && readXsrfCookie(xsrfCookieName); if (xsrfValue) { headers.set(xsrfHeaderName, xsrfValue); } } } return newConfig; } export default resolveConfig; axios-axios-aa54a5e/lib/helpers/sanitizeHeaderValue.js000066400000000000000000000030651526660636600231730ustar00rootroot00000000000000'use strict'; import utils from '../utils.js'; function trimSPorHTAB(str) { let start = 0; let end = str.length; while (start < end) { const code = str.charCodeAt(start); if (code !== 0x09 && code !== 0x20) { break; } start += 1; } while (end > start) { const code = str.charCodeAt(end - 1); if (code !== 0x09 && code !== 0x20) { break; } end -= 1; } return start === 0 && end === str.length ? str : str.slice(start, end); } // The control-code ranges are intentional: header sanitization strips C0/DEL bytes. // eslint-disable-next-line no-control-regex const INVALID_UNICODE_HEADER_VALUE_CHARS = new RegExp('[\\u0000-\\u0008\\u000a-\\u001f\\u007f]+', 'g'); // eslint-disable-next-line no-control-regex const INVALID_BYTE_STRING_HEADER_VALUE_CHARS = new RegExp('[^\\u0009\\u0020-\\u007e\\u0080-\\u00ff]+', 'g'); function sanitizeValue(value, invalidChars) { if (utils.isArray(value)) { return value.map((item) => sanitizeValue(item, invalidChars)); } return trimSPorHTAB(String(value).replace(invalidChars, '')); } export const sanitizeHeaderValue = (value) => sanitizeValue(value, INVALID_UNICODE_HEADER_VALUE_CHARS); export const sanitizeByteStringHeaderValue = (value) => sanitizeValue(value, INVALID_BYTE_STRING_HEADER_VALUE_CHARS); export function toByteStringHeaderObject(headers) { const byteStringHeaders = Object.create(null); utils.forEach(headers.toJSON(), (value, header) => { byteStringHeaders[header] = sanitizeByteStringHeaderValue(value); }); return byteStringHeaders; } axios-axios-aa54a5e/lib/helpers/shouldBypassProxy.js000066400000000000000000000344771526660636600227740ustar00rootroot00000000000000import { domainToASCII } from 'url'; const LOOPBACK_HOSTNAMES = new Set(['localhost', '0.0.0.0']); const trimTrailingDots = (value) => { let end = value.length; while (end && value.charCodeAt(end - 1) === 46) { end--; } return end === value.length ? value : value.slice(0, end); }; const isIPv4Loopback = (host) => { const parts = host.split('.'); if (parts.length !== 4) return false; if (parts[0] !== '127') return false; return parts.every((p) => /^\d+$/.test(p) && Number(p) >= 0 && Number(p) <= 255); }; /** * Canonicalize an IPv4 address written in shorthand, octal, or hex form into * dotted-decimal. IPv6 addresses and non-IP strings are returned unchanged so * the existing IPv4-mapped IPv6 unmap path and the isLoopback path can still * see them. * * Shorthand expansion mirrors Node's URL parser: literal parts fill from the * left, the final part fills the remaining octets from the right with * zero-padding on the left. * 127.1 -> 127.0.0.1 * 127.0.1 -> 127.0.0.1 * 1.2.3 -> 1.2.0.3 * * Each octet is parsed with an explicit base: 16 for `0x`/`0X` prefix, 8 for * zero-prefixed multi-digit all-`0-7` parts, 10 otherwise. Zero-prefixed * decimal-looking parts that contain `8` or `9` are rejected to match Node's * URL parser, and the comparison layer falls through to non-bypass if either * side rejects the form (fail-safe). * * Returns the input unchanged on any parse failure, out-of-range octet, or * unusual shape (1-part, 5+ parts) so the comparison layer fails closed. */ const parseIPv4Octet = (text) => { if (/^0[xX][0-9a-fA-F]+$/.test(text)) { const n = parseInt(text.slice(2), 16); return Number.isFinite(n) ? n : null; } if (text.length > 1 && /^0[0-7]+$/.test(text)) { const n = parseInt(text, 8); return Number.isFinite(n) ? n : null; } if (text.length > 1 && /^0[0-9]+$/.test(text)) { return null; } if (/^[0-9]+$/.test(text)) { const n = parseInt(text, 10); return Number.isFinite(n) ? n : null; } return null; }; const normalizeIPAddress = (host) => { if (typeof host !== 'string' || !host || host.indexOf(':') !== -1) { return host; } let h = host; if (h.charAt(0) === '[' && h.charAt(h.length - 1) === ']') { h = h.slice(1, -1); } h = trimTrailingDots(h); // Allowed characters for any IPv4 shape: digits, dot, 'x', 'X', hex digits. if (!/^[0-9.xXa-fA-F]+$/.test(h)) return host; const parts = h.split('.'); // No part may be empty (e.g. "127..0.1" or "127.0.0."). Trailing dots are // already stripped above; this guards against the empty-middle case. if (parts.some((p) => p === '')) return host; if (parts.length === 4) { // Full IPv4 form: each part is an octet. const octets = parts.map(parseIPv4Octet); if (octets.some((n) => n === null || n < 0 || n > 255)) return host; return octets.join('.'); } if (parts.length > 4) { return host; } // Shorthand: 1..3 parts. Node's URL parser treats a 1-part input as a 32-bit // integer split into octets, which has surprising semantics (e.g. "127" -> // "0.0.0.127"). Reject 1-part inputs to keep the helper predictable: the // fail-safe returns the input unchanged and the comparison layer falls // through to non-bypass. if (parts.length === 1) return host; // 2..3 parts: literal parts fill from the left, tail fills remaining octets // from the right with zero-padding. const literalOctets = parts.slice(0, -1); const tail = parts[parts.length - 1]; const tailSlots = 4 - literalOctets.length; // Tail is parsed as a full IPv4 number (hex/octal/decimal) and packed // low-byte-right into the remaining octets, matching Node's URL parser. // e.g. 127.65535 (tail 0xFFFF into 3 slots) -> 127.0.255.255; // 127.0x00ff (tail 0xFF into 3 slots) -> 127.0.0.255; // 127.0.65535 (tail 0xFFFF into 2 slots) -> 127.0.255.255. const tailValue = parseIPv4Octet(tail); if (tailValue === null) return host; const maxTail = (1 << (8 * tailSlots)) - 1; if (tailValue < 0 || tailValue > maxTail) return host; const tailOctets = new Array(tailSlots).fill(0); for (let i = tailSlots - 1, v = tailValue; i >= 0; i--, v >>= 8) { tailOctets[i] = v & 0xff; } const literal = literalOctets.map(parseIPv4Octet); if (literal.some((n) => n === null || n < 0 || n > 255)) return host; return [...literal, ...tailOctets].join('.'); }; const isIPv6ZeroGroup = (group) => /^0{1,4}$/.test(group); // The unspecified address (IPv4 0.0.0.0 / IPv6 ::) resolves to the local host // for outbound connections, so treat it as loopback-equivalent for NO_PROXY // matching. 0.0.0.0 is covered by LOOPBACK_HOSTNAMES; this handles compressed // and full IPv6 all-zero forms so both families bypass symmetrically. const isIPv6Unspecified = (host) => { if (host === '::') return true; const compressionIndex = host.indexOf('::'); if (compressionIndex !== -1) { if (compressionIndex !== host.lastIndexOf('::')) return false; const left = host.slice(0, compressionIndex); const right = host.slice(compressionIndex + 2); const leftGroups = left ? left.split(':') : []; const rightGroups = right ? right.split(':') : []; const explicitGroups = leftGroups.length + rightGroups.length; return ( explicitGroups < 8 && leftGroups.every(isIPv6ZeroGroup) && rightGroups.every(isIPv6ZeroGroup) ); } const groups = host.split(':'); return groups.length === 8 && groups.every(isIPv6ZeroGroup); }; const isIPv6Loopback = (host) => { // Collapse all-zero groups: any form of ::1 / 0:0:...:0:1 // First, strip any leading "::" by normalising with Set lookup of common forms, // then fall back to structural check. if (host === '::1') return true; // Check IPv4-mapped IPv6 loopback: ::ffff: or ::ffff: // Node's URL parser normalises ::ffff:127.0.0.1 → ::ffff:7f00:1 const v4MappedDotted = host.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/i); if (v4MappedDotted) return isIPv4Loopback(v4MappedDotted[1]); const v4MappedHex = host.match(/^::ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i); if (v4MappedHex) { const high = parseInt(v4MappedHex[1], 16); // High 16 bits must start with 127 (0x7f) — i.e. 0x7f00..0x7fff return high >= 0x7f00 && high <= 0x7fff; } // Full-form ::1 variants: any number of zero groups followed by trailing 1 // e.g. 0:0:0:0:0:0:0:1, 0000:...:0001 const groups = host.split(':'); if (groups.length === 8) { for (let i = 0; i < 7; i++) { if (!/^0+$/.test(groups[i])) return false; } return /^0*1$/.test(groups[7]); } return false; }; const isLoopback = (host) => { if (!host) return false; if (LOOPBACK_HOSTNAMES.has(host)) return true; if (isIPv4Loopback(host)) return true; if (isIPv6Unspecified(host)) return true; return isIPv6Loopback(host); }; const DEFAULT_PORTS = { http: 80, https: 443, ws: 80, wss: 443, ftp: 21, }; const parseNoProxyEntry = (entry) => { let entryHost = entry; let entryPort = 0; if (entryHost.charAt(0) === '[') { const bracketIndex = entryHost.indexOf(']'); if (bracketIndex !== -1) { const host = entryHost.slice(1, bracketIndex); const rest = entryHost.slice(bracketIndex + 1); if (rest.charAt(0) === ':' && /^\d+$/.test(rest.slice(1))) { entryPort = Number.parseInt(rest.slice(1), 10); } return [host, entryPort]; } } const firstColon = entryHost.indexOf(':'); const lastColon = entryHost.lastIndexOf(':'); if ( firstColon !== -1 && firstColon === lastColon && /^\d+$/.test(entryHost.slice(lastColon + 1)) ) { entryPort = Number.parseInt(entryHost.slice(lastColon + 1), 10); entryHost = entryHost.slice(0, lastColon); } return [entryHost, entryPort]; }; // Convert IPv4-mapped IPv6 (::ffff:0:0/96 prefix) to IPv4 dotted form so both // sides of a NO_PROXY comparison see the same canonical address. Without this, // `NO_PROXY=192.168.1.5` would not match a request to `http://[::ffff:192.168.1.5]/` // (Node's URL parser normalises that to `[::ffff:c0a8:105]`), and vice-versa, // allowing the proxy-bypass policy to be circumvented by using the alternate // representation. Returns the input unchanged when not IPv4-mapped. const IPV4_MAPPED_DOTTED_RE = /^(?:::|(?:0{1,4}:){1,4}:|(?:0{1,4}:){5})ffff:(\d+\.\d+\.\d+\.\d+)$/i; const IPV4_MAPPED_HEX_RE = /^(?:::|(?:0{1,4}:){1,4}:|(?:0{1,4}:){5})ffff:([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i; const unmapIPv4MappedIPv6 = (host) => { if (typeof host !== 'string' || host.indexOf(':') === -1) return host; const dotted = host.match(IPV4_MAPPED_DOTTED_RE); if (dotted) return dotted[1]; const hex = host.match(IPV4_MAPPED_HEX_RE); if (hex) { const high = parseInt(hex[1], 16); const low = parseInt(hex[2], 16); return `${high >> 8}.${high & 0xff}.${low >> 8}.${low & 0xff}`; } return host; }; const IPV4_OCTET_RE = /^(?:0|[1-9]\d{0,2})$/; const ipv4ToBytes = (host) => { const parts = host.split('.'); return parts.length === 4 && parts.every((part) => IPV4_OCTET_RE.test(part) && Number(part) <= 255) ? parts.map(Number) : null; }; const IPV6_GROUP_RE = /^[0-9a-f]{1,4}$/i; const ipv6ToBytes = (host) => { const halves = host.split('::'); if (halves.length > 2) { return null; } const groups = halves[0] ? halves[0].split(':') : []; if (halves.length === 2) { const rear = halves[1] ? halves[1].split(':') : []; const missing = 8 - groups.length - rear.length; if (missing < 1) { return null; } groups.push(...new Array(missing).fill('0'), ...rear); } if (groups.length !== 8 || groups.some((group) => !IPV6_GROUP_RE.test(group))) { return null; } return groups.flatMap((group) => { const value = Number.parseInt(group, 16); return [(value >> 8) & 0xff, value & 0xff]; }); }; const ipToBytes = (host) => { if (typeof host !== 'string' || !host) { return null; } return host.indexOf(':') !== -1 ? ipv6ToBytes(host) : ipv4ToBytes(host); }; const normalizeNoProxyHost = (hostname) => { if (!hostname) { return hostname; } if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') { hostname = hostname.slice(1, -1); } let trimmed = trimTrailingDots(hostname); if (trimmed.indexOf(':') !== -1) { try { trimmed = new URL('http://[' + trimmed + ']/').hostname.slice(1, -1); } catch (_err) { return trimmed; } } else if (/[\u0080-\uffff]/.test(trimmed)) { const prefix = /^[*.]+/.exec(trimmed); const start = prefix ? prefix[0] : ''; const ascii = domainToASCII(trimmed.slice(start.length)); if (!ascii) return ''; trimmed = start + trimTrailingDots(ascii); } // IPv4 shorthand/octal/hex → dotted-decimal; helper is a no-op for inputs // containing ':' (IPv6 and IPv4-mapped IPv6) so we fall through to unmap. const ipv4 = normalizeIPAddress(trimmed); if (ipv4 !== trimmed) { return ipv4; } return unmapIPv4MappedIPv6(trimmed); }; const normalizeCidrBase = (input) => { let base = input; const startsBracket = base.charAt(0) === '['; const endsBracket = base.charAt(base.length - 1) === ']'; const hasBracket = base.includes('[') || base.includes(']'); if (startsBracket || endsBracket) { if (!startsBracket || !endsBracket) { return null; } base = base.slice(1, -1); if (base.indexOf(':') === -1 || base.includes('[') || base.includes(']')) { return null; } } else if (hasBracket) { return null; } if (!base || base.charAt(base.length - 1) === '.') { return null; } const wasIPv6 = base.indexOf(':') !== -1; if (wasIPv6) { try { base = new URL(`http://[${base}]/`).hostname.slice(1, -1); } catch (_err) { return null; } } else { base = normalizeIPAddress(base); if (!ipv4ToBytes(base)) { return null; } } return { normalized: unmapIPv4MappedIPv6(base), wasIPv6 }; }; const CIDR_ENTRY_RE = /^(.+)\/(0|[1-9]\d{0,2})$/; const parseCidrEntry = (entry) => { if (entry.indexOf('/') === -1) { return undefined; } const match = CIDR_ENTRY_RE.exec(entry); if (!match) { return null; } let prefix = Number(match[2]); const parsedBase = normalizeCidrBase(match[1]); if (!parsedBase) { return null; } const { normalized, wasIPv6 } = parsedBase; if (wasIPv6 && normalized.indexOf(':') === -1) { if (prefix < 96) { return null; } prefix -= 96; } const bytes = ipToBytes(normalized); if (!bytes || prefix > bytes.length * 8) { return null; } return { bytes, prefix }; }; const isInSubnet = (addressBytes, networkBytes, prefix) => { const fullBytes = prefix >> 3; for (let i = 0; i < fullBytes; i++) { if (addressBytes[i] !== networkBytes[i]) { return false; } } const remainingBits = prefix & 7; if (remainingBits) { const mask = (0xff << (8 - remainingBits)) & 0xff; if ((addressBytes[fullBytes] & mask) !== (networkBytes[fullBytes] & mask)) { return false; } } return true; }; export default function shouldBypassProxy(location) { let parsed; try { parsed = new URL(location); } catch (_err) { return false; } const noProxy = (process.env.no_proxy || process.env.NO_PROXY || '').toLowerCase(); if (!noProxy) { return false; } if (noProxy === '*') { return true; } const port = Number.parseInt(parsed.port, 10) || DEFAULT_PORTS[parsed.protocol.split(':', 1)[0]] || 0; const hostname = normalizeNoProxyHost(parsed.hostname.toLowerCase()); const hostnameBytes = ipToBytes(hostname); return noProxy.split(/[\s,]+/).some((entry) => { if (!entry) { return false; } if (entry === '*') { return true; } const cidr = parseCidrEntry(entry); if (cidr !== undefined) { return ( cidr !== null && !!hostnameBytes && hostnameBytes.length === cidr.bytes.length && isInSubnet(hostnameBytes, cidr.bytes, cidr.prefix) ); } let [entryHost, entryPort] = parseNoProxyEntry(entry); entryHost = normalizeNoProxyHost(entryHost); if (!entryHost) { return false; } if (entryPort && entryPort !== port) { return false; } if (entryHost.charAt(0) === '*') { entryHost = entryHost.slice(1); } if (entryHost.charAt(0) === '.') { return hostname.endsWith(entryHost); } return hostname === entryHost || (isLoopback(hostname) && isLoopback(entryHost)); }); } axios-axios-aa54a5e/lib/helpers/speedometer.js000066400000000000000000000021061526660636600215460ustar00rootroot00000000000000'use strict'; /** * Calculate data maxRate * @param {Number} [samplesCount= 10] * @param {Number} [min= 1000] * @returns {Function} */ function speedometer(samplesCount, min) { samplesCount = samplesCount || 10; const bytes = new Array(samplesCount); const timestamps = new Array(samplesCount); let head = 0; let tail = 0; let firstSampleTS; min = min !== undefined ? min : 1000; return function push(chunkLength) { const now = Date.now(); const startedAt = timestamps[tail]; if (!firstSampleTS) { firstSampleTS = now; } bytes[head] = chunkLength; timestamps[head] = now; let i = tail; let bytesCount = 0; while (i !== head) { bytesCount += bytes[i++]; i = i % samplesCount; } head = (head + 1) % samplesCount; if (head === tail) { tail = (tail + 1) % samplesCount; } if (now - firstSampleTS < min) { return; } const passed = startedAt && now - startedAt; return passed ? Math.round((bytesCount * 1000) / passed) : undefined; }; } export default speedometer; axios-axios-aa54a5e/lib/helpers/spread.js000066400000000000000000000010661526660636600205140ustar00rootroot00000000000000'use strict'; /** * Syntactic sugar for invoking a function and expanding an array for arguments. * * Common use case would be to use `Function.prototype.apply`. * * ```js * function f(x, y, z) {} * const args = [1, 2, 3]; * f.apply(null, args); * ``` * * With `spread` this example can be re-written. * * ```js * spread(function(x, y, z) {})([1, 2, 3]); * ``` * * @param {Function} callback * * @returns {Function} */ export default function spread(callback) { return function wrap(arr) { return callback.apply(null, arr); }; } axios-axios-aa54a5e/lib/helpers/throttle.js000066400000000000000000000016271526660636600211060ustar00rootroot00000000000000/** * Throttle decorator * @param {Function} fn * @param {Number} freq * @return {Array} */ function throttle(fn, freq) { let timestamp = 0; let threshold = 1000 / freq; let lastArgs; let timer; const invoke = (args, now = Date.now()) => { timestamp = now; lastArgs = null; if (timer) { clearTimeout(timer); timer = null; } fn(...args); }; const throttled = (...args) => { const now = Date.now(); const passed = now - timestamp; if (passed >= threshold) { invoke(args, now); } else { lastArgs = args; if (!timer) { timer = setTimeout(() => { timer = null; invoke(lastArgs); }, threshold - passed); } } }; const flush = () => lastArgs && invoke(lastArgs); const flushWith = (...args) => invoke(args); return [throttled, flush, flushWith]; } export default throttle; axios-axios-aa54a5e/lib/helpers/toFormData.js000066400000000000000000000170641526660636600213030ustar00rootroot00000000000000'use strict'; import utils from '../utils.js'; import AxiosError from '../core/AxiosError.js'; // temporary hotfix to avoid circular references until AxiosURLSearchParams is refactored import PlatformFormData from '../platform/node/classes/FormData.js'; import PlatformBuffer from '../platform/node/classes/Buffer.js'; // Default nesting limit shared with the inverse transform (formDataToJSON) so // the FormData <-> JSON round-trip stays symmetric. export const DEFAULT_FORM_DATA_MAX_DEPTH = 100; /** * Determines if the given thing is a array or js object. * * @param {string} thing - The object or array to be visited. * * @returns {boolean} */ function isVisitable(thing) { return utils.isPlainObject(thing) || utils.isArray(thing); } /** * It removes the brackets from the end of a string * * @param {string} key - The key of the parameter. * * @returns {string} the key without the brackets. */ function removeBrackets(key) { return utils.endsWith(key, '[]') ? key.slice(0, -2) : key; } /** * It takes a path, a key, and a boolean, and returns a string * * @param {string} path - The path to the current key. * @param {string} key - The key of the current object being iterated over. * @param {string} dots - If true, the key will be rendered with dots instead of brackets. * * @returns {string} The path to the current key. */ function renderKey(path, key, dots) { if (!path) return key; return path .concat(key) .map(function each(token, i) { // eslint-disable-next-line no-param-reassign token = removeBrackets(token); return !dots && i ? '[' + token + ']' : token; }) .join(dots ? '.' : ''); } /** * If the array is an array and none of its elements are visitable, then it's a flat array. * * @param {Array} arr - The array to check * * @returns {boolean} */ function isFlatArray(arr) { return utils.isArray(arr) && !arr.some(isVisitable); } const predicates = utils.toFlatObject(utils, {}, null, function filter(prop) { return /^is[A-Z]/.test(prop); }); /** * Convert a data object to FormData * * @param {Object} obj * @param {?Object} [formData] * @param {?Object} [options] * @param {Function} [options.visitor] * @param {Boolean} [options.metaTokens = true] * @param {Boolean} [options.dots = false] * @param {?Boolean} [options.indexes = false] * * @returns {Object} **/ /** * It converts an object into a FormData object * * @param {Object} obj - The object to convert to form data. * @param {string} formData - The FormData object to append to. * @param {Object} options * * @returns */ function toFormData(obj, formData, options) { if (!utils.isObject(obj)) { throw new TypeError('target must be an object'); } // eslint-disable-next-line no-param-reassign formData = formData || new (PlatformFormData || FormData)(); const option = (name, fallback) => { const value = utils.getSafeProp(options, name); return utils.isUndefined(value) ? fallback : value; }; const metaTokens = option('metaTokens', true); // eslint-disable-next-line no-use-before-define const visitor = option('visitor') || defaultVisitor; const dots = option('dots', false); const indexes = option('indexes', false); const _Blob = option('Blob') || (typeof Blob !== 'undefined' && Blob); const maxDepth = option('maxDepth', DEFAULT_FORM_DATA_MAX_DEPTH); const useBlob = _Blob && utils.isSpecCompliantForm(formData); const stack = []; if (!utils.isFunction(visitor)) { throw new TypeError('visitor must be a function'); } function convertValue(value) { if (value === null) return ''; if (utils.isDate(value)) { return value.toISOString(); } if (utils.isBoolean(value)) { return value.toString(); } if (!useBlob && utils.isBlob(value)) { throw new AxiosError('Blob is not supported. Use a Buffer instead.'); } if (utils.isArrayBuffer(value) || utils.isTypedArray(value)) { if (useBlob && typeof _Blob === 'function') { return new _Blob([value]); } if (PlatformBuffer && PlatformBuffer.isBufferAvailable()) { return PlatformBuffer.from(value); } throw new AxiosError( 'Blob is not supported. Use a Buffer instead.', AxiosError.ERR_NOT_SUPPORT ); } return value; } function throwIfMaxDepthExceeded(depth) { if (depth > maxDepth) { throw new AxiosError( 'Object is too deeply nested (' + depth + ' levels). Max depth: ' + maxDepth, AxiosError.ERR_FORM_DATA_DEPTH_EXCEEDED ); } } function stringifyWithDepthLimit(value, depth) { if (maxDepth === Infinity) { return JSON.stringify(value); } const ancestors = []; return JSON.stringify(value, function limitDepth(_key, currentValue) { if (!utils.isObject(currentValue)) { return currentValue; } while (ancestors.length && ancestors[ancestors.length - 1] !== this) { ancestors.pop(); } ancestors.push(currentValue); throwIfMaxDepthExceeded(depth + ancestors.length - 1); return currentValue; }); } /** * Default visitor. * * @param {*} value * @param {String|Number} key * @param {Array} path * @this {FormData} * * @returns {boolean} return true to visit the each prop of the value recursively */ function defaultVisitor(value, key, path) { let arr = value; if (utils.isReactNative(formData) && utils.isReactNativeBlob(value)) { formData.append(renderKey(path, key, dots), convertValue(value)); return false; } if (value && !path && typeof value === 'object') { if (utils.endsWith(key, '{}')) { // eslint-disable-next-line no-param-reassign key = metaTokens ? key : key.slice(0, -2); // eslint-disable-next-line no-param-reassign value = stringifyWithDepthLimit(value, 1); } else if ( (utils.isArray(value) && isFlatArray(value)) || ((utils.isFileList(value) || utils.endsWith(key, '[]')) && (arr = utils.toArray(value))) ) { // eslint-disable-next-line no-param-reassign key = removeBrackets(key); arr.forEach(function each(el, index) { !(utils.isUndefined(el) || el === null) && formData.append( // eslint-disable-next-line no-nested-ternary indexes === true ? renderKey([key], index, dots) : indexes === null ? key : key + '[]', convertValue(el) ); }); return false; } } if (isVisitable(value)) { return true; } formData.append(renderKey(path, key, dots), convertValue(value)); return false; } const exposedHelpers = Object.assign(predicates, { defaultVisitor, convertValue, isVisitable, }); function build(value, path, depth = 0) { if (utils.isUndefined(value)) return; throwIfMaxDepthExceeded(depth); if (stack.indexOf(value) !== -1) { throw new Error('Circular reference detected in ' + path.join('.')); } stack.push(value); utils.forEach(value, function each(el, key) { const result = !(utils.isUndefined(el) || el === null) && visitor.call(formData, el, utils.isString(key) ? key.trim() : key, path, exposedHelpers); if (result === true) { build(el, path ? path.concat(key) : [key], depth + 1); } }); stack.pop(); } if (!utils.isObject(obj)) { throw new TypeError('data must be an object'); } build(obj); return formData; } export default toFormData; axios-axios-aa54a5e/lib/helpers/toURLEncodedForm.js000066400000000000000000000010361526660636600223460ustar00rootroot00000000000000'use strict'; import utils from '../utils.js'; import toFormData from './toFormData.js'; import platform from '../platform/index.js'; export default function toURLEncodedForm(data, options) { return toFormData(data, new platform.classes.URLSearchParams(), { visitor: function (value, key, path, helpers) { if (platform.isNode && utils.isBuffer(value)) { this.append(key, value.toString('base64')); return false; } return helpers.defaultVisitor.apply(this, arguments); }, ...options, }); } axios-axios-aa54a5e/lib/helpers/trackStream.js000066400000000000000000000033441526660636600215170ustar00rootroot00000000000000export const streamChunk = function* (chunk, chunkSize) { let len = chunk.byteLength; if (!chunkSize || len < chunkSize) { yield chunk; return; } let pos = 0; let end; while (pos < len) { end = pos + chunkSize; yield chunk.slice(pos, end); pos = end; } }; export const readBytes = async function* (iterable, chunkSize) { for await (const chunk of readStream(iterable)) { yield* streamChunk(chunk, chunkSize); } }; const readStream = async function* (stream) { if (stream[Symbol.asyncIterator]) { yield* stream; return; } const reader = stream.getReader(); try { for (;;) { const { done, value } = await reader.read(); if (done) { break; } yield value; } } finally { await reader.cancel(); } }; export const trackStream = (stream, chunkSize, onProgress, onFinish) => { const iterator = readBytes(stream, chunkSize); let bytes = 0; let done; let _onFinish = (e) => { if (!done) { done = true; onFinish && onFinish(e); } }; return new ReadableStream( { async pull(controller) { try { const { done, value } = await iterator.next(); if (done) { _onFinish(); controller.close(); return; } let len = value.byteLength; if (onProgress) { let loadedBytes = (bytes += len); onProgress(loadedBytes); } controller.enqueue(new Uint8Array(value)); } catch (err) { _onFinish(err); throw err; } }, cancel(reason) { _onFinish(reason); return iterator.return(); }, }, { highWaterMark: 2, } ); }; axios-axios-aa54a5e/lib/helpers/validator.js000066400000000000000000000060131526660636600212200ustar00rootroot00000000000000'use strict'; import { VERSION } from '../env/data.js'; import AxiosError from '../core/AxiosError.js'; const validators = {}; // eslint-disable-next-line func-names ['object', 'boolean', 'number', 'function', 'string', 'symbol'].forEach((type, i) => { validators[type] = function validator(thing) { return typeof thing === type || 'a' + (i < 1 ? 'n ' : ' ') + type; }; }); const deprecatedWarnings = {}; /** * Transitional option validator * * @param {function|boolean?} validator - set to false if the transitional option has been removed * @param {string?} version - deprecated version / removed since version * @param {string?} message - some message with additional info * * @returns {function} */ validators.transitional = function transitional(validator, version, message) { function formatMessage(opt, desc) { return ( '[Axios v' + VERSION + "] Transitional option '" + opt + "'" + desc + (message ? '. ' + message : '') ); } // eslint-disable-next-line func-names return (value, opt, opts) => { if (validator === false) { throw new AxiosError( formatMessage(opt, ' has been removed' + (version ? ' in ' + version : '')), AxiosError.ERR_DEPRECATED ); } if (version && !deprecatedWarnings[opt]) { deprecatedWarnings[opt] = true; // eslint-disable-next-line no-console console.warn( formatMessage( opt, ' has been deprecated since v' + version + ' and will be removed in the near future' ) ); } return validator ? validator(value, opt, opts) : true; }; }; validators.spelling = function spelling(correctSpelling) { return (value, opt) => { // eslint-disable-next-line no-console console.warn(`${opt} is likely a misspelling of ${correctSpelling}`); return true; }; }; /** * Assert object's properties type * * @param {object} options * @param {object} schema * @param {boolean?} allowUnknown * * @returns {object} */ function assertOptions(options, schema, allowUnknown) { if (typeof options !== 'object' || options === null) { throw new AxiosError('options must be an object', AxiosError.ERR_BAD_OPTION_VALUE); } const keys = Object.keys(options); let i = keys.length; while (i-- > 0) { const opt = keys[i]; // Use hasOwnProperty so a polluted Object.prototype. cannot supply // a non-function validator and cause a TypeError. const validator = Object.prototype.hasOwnProperty.call(schema, opt) ? schema[opt] : undefined; if (validator) { const value = options[opt]; const result = value === undefined || validator(value, opt, options); if (result !== true) { throw new AxiosError( 'option ' + opt + ' must be ' + result, AxiosError.ERR_BAD_OPTION_VALUE ); } continue; } if (allowUnknown !== true) { throw new AxiosError('Unknown option ' + opt, AxiosError.ERR_BAD_OPTION); } } } export default { assertOptions, validators, }; axios-axios-aa54a5e/lib/platform/000077500000000000000000000000001526660636600170575ustar00rootroot00000000000000axios-axios-aa54a5e/lib/platform/browser/000077500000000000000000000000001526660636600205425ustar00rootroot00000000000000axios-axios-aa54a5e/lib/platform/browser/classes/000077500000000000000000000000001526660636600221775ustar00rootroot00000000000000axios-axios-aa54a5e/lib/platform/browser/classes/Blob.js000066400000000000000000000001111526660636600234040ustar00rootroot00000000000000'use strict'; export default typeof Blob !== 'undefined' ? Blob : null; axios-axios-aa54a5e/lib/platform/browser/classes/FormData.js000066400000000000000000000001211526660636600242240ustar00rootroot00000000000000'use strict'; export default typeof FormData !== 'undefined' ? FormData : null; axios-axios-aa54a5e/lib/platform/browser/classes/URLSearchParams.js000066400000000000000000000002741526660636600254740ustar00rootroot00000000000000'use strict'; import AxiosURLSearchParams from '../../../helpers/AxiosURLSearchParams.js'; export default typeof URLSearchParams !== 'undefined' ? URLSearchParams : AxiosURLSearchParams; axios-axios-aa54a5e/lib/platform/browser/index.js000066400000000000000000000004661526660636600222150ustar00rootroot00000000000000import URLSearchParams from './classes/URLSearchParams.js'; import FormData from './classes/FormData.js'; import Blob from './classes/Blob.js'; export default { isBrowser: true, classes: { URLSearchParams, FormData, Blob, }, protocols: ['http', 'https', 'file', 'blob', 'url', 'data'], }; axios-axios-aa54a5e/lib/platform/common/000077500000000000000000000000001526660636600203475ustar00rootroot00000000000000axios-axios-aa54a5e/lib/platform/common/utils.js000066400000000000000000000031031526660636600220420ustar00rootroot00000000000000const hasBrowserEnv = typeof window !== 'undefined' && typeof document !== 'undefined'; const _navigator = (typeof navigator === 'object' && navigator) || undefined; /** * Determine if we're running in a standard browser environment * * This allows axios to run in a web worker, and react-native. * Both environments support XMLHttpRequest, but not fully standard globals. * * web workers: * typeof window -> undefined * typeof document -> undefined * * react-native: * navigator.product -> 'ReactNative' * nativescript * navigator.product -> 'NativeScript' or 'NS' * * @returns {boolean} */ const hasStandardBrowserEnv = hasBrowserEnv && (!_navigator || ['ReactNative', 'NativeScript', 'NS'].indexOf(_navigator.product) < 0); /** * Determine if we're running in a standard browser webWorker environment * * Although the `isStandardBrowserEnv` method indicates that * `allows axios to run in a web worker`, the WebWorker will still be * filtered out due to its judgment standard * `typeof window !== 'undefined' && typeof document !== 'undefined'`. * This leads to a problem when axios post `FormData` in webWorker */ const hasStandardBrowserWebWorkerEnv = (() => { return ( typeof WorkerGlobalScope !== 'undefined' && // eslint-disable-next-line no-undef self instanceof WorkerGlobalScope && typeof self.importScripts === 'function' ); })(); const origin = (hasBrowserEnv && window.location.href) || 'http://localhost'; export { hasBrowserEnv, hasStandardBrowserWebWorkerEnv, hasStandardBrowserEnv, _navigator as navigator, origin, }; axios-axios-aa54a5e/lib/platform/index.js000066400000000000000000000002041526660636600205200ustar00rootroot00000000000000import platform from './node/index.js'; import * as utils from './common/utils.js'; export default { ...utils, ...platform, }; axios-axios-aa54a5e/lib/platform/node/000077500000000000000000000000001526660636600200045ustar00rootroot00000000000000axios-axios-aa54a5e/lib/platform/node/classes/000077500000000000000000000000001526660636600214415ustar00rootroot00000000000000axios-axios-aa54a5e/lib/platform/node/classes/Buffer.js000066400000000000000000000002361526660636600232110ustar00rootroot00000000000000'use strict'; export default { isBufferAvailable() { return typeof Buffer !== 'undefined'; }, from(value) { return Buffer.from(value); } }; axios-axios-aa54a5e/lib/platform/node/classes/FormData.js000066400000000000000000000000741526660636600234750ustar00rootroot00000000000000import FormData from 'form-data'; export default FormData; axios-axios-aa54a5e/lib/platform/node/classes/URLSearchParams.js000066400000000000000000000001121526660636600247250ustar00rootroot00000000000000'use strict'; import url from 'url'; export default url.URLSearchParams; axios-axios-aa54a5e/lib/platform/node/index.js000066400000000000000000000015051526660636600214520ustar00rootroot00000000000000import crypto from 'crypto'; import URLSearchParams from './classes/URLSearchParams.js'; import FormData from './classes/FormData.js'; const ALPHA = 'abcdefghijklmnopqrstuvwxyz'; const DIGIT = '0123456789'; const ALPHABET = { DIGIT, ALPHA, ALPHA_DIGIT: ALPHA + ALPHA.toUpperCase() + DIGIT, }; const generateString = (size = 16, alphabet = ALPHABET.ALPHA_DIGIT) => { let str = ''; const { length } = alphabet; const randomValues = new Uint32Array(size); crypto.randomFillSync(randomValues); for (let i = 0; i < size; i++) { str += alphabet[randomValues[i] % length]; } return str; }; export default { isNode: true, classes: { URLSearchParams, FormData, Blob: (typeof Blob !== 'undefined' && Blob) || null, }, ALPHABET, generateString, protocols: ['http', 'https', 'file', 'data'], }; axios-axios-aa54a5e/lib/utils.js000066400000000000000000001005071526660636600167340ustar00rootroot00000000000000'use strict'; import bind from './helpers/bind.js'; // utils is a library of generic helper functions non-specific to axios const { toString } = Object.prototype; const { getPrototypeOf } = Object; const { iterator, toStringTag } = Symbol; /* Creating a function that will check if an object has a property. */ const hasOwnProperty = ( ({ hasOwnProperty }) => (obj, prop) => hasOwnProperty.call(obj, prop) )(Object.prototype); const isUnsafeObjectKey = (prop) => typeof prop === 'string' && (prop === '__proto__' || prop === 'constructor' || prop === 'prototype'); /** * Determine whether an inherited object must be treated as a shared-prototype * boundary. Cross-realm Object.prototype objects cannot be distinguished * reliably from application-created null-prototype objects because their * properties are mutable, so all inherited terminal prototypes are excluded * as a fail-closed boundary. A null-prototype source still keeps its own * properties, as produced by mergeConfig and other safe materialization paths. * * @param {*} obj The object to inspect * @param {*} prototype The object's prototype * @param {boolean} source Whether obj is the original traversal source * * @returns {boolean} True when obj is a safe prototype traversal boundary */ const isPrototypeBoundary = (obj, prototype, source) => obj === Object.prototype || (!source && prototype === null); /** * Determine whether an object can retain its identity through code paths that * add, replace, and remove config properties without bypassing unsafe-key * filtering. Immutable objects, unsafe-key-bearing objects, and objects with * accessor or restricted data properties must be materialized instead. * * @param {*} obj The object to inspect * * @returns {boolean} True when every own property is safe and fully mutable */ const isSafeAndFullyMutable = (obj) => { if (!Object.isExtensible(obj)) { return false; } const props = Object.getOwnPropertyNames(obj); if (Object.getOwnPropertySymbols) { props.push(...Object.getOwnPropertySymbols(obj)); } return props.every((prop) => { if (isUnsafeObjectKey(prop)) { return false; } const descriptor = Object.getOwnPropertyDescriptor(obj, prop); return !!descriptor && descriptor.configurable && descriptor.writable === true; }); }; /** * Walk the prototype chain (excluding the source realm's Object.prototype) * looking for an own `prop`. This distinguishes genuine own/inherited members * — including class accessors and template prototypes — from members injected * via Object.prototype pollution (e.g. `Object.prototype.username = '...'`), * which live on Object.prototype itself and are therefore never matched. * * @param {*} thing The value whose chain to inspect * @param {string|symbol} prop The property key to look for * * @returns {boolean} True when `prop` is owned below Object.prototype */ const hasOwnInPrototypeChain = (thing, prop) => { let obj = thing; const seen = []; while (obj != null) { if (seen.indexOf(obj) !== -1) { return false; } seen.push(obj); const prototype = getPrototypeOf(obj); if (isPrototypeBoundary(obj, prototype, obj === thing)) { return false; } if (hasOwnProperty(obj, prop)) { return true; } obj = prototype; } return false; }; /** * Read `obj[prop]` only when it is safe from Object.prototype pollution. Own * properties and members inherited from a non-Object.prototype source (a class * instance or template object) are honored; a value reachable only through a * polluted Object.prototype is ignored and `undefined` is returned. * * @param {*} obj The source object * @param {string|symbol} prop The property key to read * * @returns {*} The resolved value, or undefined when unsafe/absent */ const getSafeProp = (obj, prop) => obj != null && hasOwnInPrototypeChain(obj, prop) ? obj[prop] : undefined; /** * Flatten an object and its application-defined prototype chain into a * null-prototype object. Members inherited only from the source realm's * Object.prototype are deliberately excluded, while class/template members * below that boundary are preserved. * * @param {*} thing The value to flatten * * @returns {*} A null-prototype copy, or the original value when it is already * structurally safe or is not an object */ const toSafeFlatObject = (thing) => { if (thing == null || (typeof thing !== 'object' && typeof thing !== 'function')) { return thing; } const sourcePrototype = getPrototypeOf(thing); if (sourcePrototype === null && isSafeAndFullyMutable(thing)) { return thing; } const result = Object.create(null); const merged = Object.create(null); const seen = []; let current = thing; while (current != null) { if (seen.indexOf(current) !== -1) { break; } seen.push(current); const prototype = current === thing ? sourcePrototype : getPrototypeOf(current); if (isPrototypeBoundary(current, prototype, current === thing)) { break; } const props = Object.getOwnPropertyNames(current); if (Object.getOwnPropertySymbols) { props.push(...Object.getOwnPropertySymbols(current)); } for (const prop of props) { if (isUnsafeObjectKey(prop)) { continue; } if (!hasOwnProperty(merged, prop)) { result[prop] = thing[prop]; merged[prop] = true; } } current = prototype; } return result; }; const kindOf = ((cache) => (thing) => { const str = toString.call(thing); return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase()); })(Object.create(null)); const kindOfTest = (type) => { type = type.toLowerCase(); return (thing) => kindOf(thing) === type; }; const typeOfTest = (type) => (thing) => typeof thing === type; /** * Determine if a value is a non-null object * * @param {Object} val The value to test * * @returns {boolean} True if value is an Array, otherwise false */ const { isArray } = Array; /** * Determine if a value is undefined * * @param {*} val The value to test * * @returns {boolean} True if the value is undefined, otherwise false */ const isUndefined = typeOfTest('undefined'); /** * Determine if a value is a Buffer * * @param {*} val The value to test * * @returns {boolean} True if value is a Buffer, otherwise false */ function isBuffer(val) { return ( val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor) && isFunction(val.constructor.isBuffer) && val.constructor.isBuffer(val) ); } /** * Determine if a value is an ArrayBuffer * * @param {*} val The value to test * * @returns {boolean} True if value is an ArrayBuffer, otherwise false */ const isArrayBuffer = kindOfTest('ArrayBuffer'); /** * Determine if a value is a view on an ArrayBuffer * * @param {*} val The value to test * * @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false */ function isArrayBufferView(val) { let result; if (typeof ArrayBuffer !== 'undefined' && ArrayBuffer.isView) { result = ArrayBuffer.isView(val); } else { result = val && val.buffer && isArrayBuffer(val.buffer); } return result; } /** * Determine if a value is a String * * @param {*} val The value to test * * @returns {boolean} True if value is a String, otherwise false */ const isString = typeOfTest('string'); /** * Determine if a value is a Function * * @param {*} val The value to test * @returns {boolean} True if value is a Function, otherwise false */ const isFunction = typeOfTest('function'); /** * Determine if a value is a Number * * @param {*} val The value to test * * @returns {boolean} True if value is a Number, otherwise false */ const isNumber = typeOfTest('number'); /** * Determine if a value is an Object * * @param {*} thing The value to test * * @returns {boolean} True if value is an Object, otherwise false */ const isObject = (thing) => thing !== null && typeof thing === 'object'; /** * Determine if a value is a Boolean * * @param {*} thing The value to test * @returns {boolean} True if value is a Boolean, otherwise false */ const isBoolean = (thing) => thing === true || thing === false; /** * Determine if a value is a plain Object * * @param {*} val The value to test * * @returns {boolean} True if value is a plain Object, otherwise false */ const isPlainObject = (val) => { if (!isObject(val)) { return false; } const prototype = getPrototypeOf(val); return ( (prototype === null || prototype === Object.prototype || getPrototypeOf(prototype) === null) && // Treat own Symbol.toStringTag or Symbol.iterator members as evidence the // value is tagged/iterable, ignoring members reachable only through shared // or terminal prototype boundaries. This is exactly what // hasOwnInPrototypeChain(val, prop) computes here: every prototype admitted // above (null, Object.prototype, or a terminal null-prototype template) is // a boundary, so the walk never inspects anything but `val`'s own members, // and stops before even those when `val` is itself the boundary // Object.prototype. Checking that inline avoids allocating the walker's // `seen` list twice per call on the mergeConfig / merge hot path. (isPrototypeBoundary(val, prototype, true) || (!hasOwnProperty(val, toStringTag) && !hasOwnProperty(val, iterator))) ); }; /** * Determine if a value is an empty object (safely handles Buffers) * * @param {*} val The value to test * * @returns {boolean} True if value is an empty object, otherwise false */ const isEmptyObject = (val) => { // Early return for non-objects or Buffers to prevent RangeError if (!isObject(val) || isBuffer(val)) { return false; } try { return Object.keys(val).length === 0 && Object.getPrototypeOf(val) === Object.prototype; } catch (e) { // Fallback for any other objects that might cause RangeError with Object.keys() return false; } }; /** * Determine if a value is a Date * * @param {*} val The value to test * * @returns {boolean} True if value is a Date, otherwise false */ const isDate = kindOfTest('Date'); /** * Determine if a value is a File * * @param {*} val The value to test * * @returns {boolean} True if value is a File, otherwise false */ const isFile = kindOfTest('File'); /** * Determine if a value is a React Native Blob * React Native "blob": an object with a `uri` attribute. Optionally, it can * also have a `name` and `type` attribute to specify filename and content type * * @see https://github.com/facebook/react-native/blob/26684cf3adf4094eb6c405d345a75bf8c7c0bf88/Libraries/Network/FormData.js#L68-L71 * * @param {*} value The value to test * * @returns {boolean} True if value is a React Native Blob, otherwise false */ const isReactNativeBlob = (value) => { return !!(value && typeof value.uri !== 'undefined'); }; /** * Determine if environment is React Native * ReactNative `FormData` has a non-standard `getParts()` method * * @param {*} formData The formData to test * * @returns {boolean} True if environment is React Native, otherwise false */ const isReactNative = (formData) => formData && typeof formData.getParts !== 'undefined'; /** * Determine if a value is a Blob * * @param {*} val The value to test * * @returns {boolean} True if value is a Blob, otherwise false */ const isBlob = kindOfTest('Blob'); /** * Determine if a value is a FileList * * @param {*} val The value to test * * @returns {boolean} True if value is a FileList, otherwise false */ const isFileList = kindOfTest('FileList'); const isSet = kindOfTest('Set'); /** * Determine if a value is a Stream * * @param {*} val The value to test * * @returns {boolean} True if value is a Stream, otherwise false */ const isStream = (val) => isObject(val) && isFunction(val.pipe); /** * Determine if a value is a FormData * * @param {*} thing The value to test * * @returns {boolean} True if value is an FormData, otherwise false */ function getGlobal() { if (typeof globalThis !== 'undefined') return globalThis; if (typeof self !== 'undefined') return self; if (typeof window !== 'undefined') return window; if (typeof global !== 'undefined') return global; return {}; } const G = getGlobal(); const FormDataCtor = typeof G.FormData !== 'undefined' ? G.FormData : undefined; const isFormData = (thing) => { if (!thing) return false; if (FormDataCtor && thing instanceof FormDataCtor) return true; // Reject plain objects inheriting directly from Object.prototype so prototype-pollution gadgets can't spoof FormData. const proto = getPrototypeOf(thing); if (!proto || proto === Object.prototype) return false; if (!isFunction(thing.append)) return false; const kind = kindOf(thing); return ( kind === 'formdata' || // detect form-data instance (kind === 'object' && isFunction(thing.toString) && thing.toString() === '[object FormData]') ); }; /** * Determine if a value is a URLSearchParams object * * @param {*} val The value to test * * @returns {boolean} True if value is a URLSearchParams object, otherwise false */ const isURLSearchParams = kindOfTest('URLSearchParams'); const [isReadableStream, isRequest, isResponse, isHeaders] = [ 'ReadableStream', 'Request', 'Response', 'Headers', ].map(kindOfTest); /** * Trim excess whitespace off the beginning and end of a string * * @param {String} str The String to trim * * @returns {String} The String freed of excess whitespace */ const trim = (str) => { return str.trim ? str.trim() : str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); }; /** * Iterate over an Array or an Object invoking a function for each item. * * If `obj` is an Array callback will be called passing * the value, index, and complete array for each item. * * If 'obj' is an Object callback will be called passing * the value, key, and complete object for each property. * * @param {Object|Array} obj The object to iterate * @param {Function} fn The callback to invoke for each item * * @param {Object} [options] * @param {Boolean} [options.allOwnKeys = false] * @returns {any} */ function forEach(obj, fn, { allOwnKeys = false } = {}) { // Don't bother if no value provided if (obj === null || typeof obj === 'undefined') { return; } let i; let l; // Force an array if not already something iterable if (typeof obj !== 'object') { /*eslint no-param-reassign:0*/ obj = [obj]; } if (isArray(obj)) { // Iterate over array values for (i = 0, l = obj.length; i < l; i++) { fn.call(null, obj[i], i, obj); } } else { // Buffer check if (isBuffer(obj)) { return; } // Iterate over object keys const keys = allOwnKeys ? Object.getOwnPropertyNames(obj) : Object.keys(obj); const len = keys.length; let key; for (i = 0; i < len; i++) { key = keys[i]; fn.call(null, obj[key], key, obj); } } } /** * Finds a key in an object, case-insensitive, returning the actual key name. * Returns null if the object is a Buffer or if no match is found. * * @param {Object} obj - The object to search. * @param {string} key - The key to find (case-insensitive). * @returns {?string} The actual key name if found, otherwise null. */ function findKey(obj, key) { if (isBuffer(obj)) { return null; } key = key.toLowerCase(); const keys = Object.keys(obj); let i = keys.length; let _key; while (i-- > 0) { _key = keys[i]; if (key === _key.toLowerCase()) { return _key; } } return null; } const _global = (() => { /*eslint no-undef:0*/ if (typeof globalThis !== 'undefined') return globalThis; return typeof self !== 'undefined' ? self : typeof window !== 'undefined' ? window : global; })(); const isContextDefined = (context) => !isUndefined(context) && context !== _global; /** * Accepts varargs expecting each argument to be an object, then * immutably merges the properties of each object and returns result. * * When multiple objects contain the same key the later object in * the arguments list will take precedence. * * Example: * * ```js * const result = merge({foo: 123}, {foo: 456}); * console.log(result.foo); // outputs 456 * ``` * * @param {Object} obj1 Object to merge * * @returns {Object} Result of all merge properties */ function merge(...objs) { const { caseless, skipUndefined } = (isContextDefined(this) && this) || {}; const result = {}; // Caseless lookup only applies to string keys — symbol keys are // identity-matched. Every string key that reaches `result` passes through // this index first, so it can never hold two case variants of one key and a // lowercase -> key map is exactly what findKey(result, key) would rescan // Object.keys(result) for on each assignment. const lowerCaseKeys = caseless ? Object.create(null) : null; const assignValue = (val, key) => { // Skip dangerous property names to prevent prototype pollution if (key === '__proto__' || key === 'constructor' || key === 'prototype') { return; } let targetKey = key; if (lowerCaseKeys && typeof key === 'string') { const lowerKey = key.toLowerCase(); const knownKey = lowerCaseKeys[lowerKey]; // A key recorded but absent from `result` was skipped by skipUndefined; // the later spelling wins then, as it does for findKey. if (knownKey !== undefined && hasOwnProperty(result, knownKey)) { targetKey = knownKey; } else { lowerCaseKeys[lowerKey] = key; } } // Read via own-prop only — a bare `result[targetKey]` walks the prototype // chain, so a polluted Object.prototype value could surface here and get // copied into the merged result. const existing = hasOwnProperty(result, targetKey) ? result[targetKey] : undefined; if (isPlainObject(existing) && isPlainObject(val)) { result[targetKey] = merge(existing, val); } else if (isPlainObject(val)) { result[targetKey] = merge({}, val); } else if (isArray(val)) { result[targetKey] = val.slice(); } else if (!skipUndefined || !isUndefined(val)) { result[targetKey] = val; } }; for (let i = 0, l = objs.length; i < l; i++) { const source = objs[i]; if (!source || isBuffer(source)) { continue; } forEach(source, assignValue); if (typeof source !== 'object' || isArray(source) || !Object.getOwnPropertySymbols) { continue; } const symbols = Object.getOwnPropertySymbols(source); for (let j = 0; j < symbols.length; j++) { const symbol = symbols[j]; if (propertyIsEnumerable.call(source, symbol)) { assignValue(source[symbol], symbol); } } } return result; } /** * Extends object a by mutably adding to it the properties of object b. * * @param {Object} a The object to be extended * @param {Object} b The object to copy properties from * @param {Object} thisArg The object to bind function to * * @param {Object} [options] * @param {Boolean} [options.allOwnKeys] * @returns {Object} The resulting value of object a */ const extend = (a, b, thisArg, { allOwnKeys } = {}) => { forEach( b, (val, key) => { if (thisArg && isFunction(val)) { Object.defineProperty(a, key, { // Null-proto descriptor so a polluted Object.prototype.get cannot // hijack defineProperty's accessor-vs-data resolution. __proto__: null, value: bind(val, thisArg), writable: true, enumerable: true, configurable: true, }); } else { Object.defineProperty(a, key, { __proto__: null, value: val, writable: true, enumerable: true, configurable: true, }); } }, { allOwnKeys } ); return a; }; /** * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM) * * @param {string} content with BOM * * @returns {string} content value without BOM */ const stripBOM = (content) => { if (content.charCodeAt(0) === 0xfeff) { content = content.slice(1); } return content; }; /** * Inherit the prototype methods from one constructor into another * @param {function} constructor * @param {function} superConstructor * @param {object} [props] * @param {object} [descriptors] * * @returns {void} */ const inherits = (constructor, superConstructor, props, descriptors) => { constructor.prototype = Object.create(superConstructor.prototype, descriptors); Object.defineProperty(constructor.prototype, 'constructor', { __proto__: null, value: constructor, writable: true, enumerable: false, configurable: true, }); Object.defineProperty(constructor, 'super', { __proto__: null, value: superConstructor.prototype, }); props && Object.assign(constructor.prototype, props); }; /** * Resolve object with deep prototype chain to a flat object * @param {Object} sourceObj source object * @param {Object} [destObj] * @param {Function|Boolean} [filter] * @param {Function} [propFilter] * * @returns {Object} */ const toFlatObject = (sourceObj, destObj, filter, propFilter) => { let props; let i; let prop; const merged = {}; destObj = destObj || {}; // eslint-disable-next-line no-eq-null,eqeqeq if (sourceObj == null) return destObj; do { props = Object.getOwnPropertyNames(sourceObj); i = props.length; while (i-- > 0) { prop = props[i]; if ((!propFilter || propFilter(prop, sourceObj, destObj)) && !merged[prop]) { destObj[prop] = sourceObj[prop]; merged[prop] = true; } } sourceObj = filter !== false && getPrototypeOf(sourceObj); } while (sourceObj && (!filter || filter(sourceObj, destObj)) && sourceObj !== Object.prototype); return destObj; }; /** * Determines whether a string ends with the characters of a specified string * * @param {String} str * @param {String} searchString * @param {Number} [position= 0] * * @returns {boolean} */ const endsWith = (str, searchString, position) => { str = String(str); if (position === undefined || position > str.length) { position = str.length; } position -= searchString.length; const lastIndex = str.indexOf(searchString, position); return lastIndex !== -1 && lastIndex === position; }; /** * Returns new array from array like object or null if failed * * @param {*} [thing] * * @returns {?Array} */ const toArray = (thing) => { if (!thing) return null; if (isArray(thing)) return thing; let i = thing.length; if (!Number.isSafeInteger(i) || i < 0 || i > 0xffffffff) return null; // Bound the copy by actual indexed properties, including hidden and inherited // members of array-like collections, rather than by the declared length alone. const indexes = new Set(); const seen = new Set(); let source = thing; while (source != null && indexes.size < i) { if (seen.has(source)) return null; seen.add(source); const prototype = getPrototypeOf(source); if (isPrototypeBoundary(source, prototype, source === thing)) break; Object.getOwnPropertyNames(source).forEach((key) => { const index = +key; if ( Number.isInteger(index) && index >= 0 && index < i && String(index) === key && hasOwnProperty(source, key) ) { indexes.add(index); } }); source = prototype; } if (indexes.size !== i) return null; const arr = new Array(i); while (i-- > 0) { arr[i] = thing[i]; } return arr; }; /** * Checking if the Uint8Array exists and if it does, it returns a function that checks if the * thing passed in is an instance of Uint8Array * * @param {TypedArray} * * @returns {Array} */ // eslint-disable-next-line func-names const isTypedArray = ((TypedArray) => { // eslint-disable-next-line func-names return (thing) => { return TypedArray && thing instanceof TypedArray; }; })(typeof Uint8Array !== 'undefined' && getPrototypeOf(Uint8Array)); /** * For each entry in the object, call the function with the key and value. * * @param {Object} obj - The object to iterate over. * @param {Function} fn - The function to call for each entry. * * @returns {void} */ const forEachEntry = (obj, fn) => { const generator = obj && obj[iterator]; const _iterator = generator.call(obj); let result; while ((result = _iterator.next()) && !result.done) { const pair = result.value; fn.call(obj, pair[0], pair[1]); } }; /** * It takes a regular expression and a string, and returns an array of all the matches * * @param {string} regExp - The regular expression to match against. * @param {string} str - The string to search. * * @returns {Array} */ const matchAll = (regExp, str) => { let matches; const arr = []; while ((matches = regExp.exec(str)) !== null) { arr.push(matches); } return arr; }; /* Checking if the kindOfTest function returns true when passed an HTMLFormElement. */ const isHTMLForm = kindOfTest('HTMLFormElement'); const toCamelCase = (str) => { return str.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g, function replacer(m, p1, p2) { return p1.toUpperCase() + p2; }); }; const { propertyIsEnumerable } = Object.prototype; /** * Determine if a value is a RegExp object * * @param {*} val The value to test * * @returns {boolean} True if value is a RegExp object, otherwise false */ const isRegExp = kindOfTest('RegExp'); const reduceDescriptors = (obj, reducer) => { const descriptors = Object.getOwnPropertyDescriptors(obj); const reducedDescriptors = {}; forEach(descriptors, (descriptor, name) => { let ret; if ((ret = reducer(descriptor, name, obj)) !== false) { reducedDescriptors[name] = ret || descriptor; } }); Object.defineProperties(obj, reducedDescriptors); }; /** * Makes all methods read-only * @param {Object} obj */ const freezeMethods = (obj) => { reduceDescriptors(obj, (descriptor, name) => { // skip restricted props in strict mode if (isFunction(obj) && ['arguments', 'caller', 'callee'].includes(name)) { return false; } const value = obj[name]; if (!isFunction(value)) return; descriptor.enumerable = false; if ('writable' in descriptor) { descriptor.writable = false; return; } if (!descriptor.set) { descriptor.set = () => { throw Error("Can not rewrite read-only method '" + name + "'"); }; } }); }; /** * Converts an array or a delimited string into an object set with values as keys and true as values. * Useful for fast membership checks. * * @param {Array|string} arrayOrString - The array or string to convert. * @param {string} delimiter - The delimiter to use if input is a string. * @returns {Object} An object with keys from the array or string, values set to true. */ const toObjectSet = (arrayOrString, delimiter) => { const obj = {}; const define = (arr) => { arr.forEach((value) => { obj[value] = true; }); }; isArray(arrayOrString) ? define(arrayOrString) : define(String(arrayOrString).split(delimiter)); return obj; }; const noop = () => {}; const toFiniteNumber = (value, defaultValue) => { return value != null && Number.isFinite((value = +value)) ? value : defaultValue; }; /** * If the thing is a FormData object, return true, otherwise return false. * * @param {unknown} thing - The thing to check. * * @returns {boolean} */ function isSpecCompliantForm(thing) { return !!( thing && isFunction(thing.append) && thing[toStringTag] === 'FormData' && thing[iterator] ); } /** * Recursively converts an object to a JSON-compatible object, handling circular references and Buffers. * * @param {Object} obj - The object to convert. * @returns {Object} The JSON-compatible object. */ const toJSONObject = (obj) => { const visited = new WeakSet(); const visit = (source) => { if (isObject(source)) { if (visited.has(source)) { return; } //Buffer check if (isBuffer(source)) { return source; } if (!('toJSON' in source)) { // add-on descent / delete-on-ascent: preserves path semantics, so DAG nodes serialise at every occurrence (see #7230). visited.add(source); let target; if (isSet(source)) { target = []; for (const value of source) { const reducedValue = visit(value); !isUndefined(reducedValue) && target.push(reducedValue); } } else { target = isArray(source) ? [] : {}; forEach(source, (value, key) => { const reducedValue = visit(value); !isUndefined(reducedValue) && (target[key] = reducedValue); }); } visited.delete(source); return target; } } return source; }; return visit(obj); }; /** * Determines if a value is an async function. * * @param {*} thing - The value to test. * @returns {boolean} True if value is an async function, otherwise false. */ const isAsyncFn = kindOfTest('AsyncFunction'); /** * Determines if a value is thenable (has then and catch methods). * * @param {*} thing - The value to test. * @returns {boolean} True if value is thenable, otherwise false. */ const isThenable = (thing) => thing && (isObject(thing) || isFunction(thing)) && isFunction(thing.then) && isFunction(thing.catch); // original code // https://github.com/DigitalBrainJS/AxiosPromise/blob/16deab13710ec09779922131f3fa5954320f83ab/lib/utils.js#L11-L34 /** * Provides a cross-platform setImmediate implementation. * Uses native setImmediate if available, otherwise falls back to postMessage or setTimeout. * * @param {boolean} setImmediateSupported - Whether setImmediate is supported. * @param {boolean} postMessageSupported - Whether postMessage is supported. * @returns {Function} A function to schedule a callback asynchronously. */ const _setImmediate = ((setImmediateSupported, postMessageSupported) => { if (setImmediateSupported) { return setImmediate; } return postMessageSupported ? ((token, callbacks) => { _global.addEventListener( 'message', ({ source, data }) => { if (source === _global && data === token) { callbacks.length && callbacks.shift()(); } }, false ); return (cb) => { callbacks.push(cb); _global.postMessage(token, '*'); }; })(`axios@${Math.random()}`, []) : (cb) => setTimeout(cb); })(typeof setImmediate === 'function', isFunction(_global.postMessage)); /** * Schedules a microtask or asynchronous callback as soon as possible. * Uses queueMicrotask if available, otherwise falls back to process.nextTick or _setImmediate. * * @type {Function} */ const asap = typeof queueMicrotask !== 'undefined' ? queueMicrotask.bind(_global) : (typeof process !== 'undefined' && process.nextTick) || _setImmediate; // ********************* const isIterable = (thing) => thing != null && isFunction(thing[iterator]); /** * Determine if a value is iterable via an iterator that is NOT sourced solely * from a polluted Object.prototype. Use this instead of `isIterable` whenever * the iterable comes from untrusted input (e.g. user-supplied header sources), * so `Object.prototype[Symbol.iterator] = ...` cannot turn an ordinary object * into an attacker-controlled entries iterator. * * @param {*} thing The value to test * * @returns {boolean} True if value has a non-polluted iterator */ const isSafeIterable = (thing) => thing != null && hasOwnInPrototypeChain(thing, iterator) && isIterable(thing); export default { isArray, isArrayBuffer, isBuffer, isFormData, isArrayBufferView, isString, isNumber, isBoolean, isObject, isPlainObject, isEmptyObject, isReadableStream, isRequest, isResponse, isHeaders, isUndefined, isDate, isFile, isReactNativeBlob, isReactNative, isBlob, isRegExp, isFunction, isStream, isURLSearchParams, isTypedArray, isFileList, forEach, merge, extend, trim, stripBOM, inherits, toFlatObject, kindOf, kindOfTest, endsWith, toArray, forEachEntry, matchAll, isHTMLForm, hasOwnProperty, hasOwnProp: hasOwnProperty, // an alias to avoid ESLint no-prototype-builtins detection hasOwnInPrototypeChain, getSafeProp, toSafeFlatObject, reduceDescriptors, freezeMethods, toObjectSet, toCamelCase, noop, toFiniteNumber, findKey, global: _global, isContextDefined, isSpecCompliantForm, toJSONObject, isAsyncFn, isThenable, setImmediate: _setImmediate, asap, isIterable, isSafeIterable, }; axios-axios-aa54a5e/package-lock.json000066400000000000000000012406141526660636600177110ustar00rootroot00000000000000{ "name": "axios", "version": "1.21.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "axios", "version": "1.21.0", "license": "MIT", "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.6", "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" }, "devDependencies": { "@babel/core": "^7.29.0", "@babel/preset-env": "^7.29.5", "@commitlint/cli": "^21.2.0", "@commitlint/config-conventional": "^21.2.0", "@eslint/js": "^10.0.1", "@rollup/plugin-alias": "^6.0.0", "@rollup/plugin-babel": "^7.1.0", "@rollup/plugin-commonjs": "^29.0.3", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.3", "@rollup/plugin-terser": "^1.0.0", "@vitest/browser": "^4.1.9", "@vitest/browser-playwright": "^4.1.9", "abortcontroller-polyfill": "^1.7.8", "acorn": "^8.17.0", "body-parser": "^2.3.0", "cross-env": "^10.1.0", "dev-null": "^0.1.1", "eslint": "^10.6.0", "express": "^5.2.1", "formdata-node": "^6.0.3", "formidable": "^3.5.4", "fs-extra": "^11.3.6", "get-stream": "^9.0.1", "globals": "^17.7.0", "gulp": "^5.0.1", "husky": "^9.1.7", "lint-staged": "^17.0.8", "minimist": "^1.2.8", "multer": "^2.2.0", "playwright": "^1.61.1", "prettier": "^3.9.4", "rollup": "^4.60.4", "rollup-plugin-bundle-size": "^1.0.3", "selfsigned": "^5.5.0", "stream-throttle": "^0.1.3", "typescript": "^5.9.3", "vitest": "^4.1.9" } }, "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/generator": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "@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-annotate-as-pure": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.29.7" }, "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-create-class-features-plugin": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-member-expression-to-functions": "^7.29.7", "@babel/helper-optimise-call-expression": "^7.29.7", "@babel/helper-replace-supers": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", "@babel/traverse": "^7.29.7", "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-create-regexp-features-plugin": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.29.7.tgz", "integrity": "sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-define-polyfill-provider": { "version": "0.6.8", "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", "debug": "^4.4.3", "lodash.debounce": "^4.0.8", "resolve": "^1.22.11" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "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-member-expression-to-functions": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", "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-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-optimise-call-expression": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.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-remap-async-to-generator": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.29.7.tgz", "integrity": "sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-wrap-function": "^7.29.7", "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-replace-supers": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-member-expression-to-functions": "^7.29.7", "@babel/helper-optimise-call-expression": "^7.29.7", "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" }, "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/helper-wrap-function": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.29.7.tgz", "integrity": "sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.29.7", "@babel/traverse": "^7.29.7", "@babel/types": "^7.29.7" }, "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.7", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.29.7" }, "bin": { "parser": "bin/babel-parser.js" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.29.7.tgz", "integrity": "sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.29.7.tgz", "integrity": "sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.29.7.tgz", "integrity": "sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.7.tgz", "integrity": "sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.29.7.tgz", "integrity": "sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", "@babel/plugin-transform-optional-chaining": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.13.0" } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.29.7.tgz", "integrity": "sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-import-assertions": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.29.7.tgz", "integrity": "sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==", "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-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-unicode-sets-regex": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/plugin-transform-arrow-functions": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz", "integrity": "sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==", "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-transform-async-generator-functions": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.7.tgz", "integrity": "sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-remap-async-to-generator": "^7.29.7", "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-async-to-generator": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.29.7.tgz", "integrity": "sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-remap-async-to-generator": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.29.7.tgz", "integrity": "sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==", "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-transform-block-scoping": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.7.tgz", "integrity": "sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==", "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-transform-class-properties": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.29.7.tgz", "integrity": "sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-class-static-block": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.29.7.tgz", "integrity": "sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.12.0" } }, "node_modules/@babel/plugin-transform-classes": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.7.tgz", "integrity": "sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-replace-supers": "^7.29.7", "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-computed-properties": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.29.7.tgz", "integrity": "sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/template": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-destructuring": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.29.7.tgz", "integrity": "sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-dotall-regex": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.29.7.tgz", "integrity": "sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-duplicate-keys": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.29.7.tgz", "integrity": "sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==", "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-transform-duplicate-named-capturing-groups-regex": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.7.tgz", "integrity": "sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/plugin-transform-dynamic-import": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.29.7.tgz", "integrity": "sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==", "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-transform-explicit-resource-management": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.29.7.tgz", "integrity": "sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/plugin-transform-destructuring": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.29.7.tgz", "integrity": "sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==", "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-transform-export-namespace-from": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.29.7.tgz", "integrity": "sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==", "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-transform-for-of": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.29.7.tgz", "integrity": "sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-function-name": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.29.7.tgz", "integrity": "sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-json-strings": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.29.7.tgz", "integrity": "sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==", "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-transform-literals": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.29.7.tgz", "integrity": "sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==", "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-transform-logical-assignment-operators": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.29.7.tgz", "integrity": "sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==", "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-transform-member-expression-literals": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.29.7.tgz", "integrity": "sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==", "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-transform-modules-amd": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.29.7.tgz", "integrity": "sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-modules-commonjs": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz", "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-modules-systemjs": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.7.tgz", "integrity": "sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.29.7", "@babel/helper-plugin-utils": "^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-0" } }, "node_modules/@babel/plugin-transform-modules-umd": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.29.7.tgz", "integrity": "sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.7.tgz", "integrity": "sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/plugin-transform-new-target": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.29.7.tgz", "integrity": "sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==", "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-transform-nullish-coalescing-operator": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.29.7.tgz", "integrity": "sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==", "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-transform-numeric-separator": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.29.7.tgz", "integrity": "sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==", "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-transform-object-rest-spread": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.29.7.tgz", "integrity": "sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/plugin-transform-destructuring": "^7.29.7", "@babel/plugin-transform-parameters": "^7.29.7", "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-object-super": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.29.7.tgz", "integrity": "sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-replace-supers": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.29.7.tgz", "integrity": "sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==", "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-transform-optional-chaining": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.29.7.tgz", "integrity": "sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-parameters": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.29.7.tgz", "integrity": "sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==", "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-transform-private-methods": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.29.7.tgz", "integrity": "sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-private-property-in-object": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.29.7.tgz", "integrity": "sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.29.7", "@babel/helper-create-class-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-property-literals": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.29.7.tgz", "integrity": "sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==", "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-transform-regenerator": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.7.tgz", "integrity": "sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==", "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-transform-regexp-modifiers": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.29.7.tgz", "integrity": "sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/plugin-transform-reserved-words": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.29.7.tgz", "integrity": "sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==", "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-transform-shorthand-properties": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.29.7.tgz", "integrity": "sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==", "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-transform-spread": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.7.tgz", "integrity": "sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-sticky-regex": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.29.7.tgz", "integrity": "sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==", "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-transform-template-literals": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.29.7.tgz", "integrity": "sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==", "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-transform-typeof-symbol": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.29.7.tgz", "integrity": "sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==", "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-transform-unicode-escapes": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.29.7.tgz", "integrity": "sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==", "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-transform-unicode-property-regex": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.29.7.tgz", "integrity": "sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-unicode-regex": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.29.7.tgz", "integrity": "sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.29.7.tgz", "integrity": "sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/preset-env": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.7.tgz", "integrity": "sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==", "dev": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.29.7", "@babel/helper-compilation-targets": "^7.29.7", "@babel/helper-plugin-utils": "^7.29.7", "@babel/helper-validator-option": "^7.29.7", "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.29.7", "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.29.7", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.29.7", "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.7", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.29.7", "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.29.7", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-import-assertions": "^7.29.7", "@babel/plugin-syntax-import-attributes": "^7.29.7", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.29.7", "@babel/plugin-transform-async-generator-functions": "^7.29.7", "@babel/plugin-transform-async-to-generator": "^7.29.7", "@babel/plugin-transform-block-scoped-functions": "^7.29.7", "@babel/plugin-transform-block-scoping": "^7.29.7", "@babel/plugin-transform-class-properties": "^7.29.7", "@babel/plugin-transform-class-static-block": "^7.29.7", "@babel/plugin-transform-classes": "^7.29.7", "@babel/plugin-transform-computed-properties": "^7.29.7", "@babel/plugin-transform-destructuring": "^7.29.7", "@babel/plugin-transform-dotall-regex": "^7.29.7", "@babel/plugin-transform-duplicate-keys": "^7.29.7", "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.7", "@babel/plugin-transform-dynamic-import": "^7.29.7", "@babel/plugin-transform-explicit-resource-management": "^7.29.7", "@babel/plugin-transform-exponentiation-operator": "^7.29.7", "@babel/plugin-transform-export-namespace-from": "^7.29.7", "@babel/plugin-transform-for-of": "^7.29.7", "@babel/plugin-transform-function-name": "^7.29.7", "@babel/plugin-transform-json-strings": "^7.29.7", "@babel/plugin-transform-literals": "^7.29.7", "@babel/plugin-transform-logical-assignment-operators": "^7.29.7", "@babel/plugin-transform-member-expression-literals": "^7.29.7", "@babel/plugin-transform-modules-amd": "^7.29.7", "@babel/plugin-transform-modules-commonjs": "^7.29.7", "@babel/plugin-transform-modules-systemjs": "^7.29.7", "@babel/plugin-transform-modules-umd": "^7.29.7", "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.7", "@babel/plugin-transform-new-target": "^7.29.7", "@babel/plugin-transform-nullish-coalescing-operator": "^7.29.7", "@babel/plugin-transform-numeric-separator": "^7.29.7", "@babel/plugin-transform-object-rest-spread": "^7.29.7", "@babel/plugin-transform-object-super": "^7.29.7", "@babel/plugin-transform-optional-catch-binding": "^7.29.7", "@babel/plugin-transform-optional-chaining": "^7.29.7", "@babel/plugin-transform-parameters": "^7.29.7", "@babel/plugin-transform-private-methods": "^7.29.7", "@babel/plugin-transform-private-property-in-object": "^7.29.7", "@babel/plugin-transform-property-literals": "^7.29.7", "@babel/plugin-transform-regenerator": "^7.29.7", "@babel/plugin-transform-regexp-modifiers": "^7.29.7", "@babel/plugin-transform-reserved-words": "^7.29.7", "@babel/plugin-transform-shorthand-properties": "^7.29.7", "@babel/plugin-transform-spread": "^7.29.7", "@babel/plugin-transform-sticky-regex": "^7.29.7", "@babel/plugin-transform-template-literals": "^7.29.7", "@babel/plugin-transform-typeof-symbol": "^7.29.7", "@babel/plugin-transform-unicode-escapes": "^7.29.7", "@babel/plugin-transform-unicode-property-regex": "^7.29.7", "@babel/plugin-transform-unicode-regex": "^7.29.7", "@babel/plugin-transform-unicode-sets-regex": "^7.29.7", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.15", "babel-plugin-polyfill-corejs3": "^0.14.0", "babel-plugin-polyfill-regenerator": "^0.6.6", "core-js-compat": "^3.48.0", "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/preset-modules": { "version": "0.1.6-no-external-plugins", "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", "esutils": "^2.0.2" }, "peerDependencies": { "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.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.7", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", "@babel/helper-globals": "^7.29.7", "@babel/parser": "^7.29.7", "@babel/template": "^7.29.7", "@babel/types": "^7.29.7", "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "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/@blazediff/core": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/@blazediff/core/-/core-1.9.1.tgz", "integrity": "sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==", "dev": true, "license": "MIT" }, "node_modules/@cacheable/memory": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.2.0.tgz", "integrity": "sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==", "dev": true, "license": "MIT", "dependencies": { "@cacheable/utils": "^2.5.0", "@keyv/bigmap": "^1.3.1", "hookified": "^1.15.1", "keyv": "^5.6.0" } }, "node_modules/@cacheable/utils": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.5.0.tgz", "integrity": "sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==", "dev": true, "license": "MIT", "dependencies": { "hashery": "^1.5.1", "keyv": "^5.6.0" } }, "node_modules/@commitlint/cli": { "version": "21.2.3", "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-21.2.3.tgz", "integrity": "sha512-5yOX6IRjcrIReCxpyAd04hgI/yhUMpVBJ/ouwFV0wk9yx5+fYRDWZWJ5h55Tbedgfol+5m2mU5nqzbB8g3v0vQ==", "dev": true, "license": "MIT", "dependencies": { "@commitlint/config-conventional": "^21.2.3", "@commitlint/format": "^21.2.3", "@commitlint/lint": "^21.2.3", "@commitlint/load": "^21.2.3", "@commitlint/read": "^21.2.3", "@commitlint/types": "^21.2.3", "tinyexec": "^1.0.0", "yargs": "^18.0.0" }, "bin": { "commitlint": "cli.js" }, "engines": { "node": ">=22.12.0" } }, "node_modules/@commitlint/config-conventional": { "version": "21.2.3", "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-21.2.3.tgz", "integrity": "sha512-EP2n5DN2GwRWNVrLX0Jx3mpQlW5PfFi9HnD9OJ8hN/H0xFqpCFyHeGDhg+0P8/21iMMcdkcQgbNqmVklebqYsQ==", "dev": true, "license": "MIT", "dependencies": { "@commitlint/types": "^21.2.3", "conventional-changelog-conventionalcommits": "^10.0.0" }, "engines": { "node": ">=22.12.0" } }, "node_modules/@commitlint/config-validator": { "version": "21.2.3", "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-21.2.3.tgz", "integrity": "sha512-JQ13v0GafSM4fBYX8UowSy++07Was20auq0NcTdnzSEq6/05Cq5GBUMIfKkqjzyBwC9sJFmPpKWgLLhH7cygkw==", "dev": true, "license": "MIT", "dependencies": { "@commitlint/types": "^21.2.3", "ajv": "^8.11.0" }, "engines": { "node": ">=22.12.0" } }, "node_modules/@commitlint/ensure": { "version": "21.2.3", "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-21.2.3.tgz", "integrity": "sha512-gEjNKgRocbODI5/sMx2Qc9XOf2/zFTNV2aEK1QZ4gKDKxDC/VR7us3QtIX1C5d5xxHxx0I/tEbYbdSP1Am2LwA==", "dev": true, "license": "MIT", "dependencies": { "@commitlint/types": "^21.2.3", "es-toolkit": "^1.46.0" }, "engines": { "node": ">=22.12.0" } }, "node_modules/@commitlint/execute-rule": { "version": "21.0.1", "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-21.0.1.tgz", "integrity": "sha512-RifH+FmImozKBE6mozhF4K3r2RRKP7SMi/Q/zLCmExtp5e05lhHOUYqGBlFBAGNHaZxU/WYw1XuugYK9jQzqnA==", "dev": true, "license": "MIT", "engines": { "node": ">=22.12.0" } }, "node_modules/@commitlint/format": { "version": "21.2.3", "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-21.2.3.tgz", "integrity": "sha512-KKwCjXYqA8r295uhLnulDXL9yekdXmj2yp2Q0XsBPhxQ2iPuvRE8F43j7g7yexo69wTdmUQmFz15jpderluzUQ==", "dev": true, "license": "MIT", "dependencies": { "@commitlint/types": "^21.2.3", "picocolors": "^1.1.1" }, "engines": { "node": ">=22.12.0" } }, "node_modules/@commitlint/is-ignored": { "version": "21.2.3", "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-21.2.3.tgz", "integrity": "sha512-Ng4nRj/LBAOvc+GQu7bsFW4/fL89LvZ14OOC/ERLbUpnG6QBwWRzY/1oXGwPs3C8YHWjJObin4kLYo2YeEn70w==", "dev": true, "license": "MIT", "dependencies": { "@commitlint/types": "^21.2.3", "semver": "^7.6.0" }, "engines": { "node": ">=22.12.0" } }, "node_modules/@commitlint/is-ignored/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/@commitlint/lint": { "version": "21.2.3", "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-21.2.3.tgz", "integrity": "sha512-lUZcSuVLLEDkhge9ERk8Rp+FXQtrUhCK+h328BFnzKXrTuXGdqiYMEpTlNWdZaOrjyMhMJaz354ZaWEGJxcLeg==", "dev": true, "license": "MIT", "dependencies": { "@commitlint/is-ignored": "^21.2.3", "@commitlint/parse": "^21.2.3", "@commitlint/rules": "^21.2.3", "@commitlint/types": "^21.2.3" }, "engines": { "node": ">=22.12.0" } }, "node_modules/@commitlint/load": { "version": "21.2.3", "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-21.2.3.tgz", "integrity": "sha512-cDrL8lOo23Lk7knGzQdjbFcq7z2JgvjwV6PhbESVvEc+Fe21MeLjPKBJi8nKjg7j+jh79CVbVSUbZc104thXRw==", "dev": true, "license": "MIT", "dependencies": { "@commitlint/config-validator": "^21.2.3", "@commitlint/execute-rule": "^21.0.1", "@commitlint/resolve-extends": "^21.2.3", "@commitlint/types": "^21.2.3", "cosmiconfig": "^9.0.1", "cosmiconfig-typescript-loader": "^6.1.0", "es-toolkit": "^1.46.0", "is-plain-obj": "^4.1.0", "picocolors": "^1.1.1" }, "engines": { "node": ">=22.12.0" } }, "node_modules/@commitlint/message": { "version": "21.2.0", "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-21.2.0.tgz", "integrity": "sha512-YxGoiXD/HXNXLJPrQwE5poXa+XH0CBEm+mdvbHQP0g6MV/dmJyUFCzPNzZbxL93GvZ70TmtTK0Z0/IBpAqHv8g==", "dev": true, "license": "MIT", "engines": { "node": ">=22.12.0" } }, "node_modules/@commitlint/parse": { "version": "21.2.3", "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-21.2.3.tgz", "integrity": "sha512-Jt61QpF1xA88damCELe8I0FlDZPSpXNc8IhN7z5kZ0EcDYPjdZ+ZHPvsSXl69r2MIlLnNKeHhOn2cmVpziFE9w==", "dev": true, "license": "MIT", "dependencies": { "@commitlint/types": "^21.2.3", "conventional-changelog-angular": "^9.0.0", "conventional-commits-parser": "^7.0.0" }, "engines": { "node": ">=22.12.0" } }, "node_modules/@commitlint/read": { "version": "21.2.3", "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-21.2.3.tgz", "integrity": "sha512-szAepuLA1vTTr7fuudEg7jYyVHudf+FH+vptU91bhxE1WnE8TKrfTAi44HVllz8XpI8Pvp4GjR/iP5Nshgh+og==", "dev": true, "license": "MIT", "dependencies": { "@commitlint/top-level": "^21.2.0", "@commitlint/types": "^21.2.3", "@conventional-changelog/git-client": "^3.0.0", "tinyexec": "^1.0.0" }, "engines": { "node": ">=22.12.0" } }, "node_modules/@commitlint/resolve-extends": { "version": "21.2.3", "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-21.2.3.tgz", "integrity": "sha512-bqJdmAtXgAQel+l02uIsF7rHgKgx1AGZdGBjam7uK4XW54ZAQtB6XAIUXezYprQiCcRMpBh4fHs5JodXK4mVZA==", "dev": true, "license": "MIT", "dependencies": { "@commitlint/config-validator": "^21.2.3", "@commitlint/types": "^21.2.3", "es-toolkit": "^1.46.0", "global-directory": "^5.0.0", "resolve-from": "^5.0.0" }, "engines": { "node": ">=22.12.0" } }, "node_modules/@commitlint/rules": { "version": "21.2.3", "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-21.2.3.tgz", "integrity": "sha512-5+YnI/LuTx3JDl9GjqfP2z9x96M8KKDrd+4jOel/T0L1BB3oAmBlSN8xppf63QsudiVEgR8C0gRoAMrPVrms6g==", "dev": true, "license": "MIT", "dependencies": { "@commitlint/ensure": "^21.2.3", "@commitlint/message": "^21.2.0", "@commitlint/to-lines": "^21.0.1", "@commitlint/types": "^21.2.3" }, "engines": { "node": ">=22.12.0" } }, "node_modules/@commitlint/to-lines": { "version": "21.0.1", "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-21.0.1.tgz", "integrity": "sha512-bd1BFII7p1EQZre9Kaj+kKaMFP3cFCdt21K7DItVux9XP5WjLgJ0/Uy1pJJh9aPwVJ6SKg62PxqlZaHI8hQAXw==", "dev": true, "license": "MIT", "engines": { "node": ">=22.12.0" } }, "node_modules/@commitlint/top-level": { "version": "21.2.0", "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-21.2.0.tgz", "integrity": "sha512-Y5gmQ+KxzqCrBFJfLvFEPvvwD3LDiNZoTT2yeFBm96M8qhmqSzQc5DvX3rheAaAMjyIvMXOCLS/mWfdpONsjyQ==", "dev": true, "license": "MIT", "dependencies": { "escalade": "^3.2.0" }, "engines": { "node": ">=22.12.0" } }, "node_modules/@commitlint/types": { "version": "21.2.3", "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-21.2.3.tgz", "integrity": "sha512-sGEA473TlCvhCGNoTBY9OXZVWdscIXdGt0dm8EpB+2MRfmt8LNh/X56P/T3nCtEB+eH5qGEa3odphSAoUxSfSQ==", "dev": true, "license": "MIT", "dependencies": { "conventional-commits-parser": "^7.0.0", "picocolors": "^1.1.1" }, "engines": { "node": ">=22.12.0" } }, "node_modules/@conventional-changelog/git-client": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@conventional-changelog/git-client/-/git-client-3.1.2.tgz", "integrity": "sha512-jZqwnJwf7nboIlAcw/mkOjVa6DexCcUOgT2oOQgkoi3z9vR8tGFkcMy2BFcYwjhL9sYcDDXkRQDayiDieCoW7A==", "dev": true, "license": "MIT", "dependencies": { "@simple-libs/child-process-utils": "^2.0.0", "@simple-libs/stream-utils": "^2.0.0", "semver": "^7.5.2" }, "engines": { "node": ">=22" }, "peerDependencies": { "conventional-commits-filter": "^6.0.1", "conventional-commits-parser": "^7.1.2" }, "peerDependenciesMeta": { "conventional-commits-filter": { "optional": true }, "conventional-commits-parser": { "optional": true } } }, "node_modules/@conventional-changelog/git-client/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/@conventional-changelog/template": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@conventional-changelog/template/-/template-1.4.0.tgz", "integrity": "sha512-aalGyl7dbB5PArRebDIX43ZvBlXrYm9uWzGJ26t+4SzJVPsOuvfILGGbw5X4yX7i50YEmJ8zvbiWnqH/AAnZqg==", "dev": true, "license": "MIT", "engines": { "node": ">=22" } }, "node_modules/@emnapi/core": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", "dev": true, "license": "MIT", "optional": true, "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "dev": true, "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" } }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "dev": true, "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" } }, "node_modules/@epic-web/invariant": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz", "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==", "dev": true, "license": "MIT" }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "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/eslint-utils/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-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.23.5", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^3.0.5", "debug": "^4.3.1", "minimatch": "^10.2.4" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/config-helpers": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/core": "^1.2.1" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/core": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", "dev": true, "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/js": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", "dev": true, "license": "MIT", "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://eslint.org/donate" }, "peerDependencies": { "eslint": "^10.0.0" }, "peerDependenciesMeta": { "eslint": { "optional": true } } }, "node_modules/@eslint/object-schema": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", "dev": true, "license": "Apache-2.0", "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@eslint/plugin-kit": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.3.tgz", "integrity": "sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==", "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/core": "^1.2.1", "levn": "^0.4.1" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@gulpjs/messages": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@gulpjs/messages/-/messages-1.1.0.tgz", "integrity": "sha512-Ys9sazDatyTgZVb4xPlDufLweJ/Os2uHWOv+Caxvy2O85JcnT4M3vc73bi8pdLWlv3fdWQz3pdI9tVwo8rQQSg==", "dev": true, "license": "MIT", "engines": { "node": ">=10.13.0" } }, "node_modules/@gulpjs/to-absolute-glob": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@gulpjs/to-absolute-glob/-/to-absolute-glob-4.0.0.tgz", "integrity": "sha512-kjotm7XJrJ6v+7knhPaRgaT6q8F8K2jiafwYdNHLzmV0uGLuZY43FK6smNSHUPrhq5kX2slCUy+RGG/xGqmIKA==", "dev": true, "license": "MIT", "dependencies": { "is-negated-glob": "^1.0.0" }, "engines": { "node": ">=10.13.0" } }, "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/@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/source-map": { "version": "0.3.11", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" } }, "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/@keyv/bigmap": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz", "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==", "dev": true, "license": "MIT", "dependencies": { "hashery": "^1.4.0", "hookified": "^1.15.0" }, "engines": { "node": ">= 18" }, "peerDependencies": { "keyv": "^5.6.0" } }, "node_modules/@keyv/serialize": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", "dev": true, "license": "MIT" }, "node_modules/@napi-rs/lzma-linux-x64-gnu": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", "cpu": [ "x64" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^22.20 || ^24.12 || >=25" } }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "dev": true, "license": "MIT", "optional": true, "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "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/@oxc-project/types": { "version": "0.137.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" } }, "node_modules/@paralleldrive/cuid2": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.3.1.tgz", "integrity": "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw==", "dev": true, "license": "MIT", "dependencies": { "@noble/hashes": "^1.1.5" } }, "node_modules/@peculiar/asn1-cms": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.8.0.tgz", "integrity": "sha512-NgekZOrSJFSBFLFoLfwePguAWAx7z1+f2TEsWFUMyiqqfntZ4+S/S5hzqME3q4pCA0iOsFKdwiQ35dwY24eVqA==", "dev": true, "license": "MIT", "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "@peculiar/asn1-x509-attr": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "node_modules/@peculiar/asn1-csr": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.8.0.tgz", "integrity": "sha512-akbF8+uvleHs8sejNPQxwmVFuInAg6FMNHOwMILXfP518YfFJwdR3jr6oNUPOaEJfuEhn/vkNOCIT6ASUd4mbg==", "dev": true, "license": "MIT", "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "node_modules/@peculiar/asn1-ecc": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.8.0.tgz", "integrity": "sha512-ohwlk+u9Rv2NOAY1c6MfHj45ATVF8R1DUN/WCgABiRtLi2ZftlZWZX7KvpAbU8v9xPcmoILfELeEABj/rn18AQ==", "dev": true, "license": "MIT", "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "node_modules/@peculiar/asn1-pfx": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.8.0.tgz", "integrity": "sha512-5yof1ytoB++RQtaFbqSUJ8pxDJtZT6vbVqZ8XoJ61ph7UjNVvfFwAilnCodqkNsAodpy13gDhoxZXw00pghnyg==", "dev": true, "license": "MIT", "dependencies": { "@peculiar/asn1-cms": "^2.8.0", "@peculiar/asn1-pkcs8": "^2.8.0", "@peculiar/asn1-rsa": "^2.8.0", "@peculiar/asn1-schema": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "node_modules/@peculiar/asn1-pkcs8": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.8.0.tgz", "integrity": "sha512-qAKXtLpBEw9LqhKpjw3ajZSXlBur+ipW+y2ivVBQAG6F6qRx94yO+1ZR4mvw+YaCfKSaOzLeYEzsPaBp4SJELA==", "dev": true, "license": "MIT", "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "node_modules/@peculiar/asn1-pkcs9": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.8.0.tgz", "integrity": "sha512-b5nDWCnkV60+cQ141D6sVVwK9nz64R5n3zSVnklGd+ECdkW2Ol3U1a6yYFlalpSOaD557yuJB64A+q42jG7lUQ==", "dev": true, "license": "MIT", "dependencies": { "@peculiar/asn1-cms": "^2.8.0", "@peculiar/asn1-pfx": "^2.8.0", "@peculiar/asn1-pkcs8": "^2.8.0", "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "@peculiar/asn1-x509-attr": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "node_modules/@peculiar/asn1-rsa": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.8.0.tgz", "integrity": "sha512-zHEUlCqB2mk7x2lxDwHHJy7hWZOPdGHVlsmITWKB5/PbQo61atbu9PJ/0r9dQNMwFzbKPXZ8uK8/91eUhRznSg==", "dev": true, "license": "MIT", "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "node_modules/@peculiar/asn1-schema": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.8.0.tgz", "integrity": "sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q==", "dev": true, "license": "MIT", "dependencies": { "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "node_modules/@peculiar/asn1-x509": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.8.0.tgz", "integrity": "sha512-N0CMuhWUzsWEVq6F1q9X6+VKUnWzSW+cSVg+aPaGGwDdbFoFWTYgin5MHwXgpWd6y9COMBxnfy/Qc+Xc7F0Zwg==", "dev": true, "license": "MIT", "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "node_modules/@peculiar/asn1-x509-attr": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.8.0.tgz", "integrity": "sha512-tHjkfS/qhMnmrlB2J9NhflQlQ7In3khO3CfmVrriOlpTeErY9ZIKOso1hQ5JQiyrJ7ShvqVPk7E5fQmbclkSKA==", "dev": true, "license": "MIT", "dependencies": { "@peculiar/asn1-schema": "^2.8.0", "@peculiar/asn1-x509": "^2.8.0", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "node_modules/@peculiar/utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@peculiar/utils/-/utils-2.0.3.tgz", "integrity": "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==", "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.8.1" } }, "node_modules/@peculiar/x509": { "version": "1.14.3", "resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.14.3.tgz", "integrity": "sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==", "dev": true, "license": "MIT", "dependencies": { "@peculiar/asn1-cms": "^2.6.0", "@peculiar/asn1-csr": "^2.6.0", "@peculiar/asn1-ecc": "^2.6.0", "@peculiar/asn1-pkcs9": "^2.6.0", "@peculiar/asn1-rsa": "^2.6.0", "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.0", "pvtsutils": "^1.3.6", "reflect-metadata": "^0.2.2", "tslib": "^2.8.1", "tsyringe": "^4.10.0" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@polka/url": { "version": "1.0.0-next.29", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", "dev": true, "license": "MIT" }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz", "integrity": "sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "android" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-darwin-arm64": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz", "integrity": "sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-darwin-x64": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz", "integrity": "sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-freebsd-x64": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.3.tgz", "integrity": "sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.3.tgz", "integrity": "sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==", "cpu": [ "arm" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz", "integrity": "sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==", "cpu": [ "arm64" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-arm64-musl": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.3.tgz", "integrity": "sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==", "cpu": [ "arm64" ], "dev": true, "libc": [ "musl" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.3.tgz", "integrity": "sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==", "cpu": [ "ppc64" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.3.tgz", "integrity": "sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==", "cpu": [ "s390x" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-x64-gnu": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz", "integrity": "sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==", "cpu": [ "x64" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-x64-musl": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.3.tgz", "integrity": "sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==", "cpu": [ "x64" ], "dev": true, "libc": [ "musl" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-openharmony-arm64": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.3.tgz", "integrity": "sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "openharmony" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-wasm32-wasi": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.3.tgz", "integrity": "sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==", "cpu": [ "wasm32" ], "dev": true, "license": "MIT", "optional": true, "dependencies": { "@emnapi/core": "1.11.1", "@emnapi/runtime": "1.11.1", "@napi-rs/wasm-runtime": "^1.1.6" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz", "integrity": "sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-x64-msvc": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.3.tgz", "integrity": "sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/pluginutils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, "license": "MIT" }, "node_modules/@rollup/plugin-alias": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-6.0.0.tgz", "integrity": "sha512-tPCzJOtS7uuVZd+xPhoy5W4vThe6KWXNmsFCNktaAh5RTqcLiSfT4huPQIXkgJ6YCOjJHvecOAzQxLFhPxKr+g==", "dev": true, "license": "MIT", "engines": { "node": ">=20.19.0" }, "peerDependencies": { "rollup": ">=4.0.0" }, "peerDependenciesMeta": { "rollup": { "optional": true } } }, "node_modules/@rollup/plugin-babel": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-7.1.0.tgz", "integrity": "sha512-h9Y+xYha6p4wKO+FwdiPIkE+eIYCm8MzZPpX1iARIoFBnmKP9CnpT1p9dDf/DTFm6fyN8PmuLyRI5qZgchnitw==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.18.6", "@rollup/pluginutils": "^5.0.1", "workerpool": "^9.0.0" }, "engines": { "node": ">=14.0.0" }, "peerDependencies": { "@babel/core": "^7.0.0", "@types/babel__core": "^7.1.9", "rollup": "^2.0.0||^3.0.0||^4.0.0" }, "peerDependenciesMeta": { "@types/babel__core": { "optional": true }, "rollup": { "optional": true } } }, "node_modules/@rollup/plugin-commonjs": { "version": "29.0.3", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-29.0.3.tgz", "integrity": "sha512-ZaOxZceP7SOUW7Lqw5IRVweSQYWaeIPnXIGLiB690EBA3FGJTO40EEr2L5yZplJWsgTCogILRSpcAe7+U0Otdg==", "dev": true, "license": "MIT", "dependencies": { "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", "estree-walker": "^2.0.2", "fdir": "^6.2.0", "is-reference": "1.2.1", "magic-string": "^0.30.3", "picomatch": "^4.0.2" }, "engines": { "node": ">=16.0.0 || 14 >= 14.17" }, "peerDependencies": { "rollup": "^2.68.0||^3.0.0||^4.0.0" }, "peerDependenciesMeta": { "rollup": { "optional": true } } }, "node_modules/@rollup/plugin-json": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==", "dev": true, "license": "MIT", "dependencies": { "@rollup/pluginutils": "^5.1.0" }, "engines": { "node": ">=14.0.0" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "peerDependenciesMeta": { "rollup": { "optional": true } } }, "node_modules/@rollup/plugin-node-resolve": { "version": "16.0.3", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.3.tgz", "integrity": "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==", "dev": true, "license": "MIT", "dependencies": { "@rollup/pluginutils": "^5.0.1", "@types/resolve": "1.20.2", "deepmerge": "^4.2.2", "is-module": "^1.0.0", "resolve": "^1.22.1" }, "engines": { "node": ">=14.0.0" }, "peerDependencies": { "rollup": "^2.78.0||^3.0.0||^4.0.0" }, "peerDependenciesMeta": { "rollup": { "optional": true } } }, "node_modules/@rollup/plugin-terser": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-1.0.0.tgz", "integrity": "sha512-FnCxhTBx6bMOYQrar6C8h3scPt8/JwIzw3+AJ2K++6guogH5fYaIFia+zZuhqv0eo1RN7W1Pz630SyvLbDjhtQ==", "dev": true, "license": "MIT", "dependencies": { "serialize-javascript": "^7.0.3", "smob": "^1.0.0", "terser": "^5.17.4" }, "engines": { "node": ">=20.0.0" }, "peerDependencies": { "rollup": "^2.0.0||^3.0.0||^4.0.0" }, "peerDependenciesMeta": { "rollup": { "optional": true } } }, "node_modules/@rollup/pluginutils": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", "dev": true, "license": "MIT", "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "engines": { "node": ">=14.0.0" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "peerDependenciesMeta": { "rollup": { "optional": true } } }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.64.6.tgz", "integrity": "sha512-FkO19Fowv/OQeJZPVfdetJaI3/VpbZmBsnmAlR/rQ+b3QwfiGQiOpVegpSRH331kelvHTVw9hXYG3DwjAaRzyQ==", "cpu": [ "arm" ], "dev": true, "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.64.6.tgz", "integrity": "sha512-si5/Etm6MphPc2xYHnk455Shls/hoMjTy3mhXge4OPmvCw7TwC4KP4fKokAlKLV4XaQvLvgSHfafPcblLMYYdQ==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.64.6.tgz", "integrity": "sha512-PQCl3q7b0BgvVt72i2xrWM6I5KliwwVe4GpWW5QDGyUQawDS9aPZss/aGyFqBmxdRxlE8wDc9CaxiQ5DHRSp/Q==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.64.6.tgz", "integrity": "sha512-l9DFl1Itb1YYdSrP1K3U8ih44AOHa/p+Q4WMMDfGvH5qVWp+ajfgS1DlYo4yCRh8wJ2ZJv0Fqccf+ULAmMFjmg==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-freebsd-arm64": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.64.6.tgz", "integrity": "sha512-bR4GR3205N9+od0+l5Dd9/iZTDpJkIe6d6Ydp2qbcQZJLmKBdYQQRp8gbWpbMt92twKJY66o1vK4NkoKQODIrA==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-freebsd-x64": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.64.6.tgz", "integrity": "sha512-XTVNaDzcFl2TeBglvxdGgsCv9JGtAGRAoJENmGBR/XvDH2mA2K0IXc2aMfP0dR8mexSXSKifJ85FixoXX1wfTw==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.64.6.tgz", "integrity": "sha512-qWc6qbc+25ugG9vqPR9P422KJBbVfZ05mjcMggoq8tsfWP+eRfC+/tiDdIlRF5VL2pDVZWN9zAhWsmu7R1+7Dw==", "cpu": [ "arm" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.64.6.tgz", "integrity": "sha512-V4Nwkp8844X/Xnww3PI1LYS0YPei8XQEVG70R+5MPZGO7AhYoun4hCN7Svs4U3wbQLZbwiz+9xRFhUZZdRl8Ww==", "cpu": [ "arm" ], "dev": true, "libc": [ "musl" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.64.6.tgz", "integrity": "sha512-xb72A1aPaaIsp+a88VtRae/ovIEXislbUMyp7b9AQQU7ZkP2m/+ik6DW9TQcJyw5R8/RIailF9zExYwkjR++UA==", "cpu": [ "arm64" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.64.6.tgz", "integrity": "sha512-pbcs1BVBwC43hZkjhjlF8Rx8m5G0KnhLZs9Azy/gqccP6UsNSzJr0hKGIAVPqzAH1O/NdPxxC9GVGb4msQg/aw==", "cpu": [ "arm64" ], "dev": true, "libc": [ "musl" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.64.6.tgz", "integrity": "sha512-EKOC8os6Vsg487ZLKUqzLF99Jsmnq6tu7O7mAELakNpcA+8KadktFf/JYdRuHe/DzCUXmN38pUKu4XBcKlqGbQ==", "cpu": [ "loong64" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.64.6.tgz", "integrity": "sha512-v/SQ68GnSlr/HyjB79sNXesGDwkOPu9JjbHfmWaymCiM+nZsQ9TstG1KfA4MuEvTr2d3PoY8PhFxj9bt0e32JA==", "cpu": [ "loong64" ], "dev": true, "libc": [ "musl" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.64.6.tgz", "integrity": "sha512-G3pmliMQ7r15zFFNwBlaT/xTdSLi4lfgI7CUrQueyKYheNVhrd8JrWCE6Ll8xf4AA1RZFqq+mNJYntiElsVOyA==", "cpu": [ "ppc64" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.64.6.tgz", "integrity": "sha512-XQTB/IPoB/wG6x/wu9uomAt8YH61IAr5iZTubMiad/lu88sotq2ocz9hJd556+Jz2kIkF7ZQZIsUuWE3OWqtcA==", "cpu": [ "ppc64" ], "dev": true, "libc": [ "musl" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.64.6.tgz", "integrity": "sha512-O5mpokth6+ydMuwiGtY8fIjvOBfH7u6SzmDNmZ3k6GK5C6iEAC2q2ZN/T5L9jYILx0IcCT0KnfMDihQ5yQFSBw==", "cpu": [ "riscv64" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.64.6.tgz", "integrity": "sha512-aIFxbAD9v3TvIaEDsKmukjcDg1JS4/tFXw8SHyQQgzOwzWCQ4cyDK7Ju1pQKqUXDFkEu/BZCFDCDATsKhQcB/Q==", "cpu": [ "riscv64" ], "dev": true, "libc": [ "musl" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.64.6.tgz", "integrity": "sha512-fuNo1UwBc6BxnHDOxxguQL/InhheMN3RJcJMb0b4xP8r5Q3CNCrJbVg0eCIFuJ4e/zAfjR2gYbOfbzO/G7CIyw==", "cpu": [ "s390x" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.64.6.tgz", "integrity": "sha512-05v6wFKDKJPZs9xlytVrrRu6IbRuoSvlH9xNwEOafXsKyjBFZPvAcZz2VIAB/M+lblYeKa9iOYC4RDVdfcAAaw==", "cpu": [ "x64" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.64.6.tgz", "integrity": "sha512-wJXKcdMjvkc/FaavZXNP1yW4i0pfOmhZAIWRSLG2yJ0PB0TWi7dkLErhX0u0vptDcXKKO/m1Y/YTeuyvb2MXwg==", "cpu": [ "x64" ], "dev": true, "libc": [ "musl" ], "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-openbsd-x64": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.64.6.tgz", "integrity": "sha512-cwg7/h0ThvJwy8CBkmACdiHAxRWpbsCmIsjOt+Zzx0hTnIcSUmKYEV03bGg+hD827OnwUsbKh1ZZDW754xWyBA==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "openbsd" ] }, "node_modules/@rollup/rollup-openharmony-arm64": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.64.6.tgz", "integrity": "sha512-T0RkYIJbdYXJEQFw+tMBO09KDFdgmNxkHsof3wWAABrvnx15Yh8RytmrgYy9+q9cBeBNZF66MMQZC+5nzEz6vg==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "openharmony" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.64.6.tgz", "integrity": "sha512-KlfpmF1AAIdAWxt8QQNClbpLFlAUfYOzqIuJ7IXEStMHpFRuH78RG6FjqPb3Rqhh0K89F5npvG+/i3Fjl6IveQ==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.64.6.tgz", "integrity": "sha512-MrTLjd7Jlt9/ZavJZYG61KQv9Iy9bzbY+yXy73Xvwoa2uP66wvDShkyBr5Chz/yrE6lbD/zHMuTmKQVNQqBApg==", "cpu": [ "ia32" ], "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.64.6.tgz", "integrity": "sha512-8fWDH1CkPI8GdBKAxzHDza7UmgficsKo2+MVbvUgOGhB0629w5sHMg3Qu5y7TZsrYKHHWpz8fla4KvoT7WsO3Q==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.64.6.tgz", "integrity": "sha512-9/c9PWdr5GCIHkkxiHM1HjNBQMVdftP4O8QTcoUAaRza2FSqJ4axbf8QAnBuE6BIGQDl71SaRPeDdDYid5ecTw==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@sec-ant/readable-stream": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", "dev": true, "license": "MIT" }, "node_modules/@simple-libs/child-process-utils": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@simple-libs/child-process-utils/-/child-process-utils-2.0.0.tgz", "integrity": "sha512-dvNoRKLijXnD0XoJAz94pbNuB5GQgDr55UhpSPhffDkTT0Cmcqh9jSCOtwfT2d4H6MI9E7c4SgtMuJXZ6F3c6A==", "dev": true, "license": "MIT", "dependencies": { "@simple-libs/stream-utils": "^2.0.0" }, "engines": { "node": ">=22" }, "funding": { "url": "https://ko-fi.com/dangreen" } }, "node_modules/@simple-libs/stream-utils": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-2.0.0.tgz", "integrity": "sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ==", "dev": true, "license": "MIT", "engines": { "node": ">=22" }, "funding": { "url": "https://ko-fi.com/dangreen" } }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "dev": true, "license": "MIT" }, "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/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", "dev": true, "license": "MIT", "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "node_modules/@types/deep-eql": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", "dev": true, "license": "MIT" }, "node_modules/@types/esrecurse": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", "dev": true, "license": "MIT" }, "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/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/node": { "version": "26.0.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.1.tgz", "integrity": "sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==", "dev": true, "license": "MIT", "peer": true, "dependencies": { "undici-types": "~8.3.0" } }, "node_modules/@types/resolve": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", "dev": true, "license": "MIT" }, "node_modules/@vitest/browser": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-4.1.11.tgz", "integrity": "sha512-bwMovvAeuTFOK5kIFevw4VEf+1gVEICv4SYK4k3knJOxl6b1zEWud8mYKD73e1B0odAn174h1MofURy2TPWf3w==", "dev": true, "license": "MIT", "dependencies": { "@blazediff/core": "1.9.1", "@vitest/mocker": "4.1.11", "@vitest/utils": "4.1.11", "magic-string": "^0.30.21", "pngjs": "^7.0.0", "sirv": "^3.0.2", "tinyrainbow": "^3.1.0", "ws": "^8.19.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "vitest": "4.1.11" } }, "node_modules/@vitest/browser-playwright": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@vitest/browser-playwright/-/browser-playwright-4.1.11.tgz", "integrity": "sha512-riLBxPqwnJ0lWs2DN2WeUfYeKLoAjbP2Xx8cLQdSddzMi20sksIa6K2mPz79DyMZKKVKH2ksOC2yJvtNcZg8cg==", "dev": true, "license": "MIT", "dependencies": { "@vitest/browser": "4.1.11", "@vitest/mocker": "4.1.11", "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "playwright": "*", "vitest": "4.1.11" }, "peerDependenciesMeta": { "playwright": { "optional": false } } }, "node_modules/@vitest/expect": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz", "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.11", "@vitest/utils": "4.1.11", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz", "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", "dev": true, "license": "MIT", "dependencies": { "@vitest/spy": "4.1.11", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "msw": { "optional": true }, "vite": { "optional": true } } }, "node_modules/@vitest/mocker/node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, "license": "MIT", "dependencies": { "@types/estree": "^1.0.0" } }, "node_modules/@vitest/pretty-format": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz", "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", "dev": true, "license": "MIT", "dependencies": { "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz", "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", "dev": true, "license": "MIT", "dependencies": { "@vitest/utils": "4.1.11", "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz", "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", "dev": true, "license": "MIT", "dependencies": { "@vitest/pretty-format": "4.1.11", "@vitest/utils": "4.1.11", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/spy": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz", "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", "dev": true, "license": "MIT", "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz", "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", "dev": true, "license": "MIT", "dependencies": { "@vitest/pretty-format": "4.1.11", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/abortcontroller-polyfill": { "version": "1.7.8", "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.8.tgz", "integrity": "sha512-9f1iZ2uWh92VcrU9Y8x+LdM4DLj75VE0MJB8zuF1iUnroEptStw+DQ8EQPMUdfe5k+PkB1uUfDQfWbhstH8LrQ==", "dev": true, "license": "MIT" }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "dev": true, "license": "MIT", "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" }, "engines": { "node": ">= 0.6" } }, "node_modules/acorn": { "version": "8.19.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz", "integrity": "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==", "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==", "license": "MIT", "dependencies": { "debug": "4" }, "engines": { "node": ">= 6.0.0" } }, "node_modules/ajv": { "version": "8.20.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, "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/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/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/append-field": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", "dev": true, "license": "MIT" }, "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/argue-cli": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/argue-cli/-/argue-cli-3.2.0.tgz", "integrity": "sha512-VipTB0gXgGIFO2Rg9yEVN5wLt2AurJcZqDbgmYSwPwsykhLrQhs240/bfceev4w68lI8JshoOJIk9uW7tFzROw==", "dev": true, "license": "MIT", "engines": { "node": ">=22" }, "funding": { "url": "https://ko-fi.com/dangreen" } }, "node_modules/array-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", "integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/array-slice": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", "dev": true, "license": "MIT" }, "node_modules/asn1js": { "version": "3.0.10", "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz", "integrity": "sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "pvtsutils": "^1.3.6", "pvutils": "^1.1.5", "tslib": "^2.8.1" }, "engines": { "node": ">=12.0.0" } }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", "engines": { "node": ">=12" } }, "node_modules/async-done": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/async-done/-/async-done-2.0.0.tgz", "integrity": "sha512-j0s3bzYq9yKIVLKGE/tWlCpa3PfFLcrDZLTSVdnnCTGagXuXBJO4SsY9Xdk/fQBirCkH4evW5xOeJXqlAQFdsw==", "dev": true, "license": "MIT", "dependencies": { "end-of-stream": "^1.4.4", "once": "^1.4.0", "stream-exhaust": "^1.0.2" }, "engines": { "node": ">= 10.13.0" } }, "node_modules/async-settle": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-2.0.0.tgz", "integrity": "sha512-Obu/KE8FurfQRN6ODdHN9LuXqwC+JFIM9NRyZqJJ4ZfLJmIYN9Rg0/kb+wF70VV5+fJusTMQlJ1t5rF7J/ETdg==", "dev": true, "license": "MIT", "dependencies": { "async-done": "^2.0.0" }, "engines": { "node": ">= 10.13.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==", "license": "MIT" }, "node_modules/b4a": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", "dev": true, "license": "Apache-2.0", "peerDependencies": { "react-native-b4a": "*" }, "peerDependenciesMeta": { "react-native-b4a": { "optional": true } } }, "node_modules/babel-plugin-polyfill-corejs2": { "version": "0.4.17", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", "dev": true, "license": "MIT", "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-define-polyfill-provider": "^0.6.8", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-corejs3": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz", "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.8", "core-js-compat": "^3.48.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-regenerator": { "version": "0.6.8", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.8" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/bach": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/bach/-/bach-2.0.1.tgz", "integrity": "sha512-A7bvGMGiTOxGMpNupYl9HQTf0FFDNF4VCmks4PJpFyN1AX2pdKuxuwdvUz2Hu388wcgp+OvGFNsumBfFNkR7eg==", "dev": true, "license": "MIT", "dependencies": { "async-done": "^2.0.0", "async-settle": "^2.0.0", "now-and-later": "^3.0.0" }, "engines": { "node": ">=10.13.0" } }, "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/bare-events": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz", "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==", "dev": true, "license": "Apache-2.0", "peerDependencies": { "bare-abort-controller": "*" }, "peerDependenciesMeta": { "bare-abort-controller": { "optional": true } } }, "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.20", "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.20.tgz", "integrity": "sha512-H0ulySigv6icDJ1F7SjtdCD6PrhTpdYCmP0CactWy1+ekh0AFd0o1Wn5T8b+hnTmdBx19u9yhL6wvCylXMY7zw==", "dev": true, "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" }, "engines": { "node": ">=6.0.0" } }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "license": "MIT", "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/bl": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", "dev": true, "license": "MIT", "dependencies": { "buffer": "^6.0.3", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "node_modules/body-parser": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", "dev": true, "license": "MIT", "dependencies": { "bytes": "^3.1.2", "content-type": "^2.0.0", "debug": "^4.4.3", "http-errors": "^2.0.1", "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", "qs": "^6.15.2", "raw-body": "^3.0.2", "type-is": "^2.1.0" }, "engines": { "node": ">=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "node_modules/brace-expansion": { "version": "5.0.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { "node": "20 || >=22" } }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, "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/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/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", "dev": true, "dependencies": { "streamsearch": "^1.1.0" }, "engines": { "node": ">=10.16.0" } }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/bytestreamjs": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz", "integrity": "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==", "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=6.0.0" } }, "node_modules/cacheable": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.5.0.tgz", "integrity": "sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==", "dev": true, "license": "MIT", "dependencies": { "@cacheable/memory": "^2.2.0", "@cacheable/utils": "^2.5.0", "hookified": "^1.15.0", "keyv": "^5.6.0", "qified": "^0.10.1" } }, "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==", "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/caniuse-lite": { "version": "1.0.30001810", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", "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/chai": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "license": "MIT", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "engines": { "node": ">= 8.10.0" }, "funding": { "url": "https://paulmillr.com/funding/" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "node_modules/chokidar/node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, "engines": { "node": ">= 6" } }, "node_modules/cliui": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", "dev": true, "license": "ISC", "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" }, "engines": { "node": ">=20" } }, "node_modules/cliui/node_modules/emoji-regex": { "version": "10.6.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "dev": true, "license": "MIT" }, "node_modules/cliui/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/cliui/node_modules/wrap-ansi": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", "dev": true, "license": "MIT", "engines": { "node": ">=0.8" } }, "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/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, "license": "MIT" }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true, "license": "MIT" }, "node_modules/content-disposition": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-2.0.1.tgz", "integrity": "sha512-e+H0ZXHSWYrENhQzw1LPuP4oF5MzVKmDU6d3hxlvaPEYLLg62MxtQNPRx4SYSuYJSBUgnQIG4HIN2tEtNv7Dog==", "dev": true, "license": "MIT", "engines": { "node": ">=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "node_modules/content-type": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", "dev": true, "license": "MIT", "engines": { "node": ">=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "node_modules/conventional-changelog-angular": { "version": "9.4.0", "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-9.4.0.tgz", "integrity": "sha512-HdxRxuS8bBXVIuo4V82gvSwAXT0vYQUizrjs/izmPg5JdDstr8v8I5hduGL3iQbG+o310dUDxC4+LetuS5hu9w==", "dev": true, "license": "ISC", "dependencies": { "@conventional-changelog/template": "^1.4.0" }, "engines": { "node": ">=22" } }, "node_modules/conventional-changelog-conventionalcommits": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-10.2.0.tgz", "integrity": "sha512-UtlM9GqolY7OmlQh5L/UEVoKsTUpTgUVy1PU8JN5gl5Ydaejb7WRklGliG1SKPxxj7hzA173eG3Kt5fYWE2pmg==", "dev": true, "license": "ISC", "dependencies": { "@conventional-changelog/template": "^1.2.0" }, "engines": { "node": ">=22" } }, "node_modules/conventional-commits-parser": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-7.1.2.tgz", "integrity": "sha512-O+x4N2yH+ijvqWlIyTHsXTAP+algNWgGbjY2duCe8w2vUMvUB95cLRslCPfTMQyLAKlet3bhZTdu6ozn4M+QJQ==", "dev": true, "license": "MIT", "dependencies": { "@simple-libs/stream-utils": "^2.0.0", "argue-cli": "^3.1.0" }, "bin": { "conventional-commits-parser": "dist/cli/index.js" }, "engines": { "node": ">=22" } }, "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/cookie": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/cookie-signature": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "dev": true, "license": "MIT", "engines": { "node": ">=6.6.0" } }, "node_modules/copy-props": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-4.0.0.tgz", "integrity": "sha512-bVWtw1wQLzzKiYROtvNlbJgxgBYt2bMJpkCbKmXM3xyijvcjjWXEk5nyrrT3bgJ7ODb19ZohE2T0Y3FgNPyoTw==", "dev": true, "license": "MIT", "dependencies": { "each-props": "^3.0.0", "is-plain-object": "^5.0.0" }, "engines": { "node": ">= 10.13.0" } }, "node_modules/core-js-compat": { "version": "3.49.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", "dev": true, "license": "MIT", "dependencies": { "browserslist": "^4.28.1" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" } }, "node_modules/cosmiconfig": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.2.tgz", "integrity": "sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==", "dev": true, "license": "MIT", "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", "parse-json": "^5.2.0" }, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/d-fischer" }, "peerDependencies": { "typescript": ">=4.9.5" }, "peerDependenciesMeta": { "typescript": { "optional": true } } }, "node_modules/cosmiconfig-typescript-loader": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.3.0.tgz", "integrity": "sha512-Akr82WH1Wfqatyiqpj8HDkO2o2KmJRu1FhKfSNJP3K4IdXwHfEyL7MOb62i1AGQVLtIQM+iCE9CGOtrfhR+mmA==", "dev": true, "license": "MIT", "dependencies": { "jiti": "2.6.1" }, "engines": { "node": ">=v18" }, "peerDependencies": { "@types/node": "*", "cosmiconfig": ">=9", "typescript": ">=5" } }, "node_modules/cross-env": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz", "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==", "dev": true, "license": "MIT", "dependencies": { "@epic-web/invariant": "^1.0.0", "cross-spawn": "^7.0.6" }, "bin": { "cross-env": "dist/bin/cross-env.js", "cross-env-shell": "dist/bin/cross-env-shell.js" }, "engines": { "node": ">=20" } }, "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/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "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/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "license": "MIT", "engines": { "node": ">=0.4.0" } }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, "license": "Apache-2.0", "engines": { "node": ">=8" } }, "node_modules/dev-null": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/dev-null/-/dev-null-0.1.1.tgz", "integrity": "sha512-nMNZG0zfMgmdv8S5O0TM5cpwNbGKRGPCxVsr0SmA3NZZy9CYBbuNLL0PD3Acx9e5LIUgwONXtM9kM6RlawPxEQ==", "dev": true, "license": "MIT" }, "node_modules/dezalgo": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", "dev": true, "license": "ISC", "dependencies": { "asap": "^2.0.0", "wrappy": "1" } }, "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==", "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/duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "dev": true, "license": "MIT" }, "node_modules/each-props": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/each-props/-/each-props-3.0.0.tgz", "integrity": "sha512-IYf1hpuWrdzse/s/YJOrFmU15lyhSzxelNVAHTEG3DtP4QsLTWZUzcUL3HMXmKQxXpa4EIrBPpwRgj0aehdvAw==", "dev": true, "license": "MIT", "dependencies": { "is-plain-object": "^5.0.0", "object.defaults": "^1.1.0" }, "engines": { "node": ">= 10.13.0" } }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "dev": true, "license": "MIT" }, "node_modules/electron-to-chromium": { "version": "1.5.419", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.419.tgz", "integrity": "sha512-nHMPn8x4yCxCI0iSnL+LlHL5sUoUfjLXkcRIagZ4GBdrfFLFaiLNvzJWbJqZhFT9IAhw5tUSNlhggWN+otvp/A==", "dev": true, "license": "ISC" }, "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/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" } }, "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/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, "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==", "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==", "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/es-module-lexer": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.2.0.tgz", "integrity": "sha512-3lGxdTXCLfe1MYfTz1y2ksAAUM4NAOP6rPEjxGJVKO7TZ5+tvHCaQWGpC4Y3IXvW3ece0Cz1cIP4FWBxOnGCTQ==", "dev": true, "license": "MIT" }, "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==", "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==", "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/es-toolkit": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.52.0.tgz", "integrity": "sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA==", "dev": true, "license": "MIT", "workspaces": [ "docs", "benchmarks", "tests/types", "tests/browser-compat" ] }, "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-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "dev": true, "license": "MIT" }, "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": "10.12.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.12.0.tgz", "integrity": "sha512-npHjrHb2o4ehH3mE+YuxABR+0gyb0aVWCIolgJDArwCiHEIsonBm3ZKjel5b0U5+a+MbEvIyRCGCzD3eKKK2NA==", "dev": true, "license": "MIT", "workspaces": [ "packages/*" ], "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.5", "@eslint/config-helpers": "^0.7.0", "@eslint/core": "^1.2.1", "@eslint/plugin-kit": "^0.7.3", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^9.1.2", "eslint-visitor-keys": "^5.0.1", "espree": "^11.2.0", "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "11.1.5 || >11.1.6 <12", "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", "minimatch": "^10.2.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://eslint.org/donate" }, "peerDependencies": { "jiti": "*" }, "peerDependenciesMeta": { "jiti": { "optional": true } } }, "node_modules/eslint-scope": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "@types/esrecurse": "^4.3.1", "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "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/eslint/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/eslint/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/espree": { "version": "11.2.0", "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.16.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^5.0.1" }, "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "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/estree-walker": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "dev": true, "license": "MIT" }, "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/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/events-universal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", "dev": true, "license": "Apache-2.0", "dependencies": { "bare-events": "^2.7.0" } }, "node_modules/expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", "dev": true, "license": "MIT", "dependencies": { "homedir-polyfill": "^1.0.1" }, "engines": { "node": ">=0.10.0" } }, "node_modules/expect-type": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", "dev": true, "license": "Apache-2.0", "engines": { "node": ">=12.0.0" } }, "node_modules/express": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/express/-/express-5.3.0.tgz", "integrity": "sha512-qCqy1xSoaugxOD4IVHb1ZWdkvCF2W3aE2okKouWj8VwJ+ztskSiXoBnCSJqfKT+cVPJhuJqe9YLbY425+Zh00g==", "dev": true, "license": "MIT", "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.3.0", "content-disposition": "^2.0.1", "content-type": "^2.0.0", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.8", "qs": "^6.16.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.1.0", "vary": "^1.1.2" }, "engines": { "node": ">= 18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "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-fifo": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", "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-uri": { "version": "3.1.8", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/fastify" }, { "type": "opencollective", "url": "https://opencollective.com/fastify" } ], "license": "BSD-3-Clause" }, "node_modules/fastest-levenshtein": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", "dev": true, "license": "MIT", "engines": { "node": ">= 4.9.1" } }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, "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/figures": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", "integrity": "sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ==", "dev": true, "license": "MIT", "dependencies": { "escape-string-regexp": "^1.0.5", "object-assign": "^4.1.0" }, "engines": { "node": ">=0.10.0" } }, "node_modules/figures/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/file-entry-cache": { "version": "11.1.5", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-11.1.5.tgz", "integrity": "sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==", "dev": true, "license": "MIT", "dependencies": { "flat-cache": "^6.1.23" } }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/finalhandler": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "dev": true, "license": "MIT", "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" }, "engines": { "node": ">= 18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "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/findup-sync": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-5.0.0.tgz", "integrity": "sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==", "dev": true, "license": "MIT", "dependencies": { "detect-file": "^1.0.0", "is-glob": "^4.0.3", "micromatch": "^4.0.4", "resolve-dir": "^1.0.1" }, "engines": { "node": ">= 10.13.0" } }, "node_modules/fined": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fined/-/fined-2.0.0.tgz", "integrity": "sha512-OFRzsL6ZMHz5s0JrsEr+TpdGNCtrVtnuG3x1yzGNiQHT0yaDnXAj8V/lWcpJVrnoDpcwXcASxAZYbuXda2Y82A==", "dev": true, "license": "MIT", "dependencies": { "expand-tilde": "^2.0.2", "is-plain-object": "^5.0.0", "object.defaults": "^1.1.0", "object.pick": "^1.3.0", "parse-filepath": "^1.0.2" }, "engines": { "node": ">= 10.13.0" } }, "node_modules/flagged-respawn": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-2.0.0.tgz", "integrity": "sha512-Gq/a6YCi8zexmGHMuJwahTGzXlAZAOsbCVKduWXC6TlLCjjFRlExMJc4GC2NYPYZ0r/brw9P7CpRgQmlPVeOoA==", "dev": true, "license": "MIT", "engines": { "node": ">= 10.13.0" } }, "node_modules/flat-cache": { "version": "6.1.23", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-6.1.23.tgz", "integrity": "sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==", "dev": true, "license": "MIT", "dependencies": { "cacheable": "^2.5.0", "flatted": "^3.4.2", "hookified": "^1.15.0" } }, "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.1", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.1.tgz", "integrity": "sha512-FNvFGzoMLWmE6Yj9spb/zjd7yiNCHiAW9/Tg9CXrQ8wuu32HtlJOwWO11OJafl5FfY3DxTdQ0vj42zU1kvv5jg==", "funding": [ { "type": "individual", "url": "https://github.com/sponsors/RubenVerborgh" } ], "license": "MIT", "engines": { "node": ">=4.0" }, "peerDependenciesMeta": { "debug": { "optional": true } } }, "node_modules/for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/for-own": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", "integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==", "dev": true, "license": "MIT", "dependencies": { "for-in": "^1.0.1" }, "engines": { "node": ">=0.10.0" } }, "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==", "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/form-data/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==", "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/form-data/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==", "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" } }, "node_modules/formdata-node": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-6.0.3.tgz", "integrity": "sha512-8e1++BCiTzUno9v5IZ2J6bv4RU+3UKDmqWUQD0MIMVCd9AdhWkO1gw57oo1mNEX1dMq2EGI+FbWz4B92pscSQg==", "dev": true, "license": "MIT", "engines": { "node": ">= 18" } }, "node_modules/formidable": { "version": "3.5.4", "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", "dev": true, "license": "MIT", "dependencies": { "@paralleldrive/cuid2": "^2.2.2", "dezalgo": "^1.0.4", "once": "^1.4.0" }, "engines": { "node": ">=14.0.0" }, "funding": { "url": "https://ko-fi.com/tunnckoCore/commissions" } }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/fresh": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/fs-extra": { "version": "11.4.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.1.tgz", "integrity": "sha512-KYAb4c9BJQI6QqGKthV68OHe0badztdXJWKo0WtBA9IuCFPTKvE5ZdUBglP833aMjhaSPNO4A5j/EkzZtGlKjA==", "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-mkdirp-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-2.0.1.tgz", "integrity": "sha512-UTOY+59K6IA94tec8Wjqm0FSh5OVudGNB0NL/P6fB3HiE3bYOY3VYBGijsnOHNkQSwC1FKkU77pmq7xp9CskLw==", "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.8", "streamx": "^2.12.0" }, "engines": { "node": ">=10.13.0" } }, "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==", "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-east-asian-width": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", "dev": true, "license": "MIT", "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "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==", "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-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/get-stream": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", "dev": true, "license": "MIT", "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "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/glob-stream": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-8.0.3.tgz", "integrity": "sha512-fqZVj22LtFJkHODT+M4N1RJQ3TjnnQhfE9GwZI8qXscYarnhpip70poMldRnP8ipQ/w0B621kOhfc53/J9bd/A==", "dev": true, "license": "MIT", "dependencies": { "@gulpjs/to-absolute-glob": "^4.0.0", "anymatch": "^3.1.3", "fastq": "^1.13.0", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "is-negated-glob": "^1.0.0", "normalize-path": "^3.0.0", "streamx": "^2.12.5" }, "engines": { "node": ">=10.13.0" } }, "node_modules/glob-watcher": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-6.0.0.tgz", "integrity": "sha512-wGM28Ehmcnk2NqRORXFOTOR064L4imSw3EeOqU5bIwUf62eXGwg89WivH6VMahL8zlQHeodzvHpXplrqzrz3Nw==", "dev": true, "license": "MIT", "dependencies": { "async-done": "^2.0.0", "chokidar": "^3.5.3" }, "engines": { "node": ">= 10.13.0" } }, "node_modules/global-directory": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-5.0.0.tgz", "integrity": "sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w==", "dev": true, "license": "MIT", "dependencies": { "ini": "6.0.0" }, "engines": { "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/global-modules": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, "license": "MIT", "dependencies": { "global-prefix": "^1.0.1", "is-windows": "^1.0.1", "resolve-dir": "^1.0.0" }, "engines": { "node": ">=0.10.0" } }, "node_modules/global-prefix": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", "dev": true, "license": "MIT", "dependencies": { "expand-tilde": "^2.0.2", "homedir-polyfill": "^1.0.1", "ini": "^1.3.4", "is-windows": "^1.0.1", "which": "^1.2.14" }, "engines": { "node": ">=0.10.0" } }, "node_modules/global-prefix/node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true, "license": "ISC" }, "node_modules/global-prefix/node_modules/which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, "bin": { "which": "bin/which" } }, "node_modules/globals": { "version": "17.13.0", "resolved": "https://registry.npmjs.org/globals/-/globals-17.13.0.tgz", "integrity": "sha512-RwMTC61u7hrG3ZJMkZQOK4JLJrKS8QtoTii63EmWvFXHqycY9FObBJQCbsLxSO8kjKhWE5kV1GC+Vb1g3RI0Zg==", "dev": true, "license": "MIT", "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/glogg": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/glogg/-/glogg-2.2.0.tgz", "integrity": "sha512-eWv1ds/zAlz+M1ioHsyKJomfY7jbDDPpwSkv14KQj89bycx1nvK5/2Cj/T9g7kzJcX5Bc7Yv22FjfBZS/jl94A==", "dev": true, "license": "MIT", "dependencies": { "sparkles": "^2.1.0" }, "engines": { "node": ">= 10.13.0" } }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "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/gulp": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/gulp/-/gulp-5.0.1.tgz", "integrity": "sha512-PErok3DZSA5WGMd6XXV3IRNO0mlB+wW3OzhFJLEec1jSERg2j1bxJ6e5Fh6N6fn3FH2T9AP4UYNb/pYlADB9sA==", "dev": true, "license": "MIT", "dependencies": { "glob-watcher": "^6.0.0", "gulp-cli": "^3.1.0", "undertaker": "^2.0.0", "vinyl-fs": "^4.0.2" }, "bin": { "gulp": "bin/gulp.js" }, "engines": { "node": ">=10.13.0" } }, "node_modules/gulp-cli": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-3.1.0.tgz", "integrity": "sha512-zZzwlmEsTfXcxRKiCHsdyjZZnFvXWM4v1NqBJSYbuApkvVKivjcmOS2qruAJ+PkEHLFavcDKH40DPc1+t12a9Q==", "dev": true, "license": "MIT", "dependencies": { "@gulpjs/messages": "^1.1.0", "chalk": "^4.1.2", "copy-props": "^4.0.0", "gulplog": "^2.2.0", "interpret": "^3.1.1", "liftoff": "^5.0.1", "mute-stdout": "^2.0.0", "replace-homedir": "^2.0.0", "semver-greatest-satisfied-range": "^2.0.0", "string-width": "^4.2.3", "v8flags": "^4.0.0", "yargs": "^16.2.0" }, "bin": { "gulp": "bin/gulp.js" }, "engines": { "node": ">=10.13.0" } }, "node_modules/gulp-cli/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/gulp-cli/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/gulp-cli/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/gulp-cli/node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "node_modules/gulp-cli/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/gulp-cli/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/gulp-cli/node_modules/yargs": { "version": "16.2.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.2.tgz", "integrity": "sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==", "dev": true, "license": "MIT", "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" }, "engines": { "node": ">=10" } }, "node_modules/gulp-cli/node_modules/yargs-parser": { "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/gulplog": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-2.2.0.tgz", "integrity": "sha512-V2FaKiOhpR3DRXZuYdRLn/qiY0yI5XmqbTKrYbdemJ+xOh2d2MOweI/XFgMzd/9+1twdvMwllnZbWZNJ+BOm4A==", "dev": true, "license": "MIT", "dependencies": { "glogg": "^2.2.0" }, "engines": { "node": ">= 10.13.0" } }, "node_modules/gzip-size": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-3.0.0.tgz", "integrity": "sha512-6s8trQiK+OMzSaCSVXX+iqIcLV9tC+E73jrJrJTyS4h/AJhlxHvzFKqM1YLDJWRGgHX8uLkBeXkA0njNj39L4w==", "dev": true, "license": "MIT", "dependencies": { "duplexer": "^0.1.1" }, "engines": { "node": ">=0.12.0" } }, "node_modules/has-ansi": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^2.0.0" }, "engines": { "node": ">=0.10.0" } }, "node_modules/has-ansi/node_modules/ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "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-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "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==", "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/hashery": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.5.1.tgz", "integrity": "sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==", "dev": true, "license": "MIT", "dependencies": { "hookified": "^1.15.0" }, "engines": { "node": ">=20" } }, "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==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", "dev": true, "license": "MIT", "dependencies": { "parse-passwd": "^1.0.0" }, "engines": { "node": ">=0.10.0" } }, "node_modules/hookified": { "version": "1.15.1", "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==", "dev": true, "license": "MIT" }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "dev": true, "license": "MIT", "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "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==", "license": "MIT", "dependencies": { "agent-base": "6", "debug": "4" }, "engines": { "node": ">= 6" } }, "node_modules/husky": { "version": "9.1.7", "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", "dev": true, "license": "MIT", "bin": { "husky": "bin.js" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/typicode" } }, "node_modules/iconv-lite": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "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": "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/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-fresh/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/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/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/ini": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/ini/-/ini-6.0.0.tgz", "integrity": "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==", "dev": true, "license": "ISC", "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/interpret": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", "dev": true, "license": "MIT", "engines": { "node": ">=10.13.0" } }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.10" } }, "node_modules/is-absolute": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", "dev": true, "license": "MIT", "dependencies": { "is-relative": "^1.0.0", "is-windows": "^1.0.1" }, "engines": { "node": ">=0.10.0" } }, "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-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" }, "engines": { "node": ">=8" } }, "node_modules/is-core-module": { "version": "2.16.2", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "dev": true, "license": "MIT", "dependencies": { "hasown": "^2.0.3" }, "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-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-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", "dev": true, "license": "MIT" }, "node_modules/is-negated-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", "integrity": "sha512-czXVVn/QEmgvej1f50BZ648vUI+em0xqMq2Sn+QncCLN4zj1UAxlT+kw/6ggQTOaZPd1HqKQGEqbpQVtJucWug==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" } }, "node_modules/is-plain-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", "dev": true, "license": "MIT", "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-plain-object": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "dev": true, "license": "MIT" }, "node_modules/is-reference": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", "dev": true, "license": "MIT", "dependencies": { "@types/estree": "*" } }, "node_modules/is-relative": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "dev": true, "license": "MIT", "dependencies": { "is-unc-path": "^1.0.0" }, "engines": { "node": ">=0.10.0" } }, "node_modules/is-stream": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", "dev": true, "license": "MIT", "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-unc-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", "dev": true, "license": "MIT", "dependencies": { "unc-path-regex": "^0.1.2" }, "engines": { "node": ">=0.10.0" } }, "node_modules/is-valid-glob": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", "integrity": "sha512-AhiROmoEFDSsjx8hW+5sGwgKVIORcXnrlAx/R0ZSeaPw70Vw0CqkGBBhHGL58Uox2eXnU1AnvXJl1XlyedO5bA==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "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/isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/jiti": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "dev": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" } }, "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.2", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "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-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": "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/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": "5.6.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "dev": true, "license": "MIT", "dependencies": { "@keyv/serialize": "^1.1.1" } }, "node_modules/last-run": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/last-run/-/last-run-2.0.0.tgz", "integrity": "sha512-j+y6WhTLN4Itnf9j5ZQos1BGPCS8DAwmgMroR3OzfxAsBxam0hMw7J8M3KqZl0pLQJ1jNnwIexg5DYpC/ctwEQ==", "dev": true, "license": "MIT", "engines": { "node": ">= 10.13.0" } }, "node_modules/lead": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/lead/-/lead-4.0.0.tgz", "integrity": "sha512-DpMa59o5uGUWWjruMp71e6knmwKU3jRBBn1kjuLWN9EeIOxNeSAwvHf03WIl8g/ZMR2oSQC9ej3yeLBwdDc/pg==", "dev": true, "license": "MIT", "engines": { "node": ">=10.13.0" } }, "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/liftoff": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-5.0.1.tgz", "integrity": "sha512-wwLXMbuxSF8gMvubFcFRp56lkFV69twvbU5vDPbaw+Q+/rF8j0HKjGbIdlSi+LuJm9jf7k9PB+nTxnsLMPcv2Q==", "dev": true, "license": "MIT", "dependencies": { "extend": "^3.0.2", "findup-sync": "^5.0.0", "fined": "^2.0.0", "flagged-respawn": "^2.0.0", "is-plain-object": "^5.0.0", "rechoir": "^0.8.0", "resolve": "^1.20.0" }, "engines": { "node": ">=10.13.0" } }, "node_modules/lightningcss": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", "dev": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" }, "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "node_modules/lightningcss-android-arm64": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", "cpu": [ "arm64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "android" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-darwin-arm64": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", "cpu": [ "arm64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "darwin" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-darwin-x64": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", "cpu": [ "x64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "darwin" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-freebsd-x64": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", "cpu": [ "x64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "freebsd" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-arm-gnueabihf": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", "cpu": [ "arm" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-arm64-gnu": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", "cpu": [ "arm64" ], "dev": true, "libc": [ "glibc" ], "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-arm64-musl": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", "cpu": [ "arm64" ], "dev": true, "libc": [ "musl" ], "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-x64-gnu": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", "cpu": [ "x64" ], "dev": true, "libc": [ "glibc" ], "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-x64-musl": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", "cpu": [ "x64" ], "dev": true, "libc": [ "musl" ], "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-win32-arm64-msvc": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", "cpu": [ "arm64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "win32" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-win32-x64-msvc": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", "cpu": [ "x64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "win32" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/limiter": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==", "dev": true }, "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/lint-staged": { "version": "17.6.0", "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-17.6.0.tgz", "integrity": "sha512-nOhBfYkZEwkFtNzD5pK9s9LkcWoxJ+whLzEnhqi9nl7zTB5n7tg8+9iYgMGx2Wm3VlLu8cbNj0RACzY+nWi5iQ==", "dev": true, "license": "MIT", "dependencies": { "picomatch": "^4.0.7", "string-argv": "^0.3.2", "tinyexec": "^1.3.1" }, "bin": { "lint-staged": "bin/lint-staged.js" }, "engines": { "node": ">=22.22.1" }, "funding": { "url": "https://opencollective.com/lint-staged" }, "optionalDependencies": { "yaml": "^2.9.1" } }, "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.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true, "license": "MIT" }, "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/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "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==", "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/maxmin": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/maxmin/-/maxmin-2.1.0.tgz", "integrity": "sha512-NWlApBjW9az9qRPaeg7CX4sQBWwytqz32bIEo1PW9pRW+kBP9KLRfJO3UC+TV31EcQZEUq7eMzikC7zt3zPJcw==", "dev": true, "license": "MIT", "dependencies": { "chalk": "^1.0.0", "figures": "^1.0.1", "gzip-size": "^3.0.0", "pretty-bytes": "^3.0.0" }, "engines": { "node": ">=0.12" } }, "node_modules/maxmin/node_modules/ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/maxmin/node_modules/ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/maxmin/node_modules/chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", "has-ansi": "^2.0.0", "strip-ansi": "^3.0.0", "supports-color": "^2.0.0" }, "engines": { "node": ">=0.10.0" } }, "node_modules/maxmin/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/maxmin/node_modules/strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^2.0.0" }, "engines": { "node": ">=0.10.0" } }, "node_modules/maxmin/node_modules/supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true, "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/media-typer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/merge-descriptors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "dev": true, "license": "MIT", "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { "node": ">=8.6" } }, "node_modules/micromatch/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/mime-db": { "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "dev": true, "license": "MIT", "dependencies": { "mime-db": "^1.54.0" }, "engines": { "node": ">=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "node_modules/minimatch": { "version": "10.2.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.5" }, "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/mrmime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", "dev": true, "license": "MIT", "engines": { "node": ">=10" } }, "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==", "license": "MIT" }, "node_modules/multer": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/multer/-/multer-2.4.0.tgz", "integrity": "sha512-7dqa0ZcFfzbefdTuIkzOSMvZWC0J7FLqBOjJUZvDCXShIURWKxAyTT1wHhnE5q19c7jOJf43IYYKjBmZVZmvhg==", "dev": true, "license": "MIT", "dependencies": { "append-field": "^1.0.0", "busboy": "^1.6.0", "type-is": "^1.6.18" }, "engines": { "node": ">= 10.16.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "node_modules/multer/node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/multer/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/multer/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/multer/node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dev": true, "license": "MIT", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" }, "engines": { "node": ">= 0.6" } }, "node_modules/mute-stdout": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-2.0.0.tgz", "integrity": "sha512-32GSKM3Wyc8dg/p39lWPKYu8zci9mJFzV1Np9Of0ZEpe6Fhssn/FbI7ywAMd40uX+p3ZKh3T5EeCFv81qS3HmQ==", "dev": true, "license": "MIT", "engines": { "node": ">= 10.13.0" } }, "node_modules/nanoid": { "version": "3.3.16", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, "engines": { "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, "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/negotiator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/node-releases": { "version": "2.0.54", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz", "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==", "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/now-and-later": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-3.0.0.tgz", "integrity": "sha512-pGO4pzSdaxhWTGkfSfHx3hVzJVslFPwBp2Myq9MYN/ChfJZF87ochMAXnvz6/58RJSf5ik2q9tXprBBrk2cpcg==", "dev": true, "license": "MIT", "dependencies": { "once": "^1.4.0" }, "engines": { "node": ">= 10.13.0" } }, "node_modules/number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object.defaults": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", "integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==", "dev": true, "license": "MIT", "dependencies": { "array-each": "^1.0.1", "array-slice": "^1.0.0", "for-own": "^1.0.0", "isobject": "^3.0.0" }, "engines": { "node": ">=0.10.0" } }, "node_modules/object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", "dev": true, "license": "MIT", "dependencies": { "isobject": "^3.0.1" }, "engines": { "node": ">=0.10.0" } }, "node_modules/obug": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz", "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==", "dev": true, "funding": [ "https://github.com/sponsors/sxzz", "https://opencollective.com/debug" ], "license": "MIT", "engines": { "node": ">=12.20.0" } }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, "engines": { "node": ">= 0.8" } }, "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/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/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-filepath": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", "dev": true, "license": "MIT", "dependencies": { "is-absolute": "^1.0.0", "map-cache": "^0.2.0", "path-root": "^0.1.1" }, "engines": { "node": ">=0.8" } }, "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/parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" } }, "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-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-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true, "license": "MIT" }, "node_modules/path-root": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", "dev": true, "license": "MIT", "dependencies": { "path-root-regex": "^0.1.0" }, "engines": { "node": ">=0.10.0" } }, "node_modules/path-root-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/path-to-regexp": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", "dev": true, "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT" }, "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.7", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/pkijs": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.4.0.tgz", "integrity": "sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "@noble/hashes": "1.4.0", "asn1js": "^3.0.6", "bytestreamjs": "^2.0.1", "pvtsutils": "^1.3.6", "pvutils": "^1.1.3", "tslib": "^2.8.1" }, "engines": { "node": ">=16.0.0" } }, "node_modules/pkijs/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/playwright": { "version": "1.64.0", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.64.0.tgz", "integrity": "sha512-kVzTnYFYEyQ9RGzBjNCd2+yEqWmVbxl3AHPynv3qQ6JTjv4a+kwRcnqQwIpuP3NnrnIrE/Q6wWAIAvMfIE2/Yg==", "dev": true, "license": "Apache-2.0", "dependencies": { "playwright-core": "1.64.0" }, "bin": { "playwright": "cli.js" }, "engines": { "node": ">=20" } }, "node_modules/playwright-core": { "version": "1.64.0", "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.64.0.tgz", "integrity": "sha512-T9r+MZkTECl2+oUcZ26YgZzTrYidTsIhYYkJix+6iDOymOb1FB2RLLSOxRGnZGv5DStt+aduIVbt+E8WygXYpg==", "dev": true, "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" }, "engines": { "node": ">=20" } }, "node_modules/pngjs": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", "dev": true, "license": "MIT", "engines": { "node": ">=14.19.0" } }, "node_modules/postcss": { "version": "8.5.23", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", "dev": true, "funding": [ { "type": "opencollective", "url": "https://opencollective.com/postcss/" }, { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" }, { "type": "github", "url": "https://github.com/sponsors/ai" } ], "license": "MIT", "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, "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.9.10", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.10.tgz", "integrity": "sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA==", "dev": true, "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" } }, "node_modules/pretty-bytes": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-3.0.1.tgz", "integrity": "sha512-eb7ZAeUTgfh294cElcu51w+OTRp/6ItW758LjwJSK72LDevcuJn0P4eD71PLMDGPwwatXmAmYHTkzvpKlJE3ow==", "dev": true, "license": "MIT", "dependencies": { "number-is-nan": "^1.0.0" }, "engines": { "node": ">=0.10.0" } }, "node_modules/proxy-addr": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", "dev": true, "license": "MIT", "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" }, "engines": { "node": ">= 0.10" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "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==", "license": "MIT", "engines": { "node": ">=10" } }, "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/pvtsutils": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.8.1" } }, "node_modules/pvutils": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.5.tgz", "integrity": "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==", "dev": true, "license": "MIT", "engines": { "node": ">=16.0.0" } }, "node_modules/qified": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/qified/-/qified-0.10.1.tgz", "integrity": "sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==", "dev": true, "license": "MIT", "dependencies": { "hookified": "^2.1.1" }, "engines": { "node": ">=20" } }, "node_modules/qified/node_modules/hookified": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/hookified/-/hookified-2.2.0.tgz", "integrity": "sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==", "dev": true, "license": "MIT" }, "node_modules/qs": { "version": "6.16.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "es-define-property": "^1.0.1", "side-channel": "^1.1.1" }, "engines": { "node": ">=0.6" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/range-parser": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "node_modules/raw-body": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "dev": true, "license": "MIT", "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.10" } }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, "license": "MIT", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" }, "engines": { "node": ">= 6" } }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, "engines": { "node": ">=8.10.0" } }, "node_modules/readdirp/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/rechoir": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "dev": true, "license": "MIT", "dependencies": { "resolve": "^1.20.0" }, "engines": { "node": ">= 10.13.0" } }, "node_modules/reflect-metadata": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", "dev": true, "license": "Apache-2.0" }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", "dev": true, "license": "MIT" }, "node_modules/regenerate-unicode-properties": { "version": "10.2.2", "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", "dev": true, "license": "MIT", "dependencies": { "regenerate": "^1.4.2" }, "engines": { "node": ">=4" } }, "node_modules/regexpu-core": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", "dev": true, "license": "MIT", "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.2", "regjsgen": "^0.8.0", "regjsparser": "^0.13.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.2.1" }, "engines": { "node": ">=4" } }, "node_modules/regjsgen": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", "dev": true, "license": "MIT" }, "node_modules/regjsparser": { "version": "0.13.2", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.2.tgz", "integrity": "sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "jsesc": "~3.1.0" }, "bin": { "regjsparser": "bin/parser" } }, "node_modules/remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", "dev": true, "license": "ISC" }, "node_modules/replace-ext": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==", "dev": true, "license": "MIT", "engines": { "node": ">= 10" } }, "node_modules/replace-homedir": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-2.0.0.tgz", "integrity": "sha512-bgEuQQ/BHW0XkkJtawzrfzHFSN70f/3cNOiHa2QsYxqrjaC30X1k74FJ6xswVBP0sr0SpGIdVFuPwfrYziVeyw==", "dev": true, "license": "MIT", "engines": { "node": ">= 10.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": { "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/resolve-dir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", "dev": true, "license": "MIT", "dependencies": { "expand-tilde": "^2.0.0", "global-modules": "^1.0.0" }, "engines": { "node": ">=0.10.0" } }, "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-options": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-2.0.0.tgz", "integrity": "sha512-/FopbmmFOQCfsCx77BRFdKOniglTiHumLgwvd6IDPihy1GKkadZbgQJBcTb2lMzSR1pndzd96b1nZrreZ7+9/A==", "dev": true, "license": "MIT", "dependencies": { "value-or-function": "^4.0.0" }, "engines": { "node": ">= 10.13.0" } }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" } }, "node_modules/rolldown": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.3.tgz", "integrity": "sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==", "dev": true, "license": "MIT", "dependencies": { "@oxc-project/types": "=0.137.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { "rolldown": "bin/cli.mjs" }, "engines": { "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.1.3", "@rolldown/binding-darwin-arm64": "1.1.3", "@rolldown/binding-darwin-x64": "1.1.3", "@rolldown/binding-freebsd-x64": "1.1.3", "@rolldown/binding-linux-arm-gnueabihf": "1.1.3", "@rolldown/binding-linux-arm64-gnu": "1.1.3", "@rolldown/binding-linux-arm64-musl": "1.1.3", "@rolldown/binding-linux-ppc64-gnu": "1.1.3", "@rolldown/binding-linux-s390x-gnu": "1.1.3", "@rolldown/binding-linux-x64-gnu": "1.1.3", "@rolldown/binding-linux-x64-musl": "1.1.3", "@rolldown/binding-openharmony-arm64": "1.1.3", "@rolldown/binding-wasm32-wasi": "1.1.3", "@rolldown/binding-win32-arm64-msvc": "1.1.3", "@rolldown/binding-win32-x64-msvc": "1.1.3" } }, "node_modules/rollup": { "version": "4.64.6", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.64.6.tgz", "integrity": "sha512-fX1Vy+UNKdXn+xKDnu/HBXbHVr1AY8cSdQj9BU4kLv5aFCMqGpvrVbyUMmjN3DdzWhSAuTbcX8PCNEVaumthYA==", "dev": true, "license": "MIT", "dependencies": { "@types/estree": "1.0.9" }, "bin": { "rollup": "dist/bin/rollup" }, "engines": { "node": ">=18.0.0", "npm": ">=8.0.0" }, "optionalDependencies": { "@napi-rs/lzma-linux-x64-gnu": "1.5.1", "@rollup/rollup-android-arm-eabi": "4.64.6", "@rollup/rollup-android-arm64": "4.64.6", "@rollup/rollup-darwin-arm64": "4.64.6", "@rollup/rollup-darwin-x64": "4.64.6", "@rollup/rollup-freebsd-arm64": "4.64.6", "@rollup/rollup-freebsd-x64": "4.64.6", "@rollup/rollup-linux-arm-gnueabihf": "4.64.6", "@rollup/rollup-linux-arm-musleabihf": "4.64.6", "@rollup/rollup-linux-arm64-gnu": "4.64.6", "@rollup/rollup-linux-arm64-musl": "4.64.6", "@rollup/rollup-linux-loong64-gnu": "4.64.6", "@rollup/rollup-linux-loong64-musl": "4.64.6", "@rollup/rollup-linux-ppc64-gnu": "4.64.6", "@rollup/rollup-linux-ppc64-musl": "4.64.6", "@rollup/rollup-linux-riscv64-gnu": "4.64.6", "@rollup/rollup-linux-riscv64-musl": "4.64.6", "@rollup/rollup-linux-s390x-gnu": "4.64.6", "@rollup/rollup-linux-x64-gnu": "4.64.6", "@rollup/rollup-linux-x64-musl": "4.64.6", "@rollup/rollup-openbsd-x64": "4.64.6", "@rollup/rollup-openharmony-arm64": "4.64.6", "@rollup/rollup-win32-arm64-msvc": "4.64.6", "@rollup/rollup-win32-ia32-msvc": "4.64.6", "@rollup/rollup-win32-x64-gnu": "4.64.6", "@rollup/rollup-win32-x64-msvc": "4.64.6", "fsevents": "~2.3.2" } }, "node_modules/rollup-plugin-bundle-size": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/rollup-plugin-bundle-size/-/rollup-plugin-bundle-size-1.0.3.tgz", "integrity": "sha512-aWj0Pvzq90fqbI5vN1IvUrlf4utOqy+AERYxwWjegH1G8PzheMnrRIgQ5tkwKVtQMDP0bHZEACW/zLDF+XgfXQ==", "dev": true, "license": "MIT", "dependencies": { "chalk": "^1.1.3", "maxmin": "^2.1.0" } }, "node_modules/rollup-plugin-bundle-size/node_modules/ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/rollup-plugin-bundle-size/node_modules/ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/rollup-plugin-bundle-size/node_modules/chalk": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^2.2.1", "escape-string-regexp": "^1.0.2", "has-ansi": "^2.0.0", "strip-ansi": "^3.0.0", "supports-color": "^2.0.0" }, "engines": { "node": ">=0.10.0" } }, "node_modules/rollup-plugin-bundle-size/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/rollup-plugin-bundle-size/node_modules/strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^2.0.0" }, "engines": { "node": ">=0.10.0" } }, "node_modules/rollup-plugin-bundle-size/node_modules/supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", "dev": true, "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "dev": true, "license": "MIT", "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" }, "engines": { "node": ">= 18" } }, "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/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "license": "MIT" }, "node_modules/selfsigned": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-5.5.0.tgz", "integrity": "sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew==", "dev": true, "license": "MIT", "dependencies": { "@peculiar/x509": "^1.14.2", "pkijs": "^3.3.3" }, "engines": { "node": ">=18" } }, "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/semver-greatest-satisfied-range": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-2.0.0.tgz", "integrity": "sha512-lH3f6kMbwyANB7HuOWRMlLCa2itaCrZJ+SAqqkSZrZKO/cAsk2EOyaKHUtNkVLFyFW9pct22SFesFp3Z7zpA0g==", "dev": true, "license": "MIT", "dependencies": { "sver": "^1.8.3" }, "engines": { "node": ">= 10.13.0" } }, "node_modules/send": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "dev": true, "license": "MIT", "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" }, "engines": { "node": ">= 18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "node_modules/serialize-javascript": { "version": "7.0.7", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.7.tgz", "integrity": "sha512-YAy8Od6KV+uuwUuU50np8fGB/Aues6Y0nAhA9y/hId74PlKUcme4pXcBD46NWKr1Q4osN/iseZ17YqO1XfmI8g==", "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=20.0.0" } }, "node_modules/serve-static": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "dev": true, "license": "MIT", "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" }, "engines": { "node": ">= 18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true, "license": "ISC" }, "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/side-channel": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4", "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/side-channel-list": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/side-channel-map": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/side-channel-weakmap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true, "license": "ISC" }, "node_modules/sirv": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", "dev": true, "license": "MIT", "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" }, "engines": { "node": ">=18" } }, "node_modules/smob": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/smob/-/smob-1.6.2.tgz", "integrity": "sha512-RQsvleCbF8cVHEv+xuDGaA4pOizFqJ0GgjtMSRo6oP8pnN7WsigHgVGey6aILRBKv4W2YOMHLqbKdnB6hpB9fw==", "dev": true, "license": "MIT", "engines": { "node": ">=20.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-js": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "node_modules/sparkles": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-2.1.0.tgz", "integrity": "sha512-r7iW1bDw8R/cFifrD3JnQJX0K1jqT0kprL48BiBpLZLJPmAm34zsVBsK5lc7HirZYZqMW65dOXZgbAGt/I6frg==", "dev": true, "license": "MIT", "engines": { "node": ">= 10.13.0" } }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true, "license": "MIT" }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/std-env": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", "dev": true, "license": "MIT" }, "node_modules/stream-composer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-composer/-/stream-composer-1.0.2.tgz", "integrity": "sha512-bnBselmwfX5K10AH6L4c8+S5lgZMWI7ZYrz2rvYjCPB2DIMC4Ig8OpxGpNJSxRZ58oti7y1IcNvjBAz9vW5m4w==", "dev": true, "license": "MIT", "dependencies": { "streamx": "^2.13.2" } }, "node_modules/stream-exhaust": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", "dev": true, "license": "MIT" }, "node_modules/stream-throttle": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/stream-throttle/-/stream-throttle-0.1.3.tgz", "integrity": "sha512-889+B9vN9dq7/vLbGyuHeZ6/ctf5sNuGWsDy89uNxkFTAgzy0eK7+w5fL3KLNRTkLle7EgZGvHUphZW0Q26MnQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "commander": "^2.2.0", "limiter": "^1.0.5" }, "bin": { "throttleproxy": "bin/throttleproxy.js" }, "engines": { "node": ">= 0.10.0" } }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", "dev": true, "engines": { "node": ">=10.0.0" } }, "node_modules/streamx": { "version": "2.28.0", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz", "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==", "dev": true, "license": "MIT", "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" } }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" } }, "node_modules/string-argv": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", "dev": true, "license": "MIT", "engines": { "node": ">=0.6.19" } }, "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/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/string-width/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/string-width/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": { "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/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/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/sver": { "version": "1.8.4", "resolved": "https://registry.npmjs.org/sver/-/sver-1.8.4.tgz", "integrity": "sha512-71o1zfzyawLfIWBOmw8brleKyvnbn73oVHNCsu51uPMz/HWiKkkXsI31JjHW5zqXEqnPYkIiHd8ZmL7FCimLEA==", "dev": true, "license": "MIT", "optionalDependencies": { "semver": "^6.3.0" } }, "node_modules/teex": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", "dev": true, "license": "MIT", "dependencies": { "streamx": "^2.12.5" } }, "node_modules/terser": { "version": "5.48.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz", "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" }, "engines": { "node": ">=10" } }, "node_modules/text-decoder": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", "dev": true, "license": "Apache-2.0", "dependencies": { "b4a": "^1.6.4" } }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, "license": "MIT" }, "node_modules/tinyexec": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", "dev": true, "license": "MIT", "engines": { "node": ">=18" } }, "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/tinyrainbow": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", "dev": true, "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, "engines": { "node": ">=8.0" } }, "node_modules/to-through": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/to-through/-/to-through-3.0.0.tgz", "integrity": "sha512-y8MN937s/HVhEoBU1SxfHC+wxCHkV1a9gW8eAdTadYh/bGyesZIVcbjI+mSpFbSVwQici/XjBjuUyri1dnXwBw==", "dev": true, "license": "MIT", "dependencies": { "streamx": "^2.12.5" }, "engines": { "node": ">=10.13.0" } }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true, "license": "MIT", "engines": { "node": ">=0.6" } }, "node_modules/totalist": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, "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" }, "node_modules/tsyringe": { "version": "4.10.0", "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.10.0.tgz", "integrity": "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==", "dev": true, "license": "MIT", "dependencies": { "tslib": "^1.9.3" }, "engines": { "node": ">= 6.0.0" } }, "node_modules/tsyringe/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true, "license": "0BSD" }, "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-is": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", "dev": true, "license": "MIT", "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" }, "engines": { "node": ">= 18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "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/unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/undertaker": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-2.0.0.tgz", "integrity": "sha512-tO/bf30wBbTsJ7go80j0RzA2rcwX6o7XPBpeFcb+jzoeb4pfMM2zUeSDIkY1AWqeZabWxaQZ/h8N9t35QKDLPQ==", "dev": true, "license": "MIT", "dependencies": { "bach": "^2.0.1", "fast-levenshtein": "^3.0.0", "last-run": "^2.0.0", "undertaker-registry": "^2.0.0" }, "engines": { "node": ">=10.13.0" } }, "node_modules/undertaker-registry": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-2.0.0.tgz", "integrity": "sha512-+hhVICbnp+rlzZMgxXenpvTxpuvA67Bfgtt+O9WOE5jo7w/dyiF1VmoZVIHvP2EkUjsyKyTwYKlLhA+j47m1Ew==", "dev": true, "license": "MIT", "engines": { "node": ">= 10.13.0" } }, "node_modules/undertaker/node_modules/fast-levenshtein": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz", "integrity": "sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ==", "dev": true, "license": "MIT", "dependencies": { "fastest-levenshtein": "^1.0.7" } }, "node_modules/undici-types": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", "dev": true, "license": "MIT", "peer": true }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", "dev": true, "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/unicode-match-property-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "dev": true, "license": "MIT", "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" }, "engines": { "node": ">=4" } }, "node_modules/unicode-match-property-value-ecmascript": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", "dev": true, "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/unicode-property-aliases-ecmascript": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", "dev": true, "license": "MIT", "engines": { "node": ">=4" } }, "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/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/update-browserslist-db": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz", "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==", "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/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true, "license": "MIT" }, "node_modules/v8flags": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-4.0.1.tgz", "integrity": "sha512-fcRLaS4H/hrZk9hYwbdRM35D0U8IYMfEClhXxCivOojl+yTRAZH3Zy2sSy6qVCiGbV9YAtPssP6jaChqC9vPCg==", "dev": true, "license": "MIT", "engines": { "node": ">= 10.13.0" } }, "node_modules/value-or-function": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-4.0.0.tgz", "integrity": "sha512-aeVK81SIuT6aMJfNo9Vte8Dw0/FZINGBV8BfCraGtqVxIeLAEhJyoWs8SmvRVmXfGss2PmmOwZCuBPbZR+IYWg==", "dev": true, "license": "MIT", "engines": { "node": ">= 10.13.0" } }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/vinyl": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.1.tgz", "integrity": "sha512-0QwqXteBNXgnLCdWdvPQBX6FXRHtIH3VhJPTd5Lwn28tJXc34YqSCWUmkOvtJHBmB3gGoPtrOKk3Ts8/kEZ9aA==", "dev": true, "license": "MIT", "dependencies": { "clone": "^2.1.2", "remove-trailing-separator": "^1.1.0", "replace-ext": "^2.0.0", "teex": "^1.0.1" }, "engines": { "node": ">=10.13.0" } }, "node_modules/vinyl-contents": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/vinyl-contents/-/vinyl-contents-2.0.0.tgz", "integrity": "sha512-cHq6NnGyi2pZ7xwdHSW1v4Jfnho4TEGtxZHw01cmnc8+i7jgR6bRnED/LbrKan/Q7CvVLbnvA5OepnhbpjBZ5Q==", "dev": true, "license": "MIT", "dependencies": { "bl": "^5.0.0", "vinyl": "^3.0.0" }, "engines": { "node": ">=10.13.0" } }, "node_modules/vinyl-fs": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-4.0.2.tgz", "integrity": "sha512-XRFwBLLTl8lRAOYiBqxY279wY46tVxLaRhSwo3GzKEuLz1giffsOquWWboD/haGf5lx+JyTigCFfe7DWHoARIA==", "dev": true, "license": "MIT", "dependencies": { "fs-mkdirp-stream": "^2.0.1", "glob-stream": "^8.0.3", "graceful-fs": "^4.2.11", "iconv-lite": "^0.6.3", "is-valid-glob": "^1.0.0", "lead": "^4.0.0", "normalize-path": "3.0.0", "resolve-options": "^2.0.0", "stream-composer": "^1.0.2", "streamx": "^2.14.0", "to-through": "^3.0.0", "value-or-function": "^4.0.0", "vinyl": "^3.0.1", "vinyl-sourcemap": "^2.0.0" }, "engines": { "node": ">=10.13.0" } }, "node_modules/vinyl-fs/node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" } }, "node_modules/vinyl-sourcemap": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-2.0.0.tgz", "integrity": "sha512-BAEvWxbBUXvlNoFQVFVHpybBbjW1r03WhohJzJDSfgrrK5xVYIDTan6xN14DlyImShgDRv2gl9qhM6irVMsV0Q==", "dev": true, "license": "MIT", "dependencies": { "convert-source-map": "^2.0.0", "graceful-fs": "^4.2.10", "now-and-later": "^3.0.0", "streamx": "^2.12.5", "vinyl": "^3.0.0", "vinyl-contents": "^2.0.0" }, "engines": { "node": ">=10.13.0" } }, "node_modules/vite": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.2.tgz", "integrity": "sha512-6YYPbRXTxx6bRXmOn7XdnQAy5DQNHhDgtjhDHI13oe4pY93kkcdGJWxpGwOm++/Wh0QpQhDrpIoVMrmrsI5AGQ==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.16", "rolldown": "~1.1.3", "tinyglobby": "^0.2.17" }, "bin": { "vite": "bin/vite.js" }, "engines": { "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.3.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, "@vitejs/devtools": { "optional": true }, "esbuild": { "optional": true }, "jiti": { "optional": true }, "less": { "optional": true }, "sass": { "optional": true }, "sass-embedded": { "optional": true }, "stylus": { "optional": true }, "sugarss": { "optional": true }, "terser": { "optional": true }, "tsx": { "optional": true }, "yaml": { "optional": true } } }, "node_modules/vitest": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", "dev": true, "license": "MIT", "dependencies": { "@vitest/expect": "4.1.11", "@vitest/mocker": "4.1.11", "@vitest/pretty-format": "4.1.11", "@vitest/runner": "4.1.11", "@vitest/snapshot": "4.1.11", "@vitest/spy": "4.1.11", "@vitest/utils": "4.1.11", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { "node": "^20.0.0 || ^22.0.0 || >=24.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.11", "@vitest/browser-preview": "4.1.11", "@vitest/browser-webdriverio": "4.1.11", "@vitest/coverage-istanbul": "4.1.11", "@vitest/coverage-v8": "4.1.11", "@vitest/ui": "4.1.11", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { "optional": true }, "@opentelemetry/api": { "optional": true }, "@types/node": { "optional": true }, "@vitest/browser-playwright": { "optional": true }, "@vitest/browser-preview": { "optional": true }, "@vitest/browser-webdriverio": { "optional": true }, "@vitest/coverage-istanbul": { "optional": true }, "@vitest/coverage-v8": { "optional": true }, "@vitest/ui": { "optional": true }, "happy-dom": { "optional": true }, "jsdom": { "optional": true }, "vite": { "optional": false } } }, "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/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" }, "engines": { "node": ">=8" } }, "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/workerpool": { "version": "9.3.4", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", "dev": true, "license": "Apache-2.0" }, "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/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": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", "dev": true, "license": "ISC", "optional": true, "bin": { "yaml": "bin.mjs" }, "engines": { "node": ">= 14.6" }, "funding": { "url": "https://github.com/sponsors/eemeli" } }, "node_modules/yargs": { "version": "18.0.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", "dev": true, "license": "MIT", "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "string-width": "^7.2.0", "y18n": "^5.0.5", "yargs-parser": "^22.0.0" }, "engines": { "node": "^20.19.0 || ^22.12.0 || >=23" } }, "node_modules/yargs-parser": { "version": "22.0.0", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", "dev": true, "license": "ISC", "engines": { "node": "^20.19.0 || ^22.12.0 || >=23" } }, "node_modules/yargs/node_modules/emoji-regex": { "version": "10.6.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "dev": true, "license": "MIT" }, "node_modules/yargs/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "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" } } } } axios-axios-aa54a5e/package.json000066400000000000000000000150021526660636600167510ustar00rootroot00000000000000{ "name": "axios", "version": "1.21.0", "description": "Promise based HTTP client for the browser and node.js", "main": "./dist/node/axios.cjs", "module": "./index.js", "type": "module", "types": "index.d.ts", "jsdelivr": "dist/axios.min.js", "unpkg": "dist/axios.min.js", "typings": "./index.d.ts", "exports": { ".": { "types": { "require": "./index.d.cts", "default": "./index.d.ts" }, "bun": { "require": "./dist/node/axios.cjs", "default": "./index.js" }, "react-native": { "require": "./dist/browser/axios.cjs", "default": "./dist/esm/axios.js" }, "browser": { "require": "./dist/browser/axios.cjs", "default": "./index.js" }, "default": { "require": "./dist/node/axios.cjs", "default": "./index.js" } }, "./lib/adapters/http.js": "./lib/adapters/http.js", "./lib/adapters/xhr.js": "./lib/adapters/xhr.js", "./unsafe/*": "./lib/*", "./unsafe/core/settle.js": "./lib/core/settle.js", "./unsafe/core/buildFullPath.js": "./lib/core/buildFullPath.js", "./unsafe/helpers/isAbsoluteURL.js": "./lib/helpers/isAbsoluteURL.js", "./unsafe/helpers/buildURL.js": "./lib/helpers/buildURL.js", "./unsafe/helpers/combineURLs.js": "./lib/helpers/combineURLs.js", "./unsafe/adapters/http.js": "./lib/adapters/http.js", "./unsafe/adapters/xhr.js": "./lib/adapters/xhr.js", "./unsafe/utils.js": "./lib/utils.js", "./package.json": "./package.json", "./dist/browser/axios.cjs": "./dist/browser/axios.cjs", "./dist/node/axios.cjs": "./dist/node/axios.cjs" }, "browser": { "./dist/node/axios.cjs": "./dist/browser/axios.cjs", "./lib/adapters/http.js": "./lib/helpers/null.js", "./lib/platform/node/index.js": "./lib/platform/browser/index.js", "./lib/platform/node/classes/Buffer.js": "./lib/helpers/null.js", "./lib/platform/node/classes/FormData.js": "./lib/helpers/null.js" }, "react-native": { "./dist/node/axios.cjs": "./dist/browser/axios.cjs", "./lib/adapters/http.js": "./lib/helpers/null.js", "./lib/platform/node/index.js": "./lib/platform/browser/index.js", "./lib/platform/node/classes/Buffer.js": "./lib/helpers/null.js", "./lib/platform/node/classes/FormData.js": "./lib/helpers/null.js" }, "repository": { "type": "git", "url": "https://github.com/axios/axios.git" }, "keywords": [ "xhr", "http", "ajax", "promise", "node", "browser", "fetch", "rest", "api", "client" ], "author": "Matt Zabriskie", "contributors": [ "Matt Zabriskie (https://github.com/mzabriskie)", "Jay (https://github.com/jasonsaayman)", "Dmitriy Mozgovoy (https://github.com/DigitalBrainJS)", "Nick Uraltsev (https://github.com/nickuraltsev)", "Emily Morehouse (https://github.com/emilyemorehouse)", "Rubén Norte (https://github.com/rubennorte)", "Justin Beckwith (https://github.com/JustinBeckwith)", "Martti Laine (https://github.com/codeclown)", "Xianming Zhong (https://github.com/chinesedfan)", "Shaan Majid (https://github.com/shaanmajid)", "Remco Haszing (https://github.com/remcohaszing)", "Willian Agostini (https://github.com/WillianAgostini)", "Rikki Gibson (https://github.com/RikkiGibson)" ], "sideEffects": false, "license": "MIT", "bugs": { "url": "https://github.com/axios/axios/issues" }, "homepage": "https://axios-http.com", "files": [ "index.js", "index.d.ts", "index.d.cts", "CHANGELOG.md", "MIGRATION_GUIDE.md", "lib/", "dist/axios.js", "dist/axios.min.js", "dist/axios.min.js.map", "dist/esm/axios.js", "dist/esm/axios.min.js", "dist/esm/axios.min.js.map", "dist/browser/axios.cjs", "dist/node/axios.cjs" ], "scripts": { "build": "gulp clear && cross-env NODE_ENV=production rollup -c", "version": "npm run build && git add package.json", "preversion": "gulp version", "test": "npm run test:vitest", "test:vitest": "vitest run", "test:vitest:unit": "vitest run --project unit", "test:vitest:browser": "vitest run --project browser", "test:vitest:browser:headless": "vitest run --project browser-headless", "test:vitest:watch": "vitest", "test:smoke:cjs:vitest": "npm --prefix tests/smoke/cjs run test:smoke:cjs:mocha", "test:smoke:esm:vitest": "npm --prefix tests/smoke/esm run test:smoke:esm:vitest", "test:smoke:deno": "deno task --cwd tests/smoke/deno test", "test:smoke:bun": "bun test --cwd tests/smoke/bun", "test:module:cjs": "npm --prefix tests/module/cjs run test:module:cjs", "test:module:esm": "npm --prefix tests/module/esm run test:module:esm", "docs:dev": "cd docs && npm run docs:dev", "start": "node ./sandbox/server.js", "examples": "node ./examples/server.js", "lint": "eslint lib/**/*.js", "fix": "eslint --fix lib/**/*.js", "prepare": "husky" }, "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.6", "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" }, "devDependencies": { "@babel/core": "^7.29.0", "@babel/preset-env": "^7.29.5", "@commitlint/cli": "^21.2.0", "@commitlint/config-conventional": "^21.2.0", "@eslint/js": "^10.0.1", "@rollup/plugin-alias": "^6.0.0", "@rollup/plugin-babel": "^7.1.0", "@rollup/plugin-commonjs": "^29.0.3", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.3", "@rollup/plugin-terser": "^1.0.0", "@vitest/browser": "^4.1.9", "@vitest/browser-playwright": "^4.1.9", "abortcontroller-polyfill": "^1.7.8", "acorn": "^8.17.0", "body-parser": "^2.3.0", "cross-env": "^10.1.0", "dev-null": "^0.1.1", "eslint": "^10.6.0", "express": "^5.2.1", "formdata-node": "^6.0.3", "formidable": "^3.5.4", "fs-extra": "^11.3.6", "get-stream": "^9.0.1", "globals": "^17.7.0", "gulp": "^5.0.1", "husky": "^9.1.7", "lint-staged": "^17.0.8", "minimist": "^1.2.8", "multer": "^2.2.0", "playwright": "^1.61.1", "prettier": "^3.9.4", "rollup": "^4.60.4", "rollup-plugin-bundle-size": "^1.0.3", "selfsigned": "^5.5.0", "stream-throttle": "^0.1.3", "typescript": "^5.9.3", "vitest": "^4.1.9" }, "commitlint": { "rules": { "header-max-length": [ 2, "always", 130 ] }, "extends": [ "@commitlint/config-conventional" ] }, "lint-staged": { "*.{js,cjs,mjs,ts,json,md,yml,yaml}": "prettier --write" } }axios-axios-aa54a5e/rollup.config.js000066400000000000000000000064211526660636600176070ustar00rootroot00000000000000import resolve from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; import terser from '@rollup/plugin-terser'; import json from '@rollup/plugin-json'; import { babel } from '@rollup/plugin-babel'; import bundleSize from 'rollup-plugin-bundle-size'; import aliasPlugin from '@rollup/plugin-alias'; import path from 'path'; import { createRequire } from 'module'; const require = createRequire(import.meta.url); const lib = require('./package.json'); const outputFileName = 'axios'; const name = 'axios'; const namedInput = './index.js'; const defaultInput = './lib/axios.js'; const buildConfig = ({ es5, browser = true, minifiedVersion = true, alias, ...config }) => { const { file } = config.output; const ext = path.extname(file); const basename = path.basename(file, ext); const extArr = ext.split('.'); extArr.shift(); const build = ({ minified }) => ({ input: namedInput, ...config, output: { ...config.output, file: `${path.dirname(file)}/${basename}.${(minified ? ['min', ...extArr] : extArr).join('.')}`, sourcemap: minified, }, plugins: [ aliasPlugin({ entries: alias || [], }), json(), resolve({ browser }), commonjs(), minified && terser({ maxWorkers: 1, }), minified && bundleSize(), ...(es5 ? [ babel({ babelHelpers: 'bundled', presets: ['@babel/preset-env'], }), ] : []), ...(config.plugins || []), ], }); const configs = [build({ minified: false })]; if (minifiedVersion) { configs.push(build({ minified: true })); } return configs; }; const nodeCjsExternal = (id) => { if (id === 'proxy-from-env') { return false; } if (id.startsWith('.') || path.isAbsolute(id) || id.startsWith('\0')) { return false; } return true; }; export default async () => { const year = new Date().getFullYear(); const banner = `/*! Axios v${lib.version} Copyright (c) ${year} ${lib.author} and contributors */`; return [ // browser ESM bundle for CDN ...buildConfig({ input: namedInput, output: { file: `dist/esm/${outputFileName}.js`, format: 'esm', exports: 'named', banner, }, }), // Browser UMD bundle for CDN ...buildConfig({ input: defaultInput, es5: true, output: { file: `dist/${outputFileName}.js`, name, format: 'umd', exports: 'default', banner, }, }), // Browser CJS bundle ...buildConfig({ input: defaultInput, es5: false, minifiedVersion: false, output: { file: `dist/browser/${name}.cjs`, name, format: 'cjs', exports: 'default', banner, }, }), // Node.js commonjs bundle (transpiled for Node 12) { input: defaultInput, external: nodeCjsExternal, output: { file: `dist/node/${name}.cjs`, format: 'cjs', exports: 'default', banner, }, plugins: [ resolve(), commonjs(), babel({ babelHelpers: 'bundled', presets: [['@babel/preset-env', { targets: { node: '12' } }]], }), ], }, ]; }; axios-axios-aa54a5e/sandbox/000077500000000000000000000000001526660636600161235ustar00rootroot00000000000000axios-axios-aa54a5e/sandbox/client.html000066400000000000000000000334561526660636600203020ustar00rootroot00000000000000 AXIOS | Sandbox

        axios

         | Sandbox

        Input

        Request

        No Data

        Response

        No Data

        Error

        None
        axios-axios-aa54a5e/sandbox/client.js000066400000000000000000000006121526660636600177360ustar00rootroot00000000000000import axios from '../index.js'; const URL = 'http://127.0.0.1:3000/api'; const BODY = { foo: 'bar', baz: 1234, }; function handleSuccess(data) { console.log(data); } function handleFailure(data) { console.log('error', data); } // GET axios.get(URL, { params: BODY }).then(handleSuccess).catch(handleFailure); // POST axios.post(URL, BODY).then(handleSuccess).catch(handleFailure); axios-axios-aa54a5e/sandbox/server.js000066400000000000000000000060061526660636600177710ustar00rootroot00000000000000import fs from 'fs'; import path from 'path'; import http from 'http'; let server; /** * Pipes a file to the HTTP response. * * @param {http.ServerResponse} res - The HTTP response object. * @param {string} file - The relative path to the file to be served. * @param {string} [type] - Optional MIME type for the response. */ function pipeFileToResponse(res, file, type) { if (type) { res.writeHead(200, { 'Content-Type': type, }); } fs.createReadStream(path.join(path.resolve(), 'sandbox', file)).pipe(res); } /** * Handles API requests to /api. * * Collects request data, parses it as JSON, and returns a JSON response * containing the request URL, method, headers, and parsed data. * * @param {http.IncomingMessage} req - The HTTP request object. * @param {http.ServerResponse} res - The HTTP response object. */ function handleApiRequest(req, res) { let status; let result; let data = ''; req.on('data', (chunk) => { data += chunk; }); req.on('end', () => { try { status = 200; result = { url: req.url, data: data ? JSON.parse(data) : undefined, method: req.method, headers: req.headers, }; } catch (e) { console.error('Error:', e.message); status = 400; result = { error: e.message, }; } res.writeHead(status, { 'Content-Type': 'application/json', }); res.end(JSON.stringify(result)); }); } /** * Handles incoming HTTP requests. * * Serves static files like index.html and axios.js, or routes API requests to * handleApiRequest. Responds with 404 for unrecognized paths. * * @param {http.IncomingMessage} req - The HTTP request object. * @param {http.ServerResponse} res - The HTTP response object. */ function requestHandler(req, res) { req.setEncoding('utf8'); const parsed = new URL(req.url, 'http://localhost'); let pathname = parsed.pathname; console.log('[' + new Date() + ']', req.method, pathname); if (pathname === '/') { pathname = '/index.html'; } switch (pathname) { case '/index.html': pipeFileToResponse(res, './client.html', 'text/html'); break; case '/axios.js': pipeFileToResponse(res, '../dist/axios.js', 'text/javascript'); break; case '/axios.js.map': pipeFileToResponse(res, '../dist/axios.js.map', 'text/javascript'); break; case '/api': handleApiRequest(req, res); break; default: res.writeHead(404); res.end('

        404 Not Found

        '); break; } } const PORT = 3000; // Create and start the HTTP server server = http.createServer(requestHandler); server.listen(PORT, () => { console.log(`Listening on localhost:${PORT}...`); }); /** * Handles server errors, e.g., port already in use. * * @param {NodeJS.ErrnoException} error - The server error object. */ server.on('error', (error) => { if (error.code === 'EADDRINUSE') { console.log(`Address localhost:${PORT} in use. Please retry when the port is available!`); server.close(); } }); axios-axios-aa54a5e/scripts/000077500000000000000000000000001526660636600161545ustar00rootroot00000000000000axios-axios-aa54a5e/scripts/axios-build-instance.js000066400000000000000000000011711526660636600225340ustar00rootroot00000000000000import axios from '../index.js'; const { GITHUB_TOKEN } = process.env; if (GITHUB_TOKEN) { console.log(`[GITHUB_TOKEN OK]`); } else { console.warn(`[GITHUB_TOKEN is not defined]`); } const defaultTransform = axios.defaults.transformRequest; export default axios.create({ transformRequest: [ defaultTransform[0], function (data) { console.log( `[${this.method.toUpperCase()}] Request [${new URL(axios.getUri(this)).pathname}]` ); return data; }, ], baseURL: 'https://api.github.com/', headers: { ...(GITHUB_TOKEN ? { Authorization: `token ${GITHUB_TOKEN}` } : {}), }, }); axios-axios-aa54a5e/scripts/benchmark-merge-config.js000066400000000000000000000064721526660636600230150ustar00rootroot00000000000000// Run with: node scripts/benchmark-merge-config.js [path/to/axios/index.js] // To compare revisions, install dependencies in a baseline checkout and pass // its index.js path. Alternate baseline/current processes on the same machine. // This measures complete Axios requests through a no-I/O adapter, not network // latency. Timings are observations, not assertions or cross-runtime guarantees. import assert from 'node:assert/strict'; import { cpus } from 'node:os'; import { performance } from 'node:perf_hooks'; import { resolve } from 'node:path'; import { pathToFileURL } from 'node:url'; const entry = process.argv[2] ? pathToFileURL(resolve(process.argv[2])) : new URL('../index.js', import.meta.url); const { default: axios } = await import(entry.href); const warmup = 1000; const iterations = 2000; const samples = 9; const payload = { name: 'example', enabled: true }; const adapter = (config) => Promise.resolve({ data: config.data, status: 200, statusText: 'OK', headers: {}, config, }); const headers = (prefix) => Object.fromEntries(Array.from({ length: 20 }, (_, i) => [`X-${prefix}-${i}`, `value-${i}`])); const scenarios = [ { name: 'minimal GET', defaults: {}, request: (client) => client.get('/items'), verify: ({ config }) => assert.equal(config.method, 'get'), }, { name: 'typical POST', defaults: { headers: { 'X-Token': 'default' }, timeout: 5000 }, request: (client) => client.post('/items', payload, { headers: { 'x-token': 'request' }, params: { page: 1 } }), verify: ({ config }) => { assert.equal(config.method, 'post'); assert.equal(config.data, JSON.stringify(payload)); assert.equal(config.headers.get('X-Token'), 'request'); assert.equal(config.params.page, 1); }, }, { name: 'header-heavy POST (20 default + 20 request headers)', defaults: { headers: headers('Default') }, request: (() => { const config = { headers: headers('Request') }; return (client) => client.post('/items', payload, config); })(), verify: ({ config }) => { assert.equal(config.method, 'post'); assert.equal(config.data, JSON.stringify(payload)); for (let i = 0; i < 20; i++) { assert.equal(config.headers.get(`X-Default-${i}`), `value-${i}`); assert.equal(config.headers.get(`X-Request-${i}`), `value-${i}`); } }, }, ]; console.log({ entry: entry.href, node: process.version, platform: process.platform, arch: process.arch, cpu: cpus()[0]?.model, warmup, iterations, samples, }); for (const scenario of scenarios) { const client = axios.create({ ...scenario.defaults, adapter }); scenario.verify(await scenario.request(client)); for (let i = 0; i < warmup; i++) await scenario.request(client); const timings = []; for (let sample = 0; sample < samples; sample++) { let response; const start = performance.now(); for (let i = 0; i < iterations; i++) response = await scenario.request(client); timings.push(((performance.now() - start) * 1000) / iterations); scenario.verify(response); } timings.sort((a, b) => a - b); console.log({ scenario: scenario.name, medianMicroseconds: timings[Math.floor(samples / 2)].toFixed(2), minMicroseconds: timings[0].toFixed(2), maxMicroseconds: timings[samples - 1].toFixed(2), }); } axios-axios-aa54a5e/tests/000077500000000000000000000000001526660636600156275ustar00rootroot00000000000000axios-axios-aa54a5e/tests/README.md000066400000000000000000000077071526660636600171210ustar00rootroot00000000000000# Test Contribution Guide This guide explains how to contribute tests inside the `tests` directory. It is intentionally scoped to this directory only. ## Tests Directory Layout ```text tests/ browser/ # browser runtime tests setup/ # shared test setup utilities smoke/ # package-compat smoke suites (esm + cjs) unit/ # focused unit/behavior tests ``` Use the runtime-first layout already present in this directory: - Put browser-runtime behavior in `tests/browser`. - Put non-browser focused tests in `tests/unit`. - Put packaging/compatibility smoke checks in `tests/smoke/esm/tests` and `tests/smoke/cjs/tests`. - Reuse helpers from `tests/setup` instead of duplicating setup logic. ## File Naming Conventions Follow the existing file patterns: - Unit tests: `*.test.js` - Browser tests: `*.browser.test.js` - ESM smoke tests: `*.smoke.test.js` - CJS smoke tests: `*.smoke.test.cjs` When adding a new test, match the nearest existing file name pattern in the same subdirectory. ## Suite-Specific Authoring Patterns ### Unit (`tests/unit`) - Keep tests focused on one behavior or API surface. - For adapter/network behavior, prefer local test servers using utilities from `tests/setup/server.js`. - Ensure server cleanup with `try/finally` so tests do not leak resources. - Keep fixtures close to the tests that use them (see `tests/unit/adapters` for examples). Representative files: - `tests/unit/adapters/http.test.js` - `tests/unit/adapters/fetch.test.js` - `tests/unit/regression.test.js` ### Browser (`tests/browser`) - Use local in-file `MockXMLHttpRequest` style mocks when testing request behavior. - Replace global XHR in `beforeEach` and restore it in `afterEach`. - Reset spies/mocks in cleanup hooks to keep tests isolated. - Keep assertions centered on observable request/response behavior. Representative files: - `tests/browser/requests.browser.test.js` - `tests/browser/adapter.browser.test.js` - `tests/browser/defaults.browser.test.js` ### Smoke (`tests/smoke`) - Keep ESM and CJS smoke coverage aligned for compatibility-sensitive behavior. - If you add a new smoke scenario in one format, add the equivalent in the other format. - Keep smoke tests small and focused on import/runtime behavior and critical request flows. Representative files: - `tests/smoke/esm/tests/fetch.smoke.test.js` - `tests/smoke/cjs/tests/fetch.smoke.test.cjs` - `tests/smoke/esm/tests/basic.smoke.test.js` - `tests/smoke/cjs/tests/basic.smoke.test.cjs` ## Shared Setup Utilities (`tests/setup`) Use shared helpers before introducing new setup code: - `tests/setup/server.js` - Server lifecycle helpers: `startHTTPServer`, `stopHTTPServer`, `stopAllTrackedHTTPServers` - Timing helpers: `setTimeoutAsync` - Data/stream helpers used by adapter tests - `tests/setup/browser.setup.js` - Browser cleanup hook (`afterEach`) for clearing test DOM state General expectation: if a helper can be reused by multiple tests in this directory, add or extend it in `tests/setup` instead of copying setup code between test files. ## Fixtures and Test Data - Prefer colocated fixtures near the test files that need them. - Keep fixture names explicit and stable. - For matrix-like scenarios, prefer concise table-driven cases inside the test file when practical. Examples of colocated fixtures: - `tests/unit/adapters/cert.pem` - `tests/unit/adapters/key.pem` - `tests/unit/adapters/axios.png` ## Contributor Checklist Before opening a PR for tests in this directory: - File is placed in the correct suite directory (`unit`, `browser`, or `smoke`). - File name matches the local pattern (`*.test.js`, `*.browser.test.js`, `*.smoke.test.js`, `*.smoke.test.cjs`). - Test setup/teardown is explicit and leaves no global/server state behind. - Shared setup logic uses `tests/setup` helpers where possible. - Smoke tests remain ESM/CJS consistent when behavior is format-sensitive. - Fixtures are colocated and minimal. - Assertions are deterministic and avoid unnecessary timing/network flakiness. axios-axios-aa54a5e/tests/browser/000077500000000000000000000000001526660636600173125ustar00rootroot00000000000000axios-axios-aa54a5e/tests/browser/adapter.browser.test.js000066400000000000000000000107161526660636600237350ustar00rootroot00000000000000import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import axios from '../../index.js'; class MockXMLHttpRequest { constructor() { this.readyState = 0; this.status = 0; this.statusText = ''; this.responseText = ''; this.response = null; this.onreadystatechange = null; this.onloadend = null; this.upload = { addEventListener() {}, }; this.requestHeaders = {}; } open(method, url, async = true) { this.method = method; this.url = url; this.async = async; } setRequestHeader(key, value) { this.requestHeaders[key] = value; } addEventListener() {} getAllResponseHeaders() { return ''; } send(data) { this.params = data; requests.push(this); } respondWith({ status = 200, statusText = 'OK', responseText = '' } = {}) { this.status = status; this.statusText = statusText; this.responseText = responseText; this.response = responseText; this.readyState = 4; queueMicrotask(() => { if (this.onloadend) { this.onloadend(); } else if (this.onreadystatechange) { this.onreadystatechange(); } }); } abort() {} } let requests = []; let OriginalXMLHttpRequest; const sleep = (ms = 0) => new Promise((resolve) => setTimeout(resolve, ms)); const waitForRequest = async (timeoutMs = 1000) => { const start = Date.now(); while (Date.now() - start < timeoutMs) { const request = requests.at(-1); if (request) { return request; } await sleep(0); } throw new Error('Expected an XHR request to be sent'); }; describe('adapter (vitest browser)', () => { beforeEach(() => { requests = []; OriginalXMLHttpRequest = window.XMLHttpRequest; window.XMLHttpRequest = MockXMLHttpRequest; }); afterEach(() => { window.XMLHttpRequest = OriginalXMLHttpRequest; axios.interceptors.request.clear(); axios.interceptors.response.clear(); }); it('should support custom adapter', async () => { const responsePromise = axios('/foo', { adapter(config) { return new Promise((resolve) => { const request = new XMLHttpRequest(); request.open('GET', '/bar'); request.onreadystatechange = function onReadyStateChange() { resolve({ config, request, }); }; request.send(null); }); }, }); const request = await waitForRequest(); expect(request.url).toBe('/bar'); request.respondWith(); await responsePromise; }); it('should execute adapter code synchronously', async () => { let asyncFlag = false; const responsePromise = axios('/foo', { adapter(config) { return new Promise((resolve) => { const request = new XMLHttpRequest(); request.open('GET', '/bar'); request.onreadystatechange = function onReadyStateChange() { resolve({ config, request, }); }; expect(asyncFlag).toBe(false); request.send(null); }); }, }); asyncFlag = true; const request = await waitForRequest(); request.respondWith(); await responsePromise; }); it('should execute adapter code asynchronously when interceptor is present', async () => { let asyncFlag = false; axios.interceptors.request.use((config) => { config.headers.async = 'async it!'; return config; }); const responsePromise = axios('/foo', { adapter(config) { return new Promise((resolve) => { const request = new XMLHttpRequest(); request.open('GET', '/bar'); request.onreadystatechange = function onReadyStateChange() { resolve({ config, request, }); }; expect(asyncFlag).toBe(true); request.send(null); }); }, }); asyncFlag = true; const request = await waitForRequest(); request.respondWith(); await responsePromise; }); it('should sanitize request headers containing CRLF characters', async () => { const responsePromise = axios('/foo', { headers: { 'x-test': '\tok\r\nInjected: yes ', }, }); const request = await waitForRequest(); expect(request.requestHeaders['x-test']).toBe('okInjected: yes'); expect(request.requestHeaders.Injected).toBeUndefined(); request.respondWith(); await responsePromise; }); }); axios-axios-aa54a5e/tests/browser/baseURLResolution.browser.test.js000066400000000000000000000024711526660636600256750ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import resolveConfig from '../../lib/helpers/resolveConfig.js'; import isURLSameOrigin from '../../lib/helpers/isURLSameOrigin.js'; describe('document base URL resolution', function () { it('checks relative destinations against the current document base', function () { var base = document.createElement('base'); base.href = 'https://other.example.invalid/nested/'; document.head.insertBefore(base, document.head.firstChild); document.cookie = 'request-consistency=example; path=/'; try { expect(isURLSameOrigin('resource')).toBe(false); expect(isURLSameOrigin('/resource')).toBe(false); expect(isURLSameOrigin(window.location.href)).toBe(true); var config = { url: 'resource', xsrfCookieName: 'request-consistency', xsrfHeaderName: 'X-Example', }; expect(resolveConfig(config).headers.get('X-Example')).toBeUndefined(); config.withXSRFToken = true; expect(resolveConfig(config).headers.get('X-Example')).toBe('example'); delete config.withXSRFToken; base.href = window.location.href; expect(resolveConfig(config).headers.get('X-Example')).toBe('example'); } finally { base.remove(); document.cookie = 'request-consistency=; max-age=0; path=/'; } }); }); axios-axios-aa54a5e/tests/browser/basicAuth.browser.test.js000066400000000000000000000135621526660636600242220ustar00rootroot00000000000000import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import axios from '../../index.js'; class MockXMLHttpRequest { constructor() { this.requestHeaders = {}; this.responseHeaders = ''; this.readyState = 0; this.status = 0; this.statusText = ''; this.responseText = ''; this.response = null; this.onreadystatechange = null; this.onloadend = null; this.upload = { addEventListener() {}, }; } open(method, url, async = true) { this.method = method; this.url = url; this.async = async; } setRequestHeader(key, value) { this.requestHeaders[key] = value; } addEventListener() {} getAllResponseHeaders() { return this.responseHeaders; } send(data) { this.params = data; requests.push(this); } respondWith({ status = 200, statusText = 'OK', responseText = '', responseHeaders = '' } = {}) { this.status = status; this.statusText = statusText; this.responseText = responseText; this.response = responseText; this.responseHeaders = responseHeaders; this.readyState = 4; queueMicrotask(() => { if (this.onloadend) { this.onloadend(); } else if (this.onreadystatechange) { this.onreadystatechange(); } }); } abort() {} } let requests = []; let OriginalXMLHttpRequest; const startRequest = (...args) => { const promise = axios(...args); const request = requests.at(-1); expect(request).toBeDefined(); return { request, promise }; }; const flushSuccess = async (request, promise) => { request.respondWith({ status: 200 }); await promise; }; describe('basicAuth (vitest browser)', () => { beforeEach(() => { requests = []; OriginalXMLHttpRequest = window.XMLHttpRequest; window.XMLHttpRequest = MockXMLHttpRequest; }); afterEach(() => { window.XMLHttpRequest = OriginalXMLHttpRequest; }); it('should accept HTTP Basic auth with username/password', async () => { const { request, promise } = startRequest('/foo', { auth: { username: 'Aladdin', password: 'open sesame', }, }); expect(request.requestHeaders.Authorization).toBe('Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='); await flushSuccess(request, promise); }); it('should accept HTTP Basic auth credentials without the password parameter', async () => { const { request, promise } = startRequest('/foo', { auth: { username: 'Aladdin', }, }); expect(request.requestHeaders.Authorization).toBe('Basic QWxhZGRpbjo='); await flushSuccess(request, promise); }); it('should accept HTTP Basic auth credentials with non-Latin1 characters in password', async () => { const { request, promise } = startRequest('/foo', { auth: { username: 'Aladdin', password: 'open ßç£☃sesame', }, }); expect(request.requestHeaders.Authorization).toBe( 'Basic QWxhZGRpbjpvcGVuIMOfw6fCo+KYg3Nlc2FtZQ==' ); await flushSuccess(request, promise); }); it('should ignore inherited nested auth fields', async () => { Object.defineProperty(Object.prototype, 'username', { value: 'inherited-user', configurable: true, }); Object.defineProperty(Object.prototype, 'password', { value: 'inherited-pass', configurable: true, }); try { const { request, promise } = startRequest('/foo', { auth: {}, }); expect(request.requestHeaders.Authorization).toBe('Basic Og=='); await flushSuccess(request, promise); } finally { delete Object.prototype.username; delete Object.prototype.password; } }); it('should fail to encode HTTP Basic auth credentials with non-Latin1 characters in username', async () => { await expect( axios('/foo', { auth: { username: 'Aladßç£☃din', password: 'open sesame', }, }) ).rejects.toThrow(/character/i); }); it.each([undefined, false])('preserves Latin-1 usernames with opt-in %s', async (enabled) => { const { request, promise } = startRequest('/foo', { adapter: 'xhr', auth: { username: 'naïve', password: 'x' }, transitional: { encodeBasicAuthUsername: enabled }, }); expect(request.requestHeaders.Authorization).toBe('Basic bmHvdmU6eA=='); await flushSuccess(request, promise); }); it.each([ ['naïve', 'Basic bmHDr3ZlOng='], ['Ï€', 'Basic z4A6eA=='], ['😀', 'Basic 8J+YgDp4'], ])('encodes the username %s as UTF-8 when opted in', async (username, expected) => { const { request, promise } = startRequest('/foo', { adapter: 'xhr', auth: { username, password: 'x' }, transitional: { encodeBasicAuthUsername: true }, }); expect(request.requestHeaders.Authorization).toBe(expected); await flushSuccess(request, promise); }); it('allows a request to disable the instance UTF-8 opt-in', async () => { const instance = axios.create({ adapter: 'xhr', auth: { username: 'naïve', password: 'x' }, transitional: { encodeBasicAuthUsername: true }, }); for (const [config, expected] of [ [{}, 'Basic bmHDr3ZlOng='], [{ transitional: { encodeBasicAuthUsername: false } }, 'Basic bmHvdmU6eA=='], [{}, 'Basic bmHDr3ZlOng='], ]) { const promise = instance.get('/foo', config); const request = requests.at(-1); expect(request.requestHeaders.Authorization).toBe(expected); await flushSuccess(request, promise); } }); it.each(['username', 'password'])('rejects malformed UTF-16 in %s when opted in', async (key) => { await expect( axios('/foo', { adapter: 'xhr', auth: { username: 'user', password: 'x', [key]: '\uD800' }, transitional: { encodeBasicAuthUsername: true }, }) ).rejects.toMatchObject({ code: 'ERR_BAD_OPTION_VALUE', isAxiosError: true }); expect(requests).toHaveLength(0); }); }); axios-axios-aa54a5e/tests/browser/cancel.browser.test.js000066400000000000000000000136721526660636600235460ustar00rootroot00000000000000import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import axios from '../../index.js'; class MockXMLHttpRequest { constructor() { this.requestHeaders = {}; this.responseHeaders = ''; this.readyState = 0; this.status = 0; this.statusText = ''; this.responseText = ''; this.response = null; this.onreadystatechange = null; this.onloadend = null; this.onabort = null; this.upload = { addEventListener() {}, }; } open(method, url, async = true) { this.method = method; this.url = url; this.async = async; } setRequestHeader(key, value) { this.requestHeaders[key] = value; } addEventListener() {} getAllResponseHeaders() { return this.responseHeaders; } send(data) { this.params = data; requests.push(this); } respondWith({ status = 200, statusText = 'OK', responseText = '', responseHeaders = '' } = {}) { this.status = status; this.statusText = statusText; this.responseText = responseText; this.response = responseText; this.responseHeaders = responseHeaders; this.readyState = 4; queueMicrotask(() => { if (this.onloadend) { this.onloadend(); } else if (this.onreadystatechange) { this.onreadystatechange(); } }); } abort() { this.statusText = 'abort'; if (this.onabort) { this.onabort(); } } } let requests = []; let OriginalXMLHttpRequest; const waitForRequest = async (timeoutMs = 1000) => { const start = Date.now(); while (Date.now() - start < timeoutMs) { const request = requests.at(-1); if (request) { return request; } await Promise.resolve(); } throw new Error('Expected an XHR request to be sent'); }; describe('cancel (vitest browser)', () => { beforeEach(() => { requests = []; OriginalXMLHttpRequest = window.XMLHttpRequest; window.XMLHttpRequest = MockXMLHttpRequest; }); afterEach(() => { window.XMLHttpRequest = OriginalXMLHttpRequest; }); describe('when called before sending request', () => { it('rejects Promise with a CanceledError object', async () => { const source = axios.CancelToken.source(); source.cancel('Operation has been canceled.'); const error = await axios .get('/foo', { cancelToken: source.token, }) .catch((thrown) => thrown); expect(axios.isCancel(error)).toBe(true); expect(error.message).toBe('Operation has been canceled.'); expect(requests).toHaveLength(0); }); }); describe('when called after request has been sent', () => { it('rejects Promise with a CanceledError object', async () => { const source = axios.CancelToken.source(); const promise = axios.get('/foo/bar', { cancelToken: source.token, }); const request = await waitForRequest(); // Call cancel() after the request has been sent, but before response is received. source.cancel('Operation has been canceled.'); request.respondWith({ status: 200, responseText: 'OK', }); const error = await promise.catch((thrown) => thrown); expect(axios.isCancel(error)).toBe(true); expect(error.message).toBe('Operation has been canceled.'); }); it('calls abort on request object', async () => { const source = axios.CancelToken.source(); const promise = axios.get('/foo/bar', { cancelToken: source.token, }); const request = await waitForRequest(); // Call cancel() after the request has been sent, but before response is received. source.cancel(); await promise.catch(() => undefined); expect(request.statusText).toBe('abort'); }); }); it('supports cancellation using AbortController signal', async () => { const controller = new AbortController(); const promise = axios.get('/foo/bar', { signal: controller.signal, }); const request = await waitForRequest(); // Call abort() after the request has been sent, but before response is received. controller.abort(); setTimeout(() => { request.respondWith({ status: 200, responseText: 'OK', }); }, 0); const error = await promise.catch((thrown) => thrown); expect(axios.isCancel(error)).toBe(true); }); describe('listener cleanup on error paths', () => { for (const { label, trigger } of [ { label: 'network error', trigger: (r) => r.onerror(new Error('Network Error')) }, { label: 'timeout', trigger: (r) => r.ontimeout() }, { label: 'browser abort', trigger: (r) => r.onabort() }, ]) { it(`unsubscribes cancelToken listener after ${label}`, async () => { const source = axios.CancelToken.source(); const promise = axios .get('/foo/bar', { cancelToken: source.token }) .catch((thrown) => thrown); const request = await waitForRequest(); trigger(request); await promise; expect(source.token._listeners || []).toEqual([]); }); } it('removes AbortSignal listener after network error', async () => { const controller = new AbortController(); let listenerCount = 0; const nativeAdd = controller.signal.addEventListener.bind(controller.signal); const nativeRemove = controller.signal.removeEventListener.bind(controller.signal); controller.signal.addEventListener = (type, fn, options) => { if (type === 'abort') listenerCount++; return nativeAdd(type, fn, options); }; controller.signal.removeEventListener = (type, fn, options) => { if (type === 'abort') listenerCount--; return nativeRemove(type, fn, options); }; const promise = axios .get('/foo/bar', { signal: controller.signal }) .catch((thrown) => thrown); const request = await waitForRequest(); request.onerror(new Error('Network Error')); await promise; expect(listenerCount).toBe(0); }); }); }); axios-axios-aa54a5e/tests/browser/cancelToken.browser.test.js000066400000000000000000000052501526660636600245400ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import CancelToken from '../../lib/cancel/CancelToken.js'; import CanceledError from '../../lib/cancel/CanceledError.js'; describe('CancelToken (vitest browser)', () => { describe('constructor', () => { it('throws when executor is not specified', () => { expect(() => new CancelToken()).toThrowError( new TypeError('executor must be a function.') ); }); it('throws when executor is not a function', () => { expect(() => new CancelToken(123)).toThrowError( new TypeError('executor must be a function.') ); }); }); describe('reason', () => { it('returns a CanceledError if cancellation has been requested', () => { let cancel; const token = new CancelToken((c) => { cancel = c; }); cancel('Operation has been canceled.'); expect(token.reason).toBeInstanceOf(CanceledError); expect(token.reason?.message).toBe('Operation has been canceled.'); }); it('returns undefined if cancellation has not been requested', () => { const token = new CancelToken(() => {}); expect(token.reason).toBeUndefined(); }); }); describe('promise', () => { it('resolves when cancellation is requested', async () => { let cancel; const token = new CancelToken((c) => { cancel = c; }); cancel('Operation has been canceled.'); const reason = await token.promise; expect(reason).toBeInstanceOf(CanceledError); expect(reason.message).toBe('Operation has been canceled.'); }); }); describe('throwIfRequested', () => { it('throws if cancellation has been requested', () => { let cancel; const token = new CancelToken((c) => { cancel = c; }); cancel('Operation has been canceled.'); expect(() => token.throwIfRequested()).toThrow(CanceledError); expect(() => token.throwIfRequested()).toThrow('Operation has been canceled.'); }); it('does not throw if cancellation has not been requested', () => { const token = new CancelToken(() => {}); expect(() => token.throwIfRequested()).not.toThrow(); }); }); describe('source', () => { it('returns an object containing token and cancel function', () => { const source = CancelToken.source(); expect(source.token).toBeInstanceOf(CancelToken); expect(source.cancel).toBeTypeOf('function'); expect(source.token.reason).toBeUndefined(); source.cancel('Operation has been canceled.'); expect(source.token.reason).toBeInstanceOf(CanceledError); expect(source.token.reason?.message).toBe('Operation has been canceled.'); }); }); }); axios-axios-aa54a5e/tests/browser/cookies.browser.test.js000066400000000000000000000066001526660636600237460ustar00rootroot00000000000000import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import cookies from '../../lib/helpers/cookies.js'; const clearAllCookies = () => { const expiry = new Date(Date.now() - 86400000).toUTCString(); for (const cookie of document.cookie.split(';')) { const name = cookie.split('=')[0].trim(); if (!name) { continue; } // Clear both default-path and root-path cookies for the same key. document.cookie = `${name}=; expires=${expiry}`; document.cookie = `${name}=; expires=${expiry}; path=/`; } }; describe('helpers::cookies (vitest browser)', () => { beforeEach(() => { clearAllCookies(); }); afterEach(() => { clearAllCookies(); }); it('writes cookies', () => { cookies.write('foo', 'baz'); expect(document.cookie).toBe('foo=baz'); }); it('reads cookies', () => { cookies.write('foo', 'abc'); cookies.write('bar', 'def'); expect(cookies.read('foo')).toBe('abc'); expect(cookies.read('bar')).toBe('def'); }); it('reads cookies when the cookie separator has no following space', () => { const descriptor = Object.getOwnPropertyDescriptor(document, 'cookie'); Object.defineProperty(document, 'cookie', { configurable: true, get() { return 'foo=abc;bar=def'; }, }); try { expect(cookies.read('bar')).toBe('def'); } finally { if (descriptor) { Object.defineProperty(document, 'cookie', descriptor); } else { delete document.cookie; } } }); it('returns null when document.cookie is undefined', () => { const descriptor = Object.getOwnPropertyDescriptor(document, 'cookie'); Object.defineProperty(document, 'cookie', { configurable: true, get() {}, }); try { expect(cookies.read('foo')).toBeNull(); } finally { if (descriptor) { Object.defineProperty(document, 'cookie', descriptor); } else { delete document.cookie; } } }); it('removes cookies', () => { cookies.write('foo', 'bar'); cookies.remove('foo'); expect(cookies.read('foo')).toBeNull(); }); it('uri encodes values', () => { cookies.write('foo', 'bar baz%'); expect(document.cookie).toBe('foo=bar%20baz%25'); }); it('returns raw cookie value when it is not valid URI encoding', () => { const descriptor = Object.getOwnPropertyDescriptor(document, 'cookie'); Object.defineProperty(document, 'cookie', { configurable: true, get() { return 'foo=bar%'; }, }); try { expect(cookies.read('foo')).toBe('bar%'); } finally { if (descriptor) { Object.defineProperty(document, 'cookie', descriptor); } else { delete document.cookie; } } }); it('matches cookie names exactly even when the name contains regex metacharacters', () => { // previously cookies.read built a RegExp by interpolating // the requested name. Metacharacters could match a different cookie or trigger // catastrophic backtracking. A name such as "X.Y" must not match a cookie called // "XAY" set by the same site. cookies.write('XAY', 'wrong'); expect(cookies.read('X.Y')).toBeNull(); }); it('does not return a partial match for a name that is a prefix of another cookie', () => { cookies.write('xsrf-token-extra', 'wrong'); expect(cookies.read('xsrf-token')).toBeNull(); }); }); axios-axios-aa54a5e/tests/browser/defaults.browser.test.js000066400000000000000000000223721526660636600241250ustar00rootroot00000000000000import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import axios from '../../index.js'; import defaults from '../../lib/defaults/index.js'; import AxiosHeaders from '../../lib/core/AxiosHeaders.js'; class MockXMLHttpRequest { constructor() { this.requestHeaders = {}; this.responseHeaders = ''; this.readyState = 0; this.status = 0; this.statusText = ''; this.responseText = ''; this.response = null; this.onreadystatechange = null; this.onloadend = null; this.upload = { addEventListener() {}, }; } open(method, url, async = true) { this.method = method; this.url = url; this.async = async; } setRequestHeader(key, value) { this.requestHeaders[key] = value; } addEventListener() {} getAllResponseHeaders() { return this.responseHeaders; } send(data) { this.params = data; requests.push(this); } respondWith({ status = 200, statusText = 'OK', responseText = '', responseHeaders = '' } = {}) { this.status = status; this.statusText = statusText; this.responseText = responseText; this.response = responseText; this.responseHeaders = responseHeaders; this.readyState = 4; queueMicrotask(() => { if (this.onloadend) { this.onloadend(); } else if (this.onreadystatechange) { this.onreadystatechange(); } }); } abort() {} } const XSRF_COOKIE_NAME = 'CUSTOM-XSRF-TOKEN'; let requests = []; let OriginalXMLHttpRequest; const getLastRequest = () => { const request = requests.at(-1); expect(request).toBeDefined(); return request; }; const finishRequest = async (request, promise) => { request.respondWith({ status: 200 }); await promise; }; describe('defaults (vitest browser)', () => { beforeEach(() => { requests = []; OriginalXMLHttpRequest = window.XMLHttpRequest; window.XMLHttpRequest = MockXMLHttpRequest; }); afterEach(() => { window.XMLHttpRequest = OriginalXMLHttpRequest; delete axios.defaults.baseURL; delete axios.defaults.headers.get['X-CUSTOM-HEADER']; delete axios.defaults.headers.post['X-CUSTOM-HEADER']; document.cookie = `${XSRF_COOKIE_NAME}=;expires=${new Date(Date.now() - 86400000).toUTCString()}`; }); it('should transform request json', () => { expect(defaults.transformRequest[0]({ foo: 'bar' }, new AxiosHeaders())).toBe('{"foo":"bar"}'); }); it("should also transform request json when 'Content-Type' is 'application/json'", () => { const headers = new AxiosHeaders({ 'Content-Type': 'application/json', }); expect(defaults.transformRequest[0](JSON.stringify({ foo: 'bar' }), headers)).toBe('{"foo":"bar"}'); expect(defaults.transformRequest[0]([42, 43], headers)).toBe('[42,43]'); expect(defaults.transformRequest[0]('foo', headers)).toBe('"foo"'); expect(defaults.transformRequest[0](42, headers)).toBe('42'); expect(defaults.transformRequest[0](true, headers)).toBe('true'); expect(defaults.transformRequest[0](false, headers)).toBe('false'); expect(defaults.transformRequest[0](null, headers)).toBe('null'); }); it("should transform the plain data object to a FormData instance when header is 'multipart/form-data'", () => { const headers = new AxiosHeaders({ 'Content-Type': 'multipart/form-data', }); const transformed = defaults.transformRequest[0]({ x: 1 }, headers); expect(transformed).toBeInstanceOf(FormData); }); it('should do nothing to request string', () => { expect(defaults.transformRequest[0]('foo=bar', new AxiosHeaders())).toBe('foo=bar'); }); it('should transform response json', () => { const data = defaults.transformResponse[0].call(defaults, '{"foo":"bar"}'); expect(typeof data).toBe('object'); expect(data.foo).toBe('bar'); }); it('should do nothing to response string', () => { expect(defaults.transformResponse[0]('foo=bar')).toBe('foo=bar'); }); it('should use global defaults config', async () => { const promise = axios('/foo'); const request = getLastRequest(); expect(request.url).toBe('/foo'); await finishRequest(request, promise); }); it('should use modified defaults config', async () => { axios.defaults.baseURL = 'http://example.com/'; const promise = axios('/foo'); const request = getLastRequest(); expect(request.url).toBe('http://example.com/foo'); await finishRequest(request, promise); }); it('should use request config', async () => { const promise = axios('/foo', { baseURL: 'http://www.example.com', }); const request = getLastRequest(); expect(request.url).toBe('http://www.example.com/foo'); await finishRequest(request, promise); }); it('should use default config for custom instance', async () => { const instance = axios.create({ xsrfCookieName: XSRF_COOKIE_NAME, xsrfHeaderName: 'X-CUSTOM-XSRF-TOKEN', }); document.cookie = `${instance.defaults.xsrfCookieName}=foobarbaz`; const promise = instance.get('/foo'); const request = getLastRequest(); expect(request.requestHeaders[instance.defaults.xsrfHeaderName]).toBe('foobarbaz'); await finishRequest(request, promise); }); it('should use GET headers', async () => { axios.defaults.headers.get['X-CUSTOM-HEADER'] = 'foo'; const promise = axios.get('/foo'); const request = getLastRequest(); expect(request.requestHeaders['X-CUSTOM-HEADER']).toBe('foo'); await finishRequest(request, promise); }); it('should use POST headers', async () => { axios.defaults.headers.post['X-CUSTOM-HEADER'] = 'foo'; const promise = axios.post('/foo', {}); const request = getLastRequest(); expect(request.requestHeaders['X-CUSTOM-HEADER']).toBe('foo'); await finishRequest(request, promise); }); for (const [shape, link] of Object.entries({ string: '; rel="type"', Date: new Date(0), Uint8Array: new Uint8Array([65, 66]), 'application class': new (class { toString() { return '; rel="type"'; } })(), 'tagged application class': new (class { get [Symbol.toStringTag]() { return 'AxiosHeaders'; } toString() { return '; rel="type"'; } toJSON() { throw new Error('A literal header value must not be serialized as a defaults bucket'); } })(), })) { it(`should send a literal ${shape} Link header alongside method defaults`, async () => { const instance = axios.create({ headers: { post: { 'X-Post': 'post' }, get: { Authorization: 'Bearer GET_ONLY' }, }, }); const promise = instance.post('/foo', {}, { headers: { Link: link } }); const request = getLastRequest(); const headers = new AxiosHeaders(request.requestHeaders); expect(headers.get('Link')).toBe(String(link)); expect(headers.get('X-Post')).toBe('post'); expect(headers.has('Authorization')).toBe(false); expect(headers.has('post')).toBe(false); expect(headers.has('get')).toBe(false); expect(headers.has('0')).toBe(false); await finishRequest(request, promise); }); } it('should use header config', async () => { const instance = axios.create({ headers: { common: { 'X-COMMON-HEADER': 'commonHeaderValue', }, get: { 'X-GET-HEADER': 'getHeaderValue', }, post: { 'X-POST-HEADER': 'postHeaderValue', }, }, }); const promise = instance.get('/foo', { headers: { 'X-FOO-HEADER': 'fooHeaderValue', 'X-BAR-HEADER': 'barHeaderValue', }, }); const request = getLastRequest(); expect(request.requestHeaders).toEqual( AxiosHeaders.concat(defaults.headers.common, defaults.headers.get, { 'X-COMMON-HEADER': 'commonHeaderValue', 'X-GET-HEADER': 'getHeaderValue', 'X-FOO-HEADER': 'fooHeaderValue', 'X-BAR-HEADER': 'barHeaderValue', }).toJSON() ); await finishRequest(request, promise); }); it('should be used by custom instance if set before instance created', async () => { axios.defaults.baseURL = 'http://example.org/'; const instance = axios.create(); const promise = instance.get('/foo'); const request = getLastRequest(); expect(request.url).toBe('http://example.org/foo'); await finishRequest(request, promise); }); it('should not be used by custom instance if set after instance created', async () => { const instance = axios.create(); axios.defaults.baseURL = 'http://example.org/'; const promise = instance.get('/foo/users'); const request = getLastRequest(); expect(request.url).toBe('/foo/users'); await finishRequest(request, promise); }); it('should resistant to ReDoS attack', async () => { const instance = axios.create(); const start = performance.now(); const slashes = '/'.repeat(100000); instance.defaults.baseURL = `/${slashes}bar/`; const promise = instance.get('/foo'); const request = getLastRequest(); const elapsedTimeMs = performance.now() - start; expect(elapsedTimeMs).toBeLessThan(20); expect(request.url).toBe(`/${slashes}bar/foo`); await finishRequest(request, promise); }); }); axios-axios-aa54a5e/tests/browser/errorStack.browser.test.js000066400000000000000000000026461526660636600244370ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import axios from '../../index.js'; import AxiosError, { CAUSED_BY_SEPARATOR } from '../../lib/core/AxiosError.js'; describe('wrapped error stacks', () => { it('preserves the wrapped stack after reconstructed request frames', async () => { const cause = new TypeError('connection failed'); const sourceStack = cause.stack; const failure = AxiosError.from(cause, AxiosError.ERR_NETWORK); const error = await axios .request({ adapter: () => Promise.reject(failure) }) .catch((reason) => reason); expect(error).toBe(failure); expect(sourceStack).toBeTypeOf('string'); const causeIndex = error.stack.indexOf(CAUSED_BY_SEPARATOR); expect(causeIndex).toBeGreaterThan(0); expect(error.stack.slice(causeIndex)).toBe(CAUSED_BY_SEPARATOR + sourceStack); expect(error.cause).toBe(cause); expect(Object.getOwnPropertyDescriptor(error, 'cause').enumerable).toBe(false); }); it('normalizes errors even when the wrapped stack getter throws', () => { const cause = new Error('connection failed'); Object.defineProperty(cause, 'stack', { get() { throw new Error('stack unavailable'); }, }); const error = AxiosError.from(cause, AxiosError.ERR_NETWORK); expect(error.cause).toBe(cause); expect(error.code).toBe(AxiosError.ERR_NETWORK); expect(error.stack).not.toContain(CAUSED_BY_SEPARATOR); }); }); axios-axios-aa54a5e/tests/browser/fetchCache.browser.test.js000066400000000000000000000030221526660636600243220ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import axios from '../../index.js'; describe('fetch cache compatibility', () => { [undefined, 'no-store', 'no-cache'].forEach((cache) => { it(`preserves ${cache || 'the native default'} cache behavior`, async () => { let captured; const response = await axios.get('/cache', { adapter: 'fetch', fetchOptions: cache === undefined ? undefined : { cache }, env: { fetch(input) { captured = input; return Promise.resolve(new Response('ok')); }, }, }); expect(response.data).toBe('ok'); expect(captured.cache).toBe(cache || 'default'); }); }); it('omits cache when the Request implementation rejects every explicit mode', async () => { let capturedInit; class CacheDisabledRequest extends Request { constructor(input, init) { if (init && init.cache !== undefined) { throw new TypeError("The 'cache' field on 'RequestInitializerDict' is not implemented."); } super(input, init); capturedInit = init; } } const response = await axios.get('/cache-disabled', { adapter: 'fetch', env: { Request: CacheDisabledRequest, fetch() { return Promise.resolve(new Response('ok')); }, }, }); expect(response.data).toBe('ok'); expect(Object.prototype.hasOwnProperty.call(capturedInit, 'cache')).toBe(false); expect(capturedInit.redirect).toBe('follow'); }); }); axios-axios-aa54a5e/tests/browser/fetchHeaders.browser.test.js000066400000000000000000000016221526660636600246760ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import axios, { AxiosHeaders } from '../../index.js'; describe('fetch response headers', () => { it('preserves a header named __proto__ from native Response headers', async () => { const { data, headers } = await axios.get('/response-headers', { adapter: 'fetch', env: { async fetch() { return new Response('ok', { headers: [ ['__proto__', 'server-value'], ['x-other', 'other-value'], ], }); }, }, }); expect(data).toBe('ok'); expect(Object.getPrototypeOf(headers)).toBe(AxiosHeaders.prototype); expect(headers.get('__proto__')).toBe('server-value'); expect(headers.get('__PROTO__')).toBe('server-value'); expect(headers.get('x-other')).toBe('other-value'); expect(headers.toJSON().__Proto__).toBe('server-value'); }); }); axios-axios-aa54a5e/tests/browser/fetchResponseErrors.browser.test.js000066400000000000000000000043161526660636600263210ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import axios from '../../index.js'; describe('fetch response size errors', () => { for (const responseType of ['text', 'arrayBuffer', 'blob']) { for (const preserveCause of [true, false]) { it(`preserves wrapped ${responseType} limit errors ${preserveCause ? 'with' : 'without'} a cause`, async () => { let originalError; let wrappedError; let dispatchedRequest; class WrappedResponse extends Response { async [responseType]() { try { return await super[responseType](); } catch (error) { originalError = error; wrappedError = new TypeError('Load failed'); if (preserveCause) { wrappedError.cause = error; } throw wrappedError; } } } const url = new URL('/response-limit', window.location.href).href; const error = await axios .get(url, { adapter: 'fetch', responseType, maxContentLength: 512, env: { Response: WrappedResponse, async fetch(request) { dispatchedRequest = request; // No Content-Length: enforce the limit while consuming the body. return new Response( new ReadableStream({ start(controller) { controller.enqueue(new Uint8Array(1024)); controller.close(); }, }) ); }, }, }) .catch((error) => error); // Some native body readers already replace the stream's AxiosError. expect(originalError).toBeDefined(); expect(error).toBeInstanceOf(axios.AxiosError); expect(error).not.toBe(wrappedError); expect(error.code).toBe(axios.AxiosError.ERR_BAD_RESPONSE); expect(error.message).toBe('maxContentLength size of 512 exceeded'); expect(error.config.url).toBe(url); expect(error.config.maxContentLength).toBe(512); expect(error.request).toBe(dispatchedRequest); }); } } }); axios-axios-aa54a5e/tests/browser/formBodyLength.browser.test.js000066400000000000000000000001331526660636600252300ustar00rootroot00000000000000import formBodyLengthCases from '../setup/formBodyLengthCases.js'; formBodyLengthCases(); axios-axios-aa54a5e/tests/browser/formdata.browser.test.js000066400000000000000000000043361526660636600241130ustar00rootroot00000000000000import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import axios from '../../index.js'; class MockXMLHttpRequest { constructor() { this.requestHeaders = {}; this.responseHeaders = ''; this.readyState = 0; this.status = 0; this.statusText = ''; this.responseText = ''; this.response = null; this.onreadystatechange = null; this.onloadend = null; this.upload = { addEventListener() {}, }; } open(method, url, async = true) { this.method = method; this.url = url; this.async = async; } setRequestHeader(key, value) { this.requestHeaders[key] = value; } addEventListener() {} getAllResponseHeaders() { return this.responseHeaders; } send(data) { this.params = data; requests.push(this); } respondWith({ status = 200, statusText = 'OK', responseText = '', responseHeaders = '' } = {}) { this.status = status; this.statusText = statusText; this.responseText = responseText; this.response = responseText; this.responseHeaders = responseHeaders; this.readyState = 4; queueMicrotask(() => { if (this.onloadend) { this.onloadend(); } else if (this.onreadystatechange) { this.onreadystatechange(); } }); } abort() {} } let requests = []; let OriginalXMLHttpRequest; const getLastRequest = () => { const request = requests.at(-1); expect(request).toBeDefined(); return request; }; describe('formdata (vitest browser)', () => { beforeEach(() => { requests = []; OriginalXMLHttpRequest = window.XMLHttpRequest; window.XMLHttpRequest = MockXMLHttpRequest; }); afterEach(() => { window.XMLHttpRequest = OriginalXMLHttpRequest; }); it('should allow FormData posting', async () => { const responsePromise = axios.postForm('/foo', { a: 'foo', b: 'bar', }); const request = getLastRequest(); expect(request.params).toBeInstanceOf(FormData); expect(Object.fromEntries(request.params.entries())).toEqual({ a: 'foo', b: 'bar', }); request.respondWith({ status: 200, responseText: '{}', responseHeaders: 'Content-Type: application/json', }); await responsePromise; }); }); axios-axios-aa54a5e/tests/browser/headers.browser.test.js000066400000000000000000000166031526660636600237310ustar00rootroot00000000000000import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import axios, { AxiosHeaders } from '../../index.js'; class MockXMLHttpRequest { constructor() { this.requestHeaders = {}; this.readyState = 0; this.status = 0; this.statusText = ''; this.responseText = ''; this.response = null; this.onreadystatechange = null; this.onloadend = null; this.upload = { addEventListener() {}, }; } open(method, url, async = true) { this.method = method; this.url = url; this.async = async; } setRequestHeader(key, value) { this.requestHeaders[key] = value; } addEventListener() {} getAllResponseHeaders() { return ''; } send(data) { this.params = data; requests.push(this); } respondWith({ status = 200, statusText = 'OK', responseText = '' } = {}) { this.status = status; this.statusText = statusText; this.responseText = responseText; this.response = responseText; this.readyState = 4; queueMicrotask(() => { if (this.onloadend) { this.onloadend(); } else if (this.onreadystatechange) { this.onreadystatechange(); } }); } abort() {} } let requests = []; let OriginalXMLHttpRequest; const getLastRequest = () => { const request = requests.at(-1); expect(request).toBeDefined(); return request; }; const finishRequest = async (request, promise) => { request.respondWith({ status: 200 }); await promise; }; function testHeaderValue(headers, key, val) { let found = false; for (const k in headers) { if (k.toLowerCase() === key.toLowerCase()) { found = true; expect(headers[k]).toBe(val); break; } } if (!found) { if (typeof val === 'undefined') { expect(Object.prototype.hasOwnProperty.call(headers, key)).toBe(false); } else { throw new Error(`${key} was not found in headers`); } } } describe('headers (vitest browser)', () => { beforeEach(() => { requests = []; OriginalXMLHttpRequest = window.XMLHttpRequest; window.XMLHttpRequest = MockXMLHttpRequest; }); afterEach(() => { window.XMLHttpRequest = OriginalXMLHttpRequest; }); it('should default common headers', async () => { const headers = axios.defaults.headers.common; const promise = axios('/foo'); const request = getLastRequest(); for (const key in headers) { if (Object.prototype.hasOwnProperty.call(headers, key)) { expect(request.requestHeaders[key]).toBe(headers[key]); } } await finishRequest(request, promise); }); it('should preserve __proto__ in raw XHR response headers', async () => { const promise = axios.get('/response-headers', { adapter: 'xhr' }); const request = getLastRequest(); request.getAllResponseHeaders = () => '__proto__: first\r\n__PROTO__: second\r\nX-Other: other\r\n'; request.respondWith({ responseText: 'ok' }); const { data, headers } = await promise; expect(data).toBe('ok'); expect(Object.getPrototypeOf(headers)).toBe(AxiosHeaders.prototype); expect(headers.get('__proto__')).toBe('first, second'); expect(headers.get('__PROTO__')).toBe('first, second'); expect(headers.get('x-other')).toBe('other'); expect(headers.toJSON().__Proto__).toBe('first, second'); }); it('should allow request interceptors to encode Unicode header values before XHR sends them', async () => { const instance = axios.create({ adapter: 'xhr' }); instance.interceptors.request.use((config) => { config.headers.oprtName = encodeURIComponent(config.headers.oprtName); return config; }); const promise = instance.get('/foo', { headers: { oprtName: '请求用户', }, }); await new Promise((resolve) => setTimeout(resolve)); const request = getLastRequest(); expect(request.requestHeaders.oprtName).toBe(encodeURIComponent('请求用户')); await finishRequest(request, promise); }); it('should sanitize unencoded Unicode headers before passing them to XHR', async () => { const promise = axios.get('/foo', { adapter: 'xhr', headers: { oprtName: '请求用户', }, }); const request = getLastRequest(); expect(request.requestHeaders.oprtName).toBe(''); await finishRequest(request, promise); }); it('should respect common Content-Type header', async () => { const instance = axios.create(); instance.defaults.headers.common['Content-Type'] = 'application/custom'; const promise = instance.patch('/foo', ''); const request = getLastRequest(); expect(request.requestHeaders['Content-Type']).toBe('application/custom'); await finishRequest(request, promise); }); it('should add extra headers for post', async () => { const headers = AxiosHeaders.from(axios.defaults.headers.common).toJSON(); const promise = axios.post('/foo', 'fizz=buzz'); const request = getLastRequest(); for (const key in headers) { expect(request.requestHeaders[key]).toBe(headers[key]); } await finishRequest(request, promise); }); it('should reset headers by null or explicit undefined', async () => { const promise = axios.create({ headers: { common: { 'x-header-a': 'a', 'x-header-b': 'b', 'x-header-c': 'c', }, }, }).post( '/foo', { fizz: 'buzz' }, { headers: { 'Content-Type': null, 'x-header-a': null, 'x-header-b': undefined, }, } ); const request = getLastRequest(); testHeaderValue(request.requestHeaders, 'Content-Type', undefined); testHeaderValue(request.requestHeaders, 'x-header-a', undefined); testHeaderValue(request.requestHeaders, 'x-header-b', undefined); testHeaderValue(request.requestHeaders, 'x-header-c', 'c'); await finishRequest(request, promise); }); it('should use application/json when posting an object', async () => { const promise = axios.post('/foo/bar', { firstName: 'foo', lastName: 'bar', }); const request = getLastRequest(); testHeaderValue(request.requestHeaders, 'Content-Type', 'application/json'); await finishRequest(request, promise); }); it('should remove content-type if data is empty', async () => { const promise = axios.post('/foo'); const request = getLastRequest(); testHeaderValue(request.requestHeaders, 'Content-Type', undefined); await finishRequest(request, promise); }); it('should preserve content-type if data is false', async () => { const promise = axios.post('/foo', false); const request = getLastRequest(); testHeaderValue(request.requestHeaders, 'Content-Type', 'application/x-www-form-urlencoded'); await finishRequest(request, promise); }); it('should allow an AxiosHeaders instance to be used as the value of the headers option', async () => { const instance = axios.create({ headers: new AxiosHeaders({ xFoo: 'foo', xBar: 'bar', }), }); const promise = instance.get('/foo', { headers: { XFOO: 'foo2', xBaz: 'baz', }, }); const request = getLastRequest(); expect(request.requestHeaders.xFoo).toBe('foo2'); expect(request.requestHeaders.xBar).toBe('bar'); expect(request.requestHeaders.xBaz).toBe('baz'); expect(request.requestHeaders.XFOO).toBeUndefined(); await finishRequest(request, promise); }); }); axios-axios-aa54a5e/tests/browser/instance.browser.test.js000066400000000000000000000137101526660636600241160ustar00rootroot00000000000000import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import axios from '../../index.js'; class MockXMLHttpRequest { constructor() { this.requestHeaders = {}; this.responseHeaders = ''; this.readyState = 0; this.status = 0; this.statusText = ''; this.responseText = ''; this.response = null; this.onreadystatechange = null; this.onloadend = null; this.upload = { addEventListener() {}, }; } open(method, url, async = true) { this.method = method; this.url = url; this.async = async; } setRequestHeader(key, value) { this.requestHeaders[key] = value; } addEventListener() {} getAllResponseHeaders() { return this.responseHeaders; } send(data) { this.params = data; requests.push(this); } respondWith({ status = 200, statusText = 'OK', responseText = '', responseHeaders = '' } = {}) { this.status = status; this.statusText = statusText; this.responseText = responseText; this.response = responseText; this.responseHeaders = responseHeaders; this.readyState = 4; queueMicrotask(() => { if (this.onloadend) { this.onloadend(); } else if (this.onreadystatechange) { this.onreadystatechange(); } }); } } let requests = []; let OriginalXMLHttpRequest; const getLastRequest = () => { const request = requests.at(-1); expect(request).toBeDefined(); return request; }; const flushSuccess = async (request, promise) => { request.respondWith({ status: 200 }); await promise; }; const waitForRequest = async (timeoutMs = 1000) => { const start = Date.now(); while (Date.now() - start < timeoutMs) { const request = requests.at(-1); if (request) { return request; } await Promise.resolve(); } throw new Error('Expected an XHR request to be sent'); }; describe('instance (vitest browser)', () => { beforeEach(() => { requests = []; OriginalXMLHttpRequest = window.XMLHttpRequest; window.XMLHttpRequest = MockXMLHttpRequest; }); afterEach(() => { window.XMLHttpRequest = OriginalXMLHttpRequest; }); it('should have the same methods as default instance', () => { const instance = axios.create(); for (const prop in axios) { if ( [ 'Axios', 'AxiosError', 'create', 'Cancel', 'CanceledError', 'CancelToken', 'isCancel', 'all', 'spread', 'getUri', 'isAxiosError', 'mergeConfig', 'getAdapter', 'VERSION', 'default', 'toFormData', 'formToJSON', 'AxiosHeaders', 'HttpStatusCode', ].includes(prop) ) { continue; } expect(typeof instance[prop]).toBe(typeof axios[prop]); } }); it('should make an http request without verb helper', async () => { const instance = axios.create(); const promise = instance('/foo'); const request = getLastRequest(); expect(request.url).toBe('/foo'); await flushSuccess(request, promise); }); it('should make an http request with url instead of baseURL', async () => { const instance = axios.create({ url: 'https://api.example.com', }); const promise = instance('/foo'); const request = getLastRequest(); expect(request.url).toBe('/foo'); await flushSuccess(request, promise); }); it('should make an http request', async () => { const instance = axios.create(); const promise = instance.get('/foo'); const request = getLastRequest(); expect(request.url).toBe('/foo'); await flushSuccess(request, promise); }); it('should use instance options', async () => { const instance = axios.create({ timeout: 1000 }); const promise = instance.get('/foo'); const request = getLastRequest(); expect(request.timeout).toBe(1000); await flushSuccess(request, promise); }); it('should have defaults.headers', () => { const instance = axios.create({ baseURL: 'https://api.example.com', }); expect(typeof instance.defaults.headers).toBe('object'); expect(typeof instance.defaults.headers.common).toBe('object'); }); it('should have interceptors on the instance', async () => { const requestInterceptorId = axios.interceptors.request.use((config) => { config.foo = true; return config; }); const instance = axios.create(); const instanceInterceptorId = instance.interceptors.request.use((config) => { config.bar = true; return config; }); try { const responsePromise = instance.get('/foo'); const request = await waitForRequest(); request.respondWith({ status: 200, }); const response = await responsePromise; expect(response.config.foo).toBeUndefined(); expect(response.config.bar).toBe(true); } finally { axios.interceptors.request.eject(requestInterceptorId); instance.interceptors.request.eject(instanceInterceptorId); } }); it('should have getUri on the instance', () => { const instance = axios.create({ baseURL: 'https://api.example.com', }); const options = { url: 'foo/bar', params: { name: 'axios', }, }; expect(instance.getUri(options)).toBe('https://api.example.com/foo/bar?name=axios'); }); it('should correctly build url without baseURL', () => { const instance = axios.create(); const options = { url: 'foo/bar?foo=bar', params: { name: 'axios', }, }; expect(instance.getUri(options)).toBe('foo/bar?foo=bar&name=axios'); }); it('should correctly discard url hash mark', () => { const instance = axios.create(); const options = { baseURL: 'https://api.example.com', url: 'foo/bar?foo=bar#hash', params: { name: 'axios', }, }; expect(instance.getUri(options)).toBe('https://api.example.com/foo/bar?foo=bar&name=axios'); }); }); axios-axios-aa54a5e/tests/browser/interceptors.browser.test.js000066400000000000000000000536151526660636600250430ustar00rootroot00000000000000import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import axios from '../../index.js'; class MockXMLHttpRequest { constructor() { this.requestHeaders = {}; this.responseHeaders = {}; this.readyState = 0; this.status = 0; this.statusText = ''; this.responseText = ''; this.response = null; this.responseURL = ''; this.timeout = 0; this.withCredentials = false; this.onreadystatechange = null; this.onloadend = null; this.onabort = null; this.onerror = null; this.ontimeout = null; this.upload = { addEventListener() {}, }; } open(method, url, async = true) { this.method = method; this.url = url; this.async = async; } setRequestHeader(key, value) { this.requestHeaders[key] = value; } addEventListener() {} getAllResponseHeaders() { if (typeof this.responseHeaders === 'string') { return this.responseHeaders; } return Object.entries(this.responseHeaders) .map(([key, value]) => `${key}: ${value}`) .join('\n'); } send(data) { this.params = data; requests.push(this); this.readyState = 1; } respondWith({ status = 200, statusText = 'OK', responseText = '', response = null, responseHeaders = {}, headers = {}, responseURL = '', } = {}) { this.status = status; this.statusText = statusText; this.responseText = responseText; this.response = response === null ? responseText : response; this.responseHeaders = Object.keys(headers).length ? headers : responseHeaders; this.responseURL = responseURL; this.readyState = 4; this.finish(); } responseTimeout() { if (this.ontimeout) { this.ontimeout(); } } failNetworkError(message = 'Network Error') { if (this.onerror) { this.onerror({ message }); } } abort() { if (this.onabort) { this.onabort(); } } finish() { queueMicrotask(() => { if (this.onloadend) { this.onloadend(); } else if (this.onreadystatechange) { this.onreadystatechange(); } }); } } let requests = []; let OriginalXMLHttpRequest; const sleep = (ms = 0) => new Promise((resolve) => setTimeout(resolve, ms)); const waitForRequest = async (timeoutMs = 1000) => { const start = Date.now(); while (Date.now() - start < timeoutMs) { const request = requests.at(-1); if (request) { return request; } await sleep(0); } throw new Error('Expected an XHR request to be sent'); }; describe('interceptors (vitest browser)', () => { beforeEach(() => { requests = []; OriginalXMLHttpRequest = window.XMLHttpRequest; window.XMLHttpRequest = MockXMLHttpRequest; }); afterEach(() => { window.XMLHttpRequest = OriginalXMLHttpRequest; axios.interceptors.request.clear(); axios.interceptors.response.clear(); vi.restoreAllMocks(); }); it('should add a request interceptor (asynchronous by default)', async () => { let asyncFlag = false; axios.interceptors.request.use((config) => { config.headers.test = 'added by interceptor'; expect(asyncFlag).toBe(true); return config; }); const responsePromise = axios('/foo'); asyncFlag = true; const request = await waitForRequest(); expect(request.requestHeaders.test).toBe('added by interceptor'); request.respondWith(); await responsePromise; }); it('should add a request interceptor (explicitly flagged as asynchronous)', async () => { let asyncFlag = false; axios.interceptors.request.use( (config) => { config.headers.test = 'added by interceptor'; expect(asyncFlag).toBe(true); return config; }, null, { synchronous: false } ); const responsePromise = axios('/foo'); asyncFlag = true; const request = await waitForRequest(); expect(request.requestHeaders.test).toBe('added by interceptor'); request.respondWith(); await responsePromise; }); it('should add a request interceptor that is executed synchronously when flag is provided', async () => { let asyncFlag = false; axios.interceptors.request.use( (config) => { config.headers.test = 'added by synchronous interceptor'; expect(asyncFlag).toBe(false); return config; }, null, { synchronous: true } ); const responsePromise = axios('/foo'); asyncFlag = true; const request = await waitForRequest(); expect(request.requestHeaders.test).toBe('added by synchronous interceptor'); request.respondWith(); await responsePromise; }); it('should execute asynchronously when not all interceptors are explicitly flagged as synchronous', async () => { let asyncFlag = false; axios.interceptors.request.use((config) => { config.headers.foo = 'uh oh, async'; expect(asyncFlag).toBe(true); return config; }); axios.interceptors.request.use( (config) => { config.headers.test = 'added by synchronous interceptor'; expect(asyncFlag).toBe(true); return config; }, null, { synchronous: true } ); axios.interceptors.request.use((config) => { config.headers.test = 'added by the async interceptor'; expect(asyncFlag).toBe(true); return config; }); const responsePromise = axios('/foo'); asyncFlag = true; const request = await waitForRequest(); expect(request.requestHeaders.foo).toBe('uh oh, async'); expect(request.requestHeaders.test).toBe('added by synchronous interceptor'); request.respondWith(); await responsePromise; }); it('should execute request interceptor in legacy order', async () => { let sequence = ''; axios.interceptors.request.use((config) => { sequence += '1'; return config; }); axios.interceptors.request.use((config) => { sequence += '2'; return config; }); axios.interceptors.request.use((config) => { sequence += '3'; return config; }); const responsePromise = axios({ url: '/foo' }); const request = await waitForRequest(); expect(sequence).toBe('321'); request.respondWith(); await responsePromise; }); it('should execute request interceptor in order', async () => { let sequence = ''; axios.interceptors.request.use((config) => { sequence += '1'; return config; }); axios.interceptors.request.use((config) => { sequence += '2'; return config; }); axios.interceptors.request.use((config) => { sequence += '3'; return config; }); const responsePromise = axios({ url: '/foo', transitional: { legacyInterceptorReqResOrdering: false, }, }); const request = await waitForRequest(); expect(sequence).toBe('123'); request.respondWith(); await responsePromise; }); it('runs the interceptor if runWhen function is provided and resolves to true', async () => { const onGetCall = (config) => config.method === 'get'; axios.interceptors.request.use( (config) => { config.headers.test = 'special get headers'; return config; }, null, { runWhen: onGetCall } ); const responsePromise = axios('/foo'); const request = await waitForRequest(); expect(request.requestHeaders.test).toBe('special get headers'); request.respondWith(); await responsePromise; }); it('does not run the interceptor if runWhen function is provided and resolves to false', async () => { const onPostCall = (config) => config.method === 'post'; axios.interceptors.request.use( (config) => { config.headers.test = 'special get headers'; return config; }, null, { runWhen: onPostCall } ); const responsePromise = axios('/foo'); const request = await waitForRequest(); expect(request.requestHeaders.test).toBeUndefined(); request.respondWith(); await responsePromise; }); it('does not run async interceptor if runWhen resolves to false (and runs synchronously)', async () => { let asyncFlag = false; const onPostCall = (config) => config.method === 'post'; axios.interceptors.request.use( (config) => { config.headers.test = 'special get headers'; return config; }, null, { synchronous: false, runWhen: onPostCall } ); axios.interceptors.request.use( (config) => { config.headers.sync = 'hello world'; expect(asyncFlag).toBe(false); return config; }, null, { synchronous: true } ); const responsePromise = axios('/foo'); asyncFlag = true; const request = await waitForRequest(); expect(request.requestHeaders.test).toBeUndefined(); expect(request.requestHeaders.sync).toBe('hello world'); request.respondWith(); await responsePromise; }); it('should call request onRejected when interceptor throws', async () => { const rejectedSpy = vi.fn(); const error = new Error('deadly error'); axios.interceptors.request.use( () => { throw error; }, rejectedSpy, { synchronous: true } ); const responsePromise = axios('/foo').catch(() => {}); const request = await waitForRequest(); request.respondWith(); await responsePromise; expect(rejectedSpy).toHaveBeenCalledWith(error); }); it('should reject with the original error and not send when a synchronous interceptor has no onRejected', async () => { const error = new Error('deadly error'); axios.interceptors.request.use( () => { throw error; }, null, { synchronous: true } ); await expect(axios('/foo')).rejects.toBe(error); expect(requests).toHaveLength(0); }); it('should not send when a synchronous interceptor onRejected returns a rejected promise', async () => { const error = new Error('deadly error'); const rejectedSpy = vi.fn(() => Promise.reject(error)); axios.interceptors.request.use( () => { throw error; }, rejectedSpy, { synchronous: true } ); await expect(axios('/foo')).rejects.toBe(error); expect(rejectedSpy).toHaveBeenCalledWith(error); expect(requests).toHaveLength(0); }); it('should send with the last valid config when a synchronous interceptor onRejected resolves', async () => { const error = new Error('deadly error'); axios.interceptors.request.use( () => { throw error; }, () => Promise.resolve({ url: '/bar' }), { synchronous: true } ); const responsePromise = axios('/foo'); expect(requests).toHaveLength(0); const request = await waitForRequest(); expect(request.url).toBe('/foo'); request.respondWith(); await responsePromise; }); it('should reject and not send when a synchronous interceptor onRejected throws', async () => { const interceptorError = new Error('interceptor error'); const rejectedError = new Error('rejected error'); axios.interceptors.request.use( () => { throw interceptorError; }, () => { throw rejectedError; }, { synchronous: true } ); await expect(axios('/foo')).rejects.toBe(rejectedError); expect(requests).toHaveLength(0); }); it('should pass terminal synchronous request interceptor errors to response onRejected', async () => { const error = new Error('deadly error'); const responseRejectedSpy = vi.fn((reason) => Promise.reject(reason)); axios.interceptors.request.use( () => { throw error; }, null, { synchronous: true } ); axios.interceptors.response.use(null, responseRejectedSpy); await expect(axios('/foo')).rejects.toBe(error); expect(responseRejectedSpy).toHaveBeenCalledWith(error); expect(requests).toHaveLength(0); }); it('should add a request interceptor that returns a new config object', async () => { axios.interceptors.request.use(() => ({ url: '/bar', method: 'post', })); const responsePromise = axios('/foo'); const request = await waitForRequest(); expect(request.method).toBe('POST'); expect(request.url).toBe('/bar'); request.respondWith(); await responsePromise; }); it('should add a request interceptor that returns a promise', async () => { axios.interceptors.request.use((config) => new Promise((resolve) => { setTimeout(() => { config.headers.async = 'promise'; resolve(config); }, 100); }) ); const responsePromise = axios('/foo'); const request = await waitForRequest(1500); expect(request.requestHeaders.async).toBe('promise'); request.respondWith(); await responsePromise; }); it('should add multiple request interceptors', async () => { axios.interceptors.request.use((config) => { config.headers.test1 = '1'; return config; }); axios.interceptors.request.use((config) => { config.headers.test2 = '2'; return config; }); axios.interceptors.request.use((config) => { config.headers.test3 = '3'; return config; }); const responsePromise = axios('/foo'); const request = await waitForRequest(); expect(request.requestHeaders.test1).toBe('1'); expect(request.requestHeaders.test2).toBe('2'); expect(request.requestHeaders.test3).toBe('3'); request.respondWith(); await responsePromise; }); it('should add a response interceptor', async () => { axios.interceptors.response.use((data) => { data.data = `${data.data} - modified by interceptor`; return data; }); const responsePromise = axios('/foo'); const request = await waitForRequest(); request.respondWith({ status: 200, responseText: 'OK', }); const response = await responsePromise; expect(response.data).toBe('OK - modified by interceptor'); }); it('should add a response interceptor when request interceptor is defined', async () => { axios.interceptors.request.use((data) => data); axios.interceptors.response.use((data) => { data.data = `${data.data} - modified by interceptor`; return data; }); const responsePromise = axios('/foo'); const request = await waitForRequest(); request.respondWith({ status: 200, responseText: 'OK', }); const response = await responsePromise; expect(response.data).toBe('OK - modified by interceptor'); }); it('should add a response interceptor that returns a new data object', async () => { axios.interceptors.response.use(() => ({ data: 'stuff', })); const responsePromise = axios('/foo'); const request = await waitForRequest(); request.respondWith({ status: 200, responseText: 'OK', }); const response = await responsePromise; expect(response.data).toBe('stuff'); }); it('should add a response interceptor that returns a promise', async () => { axios.interceptors.response.use((data) => new Promise((resolve) => { setTimeout(() => { data.data = 'you have been promised!'; resolve(data); }, 10); }) ); const responsePromise = axios('/foo'); const request = await waitForRequest(); request.respondWith({ status: 200, responseText: 'OK', }); const response = await responsePromise; expect(response.data).toBe('you have been promised!'); }); describe('given multiple response interceptors', () => { const fireRequest = async () => { const responsePromise = axios('/foo'); const request = await waitForRequest(); request.respondWith({ status: 200, responseText: 'OK', }); return responsePromise; }; it('then each interceptor is executed', async () => { const interceptor1 = vi.fn((response) => response); const interceptor2 = vi.fn((response) => response); axios.interceptors.response.use(interceptor1); axios.interceptors.response.use(interceptor2); await fireRequest(); expect(interceptor1).toHaveBeenCalled(); expect(interceptor2).toHaveBeenCalled(); }); it('then they are executed in the order they were added', async () => { const interceptor1 = vi.fn((response) => response); const interceptor2 = vi.fn((response) => response); axios.interceptors.response.use(interceptor1); axios.interceptors.response.use(interceptor2); await fireRequest(); expect(interceptor1.mock.invocationCallOrder[0]).toBeLessThan(interceptor2.mock.invocationCallOrder[0]); }); it("then only the last interceptor's result is returned", async () => { axios.interceptors.response.use(() => 'response 1'); axios.interceptors.response.use(() => 'response 2'); const response = await fireRequest(); expect(response).toBe('response 2'); }); it("then every interceptor receives the result of its predecessor", async () => { axios.interceptors.response.use(() => 'response 1'); axios.interceptors.response.use((response) => [response, 'response 2']); const response = await fireRequest(); expect(response).toEqual(['response 1', 'response 2']); }); describe('and when the fulfillment interceptor throws', () => { const fireRequestCatch = async () => { const responsePromise = axios('/foo').catch(() => {}); const request = await waitForRequest(); request.respondWith({ status: 200, responseText: 'OK', }); await responsePromise; }; it('then the following fulfillment interceptor is not called', async () => { axios.interceptors.response.use(() => { throw new Error('throwing interceptor'); }); const interceptor2 = vi.fn((response) => response); axios.interceptors.response.use(interceptor2); await fireRequestCatch(); expect(interceptor2).not.toHaveBeenCalled(); }); it('then the following rejection interceptor is called', async () => { axios.interceptors.response.use(() => { throw new Error('throwing interceptor'); }); const rejectIntercept = vi.fn((error) => Promise.reject(error)); axios.interceptors.response.use(() => {}, rejectIntercept); await fireRequestCatch(); expect(rejectIntercept).toHaveBeenCalled(); }); it('once caught, another following fulfillment interceptor is called again', async () => { axios.interceptors.response.use(() => { throw new Error('throwing interceptor'); }); axios.interceptors.response.use( () => {}, () => 'recovered' ); const interceptor3 = vi.fn((response) => response); axios.interceptors.response.use(interceptor3); await fireRequestCatch(); expect(interceptor3).toHaveBeenCalled(); }); }); }); it('should allow removing interceptors', async () => { axios.interceptors.response.use((data) => { data.data = `${data.data}1`; return data; }); const intercept = axios.interceptors.response.use((data) => { data.data = `${data.data}2`; return data; }); axios.interceptors.response.use((data) => { data.data = `${data.data}3`; return data; }); axios.interceptors.response.eject(intercept); const responsePromise = axios('/foo'); const request = await waitForRequest(); request.respondWith({ status: 200, responseText: 'OK', }); const response = await responsePromise; expect(response.data).toBe('OK13'); }); it('should remove async interceptor before making request and execute synchronously', async () => { let asyncFlag = false; const asyncIntercept = axios.interceptors.request.use( (config) => { config.headers.async = 'async it!'; return config; }, null, { synchronous: false } ); axios.interceptors.request.use( (config) => { config.headers.sync = 'hello world'; expect(asyncFlag).toBe(false); return config; }, null, { synchronous: true } ); axios.interceptors.request.eject(asyncIntercept); const responsePromise = axios('/foo'); asyncFlag = true; const request = await waitForRequest(); expect(request.requestHeaders.async).toBeUndefined(); expect(request.requestHeaders.sync).toBe('hello world'); request.respondWith(); await responsePromise; }); it('should execute interceptors before transformers', async () => { axios.interceptors.request.use((config) => { config.data.baz = 'qux'; return config; }); const responsePromise = axios.post('/foo', { foo: 'bar', }); const request = await waitForRequest(); expect(request.params).toEqual('{"foo":"bar","baz":"qux"}'); request.respondWith(); await responsePromise; }); it('should modify base URL in request interceptor', async () => { const instance = axios.create({ baseURL: 'http://test.com/', }); instance.interceptors.request.use((config) => { config.baseURL = 'http://rebase.com/'; return config; }); const responsePromise = instance.get('/foo'); const request = await waitForRequest(); expect(request.url).toBe('http://rebase.com/foo'); request.respondWith(); await responsePromise; }); it('should clear all request interceptors', () => { const instance = axios.create({ baseURL: 'http://test.com/', }); instance.interceptors.request.use((config) => config); instance.interceptors.request.clear(); expect(instance.interceptors.request.handlers.length).toBe(0); }); it('should clear all response interceptors', () => { const instance = axios.create({ baseURL: 'http://test.com/', }); instance.interceptors.response.use((config) => config); instance.interceptors.response.clear(); expect(instance.interceptors.response.handlers.length).toBe(0); }); }); axios-axios-aa54a5e/tests/browser/isURLSameOrigin.browser.test.js000066400000000000000000000020601526660636600252620ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import isURLSameOrigin from '../../lib/helpers/isURLSameOrigin.js'; describe('helpers::isURLSameOrigin (vitest browser)', () => { it('detects same origin', () => { expect(isURLSameOrigin(window.location.href)).toBe(true); }); it('detects different origin', () => { expect(isURLSameOrigin('https://github.com/axios/axios')).toBe(false); }); it('detects a different port on the same hostname', () => { const url = new URL(window.location.href); url.port = url.port === '65535' ? '65534' : '65535'; expect(isURLSameOrigin(url.href)).toBe(false); }); it('detects a different protocol on the same hostname', () => { const url = new URL(window.location.href); url.protocol = url.protocol === 'http:' ? 'https:' : 'http:'; expect(isURLSameOrigin(url.href)).toBe(false); }); it.each(['', '/resource', 'resource?query=value#fragment'])( 'resolves the relative URL %j against the document base', (url) => { expect(isURLSameOrigin(url)).toBe(true); } ); }); axios-axios-aa54a5e/tests/browser/options.browser.test.js000066400000000000000000000135761526660636600240170ustar00rootroot00000000000000import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import axios from '../../index.js'; class MockXMLHttpRequest { constructor() { this.requestHeaders = {}; this.responseHeaders = ''; this.readyState = 0; this.status = 0; this.statusText = ''; this.responseText = ''; this.response = null; this.onreadystatechange = null; this.onloadend = null; this.upload = { addEventListener() {}, }; } open(method, url, async = true) { this.method = method; this.url = url; this.async = async; } setRequestHeader(key, value) { this.requestHeaders[key] = value; } addEventListener() {} getAllResponseHeaders() { return this.responseHeaders; } send(data) { this.params = data; requests.push(this); } respondWith({ status = 200, statusText = 'OK', responseText = '', responseHeaders = '' } = {}) { this.status = status; this.statusText = statusText; this.responseText = responseText; this.response = responseText; this.responseHeaders = responseHeaders; this.readyState = 4; queueMicrotask(() => { if (this.onloadend) { this.onloadend(); } else if (this.onreadystatechange) { this.onreadystatechange(); } }); } } let requests = []; let OriginalXMLHttpRequest; const startRequest = (...args) => { const promise = axios(...args); const request = requests.at(-1); expect(request).toBeDefined(); return { request, promise }; }; const flushSuccess = async (request, promise) => { request.respondWith({ status: 200 }); await promise; }; describe('options (vitest browser)', () => { beforeEach(() => { requests = []; OriginalXMLHttpRequest = window.XMLHttpRequest; window.XMLHttpRequest = MockXMLHttpRequest; }); afterEach(() => { window.XMLHttpRequest = OriginalXMLHttpRequest; vi.restoreAllMocks(); }); it('should default method to get', async () => { const { request, promise } = startRequest('/foo'); expect(request.method).toBe('GET'); await flushSuccess(request, promise); }); it('should accept headers', async () => { const { request, promise } = startRequest('/foo', { headers: { 'X-Requested-With': 'XMLHttpRequest', }, }); expect(request.requestHeaders['X-Requested-With']).toBe('XMLHttpRequest'); await flushSuccess(request, promise); }); it('should accept params', async () => { const { request, promise } = startRequest('/foo', { params: { foo: 123, bar: 456, }, }); expect(request.url).toBe('/foo?foo=123&bar=456'); await flushSuccess(request, promise); }); it('should allow overriding default headers', async () => { const { request, promise } = startRequest('/foo', { headers: { Accept: 'foo/bar', }, }); expect(request.requestHeaders.Accept).toBe('foo/bar'); await flushSuccess(request, promise); }); it('should accept base URL', async () => { const instance = axios.create({ baseURL: 'http://test.com/', }); const promise = instance.get('/foo'); const request = requests.at(-1); expect(request).toBeDefined(); expect(request.url).toBe('http://test.com/foo'); await flushSuccess(request, promise); }); it('should warn about baseUrl', async () => { const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); const instance = axios.create({ baseUrl: 'http://example.com/', }); const promise = instance.get('/foo'); const request = requests.at(-1); expect(request).toBeDefined(); expect(warnSpy).toHaveBeenCalledWith('baseUrl is likely a misspelling of baseURL'); expect(request.url).toBe('/foo'); await flushSuccess(request, promise); }); it('should ignore base URL if request URL is absolute', async () => { const instance = axios.create({ baseURL: 'http://someurl.com/', }); const promise = instance.get('http://someotherurl.com/'); const request = requests.at(-1); expect(request).toBeDefined(); expect(request.url).toBe('http://someotherurl.com/'); await flushSuccess(request, promise); }); it('should combine the URLs if base url and request url exist and allowAbsoluteUrls is false', async () => { const instance = axios.create({ baseURL: 'http://someurl.com/', allowAbsoluteUrls: false, }); const promise = instance.get('http://someotherurl.com/'); const request = requests.at(-1); expect(request).toBeDefined(); expect(request.url).toBe('http://someurl.com/http://someotherurl.com/'); await flushSuccess(request, promise); }); it('should change only the baseURL of the specified instance', () => { const instance1 = axios.create(); const instance2 = axios.create(); instance1.defaults.baseURL = 'http://instance1.example.com/'; expect(instance2.defaults.baseURL).not.toBe('http://instance1.example.com/'); }); it('should change only the headers of the specified instance', () => { const instance1 = axios.create(); const instance2 = axios.create(); instance1.defaults.headers.common.Authorization = 'faketoken'; instance2.defaults.headers.common.Authorization = 'differentfaketoken'; instance1.defaults.headers.common['Content-Type'] = 'application/xml'; instance2.defaults.headers.common['Content-Type'] = 'application/x-www-form-urlencoded'; expect(axios.defaults.headers.common.Authorization).toBeUndefined(); expect(instance1.defaults.headers.common.Authorization).toBe('faketoken'); expect(instance2.defaults.headers.common.Authorization).toBe('differentfaketoken'); expect(axios.defaults.headers.common['Content-Type']).toBeUndefined(); expect(instance1.defaults.headers.common['Content-Type']).toBe('application/xml'); expect(instance2.defaults.headers.common['Content-Type']).toBe('application/x-www-form-urlencoded'); }); }); axios-axios-aa54a5e/tests/browser/progress.browser.test.js000066400000000000000000000424661526660636600241700ustar00rootroot00000000000000import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import axios from '../../index.js'; // Real EventTarget + ProgressEvent dispatches so events keep genuine browser // semantics: `target` survives the dispatch, `currentTarget` does not. const $listeners = Symbol('listeners'); class ProgressEventTarget extends EventTarget { constructor() { super(); this[$listeners] = {}; } addEventListener(type, listener, options) { (this[$listeners][type] || (this[$listeners][type] = new Set())).add(listener); super.addEventListener(type, listener, options); } removeEventListener(type, listener, options) { this[$listeners][type] && this[$listeners][type].delete(listener); super.removeEventListener(type, listener, options); } } class MockXMLHttpRequest extends ProgressEventTarget { constructor() { super(); this.requestHeaders = {}; this.responseHeaders = {}; this.readyState = 0; this.status = 0; this.statusText = ''; this.responseText = ''; this.response = null; this.timeout = 0; this.withCredentials = false; this.onreadystatechange = null; this.onloadend = null; this.onabort = null; this.onerror = null; this.ontimeout = null; this.upload = new ProgressEventTarget(); // like a real XHR, invoke the onloadend property while loadend dispatches this.addEventListener('loadend', (event) => { this.onloadend && this.onloadend(event); }); } open(method, url, async = true) { this.method = method; this.url = url; this.async = async; } setRequestHeader(key, value) { this.requestHeaders[key] = value; } getAllResponseHeaders() { return Object.entries(this.responseHeaders) .map(([key, value]) => `${key}: ${value}`) .join('\n'); } send(data) { this.params = data; this.readyState = 1; requests.push(this); } abort() { this.aborted = true; this.readyState = 0; this.status = 0; } getListenerCount(type, target = 'request') { const eventTarget = target === 'upload' ? this.upload : this; return eventTarget[$listeners][type] ? eventTarget[$listeners][type].size : 0; } emit(type, target = 'request', init = {}) { const eventTarget = target === 'upload' ? this.upload : this; eventTarget.dispatchEvent(new ProgressEvent(type, init)); } respondWith({ status = 200, statusText = 'OK', responseText = '', response = null, headers = {}, } = {}) { this.status = status; this.statusText = statusText; this.responseText = responseText; this.response = response; this.responseHeaders = headers; this.readyState = 4; this.emit('progress', 'request', { loaded: responseText.length, total: responseText.length, lengthComputable: true, }); queueMicrotask(() => { if (this.onloadend) { this.emit('loadend', 'request', { loaded: this.responseText.length, total: this.responseText.length, lengthComputable: true, }); } else if (this.onreadystatechange) { this.onreadystatechange(); } }); } } let requests = []; let OriginalXMLHttpRequest; const getLastRequest = () => { const request = requests.at(-1); expect(request).toBeDefined(); return request; }; describe('progress (vitest browser)', () => { beforeEach(() => { requests = []; OriginalXMLHttpRequest = window.XMLHttpRequest; window.XMLHttpRequest = MockXMLHttpRequest; }); afterEach(() => { window.XMLHttpRequest = OriginalXMLHttpRequest; vi.restoreAllMocks(); vi.useRealTimers(); }); it.each(['success', 'cancellation'])( 'removes progress listeners from a native XHR after %s', async (kind) => { let request; const registrations = []; window.XMLHttpRequest = class extends OriginalXMLHttpRequest { constructor() { super(); request = this; for (const target of [this, this.upload]) { registrations.push({ target, add: vi.spyOn(target, 'addEventListener'), remove: vi.spyOn(target, 'removeEventListener'), }); } } }; const controller = new AbortController(); const source = axios.CancelToken.source(); const unsubscribe = vi.spyOn(source.token, 'unsubscribe'); const removeListener = vi.spyOn(controller.signal, 'removeEventListener'); const upload = vi.fn(); const download = vi.fn(() => { if (kind === 'cancellation') controller.abort(); }); const result = await axios .get(new URL('../../package.json', import.meta.url).href, { adapter: 'xhr', timeout: 5000, signal: controller.signal, cancelToken: source.token, onUploadProgress: upload, onDownloadProgress: download, }) .catch((error) => error); if (kind === 'success') expect(result.status).toBe(200); else expect(result.code).toBe('ERR_CANCELED'); expect(download).toHaveBeenCalled(); expect(unsubscribe).toHaveBeenCalled(); expect(removeListener).toHaveBeenCalledWith('abort', expect.any(Function)); for (const { target, add, remove } of registrations) { const types = target === request ? ['progress'] : ['progress', 'loadend']; for (const type of types) { const registered = add.mock.calls.filter(([eventType]) => eventType === type); expect(registered).toHaveLength(1); expect(remove).toHaveBeenCalledWith(type, registered[0][1]); } } // Verify that detached callbacks cannot be invoked by later dispatches. const downloadCount = download.mock.calls.length; const uploadCount = upload.mock.calls.length; request.dispatchEvent(new ProgressEvent('progress', { loaded: 1000 })); request.upload.dispatchEvent(new ProgressEvent('progress', { loaded: 1000 })); request.upload.dispatchEvent(new ProgressEvent('loadend')); expect(download).toHaveBeenCalledTimes(downloadCount); expect(upload).toHaveBeenCalledTimes(uploadCount); } ); it.each(['throw', 'cancel'])( 'removes listeners when a flushed upload callback triggers %s', async (kind) => { vi.useFakeTimers(); const controller = new AbortController(); const source = axios.CancelToken.source(); const unsubscribe = vi.spyOn(source.token, 'unsubscribe'); const removeListener = vi.spyOn(controller.signal, 'removeEventListener'); const upload = vi.fn(() => { if (upload.mock.calls.length !== 2) return; if (kind === 'throw') throw new Error('upload listener failure'); controller.abort(); }); const responsePromise = axios.post('/foo', 'payload', { signal: controller.signal, cancelToken: source.token, onUploadProgress: upload, onDownloadProgress: () => {}, }); const request = getLastRequest(); request.emit('progress', 'upload', { loaded: 1 }); request.emit('progress', 'upload', { loaded: 2 }); expect(upload).toHaveBeenCalledOnce(); request.respondWith({ responseText: 'ok' }); if (kind === 'cancel') { await expect(responsePromise).rejects.toMatchObject({ code: 'ERR_CANCELED' }); } else { expect((await responsePromise).status).toBe(200); } expect(upload).toHaveBeenCalledTimes(2); expect(request.getListenerCount('progress')).toBe(0); expect(request.getListenerCount('progress', 'upload')).toBe(0); expect(request.getListenerCount('loadend', 'upload')).toBe(0); expect(unsubscribe).toHaveBeenCalled(); expect(removeListener).toHaveBeenCalledWith('abort', expect.any(Function)); if (kind === 'throw') { await expect(vi.runAllTimersAsync()).rejects.toThrow('upload listener failure'); } expect(vi.getTimerCount()).toBe(0); } ); it('should add a download progress handler', async () => { const progressSpy = vi.fn(); const responsePromise = axios('/foo', { onDownloadProgress: progressSpy }); const request = getLastRequest(); request.respondWith({ status: 200, responseText: '{"foo": "bar"}', }); await responsePromise; expect(progressSpy).toHaveBeenCalled(); }); it('should add an upload progress handler', async () => { const progressSpy = vi.fn(); const responsePromise = axios('/foo', { onUploadProgress: progressSpy }); const request = getLastRequest(); expect(request.getListenerCount('progress', 'upload')).toBe(1); request.respondWith({ status: 200, responseText: '{"foo": "bar"}', }); await responsePromise; }); it('should add both upload and download progress handlers', async () => { const downloadProgressSpy = vi.fn(); const uploadProgressSpy = vi.fn(); const responsePromise = axios('/foo', { onDownloadProgress: downloadProgressSpy, onUploadProgress: uploadProgressSpy, }); const request = getLastRequest(); expect(downloadProgressSpy).not.toHaveBeenCalled(); expect(request.getListenerCount('progress', 'request')).toBe(1); expect(request.getListenerCount('progress', 'upload')).toBe(1); request.respondWith({ status: 200, responseText: '{"foo": "bar"}', }); await responsePromise; expect(downloadProgressSpy).toHaveBeenCalled(); }); it('should add a download progress handler from instance config', async () => { const progressSpy = vi.fn(); const instance = axios.create({ onDownloadProgress: progressSpy, }); const responsePromise = instance.get('/foo'); const request = getLastRequest(); request.respondWith({ status: 200, responseText: '{"foo": "bar"}', }); await responsePromise; expect(progressSpy).toHaveBeenCalled(); }); it('should add an upload progress handler from instance config', async () => { const progressSpy = vi.fn(); const instance = axios.create({ onUploadProgress: progressSpy, }); const responsePromise = instance.get('/foo'); const request = getLastRequest(); expect(request.getListenerCount('progress', 'upload')).toBe(1); request.respondWith({ status: 200, responseText: '{"foo": "bar"}', }); await responsePromise; }); it('should add upload and download progress handlers from instance config', async () => { const downloadProgressSpy = vi.fn(); const uploadProgressSpy = vi.fn(); const instance = axios.create({ onDownloadProgress: downloadProgressSpy, onUploadProgress: uploadProgressSpy, }); const responsePromise = instance.get('/foo'); const request = getLastRequest(); expect(downloadProgressSpy).not.toHaveBeenCalled(); expect(request.getListenerCount('progress', 'request')).toBe(1); expect(request.getListenerCount('progress', 'upload')).toBe(1); request.respondWith({ status: 200, responseText: '{"foo": "bar"}', }); await responsePromise; expect(downloadProgressSpy).toHaveBeenCalled(); }); it('should deliver the full streamed payload to a listener reading from the live event target', async () => { let received = ''; let lastLength = 0; const responsePromise = axios('/foo', { onDownloadProgress: ({ event }) => { const xhr = event && event.currentTarget; if (!xhr || typeof xhr.responseText !== 'string') { return; } received += xhr.responseText.slice(lastLength); lastLength = xhr.responseText.length; }, }); const request = getLastRequest(); request.responseText = 'AAAA'; request.emit('progress', 'request', { loaded: 4 }); request.responseText += 'BBBB'; request.emit('progress', 'request', { loaded: 8 }); request.responseText += 'CCCC'; request.emit('progress', 'request', { loaded: 12 }); request.respondWith({ status: 200, responseText: request.responseText }); await responsePromise; expect(received).toBe('AAAABBBBCCCC'); }); it('should always deliver a final download progress event with a live event target', async () => { const deliveries = []; const responsePromise = axios('/foo', { onDownloadProgress: ({ loaded, event }) => { deliveries.push({ loaded, liveTarget: !!(event && event.currentTarget) }); }, }); const request = getLastRequest(); request.responseText = 'AAAA'; request.emit('progress', 'request', { loaded: 4 }); request.responseText += 'BBBB'; request.emit('progress', 'request', { loaded: 8 }); request.respondWith({ status: 200, responseText: request.responseText }); await responsePromise; expect(deliveries.at(-1)).toEqual({ loaded: 8, liveTarget: true }); }); it('should flush pending download progress in the ready-state fallback', async () => { window.XMLHttpRequest = class extends MockXMLHttpRequest { constructor() { super(); delete this.onloadend; } }; const loaded = []; const responsePromise = axios('/foo', { onDownloadProgress: (event) => loaded.push(event.loaded), }); const request = getLastRequest(); request.responseText = 'AAAA'; request.emit('progress', 'request', { loaded: 4 }); request.respondWith({ status: 200, responseText: 'AAAABBBB' }); await responsePromise; expect(loaded).toEqual([4, 8]); }); it('should not force a final download progress event for a status-zero failure', async () => { const eventTypes = []; const responsePromise = axios('/foo', { onDownloadProgress: ({ event }) => { eventTypes.push(event.type); }, }); const request = getLastRequest(); request.respondWith({ status: 0 }); await expect(responsePromise).rejects.toMatchObject({ code: 'ECONNABORTED' }); expect(eventTypes).toEqual(['progress']); }); it('should replay pending upload progress when loadend reports an unsuccessful transfer', async () => { const loaded = []; const responsePromise = axios.post('/foo', 'payload', { onUploadProgress: (event) => { loaded.push(event.loaded); }, }); const request = getLastRequest(); request.emit('progress', 'upload', { loaded: 3, total: 7, lengthComputable: true }); request.emit('progress', 'upload', { loaded: 5, total: 7, lengthComputable: true }); request.emit('loadend', 'upload', { loaded: 0, total: 0, lengthComputable: false }); expect(loaded).toEqual([3, 5]); request.respondWith({ status: 200, responseText: 'ok' }); await responsePromise; }); it('should stop loadend processing when the final progress listener cancels the request', async () => { const controller = new AbortController(); const reportedErrors = []; const onWindowError = (event) => { reportedErrors.push(event.error); event.preventDefault(); }; window.addEventListener('error', onWindowError); try { const responsePromise = axios('/foo', { signal: controller.signal, onDownloadProgress: ({ event }) => { if (event && event.type === 'loadend') { controller.abort(); } }, }); const request = getLastRequest(); request.respondWith({ status: 200, responseText: 'complete' }); await expect(responsePromise).rejects.toMatchObject({ code: 'ERR_CANCELED' }); await new Promise((resolve) => setTimeout(resolve)); expect(request.aborted).toBe(true); expect(reportedErrors).toEqual([]); } finally { window.removeEventListener('error', onWindowError); } }); it('should settle the request when the final progress listener throws', async () => { // fake timers keep the rethrown listener error parked so it cannot fail the test run vi.useFakeTimers(); try { let finalDeliveries = 0; const responsePromise = axios('/foo', { onDownloadProgress: ({ event }) => { if (event && event.type === 'loadend') { finalDeliveries += 1; throw new Error('listener failure'); } }, }); const request = getLastRequest(); request.responseText = 'AAAA'; request.emit('progress', 'request', { loaded: 4 }); request.respondWith({ status: 200, responseText: request.responseText }); const response = await responsePromise; expect(finalDeliveries).toBe(1); expect(response.status).toBe(200); } finally { vi.useRealTimers(); } }); it('should keep the request reachable via event.target on throttle-deferred deliveries', async () => { vi.useFakeTimers(); try { const deliveries = []; const responsePromise = axios('/foo', { onDownloadProgress: ({ event }) => { deliveries.push(event && event.target); }, }); const request = getLastRequest(); request.responseText = 'AAAA'; request.emit('progress', 'request', { loaded: 4 }); request.responseText += 'BBBB'; request.emit('progress', 'request', { loaded: 8 }); // step past the throttle window so the second event arrives via the timer await vi.advanceTimersByTimeAsync(400); request.respondWith({ status: 200, responseText: request.responseText }); await responsePromise; expect(deliveries.length).toBeGreaterThanOrEqual(2); expect(deliveries.every((target) => target === request)).toBe(true); } finally { vi.useRealTimers(); } }); }); axios-axios-aa54a5e/tests/browser/promise.browser.test.js000066400000000000000000000054231526660636600237720ustar00rootroot00000000000000import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import axios from '../../index.js'; class MockXMLHttpRequest { constructor() { this.requestHeaders = {}; this.responseHeaders = ''; this.readyState = 0; this.status = 0; this.statusText = ''; this.responseText = ''; this.response = null; this.onreadystatechange = null; this.onloadend = null; this.upload = { addEventListener() {}, }; } open(method, url, async = true) { this.method = method; this.url = url; this.async = async; } setRequestHeader(key, value) { this.requestHeaders[key] = value; } addEventListener() {} getAllResponseHeaders() { return this.responseHeaders; } send(data) { this.params = data; requests.push(this); } respondWith({ status = 200, statusText = 'OK', responseText = '', responseHeaders = '' } = {}) { this.status = status; this.statusText = statusText; this.responseText = responseText; this.response = responseText; this.responseHeaders = responseHeaders; this.readyState = 4; queueMicrotask(() => { if (this.onloadend) { this.onloadend(); } else if (this.onreadystatechange) { this.onreadystatechange(); } }); } abort() {} } let requests = []; let OriginalXMLHttpRequest; const getLastRequest = () => { const request = requests.at(-1); expect(request).toBeDefined(); return request; }; describe('promise (vitest browser)', () => { beforeEach(() => { requests = []; OriginalXMLHttpRequest = window.XMLHttpRequest; window.XMLHttpRequest = MockXMLHttpRequest; }); afterEach(() => { window.XMLHttpRequest = OriginalXMLHttpRequest; }); it('should provide succinct object to then', async () => { const responsePromise = axios('/foo'); const request = getLastRequest(); request.respondWith({ status: 200, responseText: '{"hello":"world"}', responseHeaders: 'Content-Type: application/json', }); const response = await responsePromise; expect(typeof response).toBe('object'); expect(response.data.hello).toBe('world'); expect(response.status).toBe(200); expect(response.headers['content-type']).toBe('application/json'); expect(response.config.url).toBe('/foo'); }); it('should support all', async () => { const result = await axios.all([true, 123]); expect(result).toEqual([true, 123]); }); it('should support spread', async () => { let fulfilled = false; const result = await axios.all([123, 456]).then( axios.spread((a, b) => { expect(a + b).toBe(123 + 456); fulfilled = true; return 'hello world'; }) ); expect(fulfilled).toBe(true); expect(result).toBe('hello world'); }); }); axios-axios-aa54a5e/tests/browser/requests.browser.test.js000066400000000000000000000433231526660636600241700ustar00rootroot00000000000000import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import axios from '../../index.js'; import AxiosError from '../../lib/core/AxiosError.js'; class MockXMLHttpRequest { constructor() { this.requestHeaders = {}; this.responseHeaders = {}; this.readyState = 0; this.status = 0; this.statusText = ''; this.responseText = ''; this.response = null; this.responseURL = ''; this.timeout = 0; this.withCredentials = false; this.onreadystatechange = null; this.onloadend = null; this.onabort = null; this.onerror = null; this.ontimeout = null; this.upload = { addEventListener() {}, }; } open(method, url, async = true) { this.method = method; this.url = url; this.async = async; } setRequestHeader(key, value) { this.requestHeaders[key] = value; } addEventListener() {} getAllResponseHeaders() { return Object.entries(this.responseHeaders) .map(([key, value]) => `${key}: ${value}`) .join('\n'); } send(data) { this.params = data; this.readyState = 1; requests.push(this); } respondWith({ status = 200, statusText = 'OK', responseText = '', response = null, headers = {}, responseURL = '', } = {}) { this.status = status; this.statusText = statusText; this.responseText = responseText; this.response = response; this.responseHeaders = headers; this.responseURL = responseURL; this.readyState = 4; this.finish(); } responseTimeout() { if (this.ontimeout) { this.ontimeout(); } } failNetworkError(message = 'Network Error') { if (this.onerror) { this.onerror({ message }); } } abort() { if (this.onabort) { this.onabort(); } } finish() { queueMicrotask(() => { if (this.onloadend) { this.onloadend(); } else if (this.onreadystatechange) { this.onreadystatechange(); } }); } } let requests = []; let OriginalXMLHttpRequest; describe('native XHR scheme casing', () => { it.each(['data:', 'DATA:', 'DaTa:'])('decodes a %s URL', async (scheme) => { const response = await axios.get(scheme + 'text/plain,Hello%20World', { adapter: 'xhr', responseType: 'text', }); expect(response.status).toBe(200); expect(response.data).toBe('Hello World'); }); }); const startRequest = (...args) => { const promise = axios(...args); const request = requests.at(-1); expect(request).toBeDefined(); return { request, promise }; }; const flushSuccess = async (request, promise) => { request.respondWith({ status: 200 }); await promise; }; describe('requests (vitest browser)', () => { beforeEach(() => { requests = []; OriginalXMLHttpRequest = window.XMLHttpRequest; window.XMLHttpRequest = MockXMLHttpRequest; }); afterEach(() => { window.XMLHttpRequest = OriginalXMLHttpRequest; vi.restoreAllMocks(); }); it.each([200, 500])('reports elapsed adapter time for XHR status %s', async (status) => { const clock = vi.spyOn(Date, 'now').mockReturnValue(1000); const { request, promise } = startRequest('/timing', { adapter: 'xhr' }); clock.mockReturnValue(1025); request.respondWith({ status, responseText: '{"ok":true}' }); const result = await promise.catch((error) => error); expect(axios.isAxiosError(result)).toBe(status === 500); const response = status === 500 ? result.response : result; expect(response.duration).toBe(25); expect(response.data).toEqual({ ok: true }); }); it('should treat single string arg as url', async () => { const { request, promise } = startRequest('/foo'); expect(request.url).toBe('/foo'); expect(request.method).toBe('GET'); await flushSuccess(request, promise); }); it('should treat method value as lowercase string', async () => { const { request, promise } = startRequest({ url: '/foo', method: 'POST', }); request.respondWith({ status: 200 }); const response = await promise; expect(response.config.method).toBe('post'); }); it('should allow string arg as url, and config arg', async () => { const { request, promise } = startRequest('/foo', { method: 'post', }); expect(request.url).toBe('/foo'); expect(request.method).toBe('POST'); await flushSuccess(request, promise); }); it('should allow data', async () => { const { request, promise } = startRequest('/foo', { method: 'delete', data: { foo: 'bar' }, }); expect(request.params).toBe(JSON.stringify({ foo: 'bar' })); await flushSuccess(request, promise); }); it('should make an http request', async () => { const { request, promise } = startRequest('/foo'); expect(request.url).toBe('/foo'); await flushSuccess(request, promise); }); describe('timeouts', () => { it('should handle timeouts', async () => { const { request, promise } = startRequest({ url: '/foo', timeout: 100, }); request.responseTimeout(); const err = await promise.catch((error) => error); expect(err).toBeInstanceOf(Error); expect(err.code).toBe('ECONNABORTED'); }); describe('transitional.clarifyTimeoutError', () => { it('should throw ETIMEDOUT instead of ECONNABORTED on request timeouts', async () => { const { request, promise } = startRequest({ url: '/foo', timeout: 100, transitional: { clarifyTimeoutError: true, }, }); request.responseTimeout(); const err = await promise.catch((error) => error); expect(err).toBeInstanceOf(Error); expect(err.code).toBe('ETIMEDOUT'); }); }); }); it('should reject on network errors', async () => { const { request, promise } = startRequest('http://thisisnotaserver/foo'); request.failNetworkError(); const reason = await promise.catch((error) => error); expect(reason).toBeInstanceOf(Error); expect(reason.config.method).toBe('get'); expect(reason.config.url).toBe('http://thisisnotaserver/foo'); expect(reason.request).toBeInstanceOf(MockXMLHttpRequest); }); it('rejects malformed HTTP URLs before opening an XHR request', async () => { const openSpy = vi.spyOn(MockXMLHttpRequest.prototype, 'open'); const reason = await axios .get('\u0000https:example.com/users', { adapter: 'xhr', headers: { 'X-Test': 'yes', }, }) .catch((error) => error); expect(reason).toBeInstanceOf(AxiosError); expect(reason.code).toBe(AxiosError.ERR_INVALID_URL); expect(reason.message).toBe( 'Invalid URL "https:example.com/users": missing "//" after protocol' ); expect(reason.config.url).toBe('\u0000https:example.com/users'); expect(reason.config.headers.get('X-Test')).toBe('yes'); expect(openSpy).not.toHaveBeenCalled(); expect(requests).toHaveLength(0); }); it('should reject on abort', async () => { const { request, promise } = startRequest('/foo'); request.abort(); const reason = await promise.catch((error) => error); expect(reason).toBeInstanceOf(Error); expect(reason.config.method).toBe('get'); expect(reason.config.url).toBe('/foo'); expect(reason.request).toBeInstanceOf(MockXMLHttpRequest); }); it('should reject when validateStatus returns false', async () => { const { request, promise } = startRequest('/foo', { validateStatus(status) { return status !== 500; }, }); request.respondWith({ status: 500 }); const reason = await promise.catch((error) => error); expect(reason).toBeInstanceOf(Error); expect(reason.message).toBe('Request failed with status code 500'); expect(reason.config.method).toBe('get'); expect(reason.config.url).toBe('/foo'); expect(reason.response.status).toBe(500); }); it('should resolve when validateStatus returns true', async () => { const { request, promise } = startRequest('/foo', { validateStatus(status) { return status === 500; }, }); request.respondWith({ status: 500 }); await expect(promise).resolves.toBeDefined(); }); // https://bugzilla.mozilla.org/show_bug.cgi?id=1505389 it('should reject when the response status is 0 (non-file protocol)', async () => { const { request, promise } = startRequest('/foo'); request.respondWith({ status: 0 }); const reason = await promise.catch((error) => error); expect(reason).toBeInstanceOf(AxiosError); expect(reason.code).toBe(AxiosError.ECONNABORTED); expect(reason.message).toBe('Request aborted'); }); it('should resolve when the response status is 0 (file protocol)', async () => { const { request, promise } = startRequest('file:///xxx'); request.respondWith({ status: 0, responseURL: 'file:///xxx', }); await expect(promise).resolves.toBeDefined(); }); it('should resolve when the response status is 0 for a relative file: read', async () => { const { request, promise } = startRequest('data.json'); request.respondWith({ status: 0, responseURL: 'file:///app/data.json', }); await expect(promise).resolves.toBeDefined(); }); it('should resolve when validateStatus is null', async () => { const { request, promise } = startRequest('/foo', { validateStatus: null, }); request.respondWith({ status: 500 }); await expect(promise).resolves.toBeDefined(); }); it('should resolve when validateStatus is undefined by default', async () => { const { request, promise } = startRequest('/foo', { validateStatus: undefined, }); request.respondWith({ status: 500 }); await expect(promise).resolves.toBeDefined(); }); // https://github.com/axios/axios/issues/6688 it('should reject when validateStatus is undefined and the transitional option is disabled', async () => { const { request, promise } = startRequest('/foo', { validateStatus: undefined, transitional: { validateStatusUndefinedResolves: false }, }); request.respondWith({ status: 500 }); const reason = await promise.catch((error) => error); expect(reason).toBeInstanceOf(Error); expect(reason.message).toBe('Request failed with status code 500'); expect(reason.config.method).toBe('get'); expect(reason.config.url).toBe('/foo'); expect(reason.response.status).toBe(500); }); // https://github.com/axios/axios/issues/378 it('should return JSON when rejecting', async () => { const { request, promise } = startRequest( '/api/account/signup', { username: null, password: null, }, { method: 'post', headers: { Accept: 'application/json', }, } ); request.respondWith({ status: 400, statusText: 'Bad Request', responseText: '{"error": "BAD USERNAME", "code": 1}', }); const error = await promise.catch((err) => err); const response = error.response; expect(typeof response.data).toBe('object'); expect(response.data.error).toBe('BAD USERNAME'); expect(response.data.code).toBe(1); }); it('should make cross domain http request', async () => { const { request, promise } = startRequest('www.someurl.com/foo', { method: 'post', }); request.respondWith({ status: 200, statusText: 'OK', responseText: '{"foo": "bar"}', headers: { 'Content-Type': 'application/json', }, }); const response = await promise; expect(response.data.foo).toBe('bar'); expect(response.status).toBe(200); expect(response.statusText).toBe('OK'); expect(response.headers['content-type']).toBe('application/json'); }); it('should supply correct response', async () => { const { request, promise } = startRequest('/foo', { method: 'post', }); request.respondWith({ status: 200, statusText: 'OK', responseText: '{"foo": "bar"}', headers: { 'Content-Type': 'application/json', }, }); const response = await promise; expect(response.data.foo).toBe('bar'); expect(response.status).toBe(200); expect(response.statusText).toBe('OK'); expect(response.headers['content-type']).toBe('application/json'); }); it('should not modify the config url with relative baseURL', async () => { const { request, promise } = startRequest('/foo', { baseURL: '/api', }); request.respondWith({ status: 404, statusText: 'NOT FOUND', responseText: 'Resource not found', }); const error = await promise.catch((err) => err); const config = error.config; expect(config.baseURL).toBe('/api'); expect(config.url).toBe('/foo'); }); it('should allow overriding Content-Type header case-insensitive', async () => { const contentType = 'application/vnd.myapp.type+json'; const { request, promise } = startRequest('/foo', { method: 'post', data: { prop: 'value' }, headers: { 'Content-Type': contentType, }, }); expect(request.requestHeaders['Content-Type']).toBe(contentType); await flushSuccess(request, promise); }); it('should support binary data as array buffer', async () => { const input = new Int8Array([1, 2]); const { request, promise } = startRequest('/foo', { method: 'post', data: input.buffer, }); const output = new Int8Array(request.params); expect(output.length).toBe(2); expect(output[0]).toBe(1); expect(output[1]).toBe(2); await flushSuccess(request, promise); }); it('should support binary data as array buffer view', async () => { const input = new Int8Array([1, 2]); const { request, promise } = startRequest('/foo', { method: 'post', data: input, }); const output = new Int8Array(request.params); expect(output.length).toBe(2); expect(output[0]).toBe(1); expect(output[1]).toBe(2); await flushSuccess(request, promise); }); it('should support array buffer response', async () => { const str2ab = (str) => { const buff = new ArrayBuffer(str.length * 2); const view = new Uint16Array(buff); for (let i = 0; i < str.length; i++) { view[i] = str.charCodeAt(i); } return buff; }; const { request, promise } = startRequest('/foo', { responseType: 'arraybuffer', }); request.respondWith({ status: 200, response: str2ab('Hello world'), }); const response = await promise; expect(response.data.byteLength).toBe(22); }); it('should support URLSearchParams', async () => { const params = new URLSearchParams(); params.append('param1', 'value1'); params.append('param2', 'value2'); const { request, promise } = startRequest('/foo', { method: 'post', data: params, }); expect(request.requestHeaders['Content-Type']).toBe( 'application/x-www-form-urlencoded;charset=utf-8' ); expect(request.params).toBe('param1=value1¶m2=value2'); await flushSuccess(request, promise); }); it('should support HTTP protocol', async () => { const { request, promise } = startRequest('/foo', { method: 'get', }); expect(request.method).toBe('GET'); await flushSuccess(request, promise); }); it('should support HTTPS protocol', async () => { const { request, promise } = startRequest('https://www.google.com', { method: 'get', }); expect(request.method).toBe('GET'); await flushSuccess(request, promise); }); it('should return unsupported protocol error message', async () => { await expect(axios.get('ftp:localhost')).rejects.toMatchObject({ message: 'Unsupported protocol ftp:', }); }); it.each([ 'HTTP://example.com/CaseSensitive?token=AbC', 'HtTp://example.com/CaseSensitive?token=AbC', 'HTTPS://example.com/CaseSensitive?token=AbC', 'HtTpS://example.com/CaseSensitive?token=AbC', 'DATA:text/plain,Hello', 'DaTa:text/plain,Hello', ])('should dispatch case-insensitive schemes without changing the URL: %s', async (url) => { const promise = axios.get(url, { adapter: 'xhr' }); const request = requests.at(-1); if (request) { request.respondWith({ status: 200, responseText: 'Hello' }); } const response = await promise; expect(request.url).toBe(url); expect(response.data).toBe('Hello'); }); it.each(['FTP://example.com', 'FtP://example.com', 'DATAX:text/plain,Hello'])( 'should reject unsupported case-variant schemes before sending: %s', async (url) => { await expect(axios.get(url, { adapter: 'xhr' })).rejects.toMatchObject({ code: AxiosError.ERR_BAD_REQUEST, }); expect(requests).toHaveLength(0); } ); it('should clean up cancellation listeners after unsupported protocol rejection', async () => { const source = axios.CancelToken.source(); const controller = new AbortController(); let abortListenerCount = 0; const nativeAdd = controller.signal.addEventListener.bind(controller.signal); const nativeRemove = controller.signal.removeEventListener.bind(controller.signal); controller.signal.addEventListener = (type, fn, options) => { if (type === 'abort') { abortListenerCount++; } return nativeAdd(type, fn, options); }; controller.signal.removeEventListener = (type, fn, options) => { if (type === 'abort') { abortListenerCount--; } return nativeRemove(type, fn, options); }; await expect( axios.get('ftp:localhost', { adapter: 'xhr', cancelToken: source.token, signal: controller.signal, }) ).rejects.toMatchObject({ message: 'Unsupported protocol ftp:', }); expect(source.token._listeners || []).toEqual([]); expect(abortListenerCount).toBe(0); }); }); axios-axios-aa54a5e/tests/browser/responseDuration.browser.test.js000066400000000000000000000040431526660636600256550ustar00rootroot00000000000000import { afterEach, describe, expect, it, vi } from 'vitest'; import axios from '../../index.js'; describe('fetch response duration', () => { afterEach(() => { vi.restoreAllMocks(); }); it.each([200, 500])('includes body buffering for status %s', async (status) => { let now = 1000; vi.spyOn(Date, 'now').mockImplementation(() => now); const result = await axios .get(new URL('/timing', location.href).href, { adapter: 'fetch', env: { fetch: async () => { now += 10; return new Response( new ReadableStream({ pull(controller) { now += 15; controller.enqueue(new TextEncoder().encode('{"ok":true}')); controller.close(); }, }), { status } ); }, }, }) .catch((error) => error); expect(axios.isAxiosError(result)).toBe(status === 500); const response = status === 500 ? result.response : result; expect(response.duration).toBe(25); expect(response.data).toEqual({ ok: true }); }); it('excludes later stream consumption', async () => { let now = 1000; let controller; vi.spyOn(Date, 'now').mockImplementation(() => now); const response = await axios.get(new URL('/timing', location.href).href, { adapter: 'fetch', responseType: 'stream', env: { fetch: async () => { now += 25; return new Response( new ReadableStream({ start(streamController) { controller = streamController; }, }) ); }, }, }); try { expect(response.duration).toBe(25); now += 75; controller.enqueue(new TextEncoder().encode('body')); controller.close(); expect(await new Response(response.data).text()).toBe('body'); expect(response.duration).toBe(25); } finally { if (!response.data.locked) await response.data.cancel(); } }); }); axios-axios-aa54a5e/tests/browser/settle.browser.test.js000066400000000000000000000050351526660636600236130ustar00rootroot00000000000000import { describe, expect, it, vi } from 'vitest'; import settle from '../../lib/core/settle.js'; import AxiosError from '../../lib/core/AxiosError.js'; describe('core::settle (vitest browser)', () => { it('resolves when response status is missing', () => { const resolve = vi.fn(); const reject = vi.fn(); const response = { config: { validateStatus: () => true, }, }; settle(resolve, reject, response); expect(resolve).toHaveBeenCalledOnce(); expect(resolve).toHaveBeenCalledWith(response); expect(reject).not.toHaveBeenCalled(); }); it('resolves when validateStatus is not configured', () => { const resolve = vi.fn(); const reject = vi.fn(); const response = { status: 500, config: {}, }; settle(resolve, reject, response); expect(resolve).toHaveBeenCalledOnce(); expect(resolve).toHaveBeenCalledWith(response); expect(reject).not.toHaveBeenCalled(); }); it('resolves when validateStatus returns true', () => { const resolve = vi.fn(); const reject = vi.fn(); const response = { status: 500, config: { validateStatus: () => true, }, }; settle(resolve, reject, response); expect(resolve).toHaveBeenCalledOnce(); expect(resolve).toHaveBeenCalledWith(response); expect(reject).not.toHaveBeenCalled(); }); it('rejects with an AxiosError when validateStatus returns false', () => { const resolve = vi.fn(); const reject = vi.fn(); const request = { path: '/foo', }; const response = { status: 500, config: { validateStatus: () => false, }, request, }; settle(resolve, reject, response); expect(resolve).not.toHaveBeenCalled(); expect(reject).toHaveBeenCalledOnce(); const reason = reject.mock.calls[0][0]; expect(reason).toBeInstanceOf(AxiosError); expect(reason.message).toBe('Request failed with status code 500'); expect(reason.code).toBe(AxiosError.ERR_BAD_RESPONSE); expect(reason.config).toBe(response.config); expect(reason.request).toBe(request); expect(reason.response).toBe(response); }); it('passes response status to validateStatus', () => { const resolve = vi.fn(); const reject = vi.fn(); const validateStatus = vi.fn(); const response = { status: 500, config: { validateStatus, }, }; settle(resolve, reject, response); expect(validateStatus).toHaveBeenCalledOnce(); expect(validateStatus).toHaveBeenCalledWith(500); }); }); axios-axios-aa54a5e/tests/browser/toFormData.browser.test.js000066400000000000000000000073471526660636600243630ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import toFormData from '../../lib/helpers/toFormData.js'; describe('helpers::toFormData (vitest browser)', () => { it('converts nested data object to FormData with dots option enabled', () => { const data = { val: 123, nested: { arr: ['hello', 'world'], }, }; const form = toFormData(data, null, { dots: true }); expect(form).toBeInstanceOf(FormData); expect(Array.from(form.keys())).toHaveLength(3); expect(form.get('val')).toBe('123'); expect(form.get('nested.arr.0')).toBe('hello'); }); it('respects metaTokens option', () => { const data = { 'obj{}': { x: 1, y: 2 }, }; const serialized = JSON.stringify(data['obj{}']); const form = toFormData(data, null, { metaTokens: false }); expect(Array.from(form.keys())).toHaveLength(1); expect(form.getAll('obj')).toEqual([serialized]); }); describe('flat arrays serialization', () => { it('includes full indexes when indexes option is true', () => { const data = { arr: [1, 2, 3], arr2: [1, [2], 3], }; const form = toFormData(data, null, { indexes: true }); expect(Array.from(form.keys())).toHaveLength(6); expect(form.get('arr[0]')).toBe('1'); expect(form.get('arr[1]')).toBe('2'); expect(form.get('arr[2]')).toBe('3'); expect(form.get('arr2[0]')).toBe('1'); expect(form.get('arr2[1][0]')).toBe('2'); expect(form.get('arr2[2]')).toBe('3'); }); it('includes brackets only when indexes option is false', () => { const data = { arr: [1, 2, 3], arr2: [1, [2], 3], }; const form = toFormData(data, null, { indexes: false }); expect(Array.from(form.keys())).toHaveLength(6); expect(form.getAll('arr[]')).toEqual(['1', '2', '3']); expect(form.get('arr2[0]')).toBe('1'); expect(form.get('arr2[1][0]')).toBe('2'); expect(form.get('arr2[2]')).toBe('3'); }); it('omits brackets when indexes option is null', () => { const data = { arr: [1, 2, 3], arr2: [1, [2], 3], }; const form = toFormData(data, null, { indexes: null }); expect(Array.from(form.keys())).toHaveLength(6); expect(form.getAll('arr')).toEqual(['1', '2', '3']); expect(form.get('arr2[0]')).toBe('1'); expect(form.get('arr2[1][0]')).toBe('2'); expect(form.get('arr2[2]')).toBe('3'); }); }); it('converts nested data object to FormData', () => { const data = { val: 123, nested: { arr: ['hello', 'world'], }, }; const form = toFormData(data); expect(form).toBeInstanceOf(FormData); expect(Array.from(form.keys())).toHaveLength(3); expect(form.get('val')).toBe('123'); expect(form.get('nested[arr][0]')).toBe('hello'); }); it('appends value whose key ends with [] as separate values with the same key', () => { const data = { 'arr[]': [1, 2, 3], }; const form = toFormData(data); expect(Array.from(form.keys())).toHaveLength(3); expect(form.getAll('arr[]')).toEqual(['1', '2', '3']); }); it('appends value whose key ends with {} as a JSON string', () => { const data = { 'obj{}': { x: 1, y: 2 }, }; const serialized = JSON.stringify(data['obj{}']); const form = toFormData(data); expect(Array.from(form.keys())).toHaveLength(1); expect(form.getAll('obj{}')).toEqual([serialized]); }); it('converts typed array values to Blob for browser FormData', async () => { const form = toFormData({ file: new Uint8Array([1, 2, 3]) }); const value = form.get('file'); expect(value).toBeInstanceOf(Blob); expect([...new Uint8Array(await value.arrayBuffer())]).toEqual([1, 2, 3]); }); }); axios-axios-aa54a5e/tests/browser/transform.browser.test.js000066400000000000000000000150271526660636600243300ustar00rootroot00000000000000import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import axios from '../../index.js'; import AxiosError from '../../lib/core/AxiosError.js'; class MockXMLHttpRequest { constructor() { this.requestHeaders = {}; this.responseHeaders = ''; this.readyState = 0; this.status = 0; this.statusText = ''; this.responseText = ''; this.response = null; this.timeout = 0; this.onreadystatechange = null; this.onloadend = null; this.onabort = null; this.onerror = null; this.ontimeout = null; this.upload = { addEventListener() {}, }; } open(method, url, async = true) { this.method = method; this.url = url; this.async = async; } setRequestHeader(key, value) { this.requestHeaders[key] = value; } addEventListener() {} getAllResponseHeaders() { return this.responseHeaders; } send(data) { this.params = data; requests.push(this); } respondWith({ status = 200, statusText = 'OK', responseText = '', responseHeaders = '' } = {}) { this.status = status; this.statusText = statusText; this.responseText = responseText; this.response = responseText; this.responseHeaders = responseHeaders; this.readyState = 4; queueMicrotask(() => { if (this.onloadend) { this.onloadend(); } else if (this.onreadystatechange) { this.onreadystatechange(); } }); } abort() {} } let requests = []; let OriginalXMLHttpRequest; const getLastRequest = () => { const request = requests.at(-1); expect(request).toBeDefined(); return request; }; describe('transform (vitest browser)', () => { beforeEach(() => { requests = []; OriginalXMLHttpRequest = window.XMLHttpRequest; window.XMLHttpRequest = MockXMLHttpRequest; }); afterEach(() => { window.XMLHttpRequest = OriginalXMLHttpRequest; }); it('should transform JSON to string', async () => { const responsePromise = axios.post('/foo', { foo: 'bar' }); const request = getLastRequest(); expect(request.params).toBe('{"foo":"bar"}'); request.respondWith(); await responsePromise; }); it('should transform string to JSON', async () => { const responsePromise = axios('/foo'); const request = getLastRequest(); request.respondWith({ status: 200, responseText: '{"foo": "bar"}', }); const response = await responsePromise; expect(typeof response.data).toBe('object'); expect(response.data.foo).toBe('bar'); }); it('should throw a SyntaxError if JSON parsing failed and responseType is "json" if silentJSONParsing is false', async () => { const responsePromise = axios({ url: '/foo', responseType: 'json', transitional: { silentJSONParsing: false }, }); const request = getLastRequest(); request.respondWith({ status: 200, responseText: '{foo": "bar"}', }); const thrown = await responsePromise.catch((error) => error); expect(thrown).toBeTruthy(); expect(thrown.name).toContain('SyntaxError'); expect(thrown.code).toBe(AxiosError.ERR_BAD_RESPONSE); }); it('should send data as JSON if request content-type is application/json', async () => { const responsePromise = axios.post('/foo', 123, { headers: { 'Content-Type': 'application/json' }, }); const request = getLastRequest(); request.respondWith({ status: 200, responseText: '', }); const response = await responsePromise; expect(response).toBeTruthy(); expect(request.requestHeaders['Content-Type']).toBe('application/json'); expect(JSON.parse(request.params)).toBe(123); }); it('should not assume JSON if responseType is not `json`', async () => { const responsePromise = axios.get('/foo', { responseType: 'text', transitional: { forcedJSONParsing: false, }, }); const request = getLastRequest(); const rawData = '{"x":1}'; request.respondWith({ status: 200, responseText: rawData, }); const response = await responsePromise; expect(response).toBeTruthy(); expect(response.data).toBe(rawData); }); it('should override default transform', async () => { const responsePromise = axios.post( '/foo', { foo: 'bar' }, { transformRequest(data) { return data; }, } ); const request = getLastRequest(); expect(typeof request.params).toBe('object'); request.respondWith(); await responsePromise; }); it('should allow an Array of transformers', async () => { const responsePromise = axios.post( '/foo', { foo: 'bar' }, { transformRequest: axios.defaults.transformRequest.concat(function (data) { return data.replace('bar', 'baz'); }), } ); const request = getLastRequest(); expect(request.params).toBe('{"foo":"baz"}'); request.respondWith(); await responsePromise; }); it('should allowing mutating headers', async () => { const token = Math.floor(Math.random() * Math.pow(2, 64)).toString(36); const responsePromise = axios('/foo', { transformRequest(data, headers) { headers['X-Authorization'] = token; return data; }, }); const request = getLastRequest(); expect(request.requestHeaders['X-Authorization']).toBe(token); request.respondWith(); await responsePromise; }); it("should normalize 'content-type' header when using a custom transformRequest", async () => { const responsePromise = axios.post( '/foo', { foo: 'bar' }, { headers: { 'content-type': 'application/x-www-form-urlencoded' }, transformRequest: [ function () { return 'aa=44'; }, ], } ); const request = getLastRequest(); expect(request.requestHeaders['Content-Type']).toBe('application/x-www-form-urlencoded'); request.respondWith(); await responsePromise; }); it('should return response.data as parsed JSON object when responseType is json', async () => { const instance = axios.create({ baseURL: '/api', responseType: 'json', }); const responsePromise = instance.get('my/endpoint', { responseType: 'json' }); const request = getLastRequest(); request.respondWith({ status: 200, responseText: '{"key1": "value1"}', responseHeaders: 'content-type: application/json', }); const response = await responsePromise; expect(response).toBeTruthy(); expect(response.data).toEqual({ key1: 'value1' }); }); }); axios-axios-aa54a5e/tests/browser/xsrf.browser.test.js000066400000000000000000000235271526660636600233030ustar00rootroot00000000000000import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import axios from '../../index.js'; import cookies from '../../lib/helpers/cookies.js'; class MockXMLHttpRequest { constructor() { this.requestHeaders = {}; this.readyState = 0; this.status = 200; this.statusText = 'OK'; this.responseText = ''; this.timeout = 0; this.onreadystatechange = null; this.onloadend = null; this.onabort = null; this.onerror = null; this.ontimeout = null; this.upload = { addEventListener() {}, }; } open(method, url, async = true) { this.method = method; this.url = url; this.async = async; } setRequestHeader(key, value) { this.requestHeaders[key] = value; } addEventListener() {} getAllResponseHeaders() { return ''; } send() { requests.push(this); this.readyState = 4; queueMicrotask(() => { if (this.onloadend) { this.onloadend(); } else if (this.onreadystatechange) { this.onreadystatechange(); } }); } abort() {} } let requests = []; let OriginalXMLHttpRequest; const setXsrfCookie = (value) => { document.cookie = `${axios.defaults.xsrfCookieName}=${value}; path=/`; }; const clearXsrfCookie = () => { document.cookie = `${axios.defaults.xsrfCookieName}=; expires=${new Date( Date.now() - 86400000 ).toUTCString()}; path=/`; }; const sendRequest = async (url, config) => { const responsePromise = axios(url, config); await vi.waitFor(() => expect(requests.at(-1)).toBeDefined()); const request = requests.at(-1); await responsePromise; return request; }; describe('xsrf (vitest browser)', () => { beforeEach(() => { requests = []; OriginalXMLHttpRequest = window.XMLHttpRequest; window.XMLHttpRequest = MockXMLHttpRequest; vi.stubGlobal('cookieStore', undefined); }); afterEach(() => { clearXsrfCookie(); window.XMLHttpRequest = OriginalXMLHttpRequest; vi.restoreAllMocks(); vi.unstubAllGlobals(); }); it('should not set xsrf header if cookie is null', async () => { const request = await sendRequest('/foo'); expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBeUndefined(); }); it('should set xsrf header if cookie is set', async () => { setXsrfCookie('12345'); const request = await sendRequest('/foo'); expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBe('12345'); }); it('should decode the xsrf value from cookieStore when available', async () => { const get = vi.fn().mockResolvedValue({ name: axios.defaults.xsrfCookieName, value: 'raw%2Fvalue', }); const readSpy = vi.spyOn(cookies, 'read'); vi.stubGlobal('cookieStore', { get }); const request = await sendRequest('/foo'); expect(get).toHaveBeenCalledWith(axios.defaults.xsrfCookieName); expect(readSpy).not.toHaveBeenCalled(); expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBe('raw/value'); }); it('should fall back to document.cookie if cookieStore.get rejects', async () => { setXsrfCookie('fallback'); const get = vi.fn().mockRejectedValue(new Error('cookieStore unavailable')); const readSpy = vi.spyOn(cookies, 'read'); vi.stubGlobal('cookieStore', { get }); const request = await sendRequest('/foo'); expect(get).toHaveBeenCalledWith(axios.defaults.xsrfCookieName); expect(readSpy).toHaveBeenCalledWith(axios.defaults.xsrfCookieName); expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBe('fallback'); }); it('should not read cookieStore for a cross-origin request by default', async () => { const get = vi.fn().mockResolvedValue({ value: 'secret' }); vi.stubGlobal('cookieStore', { get }); const request = await sendRequest('http://example.com/'); expect(get).not.toHaveBeenCalled(); expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBeUndefined(); }); it('should clean up cancellation while cookieStore.get is pending', async () => { const source = axios.CancelToken.source(); const unsubscribeSpy = vi.spyOn(source.token, 'unsubscribe'); vi.stubGlobal('cookieStore', { get: vi.fn(() => new Promise(() => {})), }); const responsePromise = axios('/foo', { cancelToken: source.token }); await vi.waitFor(() => expect(source.token._listeners).toHaveLength(1)); source.cancel('canceled'); await expect(responsePromise).rejects.toMatchObject({ code: 'ERR_CANCELED' }); expect(unsubscribeSpy).toHaveBeenCalledOnce(); expect(requests).toHaveLength(0); }); it('should clean up an abort signal while cookieStore.get is pending', async () => { const controller = new AbortController(); const removeEventListenerSpy = vi.spyOn(controller.signal, 'removeEventListener'); vi.stubGlobal('cookieStore', { get: vi.fn(() => new Promise(() => {})), }); const responsePromise = axios('/foo', { signal: controller.signal }); controller.abort(); await expect(responsePromise).rejects.toMatchObject({ code: 'ERR_CANCELED' }); expect(removeEventListenerSpy).toHaveBeenCalledWith('abort', expect.any(Function)); expect(requests).toHaveLength(0); }); it('should not read cookieStore or dispatch XHR for an already aborted signal', async () => { const controller = new AbortController(); const get = vi.fn(() => new Promise(() => {})); controller.abort(); vi.stubGlobal('cookieStore', { get }); await expect(axios('/foo', { signal: controller.signal })).rejects.toMatchObject({ code: 'ERR_CANCELED', }); expect(get).not.toHaveBeenCalled(); expect(requests).toHaveLength(0); }); it('should not set xsrf header if xsrfCookieName is null', async () => { setXsrfCookie('12345'); const request = await sendRequest('/foo', { xsrfCookieName: null, }); expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBeUndefined(); }); it('should not read cookies at all if xsrfCookieName is null', async () => { const readSpy = vi.spyOn(cookies, 'read'); await sendRequest('/foo', { xsrfCookieName: null, }); expect(readSpy).not.toHaveBeenCalled(); }); it('should not set xsrf header for cross origin', async () => { setXsrfCookie('12345'); const request = await sendRequest('http://example.com/'); expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBeUndefined(); }); it('should not set xsrf header for a different port on the same hostname', async () => { setXsrfCookie('12345'); const url = new URL(window.location.href); url.port = url.port === '65535' ? '65534' : '65535'; const request = await sendRequest(url.href); expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBeUndefined(); }); it('should not set xsrf header for cross origin when using withCredentials', async () => { setXsrfCookie('12345'); const request = await sendRequest('http://example.com/', { withCredentials: true, }); expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBeUndefined(); }); describe('withXSRFToken option', () => { it('should set xsrf header for cross origin when withXSRFToken = true', async () => { const token = '12345'; setXsrfCookie(token); const request = await sendRequest('http://example.com/', { withXSRFToken: true, }); expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBe(token); }); it('should not set xsrf header for the same origin when withXSRFToken = false', async () => { const token = '12345'; setXsrfCookie(token); const request = await sendRequest('/foo', { withXSRFToken: false, }); expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBeUndefined(); }); it('should support function resolver', async () => { const token = '12345'; setXsrfCookie(token); const request = await sendRequest('/foo', { withXSRFToken: (config) => config.userFlag === 'yes', userFlag: 'yes', }); expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBe(token); }); }); // Non-boolean truthy withXSRFToken must not short-circuit // the same-origin check and leak the XSRF token cross-origin. describe('non-boolean withXSRFToken', () => { afterEach(() => { delete Object.prototype.withXSRFToken; }); const leakCases = [ ['number 1', 1], ['string "false"', 'false'], ['empty object', {}], ['empty array', []], ]; leakCases.forEach(([label, value]) => { it(`should not send xsrf header cross-origin when withXSRFToken = ${label}`, async () => { setXsrfCookie('12345'); const request = await sendRequest('http://example.com/', { withXSRFToken: value, }); expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBeUndefined(); }); }); it('should not send xsrf header cross-origin when Object.prototype.withXSRFToken is polluted', async () => { Object.prototype.withXSRFToken = 1; setXsrfCookie('12345'); const request = await sendRequest('http://example.com/'); expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBeUndefined(); }); it('should still send xsrf header cross-origin when withXSRFToken === true (strict)', async () => { const token = '12345'; setXsrfCookie(token); const request = await sendRequest('http://example.com/', { withXSRFToken: true, }); expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBe(token); }); it('should still send xsrf header same-origin when withXSRFToken is undefined', async () => { const token = '12345'; setXsrfCookie(token); const request = await sendRequest('/foo'); expect(request.requestHeaders[axios.defaults.xsrfHeaderName]).toBe(token); }); }); }); axios-axios-aa54a5e/tests/browser/xsrfCookieStore.browser.test.js000066400000000000000000000315721526660636600254510ustar00rootroot00000000000000import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import axios from '../../index.js'; import cookies from '../../lib/helpers/cookies.js'; import { nativeCookieStore } from '../setup/nativeCookieStore.js'; const NativeXMLHttpRequest = window.XMLHttpRequest; let requests; let completeRequests; let controllers; class MockXMLHttpRequest { constructor() { this.requestHeaders = {}; this.status = 200; this.statusText = 'OK'; this.responseText = ''; this.onloadend = null; this.timeout = 0; } open() {} setRequestHeader(name, value) { this.requestHeaders[name] = this.requestHeaders[name] ? this.requestHeaders[name] + ', ' + value : value; } getAllResponseHeaders() { return ''; } send() { requests.push(this); if (completeRequests) queueMicrotask(() => this.onloadend()); } abort() { this.onabort && this.onabort(); } } const startRequest = (config = {}) => { const controller = new AbortController(); controllers.push(controller); const result = {}; result.promise = axios .get('/cookie-store', { adapter: 'xhr', signal: controller.signal, ...config, }) .then( (response) => { result.response = response; }, (error) => { result.error = error; } ); return { result, controller }; }; describe('XHR Cookie Store compatibility', () => { beforeEach(() => { requests = []; controllers = []; completeRequests = true; vi.stubGlobal('XMLHttpRequest', MockXMLHttpRequest); }); afterEach(() => { controllers.forEach((controller) => controller.abort()); document.cookie = 'XSRF-TOKEN=; max-age=0; path=/'; vi.useRealTimers(); vi.restoreAllMocks(); vi.unstubAllGlobals(); }); it.each([ ['abc%2Fdef%3D', 'abc/def='], ['literal%252F', 'literal%2F'], ['a+b', 'a+b'], ['malformed%E0%A4%A', 'malformed%E0%A4%A'], ['', undefined], ])('preserves the existing header for cookie %s on every read path', async (stored, expected) => { document.cookie = `XSRF-TOKEN=${stored}; path=/`; for (const store of [ undefined, { get: vi.fn().mockResolvedValue({ value: stored }) }, { get: vi.fn().mockRejectedValue(new Error('unavailable')) }, ]) { vi.stubGlobal('cookieStore', store); const response = await axios.get('/cookie-store', { adapter: 'xhr' }); expect(response.request.requestHeaders['X-XSRF-TOKEN']).toBe(expected); } }); it.runIf(nativeCookieStore).each(['abc%2Fdef%3D', 'literal%252F', 'a+b', 'malformed%'])( 'round-trips a real browser cookie through Cookie Store: %s', async (stored) => { document.cookie = `XSRF-TOKEN=${stored}; path=/`; const expected = cookies.read('XSRF-TOKEN'); const readSpy = vi.spyOn(cookies, 'read'); vi.stubGlobal('cookieStore', nativeCookieStore); expect((await nativeCookieStore.get('XSRF-TOKEN')).value).toBe(stored); const response = await axios.get('/cookie-store', { adapter: 'xhr' }); expect(response.request.requestHeaders['X-XSRF-TOKEN']).toBe(expected); expect(readSpy).not.toHaveBeenCalled(); } ); it.each([{ xsrfCookieName: null }, { xsrfHeaderName: null }, { withXSRFToken: false }])( 'does not start a cookie read when XSRF is disabled: %j', async (config) => { const get = vi.fn().mockResolvedValue({ value: 'secret' }); const readSpy = vi.spyOn(cookies, 'readAsync'); vi.stubGlobal('cookieStore', { get }); await axios.get('/cookie-store', { adapter: 'xhr', ...config }); expect(get).not.toHaveBeenCalled(); expect(readSpy).not.toHaveBeenCalled(); } ); it('does not fall back to a stale cookie when Cookie Store returns no match', async () => { document.cookie = 'XSRF-TOKEN=stale; path=/'; vi.stubGlobal('cookieStore', { get: vi.fn().mockResolvedValue(null) }); const readSpy = vi.spyOn(cookies, 'read'); const response = await axios.get('/cookie-store', { adapter: 'xhr' }); expect(response.request.requestHeaders['X-XSRF-TOKEN']).toBeUndefined(); expect(readSpy).not.toHaveBeenCalled(); }); it.runIf(nativeCookieStore)( 'sends a decoded token through native Cookie Store and native XHR', async () => { document.cookie = 'XSRF-TOKEN=abc%2Fdef%3D; path=/'; vi.stubGlobal('cookieStore', nativeCookieStore); vi.stubGlobal('XMLHttpRequest', NativeXMLHttpRequest); const setHeader = vi.spyOn(NativeXMLHttpRequest.prototype, 'setRequestHeader'); const response = await axios.get(new URL('../../package.json', import.meta.url).href, { adapter: 'xhr', timeout: 5000, }); expect(response.status).toBe(200); expect(setHeader).toHaveBeenCalledWith('X-XSRF-TOKEN', 'abc/def='); } ); it('keeps Fetch on its existing synchronous cookie reader', async () => { document.cookie = 'XSRF-TOKEN=abc%2Fdef%3D; path=/'; const get = vi.fn(() => new Promise(() => {})); vi.stubGlobal('cookieStore', { get }); const fetch = vi.fn(async (request) => new Response(request.headers.get('X-XSRF-TOKEN'))); const response = await axios.get(window.location.href, { adapter: 'fetch', env: { fetch } }); expect(response.data).toBe('abc/def='); expect(get).not.toHaveBeenCalled(); }); it.each([ [undefined, false], [false, false], ['false', false], [1, false], [{}, false], [true, true], [() => true, true], [() => false, false], ])('preserves strict cross-origin eligibility with Cookie Store: %s', async (flag, eligible) => { const get = vi.fn().mockResolvedValue({ value: 'secret' }); vi.stubGlobal('cookieStore', { get }); const response = await axios.get('https://other.example.invalid/', { adapter: 'xhr', withXSRFToken: flag, }); expect(get).toHaveBeenCalledTimes(eligible ? 1 : 0); expect(response.request.requestHeaders['X-XSRF-TOKEN']).toBe(eligible ? 'secret' : undefined); }); it('preserves synchronous dispatch when Cookie Store has no get capability', async () => { vi.stubGlobal('cookieStore', {}); const promise = axios.get('/cookie-store', { adapter: 'xhr' }); expect(requests).toHaveLength(1); await promise; }); it.each([ ['manual', 'cookie'], [false, undefined], ])('preserves XSRF header replacement and opt-out: %s', async (initial, expected) => { vi.stubGlobal('cookieStore', { get: vi.fn().mockResolvedValue({ value: 'cookie' }) }); const response = await axios.get('/cookie-store', { adapter: 'xhr', headers: { 'X-XSRF-TOKEN': initial }, }); expect(response.request.requestHeaders['X-XSRF-TOKEN']).toBe(expected); }); it('falls back when accessing Cookie Store itself throws', async () => { document.cookie = 'XSRF-TOKEN=abc%2Fdef; path=/'; vi.stubGlobal('cookieStore', undefined); Object.defineProperty(window, 'cookieStore', { configurable: true, get() { throw new Error('unavailable'); }, }); const response = await axios.get('/cookie-store', { adapter: 'xhr' }); expect(response.request.requestHeaders['X-XSRF-TOKEN']).toBe('abc/def'); }); describe('timeout and cancellation during preparation', () => { let finishRead; beforeEach(() => { vi.useFakeTimers(); vi.stubGlobal('cookieStore', { get: vi.fn( () => new Promise((resolve) => { finishRead = resolve; }) ), }); }); it.each([ [{}, 'ECONNABORTED', 'timeout of 50ms exceeded'], [{ transitional: { clarifyTimeoutError: true } }, 'ETIMEDOUT', 'timeout of 50ms exceeded'], [{ timeoutErrorMessage: 'custom deadline' }, 'ECONNABORTED', 'custom deadline'], ])( 'times out a stalled cookie read with existing diagnostics: %j', async (config, code, message) => { const source = axios.CancelToken.source(); const unsubscribe = vi.spyOn(source.token, 'unsubscribe'); const { result, controller } = startRequest({ timeout: 50, cancelToken: source.token, ...config, }); const removeListener = vi.spyOn(controller.signal, 'removeEventListener'); await vi.advanceTimersByTimeAsync(50); expect(result.error).toMatchObject({ code, message, config: { timeout: 50 } }); expect(axios.isAxiosError(result.error)).toBe(true); expect(unsubscribe).toHaveBeenCalled(); expect(removeListener).toHaveBeenCalledWith('abort', expect.any(Function)); expect(vi.getTimerCount()).toBe(0); finishRead({ value: 'late' }); await vi.advanceTimersByTimeAsync(0); expect(requests).toHaveLength(0); } ); it('uses one timeout budget for both the cookie read and the XHR', async () => { completeRequests = false; const abort = vi.spyOn(MockXMLHttpRequest.prototype, 'abort'); const { result } = startRequest({ timeout: 50 }); await vi.advanceTimersByTimeAsync(40); finishRead({ value: 'token' }); await vi.advanceTimersByTimeAsync(0); expect(requests).toHaveLength(1); await vi.advanceTimersByTimeAsync(9); expect(result.error).toBeUndefined(); await vi.advanceTimersByTimeAsync(1); expect(result.error).toMatchObject({ code: 'ECONNABORTED', message: 'timeout of 50ms exceeded', }); expect(abort).toHaveBeenCalledOnce(); expect(vi.getTimerCount()).toBe(0); }); it('does not send when the cookie promise settles before an overdue timer runs', async () => { const { result } = startRequest({ timeout: 50 }); vi.spyOn(performance, 'now').mockReturnValue(51); finishRead({ value: 'late' }); await result.promise; expect(result.error.code).toBe('ECONNABORTED'); expect(requests).toHaveLength(0); expect(vi.getTimerCount()).toBe(0); }); it('enforces the deadline without a cancellation token or signal', async () => { const { result } = startRequest({ timeout: 50, signal: undefined }); await vi.advanceTimersByTimeAsync(50); expect(result.error.code).toBe('ECONNABORTED'); expect(requests).toHaveLength(0); expect(vi.getTimerCount()).toBe(0); }); it('clears the deadline and both cancellation subscriptions after success', async () => { const source = axios.CancelToken.source(); const unsubscribe = vi.spyOn(source.token, 'unsubscribe'); const { result, controller } = startRequest({ timeout: 50, cancelToken: source.token }); const removeListener = vi.spyOn(controller.signal, 'removeEventListener'); finishRead({ value: 'token' }); await result.promise; expect(result.response.status).toBe(200); expect(unsubscribe).toHaveBeenCalled(); expect(removeListener).toHaveBeenCalledWith('abort', expect.any(Function)); expect(vi.getTimerCount()).toBe(0); }); it.each(['signal', 'cancelToken'])( 'cancels via %s and ignores late cookie results', async (kind) => { const source = axios.CancelToken.source(); const { result, controller } = startRequest({ timeout: 50, cancelToken: source.token }); kind === 'signal' ? controller.abort() : source.cancel('canceled'); await result.promise; expect(result.error.code).toBe('ERR_CANCELED'); expect(vi.getTimerCount()).toBe(0); finishRead({ value: 'late' }); await vi.advanceTimersByTimeAsync(0); expect(requests).toHaveLength(0); } ); it('keeps timeout zero unlimited while retaining cancellation', async () => { const { result, controller } = startRequest({ timeout: 0 }); await vi.advanceTimersByTimeAsync(10000); expect(result.error).toBeUndefined(); expect(vi.getTimerCount()).toBe(0); controller.abort(); await result.promise; expect(result.error.code).toBe('ERR_CANCELED'); }); it('does not overflow long XHR timeouts into an immediate failure', async () => { const { result, controller } = startRequest({ timeout: 0x80000000 }); await vi.advanceTimersByTimeAsync(1); expect(result.error).toBeUndefined(); controller.abort(); await result.promise; expect(result.error.code).toBe('ERR_CANCELED'); expect(vi.getTimerCount()).toBe(0); }); it('cleans up when both cookie APIs reject access', async () => { vi.stubGlobal('cookieStore', { get: vi.fn().mockRejectedValue(new Error('denied')) }); vi.spyOn(cookies, 'read').mockImplementation(() => { throw new Error('cookie access denied'); }); const { result, controller } = startRequest({ timeout: 50 }); const removeListener = vi.spyOn(controller.signal, 'removeEventListener'); await result.promise; expect(axios.isAxiosError(result.error)).toBe(true); expect(result.error.message).toBe('cookie access denied'); expect(removeListener).toHaveBeenCalled(); expect(vi.getTimerCount()).toBe(0); expect(requests).toHaveLength(0); }); }); }); axios-axios-aa54a5e/tests/module/000077500000000000000000000000001526660636600171145ustar00rootroot00000000000000axios-axios-aa54a5e/tests/module/cjs/000077500000000000000000000000001526660636600176735ustar00rootroot00000000000000axios-axios-aa54a5e/tests/module/cjs/package-lock.json000066400000000000000000001073311526660636600231140ustar00rootroot00000000000000{ "name": "@axios/cjs-module-tests", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@axios/cjs-module-tests", "version": "1.0.0", "license": "MIT", "devDependencies": { "@types/node": "12.20.55", "mocha": "9.2.2", "typescript": "4.9.5" } }, "node_modules/@types/node": { "version": "12.20.55", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", "dev": true, "license": "MIT" }, "node_modules/@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true, "license": "ISC" }, "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, "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/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/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/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "license": "MIT", "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/brace-expansion": { "version": "1.1.21", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, "node_modules/browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true, "license": "ISC" }, "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/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/chalk/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/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "funding": [ { "type": "individual", "url": "https://paulmillr.com/funding/" } ], "license": "MIT", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "engines": { "node": ">= 8.10.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "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/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/debug": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", "dev": true, "license": "MIT", "dependencies": { "ms": "2.1.2" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "node_modules/debug/node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true, "license": "MIT" }, "node_modules/decamelize": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, "license": "MIT", "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.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/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/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, "engines": { "node": ">=8" } }, "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": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, "license": "BSD-3-Clause", "bin": { "flat": "cli.js" } }, "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/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/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", "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.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, "engines": { "node": "*" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, "engines": { "node": ">= 6" } }, "node_modules/glob/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/growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true, "license": "MIT", "engines": { "node": ">=4.x" } }, "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/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, "license": "MIT", "bin": { "he": "bin/he" } }, "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-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" }, "engines": { "node": ">=8" } }, "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-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-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" } }, "node_modules/is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, "license": "MIT", "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "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/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "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/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/minimatch": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, "engines": { "node": ">=10" } }, "node_modules/mocha": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", "dev": true, "license": "MIT", "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", "chokidar": "3.5.3", "debug": "4.3.3", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", "glob": "7.2.0", "growl": "1.10.5", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", "minimatch": "4.2.1", "ms": "2.1.3", "nanoid": "3.3.1", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", "workerpool": "6.2.0", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" }, "bin": { "_mocha": "bin/_mocha", "mocha": "bin/mocha" }, "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/mochajs" } }, "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/nanoid": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true, "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, "engines": { "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, "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/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/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/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/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/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" } }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, "engines": { "node": ">=8.10.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/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/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } }, "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/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-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": "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/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, "engines": { "node": ">=8.0" } }, "node_modules/typescript": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { "node": ">=4.2.0" } }, "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/workerpool": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", "dev": true, "license": "Apache-2.0" }, "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/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/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/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "license": "MIT", "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" }, "engines": { "node": ">=10" } }, "node_modules/yargs-parser": { "version": "20.2.4", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", "dev": true, "license": "ISC", "engines": { "node": ">=10" } }, "node_modules/yargs-unparser": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, "license": "MIT", "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", "flat": "^5.0.2", "is-plain-obj": "^2.1.0" }, "engines": { "node": ">=10" } }, "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" } } } } axios-axios-aa54a5e/tests/module/cjs/package.json000066400000000000000000000006321526660636600221620ustar00rootroot00000000000000{ "name": "@axios/cjs-module-tests", "version": "1.0.0", "description": "CJS module compatibility tests for axios", "private": true, "scripts": { "test:module:cjs": "mocha --timeout 10000 \"tests/**/*.module.test.cjs\"" }, "keywords": [], "author": "axios team", "license": "MIT", "devDependencies": { "@types/node": "12.20.55", "mocha": "9.2.2", "typescript": "4.9.5" } }axios-axios-aa54a5e/tests/module/cjs/tests/000077500000000000000000000000001526660636600210355ustar00rootroot00000000000000axios-axios-aa54a5e/tests/module/cjs/tests/fixture-cleanup.module.test.cjs000066400000000000000000000026101526660636600271120ustar00rootroot00000000000000const assert = require('assert'); const fs = require('fs'); const os = require('os'); const path = require('path'); const { describe, it } = require('mocha'); const { cleanupTempFixture } = require('./helpers/fixture.cjs'); describe('module fixture cleanup helper', () => { it('removes fixture directories without shelling out to rm', () => { const fixturePath = fs.mkdtempSync(path.join(os.tmpdir(), 'axios-module-fixture-')); const nestedPath = path.join(fixturePath, 'nested'); const originalPath = process.env.PATH; fs.mkdirSync(nestedPath); fs.writeFileSync(path.join(nestedPath, 'index.ts'), 'export {};\n'); process.env.PATH = ''; try { cleanupTempFixture(fixturePath); } finally { process.env.PATH = originalPath; } assert.strictEqual(fs.existsSync(fixturePath), false); }); it('removes fixture directories when fs.rmSync is unavailable', () => { const fixturePath = fs.mkdtempSync(path.join(os.tmpdir(), 'axios-module-fixture-legacy-')); const nestedPath = path.join(fixturePath, 'nested'); const originalRmSync = fs.rmSync; fs.mkdirSync(nestedPath); fs.writeFileSync(path.join(nestedPath, 'index.ts'), 'export {};\n'); fs.rmSync = undefined; try { cleanupTempFixture(fixturePath); } finally { fs.rmSync = originalRmSync; } assert.strictEqual(fs.existsSync(fixturePath), false); }); }); axios-axios-aa54a5e/tests/module/cjs/tests/helpers/000077500000000000000000000000001526660636600224775ustar00rootroot00000000000000axios-axios-aa54a5e/tests/module/cjs/tests/helpers/cjs-added-types.ts000066400000000000000000000153021526660636600260300ustar00rootroot00000000000000import axios = require('axios'); const utf8AuthConfig: axios.AxiosRequestConfig = { auth: { username: 'Ï€', password: 'x' }, transitional: { encodeBasicAuthUsername: true }, }; const legacyAuthConfig: axios.AxiosRequestConfig = { transitional: { encodeBasicAuthUsername: false }, }; const invalidAuthConfig: axios.AxiosRequestConfig = { transitional: { // @ts-expect-error -- the encoding opt-in only accepts booleans encodeBasicAuthUsername: 'true', }, }; const headers = new axios.AxiosHeaders(); const iterableHeaders: Iterable<[string, axios.AxiosHeaderValue]> = [['x-test', 'ok']]; headers.set(iterableHeaders); const serializedHeaders: string = headers.toString(); const parsedParameters: axios.AxiosHeaderParameters = axios.AxiosHeaders.parseParameters( 'multipart/form-data; boundary="test"' ); headers.set('content-type', 'multipart/form-data; boundary="test"'); const parsedHeaderParameters: axios.AxiosHeaderParameters = headers.get( 'content-type', axios.AxiosHeaders.parseParameters ); const source = axios.CancelToken.source(); source.token.subscribe((cancel) => { const message: string | undefined = cancel && cancel.message; console.log(message); }); source.token.unsubscribe(() => {}); const signal: AbortSignal = source.token.toAbortSignal(); const cancel = new axios.CanceledError<{ ok: true }>( 'stop', {} as axios.InternalAxiosRequestConfig, {} ); const cancelFlag: boolean | undefined = cancel.__CANCEL__; const cancelCtor: typeof axios.CanceledError = axios.Cancel; const cancelFromAlias = new cancelCtor('from alias'); const status = axios.HttpStatusCode.WebServerIsDown; const unknownErrorStatus = axios.HttpStatusCode.WebServerReturnsAnUnknownError; const contentTooLargeStatus = axios.HttpStatusCode.ContentTooLarge; const unprocessableContentStatus = axios.HttpStatusCode.UnprocessableContent; class CustomBlob { constructor(_parts?: any[]) {} } const serializerOptions: axios.FormSerializerOptions = { maxDepth: 2, Blob: CustomBlob, }; axios.toFormData({ file: new Uint8Array([1]) }, undefined, serializerOptions); interface SearchBody { includeArchived: boolean; } interface SearchParams { query: string; page?: number; } const paramsConfig: axios.AxiosRequestConfig = { data: { includeArchived: false }, params: { query: 'axios', page: 1 }, paramsSerializer: (params) => { const query: string = params.query; const page: number | undefined = params.page; // @ts-expect-error -- serializer params use the request params type const invalidQuery: number = params.query; void invalidQuery; return `${query}:${page ?? 1}`; }, withXSRFToken: (config) => config.params?.query === 'axios', }; const customParamsSerializer: axios.CustomParamsSerializer = (params) => params.query; const rawParamsConfig: axios.RawAxiosRequestConfig = paramsConfig; const internalParamsConfig = { ...paramsConfig, headers: new axios.AxiosHeaders(), } as axios.InternalAxiosRequestConfig; const paramsResponse = {} as axios.AxiosResponse; const paramsError = {} as axios.AxiosError; declare const paramsPromise: axios.AxiosPromise; const responseDuration: number | undefined = paramsResponse.duration; const errorResponseDuration: number | undefined = paramsError.response?.duration; paramsResponse.duration = 25; paramsResponse.duration = undefined; // @ts-expect-error -- response duration is numeric when present paramsResponse.duration = '25'; // Custom adapters can still return a response without timing metadata. const untimedResponse: axios.AxiosResponse = { data: null, status: 200, statusText: 'OK', headers: {}, config: internalParamsConfig, }; const internalQuery: string = internalParamsConfig.params!.query; const responseQuery: string = paramsResponse.config.params!.query; const errorQuery: string = paramsError.config!.params!.query; paramsPromise.then((response) => { const promiseData: SearchBody | undefined = response.config.data; const promiseParams: SearchParams | undefined = response.config.params; // @ts-expect-error -- AxiosPromise preserves the request data type const invalidPromiseData: { includeArchived: string } | undefined = response.config.data; // @ts-expect-error -- AxiosPromise preserves the request params type const invalidPromiseParams: { query: number } | undefined = response.config.params; void promiseData; void promiseParams; void invalidPromiseData; void invalidPromiseParams; }); axios.get('/search', paramsConfig).then((response) => { const duration: number | undefined = response.duration; // @ts-expect-error -- inferred response duration is not a string const invalidDuration: string = response.duration; void duration; void invalidDuration; const aliasData: SearchBody | undefined = response.config.data; const aliasParams: SearchParams | undefined = response.config.params; // @ts-expect-error -- default alias responses preserve the request data type const invalidAliasData: { includeArchived: string } | undefined = response.config.data; // @ts-expect-error -- default alias responses preserve the request params type const invalidAliasParams: { query: number } | undefined = response.config.params; void aliasData; void aliasParams; void invalidAliasData; void invalidAliasParams; }); const legacyParamsConfig: axios.AxiosRequestConfig = { params: 'legacy values remain accepted', paramsSerializer: (params) => { const legacyParams: Record = params; // @ts-expect-error -- the default serializer input remains a params record, not any const legacyParamsNumber: number = params; void legacyParamsNumber; return String(legacyParams.query); }, }; axios.get, any, SearchParams>('/search', { params: { query: 'axios' }, }); const invalidParamsConfig: axios.AxiosRequestConfig = { // @ts-expect-error -- params must match the explicit request params type params: { query: 1 }, }; axios.get, any, SearchParams>('/search', { // @ts-expect-error -- request aliases enforce their trailing params type params: { query: 1 }, }); const mergedParamsConfig = axios.mergeConfig( rawParamsConfig, paramsConfig ); const mergedQuery: string = mergedParamsConfig.params!.query; console.log( serializedHeaders, parsedParameters, parsedHeaderParameters, signal.aborted, cancelFlag, cancelFromAlias.message, status, customParamsSerializer, internalQuery, responseQuery, errorQuery, legacyParamsConfig, mergedQuery, invalidParamsConfig ); axios-axios-aa54a5e/tests/module/cjs/tests/helpers/cjs-fetch-types.ts000066400000000000000000000044111526660636600260570ustar00rootroot00000000000000import axios = require('axios'); // Importing Axios and choosing HTTP must work without the DOM library. axios.create({ adapter: 'http' }); // Existing wrappers may explicitly use the original constructor body type. type LegacyBody = ArrayBuffer | ArrayBufferView | Blob | FormData | URLSearchParams | string | null; const normalizeBody = (body: LegacyBody | ReadableStream | undefined) => ArrayBuffer.isView(body) ? new Uint8Array(body.buffer, body.byteOffset, body.byteLength).slice() : body; class LegacyResponse extends Response { constructor(body?: LegacyBody, init?: ResponseInit) { super(normalizeBody(body), init); } } // Supporting Axios's tracked stream must not require Node-only iterable inputs. class StreamCompatibleResponse extends Response { constructor(body?: LegacyBody | ReadableStream, init?: ResponseInit) { super(normalizeBody(body), init); } } axios.create({ adapter: 'fetch', env: { Response: LegacyResponse } }); axios.create({ env: { Response: StreamCompatibleResponse } }); type ResponseConstructor = NonNullable['Response']>; const ResponseOverride: ResponseConstructor = LegacyResponse; new ResponseOverride(); new ResponseOverride(null); new ResponseOverride('body'); new ResponseOverride(new ArrayBuffer(8)); new ResponseOverride(new Uint8Array(8)); new ResponseOverride(new DataView(new ArrayBuffer(8))); new ResponseOverride(new Blob(['body'])); new ResponseOverride(new FormData()); new ResponseOverride(new URLSearchParams()); // Calls through the configured constructor retain the original body contract, // including views typed through the general ArrayBufferView interface. function constructResponse(Response: ResponseConstructor, body: ArrayBufferView) { return new Response(body); } constructResponse(ResponseOverride, new Uint8Array(8)); // Invalid body values must not silently become any. // @ts-expect-error Response bodies cannot be numbers. new ResponseOverride(123); class StringOnlyResponse extends Response { constructor(body?: string | null, init?: ResponseInit) { super(body, init); } } axios.create({ env: { // @ts-expect-error String-only constructors cannot accept the existing body inputs. Response: StringOnlyResponse, }, }); axios-axios-aa54a5e/tests/module/cjs/tests/helpers/cjs-is-cancel-typing.ts000066400000000000000000000021761526660636600270000ustar00rootroot00000000000000import axios = require('axios'); declare const thrown: unknown; interface RequestData { submitted: true; } interface RequestParams { query: string; } if (axios.isCancel<{ ok: true }, RequestData, RequestParams>(thrown)) { const canceled: InstanceType< typeof axios.CanceledError<{ ok: true }, RequestData, RequestParams> > = thrown; const data: { ok: true } | undefined = thrown.response?.data; const requestData: RequestData | undefined = thrown.config?.data; const params: RequestParams | undefined = thrown.config?.params; // @ts-expect-error -- The generic response data must not be widened to any. const wrongData: { ok: false } | undefined = thrown.response?.data; // @ts-expect-error -- The generic request data must not be widened to any. const wrongRequestData: { submitted: false } | undefined = thrown.config?.data; // @ts-expect-error -- The generic request params must not be widened to any. const wrongParams: { query: number } | undefined = thrown.config?.params; console.log( canceled.message, data, requestData, params, wrongData, wrongRequestData, wrongParams ); } axios-axios-aa54a5e/tests/module/cjs/tests/helpers/cjs-typing.ts000066400000000000000000000337621526660636600251510ustar00rootroot00000000000000// @ts-nocheck import axios = require('axios'); const config: axios.AxiosRequestConfig = { url: '/user', method: 'get', allowAbsoluteUrls: false, baseURL: 'https://api.example.com/', transformRequest: (data: any) => '{"foo":"bar"}', transformResponse: [(data: any) => ({ baz: 'qux' })], headers: { 'X-FOO': 'bar' }, params: { id: 12345 }, paramsSerializer: { indexes: true, encode: (value: any) => value, serialize: (value: Record, options?: axios.ParamsSerializerOptions) => String(value), }, data: { foo: 'bar' }, timeout: 10000, withCredentials: true, auth: { username: 'janedoe', password: 's00pers3cret', }, responseType: 'json', xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN', onUploadProgress: (progressEvent: axios.AxiosProgressEvent) => {}, onDownloadProgress: (progressEvent: axios.AxiosProgressEvent) => {}, maxContentLength: 2000, maxBodyLength: 2000, validateStatus: (status: number) => status >= 200 && status < 300, maxRedirects: 5, proxy: { host: '127.0.0.1', port: 9000, }, cancelToken: new axios.CancelToken((cancel: axios.Canceler) => {}), }; const nullValidateStatusConfig: axios.AxiosRequestConfig = { validateStatus: null, }; const undefinedValidateStatusConfig: axios.AxiosRequestConfig = { validateStatus: undefined, }; const handleResponse = (response: axios.AxiosResponse) => { console.log(response.data); console.log(response.status); console.log(response.statusText); console.log(response.headers); console.log(response.config); }; const handleError = (error: axios.AxiosError) => { if (error.response) { console.log(error.response.data); console.log(error.response.status); console.log(error.response.headers); } else { console.log(error.message); } }; axios(config).then(handleResponse).catch(handleError); axios.get('/user?id=12345').then(handleResponse).catch(handleError); axios .get('/user', { params: { id: 12345 } }) .then(handleResponse) .catch(handleError); axios.head('/user').then(handleResponse).catch(handleError); axios.options('/user').then(handleResponse).catch(handleError); axios.delete('/user').then(handleResponse).catch(handleError); axios.post('/user', { foo: 'bar' }).then(handleResponse).catch(handleError); axios .post('/user', { foo: 'bar' }, { headers: { 'X-FOO': 'bar' } }) .then(handleResponse) .catch(handleError); axios.put('/user', { foo: 'bar' }).then(handleResponse).catch(handleError); axios.patch('/user', { foo: 'bar' }).then(handleResponse).catch(handleError); axios.query('/user', { foo: 'bar' }).then(handleResponse).catch(handleError); // Typed methods interface UserCreationDef { name: string; } interface User { id: number; name: string; } interface ResponseHeaders { 'x-header': string; } // with default axios.AxiosResponse result const handleUserResponse = (response: axios.AxiosResponse) => { console.log(response.data.id); console.log(response.data.name); console.log(response.status); console.log(response.statusText); console.log(response.headers); console.log(response.config); }; axios.get('/user?id=12345').then(handleUserResponse).catch(handleError); axios .get('/user', { params: { id: 12345 } }) .then(handleUserResponse) .catch(handleError); axios.head('/user').then(handleUserResponse).catch(handleError); axios.options('/user').then(handleUserResponse).catch(handleError); axios.delete('/user').then(handleUserResponse).catch(handleError); axios.post('/user', { name: 'foo', id: 1 }).then(handleUserResponse).catch(handleError); axios .post('/user', { name: 'foo', id: 1 }, { headers: { 'X-FOO': 'bar' } }) .then(handleUserResponse) .catch(handleError); axios.put('/user', { name: 'foo', id: 1 }).then(handleUserResponse).catch(handleError); axios.patch('/user', { name: 'foo', id: 1 }).then(handleUserResponse).catch(handleError); // with custom response headers axios.AxiosResponse result const handleUserResponseWithCustomHeaders = ( response: axios.AxiosResponse ) => { console.log(response.data.id); console.log(response.data.name); console.log(response.status); console.log(response.statusText); console.log(response.headers); console.log(response.config); }; axios .get>('/user?id=12345') .then(handleUserResponseWithCustomHeaders) .catch(handleError); axios .get>('/user', { params: { id: 12345 } }) .then(handleUserResponseWithCustomHeaders) .catch(handleError); axios .head>('/user') .then(handleUserResponseWithCustomHeaders) .catch(handleError); axios .options>('/user') .then(handleUserResponseWithCustomHeaders) .catch(handleError); axios .delete>('/user') .then(handleUserResponseWithCustomHeaders) .catch(handleError); axios .post>('/user', { name: 'foo', id: 1 }) .then(handleUserResponseWithCustomHeaders) .catch(handleError); axios .post>( '/user', { name: 'foo', id: 1 }, { headers: { 'X-FOO': 'bar' } } ) .then(handleUserResponseWithCustomHeaders) .catch(handleError); axios .put>('/user', { name: 'foo', id: 1 }) .then(handleUserResponseWithCustomHeaders) .catch(handleError); axios .patch>('/user', { name: 'foo', id: 1 }) .then(handleUserResponseWithCustomHeaders) .catch(handleError); // (Typed methods) with custom response type const handleStringResponse = (response: string) => { console.log(response); }; axios.get('/user?id=12345').then(handleStringResponse).catch(handleError); axios .get('/user', { params: { id: 12345 } }) .then(handleStringResponse) .catch(handleError); axios.head('/user').then(handleStringResponse).catch(handleError); axios.options('/user').then(handleStringResponse).catch(handleError); axios.delete('/user').then(handleStringResponse).catch(handleError); axios .post, string>('/user', { name: 'foo' }) .then(handleStringResponse) .catch(handleError); axios .post, string>('/user', { name: 'foo' }, { headers: { 'X-FOO': 'bar' } }) .then(handleStringResponse) .catch(handleError); axios .put, string>('/user', { name: 'foo' }) .then(handleStringResponse) .catch(handleError); axios .patch, string>('/user', { name: 'foo' }) .then(handleStringResponse) .catch(handleError); axios .request({ method: 'get', url: '/user?id=12345', }) .then(handleStringResponse) .catch(handleError); // Instances const instance1: axios.AxiosInstance = axios.create(); const instance2: axios.AxiosInstance = axios.create(config); instance1(config).then(handleResponse).catch(handleError); instance1.request(config).then(handleResponse).catch(handleError); instance1.get('/user?id=12345').then(handleResponse).catch(handleError); instance1.options('/user').then(handleResponse).catch(handleError); instance1 .get('/user', { params: { id: 12345 } }) .then(handleResponse) .catch(handleError); instance1.post('/user', { foo: 'bar' }).then(handleResponse).catch(handleError); instance1 .post('/user', { foo: 'bar' }, { headers: { 'X-FOO': 'bar' } }) .then(handleResponse) .catch(handleError); // Defaults axios.defaults.headers['X-FOO']; axios.defaults.baseURL = 'https://api.example.com/'; axios.defaults.headers.common['Accept'] = 'application/json'; axios.defaults.headers.post['X-FOO'] = 'bar'; axios.defaults.timeout = 2500; instance1.defaults.baseURL = 'https://api.example.com/'; instance1.defaults.headers.common['Accept'] = 'application/json'; instance1.defaults.headers.post['X-FOO'] = 'bar'; instance1.defaults.timeout = 2500; // axios create defaults axios.create({ headers: { foo: 'bar' } }); axios.create({ headers: { common: { foo: 'bar' } } }); axios.create({ headers: { 'Content-Type': 'application/json', }, formSerializer: { indexes: null, }, paramsSerializer: { indexes: null, }, }); // Interceptors const requestInterceptorId: number = axios.interceptors.request.use( (config: axios.InternalAxiosRequestConfig) => config, (error: any) => Promise.reject(error) ); axios.interceptors.request.eject(requestInterceptorId); axios.interceptors.request.use( (config: axios.InternalAxiosRequestConfig) => Promise.resolve(config), (error: any) => Promise.reject(error) ); axios.interceptors.request.use((config: axios.InternalAxiosRequestConfig) => config); axios.interceptors.request.use((config: axios.InternalAxiosRequestConfig) => Promise.resolve(config) ); const responseInterceptorId: number = axios.interceptors.response.use( (response: axios.AxiosResponse) => response, (error: any) => Promise.reject(error) ); axios.interceptors.response.eject(responseInterceptorId); axios.interceptors.response.use( (response: axios.AxiosResponse) => Promise.resolve(response), (error: any) => Promise.reject(error) ); axios.interceptors.request.use((req) => { // https://github.com/axios/axios/issues/5415 req.headers.set('foo', 'bar'); req.headers['Content-Type'] = 123; return req; }); const voidRequestInterceptorId = axios.interceptors.request.use( // @ts-expect-error -- Must return an axios.AxiosRequestConfig (or throw) (_response) => {}, (error: any) => Promise.reject(error) ); const voidResponseInterceptorId = axios.interceptors.response.use( // @ts-expect-error -- Must return an axios.AxiosResponse (or throw) (_response) => {}, (error: any) => Promise.reject(error) ); axios.interceptors.request.eject(voidRequestInterceptorId); axios.interceptors.response.eject(voidResponseInterceptorId); axios.interceptors.response.use((response: axios.AxiosResponse) => response); axios.interceptors.response.use((response: axios.AxiosResponse) => Promise.resolve(response)); axios.interceptors.request.clear(); axios.interceptors.response.clear(); // Adapters const adapter: axios.AxiosAdapter = (config: axios.InternalAxiosRequestConfig) => { const response: axios.AxiosResponse = { data: { foo: 'bar' }, status: 200, statusText: 'OK', headers: { 'X-FOO': 'bar' }, config, }; return Promise.resolve(response); }; axios.defaults.adapter = adapter; // axios.all const promises = [Promise.resolve(1), Promise.resolve(2)]; const promise: Promise = axios.all(promises); // axios.spread const fn1 = (a: number, b: number, c: number) => `${a}-${b}-${c}`; const fn2: (arr: number[]) => string = axios.spread(fn1); // Promises axios .get('/user') .then((response: axios.AxiosResponse) => 'foo') .then((value: string) => {}); axios .get('/user') .then((response: axios.AxiosResponse) => Promise.resolve('foo')) .then((value: string) => {}); axios .get('/user') .then( (response: axios.AxiosResponse) => 'foo', (error: any) => 'bar' ) .then((value: string) => {}); axios .get('/user') .then( (response: axios.AxiosResponse) => 'foo', (error: any) => 123 ) .then((value: string | number) => {}); axios .get('/user') .catch((error: any) => 'foo') .then((value) => {}); axios .get('/user') .catch((error: any) => Promise.resolve('foo')) .then((value) => {}); // axios.Cancellation const source: axios.CancelTokenSource = axios.CancelToken.source(); axios .get('/user', { cancelToken: source.token, }) .catch((thrown: axios.AxiosError | axios.Cancel) => { if (axios.isCancel(thrown)) { const cancel: axios.Cancel = thrown; console.log(cancel.message); } }); source.cancel('Operation has been axios.Canceled.'); // axios.AxiosError axios.get('/user').catch((error) => { if (axios.isAxiosError(error)) { const axiosError: axios.AxiosError = error; console.log(axiosError.message); } }); // FormData axios.toFormData({ x: 1 }, new FormData()); // AbortSignal axios.get('/user', { signal: new AbortController().signal }); // AxiosHeaders methods axios.get('/user', { transformRequest: (data, headers) => { headers.setContentType('text/plain'); headers['Foo'] = 'bar'; }, transformResponse: (data, headers) => { headers.has('foo'); }, }); // Max Rate axios.get('/user', { maxRate: 1000, }); axios.get('/user', { maxRate: [1000, 1000], }); // Node progress axios.get('/user', { onUploadProgress: (e: axios.AxiosProgressEvent) => { console.log(e.loaded); console.log(e.total); console.log(e.progress); console.log(e.rate); }, }); // AxiosHeaders // iterator const headers = new axios.AxiosHeaders({ foo: 'bar' }); for (const [header, value] of headers) { console.log(header, value); } // index signature (() => { const headers = new axios.AxiosHeaders({ x: 1 }); headers.y = 2; })(); // AxiosRequestHeaders (() => { const headers: axios.AxiosRequestHeaders = new axios.AxiosHeaders({ x: 1 }); headers.y = 2; headers.get('x'); })(); // AxiosHeaders instance assignment { const requestInterceptorId: number = axios.interceptors.request.use( async (config) => { config.headers.Accept = 'foo'; config.headers.setAccept('foo'); config.headers = new axios.AxiosHeaders({ x: 1 }); config.headers.foo = '1'; config.headers.set('bar', '2'); config.headers.set({ myHeader: 'myValue' }); config.headers = new axios.AxiosHeaders({ myHeader: 'myValue' }); config.headers = { ...config.headers } as axios.AxiosRequestHeaders; return config; }, (error: any) => Promise.reject(error) ); } { const config: axios.AxiosRequestConfig = { headers: new axios.AxiosHeaders({ foo: 1 }) }; axios.get('', { headers: { bar: 2, ...config.headers, }, }); } axios-axios-aa54a5e/tests/module/cjs/tests/helpers/fixture.cjs000066400000000000000000000015711526660636600246720ustar00rootroot00000000000000const fs = require('fs'); const path = require('path'); const createTempFixture = (suiteRoot, name, sourcePath, tsconfig, packageJson) => { const tempRoot = fs.mkdtempSync(path.join(suiteRoot, `.tmp-module-${name}-`)); const source = fs.readFileSync(sourcePath, 'utf8'); fs.writeFileSync(path.join(tempRoot, 'index.ts'), source); fs.writeFileSync(path.join(tempRoot, 'tsconfig.json'), JSON.stringify(tsconfig, null, 2)); if (packageJson) { fs.writeFileSync(path.join(tempRoot, 'package.json'), JSON.stringify(packageJson, null, 2)); } return tempRoot; }; const cleanupTempFixture = (dirPath) => { if (typeof fs.rmSync === 'function') { fs.rmSync(dirPath, { recursive: true, force: true }); return; } if (fs.existsSync(dirPath)) { fs.rmdirSync(dirPath, { recursive: true }); } }; module.exports = { createTempFixture, cleanupTempFixture, }; axios-axios-aa54a5e/tests/module/cjs/tests/helpers/run-command.cjs000066400000000000000000000017471526660636600254310ustar00rootroot00000000000000const { spawnSync } = require('child_process'); const formatCommand = (command, args) => [command].concat(args || []).join(' '); const runCommand = (command, args, options) => { const spawnOptions = Object.assign({ encoding: 'utf8' }, options || {}); const result = spawnSync(command, args || [], spawnOptions); const output = { code: result.status, stdout: result.stdout || '', stderr: result.stderr || '', command: formatCommand(command, args), cwd: spawnOptions.cwd || process.cwd(), }; if (result.error) { throw result.error; } if (output.code !== 0) { const error = new Error( [ 'Command failed:', ` command: ${output.command}`, ` cwd: ${output.cwd}`, ` exitCode: ${String(output.code)}`, ` stdout:\n${output.stdout}`, ` stderr:\n${output.stderr}`, ].join('\n') ); error.commandResult = output; throw error; } return output; }; module.exports = { runCommand, }; axios-axios-aa54a5e/tests/module/cjs/tests/helpers/ts-require-default.ts000066400000000000000000000025451526660636600265770ustar00rootroot00000000000000// @ts-nocheck const assert = require('assert'); const axios = require('axios').default; const { CanceledError, AxiosError, AxiosHeaders, formToJSON, spread, isAxiosError, isCancel, all, toFormData, } = axios; assert.strictEqual(typeof axios, 'function'); assert.strictEqual(typeof CanceledError, 'function'); assert.strictEqual(typeof AxiosError, 'function'); assert.strictEqual(typeof AxiosHeaders, 'function'); assert.strictEqual(typeof AxiosHeaders.parseParameters, 'function'); assert.strictEqual(typeof formToJSON, 'function'); assert.strictEqual(typeof spread, 'function'); assert.strictEqual(typeof isAxiosError, 'function'); assert.strictEqual(typeof isCancel, 'function'); assert.strictEqual(typeof all, 'function'); assert.strictEqual(typeof toFormData, 'function'); assert.strictEqual(typeof axios.CanceledError, 'function'); assert.strictEqual(typeof axios.AxiosError, 'function'); assert.strictEqual(typeof axios.AxiosHeaders, 'function'); assert.strictEqual(typeof axios.AxiosHeaders.parseParameters, 'function'); assert.strictEqual(typeof axios.formToJSON, 'function'); assert.strictEqual(typeof axios.spread, 'function'); assert.strictEqual(typeof axios.isAxiosError, 'function'); assert.strictEqual(typeof axios.isCancel, 'function'); assert.strictEqual(typeof axios.all, 'function'); assert.strictEqual(typeof axios.toFormData, 'function'); axios-axios-aa54a5e/tests/module/cjs/tests/helpers/ts-require.ts000066400000000000000000000025351526660636600251540ustar00rootroot00000000000000// @ts-nocheck const assert = require('assert'); const axios = require('axios'); const { CanceledError, AxiosError, AxiosHeaders, formToJSON, spread, isAxiosError, isCancel, all, toFormData, } = axios; assert.strictEqual(typeof axios, 'function'); assert.strictEqual(typeof CanceledError, 'function'); assert.strictEqual(typeof AxiosError, 'function'); assert.strictEqual(typeof AxiosHeaders, 'function'); assert.strictEqual(typeof AxiosHeaders.parseParameters, 'function'); assert.strictEqual(typeof formToJSON, 'function'); assert.strictEqual(typeof spread, 'function'); assert.strictEqual(typeof isAxiosError, 'function'); assert.strictEqual(typeof isCancel, 'function'); assert.strictEqual(typeof all, 'function'); assert.strictEqual(typeof toFormData, 'function'); assert.strictEqual(typeof axios.CanceledError, 'function'); assert.strictEqual(typeof axios.AxiosError, 'function'); assert.strictEqual(typeof axios.AxiosHeaders, 'function'); assert.strictEqual(typeof axios.AxiosHeaders.parseParameters, 'function'); assert.strictEqual(typeof axios.formToJSON, 'function'); assert.strictEqual(typeof axios.spread, 'function'); assert.strictEqual(typeof axios.isAxiosError, 'function'); assert.strictEqual(typeof axios.isCancel, 'function'); assert.strictEqual(typeof axios.all, 'function'); assert.strictEqual(typeof axios.toFormData, 'function'); axios-axios-aa54a5e/tests/module/cjs/tests/ts-require-default.module.test.cjs000066400000000000000000000022001526660636600275140ustar00rootroot00000000000000const path = require('path'); const { describe, it } = require('mocha'); const { createTempFixture, cleanupTempFixture } = require('./helpers/fixture.cjs'); const { runCommand } = require('./helpers/run-command.cjs'); const suiteRoot = path.resolve(__dirname, '..'); const repoRoot = path.resolve(suiteRoot, '../../..'); const tscBin = path.join(suiteRoot, 'node_modules', 'typescript', 'bin', 'tsc'); const tsconfig = { compilerOptions: { target: 'es2016', module: 'commonjs', moduleResolution: 'node', esModuleInterop: true, strict: true, skipLibCheck: true, }, }; describe('module ts-require-default compatibility', () => { it('compiles and executes require("axios").default imports', () => { const sourcePath = path.join(repoRoot, 'tests/module/cjs/tests/helpers/ts-require-default.ts'); const fixturePath = createTempFixture(suiteRoot, 'ts-require-default', sourcePath, tsconfig); try { runCommand('node', [tscBin, '-p', 'tsconfig.json'], { cwd: fixturePath }); runCommand('node', ['index.js'], { cwd: fixturePath }); } finally { cleanupTempFixture(fixturePath); } }); }); axios-axios-aa54a5e/tests/module/cjs/tests/ts-require.module.test.cjs000066400000000000000000000021401526660636600260750ustar00rootroot00000000000000const path = require('path'); const { describe, it } = require('mocha'); const { createTempFixture, cleanupTempFixture } = require('./helpers/fixture.cjs'); const { runCommand } = require('./helpers/run-command.cjs'); const suiteRoot = path.resolve(__dirname, '..'); const repoRoot = path.resolve(suiteRoot, '../../..'); const tscBin = path.join(suiteRoot, 'node_modules', 'typescript', 'bin', 'tsc'); const tsconfig = { compilerOptions: { target: 'es2016', module: 'commonjs', moduleResolution: 'node', esModuleInterop: true, strict: true, skipLibCheck: true, }, }; describe('module ts-require compatibility', () => { it('compiles and executes require("axios") imports', () => { const sourcePath = path.join(repoRoot, 'tests/module/cjs/tests/helpers/ts-require.ts'); const fixturePath = createTempFixture(suiteRoot, 'ts-require', sourcePath, tsconfig); try { runCommand('node', [tscBin, '-p', 'tsconfig.json'], { cwd: fixturePath }); runCommand('node', ['index.js'], { cwd: fixturePath }); } finally { cleanupTempFixture(fixturePath); } }); }); axios-axios-aa54a5e/tests/module/cjs/tests/typings.module.test.cjs000066400000000000000000000046031526660636600255000ustar00rootroot00000000000000const path = require('path'); const { describe, it } = require('mocha'); const { createTempFixture, cleanupTempFixture } = require('./helpers/fixture.cjs'); const { runCommand } = require('./helpers/run-command.cjs'); const suiteRoot = path.resolve(__dirname, '..'); const repoRoot = path.resolve(suiteRoot, '../../..'); const tscBin = path.join(suiteRoot, 'node_modules', 'typescript', 'bin', 'tsc'); const tsconfig = { compilerOptions: { checkJs: true, module: 'node16', }, }; describe('module cjs typings compatibility', () => { it('type-checks commonjs axios typings', () => { const sourcePath = path.join(repoRoot, 'tests/module/cjs/tests/helpers/cjs-typing.ts'); const fixturePath = createTempFixture(suiteRoot, 'typings-cjs', sourcePath, tsconfig); try { runCommand('node', [tscBin, '--noEmit', '-p', 'tsconfig.json'], { cwd: fixturePath }); } finally { cleanupTempFixture(fixturePath); } }); it('narrows isCancel to CanceledError in commonjs typings', () => { const sourcePath = path.join(repoRoot, 'tests/module/cjs/tests/helpers/cjs-is-cancel-typing.ts'); const fixturePath = createTempFixture(suiteRoot, 'typings-cjs-is-cancel', sourcePath, tsconfig); try { runCommand('node', [tscBin, '--noEmit', '-p', 'tsconfig.json'], { cwd: fixturePath }); } finally { cleanupTempFixture(fixturePath); } }); it('type-checks additive commonjs public typings', () => { const sourcePath = path.join(repoRoot, 'tests/module/cjs/tests/helpers/cjs-added-types.ts'); const fixturePath = createTempFixture(suiteRoot, 'typings-cjs-added', sourcePath, tsconfig); try { runCommand('node', [tscBin, '--noEmit', '-p', 'tsconfig.json'], { cwd: fixturePath }); } finally { cleanupTempFixture(fixturePath); } }); it('type-checks fetch Response constructors with TypeScript 4.9', () => { const sourcePath = path.join(repoRoot, 'tests/module/cjs/tests/helpers/cjs-fetch-types.ts'); const fixturePath = createTempFixture(suiteRoot, 'fetch-types-cjs', sourcePath, { compilerOptions: { module: 'node16', lib: ['ES2022', 'DOM'], types: ['node'], strict: true, skipLibCheck: false, }, }); try { runCommand('node', [tscBin, '--noEmit', '-p', 'tsconfig.json'], { cwd: fixturePath }); } finally { cleanupTempFixture(fixturePath); } }); }); axios-axios-aa54a5e/tests/module/esm/000077500000000000000000000000001526660636600177005ustar00rootroot00000000000000axios-axios-aa54a5e/tests/module/esm/package-lock.json000066400000000000000000001241741526660636600231250ustar00rootroot00000000000000{ "name": "@axios/esm-module-tests", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@axios/esm-module-tests", "version": "1.0.0", "license": "MIT", "devDependencies": { "@types/node": "20.19.39", "typescript": "5.9.3", "vitest": "4.1.9" } }, "node_modules/@emnapi/core": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", "dev": true, "license": "MIT", "optional": true, "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "dev": true, "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" } }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "dev": true, "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.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/@napi-rs/wasm-runtime": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "dev": true, "license": "MIT", "optional": true, "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "node_modules/@oxc-project/types": { "version": "0.139.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" } }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "android" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-darwin-arm64": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-darwin-x64": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-freebsd-x64": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", "cpu": [ "arm" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", "cpu": [ "arm64" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-arm64-musl": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", "cpu": [ "arm64" ], "dev": true, "libc": [ "musl" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", "cpu": [ "ppc64" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", "cpu": [ "s390x" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-x64-gnu": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", "cpu": [ "x64" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-x64-musl": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", "cpu": [ "x64" ], "dev": true, "libc": [ "musl" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-openharmony-arm64": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "openharmony" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-wasm32-wasi": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", "cpu": [ "wasm32" ], "dev": true, "license": "MIT", "optional": true, "dependencies": { "@emnapi/core": "1.11.1", "@emnapi/runtime": "1.11.1", "@napi-rs/wasm-runtime": "^1.1.6" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-x64-msvc": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/pluginutils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, "license": "MIT" }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "dev": true, "license": "MIT" }, "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/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", "dev": true, "license": "MIT", "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "node_modules/@types/deep-eql": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", "dev": true, "license": "MIT" }, "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/node": { "version": "20.19.39", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz", "integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==", "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } }, "node_modules/@vitest/expect": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.9", "@vitest/utils": "4.1.9", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", "dev": true, "license": "MIT", "dependencies": { "@vitest/spy": "4.1.9", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "msw": { "optional": true }, "vite": { "optional": true } } }, "node_modules/@vitest/pretty-format": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", "dev": true, "license": "MIT", "dependencies": { "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", "dev": true, "license": "MIT", "dependencies": { "@vitest/utils": "4.1.9", "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", "dev": true, "license": "MIT", "dependencies": { "@vitest/pretty-format": "4.1.9", "@vitest/utils": "4.1.9", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/spy": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", "dev": true, "license": "MIT", "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", "dev": true, "license": "MIT", "dependencies": { "@vitest/pretty-format": "4.1.9", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", "engines": { "node": ">=12" } }, "node_modules/chai": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", "engines": { "node": ">=18" } }, "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/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, "license": "Apache-2.0", "engines": { "node": ">=8" } }, "node_modules/es-module-lexer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", "dev": true, "license": "MIT" }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, "license": "MIT", "dependencies": { "@types/estree": "^1.0.0" } }, "node_modules/expect-type": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", "dev": true, "license": "Apache-2.0", "engines": { "node": ">=12.0.0" } }, "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/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/lightningcss": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", "dev": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" }, "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" }, "optionalDependencies": { "lightningcss-android-arm64": "1.33.0", "lightningcss-darwin-arm64": "1.33.0", "lightningcss-darwin-x64": "1.33.0", "lightningcss-freebsd-x64": "1.33.0", "lightningcss-linux-arm-gnueabihf": "1.33.0", "lightningcss-linux-arm64-gnu": "1.33.0", "lightningcss-linux-arm64-musl": "1.33.0", "lightningcss-linux-x64-gnu": "1.33.0", "lightningcss-linux-x64-musl": "1.33.0", "lightningcss-win32-arm64-msvc": "1.33.0", "lightningcss-win32-x64-msvc": "1.33.0" } }, "node_modules/lightningcss-android-arm64": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", "cpu": [ "arm64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "android" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-darwin-arm64": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", "cpu": [ "arm64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "darwin" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-darwin-x64": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", "cpu": [ "x64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "darwin" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-freebsd-x64": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", "cpu": [ "x64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "freebsd" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-arm-gnueabihf": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", "cpu": [ "arm" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-arm64-gnu": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", "cpu": [ "arm64" ], "dev": true, "libc": [ "glibc" ], "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-arm64-musl": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", "cpu": [ "arm64" ], "dev": true, "libc": [ "musl" ], "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-x64-gnu": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", "cpu": [ "x64" ], "dev": true, "libc": [ "glibc" ], "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-x64-musl": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", "cpu": [ "x64" ], "dev": true, "libc": [ "musl" ], "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-win32-arm64-msvc": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", "cpu": [ "arm64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "win32" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-win32-x64-msvc": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", "cpu": [ "x64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "win32" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/nanoid": { "version": "3.3.16", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, "engines": { "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, "node_modules/obug": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", "dev": true, "funding": [ "https://github.com/sponsors/sxzz", "https://opencollective.com/debug" ], "license": "MIT" }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT" }, "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/postcss": { "version": "8.5.23", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", "dev": true, "funding": [ { "type": "opencollective", "url": "https://opencollective.com/postcss/" }, { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" }, { "type": "github", "url": "https://github.com/sponsors/ai" } ], "license": "MIT", "dependencies": { "nanoid": "^3.3.16", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, "node_modules/rolldown": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", "dev": true, "license": "MIT", "dependencies": { "@oxc-project/types": "=0.139.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { "rolldown": "bin/cli.mjs" }, "engines": { "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.1.5", "@rolldown/binding-darwin-arm64": "1.1.5", "@rolldown/binding-darwin-x64": "1.1.5", "@rolldown/binding-freebsd-x64": "1.1.5", "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", "@rolldown/binding-linux-arm64-gnu": "1.1.5", "@rolldown/binding-linux-arm64-musl": "1.1.5", "@rolldown/binding-linux-ppc64-gnu": "1.1.5", "@rolldown/binding-linux-s390x-gnu": "1.1.5", "@rolldown/binding-linux-x64-gnu": "1.1.5", "@rolldown/binding-linux-x64-musl": "1.1.5", "@rolldown/binding-openharmony-arm64": "1.1.5", "@rolldown/binding-wasm32-wasi": "1.1.5", "@rolldown/binding-win32-arm64-msvc": "1.1.5", "@rolldown/binding-win32-x64-msvc": "1.1.5" } }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true, "license": "ISC" }, "node_modules/source-map-js": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true, "license": "MIT" }, "node_modules/std-env": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.0.0.tgz", "integrity": "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==", "dev": true, "license": "MIT" }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, "license": "MIT" }, "node_modules/tinyexec": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", "dev": true, "license": "MIT", "engines": { "node": ">=18" } }, "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/tinyrainbow": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", "dev": true, "license": "MIT", "engines": { "node": ">=14.0.0" } }, "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/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/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/vite": { "version": "8.1.5", "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz", "integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.5", "postcss": "^8.5.17", "rolldown": "~1.1.5", "tinyglobby": "^0.2.17" }, "bin": { "vite": "bin/vite.js" }, "engines": { "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.3.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, "@vitejs/devtools": { "optional": true }, "esbuild": { "optional": true }, "jiti": { "optional": true }, "less": { "optional": true }, "sass": { "optional": true }, "sass-embedded": { "optional": true }, "stylus": { "optional": true }, "sugarss": { "optional": true }, "terser": { "optional": true }, "tsx": { "optional": true }, "yaml": { "optional": true } } }, "node_modules/vitest": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", "dev": true, "license": "MIT", "dependencies": { "@vitest/expect": "4.1.9", "@vitest/mocker": "4.1.9", "@vitest/pretty-format": "4.1.9", "@vitest/runner": "4.1.9", "@vitest/snapshot": "4.1.9", "@vitest/spy": "4.1.9", "@vitest/utils": "4.1.9", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { "node": "^20.0.0 || ^22.0.0 || >=24.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.9", "@vitest/browser-preview": "4.1.9", "@vitest/browser-webdriverio": "4.1.9", "@vitest/coverage-istanbul": "4.1.9", "@vitest/coverage-v8": "4.1.9", "@vitest/ui": "4.1.9", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { "optional": true }, "@opentelemetry/api": { "optional": true }, "@types/node": { "optional": true }, "@vitest/browser-playwright": { "optional": true }, "@vitest/browser-preview": { "optional": true }, "@vitest/browser-webdriverio": { "optional": true }, "@vitest/coverage-istanbul": { "optional": true }, "@vitest/coverage-v8": { "optional": true }, "@vitest/ui": { "optional": true }, "happy-dom": { "optional": true }, "jsdom": { "optional": true }, "vite": { "optional": false } } }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" }, "engines": { "node": ">=8" } } } } axios-axios-aa54a5e/tests/module/esm/package.json000066400000000000000000000006611526660636600221710ustar00rootroot00000000000000{ "name": "@axios/esm-module-tests", "version": "1.0.0", "description": "ESM module compatibility tests for axios", "private": true, "type": "module", "scripts": { "test:module:esm": "vitest run --config vitest.config.js --project module" }, "keywords": [], "author": "axios team", "license": "MIT", "devDependencies": { "@types/node": "20.19.39", "typescript": "5.9.3", "vitest": "4.1.9" } } axios-axios-aa54a5e/tests/module/esm/tests/000077500000000000000000000000001526660636600210425ustar00rootroot00000000000000axios-axios-aa54a5e/tests/module/esm/tests/fixture-cleanup.module.test.js000066400000000000000000000014501526660636600267550ustar00rootroot00000000000000import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { describe, expect, it } from 'vitest'; import { cleanupTempFixture } from './helpers/fixture.js'; describe('module fixture cleanup helper', () => { it('removes fixture directories without shelling out to rm', () => { const fixturePath = fs.mkdtempSync(path.join(os.tmpdir(), 'axios-esm-module-fixture-')); const nestedPath = path.join(fixturePath, 'nested'); const originalPath = process.env.PATH; fs.mkdirSync(nestedPath); fs.writeFileSync(path.join(nestedPath, 'index.ts'), 'export {};\n'); process.env.PATH = ''; try { cleanupTempFixture(fixturePath); } finally { process.env.PATH = originalPath; } expect(fs.existsSync(fixturePath)).toBe(false); }); }); axios-axios-aa54a5e/tests/module/esm/tests/helpers/000077500000000000000000000000001526660636600225045ustar00rootroot00000000000000axios-axios-aa54a5e/tests/module/esm/tests/helpers/declaration-consumer.ts000066400000000000000000000015751526660636600272020ustar00rootroot00000000000000import type { AxiosInstance, AxiosResponse } from 'axios'; import { makeRequest } from './declarations/index.js'; interface User { id: number; name: string; } interface CustomResponse { user: User; fromCache: boolean; } declare const client: AxiosInstance; declare function expectType(value: T): void; const request = makeRequest(client); expectType>>(request()); const defaultResponse = request(); expectType>>(defaultResponse); // @ts-expect-error -- default responses preserve the requested data type expectType>>(defaultResponse); const customResponse = request(); expectType>(customResponse); // @ts-expect-error -- custom responses use R without an AxiosResponse wrapper expectType>>(customResponse); axios-axios-aa54a5e/tests/module/esm/tests/helpers/declaration-emit.ts000066400000000000000000000003271526660636600262770ustar00rootroot00000000000000import type { AxiosInstance, AxiosResponse } from 'axios'; export const makeRequest = function (client: AxiosInstance) { return >() => client.request({ url: '/' }); }; axios-axios-aa54a5e/tests/module/esm/tests/helpers/esm-added-types.ts000066400000000000000000000170741526660636600260520ustar00rootroot00000000000000import axios, { AxiosHeaders, CanceledError, HttpStatusCode, isCancel, toFormData, type AxiosHeaderParameters, type AxiosHeaderValue, type AxiosError, type AxiosPromise, type AxiosRequestConfig, type AxiosResponse, type CustomParamsSerializer, type FormSerializerOptions, type InternalAxiosRequestConfig, type RawAxiosRequestConfig, } from 'axios'; const utf8AuthConfig: AxiosRequestConfig = { auth: { username: 'Ï€', password: 'x' }, transitional: { encodeBasicAuthUsername: true }, }; const legacyAuthConfig: AxiosRequestConfig = { transitional: { encodeBasicAuthUsername: false }, }; const invalidAuthConfig: AxiosRequestConfig = { transitional: { // @ts-expect-error -- the encoding opt-in only accepts booleans encodeBasicAuthUsername: 'true', }, }; const headers = new AxiosHeaders(); const iterableHeaders: Iterable<[string, AxiosHeaderValue]> = [['x-test', 'ok']]; headers.set(iterableHeaders); const serializedHeaders: string = headers.toString(); const parsedParameters: AxiosHeaderParameters = AxiosHeaders.parseParameters( 'multipart/form-data; boundary="test"' ); headers.set('content-type', 'multipart/form-data; boundary="test"'); const parsedHeaderParameters: AxiosHeaderParameters = headers.get( 'content-type', AxiosHeaders.parseParameters ); const source = axios.CancelToken.source(); source.token.subscribe((cancel) => { const message: string | undefined = cancel && cancel.message; console.log(message); }); source.token.unsubscribe(() => {}); const signal: AbortSignal = source.token.toAbortSignal(); const cancel = new CanceledError<{ ok: true }>( 'stop', {} as InternalAxiosRequestConfig, {} ); const cancelFlag: boolean | undefined = cancel.__CANCEL__; const cancelCtor: typeof CanceledError = axios.Cancel; const cancelFromAlias = new cancelCtor('from alias'); const status: HttpStatusCode = HttpStatusCode.WebServerIsDown; const unknownErrorStatus: HttpStatusCode = HttpStatusCode.WebServerReturnsAnUnknownError; const contentTooLargeStatus: HttpStatusCode = HttpStatusCode.ContentTooLarge; const unprocessableContentStatus: HttpStatusCode = HttpStatusCode.UnprocessableContent; class CustomBlob { constructor(_parts?: any[]) {} } const serializerOptions: FormSerializerOptions = { maxDepth: 2, Blob: CustomBlob, }; toFormData({ file: new Uint8Array([1]) }, undefined, serializerOptions); interface SearchBody { includeArchived: boolean; } interface SearchParams { query: string; page?: number; } const paramsConfig: AxiosRequestConfig = { data: { includeArchived: false }, params: { query: 'axios', page: 1 }, paramsSerializer: (params) => { const query: string = params.query; const page: number | undefined = params.page; // @ts-expect-error -- serializer params use the request params type const invalidQuery: number = params.query; void invalidQuery; return `${query}:${page ?? 1}`; }, withXSRFToken: (config) => config.params?.query === 'axios', }; const customParamsSerializer: CustomParamsSerializer = (params) => params.query; const rawParamsConfig: RawAxiosRequestConfig = paramsConfig; const internalParamsConfig = { ...paramsConfig, headers: new AxiosHeaders(), } as InternalAxiosRequestConfig; const paramsResponse = {} as AxiosResponse; const paramsError = {} as AxiosError; declare const paramsPromise: AxiosPromise; const responseDuration: number | undefined = paramsResponse.duration; const errorResponseDuration: number | undefined = paramsError.response?.duration; paramsResponse.duration = 25; paramsResponse.duration = undefined; // @ts-expect-error -- response duration is numeric when present paramsResponse.duration = '25'; // Custom adapters can still return a response without timing metadata. const untimedResponse: AxiosResponse = { data: null, status: 200, statusText: 'OK', headers: {}, config: internalParamsConfig, }; const internalQuery: string = internalParamsConfig.params!.query; const responseQuery: string = paramsResponse.config.params!.query; const errorQuery: string = paramsError.config!.params!.query; paramsPromise.then((response) => { const promiseData: SearchBody | undefined = response.config.data; const promiseParams: SearchParams | undefined = response.config.params; // @ts-expect-error -- AxiosPromise preserves the request data type const invalidPromiseData: { includeArchived: string } | undefined = response.config.data; // @ts-expect-error -- AxiosPromise preserves the request params type const invalidPromiseParams: { query: number } | undefined = response.config.params; void promiseData; void promiseParams; void invalidPromiseData; void invalidPromiseParams; }); axios.get('/search', paramsConfig).then((response) => { const duration: number | undefined = response.duration; // @ts-expect-error -- inferred response duration is not a string const invalidDuration: string = response.duration; void duration; void invalidDuration; const aliasData: SearchBody | undefined = response.config.data; const aliasParams: SearchParams | undefined = response.config.params; // @ts-expect-error -- default alias responses preserve the request data type const invalidAliasData: { includeArchived: string } | undefined = response.config.data; // @ts-expect-error -- default alias responses preserve the request params type const invalidAliasParams: { query: number } | undefined = response.config.params; void aliasData; void aliasParams; void invalidAliasData; void invalidAliasParams; }); declare const thrown: unknown; if (isCancel(thrown)) { const canceledData: SearchBody | undefined = thrown.config?.data; const canceledParams: SearchParams | undefined = thrown.config?.params; // @ts-expect-error -- canceled request data must not be widened to any const invalidCanceledData: { includeArchived: string } | undefined = thrown.config?.data; // @ts-expect-error -- canceled request params must not be widened to any const invalidCanceledParams: { query: number } | undefined = thrown.config?.params; void canceledData; void canceledParams; void invalidCanceledData; void invalidCanceledParams; } const legacyParamsConfig: AxiosRequestConfig = { params: 'legacy values remain accepted', paramsSerializer: (params) => { const legacyParams: Record = params; // @ts-expect-error -- the default serializer input remains a params record, not any const legacyParamsNumber: number = params; void legacyParamsNumber; return String(legacyParams.query); }, }; axios.get, any, SearchParams>('/search', { params: { query: 'axios' }, }); const invalidParamsConfig: AxiosRequestConfig = { // @ts-expect-error -- params must match the explicit request params type params: { query: 1 }, }; axios.get, any, SearchParams>('/search', { // @ts-expect-error -- request aliases enforce their trailing params type params: { query: 1 }, }); const mergedParamsConfig = axios.mergeConfig( rawParamsConfig, paramsConfig ); const mergedQuery: string = mergedParamsConfig.params!.query; console.log( serializedHeaders, parsedParameters, parsedHeaderParameters, signal.aborted, cancelFlag, cancelFromAlias.message, status, customParamsSerializer, internalQuery, responseQuery, errorQuery, legacyParamsConfig, mergedQuery, invalidParamsConfig ); axios-axios-aa54a5e/tests/module/esm/tests/helpers/esm-fetch-types.ts000066400000000000000000000044251526660636600260760ustar00rootroot00000000000000import axios, { AxiosRequestConfig } from 'axios'; // Importing Axios and choosing HTTP must work without the DOM library. axios.create({ adapter: 'http' }); // Existing wrappers may explicitly use the original constructor body type. type LegacyBody = ArrayBuffer | ArrayBufferView | Blob | FormData | URLSearchParams | string | null; const normalizeBody = (body: LegacyBody | ReadableStream | undefined) => ArrayBuffer.isView(body) ? new Uint8Array(body.buffer, body.byteOffset, body.byteLength).slice() : body; class LegacyResponse extends Response { constructor(body?: LegacyBody, init?: ResponseInit) { super(normalizeBody(body), init); } } // Supporting Axios's tracked stream must not require Node-only iterable inputs. class StreamCompatibleResponse extends Response { constructor(body?: LegacyBody | ReadableStream, init?: ResponseInit) { super(normalizeBody(body), init); } } axios.create({ adapter: 'fetch', env: { Response: LegacyResponse } }); axios.create({ env: { Response: StreamCompatibleResponse } }); type ResponseConstructor = NonNullable['Response']>; const ResponseOverride: ResponseConstructor = LegacyResponse; new ResponseOverride(); new ResponseOverride(null); new ResponseOverride('body'); new ResponseOverride(new ArrayBuffer(8)); new ResponseOverride(new Uint8Array(8)); new ResponseOverride(new DataView(new ArrayBuffer(8))); new ResponseOverride(new Blob(['body'])); new ResponseOverride(new FormData()); new ResponseOverride(new URLSearchParams()); // Calls through the configured constructor retain the original body contract, // including views typed through the general ArrayBufferView interface. function constructResponse(Response: ResponseConstructor, body: ArrayBufferView) { return new Response(body); } constructResponse(ResponseOverride, new Uint8Array(8)); // Invalid body values must not silently become any. // @ts-expect-error Response bodies cannot be numbers. new ResponseOverride(123); class StringOnlyResponse extends Response { constructor(body?: string | null, init?: ResponseInit) { super(body, init); } } axios.create({ env: { // @ts-expect-error String-only constructors cannot accept the existing body inputs. Response: StringOnlyResponse, }, }); axios-axios-aa54a5e/tests/module/esm/tests/helpers/esm-functions.ts000066400000000000000000000025051526660636600256500ustar00rootroot00000000000000// @ts-nocheck import assert from 'assert'; import axios, { CanceledError, AxiosError, AxiosHeaders, formToJSON, spread, isAxiosError, isCancel, all, toFormData, } from 'axios'; assert.strictEqual(typeof axios, 'function'); assert.strictEqual(typeof CanceledError, 'function'); assert.strictEqual(typeof AxiosError, 'function'); assert.strictEqual(typeof AxiosHeaders, 'function'); assert.strictEqual(typeof AxiosHeaders.parseParameters, 'function'); assert.strictEqual(typeof formToJSON, 'function'); assert.strictEqual(typeof spread, 'function'); assert.strictEqual(typeof isAxiosError, 'function'); assert.strictEqual(typeof isCancel, 'function'); assert.strictEqual(typeof all, 'function'); assert.strictEqual(typeof toFormData, 'function'); assert.strictEqual(typeof axios.CanceledError, 'function'); assert.strictEqual(typeof axios.AxiosError, 'function'); assert.strictEqual(typeof axios.AxiosHeaders, 'function'); assert.strictEqual(typeof axios.AxiosHeaders.parseParameters, 'function'); assert.strictEqual(typeof axios.formToJSON, 'function'); assert.strictEqual(typeof axios.spread, 'function'); assert.strictEqual(typeof axios.isAxiosError, 'function'); assert.strictEqual(typeof axios.isCancel, 'function'); assert.strictEqual(typeof axios.all, 'function'); assert.strictEqual(typeof axios.toFormData, 'function'); axios-axios-aa54a5e/tests/module/esm/tests/helpers/esm-index.ts000066400000000000000000000414421526660636600247520ustar00rootroot00000000000000// @ts-nocheck import axios, { AxiosRequestConfig, AxiosHeaders, AxiosRequestHeaders, AxiosResponseHeaders, RawAxiosRequestHeaders, AxiosResponse, AxiosError, AxiosInstance, AxiosAdapter, Cancel, CancelTokenSource, Canceler, AxiosProgressEvent, ParamsSerializerOptions, toFormData, formToJSON, getAdapter, all, isCancel, isAxiosError, spread, AddressFamily, } from 'axios'; const config: AxiosRequestConfig = { url: '/user', method: 'get', baseURL: 'https://api.example.com/', allowAbsoluteUrls: false, transformRequest: (data: any) => '{"foo":"bar"}', transformResponse: [(data: any) => ({ baz: 'qux' })], headers: { 'X-FOO': 'bar' }, params: { id: 12345 }, paramsSerializer: { indexes: true, encode: (value: any) => value, serialize: (value: Record, options?: ParamsSerializerOptions) => String(value), }, data: { foo: 'bar' }, timeout: 10000, withCredentials: true, auth: { username: 'janedoe', password: 's00pers3cret', }, responseType: 'json', xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN', onUploadProgress: (progressEvent: AxiosProgressEvent) => {}, onDownloadProgress: (progressEvent: AxiosProgressEvent) => {}, maxContentLength: 2000, maxBodyLength: 2000, validateStatus: (status: number) => status >= 200 && status < 300, maxRedirects: 5, proxy: { host: '127.0.0.1', port: 9000, }, cancelToken: new axios.CancelToken((cancel: Canceler) => {}), }; const nullValidateStatusConfig: AxiosRequestConfig = { validateStatus: null, }; const undefinedValidateStatusConfig: AxiosRequestConfig = { validateStatus: undefined, }; const handleResponse = (response: AxiosResponse) => { console.log(response.data); console.log(response.status); console.log(response.statusText); console.log(response.headers); console.log(response.config); }; const handleError = (error: AxiosError) => { if (error.response) { console.log(error.response.data); console.log(error.response.status); console.log(error.response.headers); } else { console.log(error.message); } }; axios(config).then(handleResponse).catch(handleError); axios.get('/user?id=12345').then(handleResponse).catch(handleError); axios .get('/user', { params: { id: 12345 } }) .then(handleResponse) .catch(handleError); axios.head('/user').then(handleResponse).catch(handleError); axios.options('/user').then(handleResponse).catch(handleError); axios.delete('/user').then(handleResponse).catch(handleError); axios.post('/user', { foo: 'bar' }).then(handleResponse).catch(handleError); axios .post('/user', { foo: 'bar' }, { headers: { 'X-FOO': 'bar' } }) .then(handleResponse) .catch(handleError); axios.put('/user', { foo: 'bar' }).then(handleResponse).catch(handleError); axios.patch('/user', { foo: 'bar' }).then(handleResponse).catch(handleError); axios.query('/user', { foo: 'bar' }).then(handleResponse).catch(handleError); // Typed methods interface UserCreationDef { name: string; } interface User { id: number; name: string; } interface ResponseHeaders { 'x-header': string; } // with default AxiosResponse result const handleUserResponse = (response: AxiosResponse) => { console.log(response.data.id); console.log(response.data.name); console.log(response.status); console.log(response.statusText); console.log(response.headers); console.log(response.config); }; axios.get('/user?id=12345').then(handleUserResponse).catch(handleError); axios .get('/user', { params: { id: 12345 } }) .then(handleUserResponse) .catch(handleError); axios.head('/user').then(handleUserResponse).catch(handleError); axios.options('/user').then(handleUserResponse).catch(handleError); axios.delete('/user').then(handleUserResponse).catch(handleError); axios.post('/user', { name: 'foo', id: 1 }).then(handleUserResponse).catch(handleError); axios .post('/user', { name: 'foo', id: 1 }, { headers: { 'X-FOO': 'bar' } }) .then(handleUserResponse) .catch(handleError); axios.put('/user', { name: 'foo', id: 1 }).then(handleUserResponse).catch(handleError); axios.patch('/user', { name: 'foo', id: 1 }).then(handleUserResponse).catch(handleError); // with custom response headers AxiosResponse result const handleUserResponseWithCustomHeaders = ( response: AxiosResponse ) => { console.log(response.data.id); console.log(response.data.name); console.log(response.status); console.log(response.statusText); console.log(response.headers); console.log(response.config); }; axios .get>('/user?id=12345') .then(handleUserResponseWithCustomHeaders) .catch(handleError); axios .get>('/user', { params: { id: 12345 } }) .then(handleUserResponseWithCustomHeaders) .catch(handleError); axios .head>('/user') .then(handleUserResponseWithCustomHeaders) .catch(handleError); axios .options>('/user') .then(handleUserResponseWithCustomHeaders) .catch(handleError); axios .delete>('/user') .then(handleUserResponseWithCustomHeaders) .catch(handleError); axios .post>('/user', { name: 'foo', id: 1 }) .then(handleUserResponseWithCustomHeaders) .catch(handleError); axios .post>( '/user', { name: 'foo', id: 1 }, { headers: { 'X-FOO': 'bar' } } ) .then(handleUserResponseWithCustomHeaders) .catch(handleError); axios .put>('/user', { name: 'foo', id: 1 }) .then(handleUserResponseWithCustomHeaders) .catch(handleError); axios .patch>('/user', { name: 'foo', id: 1 }) .then(handleUserResponseWithCustomHeaders) .catch(handleError); // (Typed methods) with custom response type const handleStringResponse = (response: string) => { console.log(response); }; axios.get('/user?id=12345').then(handleStringResponse).catch(handleError); axios .get('/user', { params: { id: 12345 } }) .then(handleStringResponse) .catch(handleError); axios.head('/user').then(handleStringResponse).catch(handleError); axios.options('/user').then(handleStringResponse).catch(handleError); axios.delete('/user').then(handleStringResponse).catch(handleError); axios .post, string>('/user', { name: 'foo' }) .then(handleStringResponse) .catch(handleError); axios .post, string>('/user', { name: 'foo' }, { headers: { 'X-FOO': 'bar' } }) .then(handleStringResponse) .catch(handleError); axios .put, string>('/user', { name: 'foo' }) .then(handleStringResponse) .catch(handleError); axios .patch, string>('/user', { name: 'foo' }) .then(handleStringResponse) .catch(handleError); axios .request({ method: 'get', url: '/user?id=12345', }) .then(handleStringResponse) .catch(handleError); // Instances const instance1: AxiosInstance = axios.create(); const instance2: AxiosInstance = instance1.create(config); instance1(config).then(handleResponse).catch(handleError); instance1.request(config).then(handleResponse).catch(handleError); instance1.get('/user?id=12345').then(handleResponse).catch(handleError); instance1.options('/user').then(handleResponse).catch(handleError); instance1 .get('/user', { params: { id: 12345 } }) .then(handleResponse) .catch(handleError); instance1.post('/user', { foo: 'bar' }).then(handleResponse).catch(handleError); instance1 .post('/user', { foo: 'bar' }, { headers: { 'X-FOO': 'bar' } }) .then(handleResponse) .catch(handleError); // Defaults axios.defaults.headers['X-FOO']; axios.defaults.baseURL = 'https://api.example.com/'; axios.defaults.headers.common['Accept'] = 'application/json'; axios.defaults.headers.post['X-FOO'] = 'bar'; axios.defaults.timeout = 2500; instance1.defaults.baseURL = 'https://api.example.com/'; instance1.defaults.headers.common['Accept'] = 'application/json'; instance1.defaults.headers.post['X-FOO'] = 'bar'; instance1.defaults.timeout = 2500; // axios create defaults axios.create({ headers: { foo: 'bar' } }); axios.create({ headers: { common: { foo: 'bar' } } }); axios.create({ headers: { 'Content-Type': 'application/json', }, formSerializer: { indexes: null, }, paramsSerializer: { indexes: null, }, }); // Interceptors const requestInterceptorId: number = axios.interceptors.request.use( async (config) => { await axios.get('/foo', { headers: config.headers, }); return config; }, (error: any) => Promise.reject(error), { synchronous: false } ); axios.interceptors.request.eject(requestInterceptorId); axios.interceptors.request.use( (config) => Promise.resolve(config), (error: any) => Promise.reject(error) ); axios.interceptors.request.use((config) => config); axios.interceptors.request.use((config) => Promise.resolve(config)); const responseInterceptorId: number = axios.interceptors.response.use( (response: AxiosResponse) => response, (error: any) => Promise.reject(error) ); axios.interceptors.response.eject(responseInterceptorId); axios.interceptors.response.use( (response: AxiosResponse) => Promise.resolve(response), (error: any) => Promise.reject(error) ); axios.interceptors.request.use((req) => { // https://github.com/axios/axios/issues/5415 req.headers.set('foo', 'bar'); req.headers['Content-Type'] = 123; return req; }); const voidRequestInterceptorId = axios.interceptors.request.use( // @ts-expect-error -- Must return an AxiosRequestConfig (or throw) (_response) => {}, (error: any) => Promise.reject(error) ); const voidResponseInterceptorId = axios.interceptors.response.use( // @ts-expect-error -- Must return an AxiosResponse (or throw) (_response) => {}, (error: any) => Promise.reject(error) ); axios.interceptors.request.eject(voidRequestInterceptorId); axios.interceptors.response.eject(voidResponseInterceptorId); axios.interceptors.response.use((response: AxiosResponse) => response); axios.interceptors.response.use((response: AxiosResponse) => Promise.resolve(response)); axios.interceptors.request.clear(); axios.interceptors.response.clear(); // Adapters const adapter: AxiosAdapter = (config) => { const response: AxiosResponse = { data: { foo: 'bar' }, status: 200, statusText: 'OK', headers: { 'X-FOO': 'bar' }, config, }; return Promise.resolve(response); }; axios.defaults.adapter = adapter; // axios.all const promises = [Promise.resolve(1), Promise.resolve(2)]; const promise: Promise = axios.all(promises); // axios.all named export (() => { const promises = [Promise.resolve(1), Promise.resolve(2)]; const promise: Promise = all(promises); })(); // axios.spread const fn1 = (a: number, b: number, c: number) => `${a}-${b}-${c}`; const fn2: (arr: number[]) => string = axios.spread(fn1); // axios.spread named export (() => { const fn1 = (a: number, b: number, c: number) => `${a}-${b}-${c}`; const fn2: (arr: number[]) => string = spread(fn1); })(); // Promises axios .get('/user') .then((response: AxiosResponse) => 'foo') .then((value: string) => {}); axios .get('/user') .then((response: AxiosResponse) => Promise.resolve('foo')) .then((value: string) => {}); axios .get('/user') .then( (response: AxiosResponse) => 'foo', (error: any) => 'bar' ) .then((value: string) => {}); axios .get('/user') .then( (response: AxiosResponse) => 'foo', (error: any) => 123 ) .then((value: string | number) => {}); axios .get('/user') .catch((error: any) => 'foo') .then((value: any) => {}); axios .get('/user') .catch((error: any) => Promise.resolve('foo')) .then((value: any) => {}); // Cancellation const source: CancelTokenSource = axios.CancelToken.source(); axios .get('/user', { cancelToken: source.token, }) .catch((thrown: AxiosError | Cancel) => { if (axios.isCancel(thrown)) { const cancel: Cancel = thrown; console.log(cancel.message); } // named export if (isCancel(thrown)) { const cancel: Cancel = thrown; console.log(cancel.message); } }); source.cancel('Operation has been canceled.'); // AxiosError axios.get('/user').catch((error: AxiosError) => { if (axios.isAxiosError(error)) { const axiosError: AxiosError = error; console.log(axiosError.message); } // named export if (isAxiosError(error)) { const axiosError: AxiosError = error; console.log(axiosError.message); } }); // FormData axios.toFormData({ x: 1 }, new FormData()); // named export toFormData({ x: 1 }, new FormData()); // formToJSON axios.toFormData(new FormData()); // named export formToJSON(new FormData()); // AbortSignal axios.get('/user', { signal: new AbortController().signal }); // AxiosHeaders methods axios.get('/user', { transformRequest: [ (data: any, headers) => { headers.setContentType('text/plain'); return 'baz'; }, (data: any, headers) => { headers['foo'] = 'bar'; return 'baz'; }, ], transformResponse: [ (data: any, headers: AxiosResponseHeaders) => { headers.has('foo'); }, ], }); // config headers axios.get('/user', { headers: new AxiosHeaders({ x: 1 }), }); axios.get('/user', { headers: { foo: 1, }, }); // issue #5034 function getRequestConfig1(options: AxiosRequestConfig): AxiosRequestConfig { return { ...options, headers: { ...(options.headers as RawAxiosRequestHeaders), Authorization: `Bearer ...`, }, }; } function getRequestConfig2(options: AxiosRequestConfig): AxiosRequestConfig { return { ...options, headers: { ...(options.headers as AxiosHeaders).toJSON(), Authorization: `Bearer ...`, }, }; } // Max Rate axios.get('/user', { maxRate: 1000, }); axios.get('/user', { maxRate: [1000, 1000], }); // Node progress axios.get('/user', { onUploadProgress: (e: AxiosProgressEvent) => { console.log(e.loaded); console.log(e.total); console.log(e.progress); console.log(e.rate); }, }); // adapters axios.get('/user', { adapter: 'xhr', }); axios.get('/user', { adapter: 'http', }); axios.get('/user', { adapter: ['xhr', 'http'], }); { // getAdapter getAdapter(axios.create().defaults.adapter); getAdapter(undefined); getAdapter([]); getAdapter(['xhr']); getAdapter([adapter]); getAdapter(['xhr', 'http']); getAdapter([adapter, 'xhr']); getAdapter([adapter, adapter]); getAdapter('xhr'); getAdapter(adapter); const _: AxiosAdapter = getAdapter('xhr'); const __: AxiosAdapter = getAdapter(['xhr']); // @ts-expect-error getAdapter(); // @ts-expect-error getAdapter(123); // @ts-expect-error getAdapter([123]); // @ts-expect-error getAdapter('xhr', 'http'); } // AxiosHeaders // iterator const headers = new AxiosHeaders({ foo: 'bar' }); for (const [header, value] of headers) { console.log(header, value); } // index signature (() => { const headers = new AxiosHeaders({ x: 1 }); headers.y = 2; })(); // AxiosRequestHeaders (() => { const headers: AxiosRequestHeaders = new AxiosHeaders({ x: 1 }); headers.y = 2; headers.get('x'); })(); // AxiosHeaders instance assignment { const requestInterceptorId: number = axios.interceptors.request.use( async (config) => { config.headers.Accept = 'foo'; config.headers.setAccept('foo'); config.headers = new AxiosHeaders({ x: 1 }); config.headers.foo = '1'; config.headers.set('bar', '2'); config.headers.set({ myHeader: 'myValue' }); config.headers = new AxiosHeaders({ myHeader: 'myValue' }); config.headers = { ...config.headers } as AxiosRequestHeaders; return config; }, (error: any) => Promise.reject(error) ); } { const config: AxiosRequestConfig = { headers: new AxiosHeaders({ foo: 1 }) }; axios.get('', { headers: { bar: 2, ...config.headers, }, }); } // lookup axios.get('/user', { lookup: ( hostname: string, opt: object, cb: (err: Error | null, address: string, family: AddressFamily) => void ) => { cb(null, '127.0.0.1', 4); }, }); // lookup async axios.get('/user', { lookup: (hostname: string, opt: object) => { return ['127.0.0.1', 4]; }, }); // AxiosError.cause should be typed as Error to allow accessing .message axios.get('/user').catch((error: AxiosError) => { if (error.cause) { // This should not produce a type error - cause is typed as Error const causeMessage: string | undefined = error.cause.message; console.log(causeMessage); } }); axios-axios-aa54a5e/tests/module/esm/tests/helpers/fixture.js000066400000000000000000000012651526660636600245340ustar00rootroot00000000000000import fs from 'node:fs'; import path from 'node:path'; export const createTempFixture = (suiteRoot, name, sourcePath, tsconfig, packageJson) => { const tempRoot = fs.mkdtempSync(path.join(suiteRoot, `.tmp-module-${name}-`)); const source = fs.readFileSync(sourcePath, 'utf8'); fs.writeFileSync(path.join(tempRoot, 'index.ts'), source); fs.writeFileSync(path.join(tempRoot, 'tsconfig.json'), JSON.stringify(tsconfig, null, 2)); if (packageJson) { fs.writeFileSync(path.join(tempRoot, 'package.json'), JSON.stringify(packageJson, null, 2)); } return tempRoot; }; export const cleanupTempFixture = (dirPath) => { fs.rmSync(dirPath, { recursive: true, force: true }); }; axios-axios-aa54a5e/tests/module/esm/tests/helpers/run-command.js000066400000000000000000000015761526660636600252730ustar00rootroot00000000000000import { spawnSync } from 'node:child_process'; const formatCommand = (command, args) => [command, ...(args || [])].join(' '); export const runCommand = (command, args = [], options = {}) => { const spawnOptions = { encoding: 'utf8', ...options }; const result = spawnSync(command, args, spawnOptions); const output = { code: result.status, stdout: result.stdout || '', stderr: result.stderr || '', command: formatCommand(command, args), cwd: spawnOptions.cwd || process.cwd(), }; if (result.error) { throw result.error; } if (output.code !== 0) { throw new Error( [ 'Command failed:', ` command: ${output.command}`, ` cwd: ${output.cwd}`, ` exitCode: ${String(output.code)}`, ` stdout:\n${output.stdout}`, ` stderr:\n${output.stderr}`, ].join('\n') ); } return output; }; axios-axios-aa54a5e/tests/module/esm/tests/ts.module.test.js000066400000000000000000000053661526660636600243020ustar00rootroot00000000000000import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, it } from 'vitest'; import { createTempFixture, cleanupTempFixture } from './helpers/fixture.js'; import { runCommand } from './helpers/run-command.js'; const suiteRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const repoRoot = path.resolve(suiteRoot, '../../..'); const tscBin = path.join(suiteRoot, 'node_modules', 'typescript', 'bin', 'tsc'); const tsconfig = { compilerOptions: { target: 'es2016', module: 'commonjs', moduleResolution: 'node', esModuleInterop: true, strict: true, skipLibCheck: true, }, }; const declarationTsconfig = { compilerOptions: { module: 'node16', strict: true, skipLibCheck: false, declaration: true, emitDeclarationOnly: true, outDir: 'declarations', }, files: ['index.ts'], }; const declarationConsumerTsconfig = { compilerOptions: { module: 'node16', strict: true, skipLibCheck: false, noEmit: true, }, files: ['consumer.ts'], }; describe('module ts compatibility', () => { it('compiles and executes import axios syntax', () => { const sourcePath = path.join(repoRoot, 'tests/module/esm/tests/helpers/esm-functions.ts'); const fixturePath = createTempFixture(suiteRoot, 'ts', sourcePath, tsconfig, { type: 'commonjs', }); try { runCommand('node', [tscBin, '-p', 'tsconfig.json'], { cwd: fixturePath }); runCommand('node', ['index.js'], { cwd: fixturePath }); } finally { cleanupTempFixture(fixturePath); } }); [ ['ESM', 'module'], ['CommonJS', 'commonjs'], ].forEach(([name, packageType]) => { it(`emits usable ${name} declarations for forwarded request response generics`, () => { const sourcePath = path.join(repoRoot, 'tests/module/esm/tests/helpers/declaration-emit.ts'); const fixturePath = createTempFixture( suiteRoot, `declaration-emit-${packageType}`, sourcePath, declarationTsconfig, { type: packageType } ); try { runCommand('node', [tscBin, '-p', 'tsconfig.json'], { cwd: fixturePath }); fs.copyFileSync( path.join(repoRoot, 'tests/module/esm/tests/helpers/declaration-consumer.ts'), path.join(fixturePath, 'consumer.ts') ); fs.writeFileSync( path.join(fixturePath, 'tsconfig.consumer.json'), JSON.stringify(declarationConsumerTsconfig, null, 2) ); // The consumer imports from the output directory to exercise the emitted .d.ts. runCommand('node', [tscBin, '-p', 'tsconfig.consumer.json'], { cwd: fixturePath }); } finally { cleanupTempFixture(fixturePath); } }); }); }); axios-axios-aa54a5e/tests/module/esm/tests/typings.module.test.js000066400000000000000000000061751526660636600253500ustar00rootroot00000000000000import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, it } from 'vitest'; import { createTempFixture, cleanupTempFixture } from './helpers/fixture.js'; import { runCommand } from './helpers/run-command.js'; const suiteRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const repoRoot = path.resolve(suiteRoot, '../../..'); const tscBin = path.join(suiteRoot, 'node_modules', 'typescript', 'bin', 'tsc'); const tsconfig = { compilerOptions: { checkJs: true, module: 'node16', }, }; describe('module esm typings compatibility', () => { it('type-checks esm axios typings', () => { const sourcePath = path.join(repoRoot, 'tests/module/esm/tests/helpers/esm-index.ts'); const fixturePath = createTempFixture(suiteRoot, 'typings-esm', sourcePath, tsconfig, { type: 'module', }); try { runCommand('node', [tscBin, '--noEmit', '-p', 'tsconfig.json'], { cwd: fixturePath }); } finally { cleanupTempFixture(fixturePath); } }); it('type-checks additive esm public typings', () => { const sourcePath = path.join(repoRoot, 'tests/module/esm/tests/helpers/esm-added-types.ts'); const fixturePath = createTempFixture(suiteRoot, 'typings-esm-added', sourcePath, tsconfig, { type: 'module', }); try { runCommand('node', [tscBin, '--noEmit', '-p', 'tsconfig.json'], { cwd: fixturePath }); } finally { cleanupTempFixture(fixturePath); } }); it('type-checks fetch Response constructors with browser declarations', () => { const sourcePath = path.join(repoRoot, 'tests/module/esm/tests/helpers/esm-fetch-types.ts'); const fixturePath = createTempFixture( suiteRoot, 'fetch-types-browser', sourcePath, { compilerOptions: { module: 'node16', lib: ['ES2022', 'DOM'], types: ['node'], strict: true, skipLibCheck: false, }, }, { type: 'module' } ); try { runCommand('node', [tscBin, '--noEmit', '-p', 'tsconfig.json'], { cwd: fixturePath }); } finally { cleanupTempFixture(fixturePath); } }); // Node 20 types provide fetch globals without DOM. Run both package export // conditions here; the legacy CJS suite uses Node 12 types and DOM declarations. for (const mode of ['esm', 'cjs']) { it(`type-checks ${mode} imports and Response constructors without DOM declarations`, () => { const sourcePath = path.join( repoRoot, `tests/module/${mode}/tests/helpers/${mode}-fetch-types.ts` ); const fixturePath = createTempFixture( suiteRoot, `fetch-types-node-${mode}`, sourcePath, { compilerOptions: { module: 'node16', lib: ['ES2022'], types: ['node'], strict: true, skipLibCheck: false, }, }, { type: mode === 'esm' ? 'module' : 'commonjs' } ); try { runCommand('node', [tscBin, '--noEmit', '-p', 'tsconfig.json'], { cwd: fixturePath }); } finally { cleanupTempFixture(fixturePath); } }); } }); axios-axios-aa54a5e/tests/module/esm/vitest.config.js000066400000000000000000000005061526660636600230210ustar00rootroot00000000000000import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { projects: [ { test: { name: 'module', testTimeout: 60000, environment: 'node', include: ['tests/**/*.module.test.js'], setupFiles: [], }, }, ], }, }); axios-axios-aa54a5e/tests/setup/000077500000000000000000000000001526660636600167675ustar00rootroot00000000000000axios-axios-aa54a5e/tests/setup/adapters.js000066400000000000000000000004601526660636600211300ustar00rootroot00000000000000/** * Adapter that resolves without performing a request and echoes the resolved * config back on the response, so tests can assert on the outgoing headers. */ export const echoHeaders = async (config) => ({ data: null, status: 200, statusText: 'OK', headers: {}, config, request: {}, }); axios-axios-aa54a5e/tests/setup/browser.setup.js000066400000000000000000000006121526660636600221460ustar00rootroot00000000000000import { afterEach } from 'vitest'; import './nativeCookieStore.js'; // Keep the baseline browser suite on axios's synchronous document.cookie path. // Async tests opt in with an explicit mock or the saved native Cookie Store. Object.defineProperty(window, 'cookieStore', { configurable: true, writable: true, value: undefined, }); afterEach(() => { document.body.innerHTML = ''; }); axios-axios-aa54a5e/tests/setup/formBodyLengthCases.js000066400000000000000000000500541526660636600232330ustar00rootroot00000000000000import { describe, expect, it, vi } from 'vitest'; import platform from '../../lib/platform/index.js'; import axios from '../../index.js'; import createMultipartBody from '../../lib/core/createMultipartBody.js'; import formDataToBlob from '../../lib/helpers/formDataToBlob.js'; import estimateDataURLDecodedBytes from '../../lib/helpers/estimateDataURLDecodedBytes.js'; export default function formBodyLengthCases() { describe('fetch multipart body length', () => { class StreamingRequest extends Request { arrayBuffer() { throw new Error('Multipart measurement must not buffer the complete body'); } } const makeForm = () => { const form = new FormData(); form.append('message', 'café\nsecond line'); form.append('file', new Blob(['contents'], { type: 'text/plain' }), 'note.txt'); return form; }; it('uses each configured Blob constructor when reusing a fetch implementation', async () => { const created = []; class FirstBlob extends Blob { constructor(parts, options) { super(parts, options); created.push(this); } } class SecondBlob extends FirstBlob {} const envFetch = async (request) => { const form = await request.formData(); expect(form.get('message')).toBe('café\r\nsecond line'); expect(await form.get('file').text()).toBe('contents'); return new Response('ok'); }; for (const BlobConstructor of [FirstBlob, SecondBlob, FirstBlob]) { const before = created.length; const response = await axios.post('http://localhost/form', makeForm(), { adapter: 'fetch', maxBodyLength: 4096, env: { fetch: envFetch, Blob: BlobConstructor }, }); expect(response.data).toBe('ok'); expect(created.length).toBeGreaterThan(before); expect(created[created.length - 1].constructor).toBe(BlobConstructor); } }); [false, true].forEach((progress) => { it( 'uses a configured Blob without the global constructor with progress=' + progress, async () => { const NativeBlob = Blob; const form = makeForm(); let constructed = false; class CustomBlob extends NativeBlob { constructor(parts, options) { super(parts, options); constructed = true; } } vi.stubGlobal('Blob', undefined); try { const response = await axios.post('http://localhost/form', form, { adapter: 'fetch', maxBodyLength: 4096, onUploadProgress: progress ? () => {} : undefined, env: { Blob: CustomBlob, async fetch(request) { const decoded = await request.formData(); expect(decoded.get('message')).toBe('café\r\nsecond line'); expect(await decoded.get('file').text()).toBe('contents'); return new Response('ok'); }, }, }); expect(response.data).toBe('ok'); expect(constructed).toBe(true); } finally { vi.unstubAllGlobals(); } } ); }); it('falls back to the global Blob only when the configured value is undefined', async () => { const env = { Blob: undefined, async fetch(request) { expect((await request.formData()).get('message')).toBe('café\r\nsecond line'); return new Response('ok'); }, }; const config = { adapter: 'fetch', maxBodyLength: 4096, env }; expect((await axios.post('http://localhost/form', makeForm(), config)).data).toBe('ok'); env.Blob = null; await expect(axios.post('http://localhost/form', makeForm(), config)).rejects.toMatchObject({ code: 'ERR_NOT_SUPPORT', }); }); [false, true].forEach((progress) => { [undefined, 4096].forEach((limit) => { it(`preserves multipart content with progress=${progress} and limit=${limit}`, async () => { const events = []; const response = await axios.post('http://localhost/form', makeForm(), { adapter: 'fetch', headers: { 'Content-Type': 'multipart/form-data' }, maxBodyLength: limit, onUploadProgress: progress ? (event) => events.push(event) : undefined, env: { Request: StreamingRequest, async fetch(request) { const form = await request.formData(); expect(form.get('message')).toBe('café\r\nsecond line'); expect(form.get('file').name).toBe('note.txt'); expect(await form.get('file').text()).toBe('contents'); return new Response('ok'); }, }, }); expect(response.data).toBe('ok'); // Browsers without request streaming do not emit upload progress. if (events.length) { const last = events[events.length - 1]; expect(last.loaded).toBe(last.total); expect(last.total).toBeGreaterThan(20); } }); }); }); [false, true].forEach((progress) => { [undefined, 4096].forEach((limit) => { ['BoundaryMiXeD42', 'MiXeD boundary,+42'].forEach((boundary) => { it(`retains the explicit ${boundary} boundary with progress=${progress} and limit=${limit}`, async () => { const contentType = `multipart/form-data; boundary="${boundary}"`; const response = await axios.post('http://localhost/form', makeForm(), { adapter: 'fetch', headers: { 'Content-Type': contentType }, maxBodyLength: limit, onUploadProgress: progress ? () => {} : undefined, env: { async fetch(request) { expect(request.headers.get('content-type')).toBe(contentType); const body = await request.text(); expect(body).toBe( [ '--' + boundary, 'Content-Disposition: form-data; name="message"', '', 'café', 'second line', '--' + boundary, 'Content-Disposition: form-data; name="file"; filename="note.txt"', 'Content-Type: text/plain', '', 'contents', '--' + boundary + '--', '', ].join('\r\n') ); return new Response('ok'); }, }, }); expect(response.data).toBe('ok'); }); }); }); }); it('checks explicit boundary length as part of the exact body limit', async () => { const form = makeForm(); const boundary = 'MiXeD-12345'; const size = createMultipartBody( form, Blob, Infinity, () => new Error('Too long'), boundary ).size; let calls = 0; const options = { adapter: 'fetch', headers: { 'Content-Type': `multipart/form-data; boundary=${boundary}` }, env: { async fetch(request) { calls++; const decoded = await request.formData(); expect(await decoded.get('file').text()).toBe('contents'); return new Response('ok'); }, }, }; await expect( axios.post( 'http://localhost/form', form, Object.assign({}, options, { maxBodyLength: size - 1 }) ) ).rejects.toMatchObject({ code: 'ERR_BAD_REQUEST' }); expect(calls).toBe(0); expect( ( await axios.post( 'http://localhost/form', form, Object.assign({}, options, { maxBodyLength: size }) ) ).data ).toBe('ok'); expect(calls).toBe(1); }); ['', 'a'.repeat(71), 'trailing ', 'line\nbreak'].forEach((boundary) => { it('rejects invalid boundary ' + JSON.stringify(boundary), () => { expect(() => createMultipartBody(makeForm(), Blob, 4096, () => new Error('Too long'), boundary) ).toThrowError(expect.objectContaining({ code: 'ERR_BAD_OPTION_VALUE' })); }); }); const withoutPlatformRandom = (run) => { const descriptor = Object.getOwnPropertyDescriptor(platform, 'generateString'); Object.defineProperty(platform, 'generateString', { value: undefined, configurable: true }); try { run(); } finally { if (descriptor) Object.defineProperty(platform, 'generateString', descriptor); else delete platform.generateString; vi.unstubAllGlobals(); } }; it('serializes with supplied dependencies without selecting a random source', async () => { let body; withoutPlatformRandom(() => { vi.stubGlobal('crypto', undefined); const form = makeForm(); const limitError = new Error('Caller size limit'); body = formDataToBlob(form, Blob, Infinity, () => limitError, 'caller-boundary'); expect(() => formDataToBlob(form, Blob, body.size - 1, () => limitError, 'caller-boundary') ).toThrow(limitError); }); const decoded = await new Response(body).formData(); expect(decoded.get('message')).toBe('café\r\nsecond line'); expect(await decoded.get('file').text()).toBe('contents'); }); [ { input: 'Blob', code: 'ERR_NOT_SUPPORT' }, { input: 'boundary', code: 'ERR_BAD_OPTION_VALUE' }, { input: 'part size', code: 'ERR_BAD_REQUEST' }, ].forEach(({ input, code }) => { it('maps generic ' + input + ' failures to request errors in core', () => { const args = [makeForm(), Blob, Infinity, () => new Error('Too long'), 'caller-boundary']; if (input === 'Blob') args[1] = null; if (input === 'boundary') args[4] = ''; if (input === 'part size') args[0] = [['file', { size: NaN }]]; let error; try { formDataToBlob(...args); } catch (caught) { error = caught; } expect(error).toBeInstanceOf(TypeError); expect(error.code).toBeUndefined(); expect(error.isAxiosError).toBeUndefined(); expect(() => createMultipartBody(...args)).toThrowError( expect.objectContaining({ name: 'AxiosError', code, isAxiosError: true }) ); }); }); it('uses Web Crypto for generated boundaries when no platform generator is present', () => { withoutPlatformRandom(() => { let entropy = 17; const getRandomValues = vi.fn((bytes) => { bytes.fill(entropy); return bytes; }); vi.stubGlobal('crypto', { getRandomValues }); const generateType = () => { const body = createMultipartBody(makeForm(), Blob, 4096, () => new Error('Too long')); const match = /^multipart\/form-data; boundary=(axios-[a-z0-9'()+_,./:=?-]+)$/.exec( body.type ); expect(match).not.toBeNull(); expect(match[1].length).toBeLessThanOrEqual(70); return body.type; }; const first = generateType(); expect(generateType()).toBe(first); entropy = 34; expect(generateType()).not.toBe(first); expect(getRandomValues).toHaveBeenCalled(); }); }); it('reports unavailable random generation instead of choosing a fallback boundary', () => { withoutPlatformRandom(() => { vi.stubGlobal('crypto', undefined); expect(() => createMultipartBody(makeForm(), Blob, 4096, () => new Error('Too long')) ).toThrowError(expect.objectContaining({ code: 'ERR_NOT_SUPPORT' })); const explicit = createMultipartBody( makeForm(), Blob, 4096, () => new Error('Too long'), 'caller-boundary' ); expect(explicit.size).toBeGreaterThan(0); }); }); it.skipIf(typeof platform.generateString !== 'function')( 'retains the Node random source without global Web Crypto', () => { vi.stubGlobal('crypto', undefined); try { const first = createMultipartBody(makeForm(), Blob, 4096, () => new Error('Too long')); const second = createMultipartBody(makeForm(), Blob, 4096, () => new Error('Too long')); expect(first.type).toMatch(/^multipart\/form-data; boundary=axios-[a-f0-9]{36}$/); expect(second.type).not.toBe(first.type); } finally { vi.unstubAllGlobals(); } } ); [0, 16, 256].forEach((limit) => { it(`rejects multipart data above a ${limit} byte limit before dispatch`, async () => { let calls = 0; await expect( axios.post('http://localhost/form', makeForm(), { adapter: 'fetch', headers: { 'Content-Type': 'multipart/form-data' }, maxBodyLength: limit, env: { Request: StreamingRequest, fetch() { calls++; return Promise.resolve(new Response('unexpected')); }, }, }) ).rejects.toMatchObject({ code: 'ERR_BAD_REQUEST' }); expect(calls).toBe(0); }); }); [false, true].forEach((empty) => { it( 'uses an exact multipart limit for ' + (empty ? 'empty' : 'encoded') + ' fields', async () => { const form = new FormData(); if (!empty) { form.append('line\nname', 'café\n\r\r\n😀'); form.append('upload', new Blob(['bytes']), ''); } const blob = createMultipartBody(form, Blob, Infinity, () => new Error('Too long')); let constructions = 0; class CountedBlob extends Blob { constructor(parts, options) { constructions++; super(parts, options); } } expect(() => createMultipartBody(form, CountedBlob, blob.size - 1, () => new Error('Too long')) ).toThrow('Too long'); expect(constructions).toBe(0); const exact = createMultipartBody( form, CountedBlob, blob.size, () => new Error('Too long') ); expect(exact.size).toBe(blob.size); expect(constructions).toBe(1); const decoded = await new Response(exact).formData(); expect(Array.from(decoded.keys()).length).toBe(empty ? 0 : 2); if (!empty) expect(decoded.get('upload').name).toBe(''); } ); }); it('checks multipart size before native serialization or file reads', async () => { class SizedRequest extends StreamingRequest { constructor(url, init) { if (init && init.body instanceof FormData) { throw new Error('Unexpected native multipart serialization'); } super(url, init); } } const form = makeForm(); const file = form.get('file'); file.arrayBuffer = file.stream = () => { throw new Error('Unexpected file read'); }; await expect( axios.post('http://localhost/form', form, { adapter: 'fetch', headers: { 'Content-Type': 'multipart/form-data' }, maxBodyLength: 16, env: { Request: SizedRequest, fetch() { throw new Error('Unexpected dispatch'); }, }, }) ).rejects.toMatchObject({ code: 'ERR_BAD_REQUEST' }); const response = await axios.post('http://localhost/form', form, { adapter: 'fetch', maxBodyLength: 4096, env: { Request: SizedRequest, async fetch(request) { const decoded = await request.formData(); expect(await decoded.get('file').text()).toBe('contents'); return new Response('ok'); }, }, }); expect(response.data).toBe('ok'); }); it('passes a sized multipart Blob when Request is unavailable', async () => { class StreamingResponse extends Response { arrayBuffer() { throw new Error('Unexpected body buffering'); } } const response = await axios.post('http://localhost/form', makeForm(), { adapter: 'fetch', headers: { 'Content-Type': 'multipart/form-data' }, maxBodyLength: 4096, env: { Request: null, Response: StreamingResponse, fetch(_url, options) { expect(options.body instanceof Blob).toBe(true); expect(options.body.size).toBeLessThan(4096); return Promise.resolve(new Response('ok')); }, }, }); expect(response.data).toBe('ok'); }); it('keeps multipart data measurable without Request or Response constructors', async () => { const response = await axios.post('http://localhost/form', makeForm(), { adapter: 'fetch', maxBodyLength: 4096, env: { Request: null, Response: null, async fetch(_url, options) { const decoded = await new Response(options.body).formData(); expect(await decoded.get('file').text()).toBe('contents'); return new Response('ok'); }, }, }); expect(response.data).toBe('ok'); }); }); describe('fetch data URL byte estimates', () => { ['base64=x', 'BASE64=x', 'base64note=x', 'base64;note=x'].forEach((metadata) => { it('counts ' + metadata + ' as an ordinary text parameter', async () => { const url = 'data:text/plain;' + metadata + ',abcd'; expect(estimateDataURLDecodedBytes(url)).toBe(4); let calls = 0; await expect( axios.get(url, { adapter: 'fetch', maxContentLength: 3, env: { fetch() { calls++; return Promise.resolve(new Response('unexpected')); }, }, }) ).rejects.toMatchObject({ code: 'ERR_BAD_RESPONSE' }); expect(calls).toBe(0); expect((await axios.get(url, { adapter: 'fetch', maxContentLength: 4 })).data).toBe('abcd'); }); }); ['\t', '\n', '\r', ' \t\r\n '].forEach((whitespace) => { it('handles URL preprocessing before base64 for ' + JSON.stringify(whitespace), async () => { const url = 'data:text/plain;' + whitespace + 'base64,TQ=='; // URL runtimes either remove these characters or percent-encode them. // Match the resulting native Fetch body in either case. const nativeBody = await (await fetch(url)).text(); expect(['M', 'TQ==']).toContain(nativeBody); expect(estimateDataURLDecodedBytes(new URL(url).href)).toBe(nativeBody.length); expect( (await axios.get(url, { adapter: 'fetch', maxContentLength: nativeBody.length })).data ).toBe(nativeBody); await expect( axios.get(url, { adapter: 'fetch', maxContentLength: nativeBody.length - 1 }) ).rejects.toMatchObject({ code: 'ERR_BAD_RESPONSE' }); }); }); ['\f', '\v', '\u00a0'].forEach((whitespace) => { it( 'keeps non-marker whitespace as MIME metadata for ' + JSON.stringify(whitespace), async () => { const url = 'data:text/plain;' + whitespace + 'base64,TQ=='; expect(estimateDataURLDecodedBytes(new URL(url).href)).toBe(4); expect((await axios.get(url, { adapter: 'fetch', maxContentLength: 4 })).data).toBe( 'TQ==' ); await expect( axios.get(url, { adapter: 'fetch', maxContentLength: 1 }) ).rejects.toMatchObject({ code: 'ERR_BAD_RESPONSE' }); } ); }); ['base64', 'BASE64', ' BaSe64 '].forEach((encoding) => { it('preserves the native ' + encoding + ' encoding suffix', async () => { const url = 'data:text/plain;' + encoding + ',TQ=='; expect(estimateDataURLDecodedBytes(url)).toBe(1); expect((await axios.get(url, { adapter: 'fetch', maxContentLength: 1 })).data).toBe('M'); }); }); }); } axios-axios-aa54a5e/tests/setup/nativeCookieStore.js000066400000000000000000000002121526660636600227550ustar00rootroot00000000000000// Capture the capability before browser.setup.js installs the synchronous baseline. export const nativeCookieStore = window.cookieStore; axios-axios-aa54a5e/tests/setup/runModuleInChildProcess.js000066400000000000000000000010151526660636600240660ustar00rootroot00000000000000import { execFile } from 'child_process'; export function runModuleInChildProcess(source, options) { return new Promise(function (resolve, reject) { execFile( process.execPath, ['--input-type=module', '-e', source], Object.assign({ timeout: 3000, killSignal: 'SIGKILL' }, options), function (error, stdout, stderr) { if (error) { error.stdout = stdout; error.stderr = stderr; return reject(error); } resolve(stdout); } ); }); } axios-axios-aa54a5e/tests/setup/server.js000066400000000000000000000164501526660636600206410ustar00rootroot00000000000000import http from 'http'; import http2 from 'http2'; import stream from 'stream'; import getStream, { getStreamAsBuffer } from 'get-stream'; import { Throttle } from 'stream-throttle'; import { IncomingForm } from 'formidable'; import selfsigned from 'selfsigned'; export const SERVER_HANDLER_STREAM_ECHO = (req, res) => req.pipe(res); export const setTimeoutAsync = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); const certificatePromise = selfsigned.generate(null, { keySize: 2048 }); const trackedServers = new Set(); const untrackServer = (server) => { trackedServers.delete(server); }; export const startHTTPServer = async (handlerOrOptions, options) => { const certificate = await certificatePromise; const { handler, useBuffering = false, rate = undefined, // Default to 0 so the OS assigns a free ephemeral port. Tests that need // a deterministic port can still pass one explicitly. Sharing a fixed // port across many tests creates TIME_WAIT / pool-reuse races that // surface as EPIPE on the client under CI runner load. port = 0, keepAlive = 1000, useHTTP2, key = certificate.private, cert = certificate.cert, } = Object.assign( typeof handlerOrOptions === 'function' ? { handler: handlerOrOptions, } : handlerOrOptions || {}, options ); return new Promise((resolve, reject) => { const serverHandler = handler || async function (req, res) { try { req.headers['content-length'] && res.setHeader('content-length', req.headers['content-length']); let dataStream = req; if (useBuffering) { dataStream = stream.Readable.from(await getStream(req)); } const streams = [dataStream]; if (rate) { streams.push(new Throttle({ rate })); } streams.push(res); stream.pipeline(streams, (err) => { err && console.log('Server warning: ' + err.message); }); } catch (err) { console.warn('HTTP server error:', err); } }; const server = useHTTP2 ? http2.createSecureServer({ key, cert }, serverHandler) : http.createServer(serverHandler); const sessions = new Set(); if (useHTTP2) { server.on('session', (session) => { sessions.add(session); session.once('close', () => { sessions.delete(session); }); }); server.closeAllSessions = () => { for (const session of sessions) { session.destroy(); } }; } else { server.keepAliveTimeout = keepAlive; } server.listen(port, function (err) { if (err) { reject(err); return; } trackedServers.add(this); resolve(this); }); }); }; export const stopHTTPServer = async (server, timeout = 10000) => { if (!server) return; // Try a graceful close first so in-flight requests can finish writing and // clients see clean FINs instead of RSTs. Forcefully tearing down sockets // up-front (closeAllConnections) is what produces dangling RSTs that the // next test on the same port can observe as EPIPE on its client write. // Force-close only after a short grace period. const closed = new Promise((resolve) => server.close(resolve)); const grace = Math.min(2000, Math.max(0, timeout / 2)); const winner = await Promise.race([ closed.then(() => 'graceful'), setTimeoutAsync(grace).then(() => 'grace_elapsed'), ]); if (winner === 'grace_elapsed') { if (typeof server.closeAllConnections === 'function') { server.closeAllConnections(); } if (typeof server.closeAllSessions === 'function') { server.closeAllSessions(); } await Promise.race([closed, setTimeoutAsync(timeout - grace)]); } untrackServer(server); }; export const stopAllTrackedHTTPServers = async (timeout = 10000) => { const servers = Array.from(trackedServers); await Promise.all(servers.map((server) => stopHTTPServer(server, timeout))); }; export const handleFormData = (req) => { return new Promise((resolve, reject) => { const form = new IncomingForm(); form.parse(req, (err, fields, files) => { if (err) { // Drain any unread bytes so the kernel doesn't send an RST when the // server closes the response. An unread request buffer is what causes // the client write side to surface EPIPE on a subsequent test. if (typeof req.resume === 'function') req.resume(); return reject(err); } resolve({ fields, files }); }); }); }; export const nodeVersion = process.versions.node.split('.').map((v) => parseInt(v, 10)); export const generateReadable = (length = 1024 * 1024, chunkSize = 10 * 1024, sleep = 50) => { return stream.Readable.from( (async function* () { let dataLength = 0; while (dataLength < length) { const leftBytes = length - dataLength; const chunk = Buffer.alloc(leftBytes > chunkSize ? chunkSize : leftBytes); dataLength += chunk.length; yield chunk; if (sleep) { await setTimeoutAsync(sleep); } } })() ); }; export const makeReadableStream = (chunk = 'chunk', n = 10, timeout = 100) => { return new ReadableStream( { async pull(controller) { await setTimeoutAsync(timeout); n-- ? controller.enqueue(chunk) : controller.close(); }, }, { highWaterMark: 1, } ); }; export const makeEchoStream = (echo) => new WritableStream({ write(chunk) { echo && console.log('Echo chunk', chunk); }, }); export const startTestServer = async (port) => { const handler = async (req) => { const parsed = new URL(req.url, `http://localhost:${port}`); const params = Object.fromEntries(parsed.searchParams); const response = { url: req.url, pathname: parsed.pathname, params, method: req.method, headers: req.headers, }; const contentType = req.headers['content-type'] || ''; const { delay = 0 } = params; if (+delay) { await setTimeoutAsync(+delay); } switch (parsed.pathname.replace(/\/$/, '')) { case '/echo/json': default: if (contentType.startsWith('multipart/')) { const { fields, files } = await handleFormData(req); response.form = fields; response.files = files; } else { response.body = (await getStreamAsBuffer(req)).toString('hex'); } return { body: response, }; } }; return await startHTTPServer( (req, res) => { res.setHeader('Access-Control-Allow-Origin', `*`); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', '*'); res.setHeader('Access-Control-Max-Age', '86400'); if (req.method === 'OPTIONS') { res.writeHead(204); res.end(); return; } Promise.resolve(handler(req, res)).then((result) => { const { status = 200, headers = {}, body } = result || {}; res.statusCode = status; Object.entries(headers).forEach(([header, value]) => { res.setHeader(header, value); }); res.end(JSON.stringify(body, null, 2)); }); }, { port } ); }; axios-axios-aa54a5e/tests/smoke/000077500000000000000000000000001526660636600167455ustar00rootroot00000000000000axios-axios-aa54a5e/tests/smoke/bun/000077500000000000000000000000001526660636600175315ustar00rootroot00000000000000axios-axios-aa54a5e/tests/smoke/bun/bun.lock000066400000000000000000000023011526660636600211630ustar00rootroot00000000000000{ "lockfileVersion": 1, "configVersion": 1, "workspaces": { "": { "name": "@axios/bun-smoke-tests", "devDependencies": { "@types/bun": "latest", }, "peerDependencies": { "typescript": "^5", }, }, }, "packages": { "@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="], "@types/node": ["@types/node@25.5.2", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg=="], "bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="], "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="], } } axios-axios-aa54a5e/tests/smoke/bun/package.json000066400000000000000000000002711526660636600220170ustar00rootroot00000000000000{ "name": "@axios/bun-smoke-tests", "type": "module", "private": true, "devDependencies": { "@types/bun": "latest" }, "peerDependencies": { "typescript": "^5" } } axios-axios-aa54a5e/tests/smoke/bun/tests/000077500000000000000000000000001526660636600206735ustar00rootroot00000000000000axios-axios-aa54a5e/tests/smoke/bun/tests/cancel.smoke.test.ts000066400000000000000000000041661526660636600245720ustar00rootroot00000000000000import { describe, expect, test } from 'bun:test'; import axios from 'axios'; const env = (fetch: typeof globalThis.fetch) => ({ fetch, Request, Response, }); describe('cancellation', () => { test('pre-aborted AbortController cancels before fetch is called', async () => { let fetchCallCount = 0; const fetch = async () => { fetchCallCount += 1; return new Response(JSON.stringify({ ok: true }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); }; const controller = new AbortController(); controller.abort(); const err = await axios .get('https://example.com/cancel', { adapter: 'fetch', signal: controller.signal, env: env(fetch), }) .catch((e: any) => e); expect(axios.isCancel(err)).toBe(true); expect(err.code).toBe('ERR_CANCELED'); expect(fetchCallCount).toBe(0); }); test('in-flight AbortController abort cancels the request', async () => { const fetch = (_input: unknown, init?: RequestInit) => new Promise((_resolve, reject) => { const abortError = () => reject(new DOMException('The operation was aborted', 'AbortError')); const timeout = setTimeout(abortError, 20); if (init?.signal) { if (init.signal.aborted) { clearTimeout(timeout); abortError(); return; } init.signal.addEventListener( 'abort', () => { clearTimeout(timeout); abortError(); }, { once: true } ); } }); const controller = new AbortController(); const request = axios.get('https://example.com/in-flight', { adapter: 'fetch', signal: controller.signal, env: env(fetch), }); controller.abort(); const err = await request.catch((e: any) => e); expect(axios.isCancel(err)).toBe(true); expect(err.code).toBe('ERR_CANCELED'); }); test('axios.isCancel returns false for a plain Error', () => { expect(axios.isCancel(new Error('random'))).toBe(false); }); }); axios-axios-aa54a5e/tests/smoke/bun/tests/error.smoke.test.ts000066400000000000000000000015701526660636600244720ustar00rootroot00000000000000import { describe, expect, test } from 'bun:test'; import axios from 'axios'; const env = (fetch: typeof globalThis.fetch) => ({ fetch, Request, Response, }); describe('errors', () => { test('non-2xx response rejects with AxiosError and status 404', async () => { const fetch = async () => new Response(JSON.stringify({ error: 'missing' }), { status: 404, statusText: 'Not Found', headers: { 'Content-Type': 'application/json' }, }); const err = await axios .get('https://example.com/missing', { adapter: 'fetch', env: env(fetch), }) .catch((e: any) => e); expect(axios.isAxiosError(err)).toBe(true); expect(err.response.status).toBe(404); }); test('axios.isAxiosError returns false for a plain Error', () => { expect(axios.isAxiosError(new Error('plain'))).toBe(false); }); }); axios-axios-aa54a5e/tests/smoke/bun/tests/fetch.smoke.test.ts000066400000000000000000000064161526660636600244360ustar00rootroot00000000000000import { describe, expect, test } from 'bun:test'; import axios from 'axios'; const createFetchMock = ( responseFactory?: (input: unknown, init: RequestInit) => Response | Promise ) => { const calls: Array<{ input: unknown; init: RequestInit }> = []; const mockFetch = async (input: unknown, init: RequestInit = {}) => { calls.push({ input, init }); if (responseFactory) { return responseFactory(input, init); } return new Response(JSON.stringify({ ok: true }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); }; return { mockFetch, getCalls: () => calls, }; }; const getRequestMeta = async (input: unknown, init: RequestInit = {}) => { const request = input instanceof Request ? input : new Request(input as string, init); return { url: request.url, method: request.method, body: request.method === 'GET' || request.method === 'HEAD' ? undefined : await request.clone().text(), }; }; const env = (fetch: typeof globalThis.fetch) => ({ fetch, Request, Response, }); describe('fetch adapter', () => { test('GET resolves JSON response via fetch adapter', async () => { const { mockFetch, getCalls } = createFetchMock(); const response = await axios.get('https://example.com/users', { adapter: 'fetch', env: env(mockFetch), }); expect(response.status).toBe(200); expect(response.data).toEqual({ ok: true }); expect(getCalls()).toHaveLength(1); }); test('POST serializes JSON body via fetch adapter', async () => { const { mockFetch, getCalls } = createFetchMock(); await axios.post( 'https://example.com/items', { name: 'widget' }, { adapter: 'fetch', env: env(mockFetch), } ); const { input, init } = getCalls()[0]; const meta = await getRequestMeta(input, init); expect(meta.body).toBe(JSON.stringify({ name: 'widget' })); }); test('HTTP methods are forwarded correctly', async () => { const run = async ( method: 'delete' | 'head' | 'options' | 'put' | 'patch', expected: string ) => { const { mockFetch, getCalls } = createFetchMock(); if (method === 'put' || method === 'patch') { await axios[method]( 'https://example.com/items', { name: 'widget' }, { adapter: 'fetch', env: env(mockFetch), } ); } else { await axios[method]('https://example.com/items', { adapter: 'fetch', env: env(mockFetch), }); } const { input, init } = getCalls()[0]; const meta = await getRequestMeta(input, init); expect(meta.method).toBe(expected); }; await run('delete', 'DELETE'); await run('head', 'HEAD'); await run('options', 'OPTIONS'); await run('put', 'PUT'); await run('patch', 'PATCH'); }); test('full URL is preserved in the fetch request', async () => { const { mockFetch, getCalls } = createFetchMock(); await axios.get('https://example.com/users', { adapter: 'fetch', env: env(mockFetch), }); const { input, init } = getCalls()[0]; const meta = await getRequestMeta(input, init); expect(meta.url).toBe('https://example.com/users'); }); }); axios-axios-aa54a5e/tests/smoke/bun/tests/formData.smoke.test.ts000066400000000000000000000070421526660636600250760ustar00rootroot00000000000000import { describe, expect, test } from 'bun:test'; import { PassThrough, Writable } from 'node:stream'; import FormDataPackage from 'form-data'; import axios from 'axios'; const createTransportMock = ( responseFactory?: (body: Buffer, options: Record) => Record ) => { const transport = { request(options: Record, onResponse: (res: PassThrough) => void) { const chunks: Buffer[] = []; const req = new Writable({ write(chunk, _encoding, callback) { chunks.push(Buffer.from(chunk)); callback(); }, }) as Writable & Record; req.destroyed = false; req.setTimeout = () => {}; req.write = req.write.bind(req); req.destroy = () => { req.destroyed = true; return req; }; req.close = req.destroy; const originalEnd = req.end.bind(req); req.end = (...args: unknown[]) => { originalEnd(...(args as Parameters)); const body = Buffer.concat(chunks); const response = responseFactory ? responseFactory(body, options) : {}; const res = new PassThrough() as PassThrough & Record; res.statusCode = response.statusCode ?? 200; res.statusMessage = response.statusMessage ?? 'OK'; res.headers = response.headers ?? { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end(response.body ?? JSON.stringify({ ok: true })); return req; }; return req; }, }; return { transport }; }; const bodyAsUtf8 = (value: unknown) => { return Buffer.isBuffer(value) ? value.toString('utf8') : String(value); }; describe('form data', () => { test('native Bun FormData body produces multipart/form-data content-type', async () => { const form = new FormData(); form.append('username', 'janedoe'); form.append('role', 'admin'); const { transport } = createTransportMock((body, options) => ({ body: JSON.stringify({ contentType: options.headers && (options.headers['Content-Type'] || options.headers['content-type']), payload: bodyAsUtf8(body), }), })); const response = await axios.post('http://example.com/form', form, { adapter: 'http', proxy: false, transport, }); expect(response.data.contentType).toContain('multipart/form-data'); expect(response.data.payload).toContain('name="username"'); expect(response.data.payload).toContain('janedoe'); expect(response.data.payload).toContain('name="role"'); expect(response.data.payload).toContain('admin'); }); test('npm form-data package instance is serialized correctly', async () => { const form = new FormDataPackage(); form.append('project', 'axios'); form.append('mode', 'compat'); const { transport } = createTransportMock((body, options) => ({ body: JSON.stringify({ contentType: options.headers && (options.headers['Content-Type'] || options.headers['content-type']), payload: bodyAsUtf8(body), }), })); const response = await axios.post('http://example.com/npm-form-data', form as any, { adapter: 'http', proxy: false, transport, }); expect(response.data.contentType).toContain('multipart/form-data'); expect(response.data.payload).toContain('name="project"'); expect(response.data.payload).toContain('axios'); expect(response.data.payload).toContain('name="mode"'); expect(response.data.payload).toContain('compat'); }); }); axios-axios-aa54a5e/tests/smoke/bun/tests/headers.smoke.test.ts000066400000000000000000000030211526660636600247450ustar00rootroot00000000000000import { describe, expect, test } from 'bun:test'; import axios from 'axios'; const createFetchCapture = () => { const calls: Request[] = []; const fetch = async (input: unknown, init?: RequestInit) => { const request = input instanceof Request ? input : new Request(input as string, init); calls.push(request); return new Response(JSON.stringify({ ok: true }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); }; return { fetch, getCalls: () => calls, }; }; const env = (fetch: typeof globalThis.fetch) => ({ fetch, Request, Response, }); describe('headers', () => { test('custom X-Custom header is forwarded to mock fetch (case-insensitive)', async () => { const { fetch, getCalls } = createFetchCapture(); await axios.get('https://example.com/custom-headers', { adapter: 'fetch', headers: { 'X-Custom': 'trace-123', }, env: env(fetch), }); const request = getCalls()[0]; expect(request.headers.get('x-custom')).toBe('trace-123'); }); test('content-type application/json is inferred for JSON POST body', async () => { const { fetch, getCalls } = createFetchCapture(); await axios.post( 'https://example.com/post-json', { name: 'widget' }, { adapter: 'fetch', env: env(fetch), } ); const request = getCalls()[0]; const contentType = request.headers.get('content-type') || ''; expect(contentType.includes('application/json')).toBe(true); }); }); axios-axios-aa54a5e/tests/smoke/bun/tests/http.smoke.test.ts000066400000000000000000000055101526660636600243160ustar00rootroot00000000000000import { describe, expect, test } from 'bun:test'; import { EventEmitter } from 'node:events'; import { PassThrough } from 'node:stream'; import axios from 'axios'; type TransportCall = { options: Record; body: Buffer; }; const createTransportMock = ( responseFactory?: (body: Buffer, options: Record) => Record ) => { const calls: TransportCall[] = []; const transport = { request(options: Record, onResponse: (res: PassThrough) => void) { const req = new EventEmitter() as Record; const chunks: Buffer[] = []; req.destroyed = false; req.setTimeout = () => {}; req.write = (chunk?: unknown) => { if (chunk !== undefined) { chunks.push(Buffer.from(chunk as string)); } return true; }; req.destroy = () => { req.destroyed = true; }; req.close = req.destroy; req.end = (chunk?: unknown) => { if (chunk !== undefined) { chunks.push(Buffer.from(chunk as string)); } const body = Buffer.concat(chunks); calls.push({ options, body }); const response = responseFactory ? responseFactory(body, options) : {}; const res = new PassThrough() as PassThrough & Record; res.statusCode = response.statusCode ?? 200; res.statusMessage = response.statusMessage ?? 'OK'; res.headers = response.headers ?? { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end(response.body ?? JSON.stringify({ ok: true })); }; return req; }, }; return { transport, getCalls: () => calls, }; }; describe('http adapter', () => { test('GET via http adapter returns mocked response data', async () => { const { transport, getCalls } = createTransportMock(); const response = await axios.get('http://example.com/users', { adapter: 'http', proxy: false, transport, }); expect(response.status).toBe(200); expect(response.data).toEqual({ ok: true }); expect(getCalls()).toHaveLength(1); }); test('POST sends JSON-serialized body via http adapter', async () => { const { transport, getCalls } = createTransportMock(); await axios.post( 'http://example.com/items', { name: 'widget' }, { adapter: 'http', proxy: false, transport, } ); const { body } = getCalls()[0]; expect(body.toString('utf8')).toBe(JSON.stringify({ name: 'widget' })); }); test('default adapter selection in Bun routes through http adapter', async () => { const { transport, getCalls } = createTransportMock(); await axios.get('http://example.com/default-adapter', { proxy: false, transport, }); expect(getCalls()).toHaveLength(1); }); }); axios-axios-aa54a5e/tests/smoke/bun/tests/import.smoke.test.ts000066400000000000000000000013051526660636600246470ustar00rootroot00000000000000import { describe, expect, test } from 'bun:test'; import axios from 'axios'; describe('Bun importing', () => { test('default export is callable', () => { expect(typeof axios).toBe('function'); }); test('named exports are present', async () => { const exports = (await import('axios')) as Record; expect(typeof (exports.axios ?? exports.default)).toBe('function'); expect(typeof (exports.create ?? exports.default.create)).toBe('function'); expect(typeof exports.isCancel).toBe('function'); expect(typeof exports.isAxiosError).toBe('function'); expect(typeof exports.CancelToken).toBe('function'); expect(typeof exports.VERSION).toBe('string'); }); }); axios-axios-aa54a5e/tests/smoke/bun/tests/interceptors.smoke.test.ts000066400000000000000000000033171526660636600260630ustar00rootroot00000000000000import { describe, expect, test } from 'bun:test'; import axios from 'axios'; const createFetchCapture = () => { const calls: Request[] = []; const fetch = async (input: unknown, init?: RequestInit) => { const request = input instanceof Request ? input : new Request(input as string, init); calls.push(request); return new Response(JSON.stringify({ value: 'ok' }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); }; return { fetch, getCalls: () => calls, }; }; const env = (fetch: typeof globalThis.fetch) => ({ fetch, Request, Response, }); describe('interceptors', () => { test('request interceptor header is forwarded to fetch', async () => { const { fetch, getCalls } = createFetchCapture(); const client = axios.create({ adapter: 'fetch', env: env(fetch), }); client.interceptors.request.use((config: any) => { config.headers = config.headers || {}; config.headers['X-Added'] = 'yes'; return config; }); await client.get('https://example.com/interceptor-request'); expect(getCalls()).toHaveLength(1); expect(getCalls()[0].headers.get('x-added')).toBe('yes'); }); test('response interceptor transform is reflected in resolved value', async () => { const { fetch } = createFetchCapture(); const client = axios.create({ adapter: 'fetch', env: env(fetch), }); client.interceptors.response.use((response: any) => { response.data.value = String(response.data.value).toUpperCase(); return response; }); const response = await client.get('https://example.com/interceptor-response'); expect(response.data).toEqual({ value: 'OK' }); }); }); axios-axios-aa54a5e/tests/smoke/bun/tests/progress.smoke.test.ts000066400000000000000000000022551526660636600252060ustar00rootroot00000000000000import { describe, expect, test } from 'bun:test'; import axios from 'axios'; const env = (fetch: typeof globalThis.fetch) => ({ fetch, Request, Response, }); describe('progress', () => { test('onDownloadProgress fires with loaded > 0 for streaming fetch response', async () => { const samples: number[] = []; const fetch = async () => { const stream = new ReadableStream({ start(controller) { controller.enqueue(new TextEncoder().encode('ab')); controller.enqueue(new TextEncoder().encode('cd')); controller.close(); }, }); return new Response(stream, { status: 200, headers: { 'Content-Type': 'text/plain', 'Content-Length': '4', }, }); }; const response = await axios.get('https://example.com/download', { adapter: 'fetch', responseType: 'text', onDownloadProgress: ({ loaded }: { loaded: number }) => { samples.push(loaded); }, env: env(fetch), }); expect(response.data).toBe('abcd'); expect(samples.length).toBeGreaterThan(0); expect(samples.some((loaded) => loaded > 0)).toBe(true); }); }); axios-axios-aa54a5e/tests/smoke/bun/tests/timeout.smoke.test.ts000066400000000000000000000024371526660636600250320ustar00rootroot00000000000000import { describe, expect, test } from 'bun:test'; import axios from 'axios'; const env = (fetch: typeof globalThis.fetch) => ({ fetch, Request, Response, }); const createAbortedError = () => { const error = new Error('The operation was aborted') as Error & { code?: string; name: string }; error.name = 'AbortError'; error.code = 'ECONNABORTED'; return error; }; describe('timeout', () => { test('timeout: 50 with never-resolving fetch mock rejects with ETIMEDOUT', async () => { const fetch = (input: unknown, init?: RequestInit) => new Promise((_resolve, reject) => { const signal = init?.signal || (input instanceof Request ? input.signal : undefined); if (signal) { if (signal.aborted) { reject(createAbortedError()); return; } signal.addEventListener( 'abort', () => { reject(createAbortedError()); }, { once: true } ); } }); const err = await axios .get('https://example.com/timeout', { adapter: 'fetch', timeout: 50, env: env(fetch), }) .catch((e: any) => e); expect(axios.isAxiosError(err)).toBe(true); expect(err.code).toBe('ETIMEDOUT'); }); }); axios-axios-aa54a5e/tests/smoke/cjs/000077500000000000000000000000001526660636600175245ustar00rootroot00000000000000axios-axios-aa54a5e/tests/smoke/cjs/package-lock.json000066400000000000000000001125631526660636600227500ustar00rootroot00000000000000{ "name": "@axios/cjs-smoke-tests", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@axios/cjs-smoke-tests", "version": "1.0.0", "license": "MIT", "devDependencies": { "chai": "4.5.0", "mocha": "11.8.0" } }, "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/@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/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/ansi-styles": { "version": "4.3.0", "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/argparse": { "version": "2.0.1", "dev": true, "license": "Python-2.0" }, "node_modules/assertion-error": { "version": "1.1.0", "dev": true, "license": "MIT", "engines": { "node": "*" } }, "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/brace-expansion": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/browser-stdout": { "version": "1.3.1", "dev": true, "license": "ISC" }, "node_modules/camelcase": { "version": "6.3.0", "dev": true, "license": "MIT", "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/chai": { "version": "4.5.0", "dev": true, "license": "MIT", "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.3", "deep-eql": "^4.1.3", "get-func-name": "^2.0.2", "loupe": "^2.3.6", "pathval": "^1.1.1", "type-detect": "^4.1.0" }, "engines": { "node": ">=4" } }, "node_modules/chalk": { "version": "4.1.2", "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/chalk/node_modules/supports-color": { "version": "7.2.0", "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/check-error": { "version": "1.0.3", "dev": true, "license": "MIT", "dependencies": { "get-func-name": "^2.0.2" }, "engines": { "node": "*" } }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, "license": "MIT", "dependencies": { "readdirp": "^4.0.1" }, "engines": { "node": ">= 14.16.0" }, "funding": { "url": "https://paulmillr.com/funding/" } }, "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/cliui/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/cliui/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/cliui/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/cliui/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/cliui/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/color-convert": { "version": "2.0.1", "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, "engines": { "node": ">=7.0.0" } }, "node_modules/color-name": { "version": "1.1.4", "dev": true, "license": "MIT" }, "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/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/decamelize": { "version": "4.0.0", "dev": true, "license": "MIT", "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/deep-eql": { "version": "4.1.4", "dev": true, "license": "MIT", "dependencies": { "type-detect": "^4.0.0" }, "engines": { "node": ">=6" } }, "node_modules/diff": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, "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/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/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", "dev": true, "license": "MIT", "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/find-up": { "version": "5.0.0", "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": { "version": "5.0.2", "dev": true, "license": "BSD-3-Clause", "bin": { "flat": "cli.js" } }, "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/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-func-name": { "version": "2.0.2", "dev": true, "license": "MIT", "engines": { "node": "*" } }, "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/has-flag": { "version": "4.0.0", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/he": { "version": "1.2.0", "dev": true, "license": "MIT", "bin": { "he": "bin/he" } }, "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-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-plain-obj": { "version": "2.1.0", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/is-unicode-supported": { "version": "0.1.0", "dev": true, "license": "MIT", "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "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/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/js-yaml": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "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/locate-path": { "version": "6.0.0", "dev": true, "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/log-symbols": { "version": "4.1.0", "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/loupe": { "version": "2.3.7", "dev": true, "license": "MIT", "dependencies": { "get-func-name": "^2.0.1" } }, "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/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/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/mocha": { "version": "11.8.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.8.0.tgz", "integrity": "sha512-VyCeUdGN3A9lmCTTgG4yuvY9ixxaDk+xt2R/7/+1AP6EqNG+G9OKkzBwhVtVYoNX8YsxNSgAl8mOv3IAeOpFbw==", "dev": true, "license": "MIT", "dependencies": { "browser-stdout": "^1.3.1", "chokidar": "^4.0.1", "debug": "^4.3.5", "diff": "^7.0.0", "escape-string-regexp": "^4.0.0", "find-up": "^5.0.0", "glob": "^10.4.5", "he": "^1.2.0", "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", "minimatch": "^9.0.5", "ms": "^2.1.3", "picocolors": "^1.1.1", "serialize-javascript": "^6.0.2", "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", "workerpool": "^9.2.0", "yargs": "^17.7.2", "yargs-parser": "^21.1.1", "yargs-unparser": "^2.0.0" }, "bin": { "_mocha": "bin/_mocha", "mocha": "bin/mocha.js" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/ms": { "version": "2.1.3", "dev": true, "license": "MIT" }, "node_modules/p-limit": { "version": "3.1.0", "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", "dev": true, "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "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/path-exists": { "version": "4.0.0", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "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": "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/pathval": { "version": "1.1.1", "dev": true, "license": "MIT", "engines": { "node": "*" } }, "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/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" } }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, "license": "MIT", "engines": { "node": ">= 14.18.0" }, "funding": { "type": "individual", "url": "https://paulmillr.com/funding/" } }, "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/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/serialize-javascript": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } }, "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": "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/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/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/string-width-cjs/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/string-width-cjs/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/string-width-cjs/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": { "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/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-ansi-cjs/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/strip-json-comments": { "version": "3.1.1", "dev": true, "license": "MIT", "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/supports-color": { "version": "8.1.1", "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/type-detect": { "version": "4.1.0", "dev": true, "license": "MIT", "engines": { "node": ">=4" } }, "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/workerpool": { "version": "9.3.4", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", "dev": true, "license": "Apache-2.0" }, "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/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi/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/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/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/yargs-unparser": { "version": "2.0.0", "dev": true, "license": "MIT", "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", "flat": "^5.0.2", "is-plain-obj": "^2.1.0" }, "engines": { "node": ">=10" } }, "node_modules/yargs/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/yargs/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/yargs/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/yargs/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/yocto-queue": { "version": "0.1.0", "dev": true, "license": "MIT", "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } } } } axios-axios-aa54a5e/tests/smoke/cjs/package.json000066400000000000000000000005321526660636600220120ustar00rootroot00000000000000{ "name": "@axios/cjs-smoke-tests", "version": "1.0.0", "description": "CJS smoke tests for axios", "private": true, "scripts": { "test:smoke:cjs:mocha": "mocha \"tests/**/*.smoke.test.cjs\"" }, "keywords": [], "author": "axios team", "license": "MIT", "devDependencies": { "mocha": "11.8.0", "chai": "4.5.0" } }axios-axios-aa54a5e/tests/smoke/cjs/tests/000077500000000000000000000000001526660636600206665ustar00rootroot00000000000000axios-axios-aa54a5e/tests/smoke/cjs/tests/auth.smoke.test.cjs000066400000000000000000000065741526660636600244370ustar00rootroot00000000000000const http = require('http'); const axios = require('axios'); const { describe, it, afterEach } = require('mocha'); const { expect } = require('chai'); const startServer = (handler) => { return new Promise((resolve) => { const server = http.createServer(handler); server.listen(0, '127.0.0.1', () => { resolve(server); }); }); }; const stopServer = (server) => { if (!server || !server.listening) { return Promise.resolve(); } return new Promise((resolve, reject) => { server.close((error) => { if (error) { reject(error); return; } resolve(); }); }); }; describe('auth compat (dist export only)', () => { let server; afterEach(async () => { await stopServer(server); server = undefined; }); const requestWithConfig = async (config) => { server = await startServer((req, res) => { res.setHeader('Content-Type', 'text/plain'); res.end(req.headers.authorization || ''); }); const { port } = server.address(); return axios.get( `http://127.0.0.1:${port}/`, Object.assign( { proxy: false, }, config || {} ) ); }; it('sets Basic Authorization header from auth credentials', async () => { const response = await requestWithConfig({ auth: { username: 'janedoe', password: 's00pers3cret', }, }); const expected = `Basic ${Buffer.from('janedoe:s00pers3cret', 'utf8').toString('base64')}`; expect(response.data).to.equal(expected); }); it('supports auth without password', async () => { const response = await requestWithConfig({ auth: { username: 'Aladdin', }, }); const expected = `Basic ${Buffer.from('Aladdin:', 'utf8').toString('base64')}`; expect(response.data).to.equal(expected); }); it('overwrites an existing Authorization header when auth is provided', async () => { const response = await requestWithConfig({ headers: { Authorization: 'Bearer token-123', }, auth: { username: 'foo', password: 'bar', }, }); const expected = `Basic ${Buffer.from('foo:bar', 'utf8').toString('base64')}`; expect(response.data).to.equal(expected); }); it('uses URL credentials when auth config is not provided (node adapter behavior)', async () => { server = await startServer((req, res) => { res.setHeader('Content-Type', 'text/plain'); res.end(req.headers.authorization || ''); }); const { port } = server.address(); const response = await axios.get(`http://urluser:urlpass@127.0.0.1:${port}/`, { proxy: false, }); const expected = `Basic ${Buffer.from('urluser:urlpass', 'utf8').toString('base64')}`; expect(response.data).to.equal(expected); }); it('prefers auth config over URL credentials', async () => { server = await startServer((req, res) => { res.setHeader('Content-Type', 'text/plain'); res.end(req.headers.authorization || ''); }); const { port } = server.address(); const response = await axios.get(`http://urluser:urlpass@127.0.0.1:${port}/`, { proxy: false, auth: { username: 'configuser', password: 'configpass', }, }); const expected = `Basic ${Buffer.from('configuser:configpass', 'utf8').toString('base64')}`; expect(response.data).to.equal(expected); }); }); axios-axios-aa54a5e/tests/smoke/cjs/tests/basic.smoke.test.cjs000066400000000000000000000071551526660636600245530ustar00rootroot00000000000000const { EventEmitter } = require('events'); const { PassThrough } = require('stream'); const axios = require('axios'); const { describe, it } = require('mocha'); const { expect } = require('chai'); const createTransportCapture = () => { let capturedOptions; const transport = { request(options, onResponse) { capturedOptions = options; const req = new EventEmitter(); req.destroyed = false; req.setTimeout = () => {}; req.write = () => true; req.end = () => { const res = new PassThrough(); res.statusCode = 200; res.statusMessage = 'OK'; res.headers = { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end('{"ok":true}'); }; req.destroy = () => { req.destroyed = true; }; req.close = req.destroy; return req; }, }; return { transport, getCapturedOptions: () => capturedOptions, }; }; const runRequest = async (run) => { const { transport, getCapturedOptions } = createTransportCapture(); await run(transport); return getCapturedOptions(); }; describe('basic compat (dist export only)', () => { it('supports the simplest axios(url) request pattern', async () => { const options = await runRequest((transport) => axios('http://example.com/users', { transport, proxy: false, }) ); expect(options.method).to.equal('GET'); expect(options.path).to.equal('/users'); }); it('supports get()', async () => { const options = await runRequest((transport) => axios.get('http://example.com/items?limit=10', { transport, proxy: false }) ); expect(options.method).to.equal('GET'); expect(options.path).to.equal('/items?limit=10'); }); it('supports delete()', async () => { const options = await runRequest((transport) => axios.delete('http://example.com/items/1', { transport, proxy: false }) ); expect(options.method).to.equal('DELETE'); expect(options.path).to.equal('/items/1'); }); it('supports head()', async () => { const options = await runRequest((transport) => axios.head('http://example.com/health', { transport, proxy: false }) ); expect(options.method).to.equal('HEAD'); expect(options.path).to.equal('/health'); }); it('supports options()', async () => { const options = await runRequest((transport) => axios.options('http://example.com/items', { transport, proxy: false }) ); expect(options.method).to.equal('OPTIONS'); expect(options.path).to.equal('/items'); }); it('supports post()', async () => { const options = await runRequest((transport) => axios.post( 'http://example.com/items', { name: 'widget' }, { transport, proxy: false, } ) ); expect(options.method).to.equal('POST'); expect(options.path).to.equal('/items'); }); it('supports put()', async () => { const options = await runRequest((transport) => axios.put( 'http://example.com/items/1', { name: 'updated-widget' }, { transport, proxy: false, } ) ); expect(options.method).to.equal('PUT'); expect(options.path).to.equal('/items/1'); }); it('supports patch()', async () => { const options = await runRequest((transport) => axios.patch( 'http://example.com/items/1', { status: 'active' }, { transport, proxy: false, } ) ); expect(options.method).to.equal('PATCH'); expect(options.path).to.equal('/items/1'); }); }); axios-axios-aa54a5e/tests/smoke/cjs/tests/cancel.smoke.test.cjs000066400000000000000000000062101526660636600247060ustar00rootroot00000000000000const { EventEmitter } = require('events'); const axios = require('axios'); const { describe, it } = require('mocha'); const { expect } = require('chai'); const NODE_VERSION = parseInt(process.versions.node.split('.')[0]); const itWithAbortController = NODE_VERSION < 16 ? it.skip : it; const createPendingTransport = () => { let requestCount = 0; const transport = { request() { requestCount += 1; const req = new EventEmitter(); req.destroyed = false; req.setTimeout = () => {}; req.write = () => true; req.end = () => {}; req.destroy = () => { req.destroyed = true; }; req.close = req.destroy; return req; }, }; return { transport, getRequestCount: () => requestCount, }; }; describe('cancel compat (dist export only)', () => { itWithAbortController( 'supports cancellation with AbortController (pre-aborted signal)', async () => { const { transport, getRequestCount } = createPendingTransport(); const controller = new AbortController(); controller.abort(); try { const request = axios.get('http://example.com/resource', { signal: controller.signal, transport, proxy: false, }); controller.abort(); await request; } catch (error) { expect(error).to.have.property('code', 'ERR_CANCELED'); } expect(getRequestCount()).to.equal(0); } ); itWithAbortController('supports cancellation with AbortController (in-flight)', async () => { const { transport, getRequestCount } = createPendingTransport(); const controller = new AbortController(); try { const request = axios.get('http://example.com/resource', { signal: controller.signal, transport, proxy: false, }); controller.abort(); await request; } catch (error) { expect(error).to.have.property('code', 'ERR_CANCELED'); } expect(getRequestCount()).to.equal(1); }); it('supports cancellation with CancelToken (pre-canceled token)', async () => { const { transport, getRequestCount } = createPendingTransport(); const source = axios.CancelToken.source(); source.cancel('Operation canceled by the user.'); const error = await axios .get('http://example.com/resource', { cancelToken: source.token, transport, proxy: false, }) .catch((err) => err); expect(axios.isCancel(error)).to.be.true; expect(error.code).to.equal('ERR_CANCELED'); expect(getRequestCount()).to.equal(0); }); it('supports cancellation with CancelToken (in-flight)', async () => { const { transport, getRequestCount } = createPendingTransport(); const source = axios.CancelToken.source(); const request = axios.get('http://example.com/resource', { cancelToken: source.token, transport, proxy: false, }); source.cancel('Operation canceled by the user.'); const error = await request.catch((err) => err); expect(axios.isCancel(error)).to.be.true; expect(error.code).to.equal('ERR_CANCELED'); expect(getRequestCount()).to.equal(1); }); }); axios-axios-aa54a5e/tests/smoke/cjs/tests/error.smoke.test.cjs000066400000000000000000000075471526660636600246300ustar00rootroot00000000000000const { EventEmitter } = require('events'); const { PassThrough } = require('stream'); const axios = require('axios'); const { describe, it } = require('mocha'); const { expect } = require('chai'); const createTransport = (config) => { const opts = config || {}; return { request(options, onResponse) { const req = new EventEmitter(); req.destroyed = false; req.write = () => true; req.destroy = () => { req.destroyed = true; }; req.close = req.destroy; req.setTimeout = (_ms, cb) => { if (opts.timeout) { req._timeoutCallback = cb; } }; req.end = () => { if (opts.error) { req.emit('error', opts.error); return; } if (opts.timeout && req._timeoutCallback) { req._timeoutCallback(); return; } const res = new PassThrough(); res.statusCode = opts.response && opts.response.statusCode !== undefined ? opts.response.statusCode : 200; res.statusMessage = opts.response && opts.response.statusMessage ? opts.response.statusMessage : 'OK'; res.headers = opts.response && opts.response.headers ? opts.response.headers : { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end(opts.response && opts.response.body ? opts.response.body : '{"ok":true}'); }; return req; }, }; }; describe('error compat (dist export only)', () => { it('rejects with AxiosError for non-2xx responses by default', async () => { const err = await axios .get('http://example.com/fail', { proxy: false, transport: createTransport({ response: { statusCode: 500, statusMessage: 'Internal Server Error', body: '{"error":"boom"}', }, }), }) .catch((e) => e); expect(axios.isAxiosError(err)).to.be.true; expect(err.response.status).to.equal(500); expect(err.message).to.include('500'); }); it('resolves when validateStatus allows non-2xx responses', async () => { const response = await axios.get('http://example.com/allowed', { proxy: false, validateStatus: () => true, transport: createTransport({ response: { statusCode: 500, statusMessage: 'Internal Server Error', body: '{"ok":false}', }, }), }); expect(response.status).to.equal(500); expect(response.data).to.deep.equal({ ok: false }); }); it('wraps transport errors as AxiosError', async () => { const err = await axios .get('http://example.com/network', { proxy: false, transport: createTransport({ error: new Error('socket hang up'), }), }) .catch((e) => e); expect(axios.isAxiosError(err)).to.be.true; expect(err.message).to.include('socket hang up'); expect(err.toJSON).to.be.a('function'); }); it('rejects with ECONNABORTED on timeout', async () => { const err = await axios .get('http://example.com/timeout', { proxy: false, timeout: 10, transport: createTransport({ timeout: true }), }) .catch((e) => e); expect(axios.isAxiosError(err)).to.be.true; expect(err.code).to.equal('ECONNABORTED'); expect(err.message).to.equal('timeout of 10ms exceeded'); }); it('uses timeoutErrorMessage when provided', async () => { const err = await axios .get('http://example.com/timeout', { proxy: false, timeout: 25, timeoutErrorMessage: 'custom timeout message', transport: createTransport({ timeout: true }), }) .catch((e) => e); expect(axios.isAxiosError(err)).to.be.true; expect(err.code).to.equal('ECONNABORTED'); expect(err.message).to.equal('custom timeout message'); }); }); axios-axios-aa54a5e/tests/smoke/cjs/tests/fetch.smoke.test.cjs000066400000000000000000000076561526660636600245710ustar00rootroot00000000000000const axios = require('axios'); const { it, describe } = require('mocha'); const { expect } = require('chai'); const NODE_VERSION = parseInt(process.versions.node.split('.')[0]); const describeWithFetch = NODE_VERSION < 18 ? describe.skip : describe; const createFetchMock = (responseFactory) => { const calls = []; const mockFetch = async (input, init) => { calls.push({ input, init: init || {} }); if (responseFactory) { return responseFactory(input, init || {}); } return new Response(JSON.stringify({ ok: true }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); }; return { mockFetch, getCalls: () => calls, }; }; describeWithFetch('fetch compat (dist export only)', () => { it('uses fetch adapter and resolves JSON response', async () => { const { mockFetch, getCalls } = createFetchMock(); const response = await axios.get('https://example.com/users', { adapter: 'fetch', env: { fetch: mockFetch, Request, Response, }, }); expect(response.data).to.deep.equal({ ok: true }); expect(response.status).to.equal(200); expect(getCalls()).to.have.lengthOf(1); }); it('sends method, headers and body for post requests', async () => { const { mockFetch, getCalls } = createFetchMock(async (input, init) => { const requestInit = init || {}; const isRequest = input && typeof input !== 'string'; const method = isRequest ? input.method : requestInit.method; const body = isRequest && typeof input.clone === 'function' ? await input.clone().text() : requestInit.body; let contentType; if (isRequest && input.headers) { contentType = input.headers.get('content-type'); } else if (requestInit.headers) { contentType = requestInit.headers['Content-Type'] || requestInit.headers['content-type']; } return new Response( JSON.stringify({ url: typeof input === 'string' ? input : input.url, method, contentType, body, }), { status: 200, headers: { 'Content-Type': 'application/json' }, } ); }); const response = await axios.post( 'https://example.com/items', { name: 'widget' }, { adapter: 'fetch', env: { fetch: mockFetch, Request, Response, }, } ); expect(getCalls()).to.have.lengthOf(1); expect(response.data.url).to.equal('https://example.com/items'); expect(response.data.method).to.equal('POST'); expect(response.data.contentType).to.include('application/json'); expect(response.data.body).to.equal(JSON.stringify({ name: 'widget' })); }); it('rejects non-2xx fetch responses by default', async () => { const { mockFetch } = createFetchMock( () => new Response(JSON.stringify({ error: 'boom' }), { status: 500, statusText: 'Internal Server Error', headers: { 'Content-Type': 'application/json' }, }) ); const err = await axios .get('https://example.com/fail', { adapter: 'fetch', env: { fetch: mockFetch, Request, Response, }, }) .catch((e) => e); expect(axios.isAxiosError(err)).to.be.true; expect(err.response.status).to.equal(500); }); it('supports cancellation with AbortController in fetch mode', async () => { const { mockFetch } = createFetchMock(); const controller = new AbortController(); controller.abort(); const err = await axios .get('https://example.com/cancel', { adapter: 'fetch', signal: controller.signal, env: { fetch: mockFetch, Request, Response, }, }) .catch((e) => e); expect(axios.isCancel(err)).to.be.true; expect(err.code).to.equal('ERR_CANCELED'); }); }); axios-axios-aa54a5e/tests/smoke/cjs/tests/files.smoke.test.cjs000066400000000000000000000071301526660636600245650ustar00rootroot00000000000000const { PassThrough, Readable, Writable } = require('stream'); const axios = require('axios'); const { describe, it } = require('mocha'); const { expect } = require('chai'); const createCaptureTransport = (buildResponse) => { return { request(options, onResponse) { const chunks = []; const req = new Writable({ write(chunk, _encoding, callback) { chunks.push(Buffer.from(chunk)); callback(); }, }); req.destroyed = false; req.setTimeout = () => {}; req.close = () => { req.destroyed = true; }; const originalDestroy = req.destroy.bind(req); req.destroy = (...args) => { req.destroyed = true; return originalDestroy(...args); }; const originalEnd = req.end.bind(req); req.end = (...args) => { originalEnd(...args); const body = Buffer.concat(chunks); const response = buildResponse ? buildResponse(body, options) : {}; const res = new PassThrough(); res.statusCode = response.statusCode !== undefined ? response.statusCode : 200; res.statusMessage = response.statusMessage || 'OK'; res.headers = response.headers || { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end(response.body || JSON.stringify({ size: body.length })); }; req.on('error', () => {}); return req; }, }; }; describe('files compat (dist export only)', () => { it('supports posting Buffer payloads', async () => { const source = Buffer.from('binary-\x00-data', 'utf8'); const response = await axios.post('http://example.com/upload', source, { proxy: false, transport: createCaptureTransport((body) => ({ body: JSON.stringify({ echoed: body.toString('base64') }), })), }); expect(response.data.echoed).to.equal(source.toString('base64')); }); it('supports posting Uint8Array payloads', async () => { const source = Uint8Array.from([1, 2, 3, 4, 255]); const response = await axios.post('http://example.com/upload', source, { proxy: false, transport: createCaptureTransport((body) => ({ body: JSON.stringify({ echoed: Array.from(body.values()) }), })), }); expect(response.data.echoed).to.deep.equal([1, 2, 3, 4, 255]); }); it('supports posting Readable stream payloads', async () => { const streamData = ['hello ', 'stream ', 'world']; const source = Readable.from(streamData); const response = await axios.post('http://example.com/upload', source, { proxy: false, headers: { 'Content-Type': 'application/octet-stream' }, transport: createCaptureTransport((body, options) => ({ body: JSON.stringify({ text: body.toString('utf8'), contentType: options.headers && (options.headers['Content-Type'] || options.headers['content-type']), }), })), }); expect(response.data.text).to.equal('hello stream world'); expect(response.data.contentType).to.contain('application/octet-stream'); }); it('supports binary downloads with responseType=arraybuffer', async () => { const binary = Buffer.from([0xde, 0xad, 0xbe, 0xef]); const response = await axios.get('http://example.com/file.bin', { proxy: false, responseType: 'arraybuffer', transport: createCaptureTransport(() => ({ headers: { 'content-type': 'application/octet-stream' }, body: binary, })), }); expect(Buffer.isBuffer(response.data)).to.equal(true); expect(response.data.equals(binary)).to.equal(true); }); }); axios-axios-aa54a5e/tests/smoke/cjs/tests/formData.smoke.test.cjs000066400000000000000000000066521526660636600252300ustar00rootroot00000000000000const { Writable, PassThrough } = require('stream'); const axios = require('axios'); const { describe, it } = require('mocha'); const { expect } = require('chai'); const NODE_VERSION = parseInt(process.versions.node.split('.')[0]); const describeWithFormData = NODE_VERSION < 18 ? describe.skip : describe; const createCaptureTransport = (buildResponse) => { return { request(options, onResponse) { const chunks = []; const req = new Writable({ write(chunk, _encoding, callback) { chunks.push(Buffer.from(chunk)); callback(); }, }); req.destroyed = false; req.setTimeout = () => {}; req.write = req.write.bind(req); req.close = () => { req.destroyed = true; }; const originalDestroy = req.destroy.bind(req); req.destroy = (...args) => { req.destroyed = true; return originalDestroy(...args); }; const originalEnd = req.end.bind(req); req.end = (...args) => { originalEnd(...args); const body = Buffer.concat(chunks); const response = buildResponse ? buildResponse(body, options) : {}; const res = new PassThrough(); res.statusCode = response.statusCode !== undefined ? response.statusCode : 200; res.statusMessage = response.statusMessage || 'OK'; res.headers = response.headers || { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end(response.body || JSON.stringify({ ok: true })); }; return req; }, }; }; const bodyAsUtf8 = (value) => { return Buffer.isBuffer(value) ? value.toString('utf8') : String(value); }; describeWithFormData('formData compat (dist export only)', () => { it('supports posting FormData instances', async () => { const form = new FormData(); form.append('username', 'janedoe'); form.append('role', 'admin'); const response = await axios.post('http://example.com/form', form, { proxy: false, transport: createCaptureTransport((body, options) => ({ body: JSON.stringify({ contentType: options.headers && (options.headers['Content-Type'] || options.headers['content-type']), payload: bodyAsUtf8(body), }), })), }); expect(response.data.contentType).to.contain('multipart/form-data'); expect(response.data.payload).to.contain('name="username"'); expect(response.data.payload).to.contain('janedoe'); expect(response.data.payload).to.contain('name="role"'); expect(response.data.payload).to.contain('admin'); }); it('supports axios.postForm helper', async () => { const response = await axios.postForm( 'http://example.com/post-form', { project: 'axios', mode: 'compat', }, { proxy: false, transport: createCaptureTransport((body, options) => ({ body: JSON.stringify({ contentType: options.headers && (options.headers['Content-Type'] || options.headers['content-type']), payload: bodyAsUtf8(body), }), })), } ); expect(response.data.contentType).to.contain('multipart/form-data'); expect(response.data.payload).to.contain('name="project"'); expect(response.data.payload).to.contain('axios'); expect(response.data.payload).to.contain('name="mode"'); expect(response.data.payload).to.contain('compat'); }); }); axios-axios-aa54a5e/tests/smoke/cjs/tests/header-buckets.smoke.test.cjs000066400000000000000000000030171526660636600263510ustar00rootroot00000000000000const axios = require('axios'); const otherAxios = require('axios/dist/browser/axios.cjs'); const { describe, it } = require('mocha'); const { expect } = require('chai'); describe('header buckets across package exports', () => { for (const [shape, Headers] of [ ['instances', otherAxios.AxiosHeaders], ['subclasses', class extends otherAxios.AxiosHeaders {}], ]) { it(`should preserve AxiosHeaders ${shape} as buckets from a different build`, async () => { const { echoHeaders } = await import('../../../setup/adapters.js'); expect(axios.AxiosHeaders).not.to.equal(otherAxios.AxiosHeaders); const instance = axios.create({ adapter: echoHeaders, headers: { common: new Headers({ 'X-Common': 'common' }), post: new Headers({ Authorization: 'Bearer TEST_ONLY' }), }, }); for (const method of ['get', 'post']) { const { config } = await instance.request({ url: '/header-buckets', method, headers: { Link: Buffer.from('; rel="type"') }, }); expect(config.headers.get('X-Common')).to.equal('common'); expect(config.headers.get('Authorization')).to.equal( method === 'post' ? 'Bearer TEST_ONLY' : undefined ); expect(config.headers.get('Link')).to.equal('; rel="type"'); expect(config.headers.has('common')).to.equal(false); expect(config.headers.has('post')).to.equal(false); } }); } }); axios-axios-aa54a5e/tests/smoke/cjs/tests/headers.smoke.test.cjs000066400000000000000000000065631526660636600251070ustar00rootroot00000000000000const { EventEmitter } = require('events'); const { PassThrough } = require('stream'); const axios = require('axios'); const { describe, it } = require('mocha'); const { expect } = require('chai'); const normalizeHeaders = (headers) => { const result = {}; Object.entries(headers || {}).forEach(([key, value]) => { result[key.toLowerCase()] = value; }); return result; }; const createTransportCapture = () => { let capturedOptions; const transport = { request(options, onResponse) { capturedOptions = options; const req = new EventEmitter(); req.destroyed = false; req.setTimeout = () => {}; req.write = () => true; req.destroy = () => { req.destroyed = true; }; req.close = req.destroy; req.end = () => { const res = new PassThrough(); res.statusCode = 200; res.statusMessage = 'OK'; res.headers = { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end('{"ok":true}'); }; return req; }, }; return { transport, getCapturedOptions: () => capturedOptions, }; }; describe('headers compat (dist export only)', () => { it('sends default Accept header', async () => { const { transport, getCapturedOptions } = createTransportCapture(); await axios.get('http://example.com/default-headers', { transport, proxy: false, }); const headers = normalizeHeaders(getCapturedOptions().headers); expect(headers.accept).to.equal('application/json, text/plain, */*'); }); it('supports custom headers', async () => { const { transport, getCapturedOptions } = createTransportCapture(); await axios.get('http://example.com/custom-headers', { transport, proxy: false, headers: { 'X-Trace-Id': 'trace-123', Authorization: 'Bearer token-abc', }, }); const headers = normalizeHeaders(getCapturedOptions().headers); expect(headers['x-trace-id']).to.equal('trace-123'); expect(headers.authorization).to.equal('Bearer token-abc'); }); it('treats header names as case-insensitive when overriding', async () => { const { transport, getCapturedOptions } = createTransportCapture(); await axios.get('http://example.com/case-insensitive', { transport, proxy: false, headers: { authorization: 'Bearer old-token', AuThOrIzAtIoN: 'Bearer new-token', }, }); const headers = normalizeHeaders(getCapturedOptions().headers); expect(headers.authorization).to.equal('Bearer new-token'); }); it('sets content-type for json post payloads', async () => { const { transport, getCapturedOptions } = createTransportCapture(); await axios.post( 'http://example.com/post-json', { name: 'widget' }, { transport, proxy: false, } ); const headers = normalizeHeaders(getCapturedOptions().headers); expect(headers['content-type']).to.contain('application/json'); }); it('does not force content-type for get requests without body', async () => { const { transport, getCapturedOptions } = createTransportCapture(); await axios.get('http://example.com/get-no-body', { transport, proxy: false, }); const headers = normalizeHeaders(getCapturedOptions().headers); expect(headers['content-type']).to.be.undefined; }); }); axios-axios-aa54a5e/tests/smoke/cjs/tests/http2.smoke.test.cjs000066400000000000000000000041201526660636600245200ustar00rootroot00000000000000const axios = require('axios'); const { describe, it } = require('mocha'); const { expect } = require('chai'); describe('http2 compat (dist export only)', () => { it('keeps instance-level httpVersion and http2Options in request config', async () => { const client = axios.create({ baseURL: 'https://example.com', httpVersion: 2, http2Options: { rejectUnauthorized: false, }, }); const response = await client.get('/resource', { adapter: async (config) => ({ data: { httpVersion: config.httpVersion, http2Options: config.http2Options, }, status: 200, statusText: 'OK', headers: {}, config, }), }); expect(response.data.httpVersion).to.equal(2); expect(response.data.http2Options).to.deep.equal({ rejectUnauthorized: false, }); }); it('merges request http2Options with instance http2Options', async () => { const client = axios.create({ baseURL: 'https://example.com', httpVersion: 2, http2Options: { rejectUnauthorized: false, sessionTimeout: 1000, }, }); const response = await client.get('/resource', { http2Options: { sessionTimeout: 5000, customFlag: true, }, adapter: async (config) => ({ data: config.http2Options, status: 200, statusText: 'OK', headers: {}, config, }), }); expect(response.data).to.deep.equal({ rejectUnauthorized: false, sessionTimeout: 5000, customFlag: true, }); }); it('allows request-level httpVersion override', async () => { const client = axios.create({ baseURL: 'https://example.com', httpVersion: 2, }); const response = await client.get('/resource', { httpVersion: 1, adapter: async (config) => ({ data: { httpVersion: config.httpVersion, }, status: 200, statusText: 'OK', headers: {}, config, }), }); expect(response.data.httpVersion).to.equal(1); }); }); axios-axios-aa54a5e/tests/smoke/cjs/tests/import.smoke.test.cjs000066400000000000000000000011621526660636600247740ustar00rootroot00000000000000const axios = require('axios'); const { describe, it } = require('mocha'); const { expect } = require('chai'); const { CanceledError, AxiosError, AxiosHeaders } = axios; describe('CommonJS importing', () => { it('should import axios', () => { expect(typeof axios).to.be.equal('function'); }); it('should import CanceledError', () => { expect(typeof CanceledError).to.be.equal('function'); }); it('should import AxiosError', () => { expect(typeof AxiosError).to.be.equal('function'); }); it('should import AxiosHeaders', () => { expect(typeof AxiosHeaders).to.be.equal('function'); }); }); axios-axios-aa54a5e/tests/smoke/cjs/tests/instance.smoke.test.cjs000066400000000000000000000074151526660636600252750ustar00rootroot00000000000000const { EventEmitter } = require('events'); const { PassThrough } = require('stream'); const axios = require('axios'); const { describe, it } = require('mocha'); const { expect } = require('chai'); const createTransportCapture = (responseBody) => { const calls = []; const transport = { request(options, onResponse) { calls.push(options); const req = new EventEmitter(); req.destroyed = false; req.setTimeout = () => {}; req.write = () => true; req.destroy = () => { req.destroyed = true; }; req.close = req.destroy; req.end = () => { const res = new PassThrough(); res.statusCode = 200; res.statusMessage = 'OK'; res.headers = { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end(responseBody || '{"ok":true}'); }; return req; }, }; return { transport, getCalls: () => calls, }; }; describe('instance compat (dist export only)', () => { it('creates isolated instances with separate defaults', async () => { const { transport, getCalls } = createTransportCapture(); const clientA = axios.create({ baseURL: 'http://example.com/api-a', headers: { 'X-App': 'A', }, }); const clientB = axios.create({ baseURL: 'http://example.com/api-b', headers: { 'X-App': 'B', }, }); await clientA.get('/users', { transport, proxy: false }); await clientB.get('/users', { transport, proxy: false }); const [callA, callB] = getCalls(); expect(callA.path).to.equal('/api-a/users'); expect(callB.path).to.equal('/api-b/users'); expect(callA.headers['X-App']).to.equal('A'); expect(callB.headers['X-App']).to.equal('B'); }); it('supports callable instance form instance(config)', async () => { const { transport, getCalls } = createTransportCapture(); const client = axios.create({ baseURL: 'http://example.com', }); await client({ url: '/status', method: 'get', transport, proxy: false, }); expect(getCalls()).to.have.lengthOf(1); expect(getCalls()[0].method).to.equal('GET'); expect(getCalls()[0].path).to.equal('/status'); }); it('applies instance request interceptors', async () => { const { transport, getCalls } = createTransportCapture(); const client = axios.create({ baseURL: 'http://example.com', }); client.interceptors.request.use((config) => { config.headers = config.headers || {}; config.headers['X-From-Interceptor'] = 'yes'; return config; }); await client.get('/intercepted', { transport, proxy: false }); expect(getCalls()).to.have.lengthOf(1); expect(getCalls()[0].headers['X-From-Interceptor']).to.equal('yes'); }); it('applies instance response interceptors', async () => { const { transport } = createTransportCapture('{"name":"axios"}'); const client = axios.create({ baseURL: 'http://example.com', }); client.interceptors.response.use((response) => { response.data = Object.assign({}, response.data, { intercepted: true, }); return response; }); const response = await client.get('/response-interceptor', { transport, proxy: false, }); expect(response.data).to.deep.equal({ name: 'axios', intercepted: true, }); }); it('builds URLs with getUri from instance defaults and request params', () => { const client = axios.create({ baseURL: 'http://example.com/api', params: { apiKey: 'abc', }, }); const uri = client.getUri({ url: '/users', params: { page: 2, }, }); expect(uri).to.equal('http://example.com/api/users?apiKey=abc&page=2'); }); }); axios-axios-aa54a5e/tests/smoke/cjs/tests/interceptors.smoke.test.cjs000066400000000000000000000076021526660636600262100ustar00rootroot00000000000000const { EventEmitter } = require('events'); const { PassThrough } = require('stream'); const axios = require('axios'); const { describe, it } = require('mocha'); const { expect } = require('chai'); const createTransport = (responseBody) => { const calls = []; const transport = { request(options, onResponse) { calls.push(options); const req = new EventEmitter(); req.destroyed = false; req.setTimeout = () => {}; req.write = () => true; req.destroy = () => { req.destroyed = true; }; req.close = req.destroy; req.end = () => { const res = new PassThrough(); res.statusCode = 200; res.statusMessage = 'OK'; res.headers = { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end(responseBody || '{"value":"ok"}'); }; return req; }, }; return { transport, getCalls: () => calls, }; }; describe('interceptors compat (dist export only)', () => { it('applies request interceptors before dispatch', async () => { const { transport, getCalls } = createTransport(); const client = axios.create(); client.interceptors.request.use((config) => { config.headers = config.headers || {}; config.headers['X-One'] = '1'; return config; }); client.interceptors.request.use((config) => { config.headers['X-Two'] = '2'; return config; }); await client.get('http://example.com/resource', { transport, proxy: false, }); expect(getCalls()).to.have.lengthOf(1); expect(getCalls()[0].headers['X-One']).to.equal('1'); expect(getCalls()[0].headers['X-Two']).to.equal('2'); }); it('applies response interceptors in registration order', async () => { const { transport } = createTransport('{"n":1}'); const client = axios.create(); client.interceptors.response.use((response) => { response.data.n += 1; return response; }); client.interceptors.response.use((response) => { response.data.n *= 10; return response; }); const response = await client.get('http://example.com/resource', { transport, proxy: false, }); expect(response.data.n).to.equal(20); }); it('supports ejecting request interceptors', async () => { const { transport, getCalls } = createTransport(); const client = axios.create(); const id = client.interceptors.request.use((config) => { config.headers = config.headers || {}; config.headers['X-Ejected'] = 'yes'; return config; }); client.interceptors.request.eject(id); await client.get('http://example.com/resource', { transport, proxy: false, }); expect(getCalls()).to.have.lengthOf(1); expect(getCalls()[0].headers['X-Ejected']).to.be.undefined; }); it('supports async request interceptors', async () => { const { transport, getCalls } = createTransport(); const client = axios.create(); client.interceptors.request.use(async (config) => { await Promise.resolve(); config.headers = config.headers || {}; config.headers['X-Async'] = 'true'; return config; }); await client.get('http://example.com/resource', { transport, proxy: false, }); expect(getCalls()[0].headers['X-Async']).to.equal('true'); }); it('propagates errors thrown by request interceptors', async () => { const { transport, getCalls } = createTransport(); const client = axios.create(); client.interceptors.request.use(() => { throw new Error('blocked-by-interceptor'); }); const err = await client .get('http://example.com/resource', { transport, proxy: false, }) .catch((e) => e); expect(err).to.be.instanceOf(Error); expect(err.message).to.contain('blocked-by-interceptor'); expect(getCalls()).to.have.lengthOf(0); }); }); axios-axios-aa54a5e/tests/smoke/cjs/tests/progress.smoke.test.cjs000066400000000000000000000057221526660636600253340ustar00rootroot00000000000000const { Readable, Writable, PassThrough } = require('stream'); const axios = require('axios'); const { describe, it } = require('mocha'); const { expect } = require('chai'); const createProgressTransport = (config) => { const opts = config || {}; const responseChunks = opts.responseChunks || ['ok']; const responseHeaders = opts.responseHeaders || {}; return { request(_options, onResponse) { const req = new Writable({ write(_chunk, _encoding, callback) { callback(); }, }); req.destroyed = false; req.setTimeout = () => {}; req.close = () => { req.destroyed = true; }; const originalDestroy = req.destroy.bind(req); req.destroy = (...args) => { req.destroyed = true; return originalDestroy(...args); }; const originalEnd = req.end.bind(req); req.end = (...args) => { originalEnd(...args); const res = new PassThrough(); res.statusCode = 200; res.statusMessage = 'OK'; res.headers = Object.assign( { 'content-type': 'text/plain', }, responseHeaders ); res.req = req; onResponse(res); responseChunks.forEach((chunk) => { res.write(chunk); }); res.end(); }; return req; }, }; }; describe('progress compat (dist export only)', () => { it('emits upload progress events for stream payloads', async () => { const samples = []; const payload = ['abc', 'def', 'ghi']; const total = payload.join('').length; await axios.post('http://example.com/upload', Readable.from(payload), { proxy: false, headers: { 'Content-Length': String(total), }, onUploadProgress: ({ loaded, total: reportedTotal, upload }) => { samples.push({ loaded, total: reportedTotal, upload }); }, transport: createProgressTransport({ responseChunks: ['uploaded'], }), }); expect(samples.length).to.be.greaterThan(0); expect(samples[samples.length - 1]).to.deep.include({ loaded: total, total, upload: true, }); }); it('emits download progress events', async () => { const samples = []; const chunks = ['ab', 'cd', 'ef']; const total = chunks.join('').length; const response = await axios.get('http://example.com/download', { proxy: false, responseType: 'text', onDownloadProgress: ({ loaded, total: reportedTotal, download }) => { samples.push({ loaded, total: reportedTotal, download }); }, transport: createProgressTransport({ responseChunks: chunks, responseHeaders: { 'content-length': String(total), }, }), }); expect(response.data).to.equal('abcdef'); expect(samples.length).to.be.greaterThan(0); expect(samples[samples.length - 1]).to.deep.include({ loaded: total, total, download: true, }); }); }); axios-axios-aa54a5e/tests/smoke/cjs/tests/rateLimit.smoke.test.cjs000066400000000000000000000053201526660636600254140ustar00rootroot00000000000000const { EventEmitter } = require('events'); const { PassThrough } = require('stream'); const axios = require('axios'); const { describe, it } = require('mocha'); const { expect } = require('chai'); const createTransportCapture = () => { let capturedOptions; const transport = { request(options, onResponse) { capturedOptions = options; const req = new EventEmitter(); req.destroyed = false; req.setTimeout = () => {}; req.write = () => true; req.destroy = () => { req.destroyed = true; }; req.close = req.destroy; req.end = () => { const res = new PassThrough(); res.statusCode = 200; res.statusMessage = 'OK'; res.headers = { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end('{"ok":true}'); }; return req; }, }; return { transport, getCapturedOptions: () => capturedOptions, }; }; describe('rateLimit compat (dist export only)', () => { it('accepts numeric maxRate config', async () => { const response = await axios.get('http://example.com/rate', { maxRate: 1024, adapter: async (config) => ({ data: { maxRate: config.maxRate }, status: 200, statusText: 'OK', headers: {}, config, }), }); expect(response.data.maxRate).to.equal(1024); }); it('accepts tuple maxRate config [upload, download]', async () => { const response = await axios.get('http://example.com/rate', { maxRate: [2048, 4096], adapter: async (config) => ({ data: { maxRate: config.maxRate }, status: 200, statusText: 'OK', headers: {}, config, }), }); expect(response.data.maxRate).to.deep.equal([2048, 4096]); }); it('merges instance and request maxRate values', async () => { const client = axios.create({ maxRate: [1000, 2000], }); const response = await client.get('http://example.com/rate', { maxRate: [3000, 4000], adapter: async (config) => ({ data: { maxRate: config.maxRate }, status: 200, statusText: 'OK', headers: {}, config, }), }); expect(response.data.maxRate).to.deep.equal([3000, 4000]); }); it('supports maxRate in node transport flow without errors', async () => { const { transport, getCapturedOptions } = createTransportCapture(); const response = await axios.get('http://example.com/rate', { proxy: false, maxRate: [1500, 2500], transport, }); expect(response.status).to.equal(200); expect(getCapturedOptions().method).to.equal('GET'); expect(getCapturedOptions().path).to.equal('/rate'); }); }); axios-axios-aa54a5e/tests/smoke/cjs/tests/timeout.smoke.test.cjs000066400000000000000000000063731526660636600251610ustar00rootroot00000000000000const { EventEmitter } = require('events'); const { PassThrough } = require('stream'); const axios = require('axios'); const { describe, it } = require('mocha'); const { expect } = require('chai'); const createTransport = (config) => { const opts = config || {}; return { request(_options, onResponse) { const req = new EventEmitter(); req.destroyed = false; req._timeoutCallback = null; req.setTimeout = (_ms, callback) => { req._timeoutCallback = callback; }; req.write = () => true; req.destroy = () => { req.destroyed = true; }; req.close = req.destroy; req.end = () => { if (opts.triggerTimeout && req._timeoutCallback) { req._timeoutCallback(); return; } const res = new PassThrough(); res.statusCode = 200; res.statusMessage = 'OK'; res.headers = { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end(opts.body || '{"ok":true}'); }; return req; }, }; }; describe('timeout compat (dist export only)', () => { it('rejects with ECONNABORTED on timeout', async () => { const err = await axios .get('http://example.com/timeout', { proxy: false, timeout: 25, transport: createTransport({ triggerTimeout: true }), }) .catch((e) => e); expect(axios.isAxiosError(err)).to.equal(true); expect(err.code).to.equal('ECONNABORTED'); expect(err.message).to.equal('timeout of 25ms exceeded'); }); it('uses timeoutErrorMessage when provided', async () => { const err = await axios .get('http://example.com/timeout', { proxy: false, timeout: 25, timeoutErrorMessage: 'custom timeout', transport: createTransport({ triggerTimeout: true }), }) .catch((e) => e); expect(axios.isAxiosError(err)).to.equal(true); expect(err.code).to.equal('ECONNABORTED'); expect(err.message).to.equal('custom timeout'); }); it('accepts timeout as a numeric string', async () => { const err = await axios .get('http://example.com/timeout', { proxy: false, timeout: '30', transport: createTransport({ triggerTimeout: true }), }) .catch((e) => e); expect(axios.isAxiosError(err)).to.equal(true); expect(err.code).to.equal('ECONNABORTED'); expect(err.message).to.equal('timeout of 30ms exceeded'); }); it('rejects with ERR_BAD_OPTION_VALUE when timeout is not parsable', async () => { const err = await axios .get('http://example.com/timeout', { proxy: false, timeout: { invalid: true }, transport: createTransport(), }) .catch((e) => e); expect(axios.isAxiosError(err)).to.equal(true); expect(err.code).to.equal('ERR_BAD_OPTION_VALUE'); expect(err.message).to.equal('error trying to parse `config.timeout` to int'); }); it('does not time out when timeout is 0', async () => { const response = await axios.get('http://example.com/no-timeout', { proxy: false, timeout: 0, transport: createTransport({ body: '{"ok":true}' }), }); expect(response.status).to.equal(200); expect(response.data).to.deep.equal({ ok: true }); }); }); axios-axios-aa54a5e/tests/smoke/cjs/tests/urlencode.smoke.test.cjs000066400000000000000000000074241526660636600254510ustar00rootroot00000000000000const { EventEmitter } = require('events'); const { PassThrough } = require('stream'); const axios = require('axios'); const { describe, it } = require('mocha'); const { expect } = require('chai'); const createEchoTransport = () => { let capturedOptions; const transport = { request(options, onResponse) { capturedOptions = options; const chunks = []; const req = new EventEmitter(); req.destroyed = false; req.setTimeout = () => {}; req.destroy = () => { req.destroyed = true; }; req.close = req.destroy; req.write = (chunk) => { chunks.push(Buffer.from(chunk)); return true; }; req.end = (chunk) => { if (chunk) { chunks.push(Buffer.from(chunk)); } const res = new PassThrough(); res.statusCode = 200; res.statusMessage = 'OK'; res.headers = { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end( JSON.stringify({ path: options.path, body: Buffer.concat(chunks).toString('utf8'), contentType: options.headers && (options.headers['Content-Type'] || options.headers['content-type']), }) ); }; return req; }, }; return { transport, getCapturedOptions: () => capturedOptions, }; }; describe('urlencode compat (dist export only)', () => { it('serializes params into request URL', async () => { const { transport } = createEchoTransport(); const response = await axios.get('http://example.com/search', { proxy: false, transport, params: { q: 'axios docs', page: 2, }, }); expect(response.data.path).to.equal('/search?q=axios+docs&page=2'); }); it('supports custom paramsSerializer function', async () => { const { transport } = createEchoTransport(); const response = await axios.get('http://example.com/search', { proxy: false, transport, params: { q: 'ignored' }, paramsSerializer: () => 'fixed=1', }); expect(response.data.path).to.equal('/search?fixed=1'); }); it('supports URLSearchParams payloads', async () => { const { transport } = createEchoTransport(); const payload = new URLSearchParams(); payload.append('name', 'axios'); payload.append('mode', 'compat'); const response = await axios.post('http://example.com/form', payload, { proxy: false, transport, }); expect(response.data.body).to.equal('name=axios&mode=compat'); expect(response.data.contentType).to.contain('application/x-www-form-urlencoded'); }); it('serializes object payload when content-type is application/x-www-form-urlencoded', async () => { const { transport } = createEchoTransport(); const response = await axios.post( 'http://example.com/form', { name: 'axios', mode: 'compat', }, { proxy: false, transport, headers: { 'content-type': 'application/x-www-form-urlencoded', }, } ); expect(response.data.body).to.equal('name=axios&mode=compat'); expect(response.data.contentType).to.contain('application/x-www-form-urlencoded'); }); it('respects formSerializer options for index formatting', async () => { const { transport } = createEchoTransport(); const response = await axios.post( 'http://example.com/form', { arr: ['1', '2'], }, { proxy: false, transport, headers: { 'content-type': 'application/x-www-form-urlencoded', }, formSerializer: { indexes: true, }, } ); expect(response.data.body).to.equal('arr%5B0%5D=1&arr%5B1%5D=2'); }); }); axios-axios-aa54a5e/tests/smoke/deno/000077500000000000000000000000001526660636600176725ustar00rootroot00000000000000axios-axios-aa54a5e/tests/smoke/deno/deno.json000066400000000000000000000003511526660636600215110ustar00rootroot00000000000000{ "imports": { "@std/assert": "jsr:@std/assert@1.0.19", "axios": "../../../dist/esm/axios.js" }, "tasks": { "test": "deno test --allow-read tests/" }, "test": { "include": ["tests/**/*.smoke.test.ts"] } } axios-axios-aa54a5e/tests/smoke/deno/deno.lock000066400000000000000000000010171526660636600214700ustar00rootroot00000000000000{ "version": "5", "specifiers": { "jsr:@std/assert@1.0.19": "1.0.19", "jsr:@std/internal@^1.0.12": "1.0.12" }, "jsr": { "@std/assert@1.0.19": { "integrity": "eaada96ee120cb980bc47e040f82814d786fe8162ecc53c91d8df60b8755991e", "dependencies": [ "jsr:@std/internal" ] }, "@std/internal@1.0.12": { "integrity": "972a634fd5bc34b242024402972cd5143eac68d8dffaca5eaa4dba30ce17b027" } }, "workspace": { "dependencies": [ "jsr:@std/assert@1.0.19" ] } } axios-axios-aa54a5e/tests/smoke/deno/tests/000077500000000000000000000000001526660636600210345ustar00rootroot00000000000000axios-axios-aa54a5e/tests/smoke/deno/tests/cancel.smoke.test.ts000066400000000000000000000040131526660636600247220ustar00rootroot00000000000000import { assertEquals } from '@std/assert'; import axios from 'axios'; const env = (fetch: any) => ({ fetch, Request, Response, }); Deno.test('cancel: pre-aborted AbortController cancels request', async () => { let fetchCallCount = 0; const fetch = async () => { fetchCallCount += 1; return new Response(JSON.stringify({ ok: true }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); }; const controller = new AbortController(); controller.abort(); const err = await axios .get('https://example.com/cancel', { adapter: 'fetch', signal: controller.signal, env: env(fetch), }) .catch((e: any) => e); assertEquals(axios.isCancel(err), true); assertEquals(err.code, 'ERR_CANCELED'); assertEquals(fetchCallCount, 0); }); Deno.test('cancel: in-flight abort cancels request', async () => { const fetch = (_input: any, init?: any) => new Promise((_resolve, reject) => { const timeout = setTimeout(() => { reject(new DOMException('The operation was aborted', 'AbortError')); }, 20); if (init?.signal) { if (init.signal.aborted) { clearTimeout(timeout); reject(new DOMException('The operation was aborted', 'AbortError')); return; } init.signal.addEventListener( 'abort', () => { clearTimeout(timeout); reject(new DOMException('The operation was aborted', 'AbortError')); }, { once: true } ); } }); const controller = new AbortController(); const request = axios.get('https://example.com/in-flight', { adapter: 'fetch', signal: controller.signal, env: env(fetch), }); controller.abort(); const err = await request.catch((e: any) => e); assertEquals(axios.isCancel(err), true); assertEquals(err.code, 'ERR_CANCELED'); }); Deno.test('cancel: isCancel returns false for plain Error', () => { assertEquals(axios.isCancel(new Error('random')), false); }); axios-axios-aa54a5e/tests/smoke/deno/tests/error.smoke.test.ts000066400000000000000000000025171526660636600246350ustar00rootroot00000000000000import { assertEquals } from '@std/assert'; import axios from 'axios'; const env = (fetch: any) => ({ fetch, Request, Response, }); Deno.test('errors: rejects with AxiosError for 500', async () => { const fetch = async () => new Response(JSON.stringify({ error: 'boom' }), { status: 500, statusText: 'Internal Server Error', headers: { 'Content-Type': 'application/json' }, }); const err = await axios .get('https://example.com/fail', { adapter: 'fetch', env: env(fetch), }) .catch((e: any) => e); assertEquals(axios.isAxiosError(err), true); assertEquals(err.response.status, 500); assertEquals(err.response.data, { error: 'boom' }); }); Deno.test('errors: rejects with AxiosError for 404', async () => { const fetch = async () => new Response(JSON.stringify({ error: 'missing' }), { status: 404, statusText: 'Not Found', headers: { 'Content-Type': 'application/json' }, }); const err = await axios .get('https://example.com/missing', { adapter: 'fetch', env: env(fetch), }) .catch((e: any) => e); assertEquals(axios.isAxiosError(err), true); assertEquals(err.response.status, 404); }); Deno.test('errors: isAxiosError returns false for plain Error', () => { assertEquals(axios.isAxiosError(new Error('plain')), false); }); axios-axios-aa54a5e/tests/smoke/deno/tests/fetch.smoke.test.ts000066400000000000000000000061221526660636600245710ustar00rootroot00000000000000import { assertEquals } from '@std/assert'; import axios from 'axios'; const createFetchMock = ( responseFactory?: (input: any, init: any) => Response | Promise ) => { const calls: Array<{ input: any; init: any }> = []; const mockFetch = async (input: any, init: any = {}) => { calls.push({ input, init }); if (responseFactory) { return responseFactory(input, init); } return new Response(JSON.stringify({ ok: true }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); }; return { mockFetch, getCalls: () => calls, }; }; const getRequestMeta = async (input: any, init: any) => { const request = input instanceof Request ? input : new Request(input, init); return { url: request.url, method: request.method, body: request.method === 'GET' || request.method === 'HEAD' ? undefined : await request.clone().text(), }; }; const env = (fetch: any) => ({ fetch, Request, Response, }); Deno.test('fetch adapter: GET resolves JSON response', async () => { const { mockFetch, getCalls } = createFetchMock(); const response = await axios.get('https://example.com/users', { adapter: 'fetch', env: env(mockFetch), }); assertEquals(response.status, 200); assertEquals(response.data, { ok: true }); assertEquals(getCalls().length, 1); }); Deno.test('fetch adapter: forwards HTTP methods', async () => { const run = async ( method: 'delete' | 'head' | 'options' | 'post' | 'put' | 'patch', expected: string ) => { const { mockFetch, getCalls } = createFetchMock(); if (method === 'post' || method === 'put' || method === 'patch') { await axios[method]( 'https://example.com/items', { name: 'widget' }, { adapter: 'fetch', env: env(mockFetch), } ); } else { await axios[method]('https://example.com/items', { adapter: 'fetch', env: env(mockFetch), }); } const { input, init } = getCalls()[0]; const meta = await getRequestMeta(input, init); assertEquals(meta.method, expected); }; await run('delete', 'DELETE'); await run('head', 'HEAD'); await run('options', 'OPTIONS'); await run('post', 'POST'); await run('put', 'PUT'); await run('patch', 'PATCH'); }); Deno.test('fetch adapter: serializes JSON body for POST', async () => { const { mockFetch, getCalls } = createFetchMock(); await axios.post( 'https://example.com/items', { name: 'widget' }, { adapter: 'fetch', env: env(mockFetch), } ); const { input, init } = getCalls()[0]; const meta = await getRequestMeta(input, init); assertEquals(meta.body, JSON.stringify({ name: 'widget' })); }); Deno.test('fetch adapter: forwards full URL', async () => { const { mockFetch, getCalls } = createFetchMock(); await axios.get('https://example.com/users', { adapter: 'fetch', env: env(mockFetch), }); const { input, init } = getCalls()[0]; const meta = await getRequestMeta(input, init); assertEquals(meta.url, 'https://example.com/users'); }); axios-axios-aa54a5e/tests/smoke/deno/tests/headers.smoke.test.ts000066400000000000000000000042161526660636600251150ustar00rootroot00000000000000import { assertEquals } from '@std/assert'; import axios from 'axios'; const createFetchCapture = () => { const calls: Request[] = []; const fetch = async (input: any, init?: any) => { const request = input instanceof Request ? input : new Request(input, init); calls.push(request); return new Response(JSON.stringify({ ok: true }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); }; return { fetch, getCalls: () => calls, }; }; const env = (fetch: any) => ({ fetch, Request, Response, }); Deno.test('headers: default Accept header is sent', async () => { const { fetch, getCalls } = createFetchCapture(); await axios.get('https://example.com/default-headers', { adapter: 'fetch', env: env(fetch), }); const request = getCalls()[0]; assertEquals(request.headers.get('accept'), 'application/json, text/plain, */*'); }); Deno.test('headers: custom headers are forwarded', async () => { const { fetch, getCalls } = createFetchCapture(); await axios.get('https://example.com/custom-headers', { adapter: 'fetch', headers: { 'X-Trace-Id': 'trace-123', Authorization: 'Bearer token-abc', }, env: env(fetch), }); const request = getCalls()[0]; assertEquals(request.headers.get('x-trace-id'), 'trace-123'); assertEquals(request.headers.get('authorization'), 'Bearer token-abc'); }); Deno.test('headers: content-type is set for JSON POST payload', async () => { const { fetch, getCalls } = createFetchCapture(); await axios.post( 'https://example.com/post-json', { name: 'widget' }, { adapter: 'fetch', env: env(fetch), } ); const request = getCalls()[0]; const contentType = request.headers.get('content-type') || ''; assertEquals(contentType.includes('application/json'), true); }); Deno.test('headers: content-type is absent for bodyless GET', async () => { const { fetch, getCalls } = createFetchCapture(); await axios.get('https://example.com/get-no-body', { adapter: 'fetch', env: env(fetch), }); const request = getCalls()[0]; assertEquals(request.headers.get('content-type'), null); }); axios-axios-aa54a5e/tests/smoke/deno/tests/import.smoke.test.ts000066400000000000000000000012361526660636600250130ustar00rootroot00000000000000import { assertEquals } from '@std/assert'; import axios, { AxiosError, AxiosHeaders, CanceledError } from 'axios'; Deno.test('Deno importing: default export is callable', () => { assertEquals(typeof axios, 'function'); }); Deno.test('Deno importing: named exports are functions', () => { assertEquals(typeof AxiosError, 'function'); assertEquals(typeof CanceledError, 'function'); assertEquals(typeof AxiosHeaders, 'function'); }); Deno.test('Deno importing: named exports match axios properties', () => { assertEquals(axios.AxiosError, AxiosError); assertEquals(axios.CanceledError, CanceledError); assertEquals(axios.AxiosHeaders, AxiosHeaders); }); axios-axios-aa54a5e/tests/smoke/esm/000077500000000000000000000000001526660636600175315ustar00rootroot00000000000000axios-axios-aa54a5e/tests/smoke/esm/package-lock.json000066400000000000000000001145441526660636600227560ustar00rootroot00000000000000{ "name": "@axios/esm-smoke-tests", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@axios/esm-smoke-tests", "version": "1.0.0", "license": "MIT", "devDependencies": { "vitest": "4.1.11" } }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", "dev": true, "license": "MIT" }, "node_modules/@oxc-project/types": { "version": "0.148.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.148.0.tgz", "integrity": "sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A==", "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/oxc-project" } }, "node_modules/@rolldown/binding-android-arm-eabi": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.7.tgz", "integrity": "sha512-EypzgnYCwyVY4NDHKzGmNJT5b+XaQEBniHxsMdeIQLB/tcCzZnhqrzHpZFbX9iaxx+5RiB8caATBtfvZP7zVxQ==", "cpu": [ "arm" ], "dev": true, "license": "MIT", "optional": true, "os": [ "android" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.7.tgz", "integrity": "sha512-l17HE9EweWaqJZhuUuNBN/FzM62xw+DECVnJyvMsxn8vJFAGLy5QfLDoYAcronkAN8VxKZHezDpulHDPx95vFw==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "android" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-darwin-arm64": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.7.tgz", "integrity": "sha512-8ED8ELFvHXc6OCETIn4gXObPiaR6bckM/ipXtbzlPVDRMBfEGjCKgO90F9YtfdpDatVx/ZQw7aZ1vUMf/+T3Mw==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-darwin-x64": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.7.tgz", "integrity": "sha512-/WPripjtiAIZ2tWY7ddijORT0Ujg87wxWW/qcoFVCKAWVDPhtY0xr7Dj0M3GyNGz60jGwTElhro/mkF9dT7dDQ==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-freebsd-x64": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.7.tgz", "integrity": "sha512-14DI4NcqpvbICxSnGLx3PmtDaWqRP/KGSGb6C+JLLVPeZRl6dKdHba3pGsqT3vpdTqhEYIPG0MMQ8c0xYqoJxA==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.7.tgz", "integrity": "sha512-bxrWIRvHWQvbJwi+VIie/kDJmQxcNE6xxWwZdqF/ExVAigtHkv54WTLQPb+QsZdnFy18fg7JPfWGL0RH6vwIlQ==", "cpu": [ "arm" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.7.tgz", "integrity": "sha512-toOY2BChBZyuxU7OYX6Tn389di4IzAqPTycVcci0O7FSfBqzRB3RZn+K5Is6ANf4tmgRd/K1yZTsNTXbkXsnLg==", "cpu": [ "arm64" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-arm64-musl": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.7.tgz", "integrity": "sha512-lAIXTH/aiLRLxsTgQvfhjo4K1ydWIp00+V0voOr9beb/9ZmkUFrSIb03dXNFRgMNvkE6oGsF10ioQ6UsI+vS5Q==", "cpu": [ "arm64" ], "dev": true, "libc": [ "musl" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.7.tgz", "integrity": "sha512-kdnwS28Pkenp/mZMRwjXXXwxQ7pIsm+bF919LUK93BOyhcLsrVKdP2p9fxpiPNPAbNuch8ypQt0pm2P2LYCAGg==", "cpu": [ "ppc64" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.7.tgz", "integrity": "sha512-516OdsyLdr5E65paF3yBF55t8mfm9+gmtCsK3xI7XKXIT7EfRlHhxL8K/NR6Hu8BWSgF5+1w74lTL0+nxcc8Qw==", "cpu": [ "s390x" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-x64-gnu": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.7.tgz", "integrity": "sha512-r8/z8n7GFaYRln3xmP1Cxy0HH/HLM0uBUPkEuSVEfKGDA89M0FsZRZJRSwe/tJjRx+fpH/gjorfhB8tmEbSFLA==", "cpu": [ "x64" ], "dev": true, "libc": [ "glibc" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-x64-musl": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.7.tgz", "integrity": "sha512-pAsE8iiDxUg1xBqdhrTfg45AVDVpirjz00sblEYClGNNcMnDb+e8beQgqIAw6LvauX/APvgxUnwrgun/YYGBhw==", "cpu": [ "x64" ], "dev": true, "libc": [ "musl" ], "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-openharmony-arm64": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.7.tgz", "integrity": "sha512-lTcIYmmnQQA8Or/2DatS6oSqcdLHvendjS+zLu+FwgToynWMRSmQdpM65fTANJgIS4mjbMOo5KT2lnT9SAb96w==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "openharmony" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.7.tgz", "integrity": "sha512-e3Gu3WxbNk/UqQhxqU7YIYO+9ZBvWNz3U+h/qRFosscMFzdRPbXYSaSWgSnklv2fz1TgzBTcti2z35c/7irsHw==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-x64-msvc": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.7.tgz", "integrity": "sha512-W/jg5qoRSqjsEv0+dZi4e687mcHqmVuU0P4fK6qS/xjetW2Gmc1W8j//z5nAeNcC8Ttm0hV46IjcYeuVwYhuiw==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/pluginutils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, "license": "MIT" }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "dev": true, "license": "MIT" }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", "dev": true, "license": "MIT", "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "node_modules/@types/deep-eql": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", "dev": true, "license": "MIT" }, "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/@vitest/expect": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz", "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.11", "@vitest/utils": "4.1.11", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz", "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", "dev": true, "license": "MIT", "dependencies": { "@vitest/spy": "4.1.11", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "msw": { "optional": true }, "vite": { "optional": true } } }, "node_modules/@vitest/pretty-format": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz", "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", "dev": true, "license": "MIT", "dependencies": { "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz", "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", "dev": true, "license": "MIT", "dependencies": { "@vitest/utils": "4.1.11", "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz", "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", "dev": true, "license": "MIT", "dependencies": { "@vitest/pretty-format": "4.1.11", "@vitest/utils": "4.1.11", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/spy": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz", "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", "dev": true, "license": "MIT", "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz", "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", "dev": true, "license": "MIT", "dependencies": { "@vitest/pretty-format": "4.1.11", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", "engines": { "node": ">=12" } }, "node_modules/chai": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", "engines": { "node": ">=18" } }, "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/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, "license": "Apache-2.0", "engines": { "node": ">=8" } }, "node_modules/es-module-lexer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", "dev": true, "license": "MIT" }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, "license": "MIT", "dependencies": { "@types/estree": "^1.0.0" } }, "node_modules/expect-type": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", "dev": true, "license": "Apache-2.0", "engines": { "node": ">=12.0.0" } }, "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/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/lightningcss": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", "dev": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" }, "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" }, "optionalDependencies": { "lightningcss-android-arm64": "1.33.0", "lightningcss-darwin-arm64": "1.33.0", "lightningcss-darwin-x64": "1.33.0", "lightningcss-freebsd-x64": "1.33.0", "lightningcss-linux-arm-gnueabihf": "1.33.0", "lightningcss-linux-arm64-gnu": "1.33.0", "lightningcss-linux-arm64-musl": "1.33.0", "lightningcss-linux-x64-gnu": "1.33.0", "lightningcss-linux-x64-musl": "1.33.0", "lightningcss-win32-arm64-msvc": "1.33.0", "lightningcss-win32-x64-msvc": "1.33.0" } }, "node_modules/lightningcss-android-arm64": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", "cpu": [ "arm64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "android" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-darwin-arm64": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", "cpu": [ "arm64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "darwin" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-darwin-x64": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", "cpu": [ "x64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "darwin" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-freebsd-x64": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", "cpu": [ "x64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "freebsd" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-arm-gnueabihf": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", "cpu": [ "arm" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-arm64-gnu": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", "cpu": [ "arm64" ], "dev": true, "libc": [ "glibc" ], "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-arm64-musl": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", "cpu": [ "arm64" ], "dev": true, "libc": [ "musl" ], "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-x64-gnu": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", "cpu": [ "x64" ], "dev": true, "libc": [ "glibc" ], "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-x64-musl": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", "cpu": [ "x64" ], "dev": true, "libc": [ "musl" ], "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-win32-arm64-msvc": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", "cpu": [ "arm64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "win32" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-win32-x64-msvc": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", "cpu": [ "x64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "win32" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/nanoid": { "version": "3.3.18", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, "engines": { "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, "node_modules/obug": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", "dev": true, "funding": [ "https://github.com/sponsors/sxzz", "https://opencollective.com/debug" ], "license": "MIT" }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT" }, "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.7", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/postcss": { "version": "8.5.28", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", "integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==", "dev": true, "funding": [ { "type": "opencollective", "url": "https://opencollective.com/postcss/" }, { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" }, { "type": "github", "url": "https://github.com/sponsors/ai" } ], "license": "MIT", "dependencies": { "nanoid": "^3.3.18", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, "node_modules/rolldown": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.7.tgz", "integrity": "sha512-g0EtLvBjTUB7jhyV0S/TCup3v/XSVl45vUIGbOGU4QPiyjTenCe4mKuFvW9fEgYmS2Fo42AUssRmNuMziXdrig==", "dev": true, "license": "MIT", "dependencies": { "@oxc-project/types": "=0.148.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { "rolldown": "bin/cli.mjs" }, "engines": { "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { "@rolldown/binding-android-arm-eabi": "1.2.7", "@rolldown/binding-android-arm64": "1.2.7", "@rolldown/binding-darwin-arm64": "1.2.7", "@rolldown/binding-darwin-x64": "1.2.7", "@rolldown/binding-freebsd-x64": "1.2.7", "@rolldown/binding-linux-arm-gnueabihf": "1.2.7", "@rolldown/binding-linux-arm64-gnu": "1.2.7", "@rolldown/binding-linux-arm64-musl": "1.2.7", "@rolldown/binding-linux-ppc64-gnu": "1.2.7", "@rolldown/binding-linux-s390x-gnu": "1.2.7", "@rolldown/binding-linux-x64-gnu": "1.2.7", "@rolldown/binding-linux-x64-musl": "1.2.7", "@rolldown/binding-openharmony-arm64": "1.2.7", "@rolldown/binding-win32-arm64-msvc": "1.2.7", "@rolldown/binding-win32-x64-msvc": "1.2.7" } }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true, "license": "ISC" }, "node_modules/source-map-js": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true, "license": "MIT" }, "node_modules/std-env": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.0.0.tgz", "integrity": "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==", "dev": true, "license": "MIT" }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, "license": "MIT" }, "node_modules/tinyexec": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", "dev": true, "license": "MIT", "engines": { "node": ">=18" } }, "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/tinyrainbow": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz", "integrity": "sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==", "dev": true, "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/vite": { "version": "8.2.2", "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.2.tgz", "integrity": "sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.5", "postcss": "^8.5.26", "rolldown": "~1.2.4", "tinyglobby": "^0.2.17" }, "bin": { "vite": "bin/vite.js" }, "engines": { "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.4.0 || ^0.5.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, "@vitejs/devtools": { "optional": true }, "esbuild": { "optional": true }, "jiti": { "optional": true }, "less": { "optional": true }, "sass": { "optional": true }, "sass-embedded": { "optional": true }, "stylus": { "optional": true }, "sugarss": { "optional": true }, "terser": { "optional": true }, "tsx": { "optional": true }, "yaml": { "optional": true } } }, "node_modules/vitest": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", "dev": true, "license": "MIT", "dependencies": { "@vitest/expect": "4.1.11", "@vitest/mocker": "4.1.11", "@vitest/pretty-format": "4.1.11", "@vitest/runner": "4.1.11", "@vitest/snapshot": "4.1.11", "@vitest/spy": "4.1.11", "@vitest/utils": "4.1.11", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { "node": "^20.0.0 || ^22.0.0 || >=24.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.11", "@vitest/browser-preview": "4.1.11", "@vitest/browser-webdriverio": "4.1.11", "@vitest/coverage-istanbul": "4.1.11", "@vitest/coverage-v8": "4.1.11", "@vitest/ui": "4.1.11", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { "optional": true }, "@opentelemetry/api": { "optional": true }, "@types/node": { "optional": true }, "@vitest/browser-playwright": { "optional": true }, "@vitest/browser-preview": { "optional": true }, "@vitest/browser-webdriverio": { "optional": true }, "@vitest/coverage-istanbul": { "optional": true }, "@vitest/coverage-v8": { "optional": true }, "@vitest/ui": { "optional": true }, "happy-dom": { "optional": true }, "jsdom": { "optional": true }, "vite": { "optional": false } } }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" }, "engines": { "node": ">=8" } } } } axios-axios-aa54a5e/tests/smoke/esm/package.json000066400000000000000000000005311526660636600220160ustar00rootroot00000000000000{ "name": "@axios/esm-smoke-tests", "version": "1.0.0", "description": "ESM smoke tests for axios", "private": true, "scripts": { "test:smoke:esm:vitest": "vitest run --config vitest.config.js --project smoke" }, "keywords": [], "author": "axios team", "license": "MIT", "devDependencies": { "vitest": "4.1.11" } } axios-axios-aa54a5e/tests/smoke/esm/tests/000077500000000000000000000000001526660636600206735ustar00rootroot00000000000000axios-axios-aa54a5e/tests/smoke/esm/tests/auth.smoke.test.js000066400000000000000000000064771526660636600243030ustar00rootroot00000000000000import { afterEach, describe, expect, it } from 'vitest'; import http from 'http'; import axios from 'axios'; const startServer = (handler) => { return new Promise((resolve) => { const server = http.createServer(handler); server.listen(0, '127.0.0.1', () => { resolve(server); }); }); }; const stopServer = (server) => { if (!server || !server.listening) { return Promise.resolve(); } return new Promise((resolve, reject) => { server.close((error) => { if (error) { reject(error); return; } resolve(); }); }); }; describe('auth compat (dist export only)', () => { let server; afterEach(async () => { await stopServer(server); server = undefined; }); const requestWithConfig = async (config) => { server = await startServer((req, res) => { res.setHeader('Content-Type', 'text/plain'); res.end(req.headers.authorization || ''); }); const { port } = server.address(); return axios.get( `http://127.0.0.1:${port}/`, Object.assign( { proxy: false, }, config || {} ) ); }; it('sets Basic Authorization header from auth credentials', async () => { const response = await requestWithConfig({ auth: { username: 'janedoe', password: 's00pers3cret', }, }); const expected = `Basic ${Buffer.from('janedoe:s00pers3cret', 'utf8').toString('base64')}`; expect(response.data).toBe(expected); }); it('supports auth without password', async () => { const response = await requestWithConfig({ auth: { username: 'Aladdin', }, }); const expected = `Basic ${Buffer.from('Aladdin:', 'utf8').toString('base64')}`; expect(response.data).toBe(expected); }); it('overwrites an existing Authorization header when auth is provided', async () => { const response = await requestWithConfig({ headers: { Authorization: 'Bearer token-123', }, auth: { username: 'foo', password: 'bar', }, }); const expected = `Basic ${Buffer.from('foo:bar', 'utf8').toString('base64')}`; expect(response.data).toBe(expected); }); it('uses URL credentials when auth config is not provided (node adapter behavior)', async () => { server = await startServer((req, res) => { res.setHeader('Content-Type', 'text/plain'); res.end(req.headers.authorization || ''); }); const { port } = server.address(); const response = await axios.get(`http://urluser:urlpass@127.0.0.1:${port}/`, { proxy: false, }); const expected = `Basic ${Buffer.from('urluser:urlpass', 'utf8').toString('base64')}`; expect(response.data).toBe(expected); }); it('prefers auth config over URL credentials', async () => { server = await startServer((req, res) => { res.setHeader('Content-Type', 'text/plain'); res.end(req.headers.authorization || ''); }); const { port } = server.address(); const response = await axios.get(`http://urluser:urlpass@127.0.0.1:${port}/`, { proxy: false, auth: { username: 'configuser', password: 'configpass', }, }); const expected = `Basic ${Buffer.from('configuser:configpass', 'utf8').toString('base64')}`; expect(response.data).toBe(expected); }); }); axios-axios-aa54a5e/tests/smoke/esm/tests/basic.smoke.test.js000066400000000000000000000067771526660636600244260ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import { EventEmitter } from 'events'; import { PassThrough } from 'stream'; import axios from 'axios'; const createTransportCapture = () => { let capturedOptions; const transport = { request(options, onResponse) { capturedOptions = options; const req = new EventEmitter(); req.destroyed = false; req.setTimeout = () => {}; req.write = () => true; req.end = () => { const res = new PassThrough(); res.statusCode = 200; res.statusMessage = 'OK'; res.headers = { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end('{"ok":true}'); }; req.destroy = () => { req.destroyed = true; }; req.close = req.destroy; return req; }, }; return { transport, getCapturedOptions: () => capturedOptions, }; }; const runRequest = async (run) => { const { transport, getCapturedOptions } = createTransportCapture(); await run(transport); return getCapturedOptions(); }; describe('basic compat (dist export only)', () => { it('supports the simplest axios(url) request pattern', async () => { const options = await runRequest((transport) => axios('http://example.com/users', { transport, proxy: false, }) ); expect(options.method).toBe('GET'); expect(options.path).toBe('/users'); }); it('supports get()', async () => { const options = await runRequest((transport) => axios.get('http://example.com/items?limit=10', { transport, proxy: false }) ); expect(options.method).toBe('GET'); expect(options.path).toBe('/items?limit=10'); }); it('supports delete()', async () => { const options = await runRequest((transport) => axios.delete('http://example.com/items/1', { transport, proxy: false }) ); expect(options.method).toBe('DELETE'); expect(options.path).toBe('/items/1'); }); it('supports head()', async () => { const options = await runRequest((transport) => axios.head('http://example.com/health', { transport, proxy: false }) ); expect(options.method).toBe('HEAD'); expect(options.path).toBe('/health'); }); it('supports options()', async () => { const options = await runRequest((transport) => axios.options('http://example.com/items', { transport, proxy: false }) ); expect(options.method).toBe('OPTIONS'); expect(options.path).toBe('/items'); }); it('supports post()', async () => { const options = await runRequest((transport) => axios.post( 'http://example.com/items', { name: 'widget' }, { transport, proxy: false, } ) ); expect(options.method).toBe('POST'); expect(options.path).toBe('/items'); }); it('supports put()', async () => { const options = await runRequest((transport) => axios.put( 'http://example.com/items/1', { name: 'updated-widget' }, { transport, proxy: false, } ) ); expect(options.method).toBe('PUT'); expect(options.path).toBe('/items/1'); }); it('supports patch()', async () => { const options = await runRequest((transport) => axios.patch( 'http://example.com/items/1', { status: 'active' }, { transport, proxy: false, } ) ); expect(options.method).toBe('PATCH'); expect(options.path).toBe('/items/1'); }); }); axios-axios-aa54a5e/tests/smoke/esm/tests/cancel.smoke.test.js000066400000000000000000000055221526660636600245550ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import { EventEmitter } from 'events'; import axios from 'axios'; const createPendingTransport = () => { let requestCount = 0; const transport = { request() { requestCount += 1; const req = new EventEmitter(); req.destroyed = false; req.setTimeout = () => {}; req.write = () => true; req.end = () => {}; req.destroy = () => { req.destroyed = true; }; req.close = req.destroy; return req; }, }; return { transport, getRequestCount: () => requestCount, }; }; describe('cancel compat (dist export only)', () => { it('supports cancellation with AbortController (pre-aborted signal)', async () => { const { transport, getRequestCount } = createPendingTransport(); const controller = new AbortController(); controller.abort(); try { const request = axios.get('http://example.com/resource', { signal: controller.signal, transport, proxy: false, }); controller.abort(); await request; } catch (error) { expect(error.code).toBe('ERR_CANCELED'); } expect(getRequestCount()).toBe(0); }); it('supports cancellation with AbortController (in-flight)', async () => { const { transport, getRequestCount } = createPendingTransport(); const controller = new AbortController(); try { const request = axios.get('http://example.com/resource', { signal: controller.signal, transport, proxy: false, }); controller.abort(); await request; } catch (error) { expect(error.code).toBe('ERR_CANCELED'); } expect(getRequestCount()).toBe(1); }); it('supports cancellation with CancelToken (pre-canceled token)', async () => { const { transport, getRequestCount } = createPendingTransport(); const source = axios.CancelToken.source(); source.cancel('Operation canceled by the user.'); const error = await axios .get('http://example.com/resource', { cancelToken: source.token, transport, proxy: false, }) .catch((err) => err); expect(axios.isCancel(error)).toBe(true); expect(error.code).toBe('ERR_CANCELED'); expect(getRequestCount()).toBe(0); }); it('supports cancellation with CancelToken (in-flight)', async () => { const { transport, getRequestCount } = createPendingTransport(); const source = axios.CancelToken.source(); const request = axios.get('http://example.com/resource', { cancelToken: source.token, transport, proxy: false, }); source.cancel('Operation canceled by the user.'); const error = await request.catch((err) => err); expect(axios.isCancel(error)).toBe(true); expect(error.code).toBe('ERR_CANCELED'); expect(getRequestCount()).toBe(1); }); }); axios-axios-aa54a5e/tests/smoke/esm/tests/error.smoke.test.js000066400000000000000000000074761526660636600244730ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import { EventEmitter } from 'events'; import { PassThrough } from 'stream'; import axios from 'axios'; const createTransport = (config) => { const opts = config || {}; return { request(options, onResponse) { const req = new EventEmitter(); req.destroyed = false; req.write = () => true; req.destroy = () => { req.destroyed = true; }; req.close = req.destroy; req.setTimeout = (_ms, cb) => { if (opts.timeout) { req._timeoutCallback = cb; } }; req.end = () => { if (opts.error) { req.emit('error', opts.error); return; } if (opts.timeout && req._timeoutCallback) { req._timeoutCallback(); return; } const res = new PassThrough(); res.statusCode = opts.response && opts.response.statusCode !== undefined ? opts.response.statusCode : 200; res.statusMessage = opts.response && opts.response.statusMessage ? opts.response.statusMessage : 'OK'; res.headers = opts.response && opts.response.headers ? opts.response.headers : { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end( opts.response && opts.response.body !== undefined ? opts.response.body : '{"ok":true}' ); }; return req; }, }; }; describe('error compat (dist export only)', () => { it('rejects with AxiosError for non-2xx responses by default', async () => { const err = await axios .get('http://example.com/fail', { proxy: false, transport: createTransport({ response: { statusCode: 500, statusMessage: 'Internal Server Error', body: '{"error":"boom"}', }, }), }) .catch((e) => e); expect(axios.isAxiosError(err)).toBe(true); expect(err.response.status).toBe(500); expect(err.message).toContain('500'); }); it('resolves when validateStatus allows non-2xx responses', async () => { const response = await axios.get('http://example.com/allowed', { proxy: false, validateStatus: () => true, transport: createTransport({ response: { statusCode: 500, statusMessage: 'Internal Server Error', body: '{"ok":false}', }, }), }); expect(response.status).toBe(500); expect(response.data).toEqual({ ok: false }); }); it('wraps transport errors as AxiosError', async () => { const err = await axios .get('http://example.com/network', { proxy: false, transport: createTransport({ error: new Error('socket hang up'), }), }) .catch((e) => e); expect(axios.isAxiosError(err)).toBe(true); expect(err.message).toContain('socket hang up'); expect(err.toJSON).toBeTypeOf('function'); }); it('rejects with ECONNABORTED on timeout', async () => { const err = await axios .get('http://example.com/timeout', { proxy: false, timeout: 10, transport: createTransport({ timeout: true }), }) .catch((e) => e); expect(axios.isAxiosError(err)).toBe(true); expect(err.code).toBe('ECONNABORTED'); expect(err.message).toBe('timeout of 10ms exceeded'); }); it('uses timeoutErrorMessage when provided', async () => { const err = await axios .get('http://example.com/timeout', { proxy: false, timeout: 25, timeoutErrorMessage: 'custom timeout message', transport: createTransport({ timeout: true }), }) .catch((e) => e); expect(axios.isAxiosError(err)).toBe(true); expect(err.code).toBe('ECONNABORTED'); expect(err.message).toBe('custom timeout message'); }); }); axios-axios-aa54a5e/tests/smoke/esm/tests/fetch.smoke.test.js000066400000000000000000000073151526660636600244230ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import axios from 'axios'; const createFetchMock = (responseFactory) => { const calls = []; const mockFetch = async (input, init) => { calls.push({ input, init: init || {} }); if (responseFactory) { return responseFactory(input, init || {}); } return new Response(JSON.stringify({ ok: true }), { status: 200, headers: { 'Content-Type': 'application/json' }, }); }; return { mockFetch, getCalls: () => calls, }; }; describe('fetch compat (dist export only)', () => { it('uses fetch adapter and resolves JSON response', async () => { const { mockFetch, getCalls } = createFetchMock(); const response = await axios.get('https://example.com/users', { adapter: 'fetch', env: { fetch: mockFetch, Request, Response, }, }); expect(response.data).toEqual({ ok: true }); expect(response.status).toBe(200); expect(getCalls()).toHaveLength(1); }); it('sends method, headers and body for post requests', async () => { const { mockFetch, getCalls } = createFetchMock(async (input, init) => { const requestInit = init || {}; const isRequest = input && typeof input !== 'string'; const method = isRequest ? input.method : requestInit.method; const body = isRequest && typeof input.clone === 'function' ? await input.clone().text() : requestInit.body; let contentType; if (isRequest && input.headers) { contentType = input.headers.get('content-type'); } else if (requestInit.headers) { contentType = requestInit.headers['Content-Type'] || requestInit.headers['content-type']; } return new Response( JSON.stringify({ url: typeof input === 'string' ? input : input.url, method, contentType, body, }), { status: 200, headers: { 'Content-Type': 'application/json' }, } ); }); const response = await axios.post( 'https://example.com/items', { name: 'widget' }, { adapter: 'fetch', env: { fetch: mockFetch, Request, Response, }, } ); expect(getCalls()).toHaveLength(1); expect(response.data.url).toBe('https://example.com/items'); expect(response.data.method).toBe('POST'); expect(response.data.contentType).toContain('application/json'); expect(response.data.body).toBe(JSON.stringify({ name: 'widget' })); }); it('rejects non-2xx fetch responses by default', async () => { const { mockFetch } = createFetchMock( () => new Response(JSON.stringify({ error: 'boom' }), { status: 500, statusText: 'Internal Server Error', headers: { 'Content-Type': 'application/json' }, }) ); const err = await axios .get('https://example.com/fail', { adapter: 'fetch', env: { fetch: mockFetch, Request, Response, }, }) .catch((e) => e); expect(axios.isAxiosError(err)).toBe(true); expect(err.response.status).toBe(500); }); it('supports cancellation with AbortController in fetch mode', async () => { const { mockFetch } = createFetchMock(); const controller = new AbortController(); controller.abort(); const err = await axios .get('https://example.com/cancel', { adapter: 'fetch', signal: controller.signal, env: { fetch: mockFetch, Request, Response, }, }) .catch((e) => e); expect(axios.isCancel(err)).toBe(true); expect(err.code).toBe('ERR_CANCELED'); }); }); axios-axios-aa54a5e/tests/smoke/esm/tests/files.smoke.test.js000066400000000000000000000070301526660636600244260ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import { PassThrough, Readable, Writable } from 'stream'; import axios from 'axios'; const createCaptureTransport = (buildResponse) => { return { request(options, onResponse) { const chunks = []; const req = new Writable({ write(chunk, _encoding, callback) { chunks.push(Buffer.from(chunk)); callback(); }, }); req.destroyed = false; req.setTimeout = () => {}; req.close = () => { req.destroyed = true; }; const originalDestroy = req.destroy.bind(req); req.destroy = (...args) => { req.destroyed = true; return originalDestroy(...args); }; const originalEnd = req.end.bind(req); req.end = (...args) => { originalEnd(...args); const body = Buffer.concat(chunks); const response = buildResponse ? buildResponse(body, options) : {}; const res = new PassThrough(); res.statusCode = response.statusCode !== undefined ? response.statusCode : 200; res.statusMessage = response.statusMessage || 'OK'; res.headers = response.headers || { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end(response.body || JSON.stringify({ size: body.length })); }; req.on('error', () => {}); return req; }, }; }; describe('files compat (dist export only)', () => { it('supports posting Buffer payloads', async () => { const source = Buffer.from('binary-\x00-data', 'utf8'); const response = await axios.post('http://example.com/upload', source, { proxy: false, transport: createCaptureTransport((body) => ({ body: JSON.stringify({ echoed: body.toString('base64') }), })), }); expect(response.data.echoed).toBe(source.toString('base64')); }); it('supports posting Uint8Array payloads', async () => { const source = Uint8Array.from([1, 2, 3, 4, 255]); const response = await axios.post('http://example.com/upload', source, { proxy: false, transport: createCaptureTransport((body) => ({ body: JSON.stringify({ echoed: Array.from(body.values()) }), })), }); expect(response.data.echoed).toEqual([1, 2, 3, 4, 255]); }); it('supports posting Readable stream payloads', async () => { const streamData = ['hello ', 'stream ', 'world']; const source = Readable.from(streamData); const response = await axios.post('http://example.com/upload', source, { proxy: false, headers: { 'Content-Type': 'application/octet-stream' }, transport: createCaptureTransport((body, options) => ({ body: JSON.stringify({ text: body.toString('utf8'), contentType: options.headers && (options.headers['Content-Type'] || options.headers['content-type']), }), })), }); expect(response.data.text).toBe('hello stream world'); expect(response.data.contentType).toContain('application/octet-stream'); }); it('supports binary downloads with responseType=arraybuffer', async () => { const binary = Buffer.from([0xde, 0xad, 0xbe, 0xef]); const response = await axios.get('http://example.com/file.bin', { proxy: false, responseType: 'arraybuffer', transport: createCaptureTransport(() => ({ headers: { 'content-type': 'application/octet-stream' }, body: binary, })), }); expect(Buffer.isBuffer(response.data)).toBe(true); expect(response.data.equals(binary)).toBe(true); }); }); axios-axios-aa54a5e/tests/smoke/esm/tests/formData.smoke.test.js000066400000000000000000000063331526660636600250660ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import { Writable, PassThrough } from 'stream'; import axios from 'axios'; const createCaptureTransport = (buildResponse) => { return { request(options, onResponse) { const chunks = []; const req = new Writable({ write(chunk, _encoding, callback) { chunks.push(Buffer.from(chunk)); callback(); }, }); req.destroyed = false; req.setTimeout = () => {}; req.write = req.write.bind(req); req.close = () => { req.destroyed = true; }; const originalDestroy = req.destroy.bind(req); req.destroy = (...args) => { req.destroyed = true; return originalDestroy(...args); }; const originalEnd = req.end.bind(req); req.end = (...args) => { originalEnd(...args); const body = Buffer.concat(chunks); const response = buildResponse ? buildResponse(body, options) : {}; const res = new PassThrough(); res.statusCode = response.statusCode !== undefined ? response.statusCode : 200; res.statusMessage = response.statusMessage || 'OK'; res.headers = response.headers || { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end(response.body || JSON.stringify({ ok: true })); }; return req; }, }; }; const bodyAsUtf8 = (value) => { return Buffer.isBuffer(value) ? value.toString('utf8') : String(value); }; describe('formData compat (dist export only)', () => { it('supports posting FormData instances', async () => { const form = new FormData(); form.append('username', 'janedoe'); form.append('role', 'admin'); const response = await axios.post('http://example.com/form', form, { proxy: false, transport: createCaptureTransport((body, options) => ({ body: JSON.stringify({ contentType: options.headers && (options.headers['Content-Type'] || options.headers['content-type']), payload: bodyAsUtf8(body), }), })), }); expect(response.data.contentType).toContain('multipart/form-data'); expect(response.data.payload).toContain('name="username"'); expect(response.data.payload).toContain('janedoe'); expect(response.data.payload).toContain('name="role"'); expect(response.data.payload).toContain('admin'); }); it('supports axios.postForm helper', async () => { const response = await axios.postForm( 'http://example.com/post-form', { project: 'axios', mode: 'compat', }, { proxy: false, transport: createCaptureTransport((body, options) => ({ body: JSON.stringify({ contentType: options.headers && (options.headers['Content-Type'] || options.headers['content-type']), payload: bodyAsUtf8(body), }), })), } ); expect(response.data.contentType).toContain('multipart/form-data'); expect(response.data.payload).toContain('name="project"'); expect(response.data.payload).toContain('axios'); expect(response.data.payload).toContain('name="mode"'); expect(response.data.payload).toContain('compat'); }); }); axios-axios-aa54a5e/tests/smoke/esm/tests/header-buckets.smoke.test.js000066400000000000000000000032561526660636600262200ustar00rootroot00000000000000import { createRequire } from 'node:module'; import { describe, it, expect } from 'vitest'; import axios from 'axios'; import { echoHeaders } from '../../../setup/adapters.js'; const cjsAxios = createRequire(import.meta.url)('axios'); describe('header buckets across package exports', () => { for (const [name, client, otherClient] of [ ['ESM', axios, cjsAxios], ['CommonJS', cjsAxios, axios], ]) { for (const [shape, Headers] of [ ['instances', otherClient.AxiosHeaders], ['subclasses', class extends otherClient.AxiosHeaders {}], ]) { it(`should preserve foreign AxiosHeaders ${shape} as buckets in the ${name} client`, async () => { expect(client.AxiosHeaders).not.toBe(otherClient.AxiosHeaders); const instance = client.create({ adapter: echoHeaders, headers: { common: new Headers({ 'X-Common': 'common' }), post: new Headers({ Authorization: 'Bearer TEST_ONLY' }), }, }); for (const method of ['get', 'post']) { const { config } = await instance.request({ url: '/header-buckets', method, headers: { Link: Buffer.from('; rel="type"') }, }); expect(config.headers.get('X-Common')).toBe('common'); expect(config.headers.get('Authorization')).toBe( method === 'post' ? 'Bearer TEST_ONLY' : undefined ); expect(config.headers.get('Link')).toBe('; rel="type"'); expect(config.headers.has('common')).toBe(false); expect(config.headers.has('post')).toBe(false); } }); } } }); axios-axios-aa54a5e/tests/smoke/esm/tests/headers.smoke.test.js000066400000000000000000000064641526660636600247510ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import { EventEmitter } from 'events'; import { PassThrough } from 'stream'; import axios from 'axios'; const normalizeHeaders = (headers) => { const result = {}; Object.entries(headers || {}).forEach(([key, value]) => { result[key.toLowerCase()] = value; }); return result; }; const createTransportCapture = () => { let capturedOptions; const transport = { request(options, onResponse) { capturedOptions = options; const req = new EventEmitter(); req.destroyed = false; req.setTimeout = () => {}; req.write = () => true; req.destroy = () => { req.destroyed = true; }; req.close = req.destroy; req.end = () => { const res = new PassThrough(); res.statusCode = 200; res.statusMessage = 'OK'; res.headers = { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end('{"ok":true}'); }; return req; }, }; return { transport, getCapturedOptions: () => capturedOptions, }; }; describe('headers compat (dist export only)', () => { it('sends default Accept header', async () => { const { transport, getCapturedOptions } = createTransportCapture(); await axios.get('http://example.com/default-headers', { transport, proxy: false, }); const headers = normalizeHeaders(getCapturedOptions().headers); expect(headers.accept).toBe('application/json, text/plain, */*'); }); it('supports custom headers', async () => { const { transport, getCapturedOptions } = createTransportCapture(); await axios.get('http://example.com/custom-headers', { transport, proxy: false, headers: { 'X-Trace-Id': 'trace-123', Authorization: 'Bearer token-abc', }, }); const headers = normalizeHeaders(getCapturedOptions().headers); expect(headers['x-trace-id']).toBe('trace-123'); expect(headers.authorization).toBe('Bearer token-abc'); }); it('treats header names as case-insensitive when overriding', async () => { const { transport, getCapturedOptions } = createTransportCapture(); await axios.get('http://example.com/case-insensitive', { transport, proxy: false, headers: { authorization: 'Bearer old-token', AuThOrIzAtIoN: 'Bearer new-token', }, }); const headers = normalizeHeaders(getCapturedOptions().headers); expect(headers.authorization).toBe('Bearer new-token'); }); it('sets content-type for json post payloads', async () => { const { transport, getCapturedOptions } = createTransportCapture(); await axios.post( 'http://example.com/post-json', { name: 'widget' }, { transport, proxy: false, } ); const headers = normalizeHeaders(getCapturedOptions().headers); expect(headers['content-type']).toContain('application/json'); }); it('does not force content-type for get requests without body', async () => { const { transport, getCapturedOptions } = createTransportCapture(); await axios.get('http://example.com/get-no-body', { transport, proxy: false, }); const headers = normalizeHeaders(getCapturedOptions().headers); expect(headers['content-type']).toBeUndefined(); }); }); axios-axios-aa54a5e/tests/smoke/esm/tests/http2.smoke.test.js000066400000000000000000000040301526660636600243620ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import axios from 'axios'; describe('http2 compat (dist export only)', () => { it('keeps instance-level httpVersion and http2Options in request config', async () => { const client = axios.create({ baseURL: 'https://example.com', httpVersion: 2, http2Options: { rejectUnauthorized: false, }, }); const response = await client.get('/resource', { adapter: async (config) => ({ data: { httpVersion: config.httpVersion, http2Options: config.http2Options, }, status: 200, statusText: 'OK', headers: {}, config, }), }); expect(response.data.httpVersion).toBe(2); expect(response.data.http2Options).toEqual({ rejectUnauthorized: false, }); }); it('merges request http2Options with instance http2Options', async () => { const client = axios.create({ baseURL: 'https://example.com', httpVersion: 2, http2Options: { rejectUnauthorized: false, sessionTimeout: 1000, }, }); const response = await client.get('/resource', { http2Options: { sessionTimeout: 5000, customFlag: true, }, adapter: async (config) => ({ data: config.http2Options, status: 200, statusText: 'OK', headers: {}, config, }), }); expect(response.data).toEqual({ rejectUnauthorized: false, sessionTimeout: 5000, customFlag: true, }); }); it('allows request-level httpVersion override', async () => { const client = axios.create({ baseURL: 'https://example.com', httpVersion: 2, }); const response = await client.get('/resource', { httpVersion: 1, adapter: async (config) => ({ data: { httpVersion: config.httpVersion, }, status: 200, statusText: 'OK', headers: {}, config, }), }); expect(response.data.httpVersion).toBe(1); }); }); axios-axios-aa54a5e/tests/smoke/esm/tests/import.smoke.test.js000066400000000000000000000020741526660636600246410ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import axios, { CanceledError, AxiosError, AxiosHeaders } from 'axios'; import settle from 'axios/unsafe/core/settle.js'; describe('ESM importing', () => { it('should import axios', () => { expect(typeof axios).toStrictEqual('function'); }); it('should import CanceledError', () => { expect(typeof CanceledError).toStrictEqual('function'); }); it('should import AxiosError', () => { expect(typeof AxiosError).toStrictEqual('function'); }); it('should import AxiosHeaders', () => { expect(typeof AxiosHeaders).toStrictEqual('function'); }); it('should import settle', () => { expect(typeof settle).toStrictEqual('function'); }); it('should import CanceledError from axios', () => { expect(axios.CanceledError).toStrictEqual(CanceledError); }); it('should import AxiosError from axios', () => { expect(axios.AxiosError).toStrictEqual(AxiosError); }); it('should import AxiosHeaders from axios', () => { expect(axios.AxiosHeaders).toStrictEqual(AxiosHeaders); }); }); axios-axios-aa54a5e/tests/smoke/esm/tests/instance.smoke.test.js000066400000000000000000000072771526660636600251450ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import { EventEmitter } from 'events'; import { PassThrough } from 'stream'; import axios from 'axios'; const createTransportCapture = (responseBody) => { const calls = []; const transport = { request(options, onResponse) { calls.push(options); const req = new EventEmitter(); req.destroyed = false; req.setTimeout = () => {}; req.write = () => true; req.destroy = () => { req.destroyed = true; }; req.close = req.destroy; req.end = () => { const res = new PassThrough(); res.statusCode = 200; res.statusMessage = 'OK'; res.headers = { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end(responseBody ? responseBody : '{"ok":true}'); }; return req; }, }; return { transport, getCalls: () => calls, }; }; describe('instance compat (dist export only)', () => { it('creates isolated instances with separate defaults', async () => { const { transport, getCalls } = createTransportCapture(); const clientA = axios.create({ baseURL: 'http://example.com/api-a', headers: { 'X-App': 'A', }, }); const clientB = axios.create({ baseURL: 'http://example.com/api-b', headers: { 'X-App': 'B', }, }); await clientA.get('/users', { transport, proxy: false }); await clientB.get('/users', { transport, proxy: false }); const [callA, callB] = getCalls(); expect(callA.path).toBe('/api-a/users'); expect(callB.path).toBe('/api-b/users'); expect(callA.headers['X-App']).toBe('A'); expect(callB.headers['X-App']).toBe('B'); }); it('supports callable instance form instance(config)', async () => { const { transport, getCalls } = createTransportCapture(); const client = axios.create({ baseURL: 'http://example.com', }); await client({ url: '/status', method: 'get', transport, proxy: false, }); expect(getCalls()).toHaveLength(1); expect(getCalls()[0].method).toBe('GET'); expect(getCalls()[0].path).toBe('/status'); }); it('applies instance request interceptors', async () => { const { transport, getCalls } = createTransportCapture(); const client = axios.create({ baseURL: 'http://example.com', }); client.interceptors.request.use((config) => { config.headers = config.headers || {}; config.headers['X-From-Interceptor'] = 'yes'; return config; }); await client.get('/intercepted', { transport, proxy: false }); expect(getCalls()).toHaveLength(1); expect(getCalls()[0].headers['X-From-Interceptor']).toBe('yes'); }); it('applies instance response interceptors', async () => { const { transport } = createTransportCapture('{"name":"axios"}'); const client = axios.create({ baseURL: 'http://example.com', }); client.interceptors.response.use((response) => { response.data = Object.assign({}, response.data, { intercepted: true, }); return response; }); const response = await client.get('/response-interceptor', { transport, proxy: false, }); expect(response.data).toEqual({ name: 'axios', intercepted: true, }); }); it('builds URLs with getUri from instance defaults and request params', () => { const client = axios.create({ baseURL: 'http://example.com/api', params: { apiKey: 'abc', }, }); const uri = client.getUri({ url: '/users', params: { page: 2, }, }); expect(uri).toBe('http://example.com/api/users?apiKey=abc&page=2'); }); }); axios-axios-aa54a5e/tests/smoke/esm/tests/interceptors.smoke.test.js000066400000000000000000000075031526660636600260520ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import { EventEmitter } from 'events'; import { PassThrough } from 'stream'; import axios from 'axios'; const createTransport = (responseBody) => { const calls = []; const transport = { request(options, onResponse) { calls.push(options); const req = new EventEmitter(); req.destroyed = false; req.setTimeout = () => {}; req.write = () => true; req.destroy = () => { req.destroyed = true; }; req.close = req.destroy; req.end = () => { const res = new PassThrough(); res.statusCode = 200; res.statusMessage = 'OK'; res.headers = { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end(responseBody ? responseBody : '{"value":"ok"}'); }; return req; }, }; return { transport, getCalls: () => calls, }; }; describe('interceptors compat (dist export only)', () => { it('applies request interceptors before dispatch', async () => { const { transport, getCalls } = createTransport(); const client = axios.create(); client.interceptors.request.use((config) => { config.headers = config.headers || {}; config.headers['X-One'] = '1'; return config; }); client.interceptors.request.use((config) => { config.headers['X-Two'] = '2'; return config; }); await client.get('http://example.com/resource', { transport, proxy: false, }); expect(getCalls()).toHaveLength(1); expect(getCalls()[0].headers['X-One']).toBe('1'); expect(getCalls()[0].headers['X-Two']).toBe('2'); }); it('applies response interceptors in registration order', async () => { const { transport } = createTransport('{"n":1}'); const client = axios.create(); client.interceptors.response.use((response) => { response.data.n += 1; return response; }); client.interceptors.response.use((response) => { response.data.n *= 10; return response; }); const response = await client.get('http://example.com/resource', { transport, proxy: false, }); expect(response.data.n).toBe(20); }); it('supports ejecting request interceptors', async () => { const { transport, getCalls } = createTransport(); const client = axios.create(); const id = client.interceptors.request.use((config) => { config.headers = config.headers || {}; config.headers['X-Ejected'] = 'yes'; return config; }); client.interceptors.request.eject(id); await client.get('http://example.com/resource', { transport, proxy: false, }); expect(getCalls()).toHaveLength(1); expect(getCalls()[0].headers['X-Ejected']).toBeUndefined(); }); it('supports async request interceptors', async () => { const { transport, getCalls } = createTransport(); const client = axios.create(); client.interceptors.request.use(async (config) => { await Promise.resolve(); config.headers = config.headers || {}; config.headers['X-Async'] = 'true'; return config; }); await client.get('http://example.com/resource', { transport, proxy: false, }); expect(getCalls()[0].headers['X-Async']).toBe('true'); }); it('propagates errors thrown by request interceptors', async () => { const { transport, getCalls } = createTransport(); const client = axios.create(); client.interceptors.request.use(() => { throw new Error('blocked-by-interceptor'); }); const err = await client .get('http://example.com/resource', { transport, proxy: false, }) .catch((e) => e); expect(err).toBeInstanceOf(Error); expect(err.message).toContain('blocked-by-interceptor'); expect(getCalls()).toHaveLength(0); }); }); axios-axios-aa54a5e/tests/smoke/esm/tests/progress.smoke.test.js000066400000000000000000000056351526660636600252010ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import { Readable, Writable, PassThrough } from 'stream'; import axios from 'axios'; const createProgressTransport = (config) => { const opts = config || {}; const responseChunks = opts.responseChunks || ['ok']; const responseHeaders = opts.responseHeaders || {}; return { request(_options, onResponse) { const req = new Writable({ write(_chunk, _encoding, callback) { callback(); }, }); req.destroyed = false; req.setTimeout = () => {}; req.close = () => { req.destroyed = true; }; const originalDestroy = req.destroy.bind(req); req.destroy = (...args) => { req.destroyed = true; return originalDestroy(...args); }; const originalEnd = req.end.bind(req); req.end = (...args) => { originalEnd(...args); const res = new PassThrough(); res.statusCode = 200; res.statusMessage = 'OK'; res.headers = Object.assign( { 'content-type': 'text/plain', }, responseHeaders ); res.req = req; onResponse(res); responseChunks.forEach((chunk) => { res.write(chunk); }); res.end(); }; return req; }, }; }; describe('progress compat (dist export only)', () => { it('emits upload progress events for stream payloads', async () => { const samples = []; const payload = ['abc', 'def', 'ghi']; const total = payload.join('').length; await axios.post('http://example.com/upload', Readable.from(payload), { proxy: false, headers: { 'Content-Length': String(total), }, onUploadProgress: ({ loaded, total: reportedTotal, upload }) => { samples.push({ loaded, total: reportedTotal, upload }); }, transport: createProgressTransport({ responseChunks: ['uploaded'], }), }); expect(samples.length).toBeGreaterThan(0); expect(samples[samples.length - 1]).toMatchObject({ loaded: total, total, upload: true, }); }); it('emits download progress events', async () => { const samples = []; const chunks = ['ab', 'cd', 'ef']; const total = chunks.join('').length; const response = await axios.get('http://example.com/download', { proxy: false, responseType: 'text', onDownloadProgress: ({ loaded, total: reportedTotal, download }) => { samples.push({ loaded, total: reportedTotal, download }); }, transport: createProgressTransport({ responseChunks: chunks, responseHeaders: { 'content-length': String(total), }, }), }); expect(response.data).toBe('abcdef'); expect(samples.length).toBeGreaterThan(0); expect(samples[samples.length - 1]).toMatchObject({ loaded: total, total, download: true, }); }); }); axios-axios-aa54a5e/tests/smoke/esm/tests/rateLimit.smoke.test.js000066400000000000000000000052061526660636600252610ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import { EventEmitter } from 'events'; import { PassThrough } from 'stream'; import axios from 'axios'; const createTransportCapture = () => { let capturedOptions; const transport = { request(options, onResponse) { capturedOptions = options; const req = new EventEmitter(); req.destroyed = false; req.setTimeout = () => {}; req.write = () => true; req.destroy = () => { req.destroyed = true; }; req.close = req.destroy; req.end = () => { const res = new PassThrough(); res.statusCode = 200; res.statusMessage = 'OK'; res.headers = { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end('{"ok":true}'); }; return req; }, }; return { transport, getCapturedOptions: () => capturedOptions, }; }; describe('rateLimit compat (dist export only)', () => { it('accepts numeric maxRate config', async () => { const response = await axios.get('http://example.com/rate', { maxRate: 1024, adapter: async (config) => ({ data: { maxRate: config.maxRate }, status: 200, statusText: 'OK', headers: {}, config, }), }); expect(response.data.maxRate).toBe(1024); }); it('accepts tuple maxRate config [upload, download]', async () => { const response = await axios.get('http://example.com/rate', { maxRate: [2048, 4096], adapter: async (config) => ({ data: { maxRate: config.maxRate }, status: 200, statusText: 'OK', headers: {}, config, }), }); expect(response.data.maxRate).toEqual([2048, 4096]); }); it('merges instance and request maxRate values', async () => { const client = axios.create({ maxRate: [1000, 2000], }); const response = await client.get('http://example.com/rate', { maxRate: [3000, 4000], adapter: async (config) => ({ data: { maxRate: config.maxRate }, status: 200, statusText: 'OK', headers: {}, config, }), }); expect(response.data.maxRate).toEqual([3000, 4000]); }); it('supports maxRate in node transport flow without errors', async () => { const { transport, getCapturedOptions } = createTransportCapture(); const response = await axios.get('http://example.com/rate', { proxy: false, maxRate: [1500, 2500], transport, }); expect(response.status).toBe(200); expect(getCapturedOptions().method).toBe('GET'); expect(getCapturedOptions().path).toBe('/rate'); }); }); axios-axios-aa54a5e/tests/smoke/esm/tests/timeout.smoke.test.js000066400000000000000000000062541526660636600250210ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import { EventEmitter } from 'events'; import { PassThrough } from 'stream'; import axios from 'axios'; const createTransport = (config) => { const opts = config || {}; return { request(_options, onResponse) { const req = new EventEmitter(); req.destroyed = false; req._timeoutCallback = null; req.setTimeout = (_ms, callback) => { req._timeoutCallback = callback; }; req.write = () => true; req.destroy = () => { req.destroyed = true; }; req.close = req.destroy; req.end = () => { if (opts.triggerTimeout && req._timeoutCallback) { req._timeoutCallback(); return; } const res = new PassThrough(); res.statusCode = 200; res.statusMessage = 'OK'; res.headers = { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end(opts.body === undefined ? '{"ok":true}' : opts.body); }; return req; }, }; }; describe('timeout compat (dist export only)', () => { it('rejects with ECONNABORTED on timeout', async () => { const err = await axios .get('http://example.com/timeout', { proxy: false, timeout: 25, transport: createTransport({ triggerTimeout: true }), }) .catch((e) => e); expect(axios.isAxiosError(err)).toBe(true); expect(err.code).toBe('ECONNABORTED'); expect(err.message).toBe('timeout of 25ms exceeded'); }); it('uses timeoutErrorMessage when provided', async () => { const err = await axios .get('http://example.com/timeout', { proxy: false, timeout: 25, timeoutErrorMessage: 'custom timeout', transport: createTransport({ triggerTimeout: true }), }) .catch((e) => e); expect(axios.isAxiosError(err)).toBe(true); expect(err.code).toBe('ECONNABORTED'); expect(err.message).toBe('custom timeout'); }); it('accepts timeout as a numeric string', async () => { const err = await axios .get('http://example.com/timeout', { proxy: false, timeout: '30', transport: createTransport({ triggerTimeout: true }), }) .catch((e) => e); expect(axios.isAxiosError(err)).toBe(true); expect(err.code).toBe('ECONNABORTED'); expect(err.message).toBe('timeout of 30ms exceeded'); }); it('rejects with ERR_BAD_OPTION_VALUE when timeout is not parsable', async () => { const err = await axios .get('http://example.com/timeout', { proxy: false, timeout: { invalid: true }, transport: createTransport(), }) .catch((e) => e); expect(axios.isAxiosError(err)).toBe(true); expect(err.code).toBe('ERR_BAD_OPTION_VALUE'); expect(err.message).toBe('error trying to parse `config.timeout` to int'); }); it('does not time out when timeout is 0', async () => { const response = await axios.get('http://example.com/no-timeout', { proxy: false, timeout: 0, transport: createTransport({ body: '{"ok":true}' }), }); expect(response.status).toBe(200); expect(response.data).toEqual({ ok: true }); }); }); axios-axios-aa54a5e/tests/smoke/esm/tests/urlencode.smoke.test.js000066400000000000000000000073201526660636600253060ustar00rootroot00000000000000import { describe, expect, it } from 'vitest'; import { EventEmitter } from 'events'; import { PassThrough } from 'stream'; import axios from 'axios'; const createEchoTransport = () => { let capturedOptions; const transport = { request(options, onResponse) { capturedOptions = options; const chunks = []; const req = new EventEmitter(); req.destroyed = false; req.setTimeout = () => {}; req.destroy = () => { req.destroyed = true; }; req.close = req.destroy; req.write = (chunk) => { chunks.push(Buffer.from(chunk)); return true; }; req.end = (chunk) => { if (chunk) { chunks.push(Buffer.from(chunk)); } const res = new PassThrough(); res.statusCode = 200; res.statusMessage = 'OK'; res.headers = { 'content-type': 'application/json' }; res.req = req; onResponse(res); res.end( JSON.stringify({ path: options.path, body: Buffer.concat(chunks).toString('utf8'), contentType: options.headers && (options.headers['Content-Type'] || options.headers['content-type']), }) ); }; return req; }, }; return { transport, getCapturedOptions: () => capturedOptions, }; }; describe('urlencode compat (dist export only)', () => { it('serializes params into request URL', async () => { const { transport } = createEchoTransport(); const response = await axios.get('http://example.com/search', { proxy: false, transport, params: { q: 'axios docs', page: 2, }, }); expect(response.data.path).toBe('/search?q=axios+docs&page=2'); }); it('supports custom paramsSerializer function', async () => { const { transport } = createEchoTransport(); const response = await axios.get('http://example.com/search', { proxy: false, transport, params: { q: 'ignored' }, paramsSerializer: () => 'fixed=1', }); expect(response.data.path).toBe('/search?fixed=1'); }); it('supports URLSearchParams payloads', async () => { const { transport } = createEchoTransport(); const payload = new URLSearchParams(); payload.append('name', 'axios'); payload.append('mode', 'compat'); const response = await axios.post('http://example.com/form', payload, { proxy: false, transport, }); expect(response.data.body).toBe('name=axios&mode=compat'); expect(response.data.contentType).toContain('application/x-www-form-urlencoded'); }); it('serializes object payload when content-type is application/x-www-form-urlencoded', async () => { const { transport } = createEchoTransport(); const response = await axios.post( 'http://example.com/form', { name: 'axios', mode: 'compat', }, { proxy: false, transport, headers: { 'content-type': 'application/x-www-form-urlencoded', }, } ); expect(response.data.body).toBe('name=axios&mode=compat'); expect(response.data.contentType).toContain('application/x-www-form-urlencoded'); }); it('respects formSerializer options for index formatting', async () => { const { transport } = createEchoTransport(); const response = await axios.post( 'http://example.com/form', { arr: ['1', '2'], }, { proxy: false, transport, headers: { 'content-type': 'application/x-www-form-urlencoded', }, formSerializer: { indexes: true, }, } ); expect(response.data.body).toBe('arr%5B0%5D=1&arr%5B1%5D=2'); }); }); axios-axios-aa54a5e/tests/smoke/esm/vitest.config.js000066400000000000000000000004761526660636600226600ustar00rootroot00000000000000import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { testTimeout: 10000, projects: [ { test: { name: 'smoke', environment: 'node', include: ['tests/**/*.smoke.test.js'], setupFiles: [], }, }, ], }, }); axios-axios-aa54a5e/tests/unit/000077500000000000000000000000001526660636600166065ustar00rootroot00000000000000axios-axios-aa54a5e/tests/unit/adapters/000077500000000000000000000000001526660636600204115ustar00rootroot00000000000000axios-axios-aa54a5e/tests/unit/adapters/adapters.test.js000066400000000000000000000032271526660636600235340ustar00rootroot00000000000000import { beforeEach, describe, it } from 'vitest'; import assert from 'assert'; import adapters from '../../../lib/adapters/adapters.js'; import AxiosError from '../../../lib/core/AxiosError.js'; describe('adapters', () => { const store = { ...adapters.adapters }; beforeEach(() => { Object.keys(adapters.adapters).forEach((name) => { delete adapters.adapters[name]; }); Object.assign(adapters.adapters, store); }); it('should support loading by fn handle', () => { const adapter = () => {}; assert.strictEqual(adapters.getAdapter(adapter), adapter); }); it('should support loading by name', () => { const adapter = () => {}; adapters.adapters.testadapter = adapter; assert.strictEqual(adapters.getAdapter('testAdapter'), adapter); }); it('should detect adapter unavailable status', () => { adapters.adapters.testadapter = null; assert.throws(() => adapters.getAdapter('testAdapter'), /is not available in the build/); }); it('should detect adapter unsupported status', () => { adapters.adapters.testadapter = false; assert.throws( () => adapters.getAdapter('testAdapter'), (err) => { assert.ok(err instanceof AxiosError); assert.strictEqual(err.code, AxiosError.ERR_NOT_SUPPORT); assert.match(err.message, /is not supported by the environment/); return true; } ); }); it('should pick suitable adapter from the list', () => { const adapter = () => {}; Object.assign(adapters.adapters, { foo: false, bar: null, baz: adapter, }); assert.strictEqual(adapters.getAdapter(['foo', 'bar', 'baz']), adapter); }); }); axios-axios-aa54a5e/tests/unit/adapters/axios.png000066400000000000000000000032641526660636600222470ustar00rootroot00000000000000‰PNG  IHDRß2SA™'sRGB®ÎégAMA±� üa pHYsttÞfxIIDATx^í—ÁÍ-5 …i‡>¨‚ %°gK ,(ÑK ‚'= $$H,�~>ÉQ4×ñ$Nb_f‘££§ûOfìçØ3ï‹�ƒƒ§â¸óà¹8îÉÒº„+fɪ{öÁ–¯TñåÏ(wšƒsêø©’óB”—;6P·%G\àrggF¶›÷Ìö˜ƒ¡¥4R$E’;ÛöƒeÍ ÓC! Û™UIABl0vçÝà´›¯Gõx ØÊ-$E†;ͳ_‹ÜN�¨Žm#+’ÍËN»³u€z¹´-NeËZ>ÌS©¤†C²d¸ÓüDY;fô¨8°¬íÁirͦwö§ •rx�„Ô»Ñ!UngàNeáN'áîëç $ÑÛf皥�~‡5�'HÏ�í®8ªfRÝ c÷‡;k SÿK$K†;ÍÂÚvУâÀ²�Y›ÞÍ;AÏ�³­ ˆeK 4˜]¡˜Ú$’"Ã�È–hWÖDSPA„e- èçtú³CØi¹[wšÕ™âZ)ý0§KËáëc’"Ã� =Úþ¤1a6p”BĦæüv|rëÎN8?ó^¬fópx¦ì¼P‰¯Ü‰ ®T.Áü^œ5–ùÌ;—82SL§ålwîNa=¶pYå‚RtóP“ *Áë6kºÊÜ�‚?¦Y¥Ž0UX”+1pÚ�毑— qúÏìÑ %Ùt'öG”ù^&²Çwo¿}Urþ�šý6çλ(èÓ,¥§ŽS0³¨!ͬîáJ¸A%rëN¥gæF„ön;\7§&Œ&¸FFgG’Àl˜Î·ŠáNiE§ÉÌRz‚P*8$iY¾·b¸(HXåÎX˜[²Ž–£a³2øm�Ð,Ô2L«‘‚í_U ;ÂæÜ©†þ]!B= ¹X–÷À9™;dóåŽW¤ŠHÌ«;9ùbJ¢MÞj�ž™e‰hA»sÿD[q%dbaöÝ`öw A%`ugàñ+l”#ˆÕfv�ýãÓî4‡Ó”·LïO3lç¥P±¿£$Zì«üw¯Ž!3ä!Æ<‘)ÇÄ‹;I©ž‡T¤,û`q³@ô½ �ÂL=\áz¹cÍÓ$!˜õÛÌç@ô˜ï´>æX/î4ÇõÂØ3ã,»�U(è´¦ànîî”8Ãds(f‘…U©¾TpÚa¢­<6‹;ÙO˲6´ª pÙ *Žp6šz\XÖ6dAJ�Y…ÿ¯ ’D .¤…øwY˜þî<8xŽ;ž‹ã΃çâ¸óà¹8î>þÓô¯[²x4IEND®B`‚axios-axios-aa54a5e/tests/unit/adapters/basicAuth.test.js000066400000000000000000000062671526660636600236430ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import axios from '../../../index.js'; import { startHTTPServer, stopHTTPServer } from '../../setup/server.js'; describe.each(['http', 'fetch'])('Basic auth with the %s adapter', (adapter) => { it.runIf(adapter !== 'fetch' || typeof fetch === 'function')( 'preserves defaults and sends UTF-8 credentials when opted in', async () => { let requests = 0; const server = await startHTTPServer((req, res) => { requests++; res.end(req.headers.authorization); }); try { const url = `http://localhost:${server.address().port}/`; for (const enabled of [undefined, false, true]) { for (const username of ['naïve', 'Ï€', '😀']) { const previousRequests = requests; const request = axios.get(url, { adapter, proxy: false, auth: { username, password: 'naïve' }, transitional: { encodeBasicAuthUsername: enabled }, }); // Fetch's legacy config resolution rejects characters above Latin-1, // even though its final header encoding (like Node HTTP) is UTF-8. if (adapter === 'fetch' && enabled !== true && username !== 'naïve') { await assert.rejects(request, { code: 'ERR_BAD_OPTION_VALUE' }); assert.strictEqual(requests, previousRequests); } else { const { data } = await request; assert.strictEqual( data, 'Basic ' + Buffer.from(`${username}:naïve`, 'utf8').toString('base64') ); assert.strictEqual(requests, previousRequests + 1); } } } } finally { await stopHTTPServer(server); } } ); }); describe.runIf(typeof fetch === 'function')('Basic auth with custom Fetch', () => { it.each([false, true])('supports UTF-8 with Request disabled: %s', async (disableRequest) => { let authorization; const { data } = await axios.get('https://example.com/', { adapter: 'fetch', auth: { username: 'Ï€', password: 'x' }, transitional: { encodeBasicAuthUsername: true }, env: { ...(disableRequest ? { Request: null } : {}), fetch: async (input, init) => { authorization = new Headers(disableRequest ? init.headers : input.headers).get( 'Authorization' ); return new Response('ok'); }, }, }); assert.strictEqual(data, 'ok'); assert.strictEqual(authorization, 'Basic z4A6eA=='); }); it.each(['username', 'password'])('rejects malformed UTF-16 in %s before fetch', async (key) => { let called = false; await assert.rejects( axios.get('https://example.com/', { adapter: 'fetch', auth: { username: 'user', password: 'x', [key]: '\uD800' }, transitional: { encodeBasicAuthUsername: true }, env: { fetch: async () => { called = true; return new Response('unexpected'); }, }, }), { code: 'ERR_BAD_OPTION_VALUE', isAxiosError: true } ); assert.strictEqual(called, false); }); }); axios-axios-aa54a5e/tests/unit/adapters/bodyLength.test.js000066400000000000000000000135421526660636600240310ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import http from 'http'; import stream from 'stream'; import axios from '../../../index.js'; import { estimateDataURLBufferAllocation as estimate } from '../../../lib/helpers/estimateDataURLDecodedBytes.js'; async function withUploadServer(run) { var received = []; var sockets = new Set(); var server = http.createServer(function (req, res) { var chunks = []; var index = received.push(0) - 1; req.on('error', function () {}); req.on('data', function (chunk) { received[index] += chunk.length; chunks.push(chunk); }); req.on('end', function () { if (req.url === '/redirect') { res.writeHead(307, { Location: '/echo' }); res.end(); } else { res.end(Buffer.concat(chunks)); } }); }); server.on('connection', function (socket) { sockets.add(socket); socket.on('close', function () { sockets.delete(socket); }); }); await new Promise(function (resolve) { server.listen(0, '127.0.0.1', resolve); }); try { await run('http://127.0.0.1:' + server.address().port, received); } finally { sockets.forEach(function (socket) { socket.destroy(); }); await new Promise(function (resolve) { server.close(resolve); }); } } describe('encoded body length', function () { [ { name: 'redirect-capable', options: {} }, { name: 'native', options: { maxRedirects: 0 } }, { name: 'custom', options: { transport: http } }, ].forEach(function (transport) { [ { name: 'multibyte strings', chunks: ['éé'] }, { name: 'surrogate pairs', chunks: ['😀'] }, { name: 'mixed chunks', chunks: ['a', 'é', Buffer.from('b')] }, { name: 'buffers', chunks: [Buffer.from('abcd')] }, ].forEach(function (fixture) { it( 'limits ' + fixture.name + ' through the ' + transport.name + ' transport', async function () { await withUploadServer(async function (url, received) { await assert.rejects( axios.post( url, stream.Readable.from(fixture.chunks), Object.assign( { proxy: false, maxBodyLength: 3, }, transport.options ) ), function (error) { return error.code === 'ERR_BAD_REQUEST' && /maxBodyLength/.test(error.message); } ); assert.ok( received.every(function (length) { return length <= 3; }) ); }); } ); }); it( 'allows an exact byte limit through the ' + transport.name + ' transport', async function () { await withUploadServer(async function (url, received) { var response = await axios.post( url, stream.Readable.from(['é', 'é']), Object.assign( { proxy: false, maxBodyLength: 4, }, transport.options ) ); assert.strictEqual(response.data, 'éé'); assert.deepStrictEqual(received, [4]); }); } ); }); it('replays an allowed encoded body across a redirect', async function () { await withUploadServer(async function (url, received) { var response = await axios.post(url + '/redirect', stream.Readable.from(['é', 'é']), { proxy: false, maxBodyLength: 4, }); assert.strictEqual(response.data, 'éé'); assert.deepStrictEqual(received, [4, 4]); }); }); function TestBlob(parts) { this.size = parts.reduce(function (total, part) { return total + part.length; }, 0); } ['BASE64=x', 'base64=x', 'base64note=x'].forEach(function (parameter) { var url = 'data:text/plain;' + parameter + ',abcd'; it('counts ordinary metadata parameter ' + parameter + ' as text', function () { assert.strictEqual(estimate(url), 4); }); ['text', 'arraybuffer', 'stream', 'blob'].forEach(function (responseType) { it( 'checks data URL bytes before returning ' + responseType + ' with ' + parameter, async function () { await assert.rejects( axios.get(url, { maxContentLength: 3, responseType: responseType, env: { Blob: TestBlob }, }), function (error) { return error.code === 'ERR_BAD_RESPONSE'; } ); var response = await axios.get(url, { maxContentLength: 4, responseType: responseType, env: { Blob: TestBlob }, }); if (responseType === 'blob') { assert.strictEqual(response.data.size, 4); } else if (responseType === 'stream') { var chunks = []; for await (var chunk of response.data) chunks.push(chunk); assert.strictEqual(Buffer.concat(chunks).toString(), 'abcd'); } else { assert.strictEqual(response.data.toString(), 'abcd'); } } ); }); }); ['TQ', 'TWE', 'TWFu', 'TQ=='].forEach(function (encoded) { it('counts a base64 body of length ' + encoded.length, async function () { var url = 'data:text/plain;base64,' + encoded; var expected = Buffer.from(encoded, 'base64'); assert.strictEqual(estimate(url), expected.length); var response = await axios.get(url, { maxContentLength: expected.length, responseType: 'arraybuffer', }); assert.deepStrictEqual(response.data, expected); await assert.rejects( axios.get(url, { maxContentLength: expected.length - 1 }), function (error) { return error.code === 'ERR_BAD_RESPONSE'; } ); }); }); }); axios-axios-aa54a5e/tests/unit/adapters/cert.pem000066400000000000000000000017211526660636600220520ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIICpDCCAYwCCQDbqELLwgbPdDANBgkqhkiG9w0BAQUFADAUMRIwEAYDVQQDDAls b2NhbGhvc3QwHhcNMjAwNjI2MjIxMTQ3WhcNNDcxMTExMjIxMTQ3WjAUMRIwEAYD VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD6 Ogt99/dZ0UgbCuVV1RZ9n28Ov3DzrJCkjperQoXomIq3Fr4RUI1a2rwe3mtl3UzE 1IVZVvWPGdEsEQHwXfAsP/jFGTwI3HDyOhcqzFQSKsjvqJWYkOOb+2r3SBrFlRZW 09k/3lC+hx2XtuuG68u4Xgn3AlUvm2vplgCN7eiYcGeNwVuf2eHdOqTRTqiYCZLi T8GtdYMDXOrwsGZs/jUKd9U0ar/lqwMhmw07yzlVDM2MWM2tyq/asQ7Sf7vuoMFu oAtDJ3E+bK1k/7SNhdyP4RonhyUCkWG+mzoKDS1qgXroTiQSDUksAvOCTcj8BNIT ee+Lcn9FaTKNJiKiU9q/AgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAFi5ZpaUj+mU dsgOka+j2/njgNXux3cOjhm7z/N7LeTuDENAOrYa5b+j5JX/YM7RKHrkbXHsQbfs GB3ufH6QhSiCd/AdsXp/TbCE/8gdq8ykkjwVP1bvBle9oPH7x1aO/WP/odsepYUv o9aOZW4iNQVmwamU62ezglf3QD7HPeE4LnZueaFtuzRoC+aWT9v0MIeUPJLe3WDQ FEySwUuthMDJEv92/TeK0YOiunmseCu2mvdiDj6E3C9xa5q2DWgl+msu7+bPgvYO GuWaoNeQQGk7ebBO3Hk3IyaGx6Cbd8ty+YaZW7dUT+m7KCs1VkxdcDMjZJVWiJy4 4HcEcKboG4Y= -----END CERTIFICATE----- axios-axios-aa54a5e/tests/unit/adapters/dataURLProtocol.test.js000066400000000000000000000134051526660636600247460ustar00rootroot00000000000000import { describe, it, vi } from 'vitest'; import assert from 'assert'; import axios from '../../../index.js'; describe('HTTP data URL protocol handling', function () { describe.each(['data:', 'DATA:', 'DaTa:'])('%s URLs', (scheme) => { describe.each(['base64', 'BASE64', 'bAsE64'])('base64 marker %s', (marker) => { it.each(['text', 'arraybuffer', 'stream', 'blob'])( 'decodes URL-encoded base64 through the HTTP adapter as %s', async (responseType) => { const response = await axios.get(scheme + 'text/plain;' + marker + ',TQ%3D%3D', { adapter: 'http', responseType, maxContentLength: 1, }); let data = response.data; if (responseType === 'stream') { const chunks = []; for await (const chunk of data) chunks.push(chunk); data = Buffer.concat(chunks); } else if (responseType === 'blob') { assert.strictEqual(data.type, 'text/plain'); data = Buffer.from(await data.arrayBuffer()); } else if (responseType === 'text') { data = Buffer.from(data); } assert.deepStrictEqual(data, Buffer.from('M')); } ); it.each([0, 1])( 'rejects encoded data larger than a %s-byte limit', async (maxContentLength) => { await assert.rejects( axios.get(scheme + 'text/plain;' + marker + ',TWE%3D', { adapter: 'http', maxContentLength, }), { code: 'ERR_BAD_RESPONSE' } ); } ); it.each(['text', 'arraybuffer', 'stream', 'blob'])( 'decodes a %s response at maxContentLength', async (responseType) => { const response = await axios.get(scheme + 'text/plain;' + marker + ',SGVsbG8=', { adapter: 'http', responseType, maxContentLength: 5, }); let data = response.data; if (responseType === 'stream') { const chunks = []; for await (const chunk of data) chunks.push(chunk); data = Buffer.concat(chunks); } else if (responseType === 'blob') { assert.strictEqual(data.type, 'text/plain'); data = Buffer.from(await data.arrayBuffer()); } else if (responseType === 'text') { data = Buffer.from(data); } assert.deepStrictEqual(data, Buffer.from('Hello')); } ); it.each([0, 4])('rejects a response above a %s-byte limit', async (maxContentLength) => { await assert.rejects( axios.get(scheme + 'text/plain;' + marker + ',SGVsbG8=', { adapter: 'http', maxContentLength, }), { code: 'ERR_BAD_RESPONSE' } ); }); it.each([ ['percent-containing body', '%41'.repeat(4096), 3071], ['ignored input after padding', 'TQ==' + '%41'.repeat(4096), 3074], ['one-byte result with an ignored tail', 'TQ==' + 'A'.repeat(4096), 1], ['encoded padding and ignored tail', 'TQ%3D%3D' + '%25'.repeat(4096), 1], ])('rejects an oversized allocation for %s before decoding', async (_name, body, limit) => { const from = vi.spyOn(Buffer, 'from'); try { await assert.rejects( axios.get(scheme + 'text/plain;' + marker + ',' + body, { adapter: 'http', maxContentLength: limit, }), { code: 'ERR_BAD_RESPONSE' } ); assert.strictEqual( from.mock.calls.some((args) => args[1] === 'base64'), false ); } finally { from.mockRestore(); } }); }); it.each([ ['Hello', 'Hello', 5], ['%E2%82%AC', '\u20ac', 3], ])('decodes non-base64 %s at maxContentLength', async (body, expected, limit) => { const response = await axios.get(scheme + 'text/plain,' + body, { adapter: 'http', responseType: 'text', maxContentLength: limit, }); assert.strictEqual(response.data, expected); }); it.each([0, 4])( 'rejects non-base64 input above a %s-byte limit before decoding', async (limit) => { const from = vi.spyOn(Buffer, 'from'); try { await assert.rejects( axios.get(scheme + 'text/plain,Hello', { adapter: 'http', responseType: 'text', maxContentLength: limit, }), { code: 'ERR_BAD_RESPONSE' } ); assert.strictEqual( from.mock.calls.some((args) => args[0] === 'Hello' && args[1] === 'utf8'), false ); } finally { from.mockRestore(); } } ); it('rejects malformed data URLs', async () => { await assert.rejects(axios.get(scheme + 'text/plain', { adapter: 'http' }), { code: 'ERR_BAD_REQUEST', message: 'Invalid URL', }); }); }); it.each(['datax:', 'DATAX:', 'DaTaX:'])('rejects an unsupported prefix %s', async (scheme) => { await assert.rejects(axios.get(scheme + 'text/plain,Hello', { adapter: 'http' }), { code: 'ERR_BAD_REQUEST', message: 'Unsupported protocol datax:', }); }); it('continues to enforce the byte limit for a supported lowercase protocol', async function () { await assert.rejects( axios.get('data:text/plain,payload', { adapter: 'http', maxContentLength: 4, responseType: 'text', }), function (error) { return error.code === 'ERR_BAD_RESPONSE'; } ); const response = await axios.get('data:text/plain,payload', { adapter: 'http', maxContentLength: 7, responseType: 'text', }); assert.strictEqual(response.data, 'payload'); }); }); axios-axios-aa54a5e/tests/unit/adapters/directAgent.test.js000066400000000000000000000360461526660636600241670ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import http from 'http'; import https from 'https'; import net from 'net'; import fs from 'fs'; import axios from '../../../index.js'; import { __getDirectAgent as getDirectAgent, __isNodeNativeEnvProxySupported, } from '../../../lib/adapters/http.js'; import { startHTTPServer, stopHTTPServer } from '../../setup/server.js'; const nativeProxySupported = __isNodeNativeEnvProxySupported(); function pooledSockets(agent) { return [agent.sockets, agent.freeSockets].reduce(function (sockets, pool) { return Object.keys(pool).reduce(function (all, key) { return all.concat(pool[key]); }, sockets); }, []); } function CustomAgent(options) { http.Agent.call(this, options); } Object.setPrototypeOf(CustomAgent.prototype, http.Agent.prototype); describe('direct agent lifecycle', function () { it.skipIf(!nativeProxySupported)( 'destroys both pools through the configured agent', async function () { var proxy = await startHTTPServer( function (req, res) { res.end('proxy'); }, { keepAlive: 60000 } ); var origin = await startHTTPServer( function (req, res) { res.end('origin'); }, { keepAlive: 60000 } ); var agent = new http.Agent({ keepAlive: true, proxyEnv: { HTTP_PROXY: 'http://127.0.0.1:' + proxy.address().port, NO_PROXY: '', }, }); var destroyCalls = 0; agent.destroy = function (value) { assert.strictEqual(this, agent); destroyCalls++; http.Agent.prototype.destroy.call(this); return value; }; try { var url = 'http://127.0.0.1:' + origin.address().port; assert.strictEqual((await axios.get(url, { httpAgent: agent })).data, 'proxy'); var response = await axios.get(url, { httpAgent: agent, proxy: false }); assert.strictEqual(response.data, 'origin'); var direct = response.request.agent; var sourceSockets = pooledSockets(agent); var directSockets = pooledSockets(direct); assert.strictEqual(sourceSockets.length, 1); assert.strictEqual(directSockets.length, 1); assert.strictEqual(agent.destroy('closed'), 'closed'); assert.strictEqual(destroyCalls, 1); assert.ok(sourceSockets[0].destroyed); assert.ok(directSockets[0].destroyed); response = await axios.get(url, { httpAgent: agent, proxy: false }); assert.strictEqual(response.data, 'origin'); assert.notStrictEqual(response.request.socket, directSockets[0]); agent.destroy(); assert.ok(response.request.socket.destroyed); assert.strictEqual(destroyCalls, 2); } finally { agent.destroy(); await stopHTTPServer(origin); await stopHTTPServer(proxy); } } ); it.skipIf(!nativeProxySupported)( 'closes a held direct request when the configured agent is destroyed', async function () { var requestReceived; var received = new Promise(function (resolve) { requestReceived = resolve; }); var origin = await startHTTPServer(function (req) { req.resume(); requestReceived(); // Keep the response open until the application destroys its agent. }); var agent = new http.Agent({ keepAlive: true, proxyEnv: { HTTP_PROXY: 'http://127.0.0.1:1' }, }); var direct; var closeTimer; var pending = axios .get('http://127.0.0.1:' + origin.address().port, { httpAgent: agent, proxy: false, timeout: 2000, }) .then( function () { return new Error('The held request should not complete successfully'); }, function (error) { return error; } ); try { await Promise.race([ received, pending.then(function (error) { throw error; }), ]); direct = getDirectAgent(agent, http); var sockets = Object.keys(direct.sockets).reduce(function (all, key) { return all.concat(direct.sockets[key]); }, []); assert.strictEqual(sockets.length, 1); var closed = new Promise(function (resolve) { origin.close(resolve); }); agent.destroy(); assert.ok(sockets[0].destroyed); var error = await pending; assert.strictEqual(error.code, 'ECONNRESET'); await Promise.race([ closed, new Promise(function (resolve, reject) { closeTimer = setTimeout(function () { reject(new Error('Server shutdown is still waiting for the direct socket')); }, 1000); }), ]); } finally { clearTimeout(closeTimer); agent.destroy(); if (direct) direct.destroy(); await pending; await stopHTTPServer(origin); } } ); ['own', 'prototype'].forEach(function (placement) { it.skipIf(!nativeProxySupported)( 'preserves ' + placement + ' pool keys and their configured receiver', async function () { var origin = await startHTTPServer( function (req, res) { res.end('origin'); }, { keepAlive: 60000 } ); var states = new WeakMap(); function PartitionAgent(options) { http.Agent.call(this, options); states.set(this, { prefix: 'partition:', calls: 0 }); } Object.setPrototypeOf(PartitionAgent.prototype, http.Agent.prototype); var agent = new PartitionAgent({ keepAlive: true, proxyEnv: { HTTP_PROXY: 'http://127.0.0.1:1' }, }); var owner = placement === 'own' ? agent : PartitionAgent.prototype; owner.getName = function (options) { var state = states.get(this); assert.ok(state, 'pool naming must retain the configured receiver'); state.calls++; return ( state.prefix + options.headers['X-Pool'] + ':' + http.Agent.prototype.getName.call(this, options) ); }; var target = 'http://127.0.0.1:' + origin.address().port; try { var first = await axios.get(target, { httpAgent: agent, proxy: false, headers: { 'X-Pool': 'first' }, }); var second = await axios.get(target, { httpAgent: agent, proxy: false, headers: { 'X-Pool': 'second' }, }); var again = await axios.get(target, { httpAgent: agent, proxy: false, headers: { 'X-Pool': 'first' }, }); assert.strictEqual(first.data, 'origin'); assert.strictEqual(second.data, 'origin'); assert.strictEqual(again.data, 'origin'); assert.ok( first.request.socket !== second.request.socket, 'different pool keys need separate sockets' ); assert.ok( first.request.socket === again.request.socket, 'matching pool keys should reuse their socket' ); assert.ok(states.get(agent).calls > 0); } finally { agent.destroy(); await stopHTTPServer(origin); } } ); }); [ { transport: http, slot: 'httpAgent', hooks: ['addRequest', 'createSocket', 'removeSocket', 'reuseSocket', 'keepSocketAlive'], }, { transport: https, slot: 'httpsAgent', hooks: ['_getSession', '_cacheSession', '_evictSession'], }, ].forEach(function (fixture) { fixture.hooks.forEach(function (hook) { ['own', 'prototype'].forEach(function (placement) { it.skipIf(!nativeProxySupported)( 'rejects unsupported ' + placement + ' ' + hook + ' before dispatch', async function () { function PoolAgent(options) { fixture.transport.Agent.call(this, options); } Object.setPrototypeOf(PoolAgent.prototype, fixture.transport.Agent.prototype); var agent = new PoolAgent({ proxyEnv: { HTTP_PROXY: 'http://127.0.0.1:1', HTTPS_PROXY: 'http://127.0.0.1:1' }, }); var destroy = agent.destroy; var calls = 0; var owner = placement === 'own' ? agent : PoolAgent.prototype; owner[hook] = function () { throw new Error('Unsupported hook must not run'); }; var config = { proxy: false, transport: { request: function () { calls++; throw new Error('Dispatch must not run'); }, }, }; config[fixture.slot] = agent; try { await assert.rejects( axios.get(agent.protocol + '//127.0.0.1/resource', config), function (error) { return ( error.code === 'ERR_BAD_OPTION_VALUE' && error.message.indexOf(hook) !== -1 ); } ); assert.strictEqual(calls, 0); assert.strictEqual(agent.destroy, destroy); } finally { agent.destroy(); } } ); }); }); }); it.skipIf(!nativeProxySupported)( 'checks lifecycle overrides again before reusing a cached pool', function () { var agent = new http.Agent({ proxyEnv: { HTTP_PROXY: 'http://127.0.0.1:1' } }); try { getDirectAgent(agent, http); agent.addRequest = function () { throw new Error('Unsupported hook must not run'); }; assert.throws( function () { getDirectAgent(agent, http); }, function (error) { return error.code === 'ERR_BAD_OPTION_VALUE' && /addRequest/.test(error.message); } ); } finally { agent.destroy(); } } ); ['own', 'prototype'].forEach(function (placement) { it.skipIf(!nativeProxySupported)( 'preserves ' + placement + ' connection hooks and their configured receiver', async function () { var origin = await startHTTPServer(function (req, res) { res.end('origin'); }); function HookAgent(options) { http.Agent.call(this, options); this.connectionCalls = 0; } Object.setPrototypeOf(HookAgent.prototype, http.Agent.prototype); var agent = new HookAgent({ proxyEnv: { HTTP_PROXY: 'http://127.0.0.1:1' } }); var owner = placement === 'own' ? agent : HookAgent.prototype; owner.createConnection = function (options, callback) { assert.strictEqual(this, agent); this.connectionCalls++; return net.createConnection(options, callback); }; try { var response = await axios.get('http://127.0.0.1:' + origin.address().port, { httpAgent: agent, proxy: false, }); assert.strictEqual(response.data, 'origin'); assert.strictEqual(agent.connectionCalls, 1); } finally { agent.destroy(); await stopHTTPServer(origin); } } ); }); it('leaves unrelated agent options alone', async function () { var origin = await startHTTPServer(function (req, res) { res.end('origin'); }); var unused = new CustomAgent({ proxyEnv: { HTTP_PROXY: 'http://127.0.0.1:1' } }); var destroy = unused.destroy; Object.defineProperty(unused, 'options', { get: function () { throw new Error('unused agent options should not be read'); }, }); try { var response = await axios.get('http://127.0.0.1:' + origin.address().port, { httpsAgent: unused, proxy: false, }); assert.strictEqual(response.data, 'origin'); assert.strictEqual(unused.destroy, destroy); } finally { unused.destroy(); await stopHTTPServer(origin); } }); it.skipIf(!nativeProxySupported)( 'selects direct agents on protocol-changing redirects', async function () { var visited = []; var securePort; var origin = await startHTTPServer(function (req, res) { visited.push('http:' + req.url); if (req.url === '/start') { res.writeHead(302, { Location: 'https://127.0.0.1:' + securePort + '/next' }); } res.end('origin'); }); var secure = https.createServer( { key: fs.readFileSync(new URL('./key.pem', import.meta.url)), cert: fs.readFileSync(new URL('./cert.pem', import.meta.url)), }, function (req, res) { visited.push('https:' + req.url); res.writeHead(302, { Location: 'http://127.0.0.1:' + origin.address().port + '/end' }); res.end(); } ); var httpAgent = new CustomAgent({ keepAlive: true, proxyEnv: { HTTP_PROXY: 'http://127.0.0.1:1' }, }); var httpsAgent = new https.Agent({ keepAlive: true, rejectUnauthorized: false, proxyEnv: { HTTPS_PROXY: 'http://127.0.0.1:1' }, }); try { await new Promise(function (resolve) { secure.listen(0, '127.0.0.1', resolve); }); securePort = secure.address().port; var response = await axios.get('http://127.0.0.1:' + origin.address().port + '/start', { httpAgent: httpAgent, httpsAgent: httpsAgent, proxy: false, timeout: 2000, }); assert.strictEqual(response.data, 'origin'); assert.deepStrictEqual(visited, ['http:/start', 'https:/next', 'http:/end']); } finally { httpAgent.destroy(); httpsAgent.destroy(); await stopHTTPServer(secure); await stopHTTPServer(origin); } } ); it.skipIf(!nativeProxySupported)( 'keeps direct routing after proxy options are edited', async function () { var origin = await startHTTPServer(function (req, res) { res.end('origin'); }); var agent = new CustomAgent({ proxyEnv: { HTTP_PROXY: 'http://127.0.0.1:1' }, }); delete agent.options.proxyEnv.HTTP_PROXY; try { var response = await axios.get('http://127.0.0.1:' + origin.address().port, { httpAgent: agent, proxy: false, }); assert.strictEqual(response.data, 'origin'); } finally { agent.destroy(); await stopHTTPServer(origin); } } ); it('leaves custom agents unchanged when native environment proxying is unavailable', function () { var descriptor = Object.getOwnPropertyDescriptor(process, 'allowedNodeEnvironmentFlags'); var agent = new CustomAgent({ proxyEnv: { HTTP_PROXY: 'http://127.0.0.1:1' } }); try { Object.defineProperty(process, 'allowedNodeEnvironmentFlags', { value: new Set() }); assert.strictEqual(getDirectAgent(agent, http), agent); } finally { Object.defineProperty(process, 'allowedNodeEnvironmentFlags', descriptor); agent.destroy(); } }); }); axios-axios-aa54a5e/tests/unit/adapters/errorDetails.test.js000066400000000000000000000055061526660636600243720ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import https from 'https'; import net from 'net'; import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; import axios from '../../../index.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const getClosedPort = async () => { return await new Promise((resolve) => { const srv = net.createServer(); srv.listen(0, '127.0.0.1', () => { const { port } = srv.address(); srv.close(() => resolve(port)); }); }); }; describe('adapters - network-error details', () => { it('should expose ECONNREFUSED and set error.cause on connection refusal', async () => { const port = await getClosedPort(); try { await axios.get(`http://127.0.0.1:${port}`, { timeout: 500 }); assert.fail('request unexpectedly succeeded'); } catch (err) { assert.ok(err instanceof Error, 'should be an Error'); assert.strictEqual(err.isAxiosError, true, 'isAxiosError should be true'); assert.strictEqual(err.code, 'ECONNREFUSED'); assert.ok('cause' in err, 'error.cause should exist'); assert.ok(err.cause instanceof Error, 'cause should be an Error'); assert.strictEqual(err.cause && err.cause.code, 'ECONNREFUSED'); assert.strictEqual(typeof err.message, 'string'); } }); it('should expose self-signed TLS error and set error.cause', async () => { const certsDir = path.resolve(__dirname, '../../../tests/unit/adapters/'); const keyPath = path.join(certsDir, 'key.pem'); const certPath = path.join(certsDir, 'cert.pem'); const key = fs.readFileSync(keyPath); const cert = fs.readFileSync(certPath); const httpsServer = https.createServer({ key, cert }, (req, res) => res.end('ok')); await new Promise((resolve) => httpsServer.listen(0, '127.0.0.1', resolve)); const { port } = httpsServer.address(); try { await axios.get(`https://127.0.0.1:${port}`, { timeout: 500, httpsAgent: new https.Agent({ rejectUnauthorized: true }), }); assert.fail('request unexpectedly succeeded'); } catch (err) { const codeStr = String(err.code); assert.ok( /SELF_SIGNED|UNABLE_TO_VERIFY_LEAF_SIGNATURE|DEPTH_ZERO/.test(codeStr), `unexpected TLS code: ${codeStr}` ); assert.ok('cause' in err, 'error.cause should exist'); assert.ok(err.cause instanceof Error, 'cause should be an Error'); const causeCode = String(err.cause && err.cause.code); assert.ok( /SELF_SIGNED|UNABLE_TO_VERIFY_LEAF_SIGNATURE|DEPTH_ZERO/.test(causeCode), `unexpected cause code: ${causeCode}` ); assert.strictEqual(typeof err.message, 'string'); } finally { await new Promise((resolve) => httpsServer.close(resolve)); } }); }); axios-axios-aa54a5e/tests/unit/adapters/fetch.test.js000066400000000000000000002046421526660636600230260ustar00rootroot00000000000000import { describe, it, vi } from 'vitest'; import assert from 'assert'; import { startHTTPServer, stopHTTPServer, setTimeoutAsync, makeReadableStream, generateReadable, makeEchoStream, } from '../../setup/server.js'; import axios from '../../../index.js'; import AxiosError from '../../../lib/core/AxiosError.js'; import utils from '../../../lib/utils.js'; import { getFetch } from '../../../lib/adapters/fetch.js'; import stream from 'stream'; import { AbortController } from 'abortcontroller-polyfill/dist/cjs-ponyfill.js'; import util from 'util'; import NodeFormData from 'form-data'; import { VERSION } from '../../../lib/env/data.js'; const SERVER_PORT = 8010; const LOCAL_SERVER_URL = `http://localhost:${SERVER_PORT}`; const pipelineAsync = util.promisify(stream.pipeline); const fetchAxios = axios.create({ baseURL: LOCAL_SERVER_URL, adapter: 'fetch', }); const getFetchSignal = (input, init) => (init && init.signal) || (input && input.signal); const createBrokenDOMExceptionLikeError = () => Object.defineProperties( {}, { name: { get() { throw new TypeError( 'The DOMException.name getter can only be used on instances of DOMException' ); }, }, message: { get() { throw new TypeError( 'The DOMException.message getter can only be used on instances of DOMException' ); }, }, } ); describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () => { it('rejects malformed HTTP URLs before fetch normalization and preserves config', async () => { for (const url of ['\u0000https:example.com/users', 'h\nttp:example.com/users']) { await assert.rejects( () => axios.get(url, { adapter: 'fetch', headers: { 'X-Test': 'yes', }, }), (error) => { assert.ok(error instanceof AxiosError); assert.strictEqual(error.code, AxiosError.ERR_INVALID_URL); assert.match(error.message, /^Invalid URL ".*": missing "\/\/" after protocol$/); assert.strictEqual(error.config.url, url); assert.strictEqual(error.config.headers.get('X-Test'), 'yes'); return true; } ); } }); it('should sanitize request headers containing CRLF characters', async () => { const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'application/json'); res.end( JSON.stringify({ xTest: req.headers['x-test'], injected: req.headers.injected ?? null, }) ); }, { port: SERVER_PORT, } ); try { const { data } = await fetchAxios.get(`${LOCAL_SERVER_URL}/`, { headers: { 'x-test': '\tok\r\nInjected: yes ', }, }); assert.strictEqual(data.xTest, 'okInjected: yes'); assert.strictEqual(data.injected, null); } finally { await stopHTTPServer(server); } }); it('should not use inherited Symbol.iterator for request headers', async () => { const server = await startHTTPServer((req, res) => { res.setHeader('Content-Type', 'application/json'); res.end( JSON.stringify({ authorization: req.headers.authorization, xApp: req.headers['x-app'], xInjected: req.headers['x-injected'] ?? null, }) ); }); try { Object.prototype[Symbol.iterator] = function* () { yield ['X-Injected', 'yes']; yield ['Authorization', 'Bearer CHANGED']; }; const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, { headers: { Authorization: 'Bearer VALID_USER_TOKEN', 'X-App': 'safe', }, }); assert.strictEqual(data.authorization, 'Bearer VALID_USER_TOKEN'); assert.strictEqual(data.xApp, 'safe'); assert.strictEqual(data.xInjected, null); } finally { delete Object.prototype[Symbol.iterator]; await stopHTTPServer(server); } }); it('should allow request interceptors to encode Unicode header values before fetch sends them', async () => { const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'application/json'); res.end( JSON.stringify({ oprtName: req.headers.oprtname, }) ); }, { port: SERVER_PORT, } ); const instance = axios.create({ baseURL: LOCAL_SERVER_URL, adapter: 'fetch', }); instance.interceptors.request.use((config) => { config.headers.oprtName = encodeURIComponent(config.headers.oprtName); return config; }); try { const { data } = await instance.get('/', { headers: { oprtName: '请求用户', }, }); assert.strictEqual(data.oprtName, encodeURIComponent('请求用户')); } finally { await stopHTTPServer(server); } }); it('should sanitize unencoded Unicode headers before passing them to fetch', async () => { const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'application/json'); res.end( JSON.stringify({ xTest: req.headers['x-test'], }) ); }, { port: SERVER_PORT, } ); try { const { data } = await fetchAxios.get(`${LOCAL_SERVER_URL}/`, { headers: { 'x-test': '请求用户', }, }); assert.strictEqual(data.xTest, ''); } finally { await stopHTTPServer(server); } }); it('should pass a fully resolved Request and safe options to a custom fetch implementation', async () => { let captured; const fetchOptions = Object.create({ headers: { 'X-Injected': 'yes', }, }); fetchOptions.cache = 'no-store'; fetchOptions.redirect = 'error'; fetchOptions.customOption = 'custom-value'; fetchOptions.credentials = 'omit'; fetchOptions.duplex = 'full'; const customFetch = function (input, init) { captured = { input, init, argumentCount: arguments.length, }; return Promise.resolve(new Response('ok')); }; const response = await fetchAxios.get('/custom-fetch', { headers: { 'X-Safe': 'value', }, withCredentials: true, fetchOptions, env: { fetch: customFetch, }, }); assert.strictEqual(response.data, 'ok'); assert.ok(captured.input instanceof Request); assert.strictEqual(captured.input.headers.get('X-Safe'), 'value'); assert.strictEqual(captured.input.headers.get('X-Injected'), null); assert.strictEqual(captured.input.cache, 'no-store'); assert.strictEqual(captured.input.redirect, 'error'); assert.strictEqual(captured.input.credentials, 'include'); assert.strictEqual(captured.input.duplex, 'half'); assert.strictEqual(Object.getPrototypeOf(captured.init), null); assert.strictEqual(captured.init.cache, 'no-store'); assert.strictEqual(captured.init.redirect, 'error'); assert.strictEqual(captured.init.customOption, 'custom-value'); assert.strictEqual(captured.init.body, undefined); assert.strictEqual(captured.init.headers, undefined); assert.strictEqual(captured.init.method, undefined); assert.strictEqual(captured.init.signal, undefined); assert.strictEqual(captured.init.credentials, undefined); assert.strictEqual(captured.init.duplex, undefined); assert.strictEqual(captured.argumentCount, 2); }); it('should ignore Request options inherited only from Object.prototype', async () => { let captured; Object.prototype.cache = 'no-store'; Object.prototype.redirect = 'manual'; try { const response = await fetchAxios.get('/safe-request-options', { env: { fetch(input) { captured = input; return Promise.resolve(new Response('ok')); }, }, }); assert.strictEqual(response.data, 'ok'); assert.ok(captured instanceof Request); assert.strictEqual(captured.cache, 'default'); assert.strictEqual(captured.redirect, 'follow'); } finally { delete Object.prototype.cache; delete Object.prototype.redirect; } }); it('should omit the default cache mode when the runtime rejects it', async () => { let capturedCache = 'unset'; class RestrictedCacheRequest extends Request { constructor(url, init) { capturedCache = init && init.cache; if (capturedCache !== undefined && capturedCache !== 'no-store') { throw new TypeError(`Unsupported cache mode: ${capturedCache}`); } super(url, init); } } let captured; const response = await fetchAxios.get('/restricted-cache', { env: { Request: RestrictedCacheRequest, fetch(input) { captured = input; return Promise.resolve(new Response('ok')); }, }, }); assert.strictEqual(response.data, 'ok'); assert.strictEqual(capturedCache, undefined); assert.strictEqual(captured.redirect, 'follow'); }); it('should omit the default cache mode when the rejected mode is also polluted', async () => { let capturedCache = 'unset'; class RestrictedCacheRequest extends Request { constructor(url, init) { capturedCache = init && init.cache; if (capturedCache !== undefined && capturedCache !== 'no-store') { throw new TypeError(`Unsupported cache mode: ${capturedCache}`); } super(url, init); } } Object.prototype.cache = 'default'; try { const response = await fetchAxios.get('/polluted-cache', { env: { Request: RestrictedCacheRequest, fetch() { return Promise.resolve(new Response('ok')); }, }, }); assert.strictEqual(response.data, 'ok'); assert.strictEqual(capturedCache, undefined); } finally { delete Object.prototype.cache; } }); it('should keep the default cache mode when other Request options are polluted', async () => { let captured; Object.prototype.cache = 'no-store'; Object.prototype.mode = 'navigate'; try { const response = await fetchAxios.get('/polluted-mode', { env: { fetch(input) { captured = input; return Promise.resolve(new Response('ok')); }, }, }); assert.strictEqual(response.data, 'ok'); assert.strictEqual(captured.cache, 'default'); } finally { delete Object.prototype.cache; delete Object.prototype.mode; } }); [false, true].forEach((polluted) => { it(`should omit cache when every explicit mode is rejected${polluted ? ' with polluted options' : ''}`, async () => { let capturedInit; class CacheDisabledRequest extends Request { constructor(input, init) { if (init && init.cache !== undefined) { throw new TypeError( "The 'cache' field on 'RequestInitializerDict' is not implemented." ); } super(input, init); capturedInit = init; } } try { if (polluted) { Object.prototype.cache = 'default'; Object.prototype.mode = 'navigate'; } const response = await fetchAxios.get('/cache-disabled', { env: { Request: CacheDisabledRequest, fetch() { return Promise.resolve(new Response('ok')); }, }, }); assert.strictEqual(response.data, 'ok'); assert.strictEqual(Object.getPrototypeOf(capturedInit), null); assert.strictEqual(Object.prototype.hasOwnProperty.call(capturedInit, 'cache'), false); assert.strictEqual(capturedInit.mode, 'cors'); assert.strictEqual(capturedInit.redirect, 'follow'); } finally { if (polluted) { delete Object.prototype.cache; delete Object.prototype.mode; } } }); }); it('should keep the default cache mode when a polluted body prevents capability probes', async () => { let captured; Object.prototype.cache = 'no-store'; Object.prototype.body = 'unexpected GET body'; try { const response = await fetchAxios.get('/polluted-body', { env: { fetch(input) { captured = input; return Promise.resolve(new Response('ok')); }, }, }); assert.strictEqual(response.data, 'ok'); assert.strictEqual(captured.cache, 'default'); assert.strictEqual(captured.body, null); } finally { delete Object.prototype.cache; delete Object.prototype.body; } }); ['no-store', 'no-cache', 'default'].forEach((cache) => { it(`should preserve an explicit ${cache} cache option on a restricted runtime`, async () => { class RestrictedCacheRequest extends Request { constructor(input, init) { if ( init && init.cache !== undefined && init.cache !== 'no-store' && init.cache !== 'no-cache' ) { throw new TypeError(`Unsupported cache mode: ${init.cache}`); } super(input, init); } } const fetchOptions = Object.freeze({ cache }); const customFetch = vi.fn((input, init) => { assert.strictEqual(input.cache, cache); assert.strictEqual(init.cache, cache); return Promise.resolve(new Response('ok')); }); const response = fetchAxios.get('/explicit-cache', { fetchOptions, env: { Request: RestrictedCacheRequest, fetch: customFetch, }, }); if (cache === 'default') { await assert.rejects(response, { message: 'Unsupported cache mode: default' }); assert.strictEqual(customFetch.mock.calls.length, 0); } else { assert.strictEqual((await response).data, 'ok'); assert.strictEqual(customFetch.mock.calls.length, 1); } assert.strictEqual(fetchOptions.cache, cache); }); }); it('should expose an unfollowed redirect response in Node when maxRedirects is zero', async () => { let finalRequests = 0; const server = await startHTTPServer((req, res) => { if (req.url === '/start') { res.writeHead(302, { Location: '/final' }); res.end(); return; } finalRequests++; res.end('followed'); }); try { const response = await fetchAxios.get(`http://localhost:${server.address().port}/start`, { maxRedirects: 0, validateStatus: () => true, }); // Node exposes manual redirect responses; browsers return an opaque // redirect whose status and headers cannot be inspected. assert.strictEqual(response.status, 302); assert.strictEqual(finalRequests, 0); } finally { await stopHTTPServer(server); } }); describe('responses', () => { it('should support text response type', async () => { const originalData = 'my data'; const server = await startHTTPServer((req, res) => res.end(originalData), { port: SERVER_PORT, }); try { const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, { responseType: 'text', }); assert.deepStrictEqual(data, originalData); } finally { await stopHTTPServer(server); } }); it('should support arraybuffer response type', async () => { const originalData = 'my data'; const server = await startHTTPServer((req, res) => res.end(originalData), { port: SERVER_PORT, }); try { const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, { responseType: 'arraybuffer', }); assert.deepStrictEqual( data, Uint8Array.from(await new TextEncoder().encode(originalData)).buffer ); } finally { await stopHTTPServer(server); } }); it('should support blob response type', async () => { const originalData = 'my data'; const server = await startHTTPServer((req, res) => res.end(originalData), { port: SERVER_PORT, }); try { const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, { responseType: 'blob', }); assert.deepStrictEqual(data, new Blob([originalData])); } finally { await stopHTTPServer(server); } }); it('should support stream response type', async () => { const originalData = 'my data'; const server = await startHTTPServer((req, res) => res.end(originalData), { port: SERVER_PORT, }); try { const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, { responseType: 'stream', }); assert.ok(data instanceof ReadableStream, 'data is not instanceof ReadableStream'); const response = new Response(data); assert.deepStrictEqual(await response.text(), originalData); } finally { await stopHTTPServer(server); } }); it('should support formData response type', async () => { const originalData = new FormData(); originalData.append('x', '123'); const server = await startHTTPServer( async (req, res) => { const response = await new Response(originalData); res.setHeader('Content-Type', response.headers.get('Content-Type')); res.end(await response.text()); }, { port: SERVER_PORT } ); try { const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, { responseType: 'formdata', }); assert.ok(data instanceof FormData, 'data is not instanceof FormData'); assert.deepStrictEqual( Object.fromEntries(data.entries()), Object.fromEntries(originalData.entries()) ); } finally { await stopHTTPServer(server); } }, 5000); it('should support json response type', async () => { const originalData = { x: 'my data' }; const server = await startHTTPServer((req, res) => res.end(JSON.stringify(originalData)), { port: SERVER_PORT, }); try { const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, { responseType: 'json', }); assert.deepStrictEqual(data, originalData); } finally { await stopHTTPServer(server); } }); }); describe('progress', () => { describe('upload', () => { it('should support upload progress capturing', async () => { const server = await startHTTPServer( { rate: 100 * 1024, }, { port: SERVER_PORT } ); try { let content = ''; const count = 10; const chunk = 'test'; const chunkLength = Buffer.byteLength(chunk); const contentLength = count * chunkLength; const readable = stream.Readable.from( (async function* () { let i = count; while (i-- > 0) { await setTimeoutAsync(1100); content += chunk; yield chunk; } })() ); const samples = []; const { data } = await fetchAxios.post( `http://localhost:${server.address().port}/`, readable, { onUploadProgress: ({ loaded, total, progress, bytes, upload }) => { console.log( `Upload Progress ${loaded} from ${total} bytes (${(progress * 100).toFixed(1)}%)` ); samples.push({ loaded, total, progress, bytes, upload, }); }, headers: { 'Content-Length': contentLength, }, responseType: 'text', } ); await setTimeoutAsync(500); assert.strictEqual(data, content); assert.deepStrictEqual( samples, Array.from( (function* () { for (let i = 1; i <= 10; i++) { yield { loaded: chunkLength * i, total: contentLength, progress: (chunkLength * i) / contentLength, bytes: 4, upload: true, }; } })() ) ); } finally { await stopHTTPServer(server); } }, 15000); it('should not fail with get method', async () => { const server = await startHTTPServer((req, res) => res.end('OK'), { port: SERVER_PORT }); try { const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, { onUploadProgress() {}, }); assert.strictEqual(data, 'OK'); } finally { await stopHTTPServer(server); } }); }); describe('download', () => { it('should support download progress capturing', async () => { const server = await startHTTPServer( { rate: 100 * 1024, }, { port: SERVER_PORT, } ); try { let content = ''; const count = 10; const chunk = 'test'; const chunkLength = Buffer.byteLength(chunk); const contentLength = count * chunkLength; const readable = stream.Readable.from( (async function* () { let i = count; while (i-- > 0) { await setTimeoutAsync(1100); content += chunk; yield chunk; } })() ); const samples = []; const { data } = await fetchAxios.post( `http://localhost:${server.address().port}/`, readable, { onDownloadProgress: ({ loaded, total, progress, bytes, download }) => { console.log( `Download Progress ${loaded} from ${total} bytes (${(progress * 100).toFixed(1)}%)` ); samples.push({ loaded, total, progress, bytes, download, }); }, headers: { 'Content-Length': contentLength, }, responseType: 'text', maxRedirects: 0, } ); await setTimeoutAsync(500); assert.strictEqual(data, content); assert.deepStrictEqual( samples, Array.from( (function* () { for (let i = 1; i <= 10; i++) { yield { loaded: chunkLength * i, total: contentLength, progress: (chunkLength * i) / contentLength, bytes: 4, download: true, }; } })() ) ); } finally { await stopHTTPServer(server); } }, 15000); }); }); it('should support basic auth', async () => { const server = await startHTTPServer((req, res) => res.end(req.headers.authorization), { port: SERVER_PORT, }); try { const user = 'foo'; const headers = { Authorization: 'Bearer 1234' }; const res = await fetchAxios.get(`http://${user}@localhost:${server.address().port}/`, { headers, }); const base64 = Buffer.from(`${user}:`, 'utf8').toString('base64'); assert.equal(res.data, `Basic ${base64}`); } finally { await stopHTTPServer(server); } }); it('should decode basic auth credentials from the request URL', async () => { const server = await startHTTPServer( (req, res) => { res.end(req.headers.authorization); }, { port: SERVER_PORT } ); try { const response = await fetchAxios.get( `http://my%40email.com:pa%24ss@localhost:${server.address().port}/` ); const base64 = Buffer.from('my@email.com:pa$ss', 'utf8').toString('base64'); assert.strictEqual(response.data, `Basic ${base64}`); } finally { await stopHTTPServer(server); } }); it('should UTF-8 encode basic auth credentials from the request URL', async () => { const server = await startHTTPServer( (req, res) => { res.end(req.headers.authorization); }, { port: SERVER_PORT } ); try { const response = await fetchAxios.get( `http://%E7%94%A8%E6%88%B7:pa%C3%9F@localhost:${server.address().port}/` ); const base64 = Buffer.from('\u7528\u6237:pa\u00df', 'utf8').toString('base64'); assert.strictEqual(response.data, `Basic ${base64}`); } finally { await stopHTTPServer(server); } }); it('keeps malformed URL credentials percent-encoding and does not throw', async () => { const server = await startHTTPServer( (req, res) => { res.end(req.headers.authorization); }, { port: SERVER_PORT } ); try { const response = await fetchAxios.get( `http://user%:foo%zz@localhost:${server.address().port}/` ); const base64 = Buffer.from('user%:foo%zz', 'utf8').toString('base64'); assert.strictEqual(response.data, `Basic ${base64}`); } finally { await stopHTTPServer(server); } }); it('should support password-only basic auth credentials from the request URL', async () => { const server = await startHTTPServer( (req, res) => { res.end(req.headers.authorization); }, { port: SERVER_PORT } ); try { const response = await fetchAxios.get(`http://:secret@localhost:${server.address().port}/`); const base64 = Buffer.from(':secret', 'utf8').toString('base64'); assert.strictEqual(response.data, `Basic ${base64}`); } finally { await stopHTTPServer(server); } }); it('should prefer config auth over basic auth credentials from the request URL', async () => { const server = await startHTTPServer( (req, res) => { res.end(req.headers.authorization); }, { port: SERVER_PORT } ); try { const auth = { username: 'config-user', password: 'config-pass' }; const response = await fetchAxios.get( `http://url-user:url-pass@localhost:${server.address().port}/`, { auth } ); const base64 = Buffer.from('config-user:config-pass', 'utf8').toString('base64'); assert.strictEqual(response.data, `Basic ${base64}`); } finally { await stopHTTPServer(server); } }); it('should support basic auth with a header', async () => { const server = await startHTTPServer( (req, res) => { res.end(req.headers.authorization); }, { port: SERVER_PORT } ); try { const auth = { username: 'foo', password: 'bar' }; const headers = { AuThOrIzAtIoN: 'Bearer 1234' }; // wonky casing to ensure caseless comparison const response = await fetchAxios.get(`http://localhost:${server.address().port}/`, { auth, headers, }); const base64 = Buffer.from('foo:bar', 'utf8').toString('base64'); assert.strictEqual(response.data, `Basic ${base64}`); } finally { await stopHTTPServer(server); } }); it('should ignore inherited nested auth fields', async () => { const server = await startHTTPServer((req, res) => res.end(req.headers.authorization), { port: SERVER_PORT, }); Object.defineProperty(Object.prototype, 'username', { value: 'inherited-user', configurable: true, }); Object.defineProperty(Object.prototype, 'password', { value: 'inherited-pass', configurable: true, }); try { const response = await fetchAxios.get(`http://localhost:${server.address().port}/`, { auth: {}, }); assert.strictEqual(response.data, 'Basic Og=='); } finally { delete Object.prototype.username; delete Object.prototype.password; await stopHTTPServer(server); } }); it('should support stream.Readable as a payload', async () => { const server = await startHTTPServer(async (req, res) => res.end('OK'), { port: SERVER_PORT }); try { const { data } = await fetchAxios.post( `http://localhost:${server.address().port}/`, stream.Readable.from('OK') ); assert.strictEqual(data, 'OK'); } finally { await stopHTTPServer(server); } }); describe('request aborting', () => { it('should be able to abort the request stream', async () => { const server = await startHTTPServer( { rate: 100000, useBuffering: true, }, { port: SERVER_PORT } ); try { const controller = new AbortController(); setTimeout(() => { controller.abort(); }, 500); await assert.rejects(async () => { await fetchAxios.post( `http://localhost:${server.address().port}/`, makeReadableStream(), { responseType: 'stream', signal: controller.signal, } ); }, /CanceledError/); } finally { await stopHTTPServer(server); } }); it('should be able to abort the response stream', async () => { const server = await startHTTPServer( (req, res) => { pipelineAsync(generateReadable(10000, 10), res).catch(() => { // Client-side abort intentionally closes the stream early in this test. }); }, { port: SERVER_PORT } ); try { const controller = new AbortController(); setTimeout(() => { controller.abort(new Error('test')); }, 800); const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, { responseType: 'stream', signal: controller.signal, }); await assert.rejects(async () => { await data.pipeTo(makeEchoStream()); }, /^(AbortError|CanceledError):/); } finally { await stopHTTPServer(server); } }); }); it('should support a timeout', async () => { const server = await startHTTPServer( async (req, res) => { await setTimeoutAsync(1000); res.end('OK'); }, { port: 0 } ); try { const timeout = 500; const ts = Date.now(); await assert.rejects(async () => { await fetchAxios(`http://localhost:${server.address().port}/`, { timeout, }); }, /timeout/); const passed = Date.now() - ts; assert.ok(passed >= timeout - 5, `early cancellation detected (${passed} ms)`); } finally { await stopHTTPServer(server); } }); describe('fetch adapter - timeout normalization', () => { it('should reject with an AxiosError(ETIMEDOUT) on timeout', async () => { const server = await startHTTPServer( async (req, res) => { await setTimeoutAsync(1000); res.end('OK'); }, { port: 0 } ); try { await assert.rejects( () => fetchAxios(`http://localhost:${server.address().port}/`, { timeout: 200, }), (err) => { assert.strictEqual(err.name, 'AxiosError'); assert.strictEqual(err.code, 'ETIMEDOUT'); assert.match(err.message, /timeout of 200ms exceeded/); return true; } ); } finally { await stopHTTPServer(server); } }); it('should not classify a user-initiated abort as a timeout', async () => { const safariFetch = (url, init) => { const signal = getFetchSignal(url, init); return new Promise((_resolve, reject) => { const onAbort = () => { signal.removeEventListener('abort', onAbort); reject(createBrokenDOMExceptionLikeError()); }; if (signal.aborted) return onAbort(); signal.addEventListener('abort', onAbort); }); }; const controller = new AbortController(); const request = fetchAxios.get('/', { signal: controller.signal, env: { fetch: safariFetch }, }); controller.abort(); await assert.rejects( () => request, (err) => { assert.strictEqual(err.name, 'CanceledError'); assert.strictEqual(err.code, 'ERR_CANCELED'); assert.strictEqual(axios.isCancel(err), true); return true; } ); }); it('sets a non-enumerable cause on canceled fetch errors so loggers do not throw (#7205)', async () => { const underlying = new Error('abort internals'); const socket = { name: 'Socket' }; socket.self = socket; underlying.socket = socket; const abortingFetch = (url, init) => { const signal = getFetchSignal(url, init); return new Promise((_resolve, reject) => { const onAbort = () => { signal.removeEventListener('abort', onAbort); reject(underlying); }; if (signal.aborted) return onAbort(); signal.addEventListener('abort', onAbort); }); }; const controller = new AbortController(); const request = fetchAxios.get('/', { signal: controller.signal, env: { fetch: abortingFetch }, }); controller.abort(); const err = await request.catch((e) => e); assert.strictEqual(err.name, 'CanceledError'); assert.strictEqual(err.code, 'ERR_CANCELED'); assert.strictEqual(err.cause, underlying); assert.strictEqual(Object.getOwnPropertyDescriptor(err, 'cause').enumerable, false); assert.ok(!Object.keys(err).includes('cause')); assert.doesNotThrow(() => JSON.stringify(Object.fromEntries(Object.entries(err)))); }); // Timing-sensitive: a 50ms abort race observed by a fake fetch can flake // under CI runner load even though the production code is fine. Retry as // a backstop. it( 'should surface ETIMEDOUT when fetch rejects with a broken DOMException on abort (Safari)', { retry: 2 }, async () => { const safariFetch = (url, init) => { const signal = getFetchSignal(url, init); return new Promise((_resolve, reject) => { const onAbort = () => { signal.removeEventListener('abort', onAbort); reject(createBrokenDOMExceptionLikeError()); }; if (signal.aborted) return onAbort(); signal.addEventListener('abort', onAbort); }); }; await assert.rejects( () => fetchAxios.get('/', { timeout: 50, env: { fetch: safariFetch }, }), (err) => { assert.strictEqual(err.name, 'AxiosError'); assert.strictEqual(err.code, 'ETIMEDOUT'); assert.match(err.message, /timeout of 50ms exceeded/); return true; } ); } ); }); it('should combine baseURL and url', async () => { const server = await startHTTPServer(async (req, res) => res.end('OK'), { port: SERVER_PORT }); try { const res = await fetchAxios('/foo'); assert.equal(res.config.baseURL, LOCAL_SERVER_URL); assert.equal(res.config.url, '/foo'); } finally { await stopHTTPServer(server); } }); it('should send QUERY requests with a body through the fetch adapter', async () => { const server = await startHTTPServer( (req, res) => { let body = ''; req.on('data', (chunk) => { body += chunk; }); req.on('end', () => { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ method: req.method, url: req.url, body })); }); }, { port: 0 } ); try { const { data } = await fetchAxios.query(`http://localhost:${server.address().port}/search`, { selector: 'field1', }); assert.strictEqual(data.method, 'QUERY'); assert.strictEqual(data.url, '/search'); assert.deepStrictEqual(JSON.parse(data.body), { selector: 'field1' }); } finally { await stopHTTPServer(server); } }); it('should support params', async () => { const server = await startHTTPServer((req, res) => res.end(req.url), { port: SERVER_PORT }); try { const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/?test=1`, { params: { foo: 1, bar: 2, }, }); assert.strictEqual(data, '/?test=1&foo=1&bar=2'); } finally { await stopHTTPServer(server); } }); it('should handle fetch failed error as an AxiosError with ERR_NETWORK code', async () => { try { await fetchAxios('http://notExistsUrl.in.nowhere'); assert.fail('should fail'); } catch (err) { assert.strictEqual(String(err), 'AxiosError: Network Error'); assert.strictEqual(err.cause && err.cause.code, 'ENOTFOUND'); // `cause` must be non-enumerable so own-property serialization is safe (#7205). assert.strictEqual(Object.getOwnPropertyDescriptor(err, 'cause').enumerable, false); } }); it('sets a non-enumerable cause on network errors so loggers do not throw (#7205)', async () => { // Underlying error carrying a circular reference, like a Node socket. const underlying = new Error('connect ECONNREFUSED'); underlying.code = 'ECONNREFUSED'; const socket = { name: 'Socket' }; socket.self = socket; // circular underlying.socket = socket; const failingFetch = () => Promise.reject(Object.assign(new TypeError('fetch failed'), { cause: underlying })); const err = await fetchAxios.get('/', { env: { fetch: failingFetch } }).catch((e) => e); assert.strictEqual(err.code, 'ERR_NETWORK'); assert.strictEqual(err.cause, underlying); // still accessible for debugging assert.strictEqual(Object.getOwnPropertyDescriptor(err, 'cause').enumerable, false); assert.ok(!Object.keys(err).includes('cause')); // pino/winston-style own-property walk must not throw on the circular cause. assert.doesNotThrow(() => JSON.stringify(Object.fromEntries(Object.entries(err)))); }); it('should not unwrap a foreign AxiosError from a fetch TypeError cause', async () => { const foreignRequest = { foreign: true }; const foreignConfig = { url: 'http://foreign.test/' }; const foreignError = new AxiosError( 'Foreign Error', 'ERR_FOREIGN', foreignConfig, foreignRequest ); const failingFetch = () => Promise.reject(Object.assign(new TypeError('fetch failed'), { cause: foreignError })); const err = await fetchAxios .get('/current', { env: { fetch: failingFetch } }) .catch((error) => error); assert.notStrictEqual(err, foreignError); assert.strictEqual(err.code, AxiosError.ERR_NETWORK); assert.strictEqual(err.config.url, '/current'); assert.notStrictEqual(err.config, foreignConfig); assert.notStrictEqual(err.request, foreignRequest); assert.strictEqual(err.cause, foreignError); }); it('should get response headers', async () => { const server = await startHTTPServer( (req, res) => { res.setHeader('foo', 'bar'); res.end(req.url); }, { port: SERVER_PORT } ); try { const { headers } = await fetchAxios.get(`http://localhost:${server.address().port}/`, { responseType: 'stream', }); assert.strictEqual(headers.get('foo'), 'bar'); } finally { await stopHTTPServer(server); } }); it('should preserve a response header named __proto__', async () => { const server = await startHTTPServer((req, res) => { res.setHeader('__proto__', 'server-value'); res.setHeader('x-other', 'other-value'); res.end('ok'); }); try { const { data, headers } = await fetchAxios.get(`http://localhost:${server.address().port}/`); assert.strictEqual(data, 'ok'); assert.strictEqual(Object.getPrototypeOf(headers), axios.AxiosHeaders.prototype); assert.strictEqual(headers.get('__proto__'), 'server-value'); assert.strictEqual(headers.get('__PROTO__'), 'server-value'); assert.strictEqual(headers.get('x-other'), 'other-value'); assert.strictEqual(headers.toJSON().__Proto__, 'server-value'); } finally { await stopHTTPServer(server); } }); describe('fetch adapter - Content-Type handling', () => { it('should set correct Content-Type for FormData automatically', async () => { const form = new NodeFormData(); form.append('foo', 'bar'); const server = await startHTTPServer( (req, res) => { const contentType = req.headers['content-type']; assert.match(contentType, /^multipart\/form-data; boundary=/i); res.end('OK'); }, { port: SERVER_PORT } ); try { await fetchAxios.post(`http://localhost:${server.address().port}/form`, form); } finally { await stopHTTPServer(server); } }); it('should remove manually set Content-Type without boundary for FormData', async () => { const form = new FormData(); form.append('foo', 'bar'); const server = await startHTTPServer( (req, res) => { const contentType = req.headers['content-type']; assert.match(contentType, /^multipart\/form-data; boundary=/i); res.end('OK'); }, { port: SERVER_PORT } ); try { await fetchAxios.post(`http://localhost:${server.address().port}/form`, form, { headers: { 'Content-Type': 'multipart/form-data' }, }); } finally { await stopHTTPServer(server); } }); it('should preserve Content-Type if it already has boundary', async () => { const form = new FormData(); form.append('foo', 'bar'); const customBoundary = '----CustomBoundary123'; const server = await startHTTPServer( (req, res) => { const contentType = req.headers['content-type']; assert.ok(contentType.includes(customBoundary)); res.end('OK'); }, { port: SERVER_PORT } ); try { await fetchAxios.post(`http://localhost:${server.address().port}/form`, form, { headers: { 'Content-Type': `multipart/form-data; boundary=${customBoundary}`, }, }); } finally { await stopHTTPServer(server); } }); }); describe('fetch adapter - User-Agent header', () => { it('should set User-Agent header to axios/ by default', async () => { const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ userAgent: req.headers['user-agent'] })); }, { port: SERVER_PORT } ); try { const { data } = await fetchAxios.post(`http://localhost:${server.address().port}/`, { payload: 'test', }); assert.strictEqual(data.userAgent, `axios/${VERSION}`); } finally { await stopHTTPServer(server); } }); it('should not override a user-provided User-Agent header', async () => { const customUA = 'my-custom-agent/1.0'; const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ userAgent: req.headers['user-agent'] })); }, { port: SERVER_PORT } ); try { const { data } = await fetchAxios.post( `http://localhost:${server.address().port}/`, { payload: 'test' }, { headers: { 'User-Agent': customUA } } ); assert.strictEqual(data.userAgent, customUA); } finally { await stopHTTPServer(server); } }); }); describe('env config', () => { it('should fallback to globalThis when utils.global is temporarily undefined', () => { const originalGlobal = utils.global; try { utils.global = undefined; assert.doesNotThrow(() => getFetch({ env: { fetch() {}, }, }) ); } finally { utils.global = originalGlobal; } }); it('should respect env fetch API configuration', async () => { const { data, headers } = await fetchAxios.get('/', { env: { fetch() { return { headers: { foo: '1', }, text: async () => 'test', }; }, }, }); assert.strictEqual(headers.get('foo'), '1'); assert.strictEqual(data, 'test'); }); it('should be able to request with lack of Request object', async () => { const form = new FormData(); form.append('x', '1'); const { data, headers } = await fetchAxios.post('/', form, { onUploadProgress() { // dummy listener to activate streaming }, env: { Request: null, fetch() { return { headers: { foo: '1', }, text: async () => 'test', }; }, }, }); assert.strictEqual(headers.get('foo'), '1'); assert.strictEqual(data, 'test'); }); it('should pass safe options when the Request object is unavailable', async () => { let captured; Object.prototype.cache = 'no-store'; Object.prototype.redirect = 'manual'; try { const { data } = await fetchAxios.get('/', { env: { Request: null, fetch(_url, init) { captured = init; return { headers: {}, text: async () => 'test', }; }, }, }); assert.strictEqual(data, 'test'); assert.strictEqual(Object.getPrototypeOf(captured), null); assert.strictEqual(captured.cache, undefined); assert.strictEqual(captured.redirect, undefined); } finally { delete Object.prototype.cache; delete Object.prototype.redirect; } }); it('should be able to handle response with lack of Response object', async () => { const { data, headers } = await fetchAxios.get('/', { onDownloadProgress() { // dummy listener to activate streaming }, env: { Request: null, Response: null, fetch() { return { headers: { foo: '1', }, text: async () => 'test', }; }, }, }); assert.strictEqual(headers.get('foo'), '1'); assert.strictEqual(data, 'test'); }); it('should fallback to the global on undefined env value', async () => { const server = await startHTTPServer((req, res) => res.end('OK'), { port: SERVER_PORT }); try { const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, { env: { fetch: undefined, }, }); assert.strictEqual(data, 'OK'); } finally { await stopHTTPServer(server); } }); it('should use current global fetch when env fetch is not specified', async () => { const globalFetch = global.fetch; vi.stubGlobal('fetch', async () => { return { headers: { foo: '1', }, text: async () => 'global', }; }); const server = await startHTTPServer((req, res) => res.end('OK'), { port: SERVER_PORT }); try { const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, { env: { fetch: undefined, }, }); assert.strictEqual(data, 'global'); } finally { vi.stubGlobal('fetch', globalFetch); await stopHTTPServer(server); } }); }); describe('size limits', () => { // Give each server its own port so aborted uploads cannot reset later // requests through a reused fetch connection pool. const makeUploadStream = (totalBytes, chunkSize = 512) => { let remaining = totalBytes; return new ReadableStream({ pull(controller) { if (remaining <= 0) { controller.close(); return; } const size = Math.min(chunkSize, remaining); remaining -= size; controller.enqueue(new Uint8Array(size)); }, }); }; it('should reject an outbound body that exceeds maxBodyLength with ERR_BAD_REQUEST', async () => { const server = await startHTTPServer( (req, res) => { res.end('ok'); }, { port: 0 } ); try { await assert.rejects( fetchAxios.post(`http://localhost:${server.address().port}/`, 'A'.repeat(2048), { maxBodyLength: 1024, }), (err) => { assert.strictEqual(err.code, 'ERR_BAD_REQUEST'); assert.match(err.message, /Request body larger than maxBodyLength limit/); return true; } ); } finally { await stopHTTPServer(server); } }); it('should reject a streamed outbound body that exceeds maxBodyLength during upload', async () => { let bytesReceived = 0; const server = await startHTTPServer( (req, res) => { req.on('data', (chunk) => { bytesReceived += chunk.length; }); req.on('error', () => {}); req.on('end', () => { res.end('ok'); }); }, { port: 0 } ); try { await assert.rejects( fetchAxios.post(`http://localhost:${server.address().port}/`, makeUploadStream(2048), { maxBodyLength: 1024, headers: { 'Content-Type': 'application/octet-stream' }, }), (err) => { assert.strictEqual(err.code, 'ERR_BAD_REQUEST'); assert.strictEqual(err.message, 'Request body larger than maxBodyLength limit'); return true; } ); assert.ok( bytesReceived <= 1024, `server should not receive more than maxBodyLength; got ${bytesReceived}` ); } finally { await stopHTTPServer(server); } }); it('should enforce maxBodyLength on a stream even when a smaller Content-Length is declared', async () => { let bytesReceived = 0; const server = await startHTTPServer( (req, res) => { req.on('data', (chunk) => { bytesReceived += chunk.length; }); req.on('error', () => {}); req.on('end', () => { res.end('ok'); }); }, { port: 0 } ); try { await assert.rejects( // A caller-declared Content-Length that under-reports the real body // must not let an oversized stream slip past the limit. fetchAxios.post(`http://localhost:${server.address().port}/`, makeUploadStream(8192), { maxBodyLength: 1024, headers: { 'Content-Type': 'application/octet-stream', 'Content-Length': '500', }, }), (err) => { assert.strictEqual(err.code, 'ERR_BAD_REQUEST'); assert.strictEqual(err.message, 'Request body larger than maxBodyLength limit'); return true; } ); assert.ok( bytesReceived <= 1024, `server should not receive more than maxBodyLength; got ${bytesReceived}` ); } finally { await stopHTTPServer(server); } }); it('should enforce maxBodyLength with custom fetch when Request is unavailable', async () => { let bytesRead = 0; await assert.rejects( fetchAxios.post('/', makeUploadStream(2048), { maxBodyLength: 1024, headers: { 'Content-Type': 'application/octet-stream', 'Content-Length': '1', }, env: { Request: null, async fetch(_url, options) { for await (const chunk of options.body) { bytesRead += chunk.byteLength; } return { headers: {}, status: 200, statusText: 'OK', text: async () => 'ok', }; }, }, }), (err) => { assert.strictEqual(err.code, 'ERR_BAD_REQUEST'); assert.strictEqual(err.message, 'Request body larger than maxBodyLength limit'); return true; } ); assert.ok(bytesRead <= 1024, `custom fetch read too many bytes; got ${bytesRead}`); }); it('should not force ReadableStream bodies when Request does not support request streams', async () => { let fetchCalled = false; class NoStreamRequest { constructor(_url, init) { if (init && utils.isReadableStream(init.body)) { throw new TypeError('ReadableStream request bodies are unsupported'); } } } await assert.rejects( fetchAxios.post('/', stream.Readable.from([Buffer.alloc(2048)]), { maxBodyLength: 1024, headers: { 'Content-Type': 'application/octet-stream', }, env: { Request: NoStreamRequest, Response: null, async fetch() { fetchCalled = true; return { headers: {}, status: 200, statusText: 'OK', text: async () => 'ok', }; }, }, }), (err) => { assert.strictEqual(err.code, 'ERR_NOT_SUPPORT'); assert.strictEqual( err.message, 'Stream request bodies are not supported by the current fetch implementation' ); return true; } ); assert.strictEqual(fetchCalled, false, 'fetch must not receive a forced ReadableStream body'); }); it('should reject a response whose Content-Length exceeds maxContentLength with ERR_BAD_RESPONSE', async () => { const payload = 'A'.repeat(8 * 1024); const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Length', Buffer.byteLength(payload)); res.end(payload); }, { port: 0 } ); try { await assert.rejects( fetchAxios.get(`http://localhost:${server.address().port}/`, { maxContentLength: 1024, }), (err) => { assert.strictEqual(err.code, 'ERR_BAD_RESPONSE'); assert.match(err.message, /maxContentLength size of 1024 exceeded/); return true; } ); } finally { await stopHTTPServer(server); } }); it('should handle plain object response headers while enforcing maxContentLength', async () => { const { data, headers } = await fetchAxios.get('/', { maxContentLength: 10, env: { async fetch() { return { status: 200, statusText: 'OK', headers: { 'content-length': '4', foo: 'bar', }, body: new ReadableStream({ start(controller) { controller.enqueue(new Uint8Array([116, 101, 115, 116])); controller.close(); }, }), }; }, }, }); assert.strictEqual(data, 'test'); assert.strictEqual(headers.get('foo'), 'bar'); }); it('should reject a chunked response that exceeds maxContentLength during streaming', async () => { const server = await startHTTPServer( (req, res) => { // Omit content-length so the cheap pre-check cannot fire; force // the stream-based enforcement path. res.setHeader('Transfer-Encoding', 'chunked'); const chunk = 'B'.repeat(1024); let sent = 0; const writeNext = () => { if (sent >= 8) { return res.end(); } sent++; res.write(chunk, writeNext); }; writeNext(); }, { port: 0 } ); try { await assert.rejects( fetchAxios.get(`http://localhost:${server.address().port}/`, { maxContentLength: 512, }), (err) => { assert.strictEqual(err.code, 'ERR_BAD_RESPONSE'); assert.match(err.message, /maxContentLength size of 512 exceeded/); return true; } ); } finally { await stopHTTPServer(server); } }); for (const preserveCause of [true, false]) { it(`should preserve a response size error when the runtime wraps it ${preserveCause ? 'with' : 'without'} a cause`, async () => { let originalError; let wrappedError; let dispatchedRequest; class WrappedResponse extends Response { async text() { try { return await super.text(); } catch (error) { originalError = error; wrappedError = new TypeError('fetch failed'); if (preserveCause) { wrappedError.cause = error; } throw wrappedError; } } } await assert.rejects( fetchAxios.get('/wrapped-response-limit', { maxContentLength: 512, env: { Response: WrappedResponse, async fetch(request) { dispatchedRequest = request; // No Content-Length: the actual body must cross the streaming limit. return new Response( new ReadableStream({ start(controller) { controller.enqueue(new Uint8Array(1024)); controller.close(); }, }) ); }, }, }), (error) => { assert.ok(originalError instanceof AxiosError); assert.ok(wrappedError instanceof TypeError); assert.strictEqual(error, originalError); assert.strictEqual(error.code, AxiosError.ERR_BAD_RESPONSE); assert.strictEqual(error.message, 'maxContentLength size of 512 exceeded'); assert.strictEqual(error.config.url, '/wrapped-response-limit'); assert.strictEqual(error.config.maxContentLength, 512); assert.strictEqual(error.request, dispatchedRequest); return true; } ); }); } it('should keep response size errors local to each invocation of a cached adapter', async () => { class WrappedResponse extends Response { async text() { try { return await super.text(); } catch (error) { throw new TypeError('fetch failed'); } } } const client = axios.create({ adapter: 'fetch', baseURL: LOCAL_SERVER_URL, maxContentLength: 512, env: { Response: WrappedResponse, async fetch(request) { const path = new URL(request.url).pathname; if (path === '/network-error') { throw new TypeError('fetch failed'); } const size = path === '/oversized' ? 1024 : path === '/empty' ? 0 : 512; return new Response('A'.repeat(size)); }, }, }); const results = await Promise.all( ['/oversized', '/exact', '/empty'].map((path) => client.get(path).catch((error) => error) ) ); assert.ok(results[0] instanceof AxiosError); assert.strictEqual(results[0].code, AxiosError.ERR_BAD_RESPONSE); assert.strictEqual(results[0].config.url, '/oversized'); assert.strictEqual(results[1].data, 'A'.repeat(512)); assert.strictEqual(results[2].data, ''); await assert.rejects(client.get('/network-error'), (error) => { assert.strictEqual(error.code, AxiosError.ERR_NETWORK); assert.strictEqual(error.config.url, '/network-error'); return true; }); assert.strictEqual((await client.get('/after')).data, 'A'.repeat(512)); }); it('should reject a data: URL whose decoded size exceeds maxContentLength (base64)', async () => { const payload = 'A'.repeat(4096); const dataUrl = 'data:application/octet-stream;base64,' + Buffer.from(payload).toString('base64'); // Use a dedicated instance without baseURL — combineURLs would otherwise // prepend baseURL to a data: URL and neutralise the pre-check. const bareAxios = axios.create({ adapter: 'fetch' }); await assert.rejects(bareAxios.get(dataUrl, { maxContentLength: 16 }), (err) => { assert.strictEqual(err.code, 'ERR_BAD_RESPONSE'); assert.match(err.message, /maxContentLength size of 16 exceeded/); return true; }); }); it('should reject percent-embedded base64 whose decoded size exceeds maxContentLength', async () => { const body = 'QQ' + '%41'.repeat(4000); const dataUrl = 'data:application/octet-stream;base64,' + body; const bareAxios = axios.create({ adapter: 'fetch' }); await assert.rejects(bareAxios.get(dataUrl, { maxContentLength: 3000 }), (err) => { assert.strictEqual(err.code, AxiosError.ERR_BAD_RESPONSE); assert.match(err.message, /maxContentLength size of 3000 exceeded/); return true; }); }); it('should allow percent-encoded base64 padding at the decoded maxContentLength', async () => { const bareAxios = axios.create({ adapter: 'fetch' }); const { data } = await bareAxios.get('data:text/plain;base64,TQ%3D%3D', { maxContentLength: 1, }); assert.strictEqual(data, 'M'); }); it('should ignore a data: URL fragment when enforcing maxContentLength', async () => { const bareAxios = axios.create({ adapter: 'fetch' }); const dataUrl = 'data:text/plain;base64,TQ==#' + 'x'.repeat(4096); const { data } = await bareAxios.get(dataUrl, { maxContentLength: 1 }); assert.strictEqual(data, 'M'); }); it('should reject a data: URL whose body size exceeds maxContentLength (non-base64)', async () => { const dataUrl = 'data:text/plain,' + 'X'.repeat(4096); const bareAxios = axios.create({ adapter: 'fetch' }); await assert.rejects(bareAxios.get(dataUrl, { maxContentLength: 16 }), (err) => { assert.strictEqual(err.code, 'ERR_BAD_RESPONSE'); assert.match(err.message, /maxContentLength size of 16 exceeded/); return true; }); }); it('should allow a percent-encoded data: URL within decoded maxContentLength', async () => { const bareAxios = axios.create({ adapter: 'fetch' }); const { data } = await bareAxios.get('data:text/plain,%E2%82%AC', { maxContentLength: 4, }); assert.strictEqual(data, '\u20ac'); }); it('should allow a response at or below maxContentLength', async () => { const payload = 'ok'; const server = await startHTTPServer( (req, res) => { res.end(payload); }, { port: 0 } ); try { const { data } = await fetchAxios.get(`http://localhost:${server.address().port}/`, { maxContentLength: 1024, }); assert.strictEqual(data, payload); } finally { await stopHTTPServer(server); } }); it('should allow a streamed outbound body at or below maxBodyLength', async () => { const payloadLength = 1024; let bytesReceived = 0; const server = await startHTTPServer( (req, res) => { req.on('data', (chunk) => { bytesReceived += chunk.length; }); req.on('end', () => { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ received: bytesReceived })); }); }, { port: 0 } ); try { const { data } = await fetchAxios.post( `http://localhost:${server.address().port}/`, makeUploadStream(payloadLength), { maxBodyLength: 1024, headers: { 'Content-Type': 'application/octet-stream' }, } ); assert.strictEqual(data.received, payloadLength); } finally { await stopHTTPServer(server); } }); it('should allow a body at or below maxBodyLength', async () => { const payload = 'hello'; let received; const server = await startHTTPServer( (req, res) => { const chunks = []; req.on('data', (c) => chunks.push(c)); req.on('end', () => { received = Buffer.concat(chunks).toString(); res.end('ok'); }); }, { port: 0 } ); try { await fetchAxios.post(`http://localhost:${server.address().port}/`, payload, { maxBodyLength: 1024, }); assert.strictEqual(received, payload); } finally { await stopHTTPServer(server); } }); }); describe('capability probe cleanup', () => { it('should cancel the ReadableStream created during the request stream probe', () => { // The fetch adapter factory probes for request-stream support by creating // a ReadableStream as a Request body. Previously the stream was never // cancelled, leaving a dangling pull-algorithm promise (async resource leak // visible via `--detect-async-leaks` or Node.js async_hooks). // // Calling getFetch with a unique env triggers a fresh factory() execution // (including the probe). We spy on ReadableStream.prototype.cancel to // verify it is invoked during the probe. const cancelSpy = vi.spyOn(ReadableStream.prototype, 'cancel'); try { // Unique fetch function ensures cache miss → factory() re-runs the probe. const uniqueFetch = async () => new Response('ok'); getFetch({ env: { fetch: uniqueFetch } }); assert.ok( cancelSpy.mock.calls.length > 0, 'ReadableStream.prototype.cancel should be called during the capability probe' ); } finally { cancelSpy.mockRestore(); } }); }); }); axios-axios-aa54a5e/tests/unit/adapters/formBodyLength.test.js000066400000000000000000000001361526660636600246500ustar00rootroot00000000000000import formBodyLengthCases from '../../setup/formBodyLengthCases.js'; formBodyLengthCases(); axios-axios-aa54a5e/tests/unit/adapters/http.test.js000066400000000000000000007033151526660636600227150ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import { startHTTPServer, stopHTTPServer, SERVER_HANDLER_STREAM_ECHO, handleFormData, setTimeoutAsync, generateReadable, } from '../../setup/server.js'; import axios from '../../../index.js'; import AxiosError from '../../../lib/core/AxiosError.js'; import httpAdapter, { __isNodeEnvProxyEnabled, __isSameOriginRedirect, __setProxy, } from '../../../lib/adapters/http.js'; import HttpsProxyAgent from 'https-proxy-agent'; import http from 'http'; import https from 'https'; import net from 'net'; import stream from 'stream'; import zlib from 'zlib'; import fs from 'fs'; import os from 'os'; import path from 'path'; import devNull from 'dev-null'; import FormDataLegacy from 'form-data'; import { IncomingForm } from 'formidable'; import { FormData as FormDataPolyfill, Blob as BlobPolyfill } from 'formdata-node'; import express from 'express'; import multer from 'multer'; import getStream from 'get-stream'; import bodyParser from 'body-parser'; import { AbortController } from 'abortcontroller-polyfill/dist/cjs-ponyfill.js'; import { lookup } from 'dns'; import { EventEmitter } from 'events'; const OPEN_WEB_PORT = 80; const SERVER_PORT = 8020; const PROXY_PORT = 8030; const ALTERNATE_SERVER_PORT = 8040; describe('supports http with nodejs', () => { const adaptersTestsDir = path.join(process.cwd(), 'tests/unit/adapters'); const thisTestFilePath = path.join(adaptersTestsDir, 'http.test.js'); const FormDataSpecCompliant = typeof FormData !== 'undefined' ? FormData : FormDataPolyfill; const BlobSpecCompliant = typeof Blob !== 'undefined' ? Blob : BlobPolyfill; const isBlobSupported = typeof Blob !== 'undefined'; function toleranceRange(positive, negative) { const p = 1 + positive / 100; const n = 1 - negative / 100; return (actualValue, value) => { return actualValue > value ? actualValue <= value * p : actualValue >= value * n; }; } class HangingConnectSocket extends stream.Duplex { constructor() { super(); this.connecting = true; } _read() {} _write(_chunk, _encoding, callback) { callback(); } setKeepAlive() { return this; } setNoDelay() { return this; } setTimeout() { return this; } } class HangingConnectAgent extends http.Agent { createConnection() { return new HangingConnectSocket(); } } it('should support IPv4 literal strings', async () => { const data = { firstName: 'Fred', lastName: 'Flintstone', emailAddr: 'fred@example.com', }; const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(data)); }, { port: SERVER_PORT } ); try { const { data: responseData } = await axios.get(`http://127.0.0.1:${server.address().port}`); assert.deepStrictEqual(responseData, data); } finally { await stopHTTPServer(server); } }); it('should support IPv6 literal strings', async () => { var data = { firstName: 'Fred', lastName: 'Flintstone', emailAddr: 'fred@example.com', }; const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(data)); }, { port: SERVER_PORT } ); try { const { data: responseData } = await axios.get(`http://[::1]:${server.address().port}`, { proxy: false, }); assert.deepStrictEqual(responseData, data); } finally { await stopHTTPServer(server); } }); it('should throw an error if the timeout property is not parsable as a number', async () => { const server = await startHTTPServer( (req, res) => { setTimeout(() => { res.end(); }, 1000); }, { port: SERVER_PORT } ); try { await assert.rejects( axios.get(`http://localhost:${server.address().port}`, { timeout: { strangeTimeout: 250 }, }), (error) => { assert.strictEqual(error.code, AxiosError.ERR_BAD_OPTION_VALUE); assert.strictEqual(error.message, 'error trying to parse `config.timeout` to int'); return true; } ); } finally { await stopHTTPServer(server); } }); it('should sanitize request headers containing CRLF characters', async () => { const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'application/json'); res.end( JSON.stringify({ xTest: req.headers['x-test'], injected: req.headers.injected ?? null, }) ); }, { port: SERVER_PORT } ); try { const { data } = await axios.get(`http://localhost:${server.address().port}/`, { headers: { 'x-test': '\tok\r\nInjected: yes ', }, }); assert.strictEqual(data.xTest, 'okInjected: yes'); assert.strictEqual(data.injected, null); } finally { await stopHTTPServer(server); } }); it('should allow request interceptors to encode Unicode header values before Node sends them', async () => { const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'application/json'); res.end( JSON.stringify({ oprtName: req.headers.oprtname, }) ); }, { port: SERVER_PORT } ); const instance = axios.create({ proxy: false }); instance.interceptors.request.use((config) => { config.headers.oprtName = encodeURIComponent(config.headers.oprtName); return config; }); try { const { data } = await instance.get(`http://localhost:${server.address().port}/`, { headers: { oprtName: '请求用户', }, }); assert.strictEqual(data.oprtName, encodeURIComponent('请求用户')); } finally { await stopHTTPServer(server); } }); it('should sanitize unencoded Unicode request headers before passing them to Node', async () => { const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'application/json'); res.end( JSON.stringify({ xTest: req.headers['x-test'], }) ); }, { port: SERVER_PORT } ); try { const { data } = await axios.get(`http://localhost:${server.address().port}/`, { proxy: false, headers: { 'x-test': '请求用户', }, }); assert.strictEqual(data.xTest, ''); } finally { await stopHTTPServer(server); } }); it('should parse the timeout property', async () => { const server = await startHTTPServer( (req, res) => { setTimeout(() => { res.end(); }, 1000); }, { port: SERVER_PORT } ); try { await assert.rejects( axios.get(`http://localhost:${server.address().port}`, { timeout: '250', }), (error) => { assert.strictEqual(error.code, 'ECONNABORTED'); assert.strictEqual(error.message, 'timeout of 250ms exceeded'); return true; } ); } finally { await stopHTTPServer(server); } }); it('should respect the timeout property', async () => { const server = await startHTTPServer( (req, res) => { setTimeout(() => { res.end(); }, 1000); }, { port: SERVER_PORT } ); try { await assert.rejects( axios.get(`http://localhost:${server.address().port}`, { timeout: 250, }), (error) => { assert.strictEqual(error.code, 'ECONNABORTED'); assert.strictEqual(error.message, 'timeout of 250ms exceeded'); return true; } ); } finally { await stopHTTPServer(server); } }); it('should respect the timeout property during TCP connect with maxRedirects set to 0', async () => { const timeout = 100; const guardTimeout = 1000; const started = Date.now(); const controller = new AbortController(); const agent = new HangingConnectAgent(); let guardTimer; const request = axios.get('http://connect-timeout.test/', { httpAgent: agent, maxRedirects: 0, proxy: false, signal: controller.signal, timeout, }); const guard = new Promise((_resolve, reject) => { guardTimer = setTimeout(() => { controller.abort(); reject(new Error('request did not honor timeout during connect')); }, guardTimeout); }); try { await assert.rejects(Promise.race([request, guard]), (error) => { const elapsed = Date.now() - started; assert.strictEqual(error.code, 'ECONNABORTED'); assert.strictEqual(error.message, `timeout of ${timeout}ms exceeded`); assert.ok(elapsed < guardTimeout, `request timed out after ${elapsed}ms`); return true; }); } finally { clearTimeout(guardTimer); controller.abort(); agent.destroy(); } }); it('should not time out immediately for timeout set to zero during TCP connect', async () => { const controller = new AbortController(); const agent = new HangingConnectAgent(); const request = axios .get('http://connect-timeout.test/', { httpAgent: agent, maxRedirects: 0, proxy: false, signal: controller.signal, timeout: '0', }) .then( () => null, (error) => error ); try { await setTimeoutAsync(50); controller.abort(); const error = await request; assert.strictEqual(error.code, AxiosError.ERR_CANCELED); } finally { controller.abort(); agent.destroy(); } }); it('should respect the timeoutErrorMessage property', async () => { const server = await startHTTPServer( (req, res) => { setTimeout(() => { res.end(); }, 1000); }, { port: SERVER_PORT } ); try { await assert.rejects( axios.get(`http://localhost:${server.address().port}`, { timeout: 250, timeoutErrorMessage: 'oops, timeout', }), (error) => { assert.strictEqual(error.code, 'ECONNABORTED'); assert.strictEqual(error.message, 'oops, timeout'); return true; } ); } finally { await stopHTTPServer(server); } }); it('should allow passing JSON', async () => { const data = { firstName: 'Fred', lastName: 'Flintstone', emailAddr: 'fred@example.com', }; const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(data)); }, { port: SERVER_PORT } ); try { const { data: responseData } = await axios.get(`http://localhost:${server.address().port}`); assert.deepStrictEqual(responseData, data); } finally { await stopHTTPServer(server); } }); it('should allow passing JSON with BOM', async () => { const data = { firstName: 'Fred', lastName: 'Flintstone', emailAddr: 'fred@example.com', }; const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'application/json'); const bomBuffer = Buffer.from([0xef, 0xbb, 0xbf]); const jsonBuffer = Buffer.from(JSON.stringify(data)); res.end(Buffer.concat([bomBuffer, jsonBuffer])); }, { port: SERVER_PORT } ); try { const { data: responseData } = await axios.get(`http://localhost:${server.address().port}`); assert.deepStrictEqual(responseData, data); } finally { await stopHTTPServer(server); } }); it('should redirect', async () => { const expectedResponse = 'test response'; const server = await startHTTPServer( (req, res) => { if (req.url === '/one') { res.setHeader('Location', '/two'); res.statusCode = 302; res.end(); return; } res.end(expectedResponse); }, { port: SERVER_PORT } ); try { const response = await axios.get(`http://localhost:${server.address().port}/one`, { maxRedirects: 1, }); assert.strictEqual(response.data, expectedResponse); assert.strictEqual(response.request.path, '/two'); } finally { await stopHTTPServer(server); } }); it('should not redirect', async () => { const server = await startHTTPServer( (req, res) => { res.setHeader('Location', '/foo'); res.statusCode = 302; res.end(); }, { port: SERVER_PORT } ); try { const response = await axios.get(`http://localhost:${server.address().port}/one`, { maxRedirects: 0, }); assert.strictEqual(response.status, 302); assert.strictEqual(response.headers.location, '/foo'); } catch (error) { assert.strictEqual(error.message, 'Request failed with status code 302'); assert.strictEqual(error.response.status, 302); assert.strictEqual(error.response.headers.location, '/foo'); } finally { await stopHTTPServer(server); } }); it('should support max redirects', async () => { var i = 1; const server = await startHTTPServer( (req, res) => { res.setHeader('Location', `/${i}`); res.statusCode = 302; res.end(); i++; }, { port: SERVER_PORT } ); try { await axios.get(`http://localhost:${server.address().port}`, { maxRedirects: 3, }); } catch (error) { assert.strictEqual(error.code, AxiosError.ERR_FR_TOO_MANY_REDIRECTS); assert.strictEqual(error.message, 'Maximum number of redirects exceeded'); } finally { await stopHTTPServer(server); } }); it('should support beforeRedirect', async () => { const server = await startHTTPServer( (req, res) => { res.setHeader('Location', '/foo'); res.statusCode = 302; res.end(); }, { port: SERVER_PORT } ); try { await axios.get(`http://localhost:${server.address().port}/one`, { maxRedirects: 3, beforeRedirect: (options, responseDetails) => { if (options.path === '/foo' && responseDetails.headers.location === '/foo') { throw new Error('Provided path is not allowed'); } }, }); } catch (error) { assert.strictEqual(error.message, 'Redirected request failed: Provided path is not allowed'); } finally { await stopHTTPServer(server); } }); it('should pass requestDetails to beforeRedirect with the original URL', async () => { const server = await startHTTPServer( (req, res) => { res.setHeader('Location', '/foo'); res.statusCode = 302; res.end(); }, { port: SERVER_PORT } ); const originalUrl = `http://localhost:${server.address().port}/bar`; let capturedUrl; try { await axios.get(originalUrl, { maxRedirects: 3, beforeRedirect: (options, responseDetails, requestDetails) => { if (options.path === '/foo' && responseDetails.headers.location === '/foo') { capturedUrl = requestDetails.url; throw new Error('Provided path is not allowed'); } }, }); } catch (error) { assert.strictEqual(error.message, 'Redirected request failed: Provided path is not allowed'); assert.strictEqual(capturedUrl, originalUrl); } finally { await stopHTTPServer(server); } }); it('should support beforeRedirect and proxy with redirect', async () => { let requestCount = 0; let proxyUseCount = 0; let totalRedirectCount = 5; let configBeforeRedirectCount = 0; const server = await startHTTPServer( (req, res) => { requestCount += 1; if (requestCount <= totalRedirectCount) { res.setHeader('Location', `http://localhost:${SERVER_PORT}`); res.writeHead(302); } res.end(); }, { port: SERVER_PORT } ); const proxy = await startHTTPServer( (req, res) => { proxyUseCount += 1; const targetUrl = new URL(req.url, `http://localhost:${server.address().port}`); const opts = { host: targetUrl.hostname, port: targetUrl.port, path: targetUrl.path, method: req.method, }; const request = http.get(opts, (response) => { res.writeHead(response.statusCode, response.headers); stream.pipeline(response, res, () => {}); }); request.on('error', (err) => { console.warn('request error', err); res.statusCode = 500; res.end(); }); }, { port: PROXY_PORT } ); await axios.get(`http://localhost:${server.address().port}/`, { proxy: { host: 'localhost', port: PROXY_PORT, }, maxRedirects: totalRedirectCount, beforeRedirect: (options) => { configBeforeRedirectCount += 1; }, }); assert.strictEqual(totalRedirectCount, configBeforeRedirectCount); assert.strictEqual(totalRedirectCount + 1, proxyUseCount); await stopHTTPServer(server); await stopHTTPServer(proxy); }); it('should strip sensitiveHeaders on cross-origin redirect', async () => { let capturedHeaders; // destination server — different port means different origin const destination = await startHTTPServer((req, res) => { capturedHeaders = req.headers; res.statusCode = 200; res.end('ok'); }); // origin server — redirects to destination (cross-origin) const origin = await startHTTPServer((req, res) => { res.setHeader('Location', `http://localhost:${destination.address().port}/dest`); res.statusCode = 302; res.end(); }); try { await axios.get(`http://localhost:${origin.address().port}/src`, { maxRedirects: 5, headers: { 'X-API-Key': 'secret', 'X-Other': 'keep' }, sensitiveHeaders: ['X-API-Key'], }); assert.strictEqual(capturedHeaders['x-api-key'], undefined, 'X-API-Key should be stripped'); assert.strictEqual(capturedHeaders['x-other'], 'keep', 'X-Other should be preserved'); } finally { await stopHTTPServer(origin); await stopHTTPServer(destination); } }); it('should preserve sensitiveHeaders on same-origin redirect', async () => { let capturedHeaders; let requestCount = 0; const server = await startHTTPServer((req, res) => { requestCount++; if (requestCount === 1) { res.setHeader('Location', '/dest'); res.statusCode = 302; res.end(); } else { capturedHeaders = req.headers; res.statusCode = 200; res.end('ok'); } }); try { await axios.get(`http://localhost:${server.address().port}/src`, { maxRedirects: 5, headers: { 'X-API-Key': 'secret' }, sensitiveHeaders: ['X-API-Key'], }); assert.strictEqual( capturedHeaders['x-api-key'], 'secret', 'X-API-Key should be preserved on same-origin redirect' ); } finally { await stopHTTPServer(server); } }); it('should strip sensitiveHeaders case-insensitively on cross-origin redirect', async () => { let capturedHeaders; const destination = await startHTTPServer((req, res) => { capturedHeaders = req.headers; res.statusCode = 200; res.end('ok'); }); const origin = await startHTTPServer((req, res) => { res.setHeader('Location', `http://localhost:${destination.address().port}/dest`); res.statusCode = 302; res.end(); }); try { await axios.get(`http://localhost:${origin.address().port}/src`, { maxRedirects: 5, // Header sent with mixed casing; sensitiveHeaders list uses different casing headers: { 'X-Api-Key': 'secret' }, sensitiveHeaders: ['x-api-key'], }); assert.strictEqual( capturedHeaders['x-api-key'], undefined, 'X-Api-Key should be stripped case-insensitively' ); } finally { await stopHTTPServer(origin); await stopHTTPServer(destination); } }); it('should strip sensitiveHeaders configured on an instance', async () => { let capturedHeaders; const destination = await startHTTPServer((req, res) => { capturedHeaders = req.headers; res.statusCode = 200; res.end('ok'); }); const origin = await startHTTPServer((req, res) => { res.setHeader('Location', `http://localhost:${destination.address().port}/dest`); res.statusCode = 302; res.end(); }); const client = axios.create({ headers: { 'X-API-Key': 'secret', 'X-Other': 'keep' }, sensitiveHeaders: ['X-API-Key'], }); try { await client.get(`http://localhost:${origin.address().port}/src`, { maxRedirects: 5, }); assert.strictEqual(capturedHeaders['x-api-key'], undefined, 'X-API-Key should be stripped'); assert.strictEqual(capturedHeaders['x-other'], 'keep', 'X-Other should be preserved'); } finally { await stopHTTPServer(origin); await stopHTTPServer(destination); } }); it('should reject invalid sensitiveHeaders config', async () => { await assert.rejects( axios.get('http://localhost:1/', { sensitiveHeaders: 'X-API-Key' }), (error) => { assert.strictEqual(error.code, AxiosError.ERR_BAD_OPTION_VALUE); assert.strictEqual(error.message, 'sensitiveHeaders must be an array of strings'); return true; } ); await assert.rejects( axios.get('http://localhost:1/', { sensitiveHeaders: [null] }), (error) => { assert.strictEqual(error.code, AxiosError.ERR_BAD_OPTION_VALUE); assert.strictEqual(error.message, 'sensitiveHeaders must be an array of strings'); return true; } ); }); it('should fail closed when sensitiveHeaders redirect origin cannot be parsed', () => { assert.strictEqual( __isSameOriginRedirect({ href: 'http://localhost/final' }, { url: 'http://localhost/start' }), true ); assert.strictEqual( __isSameOriginRedirect({ href: 'http://[::1' }, { url: 'http://localhost/start' }), false ); assert.strictEqual(__isSameOriginRedirect({ href: 'http://localhost/final' }), false); }); it('should wrap HTTP errors and keep stack', async () => { const server = await startHTTPServer( (req, res) => { res.statusCode = 400; res.end(); }, { port: SERVER_PORT } ); try { await assert.rejects( async function stackTraceTest() { await axios.get(`http://localhost:${server.address().port}/`); }, (error) => { const matches = [...error.stack.matchAll(/stackTraceTest/g)]; assert.strictEqual(error.name, 'AxiosError'); assert.strictEqual(error.isAxiosError, true); assert.strictEqual(error.code, AxiosError.ERR_BAD_REQUEST); assert.strictEqual(error.message, 'Request failed with status code 400'); assert.strictEqual(matches.length, 1, error.stack); return true; } ); } finally { await stopHTTPServer(server); } }); it('should wrap interceptor errors and keep stack', async () => { const axiosInstance = axios.create(); axiosInstance.interceptors.request.use((res) => { throw new Error('from request interceptor'); }); const server = await startHTTPServer( (req, res) => { res.end(); }, { port: SERVER_PORT } ); try { await assert.rejects( async function stackTraceTest() { await axiosInstance.get(`http://localhost:${server.address().port}/one`); }, (error) => { const matches = [...error.stack.matchAll(/stackTraceTest/g)]; assert.strictEqual(error.name, 'Error'); assert.strictEqual(error.message, 'from request interceptor'); assert.strictEqual(matches.length, 1, error.stack); return true; } ); } finally { await stopHTTPServer(server); } }); it('should preserve the HTTP verb on redirect', async () => { const server = await startHTTPServer( (req, res) => { if (req.method.toLowerCase() !== 'head') { res.statusCode = 400; res.end(); return; } var parsed = new URL(req.url, 'http://localhost'); if (parsed.pathname === '/one') { res.setHeader('Location', '/two'); res.statusCode = 302; res.end(); } else { res.end(); } }, { port: SERVER_PORT } ); try { const response = await axios.head(`http://localhost:${server.address().port}/one`); assert.strictEqual(response.status, 200); } finally { await stopHTTPServer(server); } }); describe('compression', async () => { const isZstdSupported = typeof zlib.createZstdDecompress === 'function' && typeof zlib.zstdCompress === 'function'; it('should support transparent gunzip', async () => { const data = { firstName: 'Fred', lastName: 'Flintstone', emailAddr: 'fred@example.com', }; const zipped = await new Promise((resolve, reject) => { zlib.gzip(JSON.stringify(data), (error, compressed) => { if (error) { reject(error); return; } resolve(compressed); }); }); const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'application/json'); res.setHeader('Content-Encoding', 'gzip'); res.end(zipped); }, { port: SERVER_PORT } ); try { const { data: responseData } = await axios.get( `http://localhost:${server.address().port}/` ); assert.deepStrictEqual(responseData, data); } finally { await stopHTTPServer(server); } }); it('should support gunzip error handling', async () => { const server = await startHTTPServer( (req, res) => { res.statusCode = 206; res.setHeader('Content-Type', 'application/json'); res.setHeader('Content-Encoding', 'gzip'); res.setHeader('X-Stream-Error', 'yes'); res.end('invalid response'); }, { port: SERVER_PORT } ); try { await assert.rejects( async () => { await axios.get(`http://localhost:${server.address().port}/`); }, (error) => { assert.strictEqual(error.response.status, 206); assert.strictEqual(error.response.headers.get('x-stream-error'), 'yes'); assert.strictEqual(error.status, 206); return true; } ); } finally { await stopHTTPServer(server); } }); it('should support disabling automatic decompression of response data', async () => { const data = 'Test data'; const zipped = await new Promise((resolve, reject) => { zlib.gzip(data, (error, compressed) => { if (error) { reject(error); return; } resolve(compressed); }); }); const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'text/html;charset=utf-8'); res.setHeader('Content-Encoding', 'gzip'); res.end(zipped); }, { port: SERVER_PORT } ); try { const response = await axios.get(`http://localhost:${server.address().port}/`, { decompress: false, responseType: 'arraybuffer', }); assert.strictEqual(response.data.toString('base64'), zipped.toString('base64')); } finally { await stopHTTPServer(server); } }); it('should not advertise zstd by default', async () => { let acceptEncoding; const server = await startHTTPServer( (req, res) => { acceptEncoding = req.headers['accept-encoding']; res.end('ok'); }, { port: SERVER_PORT } ); try { await axios.get(`http://localhost:${server.address().port}/`); assert.strictEqual(acceptEncoding.includes('zstd'), false); } finally { await stopHTTPServer(server); } }); it('should advertise zstd when enabled through transitional config and supported', async () => { if (!isZstdSupported) { return; } let acceptEncoding; const server = await startHTTPServer( (req, res) => { acceptEncoding = req.headers['accept-encoding']; res.end('ok'); }, { port: SERVER_PORT } ); try { await axios.get(`http://localhost:${server.address().port}/`, { transitional: { advertiseZstdAcceptEncoding: true, }, }); assert.strictEqual(acceptEncoding.includes('zstd'), true); } finally { await stopHTTPServer(server); } }); describe('algorithms', () => { const responseBody = 'str'; const gzip = (value) => new Promise((resolve, reject) => { zlib.gzip(value, (error, compressed) => { if (error) { reject(error); return; } resolve(compressed); }); }); const deflate = (value) => new Promise((resolve, reject) => { zlib.deflate(value, (error, compressed) => { if (error) { reject(error); return; } resolve(compressed); }); }); const deflateRaw = (value) => new Promise((resolve, reject) => { zlib.deflateRaw(value, (error, compressed) => { if (error) { reject(error); return; } resolve(compressed); }); }); const brotliCompress = (value) => new Promise((resolve, reject) => { zlib.brotliCompress(value, (error, compressed) => { if (error) { reject(error); return; } resolve(compressed); }); }); const zstdCompress = (value) => new Promise((resolve, reject) => { zlib.zstdCompress(value, (error, compressed) => { if (error) { reject(error); return; } resolve(compressed); }); }); for (const [typeName, zipped] of Object.entries({ gzip: gzip(responseBody), GZIP: gzip(responseBody), compress: gzip(responseBody), deflate: deflate(responseBody), 'deflate-raw': deflateRaw(responseBody), br: brotliCompress(responseBody), ...(isZstdSupported ? { zstd: zstdCompress(responseBody) } : {}), })) { const type = typeName.split('-')[0]; describe(`${typeName} decompression`, () => { it('should support decompression', async () => { const server = await startHTTPServer( async (req, res) => { res.setHeader('Content-Encoding', type); res.end(await zipped); }, { port: SERVER_PORT } ); try { const { data } = await axios.get(`http://localhost:${server.address().port}`); assert.strictEqual(data, responseBody); } finally { await stopHTTPServer(server); } }); it(`should not fail if response content-length header is missing (${type})`, async () => { const server = await startHTTPServer( async (req, res) => { res.setHeader('Content-Encoding', type); res.removeHeader('Content-Length'); res.end(await zipped); }, { port: SERVER_PORT } ); try { const { data } = await axios.get(`http://localhost:${server.address().port}`); assert.strictEqual(data, responseBody); } finally { await stopHTTPServer(server); } }); it('should not fail with chunked responses (without Content-Length header)', async () => { const server = await startHTTPServer( async (req, res) => { res.setHeader('Content-Encoding', type); res.setHeader('Transfer-Encoding', 'chunked'); res.removeHeader('Content-Length'); res.write(await zipped); res.end(); }, { port: SERVER_PORT } ); try { const { data } = await axios.get(`http://localhost:${server.address().port}`); assert.strictEqual(data, responseBody); } finally { await stopHTTPServer(server); } }); it('should not fail with an empty response without content-length header (Z_BUF_ERROR)', async () => { const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Encoding', type); res.removeHeader('Content-Length'); res.end(); }, { port: SERVER_PORT } ); try { const { data } = await axios.get(`http://localhost:${server.address().port}`); assert.strictEqual(data, ''); } finally { await stopHTTPServer(server); } }); it('should not fail with an empty response with content-length header (Z_BUF_ERROR)', async () => { const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Encoding', type); res.end(); }, { port: SERVER_PORT } ); try { await axios.get(`http://localhost:${server.address().port}`); } finally { await stopHTTPServer(server); } }); it('should reject when the server aborts mid-stream and maxRedirects is 0', async () => { const server = await startHTTPServer( async (req, res) => { res.setHeader('Content-Encoding', type); res.setHeader('Transfer-Encoding', 'chunked'); res.removeHeader('Content-Length'); res.write(await zipped); setTimeout(() => res.socket.destroy(), 10); }, { port: SERVER_PORT } ); try { await assert.rejects( axios.get(`http://localhost:${server.address().port}`, { maxRedirects: 0 }), (err) => err && err.code === 'ECONNRESET' ); } finally { await stopHTTPServer(server); } }); }); } }); }); it('should support UTF8', async () => { const str = Array(100000).join('ж'); const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'text/html; charset=UTF-8'); res.end(str); }, { port: SERVER_PORT } ); try { const response = await axios.get(`http://localhost:${server.address().port}/`); assert.strictEqual(response.data, str); } finally { await stopHTTPServer(server); } }); it('should support basic auth', async () => { const server = await startHTTPServer( (req, res) => { res.end(req.headers.authorization); }, { port: SERVER_PORT } ); try { const user = 'foo'; const headers = { Authorization: 'Bearer 1234' }; const response = await axios.get(`http://${user}@localhost:${server.address().port}/`, { headers, }); const base64 = Buffer.from(`${user}:`, 'utf8').toString('base64'); assert.strictEqual(response.data, `Basic ${base64}`); } finally { await stopHTTPServer(server); } }); it('should decode basic auth credentials from the request URL', async () => { const server = await startHTTPServer( (req, res) => { res.end(req.headers.authorization); }, { port: SERVER_PORT } ); try { const response = await axios.get( `http://my%40email.com:pa%24ss@localhost:${server.address().port}/` ); const base64 = Buffer.from('my@email.com:pa$ss', 'utf8').toString('base64'); assert.strictEqual(response.data, `Basic ${base64}`); } finally { await stopHTTPServer(server); } }); it('keeps malformed URL credentials percent-encoding and does not throw', async () => { const server = await startHTTPServer( (req, res) => { res.end(req.headers.authorization); }, { port: SERVER_PORT } ); try { const response = await axios.get(`http://user%:foo%zz@localhost:${server.address().port}/`); const base64 = Buffer.from('user%:foo%zz', 'utf8').toString('base64'); assert.strictEqual(response.data, `Basic ${base64}`); } finally { await stopHTTPServer(server); } }); it('should support password-only basic auth credentials from the request URL', async () => { const server = await startHTTPServer( (req, res) => { res.end(req.headers.authorization); }, { port: SERVER_PORT } ); try { const response = await axios.get(`http://:secret@localhost:${server.address().port}/`); const base64 = Buffer.from(':secret', 'utf8').toString('base64'); assert.strictEqual(response.data, `Basic ${base64}`); } finally { await stopHTTPServer(server); } }); it('should support basic auth with a header', async () => { const server = await startHTTPServer( (req, res) => { res.end(req.headers.authorization); }, { port: SERVER_PORT } ); try { const auth = { username: 'foo', password: 'bar' }; const headers = { AuThOrIzAtIoN: 'Bearer 1234' }; // wonky casing to ensure caseless comparison const response = await axios.get(`http://localhost:${server.address().port}/`, { auth, headers, }); const base64 = Buffer.from('foo:bar', 'utf8').toString('base64'); assert.strictEqual(response.data, `Basic ${base64}`); } finally { await stopHTTPServer(server); } }); it('should ignore inherited nested request option fields in http adapter', async () => { const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'application/json'); res.end( JSON.stringify({ authorization: req.headers.authorization, url: req.url, }) ); }, { port: SERVER_PORT } ); Object.defineProperty(Object.prototype, 'username', { value: 'inherited-user', configurable: true, }); Object.defineProperty(Object.prototype, 'password', { value: 'inherited-pass', configurable: true, }); Object.defineProperty(Object.prototype, 'serialize', { value() { return 'inherited=1'; }, configurable: true, }); try { const response = await axios.get(`http://localhost:${server.address().port}/demo`, { auth: {}, params: { value: 'a b' }, paramsSerializer: {}, }); assert.deepStrictEqual(response.data, { authorization: 'Basic Og==', url: '/demo?value=a+b', }); } finally { delete Object.prototype.username; delete Object.prototype.password; delete Object.prototype.serialize; await stopHTTPServer(server); } }); it('should ignore inherited proxy when http adapter receives a plain config', async () => { const proxyEnvKeys = ['http_proxy', 'HTTP_PROXY', 'https_proxy', 'HTTPS_PROXY']; const originalProxyEnv = Object.create(null); let proxy; let target; let proxyHits = 0; let targetHits = 0; for (const key of proxyEnvKeys) { originalProxyEnv[key] = process.env[key]; delete process.env[key]; } try { proxy = await startHTTPServer((req, res) => { proxyHits += 1; res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ via: 'proxy', url: req.url })); }); target = await startHTTPServer((req, res) => { targetHits += 1; res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ via: 'target', url: req.url })); }); Object.defineProperty(Object.prototype, 'proxy', { value: { protocol: 'http', host: '127.0.0.1', port: proxy.address().port, }, configurable: true, }); const response = await httpAdapter({ method: 'get', url: `http://127.0.0.1:${target.address().port}/direct`, headers: {}, maxRedirects: 0, maxContentLength: -1, maxBodyLength: -1, timeout: 0, }); const data = JSON.parse(response.data); assert.strictEqual(proxyHits, 0); assert.strictEqual(targetHits, 1); assert.deepStrictEqual(data, { via: 'target', url: '/direct' }); } finally { delete Object.prototype.proxy; for (const key of proxyEnvKeys) { if (originalProxyEnv[key] === undefined) { delete process.env[key]; } else { process.env[key] = originalProxyEnv[key]; } } await stopHTTPServer(target); await stopHTTPServer(proxy); } }); it('should ignore inherited paramsSerializer when http adapter receives a plain config', async () => { let server; let serializerInvoked = false; Object.defineProperty(Object.prototype, 'paramsSerializer', { value() { serializerInvoked = true; return 'inherited=1'; }, configurable: true, }); try { server = await startHTTPServer((req, res) => { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ url: req.url })); }); const response = await httpAdapter({ method: 'get', url: `http://127.0.0.1:${server.address().port}/direct`, headers: {}, params: { value: 'a b' }, proxy: false, maxRedirects: 0, maxContentLength: -1, maxBodyLength: -1, timeout: 0, }); const data = JSON.parse(response.data); assert.strictEqual(serializerInvoked, false); assert.deepStrictEqual(data, { url: '/direct?value=a+b' }); } finally { delete Object.prototype.paramsSerializer; await stopHTTPServer(server); } }); it('should preserve basic auth across same-origin 303 POST -> GET redirect', async () => { const server = await startHTTPServer( (req, res) => { if (req.url === '/login') { res.setHeader('Location', '/profile'); res.statusCode = 303; res.end(); return; } res.end(req.headers.authorization || ''); }, { port: SERVER_PORT } ); try { const auth = { username: 'foo', password: 'bar' }; const response = await axios.post( `http://localhost:${server.address().port}/login`, { hello: 'world' }, { auth, maxRedirects: 1 } ); const base64 = Buffer.from('foo:bar', 'utf8').toString('base64'); assert.strictEqual(response.data, `Basic ${base64}`); assert.strictEqual(response.request.path, '/profile'); } finally { await stopHTTPServer(server); } }); it('should strip basic auth on cross-origin redirect', async () => { const targetServer = await startHTTPServer( (req, res) => { res.end(req.headers.authorization || 'no-auth'); }, { port: ALTERNATE_SERVER_PORT } ); const redirectServer = await startHTTPServer( (req, res) => { res.setHeader('Location', `http://127.0.0.1:${targetServer.address().port}/`); res.statusCode = 302; res.end(); }, { port: SERVER_PORT } ); try { const auth = { username: 'foo', password: 'bar' }; const response = await axios.get(`http://localhost:${redirectServer.address().port}/start`, { auth, maxRedirects: 1, }); assert.strictEqual(response.data, 'no-auth'); } finally { await stopHTTPServer(redirectServer); await stopHTTPServer(targetServer); } }); it('should preserve basic auth across multi-hop same-origin redirects', async () => { const server = await startHTTPServer( (req, res) => { if (req.url === '/a') { res.setHeader('Location', '/b'); res.statusCode = 302; res.end(); return; } if (req.url === '/b') { res.setHeader('Location', '/c'); res.statusCode = 302; res.end(); return; } res.end(req.headers.authorization || ''); }, { port: SERVER_PORT } ); try { const auth = { username: 'foo', password: 'bar' }; const response = await axios.get(`http://localhost:${server.address().port}/a`, { auth, maxRedirects: 5, }); const base64 = Buffer.from('foo:bar', 'utf8').toString('base64'); assert.strictEqual(response.data, `Basic ${base64}`); assert.strictEqual(response.request.path, '/c'); } finally { await stopHTTPServer(server); } }); it('should provides a default User-Agent header', async () => { const server = await startHTTPServer( (req, res) => { res.end(req.headers['user-agent']); }, { port: SERVER_PORT } ); try { const response = await axios.get(`http://localhost:${server.address().port}/`); assert.ok( /^axios\/[\d.]+[-]?[a-z]*[.]?[\d]+$/.test(response.data), `User-Agent header does not match: ${response.data}` ); } finally { await stopHTTPServer(server); } }); it('should allow the User-Agent header to be overridden', async () => { const server = await startHTTPServer( (req, res) => { res.end(req.headers['user-agent']); }, { port: SERVER_PORT } ); try { const headers = { 'UsEr-AgEnT': 'foo bar' }; // wonky casing to ensure caseless comparison const response = await axios.get(`http://localhost:${server.address().port}/`, { headers }); assert.strictEqual(response.data, 'foo bar'); } finally { await stopHTTPServer(server); } }); it('should allow the Content-Length header to be overridden', async () => { const server = await startHTTPServer( (req, res) => { assert.strictEqual(req.headers['content-length'], '42'); res.end(); }, { port: SERVER_PORT } ); try { const headers = { 'CoNtEnT-lEnGtH': '42' }; // wonky casing to ensure caseless comparison await axios.post(`http://localhost:${server.address().port}/`, 'foo', { headers }); } finally { await stopHTTPServer(server); } }); it('should support max content length', async () => { const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'text/html; charset=UTF-8'); res.end(Array(5000).join('#')); }, { port: SERVER_PORT } ); try { await assert.rejects( axios.get(`http://localhost:${server.address().port}/`, { maxContentLength: 2000, maxRedirects: 0, }), /maxContentLength size of 2000 exceeded/ ); } finally { await stopHTTPServer(server); } }); it('should support max content length for redirected', async () => { const str = Array(100000).join('ж'); const server = await startHTTPServer( (req, res) => { const parsed = new URL(req.url, 'http://localhost'); if (parsed.pathname === '/two') { res.setHeader('Content-Type', 'text/html; charset=UTF-8'); res.end(str); return; } res.setHeader('Location', '/two'); res.statusCode = 302; res.end(); }, { port: SERVER_PORT } ); try { await assert.rejects( axios.get(`http://localhost:${server.address().port}/one`, { maxContentLength: 2000, }), (error) => { assert.strictEqual(error.message, 'maxContentLength size of 2000 exceeded'); return true; } ); } finally { await stopHTTPServer(server); } }); it('should support max body length', async () => { const data = Array(100000).join('ж'); const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'text/html; charset=UTF-8'); res.end(); }, { port: SERVER_PORT } ); try { await assert.rejects( axios.post( `http://localhost:${server.address().port}/`, { data, }, { maxBodyLength: 2000, } ), (error) => { assert.strictEqual(error.message, 'Request body larger than maxBodyLength limit'); return true; } ); } finally { await stopHTTPServer(server); } }); it('should enforce maxContentLength for streamed responses', async () => { const size = 2 * 1024 * 1024; const body = Buffer.alloc(size, 0x63); const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'application/octet-stream'); res.end(body); }, { port: SERVER_PORT } ); try { const response = await axios.get(`http://localhost:${server.address().port}/`, { responseType: 'stream', maxContentLength: 1024, }); let bytesRead = 0; const err = await new Promise((resolve) => { response.data.on('data', (chunk) => { bytesRead += chunk.length; }); response.data.on('error', resolve); response.data.on('end', () => resolve(null)); }); assert.ok(err, 'stream should emit an error'); assert.strictEqual(err.message, 'maxContentLength size of 1024 exceeded'); assert.ok(bytesRead <= 1024 * 64, `stream should not deliver full payload; got ${bytesRead}`); } finally { await stopHTTPServer(server); } }); it('should allow streamed responses under maxContentLength', async () => { const body = Buffer.alloc(512, 0x64); const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'application/octet-stream'); res.end(body); }, { port: SERVER_PORT } ); try { const response = await axios.get(`http://localhost:${server.address().port}/`, { responseType: 'stream', maxContentLength: 1024, }); const chunks = []; await new Promise((resolve, reject) => { response.data.on('data', (chunk) => chunks.push(chunk)); response.data.on('error', reject); response.data.on('end', resolve); }); assert.strictEqual(Buffer.concat(chunks).length, body.length); } finally { await stopHTTPServer(server); } }); it('should enforce maxBodyLength for streamed uploads with maxRedirects: 0', async () => { let bytesReceived = 0; const server = await startHTTPServer( (req, res) => { req.on('data', (chunk) => { bytesReceived += chunk.length; }); req.on('end', () => { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ received: bytesReceived })); }); }, { port: SERVER_PORT } ); try { const size = 2 * 1024 * 1024; const buf = Buffer.alloc(size, 0x61); const source = stream.Readable.from([buf]); await assert.rejects( axios.post(`http://localhost:${server.address().port}/`, source, { maxBodyLength: 1024, maxRedirects: 0, headers: { 'Content-Type': 'application/octet-stream' }, }), (error) => { assert.strictEqual(error.message, 'Request body larger than maxBodyLength limit'); return true; } ); assert.ok( bytesReceived <= 1024 * 4, `server should not receive full payload; got ${bytesReceived}` ); } finally { await stopHTTPServer(server); } }); it('should allow streamed uploads under maxBodyLength with maxRedirects: 0', async () => { let bytesReceived = 0; const server = await startHTTPServer( (req, res) => { req.on('data', (chunk) => { bytesReceived += chunk.length; }); req.on('end', () => { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ received: bytesReceived })); }); }, { port: SERVER_PORT } ); try { const payload = Buffer.alloc(512, 0x62); const source = stream.Readable.from([payload]); const response = await axios.post(`http://localhost:${server.address().port}/`, source, { maxBodyLength: 1024, maxRedirects: 0, headers: { 'Content-Type': 'application/octet-stream' }, }); assert.strictEqual(response.data.received, payload.length); } finally { await stopHTTPServer(server); } }); it('should properly support default max body length (follow-redirects as well)', async () => { // Taken from follow-redirects defaults. const followRedirectsMaxBodyDefaults = 10 * 1024 * 1024; const data = Array(2 * followRedirectsMaxBodyDefaults).join('ж'); const server = await startHTTPServer( (req, res) => { // Consume the req stream before responding to avoid ECONNRESET. req.on('data', () => {}); req.on('end', () => { res.end('OK'); }); }, { port: SERVER_PORT } ); try { const response = await axios.post(`http://localhost:${server.address().port}/`, { data, }); assert.strictEqual(response.data, 'OK', 'should handle response'); } finally { await stopHTTPServer(server); } }); it('should display error while parsing params', async () => { const server = await startHTTPServer(() => {}, { port: SERVER_PORT }); try { await assert.rejects( axios.get(`http://localhost:${server.address().port}/`, { params: { errorParam: new Date(undefined), }, }), (error) => { assert.ok(error instanceof AxiosError, 'error should be an AxiosError'); assert.strictEqual(error.code, AxiosError.ERR_BAD_REQUEST); assert.strictEqual(error.exists, true); assert.strictEqual(error.url, `http://localhost:${server.address().port}/`); return true; } ); } finally { await stopHTTPServer(server); } }); it('should support sockets', async () => { let socketName = path.join( os.tmpdir(), `axios-test-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}.sock` ); if (process.platform === 'win32') { socketName = '\\\\.\\pipe\\libuv-test'; } let server; try { server = await new Promise((resolve, reject) => { const socketServer = net .createServer((socket) => { socket.on('data', () => { socket.end('HTTP/1.1 200 OK\r\n\r\n'); }); }) .listen(socketName, () => resolve(socketServer)); socketServer.on('error', reject); }); } catch (error) { if (error && error.code === 'EPERM') { return; } throw error; } try { const response = await axios({ socketPath: socketName, url: 'http://localhost:4444/socket', }); assert.strictEqual(response.status, 200); assert.strictEqual(response.statusText, 'OK'); } finally { await new Promise((resolve, reject) => { server.close((error) => { if (error) { reject(error); return; } resolve(); }); }); } }); describe('streams', () => { it('should support streams', async () => { const server = await startHTTPServer( (req, res) => { req.pipe(res); }, { port: SERVER_PORT } ); try { const response = await axios.post( `http://localhost:${server.address().port}/`, fs.createReadStream(thisTestFilePath), { responseType: 'stream', } ); const responseText = await new Promise((resolve, reject) => { const chunks = []; response.data.on('data', (chunk) => { chunks.push(chunk); }); response.data.on('end', () => { resolve(Buffer.concat(chunks).toString('utf8')); }); response.data.on('error', reject); }); assert.strictEqual(responseText, fs.readFileSync(thisTestFilePath, 'utf8')); } finally { await stopHTTPServer(server); } }); it('should pass errors for a failed stream', async () => { const server = await startHTTPServer(() => {}, { port: SERVER_PORT }); const notExistPath = path.join(adaptersTestsDir, 'does_not_exist'); try { await assert.rejects( axios.post( `http://localhost:${server.address().port}/`, fs.createReadStream(notExistPath) ), (error) => { assert.strictEqual( error.message, `ENOENT: no such file or directory, open '${notExistPath}'` ); return true; } ); } finally { await stopHTTPServer(server); } }); it('should destroy the response stream with an error on request stream destroying', async () => { const server = await startHTTPServer(); const requestStream = generateReadable(); setTimeout(() => { requestStream.destroy(); }, 1000); const { data } = await axios.post( `http://localhost:${server.address().port}/`, requestStream, { responseType: 'stream', } ); let streamError; data.on('error', (error) => { streamError = error; }); try { await new Promise((resolve, reject) => { stream.pipeline(data, devNull(), (error) => { if (error) { reject(error); return; } resolve(); }); }); assert.fail('stream was not aborted'); } catch (error) { // Expected: the request stream is destroyed before completion. } finally { assert.strictEqual(streamError && streamError.code, 'ERR_CANCELED'); await stopHTTPServer(server); } }); }); it('should support buffers', async () => { const buf = Buffer.alloc(1024, 'x'); // Unsafe buffer < Buffer.poolSize (8192 bytes) const server = await startHTTPServer( (req, res) => { assert.strictEqual(req.headers['content-length'], buf.length.toString()); req.pipe(res); }, { port: SERVER_PORT } ); try { const response = await axios.post(`http://localhost:${server.address().port}/`, buf, { responseType: 'stream', }); const responseText = await new Promise((resolve, reject) => { const chunks = []; response.data.on('data', (chunk) => { chunks.push(chunk); }); response.data.on('end', () => { resolve(Buffer.concat(chunks).toString('utf8')); }); response.data.on('error', reject); }); assert.strictEqual(responseText, buf.toString()); } finally { await stopHTTPServer(server); } }); it('should support HTTP proxies', async () => { const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'text/html; charset=UTF-8'); res.end('12345'); }, { port: SERVER_PORT } ); let connectAttempts = 0; const proxy = await startHTTPServer( (request, response) => { const parsed = new URL(request.url); const opts = { host: parsed.hostname, port: parsed.port, path: `${parsed.pathname}${parsed.search}`, }; http.get(opts, (res) => { let body = ''; res.on('data', (data) => { body += data; }); res.on('end', () => { response.setHeader('Content-Type', 'text/html; charset=UTF-8'); response.end(body + '6789'); }); }); }, { port: PROXY_PORT } ); proxy.on('connect', (req, sock) => { connectAttempts += 1; sock.end(); }); try { const response = await axios.get(`http://localhost:${server.address().port}/`, { proxy: { host: 'localhost', port: proxy.address().port, }, }); assert.strictEqual(Number(response.data), 123456789, 'should pass through proxy'); assert.strictEqual( connectAttempts, 0, 'HTTP targets must use forward-proxy mode, not CONNECT' ); } finally { await stopHTTPServer(server); await stopHTTPServer(proxy); } }); it('should support HTTPS proxies', async () => { const tlsOptions = { key: fs.readFileSync(path.join(adaptersTestsDir, 'key.pem')), cert: fs.readFileSync(path.join(adaptersTestsDir, 'cert.pem')), }; const closeServer = (server) => new Promise((resolve, reject) => { server.close((error) => { if (error) { reject(error); return; } resolve(); }); }); const server = await new Promise((resolve, reject) => { const httpsServer = https.createServer(tlsOptions, (req, res) => { res.setHeader('Content-Type', 'text/html; charset=UTF-8'); res.end('12345'); }); httpsServer.listen(0, 'localhost', () => resolve(httpsServer)); httpsServer.on('error', reject); }); let plaintextRequests = 0; const connectTargets = []; const upstreamSockets = []; const proxy = await new Promise((resolve, reject) => { const httpsProxy = https.createServer(tlsOptions, () => { plaintextRequests += 1; }); httpsProxy.on('connect', (req, clientSocket, head) => { connectTargets.push(req.url); const [targetHost, targetPort] = req.url.split(':'); const upstream = net.connect(Number(targetPort), targetHost, () => { clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n'); if (head && head.length) upstream.write(head); upstream.pipe(clientSocket); clientSocket.pipe(upstream); }); upstreamSockets.push(upstream); upstream.on('error', () => clientSocket.destroy()); clientSocket.on('error', () => upstream.destroy()); }); httpsProxy.listen(0, '127.0.0.1', () => resolve(httpsProxy)); httpsProxy.on('error', reject); }); const originalReject = process.env.NODE_TLS_REJECT_UNAUTHORIZED; process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; const tunnelingAgent = new HttpsProxyAgent({ protocol: 'https:', host: '127.0.0.1', port: proxy.address().port, ALPNProtocols: ['http/1.1'], rejectUnauthorized: false, }); try { const response = await axios.get(`https://localhost:${server.address().port}/`, { httpsAgent: tunnelingAgent, }); // axios may auto-parse the body as JSON; compare as number to tolerate either form. assert.strictEqual(Number(response.data), 12345, 'origin body should be received unmodified'); assert.strictEqual(plaintextRequests, 0, 'proxy must not see plaintext requests'); assert.strictEqual(connectTargets.length, 1, 'proxy should see exactly one CONNECT'); assert.ok( connectTargets[0].startsWith(`localhost:${server.address().port}`), `CONNECT should target the origin: ${connectTargets[0]}` ); } finally { if (originalReject === undefined) { delete process.env.NODE_TLS_REJECT_UNAUTHORIZED; } else { process.env.NODE_TLS_REJECT_UNAUTHORIZED = originalReject; } tunnelingAgent.destroy(); // Tear down everything synchronously. server.close() on tls.Server can hang // when CONNECT-tunneled sockets have been pumped through, even after // closeAllConnections — destroy the underlying handles directly so the // test doesn't wait on a graceful shutdown. for (const s of upstreamSockets) s.destroy(); server.closeAllConnections?.(); proxy.closeAllConnections?.(); server.close(); proxy.close(); server.unref?.(); proxy.unref?.(); } }); it('should CONNECT-tunnel HTTPS targets through an HTTP proxy by default (issue #6320)', async () => { const tlsOptions = { key: fs.readFileSync(path.join(adaptersTestsDir, 'key.pem')), cert: fs.readFileSync(path.join(adaptersTestsDir, 'cert.pem')), }; const origin = await new Promise((resolve, reject) => { const s = https.createServer(tlsOptions, (req, res) => { if (req.headers['proxy-authorization']) { // Proxy-Authorization MUST NOT reach the origin under tunneling. res.writeHead(500); res.end('LEAKED:' + req.headers['proxy-authorization']); return; } res.setHeader('Content-Type', 'text/html; charset=UTF-8'); res.end('secret-body-12345'); }); s.listen(0, 'localhost', () => resolve(s)); s.on('error', reject); }); const captured = { plaintext: 0, connectTargets: [], connectAuth: [] }; const upstreamSockets = []; const proxy = await new Promise((resolve, reject) => { const p = http.createServer((req) => { // Plaintext arrival = tunneling regression. Capture URL/headers so // assertions below can show what leaked. captured.plaintext += 1; captured.plaintextUrl = req.url; }); p.on('connect', (req, clientSocket, head) => { captured.connectTargets.push(req.url); captured.connectAuth.push(req.headers['proxy-authorization'] || null); const [host, port] = req.url.split(':'); const upstream = net.connect(Number(port), host, () => { clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n'); if (head && head.length) upstream.write(head); upstream.pipe(clientSocket); clientSocket.pipe(upstream); }); upstreamSockets.push(upstream); upstream.on('error', () => clientSocket.destroy()); clientSocket.on('error', () => upstream.destroy()); }); p.listen(0, '127.0.0.1', () => resolve(p)); p.on('error', reject); }); const originalReject = process.env.NODE_TLS_REJECT_UNAUTHORIZED; process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; try { const response = await axios.post( `https://localhost:${origin.address().port}/path?token=abc123`, { sensitive: 'leak-canary' }, { proxy: { host: '127.0.0.1', port: proxy.address().port, protocol: 'http', auth: { username: 'admin', password: 'secret' }, }, validateStatus: () => true, } ); assert.strictEqual( response.data, 'secret-body-12345', 'origin body should arrive unmodified through the tunnel' ); assert.strictEqual(captured.plaintext, 0, 'proxy must not see any plaintext request line'); assert.strictEqual(captured.connectTargets.length, 1, 'proxy should see exactly one CONNECT'); assert.ok( captured.connectTargets[0].startsWith(`localhost:${origin.address().port}`), `CONNECT should target the origin host:port, got ${captured.connectTargets[0]}` ); assert.ok( captured.connectAuth[0], 'Proxy-Authorization should be present on the CONNECT request' ); assert.match(captured.connectAuth[0], /^Basic /, 'CONNECT auth should be Basic-encoded'); const decoded = Buffer.from(captured.connectAuth[0].slice(6), 'base64').toString('utf8'); assert.strictEqual(decoded, 'admin:secret', 'Proxy-Authorization credentials should match'); } finally { if (originalReject === undefined) { delete process.env.NODE_TLS_REJECT_UNAUTHORIZED; } else { process.env.NODE_TLS_REJECT_UNAUTHORIZED = originalReject; } for (const s of upstreamSockets) s.destroy(); origin.closeAllConnections?.(); proxy.closeAllConnections?.(); origin.close(); proxy.close(); origin.unref?.(); proxy.unref?.(); } }); it('should apply httpsAgent TLS options to CONNECT-tunneled origins (issue #10953)', async () => { const tlsOptions = { key: fs.readFileSync(path.join(adaptersTestsDir, 'key.pem')), cert: fs.readFileSync(path.join(adaptersTestsDir, 'cert.pem')), }; const origin = await new Promise((resolve, reject) => { const s = https.createServer(tlsOptions, (req, res) => { res.setHeader('Content-Type', 'text/html; charset=UTF-8'); res.end('trusted-through-agent'); }); s.listen(0, 'localhost', () => resolve(s)); s.on('error', reject); }); const captured = { plaintext: 0, connectTargets: [] }; const upstreamSockets = []; const proxy = await new Promise((resolve, reject) => { const p = http.createServer(() => { captured.plaintext += 1; }); p.on('connect', (req, clientSocket, head) => { captured.connectTargets.push(req.url); const [host, port] = req.url.split(':'); const upstream = net.connect(Number(port), host, () => { clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n'); if (head && head.length) upstream.write(head); upstream.pipe(clientSocket); clientSocket.pipe(upstream); }); upstreamSockets.push(upstream); upstream.on('error', () => clientSocket.destroy()); clientSocket.on('error', () => upstream.destroy()); }); p.listen(0, '127.0.0.1', () => resolve(p)); p.on('error', reject); }); const httpsAgent = new https.Agent({ ca: tlsOptions.cert }); try { const response = await axios.get(`https://localhost:${origin.address().port}/`, { httpsAgent, proxy: { host: '127.0.0.1', port: proxy.address().port, protocol: 'http', }, }); assert.strictEqual(response.data, 'trusted-through-agent'); assert.strictEqual(captured.plaintext, 0, 'proxy must not see plaintext HTTPS requests'); assert.strictEqual(captured.connectTargets.length, 1, 'proxy should see exactly one CONNECT'); assert.ok( captured.connectTargets[0].startsWith(`localhost:${origin.address().port}`), `CONNECT should target the origin host:port, got ${captured.connectTargets[0]}` ); } finally { httpsAgent.destroy(); for (const s of upstreamSockets) s.destroy(); origin.closeAllConnections?.(); proxy.closeAllConnections?.(); origin.close(); proxy.close(); origin.unref?.(); proxy.unref?.(); } }); describe('CONNECT origin TLS options', () => { const tlsOptions = { key: fs.readFileSync(path.join(adaptersTestsDir, 'key.pem')), cert: fs.readFileSync(path.join(adaptersTestsDir, 'cert.pem')), }; const untrustedCertificate = { code: 'DEPTH_ZERO_SELF_SIGNED_CERT' }; async function withTunnel(test, secureProxy = false) { const originalAgent = https.globalAgent; const agents = []; const servers = []; const sockets = new Set(); const captured = { plaintext: 0, connectTargets: [], originRequests: 0 }; const createAgent = (options = {}) => { const agent = new https.Agent({ rejectUnauthorized: true, ...options }); agents.push(agent); return agent; }; const trackSocket = (socket) => { sockets.add(socket); socket.on('close', () => sockets.delete(socket)); }; const startHTTPS = async (handler) => { const server = https.createServer(tlsOptions, handler); servers.push(server); server.on('connection', trackSocket); await new Promise((resolve, reject) => { server.once('error', reject); server.listen(0, 'localhost', resolve); }); return server; }; try { const origin = await startHTTPS((_req, res) => { captured.originRequests += 1; res.end('trusted-through-tunnel'); }); const onPlaintext = (_req, res) => { captured.plaintext += 1; res.writeHead(500).end('expected CONNECT'); }; const proxy = secureProxy ? await startHTTPS(onPlaintext) : await startHTTPServer(onPlaintext); if (!secureProxy) { servers.push(proxy); proxy.on('connection', trackSocket); } proxy.on('connect', (req, clientSocket, head) => { captured.connectTargets.push(req.url); const upstream = net.connect(origin.address().port, 'localhost', () => { clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n'); if (head.length) upstream.write(head); upstream.pipe(clientSocket); clientSocket.pipe(upstream); }); trackSocket(upstream); trackSocket(clientSocket); upstream.on('error', () => clientSocket.destroy()); clientSocket.on('error', () => upstream.destroy()); upstream.on('close', () => clientSocket.destroy()); clientSocket.on('close', () => upstream.destroy()); }); const globalAgent = (https.globalAgent = createAgent()); const target = `localhost:${origin.address().port}`; const request = (config = {}) => axios.get(`https://${target}/`, { proxy: { protocol: secureProxy ? 'https' : 'http', host: 'localhost', port: proxy.address().port, }, timeout: 2000, ...config, }); await test({ request, globalAgent, createAgent, captured, cert: tlsOptions.cert }); assert.strictEqual( captured.plaintext, 0, 'proxy must not receive a plaintext origin request' ); for (const connectTarget of captured.connectTargets) { assert.strictEqual(connectTarget, target, 'CONNECT must target the origin'); } } finally { https.globalAgent = originalAgent; for (const agent of agents) agent.destroy(); for (const socket of sockets) socket.destroy(); await Promise.all(servers.map((server) => stopHTTPServer(server))); } } it('applies https.globalAgent TLS options to the tunneled origin', async () => { await withTunnel(async ({ request, globalAgent, captured, cert }) => { globalAgent.options.ca = cert; assert.strictEqual((await request()).data, 'trusted-through-tunnel'); assert.strictEqual(captured.connectTargets.length, 1); }); }); it('observes global CA installation and removal after tunnel caching', async () => { await withTunnel(async ({ request, globalAgent, captured, cert }) => { await assert.rejects(request(), untrustedCertificate); globalAgent.options.ca = cert; assert.strictEqual((await request()).status, 200); delete globalAgent.options.ca; await assert.rejects(request(), untrustedCertificate); assert.strictEqual(captured.connectTargets.length, 3); assert.strictEqual(captured.originRequests, 1); }); }); it('observes a replacement global agent after tunnel caching', async () => { await withTunnel(async ({ request, createAgent, captured, cert }) => { await assert.rejects(request(), untrustedCertificate); https.globalAgent = createAgent({ ca: cert }); assert.strictEqual((await request()).status, 200); assert.strictEqual(captured.connectTargets.length, 2); }); }); it('preserves explicit CA arrays independently of global trust', async () => { await withTunnel(async ({ request, createAgent, cert }) => { assert.strictEqual( (await request({ httpsAgent: createAgent({ ca: [cert] }) })).status, 200 ); }); }); it('does not merge global trust into an explicit agent', async () => { await withTunnel(async ({ request, globalAgent, createAgent, captured, cert }) => { globalAgent.options.ca = cert; const httpsAgent = createAgent(); await assert.rejects(request({ httpsAgent }), untrustedCertificate); globalAgent.options.rejectUnauthorized = false; await assert.rejects(request({ httpsAgent }), untrustedCertificate); assert.strictEqual(captured.originRequests, 0); }); }); it('observes explicit-agent CA changes after tunnel caching', async () => { await withTunnel(async ({ request, createAgent, captured, cert }) => { const httpsAgent = createAgent(); await assert.rejects(request({ httpsAgent }), untrustedCertificate); httpsAgent.options.ca = cert; assert.strictEqual((await request({ httpsAgent })).status, 200); delete httpsAgent.options.ca; await assert.rejects(request({ httpsAgent }), untrustedCertificate); assert.strictEqual(captured.originRequests, 1); }); }); it('preserves the global certificate identity check', async () => { await withTunnel(async ({ request, globalAgent, captured, cert }) => { globalAgent.options.ca = cert; globalAgent.options.checkServerIdentity = () => Object.assign(new Error('test identity rejection'), { code: 'TEST_IDENTITY_REJECT' }); await assert.rejects(request(), { code: 'TEST_IDENTITY_REJECT' }); assert.strictEqual(captured.originRequests, 0); }); }); for (const option of ['ca', 'rejectUnauthorized']) { it(`does not apply global ${option} to the HTTPS proxy connection`, async () => { await withTunnel(async ({ request, globalAgent, captured, cert }) => { globalAgent.options[option] = option === 'ca' ? cert : false; await assert.rejects(request(), untrustedCertificate); assert.strictEqual(captured.connectTargets.length, 0); assert.strictEqual(captured.originRequests, 0); }, true); }); } it('preserves explicit TLS options for an HTTPS proxy and its origin', async () => { await withTunnel(async ({ request, createAgent, captured, cert }) => { assert.strictEqual((await request({ httpsAgent: createAgent({ ca: cert }) })).status, 200); assert.strictEqual(captured.connectTargets.length, 1); }, true); }); }); it('should surface a CONNECT 407 from the proxy as an AxiosError (issue #6320)', async () => { const proxy = await new Promise((resolve, reject) => { const p = http.createServer(); p.on('connect', (req, clientSocket) => { clientSocket.write( 'HTTP/1.1 407 Proxy Authentication Required\r\n' + 'Proxy-Authenticate: Basic realm="proxy"\r\n' + 'Content-Length: 0\r\n' + '\r\n' ); clientSocket.end(); }); p.listen(0, '127.0.0.1', () => resolve(p)); p.on('error', reject); }); try { await assert.rejects( async () => { await axios.get('https://127.0.0.1:1/', { proxy: { host: '127.0.0.1', port: proxy.address().port, protocol: 'http', }, timeout: 4000, }); }, (err) => { assert.ok(err instanceof AxiosError, 'rejection should be an AxiosError'); return true; } ); } finally { proxy.closeAllConnections?.(); proxy.close(); proxy.unref?.(); } }); it('should not pass through disabled proxy', async () => { const originalHttpProxy = process.env.http_proxy; process.env.http_proxy = 'http://does-not-exists.example.com:4242/'; const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'text/html; charset=UTF-8'); res.end('123456789'); }, { port: SERVER_PORT } ); try { const response = await axios.get(`http://localhost:${server.address().port}/`, { proxy: false, }); assert.strictEqual(Number(response.data), 123456789, 'should not pass through proxy'); } finally { await stopHTTPServer(server); if (originalHttpProxy === undefined) { delete process.env.http_proxy; } else { process.env.http_proxy = originalHttpProxy; } } }); it('should support proxy set via env var', async () => { const originalHttpProxy = process.env.http_proxy; const originalHTTPProxy = process.env.HTTP_PROXY; const originalNoProxy = process.env.no_proxy; const originalNOProxy = process.env.NO_PROXY; const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'text/html; charset=UTF-8'); res.end('4567'); }, { port: SERVER_PORT } ); const proxy = await startHTTPServer( (request, response) => { const parsed = new URL(request.url); const opts = { host: parsed.hostname, port: parsed.port, path: `${parsed.pathname}${parsed.search}`, }; http.get(opts, (res) => { let body = ''; res.on('data', (data) => { body += data; }); res.on('end', () => { response.setHeader('Content-Type', 'text/html; charset=UTF-8'); response.end(body + '1234'); }); }); }, { port: PROXY_PORT } ); const proxyUrl = `http://localhost:${proxy.address().port}/`; process.env.http_proxy = proxyUrl; process.env.HTTP_PROXY = proxyUrl; process.env.no_proxy = ''; process.env.NO_PROXY = ''; try { const response = await axios.get(`http://localhost:${server.address().port}/`); assert.strictEqual( String(response.data), '45671234', 'should use proxy set by process.env.http_proxy' ); } finally { await stopHTTPServer(server); await stopHTTPServer(proxy); if (originalHttpProxy === undefined) { delete process.env.http_proxy; } else { process.env.http_proxy = originalHttpProxy; } if (originalHTTPProxy === undefined) { delete process.env.HTTP_PROXY; } else { process.env.HTTP_PROXY = originalHTTPProxy; } if (originalNoProxy === undefined) { delete process.env.no_proxy; } else { process.env.no_proxy = originalNoProxy; } if (originalNOProxy === undefined) { delete process.env.NO_PROXY; } else { process.env.NO_PROXY = originalNOProxy; } } }); it('should detect Node native env proxy support from the selected agent', () => { const nativeProxyAgent = { options: { proxyEnv: { HTTP_PROXY: 'http://proxy.local:9000' } } }; const plainAgent = { options: {} }; assert.strictEqual(__isNodeEnvProxyEnabled(nativeProxyAgent, '22.20.0'), false); assert.strictEqual(__isNodeEnvProxyEnabled(nativeProxyAgent, '22.21.0'), true); assert.strictEqual(__isNodeEnvProxyEnabled(nativeProxyAgent, '24.4.0'), false); assert.strictEqual(__isNodeEnvProxyEnabled(nativeProxyAgent, '24.5.0'), true); assert.strictEqual(__isNodeEnvProxyEnabled(nativeProxyAgent, '25.0.0'), true); assert.strictEqual(__isNodeEnvProxyEnabled(plainAgent, '24.5.0'), false); assert.strictEqual(__isNodeEnvProxyEnabled(undefined, '24.5.0'), false); }); it('should report proxy false as direct when the selected agent uses proxyEnv', () => { const options = { headers: {}, beforeRedirects: {}, hostname: 'target.example', host: 'target.example', port: '4000', protocol: 'http:', path: '/resource', }; const nativeProxyAgent = { options: { proxyEnv: { HTTP_PROXY: 'http://proxy.local:9000' } } }; const proxyApplied = __setProxy( options, false, 'http://target.example:4000/resource', false, undefined, nativeProxyAgent ); assert.strictEqual(proxyApplied, false); assert.strictEqual(options.hostname, 'target.example'); assert.strictEqual(options.port, '4000'); assert.strictEqual(options.path, '/resource'); }); it('should leave env proxy handling to supported Node versions when the selected agent uses proxyEnv', () => { const originalHttpProxy = process.env.http_proxy; const originalHTTPProxy = process.env.HTTP_PROXY; const originalNoProxy = process.env.no_proxy; const originalNOProxy = process.env.NO_PROXY; const originalNodeUseEnvProxy = process.env.NODE_USE_ENV_PROXY; process.env.NODE_USE_ENV_PROXY = '1'; process.env.http_proxy = 'http://proxy.local:9000/'; process.env.HTTP_PROXY = 'http://proxy.local:9000/'; process.env.no_proxy = ''; process.env.NO_PROXY = ''; try { const options = { headers: {}, beforeRedirects: {}, hostname: 'target.example', host: 'target.example', port: '4000', protocol: 'http:', path: '/resource', }; const nativeProxyAgent = { options: { proxyEnv: process.env } }; __setProxy( options, undefined, 'http://target.example:4000/resource', false, undefined, nativeProxyAgent ); if (__isNodeEnvProxyEnabled(nativeProxyAgent, process.versions.node)) { assert.strictEqual(options.hostname, 'target.example'); assert.strictEqual(options.port, '4000'); assert.strictEqual(options.path, '/resource'); assert.strictEqual(options.headers.host, undefined); } else { assert.strictEqual(options.hostname, 'proxy.local'); assert.strictEqual(options.port, '9000'); assert.strictEqual(options.path, 'http://target.example:4000/resource'); } assert.strictEqual(typeof options.beforeRedirects.proxy, 'function'); } finally { if (originalHttpProxy === undefined) { delete process.env.http_proxy; } else { process.env.http_proxy = originalHttpProxy; } if (originalHTTPProxy === undefined) { delete process.env.HTTP_PROXY; } else { process.env.HTTP_PROXY = originalHTTPProxy; } if (originalNoProxy === undefined) { delete process.env.no_proxy; } else { process.env.no_proxy = originalNoProxy; } if (originalNOProxy === undefined) { delete process.env.NO_PROXY; } else { process.env.NO_PROXY = originalNOProxy; } if (originalNodeUseEnvProxy === undefined) { delete process.env.NODE_USE_ENV_PROXY; } else { process.env.NODE_USE_ENV_PROXY = originalNodeUseEnvProxy; } } }); it('should keep axios env proxy handling when the selected agent has no proxyEnv', () => { const originalHttpProxy = process.env.http_proxy; const originalHTTPProxy = process.env.HTTP_PROXY; const originalNoProxy = process.env.no_proxy; const originalNOProxy = process.env.NO_PROXY; const originalNodeUseEnvProxy = process.env.NODE_USE_ENV_PROXY; process.env.NODE_USE_ENV_PROXY = '1'; process.env.http_proxy = 'http://proxy.local:9000/'; process.env.HTTP_PROXY = 'http://proxy.local:9000/'; process.env.no_proxy = ''; process.env.NO_PROXY = ''; try { const options = { headers: {}, beforeRedirects: {}, hostname: 'target.example', host: 'target.example', port: '4000', protocol: 'http:', path: '/resource', }; const plainAgent = { options: {} }; __setProxy( options, undefined, 'http://target.example:4000/resource', false, undefined, plainAgent ); assert.strictEqual(options.hostname, 'proxy.local'); assert.strictEqual(options.port, '9000'); assert.strictEqual(options.path, 'http://target.example:4000/resource'); assert.strictEqual(typeof options.beforeRedirects.proxy, 'function'); } finally { if (originalHttpProxy === undefined) { delete process.env.http_proxy; } else { process.env.http_proxy = originalHttpProxy; } if (originalHTTPProxy === undefined) { delete process.env.HTTP_PROXY; } else { process.env.HTTP_PROXY = originalHTTPProxy; } if (originalNoProxy === undefined) { delete process.env.no_proxy; } else { process.env.no_proxy = originalNoProxy; } if (originalNOProxy === undefined) { delete process.env.NO_PROXY; } else { process.env.NO_PROXY = originalNOProxy; } if (originalNodeUseEnvProxy === undefined) { delete process.env.NODE_USE_ENV_PROXY; } else { process.env.NODE_USE_ENV_PROXY = originalNodeUseEnvProxy; } } }); it('should support HTTPS proxy set via env var', async () => { const originalHttpsProxy = process.env.https_proxy; const originalHTTPSProxy = process.env.HTTPS_PROXY; const originalNoProxy = process.env.no_proxy; const originalNOProxy = process.env.NO_PROXY; const tlsOptions = { key: fs.readFileSync(path.join(adaptersTestsDir, 'key.pem')), cert: fs.readFileSync(path.join(adaptersTestsDir, 'cert.pem')), }; const closeServer = (server) => new Promise((resolve, reject) => { server.close((error) => { if (error) { reject(error); return; } resolve(); }); }); const server = await new Promise((resolve, reject) => { const httpsServer = https.createServer(tlsOptions, (req, res) => { res.setHeader('Content-Type', 'text/html; charset=UTF-8'); res.end('12345'); }); httpsServer.listen(0, 'localhost', () => resolve(httpsServer)); httpsServer.on('error', reject); }); let plaintextRequests = 0; const connectTargets = []; const upstreamSockets = []; const proxy = await new Promise((resolve, reject) => { const httpsProxy = https.createServer(tlsOptions, () => { plaintextRequests += 1; }); httpsProxy.on('connect', (req, clientSocket, head) => { connectTargets.push(req.url); const [targetHost, targetPort] = req.url.split(':'); const upstream = net.connect(Number(targetPort), targetHost, () => { clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n'); if (head && head.length) upstream.write(head); upstream.pipe(clientSocket); clientSocket.pipe(upstream); }); upstreamSockets.push(upstream); upstream.on('error', () => clientSocket.destroy()); clientSocket.on('error', () => upstream.destroy()); }); httpsProxy.listen(0, '127.0.0.1', () => resolve(httpsProxy)); httpsProxy.on('error', reject); }); const proxyUrl = `https://127.0.0.1:${proxy.address().port}/`; process.env.https_proxy = proxyUrl; process.env.HTTPS_PROXY = proxyUrl; process.env.no_proxy = ''; process.env.NO_PROXY = ''; const originalReject = process.env.NODE_TLS_REJECT_UNAUTHORIZED; process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; try { const response = await axios.get(`https://localhost:${server.address().port}/`); assert.strictEqual(Number(response.data), 12345, 'origin body should be received unmodified'); assert.strictEqual(plaintextRequests, 0, 'proxy must not see plaintext requests'); assert.strictEqual(connectTargets.length, 1, 'proxy should see exactly one CONNECT'); } finally { if (originalReject === undefined) { delete process.env.NODE_TLS_REJECT_UNAUTHORIZED; } else { process.env.NODE_TLS_REJECT_UNAUTHORIZED = originalReject; } for (const s of upstreamSockets) s.destroy(); server.closeAllConnections?.(); proxy.closeAllConnections?.(); server.close(); proxy.close(); server.unref?.(); proxy.unref?.(); if (originalHttpsProxy === undefined) { delete process.env.https_proxy; } else { process.env.https_proxy = originalHttpsProxy; } if (originalHTTPSProxy === undefined) { delete process.env.HTTPS_PROXY; } else { process.env.HTTPS_PROXY = originalHTTPSProxy; } if (originalNoProxy === undefined) { delete process.env.no_proxy; } else { process.env.no_proxy = originalNoProxy; } if (originalNOProxy === undefined) { delete process.env.NO_PROXY; } else { process.env.NO_PROXY = originalNOProxy; } } }); it('should re-evaluate proxy on redirect when proxy set via env var', async () => { const originalHttpProxy = process.env.http_proxy; const originalHTTPProxy = process.env.HTTP_PROXY; const originalNoProxy = process.env.no_proxy; const originalNOProxy = process.env.NO_PROXY; let proxyUseCount = 0; const server = await startHTTPServer( (req, res) => { res.setHeader('Location', `http://localhost:${proxy.address().port}/redirected`); res.statusCode = 302; res.end(); }, { port: SERVER_PORT } ); const proxy = await startHTTPServer( (request, response) => { const parsed = new URL(request.url, 'http://localhost'); if (parsed.pathname === '/redirected') { response.statusCode = 200; response.end(); return; } proxyUseCount += 1; const opts = { host: parsed.hostname, port: parsed.port, path: `${parsed.pathname}${parsed.search}`, protocol: parsed.protocol, }; http.get(opts, (res) => { let body = ''; res.on('data', (data) => { body += data; }); res.on('end', () => { response.setHeader('Content-Type', 'text/html; charset=UTF-8'); response.setHeader('Location', res.headers.location); response.end(body); }); }); }, { port: PROXY_PORT } ); const proxyUrl = `http://localhost:${proxy.address().port}`; process.env.http_proxy = proxyUrl; process.env.HTTP_PROXY = proxyUrl; process.env.no_proxy = `localhost:${proxy.address().port}`; process.env.NO_PROXY = `localhost:${proxy.address().port}`; try { const response = await axios.get(`http://localhost:${server.address().port}/`); assert.equal(response.status, 200); assert.equal(proxyUseCount, 1); } finally { await stopHTTPServer(server); await stopHTTPServer(proxy); if (originalHttpProxy === undefined) { delete process.env.http_proxy; } else { process.env.http_proxy = originalHttpProxy; } if (originalHTTPProxy === undefined) { delete process.env.HTTP_PROXY; } else { process.env.HTTP_PROXY = originalHTTPProxy; } if (originalNoProxy === undefined) { delete process.env.no_proxy; } else { process.env.no_proxy = originalNoProxy; } if (originalNOProxy === undefined) { delete process.env.NO_PROXY; } else { process.env.NO_PROXY = originalNOProxy; } } }); it('should not use proxy for domains in no_proxy', async () => { const originalHttpProxy = process.env.http_proxy; const originalHTTPProxy = process.env.HTTP_PROXY; const originalNoProxy = process.env.no_proxy; const originalNOProxy = process.env.NO_PROXY; const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'text/html; charset=UTF-8'); res.end('4567'); }, { port: SERVER_PORT } ); const proxy = await startHTTPServer( (request, response) => { const parsed = new URL(request.url); const opts = { host: parsed.hostname, port: parsed.port, path: `${parsed.pathname}${parsed.search}`, }; http.get(opts, (res) => { let body = ''; res.on('data', (data) => { body += data; }); res.on('end', () => { response.setHeader('Content-Type', 'text/html; charset=UTF-8'); response.end(body + '1234'); }); }); }, { port: PROXY_PORT } ); const noProxyValue = 'foo.com, localhost,bar.net , , quix.co'; const proxyUrl = `http://localhost:${proxy.address().port}/`; process.env.http_proxy = proxyUrl; process.env.HTTP_PROXY = proxyUrl; process.env.no_proxy = noProxyValue; process.env.NO_PROXY = noProxyValue; try { const response = await axios.get(`http://localhost:${server.address().port}/`); assert.equal(response.data, '4567', 'should not use proxy for domains in no_proxy'); } finally { await stopHTTPServer(server); await stopHTTPServer(proxy); if (originalHttpProxy === undefined) { delete process.env.http_proxy; } else { process.env.http_proxy = originalHttpProxy; } if (originalHTTPProxy === undefined) { delete process.env.HTTP_PROXY; } else { process.env.HTTP_PROXY = originalHTTPProxy; } if (originalNoProxy === undefined) { delete process.env.no_proxy; } else { process.env.no_proxy = originalNoProxy; } if (originalNOProxy === undefined) { delete process.env.NO_PROXY; } else { process.env.NO_PROXY = originalNOProxy; } } }); it('should not use proxy for localhost with trailing dot when listed in no_proxy', async () => { const originalHttpProxy = process.env.http_proxy; const originalHTTPProxy = process.env.HTTP_PROXY; const originalNoProxy = process.env.no_proxy; const originalNOProxy = process.env.NO_PROXY; let proxyRequests = 0; const proxy = await startHTTPServer( (_, response) => { proxyRequests += 1; response.end('proxied'); }, { port: PROXY_PORT } ); const noProxyValue = 'localhost,127.0.0.1,::1'; const proxyUrl = `http://localhost:${proxy.address().port}/`; process.env.http_proxy = proxyUrl; process.env.HTTP_PROXY = proxyUrl; process.env.no_proxy = noProxyValue; process.env.NO_PROXY = noProxyValue; try { await assert.rejects(axios.get('http://localhost.:1/', { timeout: 100 })); assert.equal(proxyRequests, 0, 'should not use proxy for localhost with trailing dot'); } finally { await stopHTTPServer(proxy); if (originalHttpProxy === undefined) { delete process.env.http_proxy; } else { process.env.http_proxy = originalHttpProxy; } if (originalHTTPProxy === undefined) { delete process.env.HTTP_PROXY; } else { process.env.HTTP_PROXY = originalHTTPProxy; } if (originalNoProxy === undefined) { delete process.env.no_proxy; } else { process.env.no_proxy = originalNoProxy; } if (originalNOProxy === undefined) { delete process.env.NO_PROXY; } else { process.env.NO_PROXY = originalNOProxy; } } }); it('should not use proxy for bracketed IPv6 loopback when listed in no_proxy', async () => { const originalHttpProxy = process.env.http_proxy; const originalHTTPProxy = process.env.HTTP_PROXY; const originalNoProxy = process.env.no_proxy; const originalNOProxy = process.env.NO_PROXY; let proxyRequests = 0; const proxy = await startHTTPServer( (_, response) => { proxyRequests += 1; response.end('proxied'); }, { port: PROXY_PORT } ); const noProxyValue = 'localhost,127.0.0.1,::1'; const proxyUrl = `http://localhost:${proxy.address().port}/`; process.env.http_proxy = proxyUrl; process.env.HTTP_PROXY = proxyUrl; process.env.no_proxy = noProxyValue; process.env.NO_PROXY = noProxyValue; try { await assert.rejects(axios.get('http://[::1]:1/', { timeout: 100 })); assert.equal(proxyRequests, 0, 'should not use proxy for IPv6 loopback'); } finally { await stopHTTPServer(proxy); if (originalHttpProxy === undefined) { delete process.env.http_proxy; } else { process.env.http_proxy = originalHttpProxy; } if (originalHTTPProxy === undefined) { delete process.env.HTTP_PROXY; } else { process.env.HTTP_PROXY = originalHTTPProxy; } if (originalNoProxy === undefined) { delete process.env.no_proxy; } else { process.env.no_proxy = originalNoProxy; } if (originalNOProxy === undefined) { delete process.env.NO_PROXY; } else { process.env.NO_PROXY = originalNOProxy; } } }); it('should use proxy for domains not in no_proxy', async () => { const originalHttpProxy = process.env.http_proxy; const originalHTTPProxy = process.env.HTTP_PROXY; const originalNoProxy = process.env.no_proxy; const originalNOProxy = process.env.NO_PROXY; const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'text/html; charset=UTF-8'); res.end('4567'); }, { port: SERVER_PORT } ); const proxy = await startHTTPServer( (request, response) => { const parsed = new URL(request.url); const opts = { host: parsed.hostname, port: parsed.port, path: `${parsed.pathname}${parsed.search}`, }; http.get(opts, (res) => { let body = ''; res.on('data', (data) => { body += data; }); res.on('end', () => { response.setHeader('Content-Type', 'text/html; charset=UTF-8'); response.end(body + '1234'); }); }); }, { port: PROXY_PORT } ); const noProxyValue = 'foo.com, ,bar.net , quix.co'; const proxyUrl = `http://localhost:${proxy.address().port}/`; process.env.http_proxy = proxyUrl; process.env.HTTP_PROXY = proxyUrl; process.env.no_proxy = noProxyValue; process.env.NO_PROXY = noProxyValue; try { const response = await axios.get(`http://localhost:${server.address().port}/`); assert.equal(response.data, '45671234', 'should use proxy for domains not in no_proxy'); } finally { await stopHTTPServer(server); await stopHTTPServer(proxy); if (originalHttpProxy === undefined) { delete process.env.http_proxy; } else { process.env.http_proxy = originalHttpProxy; } if (originalHTTPProxy === undefined) { delete process.env.HTTP_PROXY; } else { process.env.HTTP_PROXY = originalHTTPProxy; } if (originalNoProxy === undefined) { delete process.env.no_proxy; } else { process.env.no_proxy = originalNoProxy; } if (originalNOProxy === undefined) { delete process.env.NO_PROXY; } else { process.env.NO_PROXY = originalNOProxy; } } }); it('should support HTTP proxy auth', async () => { const server = await startHTTPServer( (req, res) => { res.end(); }, { port: SERVER_PORT } ); const proxy = await startHTTPServer( (request, response) => { const parsed = new URL(request.url); const opts = { host: parsed.hostname, port: parsed.port, path: `${parsed.pathname}${parsed.search}`, }; const proxyAuth = request.headers['proxy-authorization']; http.get(opts, (res) => { res.on('data', () => {}); res.on('end', () => { response.setHeader('Content-Type', 'text/html; charset=UTF-8'); response.end(proxyAuth); }); }); }, { port: PROXY_PORT } ); try { const response = await axios.get(`http://localhost:${server.address().port}/`, { proxy: { host: 'localhost', port: proxy.address().port, auth: { username: 'user', password: 'pass', }, }, }); const base64 = Buffer.from('user:pass', 'utf8').toString('base64'); assert.equal(response.data, `Basic ${base64}`, 'should authenticate to the proxy'); } finally { await stopHTTPServer(server); await stopHTTPServer(proxy); } }); it('should support proxy auth from env', async () => { const originalHttpProxy = process.env.http_proxy; const originalHTTPProxy = process.env.HTTP_PROXY; const originalNoProxy = process.env.no_proxy; const originalNOProxy = process.env.NO_PROXY; const server = await startHTTPServer( (req, res) => { res.end(); }, { port: SERVER_PORT } ); const proxy = await startHTTPServer( (request, response) => { const parsed = new URL(request.url); const opts = { host: parsed.hostname, port: parsed.port, path: `${parsed.pathname}${parsed.search}`, }; const proxyAuth = request.headers['proxy-authorization']; http.get(opts, (res) => { res.on('data', () => {}); res.on('end', () => { response.setHeader('Content-Type', 'text/html; charset=UTF-8'); response.end(proxyAuth); }); }); }, { port: PROXY_PORT } ); const proxyUrl = `http://user:pass@localhost:${proxy.address().port}/`; process.env.http_proxy = proxyUrl; process.env.HTTP_PROXY = proxyUrl; process.env.no_proxy = ''; process.env.NO_PROXY = ''; try { const response = await axios.get(`http://localhost:${server.address().port}/`); const base64 = Buffer.from('user:pass', 'utf8').toString('base64'); assert.equal( response.data, `Basic ${base64}`, 'should authenticate to the proxy set by process.env.http_proxy' ); } finally { await stopHTTPServer(server); await stopHTTPServer(proxy); if (originalHttpProxy === undefined) { delete process.env.http_proxy; } else { process.env.http_proxy = originalHttpProxy; } if (originalHTTPProxy === undefined) { delete process.env.HTTP_PROXY; } else { process.env.HTTP_PROXY = originalHTTPProxy; } if (originalNoProxy === undefined) { delete process.env.no_proxy; } else { process.env.no_proxy = originalNoProxy; } if (originalNOProxy === undefined) { delete process.env.NO_PROXY; } else { process.env.NO_PROXY = originalNOProxy; } } }); describe('when invalid proxy options are provided', () => { it('should throw error', async () => { const proxy = { protocol: 'http:', host: 'hostname.abc.xyz', port: PROXY_PORT, auth: { username: '', password: '', }, }; await assert.rejects(axios.get('https://test-domain.abc', { proxy }), (error) => { assert.strictEqual(error.message, 'Invalid proxy authorization'); assert.strictEqual(error.code, 'ERR_BAD_OPTION'); assert.deepStrictEqual(error.config.proxy, proxy); return true; }); }); }); describe('different options for direct proxy configuration (without env variables)', () => { const destination = 'www.example.com'; const testCases = [ { description: 'hostname and trailing colon in protocol', proxyConfig: { hostname: '127.0.0.1', protocol: 'http:', port: OPEN_WEB_PORT }, expectedOptions: { host: '127.0.0.1', protocol: 'http:', port: OPEN_WEB_PORT, path: destination, }, }, { description: 'hostname and no trailing colon in protocol', proxyConfig: { hostname: '127.0.0.1', protocol: 'http', port: OPEN_WEB_PORT }, expectedOptions: { host: '127.0.0.1', protocol: 'http:', port: OPEN_WEB_PORT, path: destination, }, }, { description: 'both hostname and host -> hostname takes precedence', proxyConfig: { hostname: '127.0.0.1', host: '0.0.0.0', protocol: 'http', port: OPEN_WEB_PORT, }, expectedOptions: { host: '127.0.0.1', protocol: 'http:', port: OPEN_WEB_PORT, path: destination, }, }, { description: 'only host and https protocol', proxyConfig: { host: '0.0.0.0', protocol: 'https', port: OPEN_WEB_PORT }, expectedOptions: { host: '0.0.0.0', protocol: 'https:', port: OPEN_WEB_PORT, path: destination, }, }, ]; for (const test of testCases) { it(test.description, () => { const options = { headers: {}, beforeRedirects: {} }; __setProxy(options, test.proxyConfig, destination); for (const [key, expected] of Object.entries(test.expectedOptions)) { assert.strictEqual(options[key], expected); } }); } }); describe('Host header preservation when forwarding through a proxy (#10805)', () => { const proxyConfig = { hostname: '127.0.0.1', protocol: 'http:', port: 8888 }; it('defaults the Host header to the request target when the user does not set one', () => { const options = { headers: {}, beforeRedirects: {}, hostname: '127.0.0.1', port: 4000, }; __setProxy(options, proxyConfig, 'http://127.0.0.1:4000/'); assert.strictEqual(options.headers.host, '127.0.0.1:4000'); }); it('preserves a user-supplied lowercase host header', () => { const options = { headers: { host: 'example.com' }, beforeRedirects: {}, hostname: '127.0.0.1', port: 4000, }; __setProxy(options, proxyConfig, 'http://127.0.0.1:4000/'); assert.strictEqual(options.headers.host, 'example.com'); }); it('preserves a user-supplied Host header regardless of casing', () => { const options = { headers: { Host: 'example.com' }, beforeRedirects: {}, hostname: '127.0.0.1', port: 4000, }; __setProxy(options, proxyConfig, 'http://127.0.0.1:4000/'); assert.strictEqual(options.headers.Host, 'example.com'); assert.strictEqual(options.headers.host, undefined); }); it('preserves a user-supplied Host header across a redirect re-invocation', () => { const options = { headers: { Host: 'example.com' }, beforeRedirects: {}, hostname: '127.0.0.1', port: 4000, }; __setProxy(options, proxyConfig, 'http://127.0.0.1:4000/', true); assert.strictEqual(options.headers.Host, 'example.com'); assert.strictEqual(options.headers.host, undefined); }); it('ignores polluted prototype Host fields when detecting user-supplied headers', () => { Object.prototype.host = 'polluted.example.com'; const options = { headers: {}, beforeRedirects: {}, hostname: '127.0.0.1', port: 4000, }; try { __setProxy(options, proxyConfig, 'http://127.0.0.1:4000/'); assert.strictEqual(options.headers.host, '127.0.0.1:4000'); } finally { delete Object.prototype.host; } }); }); describe('Proxy-Authorization header leak on redirect', () => { it('clears a stale Proxy-Authorization header when redirected request resolves to no proxy (configProxy=false)', () => { const options = { headers: {}, beforeRedirects: {}, hostname: 'initial.example.com', host: 'initial.example.com', port: 80, }; __setProxy( options, { host: '127.0.0.1', port: 8030, auth: { username: 'user', password: 'pass' } }, 'http://initial.example.com/start' ); assert.strictEqual( options.headers['Proxy-Authorization'], 'Basic ' + Buffer.from('user:pass', 'utf8').toString('base64'), 'initial request should carry Proxy-Authorization' ); // Simulate redirect re-invocation where the redirected request is resolved to no proxy. // This mirrors the beforeRedirects.proxy hook being called with configProxy=false. const redirectOptions = { headers: { ...options.headers }, beforeRedirects: {}, hostname: 'attacker.example.com', host: 'attacker.example.com', port: 443, }; __setProxy(redirectOptions, false, 'https://attacker.example.com/final', true); assert.strictEqual( redirectOptions.headers['Proxy-Authorization'], undefined, 'stale Proxy-Authorization must be stripped when redirected request no longer uses a proxy' ); }); it('clears a stale Proxy-Authorization header when environment-derived proxy is bypassed on redirect (NO_PROXY)', () => { const originalHttpProxy = process.env.http_proxy; const originalHttpsProxy = process.env.https_proxy; const originalNoProxy = process.env.no_proxy; process.env.http_proxy = 'http://user:pass@127.0.0.1:8030'; process.env.https_proxy = 'http://user:pass@127.0.0.1:8030'; process.env.no_proxy = 'attacker.example.com'; try { const options = { headers: {}, beforeRedirects: {}, hostname: 'initial.example.com', host: 'initial.example.com', port: 80, }; __setProxy(options, undefined, 'http://initial.example.com/start'); assert.strictEqual( options.headers['Proxy-Authorization'], 'Basic ' + Buffer.from('user:pass', 'utf8').toString('base64'), 'initial request should pick up proxy credentials from env' ); const redirectOptions = { headers: { ...options.headers }, beforeRedirects: {}, hostname: 'attacker.example.com', host: 'attacker.example.com', port: 443, protocol: 'https:', }; __setProxy(redirectOptions, undefined, 'https://attacker.example.com/final', true); assert.strictEqual( redirectOptions.headers['Proxy-Authorization'], undefined, 'stale Proxy-Authorization must be stripped when redirect target is covered by NO_PROXY' ); } finally { if (originalHttpProxy === undefined) delete process.env.http_proxy; else process.env.http_proxy = originalHttpProxy; if (originalHttpsProxy === undefined) delete process.env.https_proxy; else process.env.https_proxy = originalHttpsProxy; if (originalNoProxy === undefined) delete process.env.no_proxy; else process.env.no_proxy = originalNoProxy; } }); it('replaces Proxy-Authorization when redirect target resolves to a different proxy without credentials', () => { const options = { headers: {}, beforeRedirects: {}, hostname: 'initial.example.com', host: 'initial.example.com', port: 80, }; __setProxy( options, { host: '127.0.0.1', port: 8030, auth: { username: 'user', password: 'pass' } }, 'http://initial.example.com/start' ); assert.ok( options.headers['Proxy-Authorization'], 'precondition: initial proxy auth header set' ); const redirectOptions = { headers: { ...options.headers }, beforeRedirects: {}, hostname: 'second.example.com', host: 'second.example.com', port: 80, }; __setProxy( redirectOptions, { host: '127.0.0.2', port: 8031 }, 'http://second.example.com/final', true ); assert.strictEqual( redirectOptions.headers['Proxy-Authorization'], undefined, 'stale credentials from previous proxy must not leak to a new proxy without credentials' ); }); it('strips stale Proxy-Authorization when the beforeRedirects.proxy hook is invoked with configProxy=false', () => { const options = { headers: { 'Proxy-Authorization': 'Basic ' + Buffer.from('user:pass', 'utf8').toString('base64'), }, beforeRedirects: {}, hostname: 'initial.example.com', host: 'initial.example.com', port: 80, }; __setProxy(options, false, 'http://initial.example.com/start'); assert.strictEqual( typeof options.beforeRedirects.proxy, 'function', 'initial setProxy must install redirect hook' ); const redirectOptions = { headers: { 'Proxy-Authorization': 'Basic ' + Buffer.from('user:pass', 'utf8').toString('base64'), }, beforeRedirects: {}, hostname: 'attacker.example.com', host: 'attacker.example.com', port: 443, href: 'https://attacker.example.com/final', }; options.beforeRedirects.proxy(redirectOptions); assert.strictEqual( redirectOptions.headers['Proxy-Authorization'], undefined, 'beforeRedirects.proxy hook must strip stale Proxy-Authorization when redirect target has no proxy' ); }); it('preserves a user-supplied Proxy-Authorization header on the initial request when no proxy is configured', () => { const userValue = 'Basic ' + Buffer.from('alice:secret', 'utf8').toString('base64'); const options = { headers: { 'Proxy-Authorization': userValue }, beforeRedirects: {}, hostname: 'example.com', host: 'example.com', port: 80, }; __setProxy(options, false, 'http://example.com/start'); assert.strictEqual( options.headers['Proxy-Authorization'], userValue, 'user-supplied Proxy-Authorization must not be stripped on the initial request' ); }); it('strips stale Proxy-Authorization regardless of header key casing', () => { const staleValue = 'Basic ' + Buffer.from('user:pass', 'utf8').toString('base64'); const casings = [ 'proxy-authorization', 'PROXY-AUTHORIZATION', 'Proxy-authorization', 'pRoXy-AuThOrIzAtIoN', ]; for (const casing of casings) { const redirectOptions = { headers: { [casing]: staleValue }, beforeRedirects: {}, hostname: 'attacker.example.com', host: 'attacker.example.com', port: 443, }; __setProxy(redirectOptions, false, 'https://attacker.example.com/final', true); const leaked = Object.keys(redirectOptions.headers).filter( (name) => name.toLowerCase() === 'proxy-authorization' ); assert.deepStrictEqual( leaked, [], `stale Proxy-Authorization with key "${casing}" must be stripped regardless of casing` ); } }); // End-to-end exercise of the redirect leak. An // authenticated env-supplied proxy sees the initial request, 302s the // client to a target that NO_PROXY excludes, and the redirected request // must not carry the stale Proxy-Authorization to the direct target. it('does not forward Proxy-Authorization to a redirect target that resolves to no-proxy', async () => { const startServer = (handler) => new Promise((resolve) => { const s = http.createServer(handler); s.listen(0, '127.0.0.1', () => resolve(s)); }); const stop = (s) => new Promise((r) => s.close(r)); let attackerPort; const proxySaw = []; const attackerSaw = []; // The proxy receives the absolute-form URL (`GET http://target/path`) on // the initial request, then forwards to the destination. We short-circuit // by responding directly with the redirect. const corpProxy = await startServer((req, res) => { proxySaw.push({ url: req.url, proxyAuth: req.headers['proxy-authorization'] }); res.writeHead(302, { Location: `http://127.0.0.1:${attackerPort}/final` }); res.end(); }); const attacker = await startServer((req, res) => { attackerSaw.push({ url: req.url, proxyAuth: req.headers['proxy-authorization'], authorization: req.headers.authorization, }); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end('{"final":true}'); }); attackerPort = attacker.address().port; const corpProxyPort = corpProxy.address().port; const originalHttpProxy = process.env.http_proxy; const originalNoProxy = process.env.no_proxy; process.env.http_proxy = `http://user:pass@127.0.0.1:${corpProxyPort}`; // NO_PROXY entry covers only the attacker target (port-specific), so the // initial request still uses the proxy but the redirect resolves direct. process.env.no_proxy = `127.0.0.1:${attackerPort}`; try { await axios.get('http://example.com/start'); assert.ok( proxySaw.some((h) => h.proxyAuth), 'precondition: corp proxy must see Proxy-Authorization on the initial request' ); assert.strictEqual( attackerSaw.length, 1, 'attacker target must receive exactly the redirected request' ); assert.strictEqual( attackerSaw[0].proxyAuth, undefined, 'stale Proxy-Authorization must not leak to the redirect target' ); } finally { if (originalHttpProxy === undefined) delete process.env.http_proxy; else process.env.http_proxy = originalHttpProxy; if (originalNoProxy === undefined) delete process.env.no_proxy; else process.env.no_proxy = originalNoProxy; await stop(corpProxy); await stop(attacker); } }, 10000); }); it('should support cancel', async () => { const source = axios.CancelToken.source(); const server = await startHTTPServer( (req, res) => { // Call cancel() when the request has been sent but no response received. source.cancel('Operation has been canceled.'); }, { port: SERVER_PORT } ); try { await assert.rejects( async function stackTraceTest() { await axios.get(`http://localhost:${server.address().port}/`, { cancelToken: source.token, }); }, (thrown) => { assert.ok( thrown instanceof axios.Cancel, 'Promise must be rejected with a CanceledError object' ); assert.equal(thrown.message, 'Operation has been canceled.'); return true; } ); } finally { await stopHTTPServer(server); } }); it('should combine baseURL and url', async () => { const server = await startHTTPServer( (req, res) => { res.end(); }, { port: SERVER_PORT } ); try { const response = await axios.get('/foo', { baseURL: `http://localhost:${server.address().port}/`, }); assert.equal(response.config.baseURL, `http://localhost:${server.address().port}/`); assert.equal(response.config.url, '/foo'); } finally { await stopHTTPServer(server); } }); it('should support HTTP protocol', async () => { const server = await startHTTPServer( (req, res) => { setTimeout(() => { res.end(); }, 1000); }, { port: SERVER_PORT } ); try { const response = await axios.get(`http://localhost:${server.address().port}`); assert.equal(response.request.agent.protocol, 'http:'); } finally { await stopHTTPServer(server); } }); it('should support HTTPS protocol', async () => { const tlsOptions = { key: fs.readFileSync(path.join(adaptersTestsDir, 'key.pem')), cert: fs.readFileSync(path.join(adaptersTestsDir, 'cert.pem')), }; const server = await new Promise((resolve, reject) => { const httpsServer = https .createServer( tlsOptions, (req, res) => { setTimeout(() => { res.end(); }, 1000); }, { port: SERVER_PORT } ) .listen(SERVER_PORT, () => resolve(httpsServer)); httpsServer.on('error', reject); }); try { const response = await axios.get(`https://localhost:${server.address().port}`, { httpsAgent: new https.Agent({ rejectUnauthorized: false, }), }); assert.equal(response.request.agent.protocol, 'https:'); } finally { await new Promise((resolve, reject) => { server.close((error) => { if (error) { reject(error); return; } resolve(); }); }); } }); describe('HTTPS CONNECT tunneling agent management', () => { const buildOptions = () => ({ headers: {}, beforeRedirects: {}, hostname: 'example.com', host: 'example.com', port: 443, path: '/', protocol: 'https:', }); const proxyConfig = { host: '127.0.0.1', port: 8030, protocol: 'http' }; it('reuses the same tunneling agent for repeated requests through the same proxy', () => { const a = buildOptions(); const b = buildOptions(); __setProxy(a, proxyConfig, 'https://example.com/'); __setProxy(b, proxyConfig, 'https://example.com/'); assert.ok(a.agent, 'first request must install a tunneling agent'); assert.strictEqual( a.agent, b.agent, 'subsequent requests through the same proxy must share one tunneling agent so socket pooling works' ); }); it('still tunnels through the proxy when a non-proxy httpsAgent is supplied', () => { const userAgent = new https.Agent({ rejectUnauthorized: false }); const options = buildOptions(); __setProxy(options, proxyConfig, 'https://example.com/', false, userAgent); assert.ok( options.agent, 'proxy must not be silently bypassed when a custom httpsAgent is set' ); assert.notStrictEqual( options.agent, userAgent, 'tunneling agent must be installed in place of the user agent (its TLS options are forwarded internally)' ); assert.ok(options.agent instanceof HttpsProxyAgent); }); it('includes user httpsAgent options in the tunneling agent constructor options', () => { const userAgent = new https.Agent({ rejectUnauthorized: false, ca: 'sentinel-ca' }); const options = buildOptions(); __setProxy(options, proxyConfig, 'https://example.com/', false, userAgent); // Origin TLS behavior is covered by the issue #10953 integration test. assert.strictEqual(options.agent.proxy.rejectUnauthorized, false); assert.strictEqual(options.agent.proxy.ca, 'sentinel-ca'); }); it('respects a user-supplied HttpsProxyAgent without installing its own', () => { const userTunnel = new HttpsProxyAgent({ protocol: 'http:', hostname: '127.0.0.1', port: 9999, }); const options = buildOptions(); __setProxy(options, proxyConfig, 'https://example.com/', false, userTunnel); // The user is handling tunneling end-to-end; setProxy must not overwrite agent. assert.strictEqual(options.agent, undefined, 'must not install a competing tunneling agent'); }); it('does not strip a user-supplied HttpsProxyAgent on redirect', () => { const userTunnel = new HttpsProxyAgent({ protocol: 'http:', hostname: '127.0.0.1', port: 9999, }); const redirectOptions = { headers: {}, beforeRedirects: {}, hostname: 'redirect.example.com', host: 'redirect.example.com', port: 443, path: '/', protocol: 'https:', agent: userTunnel, }; __setProxy(redirectOptions, false, 'https://redirect.example.com/', true); assert.strictEqual( redirectOptions.agent, userTunnel, 'user-supplied HttpsProxyAgent must survive redirects (no proxy on redirect target)' ); }); it('strips its own tunneling agent on redirect when the redirect target has no proxy', () => { const initial = buildOptions(); __setProxy(initial, proxyConfig, 'https://example.com/'); assert.ok( initial.agent instanceof HttpsProxyAgent, 'precondition: tunneling agent installed' ); const redirectOptions = { headers: {}, beforeRedirects: {}, hostname: 'final.example.com', host: 'final.example.com', port: 443, path: '/', protocol: 'https:', agent: initial.agent, }; __setProxy(redirectOptions, false, 'https://final.example.com/', true); assert.strictEqual( redirectOptions.agent, undefined, 'axios-installed tunneling agent must be cleared when redirect drops the proxy' ); }); it('handles IPv6 literal proxy hosts', () => { const options = buildOptions(); __setProxy(options, { host: '::1', port: 8030, protocol: 'http' }, 'https://example.com/'); assert.ok( options.agent instanceof HttpsProxyAgent, 'must build a tunneling agent for an IPv6 proxy host' ); }); }); it('should return malformed URL', async () => { await assert.rejects(axios.get('tel:484-695-3408'), (error) => { assert.equal(error.message, 'Unsupported protocol tel:'); return true; }); }); it('should return unsupported protocol', async () => { await assert.rejects(axios.get('ftp:google.com'), (error) => { assert.equal(error.message, 'Unsupported protocol ftp:'); return true; }); }); it('rejects malformed HTTP URLs before Node URL normalization and preserves config', async () => { for (const url of ['\u0000https:example.com/users', 'h\nttp:example.com/users']) { await assert.rejects( () => axios.get(url, { adapter: 'http', headers: { 'X-Test': 'yes', }, }), (error) => { assert.ok(error instanceof AxiosError); assert.strictEqual(error.code, AxiosError.ERR_INVALID_URL); assert.match(error.message, /^Invalid URL ".*": missing "\/\/" after protocol$/); assert.strictEqual(error.config.url, url); assert.strictEqual(error.config.headers.get('X-Test'), 'yes'); return true; } ); } }); it('should supply a user-agent if one is not specified', async () => { const server = await startHTTPServer( (req, res) => { assert.equal(req.headers['user-agent'], `axios/${axios.VERSION}`); res.end(); }, { port: SERVER_PORT } ); try { await axios.get(`http://localhost:${server.address().port}/`); } finally { await stopHTTPServer(server); } }); it('should omit a user-agent if one is explicitly disclaimed', async () => { const server = await startHTTPServer( (req, res) => { assert.equal('user-agent' in req.headers, false); assert.equal('User-Agent' in req.headers, false); res.end(); }, { port: SERVER_PORT } ); try { await axios.get(`http://localhost:${server.address().port}/`, { headers: { 'User-Agent': null, }, }); } finally { await stopHTTPServer(server); } }); it('should throw an error if http server that aborts a chunked request', async () => { const server = await startHTTPServer( (req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain', 'X-Stream-Aborted': 'yes' }); res.write('chunk 1'); setTimeout(() => { res.write('chunk 2'); }, 100); setTimeout(() => { res.destroy(); }, 200); }, { port: SERVER_PORT } ); try { await assert.rejects( axios.get(`http://localhost:${server.address().port}/aborted`, { timeout: 500, }), (error) => { assert.strictEqual(error.code, 'ERR_BAD_RESPONSE'); assert.strictEqual(error.message, 'stream has been aborted'); assert.strictEqual(error.response.status, 200); assert.strictEqual(error.response.headers.get('x-stream-aborted'), 'yes'); assert.strictEqual(error.status, 200); return true; } ); } finally { await stopHTTPServer(server); } }); it('should able to cancel multiple requests with CancelToken', async () => { const server = await startHTTPServer( (req, res) => { res.end('ok'); }, { port: SERVER_PORT } ); try { const source = axios.CancelToken.source(); const canceledStack = []; const requests = [1, 2, 3, 4, 5].map(async (id) => { try { await axios.get('/foo/bar', { baseURL: `http://localhost:${server.address().port}`, cancelToken: source.token, }); } catch (error) { if (!axios.isCancel(error)) { throw error; } canceledStack.push(id); } }); source.cancel('Aborted by user'); await Promise.all(requests); assert.deepStrictEqual(canceledStack.sort(), [1, 2, 3, 4, 5]); } finally { await stopHTTPServer(server); } }); describe('FormData', () => { describe('form-data instance (https://www.npmjs.com/package/form-data)', () => { it('should allow passing FormData', async () => { const form = new FormDataLegacy(); const file1 = Buffer.from('foo', 'utf8'); const image = path.resolve(adaptersTestsDir, './axios.png'); const fileStream = fs.createReadStream(image); const stat = fs.statSync(image); form.append('foo', 'bar'); form.append('file1', file1, { filename: 'bar.jpg', filepath: 'temp/bar.jpg', contentType: 'image/jpeg', }); form.append('fileStream', fileStream); const server = await startHTTPServer( (req, res) => { const receivedForm = new IncomingForm(); assert.ok(req.rawHeaders.some((header) => header.toLowerCase() === 'content-length')); receivedForm.parse(req, (error, fields, files) => { if (error) { res.statusCode = 500; res.end(error.message); return; } res.end( JSON.stringify({ fields, files, }) ); }); }, { port: SERVER_PORT } ); try { const response = await axios.post(`http://localhost:${server.address().port}/`, form, { headers: { 'Content-Type': 'multipart/form-data', }, }); assert.deepStrictEqual(response.data.fields, { foo: ['bar'] }); assert.strictEqual(response.data.files.file1[0].mimetype, 'image/jpeg'); assert.strictEqual(response.data.files.file1[0].originalFilename, 'temp/bar.jpg'); assert.strictEqual(response.data.files.file1[0].size, 3); assert.strictEqual(response.data.files.fileStream[0].mimetype, 'image/png'); assert.strictEqual(response.data.files.fileStream[0].originalFilename, 'axios.png'); assert.strictEqual(response.data.files.fileStream[0].size, stat.size); } finally { await stopHTTPServer(server); } }); }); describe('SpecCompliant FormData', () => { it('should allow passing FormData', { retry: 2 }, async () => { // Use an ephemeral port and a non-keep-alive agent. Sharing the fixed // SERVER_PORT across tests can leave keep-alive sockets in the global // pool that a follow-up test picks up just as the server FINs them, // which surfaces here as EPIPE on the multipart write. const server = await startHTTPServer( async (req, res) => { const { fields, files } = await handleFormData(req); res.end( JSON.stringify({ fields, files, }) ); }, { port: 0 } ); const oneShotAgent = new http.Agent({ keepAlive: false }); try { const form = new FormDataSpecCompliant(); const blobContent = 'blob-content'; const blob = new BlobSpecCompliant([blobContent], { type: 'image/jpeg' }); form.append('foo1', 'bar1'); form.append('foo2', 'bar2'); form.append('file1', blob); const { data } = await axios.post(`http://localhost:${server.address().port}`, form, { maxRedirects: 0, httpAgent: oneShotAgent, headers: { Connection: 'close' }, }); assert.deepStrictEqual(data.fields, { foo1: ['bar1'], foo2: ['bar2'] }); assert.deepStrictEqual(typeof data.files.file1[0], 'object'); const { size, mimetype, originalFilename } = data.files.file1[0]; assert.deepStrictEqual( { size, mimetype, originalFilename }, { mimetype: 'image/jpeg', originalFilename: 'blob', size: Buffer.from(blobContent).byteLength, } ); } finally { oneShotAgent.destroy(); await stopHTTPServer(server); } }); }); describe('prototype pollution', () => { const pollutedKeys = ['getHeaders', 'append', 'pipe', 'on', 'once']; const toStringTagSym = Symbol.toStringTag; it('should not use inherited Symbol.iterator for request or response headers', async () => { let capturedHeaders; const stubTransport = { request(options, handleResponse) { capturedHeaders = { ...options.headers }; const req = new EventEmitter(); req.write = () => true; req.setTimeout = () => {}; req.destroy = () => {}; req.end = () => { const res = new stream.Readable({ read() {} }); res.statusCode = 200; res.statusMessage = 'OK'; res.headers = { 'x-server': 'real' }; res.rawHeaders = []; res.req = req; process.nextTick(() => { handleResponse(res); res.push(null); }); }; return req; }, }; try { Object.prototype[Symbol.iterator] = function* () { yield ['X-Injected', 'yes']; yield ['Authorization', 'Bearer CHANGED']; }; const response = await axios.get('http://stub.invalid/', { headers: { Authorization: 'Bearer VALID_USER_TOKEN', 'X-App': 'safe', }, transport: stubTransport, maxRedirects: 0, }); assert.ok(capturedHeaders, 'transport was not invoked'); assert.strictEqual(capturedHeaders['X-App'], 'safe'); assert.strictEqual( capturedHeaders.Authorization || capturedHeaders.authorization, 'Bearer VALID_USER_TOKEN' ); assert.strictEqual( capturedHeaders['X-Injected'] || capturedHeaders['x-injected'], undefined ); assert.strictEqual(response.headers.get('x-server'), 'real'); assert.strictEqual(response.headers.get('x-injected'), undefined); } finally { delete Object.prototype[Symbol.iterator]; } }); function pollute() { Object.prototype[toStringTagSym] = 'FormData'; Object.prototype.append = () => {}; Object.prototype.getHeaders = () => ({ 'x-injected': 'attacker', authorization: 'Bearer ATTACKER_TOKEN', }); Object.prototype.pipe = function (d) { if (d && d.end) d.end(); return d; }; Object.prototype.on = function () { return this; }; Object.prototype.once = function () { return this; }; } function cleanup() { for (const k of pollutedKeys) delete Object.prototype[k]; delete Object.prototype[toStringTagSym]; } it('should not merge prototype-polluted getHeaders into outgoing request', async () => { // Use a stub transport rather than a real HTTP server: polluting // Object.prototype in-process can destabilise Node's HTTP server // internals and cause spurious ECONNRESET. The stub captures the final // outgoing headers axios constructs, which is what this test asserts on. let capturedHeaders; const stubTransport = { request(options, handleResponse) { capturedHeaders = { ...options.headers }; const req = new EventEmitter(); req.write = () => true; req.setTimeout = () => {}; req.destroy = () => {}; req.end = () => { const res = new stream.Readable({ read() {} }); res.statusCode = 200; res.statusMessage = 'OK'; res.headers = {}; res.rawHeaders = []; res.req = req; process.nextTick(() => { handleResponse(res); res.push(null); }); }; return req; }, }; try { pollute(); await axios.post( 'http://stub.invalid/', { userId: 42 }, { headers: { Authorization: 'Bearer VALID_USER_TOKEN' }, transport: stubTransport, maxRedirects: 0, } ); } finally { cleanup(); } assert.ok(capturedHeaders, 'transport was not invoked'); assert.strictEqual(capturedHeaders['x-injected'], undefined); assert.notStrictEqual(capturedHeaders['Authorization'], 'Bearer ATTACKER_TOKEN'); assert.notStrictEqual(capturedHeaders['authorization'], 'Bearer ATTACKER_TOKEN'); }); }); describe('formDataHeaderPolicy', () => { function createStubTransport(captureHeaders) { return { request(options, handleResponse) { captureHeaders({ ...options.headers }); const req = new EventEmitter(); req.write = () => true; req.setTimeout = () => {}; req.destroy = () => {}; req.end = () => { const res = new stream.Readable({ read() {} }); res.statusCode = 200; res.statusMessage = 'OK'; res.headers = {}; res.rawHeaders = []; res.req = req; process.nextTick(() => { handleResponse(res); res.push(null); }); }; return req; }, }; } class CustomFormData extends stream.Readable { _read() { this.push(null); } append() {} getHeaders() { return { 'content-type': 'multipart/form-data; boundary=----fake', 'x-injected': 'custom', 'x-forwarded-for': '10.0.0.1', authorization: 'Bearer CUSTOM_TOKEN', host: 'custom.example.com', }; } get [Symbol.toStringTag]() { return 'FormData'; } } it('preserves legacy getHeaders() propagation by default', async () => { let capturedHeaders; await axios.post('http://stub.invalid/', new CustomFormData(), { transport: createStubTransport((headers) => { capturedHeaders = headers; }), maxRedirects: 0, }); assert.ok(capturedHeaders, 'transport was not invoked'); const ct = capturedHeaders['Content-Type'] || capturedHeaders['content-type']; assert.match(ct, /multipart\/form-data/); assert.strictEqual(capturedHeaders['x-injected'], 'custom'); assert.strictEqual(capturedHeaders['x-forwarded-for'], '10.0.0.1'); assert.strictEqual( capturedHeaders.Authorization || capturedHeaders.authorization, 'Bearer CUSTOM_TOKEN' ); assert.strictEqual(capturedHeaders.Host || capturedHeaders.host, 'custom.example.com'); }); it('only copies content headers when formDataHeaderPolicy is content-only', async () => { let capturedHeaders; await axios.post('http://stub.invalid/', new CustomFormData(), { transport: createStubTransport((headers) => { capturedHeaders = headers; }), maxRedirects: 0, formDataHeaderPolicy: 'content-only', }); assert.ok(capturedHeaders, 'transport was not invoked'); const ct = capturedHeaders['Content-Type'] || capturedHeaders['content-type']; assert.match(ct, /multipart\/form-data/); assert.strictEqual(capturedHeaders['x-injected'], undefined); assert.strictEqual(capturedHeaders['x-forwarded-for'], undefined); assert.strictEqual( capturedHeaders.Authorization || capturedHeaders.authorization, undefined ); assert.strictEqual(capturedHeaders.Host || capturedHeaders.host, undefined); }); it('ignores undefined getHeaders() results with content-only policy', async () => { let capturedHeaders; class FormDataWithoutHeaders extends CustomFormData { getHeaders() {} } await axios.post('http://stub.invalid/', new FormDataWithoutHeaders(), { headers: { 'X-Test': 'ok', }, transport: createStubTransport((headers) => { capturedHeaders = headers; }), maxRedirects: 0, formDataHeaderPolicy: 'content-only', }); assert.ok(capturedHeaders, 'transport was not invoked'); assert.strictEqual(capturedHeaders['X-Test'] || capturedHeaders['x-test'], 'ok'); }); }); }); describe('toFormData helper', () => { it('should properly serialize nested objects for parsing with multer.js (express.js)', async () => { const app = express(); const obj = { arr1: ['1', '2', '3'], arr2: ['1', ['2'], '3'], obj: { x: '1', y: { z: '1' } }, users: [ { name: 'Peter', surname: 'griffin' }, { name: 'Thomas', surname: 'Anderson' }, ], }; app.post('/', multer().none(), (req, res) => { res.send(JSON.stringify(req.body)); }); const server = await new Promise( (resolve, reject) => { const expressServer = app.listen(0, () => resolve(expressServer)); expressServer.on('error', reject); }, { port: SERVER_PORT } ); try { await Promise.all( [null, false, true].map((mode) => axios .postForm(`http://localhost:${server.address().port}/`, obj, { formSerializer: { indexes: mode }, }) .then((response) => { assert.deepStrictEqual(response.data, obj, `Index mode ${mode}`); }) ) ); } finally { await new Promise((resolve, reject) => { server.close((error) => { if (error) { reject(error); return; } resolve(); }); }); } }); it('should only match explicit routes for express 5 form handlers', async () => { const app = express(); app.post('/', multer().none(), (req, res) => { res.status(200).send(JSON.stringify({ route: 'root', body: req.body })); }); app.post('/unexpected', multer().none(), (req, res) => { res.status(418).send('wrong-route'); }); const server = await new Promise( (resolve, reject) => { const expressServer = app.listen(0, () => resolve(expressServer)); expressServer.on('error', reject); }, { port: SERVER_PORT } ); const rootUrl = `http://localhost:${server.address().port}`; try { const rootResponse = await axios.postForm(rootUrl, { foo: 'bar' }); assert.strictEqual(rootResponse.status, 200); assert.deepStrictEqual(rootResponse.data, { route: 'root', body: { foo: 'bar' } }); await assert.rejects( () => axios.postForm(`${rootUrl}/unexpected`, { foo: 'bar' }), (error) => { assert.strictEqual(error.response.status, 418); assert.strictEqual(error.response.data, 'wrong-route'); return true; } ); } finally { await new Promise((resolve, reject) => { server.close((error) => { if (error) { reject(error); return; } resolve(); }); }); } }); }); describe('Blob', () => { it('should support Blob', async () => { const server = await startHTTPServer( async (req, res) => { res.end(await getStream(req)); }, { port: SERVER_PORT } ); try { const blobContent = 'blob-content'; const blob = new BlobSpecCompliant([blobContent], { type: 'image/jpeg' }); const { data } = await axios.post(`http://localhost:${server.address().port}`, blob, { maxRedirects: 0, }); assert.deepStrictEqual(data, blobContent); } finally { await stopHTTPServer(server); } }); }); describe('URLEncoded Form', () => { it('should post object data as url-encoded form regardless of content-type header casing', async () => { const app = express(); const obj = { arr1: ['1', '2', '3'], arr2: ['1', ['2'], '3'], obj: { x: '1', y: { z: '1' } }, users: [ { name: 'Peter', surname: 'griffin' }, { name: 'Thomas', surname: 'Anderson' }, ], }; app.use(bodyParser.urlencoded({ extended: true })); app.post('/', (req, res) => { res.send(JSON.stringify(req.body)); }); const server = await new Promise( (resolve, reject) => { const expressServer = app.listen(0, () => resolve(expressServer)); expressServer.on('error', reject); }, { port: SERVER_PORT } ); try { for (const headerName of ['content-type', 'Content-Type']) { const response = await axios.post(`http://localhost:${server.address().port}/`, obj, { headers: { [headerName]: 'application/x-www-form-urlencoded', }, }); assert.deepStrictEqual(response.data, obj); } } finally { await new Promise((resolve, reject) => { server.close((error) => { if (error) { reject(error); return; } resolve(); }); }); } }); it('should respect formSerializer config', async () => { const obj = { arr1: ['1', '2', '3'], arr2: ['1', ['2'], '3'], }; const form = new URLSearchParams(); form.append('arr1[0]', '1'); form.append('arr1[1]', '2'); form.append('arr1[2]', '3'); form.append('arr2[0]', '1'); form.append('arr2[1][0]', '2'); form.append('arr2[2]', '3'); const server = await startHTTPServer( (req, res) => { req.pipe(res); }, { port: SERVER_PORT } ); try { const response = await axios.post(`http://localhost:${server.address().port}/`, obj, { headers: { 'content-type': 'application/x-www-form-urlencoded', }, formSerializer: { indexes: true, }, }); assert.strictEqual(response.data, form.toString()); } finally { await stopHTTPServer(server); } }); it('should parse nested urlencoded payloads and ignore mismatched content-type', async () => { const app = express(); app.use(bodyParser.urlencoded({ extended: true })); app.post('/', (req, res) => { const parserRanBeforeHandler = Boolean(req.body && Object.keys(req.body).length); res.send( JSON.stringify({ parserRanBeforeHandler, body: req.body, }) ); }); const server = await new Promise( (resolve, reject) => { const expressServer = app.listen(0, () => resolve(expressServer)); expressServer.on('error', reject); }, { port: SERVER_PORT } ); const rootUrl = `http://localhost:${server.address().port}/`; const payload = 'user[name]=Peter&tags[]=a&tags[]=b'; try { const parsedResponse = await axios.post(rootUrl, payload, { headers: { 'content-type': 'application/x-www-form-urlencoded', }, }); assert.deepStrictEqual(parsedResponse.data, { parserRanBeforeHandler: true, body: { user: { name: 'Peter' }, tags: ['a', 'b'], }, }); const ignoredResponse = await axios.post(rootUrl, payload, { headers: { 'content-type': 'text/plain', }, }); assert.strictEqual(ignoredResponse.data.parserRanBeforeHandler, false); assert.notDeepStrictEqual(ignoredResponse.data.body, { user: { name: 'Peter' }, tags: ['a', 'b'], }); } finally { await new Promise((resolve, reject) => { server.close((error) => { if (error) { reject(error); return; } resolve(); }); }); } }); }); describe('Data URL', () => { it('should support requesting data URL as a Buffer', async () => { const buffer = Buffer.from('123'); const dataURI = `data:application/octet-stream;base64,${buffer.toString('base64')}`; const { data } = await axios.get(dataURI); assert.deepStrictEqual(data, buffer); }); it('should support requesting data URL as a Blob (if supported by the environment)', async () => { if (!isBlobSupported) { return; } const buffer = Buffer.from('123'); const dataURI = `data:application/octet-stream;base64,${buffer.toString('base64')}`; const { data } = await axios.get(dataURI, { responseType: 'blob' }); assert.strictEqual(data.type, 'application/octet-stream'); assert.deepStrictEqual(await data.text(), '123'); }); it('should support requesting data URL as a String (text)', async () => { const buffer = Buffer.from('123', 'utf-8'); const dataURI = `data:application/octet-stream;base64,${buffer.toString('base64')}`; const { data } = await axios.get(dataURI, { responseType: 'text' }); assert.deepStrictEqual(data, '123'); }); it('should support requesting data URL as a Stream', async () => { const buffer = Buffer.from('123', 'utf-8'); const dataURI = `data:application/octet-stream;base64,${buffer.toString('base64')}`; const { data } = await axios.get(dataURI, { responseType: 'stream' }); assert.strictEqual(await getStream(data), '123'); }); it('should allow a base64 data URL at the Buffer allocation limit', async () => { const dataURI = 'data:application/octet-stream;base64,TQ=='; const { data } = await axios.get(dataURI, { maxContentLength: 1 }); assert.deepStrictEqual(data, Buffer.from('M')); }); it('should reject percent-embedded base64 whose Buffer allocation exceeds the limit', async () => { const body = 'QQ' + '%41'.repeat(4000); const dataURI = 'data:application/octet-stream;base64,' + body; await assert.rejects(axios.get(dataURI, { maxContentLength: 3000 }), (err) => { assert.strictEqual(err.code, AxiosError.ERR_BAD_RESPONSE); assert.match(err.message, /maxContentLength size of 3000 exceeded/); return true; }); }); it('should count ignored input after base64 padding toward the Buffer allocation limit', async () => { const dataURI = 'data:application/octet-stream;base64,TQ==' + '%'.repeat(4096); await assert.rejects(axios.get(dataURI, { maxContentLength: 1 }), (err) => { assert.strictEqual(err.code, AxiosError.ERR_BAD_RESPONSE); assert.match(err.message, /maxContentLength size of 1 exceeded/); return true; }); }); }); describe('progress', () => { describe('upload', () => { it('should support upload progress capturing', async () => { const server = await startHTTPServer( { rate: 100 * 1024, }, { port: SERVER_PORT } ); try { let content = ''; const count = 10; const chunk = 'test'; const chunkLength = Buffer.byteLength(chunk); const contentLength = count * chunkLength; const readable = stream.Readable.from( (async function* () { let i = count; while (i-- > 0) { await setTimeoutAsync(1100); content += chunk; yield chunk; } })() ); const samples = []; const { data } = await axios.post(`http://localhost:${server.address().port}`, readable, { onUploadProgress: ({ loaded, total, progress, bytes, upload }) => { samples.push({ loaded, total, progress, bytes, upload, }); }, headers: { 'Content-Length': contentLength, }, responseType: 'text', }); assert.strictEqual(data, content); assert.deepStrictEqual( samples, Array.from( (function* () { for (let i = 1; i <= 10; i++) { yield { loaded: chunkLength * i, total: contentLength, progress: (chunkLength * i) / contentLength, bytes: 4, upload: true, }; } })() ) ); } finally { await stopHTTPServer(server); } }, 15000); }); describe('download', () => { it('should support download progress capturing', async () => { const server = await startHTTPServer( { rate: 100 * 1024, }, { port: SERVER_PORT } ); try { let content = ''; const count = 10; const chunk = 'test'; const chunkLength = Buffer.byteLength(chunk); const contentLength = count * chunkLength; const readable = stream.Readable.from( (async function* () { let i = count; while (i-- > 0) { await setTimeoutAsync(1100); content += chunk; yield chunk; } })() ); const samples = []; const { data } = await axios.post(`http://localhost:${server.address().port}`, readable, { onDownloadProgress: ({ loaded, total, progress, bytes, download }) => { samples.push({ loaded, total, progress, bytes, download, }); }, headers: { 'Content-Length': contentLength, }, responseType: 'text', maxRedirects: 0, }); assert.strictEqual(data, content); assert.deepStrictEqual( samples, Array.from( (function* () { for (let i = 1; i <= 10; i++) { yield { loaded: chunkLength * i, total: contentLength, progress: (chunkLength * i) / contentLength, bytes: 4, download: true, }; } })() ) ); } finally { await stopHTTPServer(server); } }, 15000); it('should flush final download progress before a streamed response closes', async () => { const chunks = ['test', 'test', 'test', 'test']; const contentLength = chunks.reduce((total, chunk) => total + Buffer.byteLength(chunk), 0); const server = await startHTTPServer( async (req, res) => { res.setHeader('Content-Length', contentLength); for (const chunk of chunks) { res.write(chunk); await setTimeoutAsync(10); } res.end(); }, { port: SERVER_PORT } ); try { const events = []; const { data } = await axios.get(`http://localhost:${server.address().port}`, { responseType: 'stream', onDownloadProgress: ({ loaded }) => { events.push(`progress:${loaded}`); }, maxRedirects: 0, }); await new Promise((resolve, reject) => { data.on('error', reject); data.on('close', () => { events.push('close'); resolve(); }); data.resume(); }); await new Promise((resolve) => setImmediate(resolve)); const finalProgressIndex = events.indexOf(`progress:${contentLength}`); const closeIndex = events.indexOf('close'); assert.ok(finalProgressIndex !== -1, `expected final progress, got ${events.join(', ')}`); assert.ok( finalProgressIndex < closeIndex, `expected final progress before close, got ${events.join(', ')}` ); assert.strictEqual( events[events.length - 1], 'close', `expected no download progress after close, got ${events.join(', ')}` ); } finally { await stopHTTPServer(server); } }, 15000); }); }); describe('Rate limit', () => { it('should support upload rate limit', async () => { const secs = 10; const configRate = 100000; const chunkLength = configRate * secs; const server = await startHTTPServer(); try { const buf = Buffer.alloc(chunkLength).fill('s'); const samples = []; const skip = 4; const compareValues = toleranceRange(50, 50); const { data } = await axios.post(`http://localhost:${server.address().port}`, buf, { onUploadProgress: ({ loaded, total, progress, bytes, rate }) => { samples.push({ loaded, total, progress, bytes, rate, }); }, maxRate: [configRate], responseType: 'text', maxRedirects: 0, }); samples.slice(skip).forEach(({ rate, progress }, i, _samples) => { assert.ok( compareValues(rate, configRate), `Rate sample at index ${i} is out of the expected range (${rate} / ${configRate}) [${_samples .map((sample) => sample.rate) .join(', ')}]` ); const progressTicksRate = 2; const expectedProgress = (i + skip) / secs / progressTicksRate; assert.ok( Math.abs(expectedProgress - progress) < 0.25, `Progress sample at index ${i} is out of the expected range (${progress} / ${expectedProgress}) [${_samples .map((sample) => sample.progress) .join(', ')}]` ); }); assert.strictEqual(data, buf.toString(), 'content corrupted'); } finally { await stopHTTPServer(server); } }, 30000); it('should support download rate limit', async () => { const secs = 10; const configRate = 100000; const chunkLength = configRate * secs; const server = await startHTTPServer(); try { const buf = Buffer.alloc(chunkLength).fill('s'); const samples = []; const skip = 4; const compareValues = toleranceRange(50, 50); const { data } = await axios.post(`http://localhost:${server.address().port}`, buf, { onDownloadProgress: ({ loaded, total, progress, bytes, rate }) => { samples.push({ loaded, total, progress, bytes, rate, }); }, maxRate: [0, configRate], responseType: 'text', maxRedirects: 0, }); samples.slice(skip).forEach(({ rate, progress }, i, _samples) => { assert.ok( compareValues(rate, configRate), `Rate sample at index ${i} is out of the expected range (${rate} / ${configRate}) [${_samples .map((sample) => sample.rate) .join(', ')}]` ); const progressTicksRate = 3; const expectedProgress = (i + skip) / secs / progressTicksRate; assert.ok( Math.abs(expectedProgress - progress) < 0.25, `Progress sample at index ${i} is out of the expected range (${progress} / ${expectedProgress}) [${_samples .map((sample) => sample.progress) .join(', ')}]` ); }); assert.strictEqual(data, buf.toString(), 'content corrupted'); } finally { await stopHTTPServer(server); } }, 30000); }); describe('request aborting', () => { it('should be able to abort the response stream', async () => { const server = await startHTTPServer( { rate: 100000, useBuffering: true, }, { port: SERVER_PORT } ); try { const buf = Buffer.alloc(1024 * 1024); const controller = new AbortController(); const { data } = await axios.post(`http://localhost:${server.address().port}`, buf, { responseType: 'stream', signal: controller.signal, maxRedirects: 0, }); setTimeout(() => { controller.abort(); }, 500); let streamError; data.on('error', (error) => { streamError = error; }); await assert.rejects( new Promise((resolve, reject) => { stream.pipeline(data, devNull(), (error) => { if (error) { reject(error); return; } resolve(); }); }) ); assert.strictEqual(streamError && streamError.code, 'ERR_CANCELED'); } finally { await stopHTTPServer(server); } }); }); it('should properly handle synchronous errors inside the adapter', async () => { await assert.rejects(() => axios.get('http://192.168.0.285'), /Invalid URL/); }); it('should support function as paramsSerializer value', async () => { const server = await startHTTPServer((req, res) => res.end(req.url), { port: SERVER_PORT }); try { const { data } = await axios.post(`http://localhost:${server.address().port}`, 'test', { params: { x: 1, }, paramsSerializer: () => 'foo', maxRedirects: 0, }); assert.strictEqual(data, '/?foo'); } finally { await stopHTTPServer(server); } }); describe('DNS', () => { it('should support a custom DNS lookup function', async () => { const server = await startHTTPServer(SERVER_HANDLER_STREAM_ECHO); const payload = 'test'; let isCalled = false; try { const { data } = await axios.post( `http://fake-name.axios:${server.address().port}`, payload, { lookup: (hostname, opt, cb) => { isCalled = true; cb(null, '127.0.0.1', 4); }, } ); assert.ok(isCalled); assert.strictEqual(data, payload); } finally { await stopHTTPServer(server); } }); it('should support a custom DNS lookup function with address entry passing', async () => { const server = await startHTTPServer(SERVER_HANDLER_STREAM_ECHO); const payload = 'test'; let isCalled = false; try { const { data } = await axios.post( `http://fake-name.axios:${server.address().port}`, payload, { lookup: (hostname, opt, cb) => { isCalled = true; cb(null, { address: '127.0.0.1', family: 4 }); }, } ); assert.ok(isCalled); assert.strictEqual(data, payload); } finally { await stopHTTPServer(server); } }); it('should support a custom DNS lookup function (async)', async () => { const server = await startHTTPServer(SERVER_HANDLER_STREAM_ECHO); const payload = 'test'; let isCalled = false; try { const { data } = await axios.post( `http://fake-name.axios:${server.address().port}`, payload, { lookup: async (hostname, opt) => { isCalled = true; return ['127.0.0.1', 4]; }, } ); assert.ok(isCalled); assert.strictEqual(data, payload); } finally { await stopHTTPServer(server); } }); it('should support a custom DNS lookup function with address entry (async)', async () => { const server = await startHTTPServer(SERVER_HANDLER_STREAM_ECHO); const payload = 'test'; let isCalled = false; try { const { data } = await axios.post( `http://fake-name.axios:${server.address().port}`, payload, { lookup: async (hostname, opt) => { isCalled = true; return { address: '127.0.0.1', family: 4 }; }, } ); assert.ok(isCalled); assert.strictEqual(data, payload); } finally { await stopHTTPServer(server); } }); it('should support a custom DNS lookup function that returns only IP address (async)', async () => { const server = await startHTTPServer(SERVER_HANDLER_STREAM_ECHO); const payload = 'test'; let isCalled = false; try { const { data } = await axios.post( `http://fake-name.axios:${server.address().port}`, payload, { lookup: async (hostname, opt) => { isCalled = true; return '127.0.0.1'; }, } ); assert.ok(isCalled); assert.strictEqual(data, payload); } finally { await stopHTTPServer(server); } }); it('should handle errors', async () => { await assert.rejects(async () => { await axios.get('https://no-such-domain-987654.com', { lookup: (hostname, opt, cb) => { const err = new Error('getaddrinfo ENOTFOUND ' + hostname); err.code = 'ENOTFOUND'; cb(err); }, }); }, /ENOTFOUND/); }); }); describe('JSON', () => { it('should support reviver on JSON.parse', async () => { const server = await startHTTPServer( async (_, res) => { res.end( JSON.stringify({ foo: 'bar', }) ); }, { port: SERVER_PORT } ); try { const { data } = await axios.get(`http://localhost:${server.address().port}`, { parseReviver: (key, value) => { return key === 'foo' ? 'success' : value; }, }); assert.deepStrictEqual(data, { foo: 'success' }); } finally { await stopHTTPServer(server); } }); }); describe('HTTP2', () => { const createHttp2Axios = (baseURL) => axios.create({ baseURL, httpVersion: 2, http2Options: { rejectUnauthorized: false, }, }); it('should merge request http2Options with its instance config', async () => { const http2Axios = createHttp2Axios('https://localhost:8080'); const { data } = await http2Axios.get('/', { http2Options: { foo: 'test', }, adapter: async (config) => { return { data: config.http2Options, }; }, }); assert.deepStrictEqual(data, { rejectUnauthorized: false, foo: 'test', }); }); it('should support http2 transport', async () => { const server = await startHTTPServer( (req, res) => { res.end('OK'); }, { useHTTP2: true, port: SERVER_PORT, } ); try { const localServerURL = `https://localhost:${server.address().port}`; const http2Axios = createHttp2Axios(localServerURL); const { data } = await http2Axios.get(localServerURL); assert.deepStrictEqual(data, 'OK'); } finally { await stopHTTPServer(server); } }); it('should use a custom DNS lookup for HTTP/2 connections', async () => { const server = await startHTTPServer( (req, res) => { res.end('OK'); }, { useHTTP2: true, port: SERVER_PORT, } ); let lookupCalls = 0; try { const http2Axios = createHttp2Axios(`https://custom-host.axios:${server.address().port}`); const { data } = await http2Axios.get('/', { lookup: async () => { lookupCalls++; return '127.0.0.1'; }, }); assert.strictEqual(data, 'OK'); assert.ok(lookupCalls > 0); } finally { await stopHTTPServer(server); } }); it('should reuse an HTTP/2 session with the same custom DNS lookup', async () => { const server = await startHTTPServer( (req, res) => { res.end('OK'); }, { useHTTP2: true, port: SERVER_PORT, } ); let sessions = 0; const lookup = async () => '127.0.0.1'; server.on('session', () => { sessions++; }); try { const http2Axios = axios.create({ baseURL: `https://custom-host.axios:${server.address().port}`, httpVersion: 2, proxy: false, lookup, http2Options: { rejectUnauthorized: false, sessionTimeout: 10000, }, }); await http2Axios.get('/one'); await http2Axios.get('/two'); assert.strictEqual(sessions, 1); } finally { await stopHTTPServer(server); } }); it('should reject when an HTTP/2 request selects a proxy', async () => { const server = await startHTTPServer( (req, res) => { res.end('unexpected'); }, { useHTTP2: true, port: SERVER_PORT, } ); try { const http2Axios = createHttp2Axios(`https://localhost:${server.address().port}`); await assert.rejects( http2Axios.get('/', { proxy: { protocol: 'http', host: '127.0.0.1', port: 1, }, }), (error) => { assert.strictEqual(error.code, AxiosError.ERR_NOT_SUPPORT); assert.match(error.message, /HTTP\/2 requests with a proxy are not supported/); return true; } ); } finally { await stopHTTPServer(server); } }); it('should connect directly for HTTP/2 when only an environment proxy is configured', async () => { const proxyEnvKeys = ['https_proxy', 'HTTPS_PROXY', 'no_proxy', 'NO_PROXY']; const originalProxyEnv = Object.create(null); const server = await startHTTPServer( (req, res) => { res.end('OK'); }, { useHTTP2: true, port: SERVER_PORT, } ); for (const key of proxyEnvKeys) { originalProxyEnv[key] = process.env[key]; } process.env.https_proxy = 'http://127.0.0.1:1'; process.env.HTTPS_PROXY = 'http://127.0.0.1:1'; process.env.no_proxy = ''; process.env.NO_PROXY = ''; try { const http2Axios = createHttp2Axios(`https://localhost:${server.address().port}`); const { data } = await http2Axios.get('/'); assert.strictEqual(data, 'OK'); } finally { for (const key of proxyEnvKeys) { if (originalProxyEnv[key] === undefined) { delete process.env[key]; } else { process.env[key] = originalProxyEnv[key]; } } await stopHTTPServer(server); } }); it('should honor proxy false for HTTP/2 with a proxyEnv agent', async () => { const server = await startHTTPServer( (req, res) => { res.end('OK'); }, { useHTTP2: true, port: SERVER_PORT, } ); try { const http2Axios = createHttp2Axios(`https://localhost:${server.address().port}`); const { data } = await http2Axios.get('/', { proxy: false, httpsAgent: new https.Agent({ proxyEnv: { HTTPS_PROXY: 'http://127.0.0.1:1' }, }), }); assert.strictEqual(data, 'OK'); } finally { await stopHTTPServer(server); } }); it('should connect directly for HTTP/2 when only the selected agent has proxyEnv', async () => { const server = await startHTTPServer( (req, res) => { res.end('OK'); }, { useHTTP2: true, port: SERVER_PORT, } ); try { const http2Axios = createHttp2Axios(`https://localhost:${server.address().port}`); const { data } = await http2Axios.get('/', { httpsAgent: new https.Agent({ proxyEnv: { HTTPS_PROXY: 'http://127.0.0.1:1' }, }), }); assert.strictEqual(data, 'OK'); } finally { await stopHTTPServer(server); } }); it('should reject HTTP/2 connection failures without an uncaught session error', async () => { await assert.rejects( axios.get('https://127.0.0.1:1/', { httpVersion: 2, proxy: false, timeout: 1000, http2Options: { rejectUnauthorized: false, }, }) ); }); it('should support request payload', async () => { const server = await startHTTPServer(null, { useHTTP2: true, port: SERVER_PORT, }); try { const localServerURL = `https://localhost:${server.address().port}`; const http2Axios = createHttp2Axios(localServerURL); const payload = 'DATA'; const { data } = await http2Axios.post(localServerURL, payload); assert.deepStrictEqual(data, payload); } finally { await stopHTTPServer(server); } }); it('should enforce maxBodyLength for HTTP/2 streamed uploads', async () => { let bytesReceived = 0; const server = await startHTTPServer( (req, res) => { req.on('data', (chunk) => { bytesReceived += chunk.length; }); req.on('error', () => {}); req.on('end', () => { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ received: bytesReceived })); }); }, { useHTTP2: true, port: SERVER_PORT, } ); try { const localServerURL = `https://localhost:${server.address().port}`; const http2Axios = createHttp2Axios(localServerURL); const payload = Buffer.alloc(2 * 1024 * 1024, 0x63); const source = stream.Readable.from([payload]); await assert.rejects( http2Axios.post(localServerURL, source, { maxBodyLength: 1024, headers: { 'Content-Type': 'application/octet-stream' }, }), (error) => { assert.strictEqual(error.message, 'Request body larger than maxBodyLength limit'); assert.strictEqual(error.code, AxiosError.ERR_BAD_REQUEST); return true; } ); assert.ok( bytesReceived <= 1024 * 4, `server should not receive full payload; got ${bytesReceived}` ); } finally { if (server.closeAllSessions) { server.closeAllSessions(); } await stopHTTPServer(server); } }); it('should support FormData as a payload', async () => { if (typeof FormData !== 'function') { return; } const server = await startHTTPServer( async (req, res) => { const { fields, files } = await handleFormData(req); res.end( JSON.stringify({ fields, files, }) ); }, { useHTTP2: true, port: SERVER_PORT, } ); try { const localServerURL = `https://localhost:${server.address().port}`; const http2Axios = createHttp2Axios(localServerURL); const form = new FormData(); form.append('x', 'foo'); form.append('y', 'bar'); const { data } = await http2Axios.post(localServerURL, form); assert.deepStrictEqual(data, { fields: { x: ['foo'], y: ['bar'], }, files: {}, }); } finally { await stopHTTPServer(server); } }); describe('response types', () => { const originalData = '{"test": "OK"}'; const fixtures = { text: (value) => assert.strictEqual(value, originalData), arraybuffer: (value) => assert.deepStrictEqual(value, Buffer.from(originalData)), stream: async (value) => assert.deepStrictEqual(await getStream(value), originalData), json: async (value) => assert.deepStrictEqual(value, JSON.parse(originalData)), }; for (const [responseType, assertValue] of Object.entries(fixtures)) { it(`should support ${responseType} response type`, async () => { const server = await startHTTPServer( (req, res) => { res.end(originalData); }, { useHTTP2: true, port: SERVER_PORT, } ); try { const localServerURL = `https://localhost:${server.address().port}`; const http2Axios = createHttp2Axios(localServerURL); const { data } = await http2Axios.get(localServerURL, { responseType, }); await assertValue(data); } finally { await stopHTTPServer(server); } }); } }); it('should support request timeout', async () => { let isAborted = false; let aborted; const promise = new Promise((resolve) => (aborted = resolve)); const server = await startHTTPServer( (req, res) => { setTimeout(() => { res.end('OK'); }, 15000); }, { useHTTP2: true, port: SERVER_PORT, } ); try { const localServerURL = `https://localhost:${server.address().port}`; const http2Axios = createHttp2Axios(localServerURL); server.on('stream', (http2Stream) => { http2Stream.once('aborted', () => { isAborted = true; aborted(); }); }); await assert.rejects(async () => { await http2Axios.get(localServerURL, { timeout: 500, }); }, /timeout/); await promise; assert.ok(isAborted); } finally { await stopHTTPServer(server); } }); it('should support request cancellation', async () => { if (typeof AbortSignal !== 'function' || !AbortSignal.timeout) { return; } let isAborted = false; let aborted; const promise = new Promise((resolve) => (aborted = resolve)); const server = await startHTTPServer( (req, res) => { setTimeout(() => { res.end('OK'); }, 15000); }, { useHTTP2: true, port: SERVER_PORT, } ); try { const localServerURL = `https://localhost:${server.address().port}`; const http2Axios = createHttp2Axios(localServerURL); server.on('stream', (http2Stream) => { http2Stream.once('aborted', () => { isAborted = true; aborted(); }); }); await assert.rejects(async () => { await http2Axios.get(localServerURL, { signal: AbortSignal.timeout(500), }); }, /CanceledError: canceled/); await promise; assert.ok(isAborted); } finally { await stopHTTPServer(server); } }); it('should support stream response cancellation', async () => { let isAborted = false; const source = axios.CancelToken.source(); let aborted; const promise = new Promise((resolve) => (aborted = resolve)); const server = await startHTTPServer( (req, res) => { generateReadable(10000, 100, 100).pipe(res); }, { useHTTP2: true, port: SERVER_PORT, } ); try { const localServerURL = `https://localhost:${server.address().port}`; const http2Axios = createHttp2Axios(localServerURL); server.on('stream', (http2Stream) => { http2Stream.once('aborted', () => { isAborted = true; aborted(); }); }); const { data } = await http2Axios.get(localServerURL, { cancelToken: source.token, responseType: 'stream', }); setTimeout(() => source.cancel()); await assert.rejects( new Promise((resolve, reject) => { stream.pipeline(data, devNull(), (error) => { if (error) { reject(error); return; } resolve(); }); }), /CanceledError: canceled/ ); await promise; assert.ok(isAborted); } finally { await stopHTTPServer(server); } }); describe('session', () => { // HTTP2 session tests are sensitive to cross-test port reuse: when one // test's server is torn down (closeAllSessions destroys h2 sessions), // a follow-up test binding the same port can observe a "Premature // close" on its own stream. Use ephemeral ports (port: 0, the default // from startHTTPServer) and a small retry budget as a backstop. it('should reuse session for the target authority', { retry: 2 }, async () => { const server = await startHTTPServer( (req, res) => { setTimeout(() => res.end('OK'), 1000); }, { useHTTP2: true, } ); try { const localServerURL = `https://localhost:${server.address().port}`; const http2Axios = createHttp2Axios(localServerURL); const [response1, response2] = await Promise.all([ http2Axios.get(localServerURL, { responseType: 'stream', }), http2Axios.get(localServerURL, { responseType: 'stream', }), ]); assert.strictEqual(response1.data.session, response2.data.session); assert.deepStrictEqual( await Promise.all([getStream(response1.data), getStream(response2.data)]), ['OK', 'OK'] ); } finally { await stopHTTPServer(server); } }); it('should use different sessions for different authorities', { retry: 2 }, async () => { const server = await startHTTPServer( (req, res) => { setTimeout(() => { res.end('OK'); }, 2000); }, { useHTTP2: true, } ); const server2 = await startHTTPServer( (req, res) => { setTimeout(() => { res.end('OK'); }, 2000); }, { useHTTP2: true, } ); try { const localServerURL = `https://localhost:${server.address().port}`; const localServerURL2 = `https://localhost:${server2.address().port}`; const http2Axios = createHttp2Axios(localServerURL); const [response1, response2] = await Promise.all([ http2Axios.get(localServerURL, { responseType: 'stream', }), http2Axios.get(localServerURL2, { responseType: 'stream', }), ]); assert.notStrictEqual(response1.data.session, response2.data.session); assert.deepStrictEqual( await Promise.all([getStream(response1.data), getStream(response2.data)]), ['OK', 'OK'] ); } finally { await Promise.all([stopHTTPServer(server), stopHTTPServer(server2)]); } }); it( 'should use different sessions for requests with different http2Options set', { retry: 2 }, async () => { const server = await startHTTPServer( (req, res) => { setTimeout(() => { res.end('OK'); }, 1000); }, { useHTTP2: true, } ); try { const localServerURL = `https://localhost:${server.address().port}`; const http2Axios = createHttp2Axios(localServerURL); const [response1, response2] = await Promise.all([ http2Axios.get(localServerURL, { http2Options: { sessionTimeout: 2000, }, }), http2Axios.get(localServerURL, { http2Options: { sessionTimeout: 4000, }, }), ]); assert.notStrictEqual(response1.request.session, response2.request.session); assert.deepStrictEqual([response1.data, response2.data], ['OK', 'OK']); } finally { await stopHTTPServer(server); } } ); it( 'should use the same session for request with the same resolved http2Options set', { retry: 2 }, async () => { const server = await startHTTPServer( (req, res) => { setTimeout(() => res.end('OK'), 1000); }, { useHTTP2: true, } ); try { const localServerURL = `https://localhost:${server.address().port}`; const http2Axios = createHttp2Axios(localServerURL); const responses = await Promise.all([ http2Axios.get(localServerURL, { responseType: 'stream', }), http2Axios.get(localServerURL, { responseType: 'stream', http2Options: undefined, }), http2Axios.get(localServerURL, { responseType: 'stream', http2Options: {}, }), ]); assert.strictEqual(responses[1].data.session, responses[0].data.session); assert.strictEqual(responses[2].data.session, responses[0].data.session); assert.deepStrictEqual( await Promise.all(responses.map(({ data }) => getStream(data))), ['OK', 'OK', 'OK'] ); } finally { await stopHTTPServer(server); } } ); it( 'should use different sessions after previous session timeout', { retry: 2, timeout: 15000 }, async () => { const server = await startHTTPServer( (req, res) => { setTimeout(() => res.end('OK'), 100); }, { useHTTP2: true, } ); try { const localServerURL = `https://localhost:${server.address().port}`; const http2Axios = createHttp2Axios(localServerURL); const response1 = await http2Axios.get(localServerURL, { responseType: 'stream', http2Options: { sessionTimeout: 1000, }, }); const session1 = response1.data.session; const data1 = await getStream(response1.data); await setTimeoutAsync(5000); const response2 = await http2Axios.get(localServerURL, { responseType: 'stream', http2Options: { sessionTimeout: 1000, }, }); const session2 = response2.data.session; const data2 = await getStream(response2.data); assert.notStrictEqual(session1, session2); assert.strictEqual(data1, 'OK'); assert.strictEqual(data2, 'OK'); } finally { await stopHTTPServer(server); } } ); }); }); it('should not abort stream on settle rejection', async () => { const server = await startHTTPServer( (req, res) => { res.statusCode = 404; res.end('OK'); }, { port: SERVER_PORT } ); try { let error; try { await axios.get(`http://localhost:${server.address().port}`, { responseType: 'stream', }); } catch (err) { error = err; } assert.ok(error, 'request should be rejected'); assert.strictEqual(await getStream(error.response.data), 'OK'); } finally { await stopHTTPServer(server); } }); it('should reject when only the request socket emits an error', async () => { const noop = () => {}; const socket = new EventEmitter(); socket.setKeepAlive = noop; socket.on('error', noop); const transport = { request() { return new (class MockRequest extends EventEmitter { constructor() { super(); this.destroyed = false; } setTimeout() {} write() {} end() { this.emit('socket', socket); setImmediate(() => { socket.emit('error', Object.assign(new Error('write EPIPE'), { code: 'EPIPE' })); }); } destroy(err) { if (this.destroyed) { return; } this.destroyed = true; err && this.emit('error', err); this.emit('close'); } })(); }, }; const error = await Promise.race([ axios.post('http://example.com/', 'test', { transport, maxRedirects: 0, }), setTimeoutAsync(200).then(() => { throw new Error('socket error did not reject the request'); }), ]).catch((err) => err); assert.ok(error instanceof AxiosError); assert.strictEqual(error.code, 'EPIPE'); assert.strictEqual(error.message, 'write EPIPE'); }); describe('keep-alive', () => { it('should not emit MaxListenersExceededWarning under concurrent requests through a pooled keep-alive agent (regression #10780)', async () => { const server = await startHTTPServer( (req, res) => { // Small delay forces concurrent requests to queue on the single pooled socket. setTimeout(() => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('ok'); }, 5); }, { port: SERVER_PORT } ); const warnings = []; const warningHandler = (warning) => { if (warning && warning.name === 'MaxListenersExceededWarning') { warnings.push(warning); } }; process.on('warning', warningHandler); const agent = new http.Agent({ keepAlive: true, maxSockets: 1 }); try { const baseURL = `http://localhost:${server.address().port}`; const CONCURRENCY = 30; const results = await Promise.all( Array.from({ length: CONCURRENCY }, (_, i) => axios.get(`/req-${i}`, { baseURL, httpAgent: agent }) ) ); assert.strictEqual(results.length, CONCURRENCY); for (const r of results) { assert.strictEqual(r.status, 200); assert.strictEqual(r.data, 'ok'); } // Allow any deferred process 'warning' emissions to flush. await setTimeoutAsync(50); assert.strictEqual( warnings.length, 0, `expected no MaxListenersExceededWarning, got ${warnings.length}: ${warnings.map((w) => w.message).join('; ')}` ); // Inspect live sockets on the agent: none should have more than one // axios-installed error listener, regardless of how many requests ran. const allSockets = [] .concat(...Object.values(agent.sockets || {})) .concat(...Object.values(agent.freeSockets || {})); for (const sock of allSockets) { assert.ok( sock.listenerCount('error') <= 2, `socket should have at most a couple of error listeners (agent + axios), got ${sock.listenerCount('error')}` ); } } finally { process.removeListener('warning', warningHandler); agent.destroy(); await stopHTTPServer(server); } }, 30000); it('should not leak memory via retained request closures under a long burst of keep-alive requests (regression #10780)', async () => { // This guards against stage88's report of OOM at ~480k sequential requests: // if the per-request closure leaked, heap would grow linearly. We simulate // a shorter burst and verify retained closures are released (via WeakRef // reachability check after GC, if exposed). if (typeof global.gc !== 'function') { // Skip when GC is not exposed (run with `node --expose-gc`). return; } const server = await startHTTPServer( (req, res) => { res.writeHead(200); res.end('ok'); }, { port: SERVER_PORT } ); const agent = new http.Agent({ keepAlive: true, maxSockets: 4 }); try { const baseURL = `http://localhost:${server.address().port}`; const refs = []; for (let i = 0; i < 200; i += 1) { // eslint-disable-next-line no-await-in-loop const response = await axios.get('/', { baseURL, httpAgent: agent }); refs.push(new WeakRef(response.request)); } // Drop strong refs and force GC. global.gc(); await setTimeoutAsync(10); global.gc(); const retained = refs.filter((r) => r.deref() !== undefined).length; // Some trailing requests may still be referenced in internal buffers. // The fix's correctness: retained count scales with agent socket count, // NOT with request count. A pre-fix leak would keep >>socket count. assert.ok( retained <= 20, `expected most request objects to be collectible after GC; ${retained}/200 retained suggests a closure leak` ); } finally { agent.destroy(); await stopHTTPServer(server); } }, 30000); it('should not fail with "socket hang up" when using timeouts', async () => { const server = await startHTTPServer( async (req, res) => { if (req.url === '/wait') { await new Promise((resolve) => setTimeout(resolve, 5000)); } res.end('ok'); }, { port: SERVER_PORT } ); try { const baseURL = `http://localhost:${server.address().port}`; await axios.get('/1', { baseURL, timeout: 1000 }); await axios.get('/wait', { baseURL, timeout: 0 }); } finally { await stopHTTPServer(server); } }, 15000); it('should install at most one socket error listener across reused keep-alive sockets', async () => { const noop = () => {}; const socket = new EventEmitter(); socket.setKeepAlive = noop; socket.on('error', noop); const baseErrorListenerCount = socket.listenerCount('error'); const transport = { request(_, cb) { return new (class MockRequest extends EventEmitter { constructor() { super(); this.destroyed = false; } setTimeout() {} write() {} end() { this.emit('socket', socket); setImmediate(() => { const response = stream.Readable.from(['ok']); response.statusCode = 200; response.headers = {}; cb(response); this.emit('close'); }); } destroy(err) { if (this.destroyed) { return; } this.destroyed = true; err && this.emit('error', err); this.emit('close'); } })(); }, }; // First request: axios installs its single per-socket listener. await axios.get('http://example.com/first', { transport, maxRedirects: 0, }); await setTimeoutAsync(0); assert.strictEqual( socket.listenerCount('error'), baseErrorListenerCount + 1, 'axios should install exactly one socket error listener' ); // Many subsequent requests reusing the same socket must not add more listeners. for (let i = 0; i < 20; i += 1) { // eslint-disable-next-line no-await-in-loop await axios.get(`http://example.com/next-${i}`, { transport, maxRedirects: 0, }); // eslint-disable-next-line no-await-in-loop await setTimeoutAsync(0); assert.strictEqual( socket.listenerCount('error'), baseErrorListenerCount + 1, 'listener count must stay constant across keep-alive reuse' ); } }); it('should reuse one context-free error listener across different sockets', async () => { const noop = () => {}; const sockets = [new EventEmitter(), new EventEmitter()]; for (const socket of sockets) { socket.setKeepAlive = noop; socket.on('error', noop); } const createdRequests = []; const transport = { request() { const socket = sockets[createdRequests.length]; const req = new (class MockRequest extends EventEmitter { constructor() { super(); this.destroyed = false; } setTimeout() {} write() {} end() { this.emit('socket', socket); } destroy(err) { if (this.destroyed) { return; } this.destroyed = true; err && this.emit('error', err); this.emit('close'); } })(); createdRequests.push(req); return req; }, }; const pending = sockets.map((_, index) => axios .get(`http://example.com/socket-${index}`, { transport, maxRedirects: 0, }) .catch((err) => err) ); await setTimeoutAsync(0); const axiosListeners = sockets.map((socket) => socket.listeners('error').filter((listener) => listener !== noop) ); assert.strictEqual(axiosListeners[0].length, 1); assert.strictEqual(axiosListeners[1].length, 1); assert.strictEqual( axiosListeners[0][0], axiosListeners[1][0], 'different sockets should share the same module-scope listener' ); sockets[0].emit('error', Object.assign(new Error('first socket failed'), { code: 'EPIPE' })); assert.strictEqual(createdRequests[0].destroyed, true); assert.strictEqual(createdRequests[1].destroyed, false); sockets[1].emit( 'error', Object.assign(new Error('second socket failed'), { code: 'ECONNRESET' }) ); const errors = await Promise.all(pending); assert.ok(errors[0] instanceof AxiosError); assert.strictEqual(errors[0].code, 'EPIPE'); assert.ok(errors[1] instanceof AxiosError); assert.strictEqual(errors[1].code, 'ECONNRESET'); assert.strictEqual(createdRequests[1].destroyed, true); }); it('should not accumulate socket error listeners when a pooled socket is reassigned before the previous request closes (regression #10780)', async () => { const noop = () => {}; const socket = new EventEmitter(); socket.setKeepAlive = noop; socket.on('error', noop); const baseErrorListenerCount = socket.listenerCount('error'); // Each request defers its 'close' emission so that the socket is // reassigned to the next request before the previous one closes. // This reproduces the race condition described in #10780. const pendingRequests = []; const transport = { request(_, cb) { const req = new (class MockRequest extends EventEmitter { constructor() { super(); this.destroyed = false; } setTimeout() {} write() {} end() { // Share the single pooled socket across every request. this.emit('socket', socket); setImmediate(() => { const response = stream.Readable.from(['ok']); response.statusCode = 200; response.headers = {}; cb(response); // Intentionally do NOT emit 'close' yet. Collect the req // so close can be emitted later, after other reqs have // already claimed the socket. pendingRequests.push(this); }); } destroy(err) { if (this.destroyed) return; this.destroyed = true; err && this.emit('error', err); this.emit('close'); } })(); return req; }, }; const results = await Promise.all( Array.from({ length: 20 }, (_, i) => axios.get(`http://example.com/concurrent-${i}`, { transport, maxRedirects: 0, }) ) ); assert.strictEqual(results.length, 20); // Critical assertion: despite 20 concurrent requests all claiming the // same pooled socket before any emitted 'close', only ONE axios listener // must be attached. This is the difference between the pre-fix // behaviour (20 listeners, MaxListenersExceededWarning) and the fix. assert.strictEqual( socket.listenerCount('error'), baseErrorListenerCount + 1, `expected a single axios socket error listener under concurrent reuse, got ${socket.listenerCount('error') - baseErrorListenerCount}` ); // Now drain the queued close events. Listener count must still be 1. for (const req of pendingRequests) { req.emit('close'); } await setTimeoutAsync(0); assert.strictEqual( socket.listenerCount('error'), baseErrorListenerCount + 1, 'listener must persist on the socket after requests close (cleanup is per-request ownership, not per-listener removal)' ); }); it('should route a socket error to the currently-active request after the socket has been reassigned', async () => { const noop = () => {}; const socket = new EventEmitter(); socket.setKeepAlive = noop; socket.on('error', noop); const createdReqs = []; // First transport: completes cleanly (emits response then close). const cleanTransport = { request(_, cb) { const emitter = new (class MockRequest extends EventEmitter { constructor() { super(); this.destroyed = false; createdReqs.push(this); } setTimeout() {} write() {} end() { this.emit('socket', socket); setImmediate(() => { const response = stream.Readable.from(['ok']); response.statusCode = 200; response.headers = {}; cb(response); this.emit('close'); }); } destroy(err) { if (this.destroyed) return; this.destroyed = true; err && this.emit('error', err); this.emit('close'); } })(); return emitter; }, }; // Second transport: emits socket error instead of a response. const errorTransport = { request() { const emitter = new (class MockRequest extends EventEmitter { constructor() { super(); this.destroyed = false; createdReqs.push(this); } setTimeout() {} write() {} end() { this.emit('socket', socket); setImmediate(() => { socket.emit('error', Object.assign(new Error('boom'), { code: 'EPIPE' })); }); } destroy(err) { if (this.destroyed) return; this.destroyed = true; err && this.emit('error', err); this.emit('close'); } })(); return emitter; }, }; // First request completes successfully; socket is released. await axios.get('http://example.com/first', { transport: cleanTransport, maxRedirects: 0 }); await setTimeoutAsync(0); const firstReq = createdReqs[0]; assert.ok( firstReq && firstReq.destroyed === false, 'first request must not have been destroyed by a socket error' ); // Stray socket error after first req has closed: must not destroy firstReq. socket.emit('error', new Error('stray error after close')); assert.strictEqual( firstReq.destroyed, false, 'socket error after close must not destroy the old request' ); // Second request claims the socket, then its socket errors. It should reject. const err = await axios .get('http://example.com/second', { transport: errorTransport, maxRedirects: 0 }) .catch((e) => e); assert.ok(err instanceof AxiosError, 'second request should reject with an AxiosError'); assert.strictEqual(err.code, 'EPIPE'); const secondReq = createdReqs[1]; assert.strictEqual( secondReq.destroyed, true, 'second request should be destroyed by its own active socket error' ); }); it('should not throw TypeError when a proxy agent stream does not define setKeepAlive (regression #10908)', async () => { // proxy agents (e.g. agent-base) may provide a generic Duplex stream as // the socket; that stream does not define setKeepAlive. const socket = new stream.Duplex({ read() {}, write(_chunk, _encoding, callback) { callback(); }, }); assert.strictEqual(typeof socket.setKeepAlive, 'undefined'); const transport = { request(_, cb) { return new (class MockRequest extends EventEmitter { constructor() { super(); this.destroyed = false; } setTimeout() {} write() {} end() { this.emit('socket', socket); setImmediate(() => { const response = stream.Readable.from(['ok']); response.statusCode = 200; response.headers = {}; cb(response); this.emit('close'); }); } destroy(err) { if (this.destroyed) return; this.destroyed = true; err && this.emit('error', err); this.emit('close'); } })(); }, }; const result = await axios.get('http://example.com/', { transport, maxRedirects: 0, }); assert.strictEqual(result.status, 200); }); }); describe('redirect listener accumulation', () => { it('should not emit MaxListenersExceededWarning when a single request follows >= 11 redirects', async () => { const REDIRECT_COUNT = 11; const server = await startHTTPServer( (req, res) => { const match = req.url.match(/^\/redirect\/(\d+)$/); if (match) { const n = Number(match[1]); if (n < REDIRECT_COUNT) { res.writeHead(302, { Location: `/redirect/${n + 1}` }); } else { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ redirects: n })); return; } res.end(); return; } res.writeHead(302, { Location: '/redirect/1' }); res.end(); }, { port: SERVER_PORT } ); const warnings = []; const warningHandler = (warning) => { if (warning && warning.name === 'MaxListenersExceededWarning') { warnings.push(warning); } }; process.on('warning', warningHandler); try { const baseURL = `http://localhost:${server.address().port}`; const response = await axios.get('/start', { baseURL, maxRedirects: REDIRECT_COUNT + 5, }); assert.strictEqual(response.status, 200); assert.deepStrictEqual(response.data, { redirects: REDIRECT_COUNT }); // Allow any deferred process 'warning' emissions to flush. await setTimeoutAsync(50); assert.strictEqual( warnings.length, 0, `expected no MaxListenersExceededWarning across ${REDIRECT_COUNT} redirects, got ${warnings.length}: ${warnings.map((w) => w.message).join('; ')}` ); } finally { process.removeListener('warning', warningHandler); await stopHTTPServer(server); } }, 30000); it('should attach at most one close listener to the outer request across a long redirect chain', async () => { const REDIRECT_COUNT = 20; const maxObservedCloseListeners = { value: 0 }; const server = await startHTTPServer( (req, res) => { const match = req.url.match(/^\/r\/(\d+)$/); if (match) { const n = Number(match[1]); if (n < REDIRECT_COUNT) { res.writeHead(302, { Location: `/r/${n + 1}` }); } else { res.writeHead(200); res.end('done'); return; } res.end(); return; } res.writeHead(302, { Location: '/r/1' }); res.end(); }, { port: SERVER_PORT } ); try { const baseURL = `http://localhost:${server.address().port}`; // Patch EventEmitter.prototype.on briefly to observe the peak close-listener // count on any emitter. The outer RedirectableRequest is the only target // that would accumulate listeners under the bug. Other emitters in the // process (server sockets, etc.) will also be observed but are irrelevant // as long as the peak stays within a small bound. const originalOn = EventEmitter.prototype.on; const originalOnce = EventEmitter.prototype.once; function record(eventName) { if (eventName === 'close') { const count = this.listenerCount('close'); if (count > maxObservedCloseListeners.value) { maxObservedCloseListeners.value = count; } } } EventEmitter.prototype.on = function patchedOn(eventName, listener) { const res = originalOn.call(this, eventName, listener); record.call(this, eventName); return res; }; EventEmitter.prototype.once = function patchedOnce(eventName, listener) { const res = originalOnce.call(this, eventName, listener); record.call(this, eventName); return res; }; try { const response = await axios.get('/start', { baseURL, maxRedirects: REDIRECT_COUNT + 5, }); assert.strictEqual(response.status, 200); } finally { EventEmitter.prototype.on = originalOn; EventEmitter.prototype.once = originalOnce; } // Pre-fix: peak would be >= REDIRECT_COUNT (one axios close listener per hop // on the outer RedirectableRequest). Post-fix: axios attaches exactly one // close listener to the outer request; framework internals typically add // a couple more. A generous bound of 10 distinguishes the behaviours. assert.ok( maxObservedCloseListeners.value < 10, `close listener count should stay below 10 across ${REDIRECT_COUNT} redirects, peak was ${maxObservedCloseListeners.value}` ); } finally { await stopHTTPServer(server); } }, 30000); }); describe('option validation', () => { it('should reject invalid address with AxiosError', async () => { const url = 'http://localhost:1/'; const lookup = (hostname, opt, cb) => { cb(null, 12345, 4); }; await assert.rejects(axios.get(url, { lookup }), (error) => { assert.ok(error instanceof AxiosError); assert.strictEqual(error.code, AxiosError.ERR_BAD_OPTION_VALUE); assert.strictEqual(error.message, 'address must be a string'); assert.strictEqual(error.config.url, url); assert.strictEqual(error.config.lookup, lookup); return true; }); }); it('should reject invalid httpVersion with AxiosError', async () => { await assert.rejects( axios.get('http://localhost:1/', { httpVersion: 'abc', }), (error) => { assert.ok(error instanceof AxiosError); assert.strictEqual(error.code, AxiosError.ERR_BAD_OPTION_VALUE); assert.strictEqual(error.message, `Invalid protocol version: 'abc' is not a number`); assert.strictEqual(error.config.httpVersion, 'abc'); return true; } ); }); for (const [type, httpVersion] of [ ['Symbol', Symbol('1')], ['BigInt', 1n], [ 'object with throwing primitive conversion', { [Symbol.toPrimitive]() { throw new TypeError('cannot convert'); }, }, ], ]) { it(`should reject ${type} httpVersion with AxiosError`, async () => { await assert.rejects( axios.get('http://localhost:1/', { httpVersion, }), (error) => { assert.ok(error instanceof AxiosError); assert.strictEqual(error.code, AxiosError.ERR_BAD_OPTION_VALUE); assert.strictEqual(error.message, 'Invalid protocol version: value is not a number'); assert.deepStrictEqual(error.config.httpVersion, httpVersion); return true; } ); }); } it('should reject unsupported httpVersion with AxiosError', async () => { await assert.rejects( axios.get('http://localhost:1/', { httpVersion: 3, }), (error) => { assert.ok(error instanceof AxiosError); assert.strictEqual(error.code, AxiosError.ERR_BAD_OPTION_VALUE); assert.strictEqual(error.message, "Unsupported protocol version '3'"); assert.strictEqual(error.config.httpVersion, 3); return true; } ); }); }); describe('socketPath security', () => { function makeSocketPath() { const pipe = `axios-socketpath-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`; return os.platform() === 'win32' ? `\\\\.\\pipe\\${pipe}` : path.join(os.tmpdir(), `${pipe}.sock`); } function startUnixServer(socketPath, onRequest) { return new Promise((resolveStart, rejectStart) => { const server = http.createServer((req, res) => { onRequest && onRequest(req); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ ok: true, url: req.url })); }); try { fs.unlinkSync(socketPath); } catch (_) { /* noop */ } server.once('error', rejectStart); server.listen(socketPath, () => resolveStart(server)); }); } function stopUnixServer(server, socketPath) { return new Promise((done) => { server.close(() => { try { fs.unlinkSync(socketPath); } catch (_) { /* noop */ } done(); }); }); } it('allows socketPath when no allowedSocketPaths is set (backwards compatible)', async () => { const socketPath = makeSocketPath(); const server = await startUnixServer(socketPath); try { const res = await axios.get('http://localhost/echo', { socketPath }); assert.strictEqual(res.status, 200); assert.strictEqual(res.data.ok, true); } finally { await stopUnixServer(server, socketPath); } }); it('accepts a path-only url when socketPath is set (regression #6611)', async () => { const socketPath = makeSocketPath(); const server = await startUnixServer(socketPath); try { const res = await axios.get('/echo?q=1', { socketPath }); assert.strictEqual(res.status, 200); assert.strictEqual(res.data.ok, true); assert.strictEqual(res.data.url, '/echo?q=1'); } finally { await stopUnixServer(server, socketPath); } }); it('accepts a path-only url when socketPath matches allowedSocketPaths', async () => { const socketPath = makeSocketPath(); const server = await startUnixServer(socketPath); try { const res = await axios.get('/echo?q=1', { socketPath, allowedSocketPaths: [socketPath], }); assert.strictEqual(res.status, 200); assert.strictEqual(res.data.ok, true); assert.strictEqual(res.data.url, '/echo?q=1'); } finally { await stopUnixServer(server, socketPath); } }); it('ignores a prototype-polluted socketPath (security, regression #6611)', async () => { const socketPath = makeSocketPath(); let requestCount = 0; const server = await startUnixServer(socketPath, () => { requestCount += 1; }); // Pollute the prototype so `socketPath` is visible via the chain but is // NOT an own property of the request config. Object.prototype.socketPath = socketPath; try { // With no own socketPath, the polluted prototype value must not be // honored: the path-only url gets no synthetic base and the request is // never routed to the (attacker-controlled) socket, so it rejects // instead of silently connecting. await assert.rejects(axios.get('/echo?q=1'), (err) => { assert.ok(err instanceof Error); assert.strictEqual(err.code, AxiosError.ERR_INVALID_URL); return true; }); assert.strictEqual(requestCount, 0); } finally { delete Object.prototype.socketPath; await stopUnixServer(server, socketPath); } }); it('allows socketPath when it matches an allowedSocketPaths string', async () => { const socketPath = makeSocketPath(); const server = await startUnixServer(socketPath); try { const res = await axios.get('http://localhost/echo', { socketPath, allowedSocketPaths: socketPath, }); assert.strictEqual(res.status, 200); } finally { await stopUnixServer(server, socketPath); } }); it('allows socketPath when it matches an entry in allowedSocketPaths array', async () => { const socketPath = makeSocketPath(); const server = await startUnixServer(socketPath); try { const res = await axios.get('http://localhost/echo', { socketPath, allowedSocketPaths: ['/var/run/does-not-exist.sock', socketPath], }); assert.strictEqual(res.status, 200); } finally { await stopUnixServer(server, socketPath); } }); it('rejects socketPath not in allowedSocketPaths', async () => { await assert.rejects( axios.get('http://localhost/echo', { socketPath: '/var/run/docker.sock', allowedSocketPaths: ['/tmp/allowed.sock'], }), (err) => { assert.ok(err instanceof AxiosError); assert.strictEqual(err.code, AxiosError.ERR_BAD_OPTION_VALUE); assert.match(err.message, /allowedSocketPaths/); return true; } ); }); it('rejects socketPath attempting path traversal that escapes allowlist', async () => { const allowedDir = path.join(os.tmpdir(), 'axios-allowed'); const allowed = path.join(allowedDir, 'app.sock'); await assert.rejects( axios.get('http://localhost/echo', { socketPath: path.join(allowedDir, '..', 'other.sock'), allowedSocketPaths: [allowed], }), (err) => { assert.strictEqual(err.code, AxiosError.ERR_BAD_OPTION_VALUE); return true; } ); }); it('treats relative and absolute allowedSocketPaths entries equivalently', async () => { const socketPath = makeSocketPath(); const server = await startUnixServer(socketPath); try { const relative = path.relative(process.cwd(), socketPath); const res = await axios.get('http://localhost/echo', { socketPath, allowedSocketPaths: [relative], }); assert.strictEqual(res.status, 200); } finally { await stopUnixServer(server, socketPath); } }); it('rejects non-string socketPath', async () => { await assert.rejects(axios.get('http://localhost/echo', { socketPath: 12345 }), (err) => { assert.ok(err instanceof AxiosError); assert.strictEqual(err.code, AxiosError.ERR_BAD_OPTION_VALUE); assert.match(err.message, /socketPath must be a string/); return true; }); }); it('empty allowedSocketPaths array blocks all socketPath values', async () => { await assert.rejects( axios.get('http://localhost/echo', { socketPath: '/tmp/anything.sock', allowedSocketPaths: [], }), (err) => { assert.strictEqual(err.code, AxiosError.ERR_BAD_OPTION_VALUE); return true; } ); }); }); }); axios-axios-aa54a5e/tests/unit/adapters/http2Session.test.js000066400000000000000000000037531526660636600243420ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import getStream from 'get-stream'; import axios from '../../../index.js'; import { startHTTPServer, stopHTTPServer } from '../../setup/server.js'; describe('HTTP/2 GOAWAY session handling', () => { it('opens a new connection inside the GOAWAY callback while the old response drains', async () => { let firstResponse; let serverSession; let connections = 0; const clientSessions = new Set(); const server = await startHTTPServer( (req, res) => { if (req.url === '/first') { firstResponse = res; serverSession = req.stream.session; res.write('start-'); } else { res.end('next'); } }, { useHTTP2: true } ); server.on('session', () => { connections++; }); try { const client = axios.create({ baseURL: `https://localhost:${server.address().port}`, httpVersion: 2, proxy: false, responseType: 'stream', http2Options: { rejectUnauthorized: false }, }); const first = await client.get('/first'); const firstSession = first.data.session; clientSessions.add(firstSession); const nextResponse = new Promise((resolve, reject) => { firstSession.once('goaway', () => { // Request synchronously, before Node finishes processing GOAWAY. client.get('/next').then(resolve, reject); }); }); serverSession.goaway(); const second = await nextResponse; clientSessions.add(second.data.session); assert.notStrictEqual(second.data.session, firstSession); assert.strictEqual(connections, 2); assert.strictEqual(await getStream(second.data), 'next'); firstResponse.end('end'); assert.strictEqual(await getStream(first.data), 'start-end'); } finally { for (const session of clientSessions) { session.destroy(); } await stopHTTPServer(server); } }); }); axios-axios-aa54a5e/tests/unit/adapters/key.pem000066400000000000000000000032131526660636600217030ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEA+joLfff3WdFIGwrlVdUWfZ9vDr9w86yQpI6Xq0KF6JiKtxa+ EVCNWtq8Ht5rZd1MxNSFWVb1jxnRLBEB8F3wLD/4xRk8CNxw8joXKsxUEirI76iV mJDjm/tq90gaxZUWVtPZP95Qvocdl7brhuvLuF4J9wJVL5tr6ZYAje3omHBnjcFb n9nh3Tqk0U6omAmS4k/BrXWDA1zq8LBmbP41CnfVNGq/5asDIZsNO8s5VQzNjFjN rcqv2rEO0n+77qDBbqALQydxPmytZP+0jYXcj+EaJ4clApFhvps6Cg0taoF66E4k Eg1JLALzgk3I/ATSE3nvi3J/RWkyjSYiolPavwIDAQABAoIBAEbMi5ndwjfAlkVI hPEPNKjgpnymwB/CEL7utY04akkQeBcrsSWXBBfT0exuBDczMVhzxTMs/pe5t0xf l4vaGG18wDeMV0cukCqJMyrh21u0jVv5+DHNtQjaTz6eQSzsbQCuOkbu8SuncUEO +X8YUnDc8rbYCyBIOnVCAvAlg201uW0G5G9NEwJOu6cAKMKkogdHqv+FRX96C5hm gtbGEzpGV2vVClgMwMcX49ucluZvqLvit/yehNVd0VOtW/kuLup4R6q0abHRapDd 95rJAhPvar4mzP+UgJrGQ9hozqhizDthBjnsmGeMBUiBCkay7OXIZpvLoCpQkti1 WIWuikkCgYEA/oZqq71RT1nPuI7rlcjx3AeWe2EUQtKhQMJBiPx5eLLP6gII8+v2 pD1qlmJM2eyIK0lzuskLIulTAA5Z+ejORDbvmn/DdT0CSvdrUFrcvnrRQnt2M5M2 9VDRp6nvPE0H4kRZJrtITyLn0dv5ABf2L32i4dPCMePjKjSUygJSHrsCgYEA+61A cIqch/lrQTk8hG7Y6p0EJzSInFVaKuZoMYpLhlDQcVvSDIQbGgRAN6BKTdxeQ+tK hSxBSm2mze11aHig8GBGgdBFLaJOZRo6G+2fl+s1t1FCHfsaFhHwheZJONHMpKKd Qm/7L/V35QV9YG0lPZ01TM6d5lXuKsmUNvBJTc0CgYASYajAgGqn3WeX/5JZ/eoh ptaiUG+DJ+0HXUAYYYtwQRGs57q3yvnEAL963tyH/IIVBjf6bFyGh+07ms26s6p5 2LHTKZj3FZHd0iKI6hb5FquYLoxpyx7z9oM9pZMmerWwDJmXp3zgYjf1uvovnItm AJ/LyVxD+B5GxQdd028U0wKBgG4OllZglxDzJk7wa6FyI9N89Fr8oxzSSkrmVPwN APfskSpxP8qPXpai8z4gDz47NtG2q/DOqIKWrtHwnF4iGibjwxFzdTz+dA/MR0r9 P8QcbHIMy7/2lbK/B5JWYQDC5h28qs5pz8tqKZLyMqCfOiDWhX9f/zbBrxPw8KqR q0ylAoGAL/0kemA/Tmxpwmp0S0oCqnA4gbCgS7qnApxB09xTewc/tuvraXc3Mzea EvqDXLXK0R7O4E3vo0Mr23SodRVlFPevsmUUJLPJMJcxdfnSJgX+qE/UC8Ux+UMi eYufYRDYSslfL2rt9D7abnnbqSfsHymJKukWpElIgJTklQUru4k= -----END RSA PRIVATE KEY----- axios-axios-aa54a5e/tests/unit/adapters/responseDuration.test.js000066400000000000000000000041451526660636600252750ustar00rootroot00000000000000import { afterEach, describe, it, vi } from 'vitest'; import assert from 'node:assert/strict'; import axios from '../../../index.js'; import { startHTTPServer, stopHTTPServer } from '../../setup/server.js'; describe.each(['http', 'fetch'])('%s response duration', (adapter) => { afterEach(() => { vi.restoreAllMocks(); }); it.each([200, 500])('times a buffered response with status %s', async (status) => { let now = 1000; const server = await startHTTPServer((_req, res) => { now += 25; res.writeHead(status, { 'Content-Type': 'application/json' }); res.end('{"ok":true}'); }); try { vi.spyOn(Date, 'now').mockImplementation(() => now); const result = await axios .get(`http://localhost:${server.address().port}`, { adapter, proxy: false, }) .catch((error) => error); assert.equal(axios.isAxiosError(result), status === 500); const response = status === 500 ? result.response : result; assert.equal(response.duration, 25); assert.deepEqual(response.data, { ok: true }); } finally { await stopHTTPServer(server); } }); it('stops timing when the response stream is available', async () => { let now = 1000; let serverResponse; const server = await startHTTPServer((_req, res) => { serverResponse = res; now += 25; res.writeHead(200); res.write('first'); }); try { vi.spyOn(Date, 'now').mockImplementation(() => now); const response = await axios.get(`http://localhost:${server.address().port}`, { adapter, proxy: false, responseType: 'stream', }); assert.equal(serverResponse.writableEnded, false); assert.equal(response.duration, 25); now += 75; serverResponse.end('second'); const chunks = []; for await (const chunk of response.data) chunks.push(Buffer.from(chunk)); assert.equal(Buffer.concat(chunks).toString(), 'firstsecond'); assert.equal(response.duration, 25); } finally { serverResponse?.end(); await stopHTTPServer(server); } }); }); axios-axios-aa54a5e/tests/unit/adapters/routingConsistency.test.js000066400000000000000000000265051526660636600256460ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import http from 'http'; import https from 'https'; import net from 'net'; import fs from 'fs'; import axios from '../../../index.js'; import shouldBypassProxy from '../../../lib/helpers/shouldBypassProxy.js'; import { __getDirectAgent as getDirectAgent, __isNodeNativeEnvProxySupported as nativeProxySupported, } from '../../../lib/adapters/http.js'; function listen(server) { return new Promise(function (resolve) { server.listen(0, '127.0.0.1', function () { resolve(server.address().port); }); }); } function close(server) { return new Promise(function (resolve) { server.close(resolve); }); } function lookup(hostname, options, callback) { if (options && options.all) callback(null, [{ address: '127.0.0.1', family: 4 }]); else callback(null, '127.0.0.1', 4); } describe('HTTP routing consistency', function () { it.skipIf(!nativeProxySupported())( 'honors direct HTTPS requests with native proxy agents', async function () { var targets = []; var sockets = []; var port; var origin = https.createServer( { key: fs.readFileSync(new URL('./key.pem', import.meta.url)), cert: fs.readFileSync(new URL('./cert.pem', import.meta.url)), }, function (req, res) { res.end('direct'); } ); var proxy = http.createServer(); proxy.on('connect', function (req, socket, head) { targets.push(req.url); var upstream = net.connect(port, '127.0.0.1', function () { socket.write('HTTP/1.1 200 Connection Established\r\n\r\n'); if (head.length) upstream.write(head); socket.pipe(upstream); upstream.pipe(socket); }); sockets.push(socket, upstream); upstream.on('error', function () { socket.destroy(); }); socket.on('error', function () { upstream.destroy(); }); }); var agent; try { port = await listen(origin); var proxyPort = await listen(proxy); agent = new https.Agent({ rejectUnauthorized: false, proxyEnv: { HTTPS_PROXY: 'http://127.0.0.1:' + proxyPort, NO_PROXY: '' }, }); var target = 'https://127.0.0.1:' + port + '/'; var control = await axios.get(target, { httpsAgent: agent, timeout: 2000 }); assert.strictEqual(control.data, 'direct'); assert.strictEqual(targets.length, 1); for (var redirects = 0; redirects < 2; redirects++) { var response = await axios.get(target, { proxy: false, httpsAgent: agent, maxRedirects: redirects ? 5 : 0, timeout: 2000, }); assert.strictEqual(response.data, 'direct'); } assert.strictEqual(targets.length, 1); } finally { if (agent) { agent.destroy(); } sockets.forEach(function (socket) { socket.destroy(); }); await close(proxy); await close(origin); } } ); it('accepts subclasses with native proxy options for direct requests', function () { function CustomAgent() { http.Agent.call(this, { proxyEnv: { HTTP_PROXY: 'http://127.0.0.1:1' } }); } Object.setPrototypeOf(CustomAgent.prototype, http.Agent.prototype); var agent = new CustomAgent(); try { var direct = getDirectAgent(agent, http); if (nativeProxySupported()) { assert.notStrictEqual(direct, agent); assert.strictEqual(direct.options.proxyEnv, undefined); } else { assert.strictEqual(direct, agent); } } finally { agent.destroy(); } }); it('restores the configured HTTPS agent when a redirect bypasses the proxy', async function () { var keys = ['https_proxy', 'HTTPS_PROXY', 'no_proxy', 'NO_PROXY']; var saved = {}; keys.forEach(function (key) { saved[key] = process.env[key]; delete process.env[key]; }); var targets = []; var sockets = []; var port; var origin = https.createServer( { key: fs.readFileSync(new URL('./key.pem', import.meta.url)), cert: fs.readFileSync(new URL('./cert.pem', import.meta.url)), }, function (req, res) { if (req.url === '/start') { res.writeHead(302, { Location: 'https://direct.example.invalid:' + port + '/end' }); res.end(); } else { res.end('direct'); } } ); var proxy = http.createServer(); proxy.on('connect', function (req, socket, head) { targets.push(req.url); var upstream = net.connect(port, '127.0.0.1', function () { socket.write('HTTP/1.1 200 Connection Established\r\n\r\n'); if (head.length) upstream.write(head); upstream.pipe(socket); socket.pipe(upstream); }); sockets.push(socket, upstream); upstream.on('error', function () { socket.destroy(); }); socket.on('error', function () { upstream.destroy(); }); }); var agent = new https.Agent({ rejectUnauthorized: false, lookup: lookup }); try { port = await listen(origin); var proxyPort = await listen(proxy); process.env.https_proxy = 'http://127.0.0.1:' + proxyPort; process.env.no_proxy = 'direct.example.invalid'; var response = await axios.get('https://first.example.invalid:' + port + '/start', { httpsAgent: agent, timeout: 2000, }); assert.strictEqual(response.data, 'direct'); assert.deepStrictEqual(targets, ['first.example.invalid:' + port]); } finally { keys.forEach(function (key) { if (saved[key] === undefined) delete process.env[key]; else process.env[key] = saved[key]; }); agent.destroy(); sockets.forEach(function (socket) { socket.destroy(); }); await close(proxy); await close(origin); } }); [false, true].forEach(function (changeOrigin) { it( 'handles request headers on ' + (changeOrigin ? 'child-host' : 'same-origin') + ' redirects', async function () { var received; var port; var server = http.createServer(function (req, res) { if (req.url === '/start') { res.writeHead(302, { Location: 'http://' + (changeOrigin ? 'child.' : '') + 'api.example.invalid:' + port + '/end', }); res.end(); } else { received = req.headers; res.end('ok'); } }); var agent = new http.Agent({ lookup: lookup }); try { port = await listen(server); await axios.get('http://api.example.invalid:' + port + '/start', { proxy: false, httpAgent: agent, headers: { Authorization: 'Bearer example', Cookie: 'session=example' }, timeout: 2000, }); assert.strictEqual(received.authorization, changeOrigin ? undefined : 'Bearer example'); assert.strictEqual(received.cookie, changeOrigin ? undefined : 'session=example'); } finally { agent.destroy(); await close(server); } } ); }); it.skipIf(!nativeProxySupported())( 'honors direct requests with native proxy agents and redirects', async function () { var proxyHits = 0; var directHeaders; var proxy = http.createServer(function (req, res) { proxyHits++; res.end('proxied'); }); var origin = http.createServer(function (req, res) { if (req.url === '/start') { res.writeHead(302, { Location: '/end' }); res.end(); } else { directHeaders = req.headers; res.end('direct'); } }); var originalAgent = http.globalAgent; var agent; try { var proxyPort = await listen(proxy); var port = await listen(origin); agent = new http.Agent({ keepAlive: true, proxyEnv: { HTTP_PROXY: 'http://127.0.0.1:' + proxyPort, NO_PROXY: '' }, }); http.globalAgent = agent; var url = 'http://127.0.0.1:' + port; var control = await axios.get(url + '/end', { httpAgent: agent, timeout: 2000 }); assert.strictEqual(control.data, 'proxied'); assert.strictEqual(proxyHits, 1); for (var supplied = 0; supplied < 2; supplied++) { for (var redirects = 0; redirects < 2; redirects++) { var response = await axios.get(url + (redirects ? '/start' : '/end'), { proxy: false, httpAgent: supplied ? agent : undefined, maxRedirects: redirects ? 5 : 0, headers: { Authorization: 'Bearer example' }, timeout: 2000, }); assert.strictEqual(response.data, 'direct'); assert.strictEqual(directHeaders.authorization, 'Bearer example'); } } assert.strictEqual(proxyHits, 1); assert.ok(agent.options.proxyEnv.HTTP_PROXY); } finally { http.globalAgent = originalAgent; if (agent) { agent.destroy(); } await close(proxy); await close(origin); } } ); it.skipIf(!nativeProxySupported())( 'keeps direct agent options and reuses their connection pool', function () { var agent = new https.Agent({ ca: 'example-ca', rejectUnauthorized: false, maxSockets: 3, proxyEnv: { HTTPS_PROXY: 'http://127.0.0.1:1' }, }); try { var direct = getDirectAgent(agent, https); assert.notStrictEqual(direct, agent); assert.strictEqual(getDirectAgent(agent, https), direct); assert.strictEqual(direct.options.ca, 'example-ca'); assert.strictEqual(direct.options.rejectUnauthorized, false); assert.strictEqual(direct.maxSockets, 3); assert.strictEqual(direct.options.proxyEnv, undefined); direct.destroy(); } finally { agent.destroy(); } } ); it('matches equivalent host spellings in bypass entries', function () { var original = {}; var keys = ['no_proxy', 'NO_PROXY', 'npm_config_no_proxy', 'NPM_CONFIG_NO_PROXY']; keys.forEach(function (key) { original[key] = process.env[key]; delete process.env[key]; }); try { [ ['bücher.example', 'http://xn--bcher-kva.example/', true], ['xn--bcher-kva.example', 'http://bücher.example/', true], ['.bücher.example', 'http://child.xn--bcher-kva.example/', true], ['*.bücher.example:8080', 'http://child.bücher.example:8080/', true], ['*.bücher.example:8080', 'http://child.bücher.example:8081/', false], ['bücher.example', 'http://other.example/', false], ['[2001:0db8:0000:0000:0000:0000:0000:0001]', 'http://[2001:db8::1]/', true], ['2001:db8:0:0:0:0:0:1', 'http://[2001:db8::1]/', true], ['[2001:db8:0:0:0:0:0:1]:8080', 'http://[2001:db8::1]:8081/', false], ['[2001:db8::1]', 'http://[2001:db8::2]/', false], ].forEach(function (example) { process.env.no_proxy = example[0]; assert.strictEqual(shouldBypassProxy(example[1]), example[2], example[0]); }); } finally { keys.forEach(function (key) { if (original[key] === undefined) delete process.env[key]; else process.env[key] = original[key]; }); } }); }); axios-axios-aa54a5e/tests/unit/adapters/runtimeConsistency.test.js000066400000000000000000000141071526660636600256350ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import { runModuleInChildProcess } from '../../setup/runModuleInChildProcess.js'; import { AbortController } from 'abortcontroller-polyfill/dist/cjs-ponyfill.js'; import axios from '../../../index.js'; import composeSignals from '../../../lib/helpers/composeSignals.js'; import { getFetch } from '../../../lib/adapters/fetch.js'; import estimateDataURLDecodedBytes from '../../../lib/helpers/estimateDataURLDecodedBytes.js'; describe('runtime option consistency', function () { ['data:', 'DATA:', 'DaTa:', ' \tDATA:', 'd\na\rt\ta:'].forEach(function (scheme) { it('checks the size of ' + JSON.stringify(scheme) + ' URLs before fetch', async function () { var calls = 0; var fetch = function () { calls++; return Promise.resolve(new Response('unexpected')); }; await assert.rejects( axios.get(scheme + 'text/plain,' + 'a'.repeat(64), { adapter: 'fetch', maxContentLength: 16, env: { fetch: fetch }, }), function (error) { return error.code === 'ERR_BAD_RESPONSE'; } ); assert.strictEqual(calls, 0); }); }); it('estimates case-variant data URL payloads consistently', function () { assert.strictEqual(estimateDataURLDecodedBytes('DATA:text/plain;base64,YWJjZA=='), 4); assert.strictEqual(estimateDataURLDecodedBytes('DaTa:text/plain,%41%42'), 2); }); it.skipIf(typeof fetch !== 'function')( 'retains a permitted case-variant data response', async function () { var response = await axios.get('DATA:text/plain,ok', { adapter: 'fetch', maxContentLength: 2, }); assert.strictEqual(response.data, 'ok'); } ); it.skipIf(typeof fetch !== 'function' || typeof Response !== 'function')( 'selects fetch implementations only from own environment entries', async function () { var inheritedCalls = 0; var ownCalls = 0; var env = { Request: null, Response: null }; Object.defineProperty(Object.prototype, 'fetch', { configurable: true, writable: true, value: function () { inheritedCalls++; return Promise.resolve(new Response('inherited')); }, }); try { var adapter = getFetch({ env: env }); var response = await axios.get('data:text/plain,ok', { adapter: adapter }); assert.strictEqual(response.data, 'ok'); assert.strictEqual(inheritedCalls, 0); var ownResponse = await axios.get('data:text/plain,unused', { adapter: 'fetch', env: { fetch: function () { ownCalls++; return Promise.resolve(new Response('own')); }, }, }); assert.strictEqual(ownResponse.data, 'own'); assert.strictEqual(ownCalls, 1); } finally { delete Object.prototype.fetch; } assert.strictEqual(getFetch({ env: env }), getFetch({ env: env })); } ); it('ignores a shared unsubscribe member during signal cleanup', async function () { var controller = new AbortController(); var calls = 0; Object.defineProperty(Object.prototype, 'unsubscribe', { configurable: true, writable: true, value: function () { calls++; }, }); try { var signal = composeSignals([controller.signal]); signal.unsubscribe(); await new Promise(function (resolve) { setImmediate(resolve); }); assert.strictEqual(calls, 0); controller.abort(); assert.strictEqual(signal.aborted, false); } finally { delete Object.prototype.unsubscribe; } }); it('retains an explicitly supplied signal cleanup method', async function () { var controller = new AbortController(); var calls = 0; controller.signal.unsubscribe = function (listener) { calls++; this.removeEventListener('abort', listener); }; var signal = composeSignals([controller.signal]); signal.unsubscribe(); signal.unsubscribe(); await new Promise(function (resolve) { setImmediate(resolve); }); assert.strictEqual(calls, 1); }); it('uses own absolute URL defaults', async function () { var client = axios.create({ baseURL: 'http://base.example.invalid/api/' }); var adapter = function (config) { return Promise.resolve({ data: client.getUri(config), status: 200, headers: {}, config: config, }); }; Object.defineProperty(Object.prototype, 'allowAbsoluteUrls', { configurable: true, writable: true, value: false, }); try { var response = await client.get('http://other.example.invalid/resource', { adapter: adapter, }); assert.strictEqual(response.data, 'http://other.example.invalid/resource'); var explicit = await client.get('http://other.example.invalid/resource', { adapter: adapter, allowAbsoluteUrls: false, }); assert.strictEqual( explicit.data, 'http://base.example.invalid/api/http://other.example.invalid/resource' ); } finally { delete Object.prototype.allowAbsoluteUrls; } }); [0.5, 1, 1.5].forEach(function (rate) { it('services a timer after the leading byte at a rate of ' + rate, function () { // This checks event-loop responsiveness before the first 500ms window // rolls over. Only the leading byte should be emitted before cleanup. var moduleURL = new URL('../../../lib/helpers/AxiosTransformStream.js', import.meta.url).href; var source = 'import Transform from ' + JSON.stringify(moduleURL) + ';' + 'var stream = new Transform({maxRate: ' + rate + '});' + 'var bytes = 0; stream.on("data", function(chunk) {bytes += chunk.length;});' + 'stream.end(Buffer.alloc(101));' + 'setTimeout(function() { stream.destroy(); console.log(bytes); }, 30);'; return runModuleInChildProcess(source).then(function (stdout) { assert.strictEqual(Number(stdout.trim()), 1); }); }); }); }); axios-axios-aa54a5e/tests/unit/adapters/xhr.test.js000066400000000000000000000332101526660636600225250ustar00rootroot00000000000000import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; // The xhr adapter fixes `isXHRAdapterSupported` and `platform.origin` at module load, // so each case stubs the globals first and imports axios into a fresh module registry. // The stub fires only readystatechange and loadend with status 0, which is both the // Firefox 152 navigation-cancel shape (https://bugzilla.mozilla.org/show_bug.cgi?id=1505389) // and the shape of a successful file: read in an environment without responseURL. // Use native dispatch semantics, including removal during event delivery. const $listeners = Symbol('listeners'); class StubEventTarget extends EventTarget { constructor() { super(); this[$listeners] = {}; } addEventListener(type, fn) { (this[$listeners][type] || (this[$listeners][type] = new Set())).add(fn); super.addEventListener(type, fn); } removeEventListener(type, fn) { this[$listeners][type] && this[$listeners][type].delete(fn); super.removeEventListener(type, fn); } listenerCount(type) { return this[$listeners][type] ? this[$listeners][type].size : 0; } dispatch(type, init) { this.dispatchEvent(Object.assign(new Event(type), init)); } } let lastInstance; class StubXMLHttpRequest extends StubEventTarget { constructor() { super(); lastInstance = this; this.readyState = 0; this.status = 0; this.statusText = ''; this.responseText = ''; this.response = ''; this.timeout = 0; this.onloadend = null; this.onabort = null; this.onerror = null; this.ontimeout = null; this.onreadystatechange = null; this.upload = new StubEventTarget(); this.aborted = false; } open() {} setRequestHeader() {} getAllResponseHeaders() { return ''; } abort() { this.aborted = true; if (this.readyState > 0 && this.readyState < 4) { this.readyState = 4; this.status = 0; this.onabort && this.onabort(); this.onloadend && this.onloadend(); } this.readyState = 0; } // Complete explicitly so tests can queue progress before settlement. respondWith({ status = 200, responseText = '' } = {}) { this.readyState = 4; this.status = status; this.responseText = responseText; this.response = responseText; this.onreadystatechange && this.onreadystatechange(); this.onloadend && this.onloadend(); } send() { setTimeout(() => { this.readyState = 4; this.onreadystatechange && this.onreadystatechange(); this.onloadend && this.onloadend(); }); } } async function importAxiosForPage(originHref) { vi.resetModules(); vi.stubGlobal('window', { location: { href: originHref } }); vi.stubGlobal('document', { cookie: '' }); vi.stubGlobal('XMLHttpRequest', StubXMLHttpRequest); const { default: axios } = await import('../../../index.js'); return axios; } describe('xhr adapter status 0 handling', () => { afterEach(() => { vi.unstubAllGlobals(); vi.resetModules(); }); it('should resolve a relative file: read when responseURL is unavailable', async () => { const axios = await importAxiosForPage('file:///app/index.html'); const response = await axios.get('data.json', { adapter: 'xhr' }); expect(response.status).toBe(0); }); it('should reject with ECONNABORTED for a relative request from an http page', async () => { const axios = await importAxiosForPage('http://localhost/index.html'); const reason = await axios.get('data.json', { adapter: 'xhr' }).catch((error) => error); expect(reason).toBeInstanceOf(axios.AxiosError); expect(reason.code).toBe(axios.AxiosError.ECONNABORTED); expect(reason.message).toBe('Request aborted'); }); it('should reject before validateStatus can accept a status 0 response', async () => { const axios = await importAxiosForPage('http://localhost/index.html'); const validateStatus = vi.fn(() => true); const reason = await axios .get('data.json', { adapter: 'xhr', validateStatus }) .catch((error) => error); expect(reason).toBeInstanceOf(axios.AxiosError); expect(reason.code).toBe(axios.AxiosError.ECONNABORTED); expect(validateStatus).not.toHaveBeenCalled(); }); it('should resolve an absolute file: read from an http page when responseURL is unavailable', async () => { const axios = await importAxiosForPage('http://localhost/index.html'); const response = await axios.get('file:///app/data.json', { adapter: 'xhr' }); expect(response.status).toBe(0); }); it('should reject with ECONNABORTED for an absolute http request from a file: page', async () => { const axios = await importAxiosForPage('file:///app/index.html'); const reason = await axios .get('https://api.example.com/things', { adapter: 'xhr' }) .catch((error) => error); expect(reason).toBeInstanceOf(axios.AxiosError); expect(reason.code).toBe(axios.AxiosError.ECONNABORTED); }); it.each([ ['an ASCII space', ' https://api.example.com/things'], ['a C0 control', '\u0000https://api.example.com/things'], ])( 'should reject an absolute http request from a file: page when prefixed by %s', async (description, url) => { const axios = await importAxiosForPage('file:///app/index.html'); const reason = await axios.get(url, { adapter: 'xhr' }).catch((error) => error); expect(reason).toBeInstanceOf(axios.AxiosError); expect(reason.code).toBe(axios.AxiosError.ECONNABORTED); } ); }); describe('xhr adapter progress listener cleanup', () => { let axios, controller, source, subscribe, unsubscribe, addListener, removeListener; beforeEach(async () => { vi.useFakeTimers(); axios = await importAxiosForPage('http://localhost/index.html'); // Completion is controlled by each test, with no stray automatic loadend timer. vi.spyOn(StubXMLHttpRequest.prototype, 'send').mockImplementation(function () { this.readyState = 2; }); controller = new AbortController(); source = axios.CancelToken.source(); subscribe = vi.spyOn(source.token, 'subscribe'); unsubscribe = vi.spyOn(source.token, 'unsubscribe'); addListener = vi.spyOn(controller.signal, 'addEventListener'); removeListener = vi.spyOn(controller.signal, 'removeEventListener'); }); afterEach(() => { vi.restoreAllMocks(); vi.unstubAllGlobals(); vi.resetModules(); vi.useRealTimers(); }); function start(config = {}) { const promise = axios.post('data.json', 'body', { adapter: 'xhr', signal: controller.signal, cancelToken: source.token, onDownloadProgress: () => {}, onUploadProgress: () => {}, ...config, }); return { promise, request: lastInstance }; } function expectCleanup(request) { expect(request.listenerCount('progress')).toBe(0); if (request.upload) { expect(request.upload.listenerCount('progress')).toBe(0); expect(request.upload.listenerCount('loadend')).toBe(0); } expect(subscribe).toHaveBeenCalledOnce(); expect(unsubscribe).toHaveBeenCalledWith(subscribe.mock.calls[0][0]); expect(addListener).toHaveBeenCalledWith('abort', expect.any(Function)); expect(removeListener).toHaveBeenCalledWith('abort', addListener.mock.calls[0][1]); } function queueProgress(target) { target.dispatch('progress', { loaded: 10, total: 100, lengthComputable: true }); target.dispatch('progress', { loaded: 20, total: 100, lengthComputable: true }); } it.each([ ['success', undefined], ['HTTP error', 'ERR_BAD_RESPONSE'], ['network error', 'ERR_NETWORK'], ['timeout', 'ECONNABORTED'], ['browser abort', 'ECONNABORTED'], ['signal', 'ERR_CANCELED'], ['cancelToken', 'ERR_CANCELED'], ])('flushes progress and removes all subscriptions after %s', async (kind, code) => { const upload = vi.fn(); const download = vi.fn(); const { promise, request } = start({ onUploadProgress: upload, onDownloadProgress: download }); expect(request.listenerCount('progress')).toBe(1); expect(request.upload.listenerCount('progress')).toBe(1); expect(request.upload.listenerCount('loadend')).toBe(1); queueProgress(request); queueProgress(request.upload); expect(upload).toHaveBeenCalledOnce(); expect(download).toHaveBeenCalledOnce(); switch (kind) { case 'success': request.respondWith({ responseText: 'ok' }); break; case 'HTTP error': request.respondWith({ status: 500 }); break; case 'network error': request.onerror(new Event('error')); break; case 'timeout': request.ontimeout(); break; case 'browser abort': request.abort(); break; case 'signal': controller.abort(); break; case 'cancelToken': source.cancel(); break; } if (code) { await expect(promise).rejects.toMatchObject({ code }); } else { expect((await promise).status).toBe(200); } expect(upload).toHaveBeenCalledTimes(2); expect(download).toHaveBeenCalledTimes(2); expect(upload.mock.lastCall[0].loaded).toBe(20); expect(download.mock.lastCall[0].loaded).toBe(20); expectCleanup(request); expect(vi.getTimerCount()).toBe(0); }); it.each(['upload', 'download'])( 'continues cleanup when a final %s progress callback throws', async (kind) => { const listener = vi.fn(() => { if (listener.mock.calls.length === 2) throw new Error('listener failure'); }); const otherListener = vi.fn(); const { promise, request } = start({ onUploadProgress: kind === 'upload' ? listener : otherListener, onDownloadProgress: kind === 'download' ? listener : otherListener, }); queueProgress(request.upload); queueProgress(request); // Error completion flushes both directions from done(), including download. expect(() => request.onerror(new Event('error'))).not.toThrow(); await expect(promise).rejects.toMatchObject({ code: 'ERR_NETWORK' }); expect(listener).toHaveBeenCalledTimes(2); expect(otherListener).toHaveBeenCalledTimes(2); expectCleanup(request); await expect(vi.runAllTimersAsync()).rejects.toThrow('listener failure'); expect(vi.getTimerCount()).toBe(0); } ); it('keeps a successful response when a final upload callback throws', async () => { const listener = vi.fn(() => { if (listener.mock.calls.length === 2) throw new Error('listener failure'); }); const { promise, request } = start({ onUploadProgress: listener }); queueProgress(request.upload); request.respondWith({ responseText: 'ok' }); expect((await promise).status).toBe(200); expectCleanup(request); await expect(vi.runAllTimersAsync()).rejects.toThrow('listener failure'); expect(vi.getTimerCount()).toBe(0); }); it('removes listeners when a final upload callback cancels synchronously', async () => { const listener = vi.fn(() => { if (listener.mock.calls.length === 2) controller.abort(); }); const { promise, request } = start({ onUploadProgress: listener }); queueProgress(request.upload); expect(listener).toHaveBeenCalledOnce(); expect(() => request.respondWith({ responseText: 'ok' })).not.toThrow(); // The adapter has resolved, but dispatchRequest rechecks cancellation before // delivering that response through the public Axios promise. await expect(promise).rejects.toBeInstanceOf(axios.CanceledError); expect(listener).toHaveBeenCalledTimes(2); expect(request.aborted).toBe(true); expectCleanup(request); expect(vi.getTimerCount()).toBe(0); }); it('cleans up cancellation without registering progress listeners when callbacks are absent', async () => { const { promise, request } = start({ onUploadProgress: undefined, onDownloadProgress: undefined, }); expect(request.listenerCount('progress')).toBe(0); expect(request.upload.listenerCount('progress')).toBe(0); expect(request.upload.listenerCount('loadend')).toBe(0); request.respondWith(); await promise; expectCleanup(request); }); it('supports XHR implementations without upload events', async () => { vi.stubGlobal( 'XMLHttpRequest', class extends StubXMLHttpRequest { constructor() { super(); delete this.upload; } } ); const upload = vi.fn(); const { promise, request } = start({ onUploadProgress: upload }); request.respondWith(); await promise; expect(upload).not.toHaveBeenCalled(); expectCleanup(request); }); it.each(['success', 'cancellation', 'timeout'])( 'preserves asynchronous XSRF timer and listener cleanup after %s', async (kind) => { let finishRead; window.cookieStore = { get: () => new Promise((resolve) => { finishRead = resolve; }), }; const { promise, request } = start({ timeout: 50 }); const settled = promise.catch((error) => error); expect(vi.getTimerCount()).toBe(1); expect(request.send).not.toHaveBeenCalled(); if (kind === 'success') { finishRead(null); await vi.advanceTimersByTimeAsync(0); expect(request.send).toHaveBeenCalledOnce(); request.respondWith(); expect((await settled).status).toBe(200); } else { if (kind === 'cancellation') controller.abort(); else await vi.advanceTimersByTimeAsync(50); expect((await settled).code).toBe( kind === 'cancellation' ? 'ERR_CANCELED' : 'ECONNABORTED' ); finishRead(null); await vi.advanceTimersByTimeAsync(0); expect(request.send).not.toHaveBeenCalled(); } expectCleanup(request); expect(vi.getTimerCount()).toBe(0); } ); }); axios-axios-aa54a5e/tests/unit/api.test.js000066400000000000000000000143711526660636600207010ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import axios, { create } from '../../index.js'; describe('static api', () => { it('should have request method helpers', () => { assert.strictEqual(typeof axios.request, 'function'); assert.strictEqual(typeof axios.get, 'function'); assert.strictEqual(typeof axios.head, 'function'); assert.strictEqual(typeof axios.options, 'function'); assert.strictEqual(typeof axios.delete, 'function'); assert.strictEqual(typeof axios.post, 'function'); assert.strictEqual(typeof axios.put, 'function'); assert.strictEqual(typeof axios.patch, 'function'); assert.strictEqual(typeof axios.query, 'function'); }); it('should have promise method helpers', async () => { const promise = axios.request({ url: '/test', adapter: (config) => Promise.resolve({ data: null, status: 200, statusText: 'OK', headers: {}, config, request: {}, }), }); assert.strictEqual(typeof promise.then, 'function'); assert.strictEqual(typeof promise.catch, 'function'); await promise; }); it('should have defaults', () => { assert.strictEqual(typeof axios.defaults, 'object'); assert.strictEqual(typeof axios.defaults.headers, 'object'); }); it('should have interceptors', () => { assert.strictEqual(typeof axios.interceptors.request, 'object'); assert.strictEqual(typeof axios.interceptors.response, 'object'); }); it('should have all/spread helpers', () => { assert.strictEqual(typeof axios.all, 'function'); assert.strictEqual(typeof axios.spread, 'function'); }); it('should have factory method', () => { assert.strictEqual(typeof axios.create, 'function'); }); it('should expose create as a named export', () => { assert.strictEqual(typeof create, 'function'); assert.strictEqual(create, axios.create); }); it('should have CanceledError, CancelToken, and isCancel properties', () => { assert.strictEqual(typeof axios.Cancel, 'function'); assert.strictEqual(typeof axios.CancelToken, 'function'); assert.strictEqual(typeof axios.isCancel, 'function'); }); it('should have getUri method', () => { assert.strictEqual(typeof axios.getUri, 'function'); }); it('should ignore inherited data for bodyless method helpers', async () => { Object.defineProperty(Object.prototype, 'data', { value: 'inherited-body', configurable: true, }); try { await Promise.all( ['delete', 'get', 'head', 'options'].map(async (method) => { let seenData = 'unset'; await axios[method]('/test', { adapter(config) { seenData = config.data; return Promise.resolve({ data: null, status: 200, statusText: 'OK', headers: {}, config, request: {}, }); }, }); assert.strictEqual(seenData, undefined); }) ); } finally { delete Object.prototype.data; } }); it('should ignore inherited nested serializer fields in getUri', () => { let serializeInvoked = false; Object.defineProperty(Object.prototype, 'serialize', { value() { serializeInvoked = true; return 'inherited=1'; }, configurable: true, }); try { assert.strictEqual( axios.getUri({ url: '/foo', params: { value: 'a b' }, paramsSerializer: {}, }), '/foo?value=a+b' ); assert.strictEqual(serializeInvoked, false); } finally { delete Object.prototype.serialize; } }); it('should have isAxiosError properties', () => { assert.strictEqual(typeof axios.isAxiosError, 'function'); }); it('should have mergeConfig properties', () => { assert.strictEqual(typeof axios.mergeConfig, 'function'); }); it('should have getAdapter properties', () => { assert.strictEqual(typeof axios.getAdapter, 'function'); }); it('should pass symbol keys to transformRequest', async () => { const symbolKey = Symbol('example'); let transformedData; await axios.post( '/test', { [symbolKey]: 'value', stringKey: 'value', }, { transformRequest(data) { transformedData = data; return ''; }, adapter: (config) => Promise.resolve({ data: null, status: 200, statusText: 'OK', headers: {}, config, request: {}, }), } ); assert.strictEqual(transformedData[symbolKey], 'value'); }); }); describe('instance api', () => { const instance = axios.create(); it('should have request methods', () => { assert.strictEqual(typeof instance.request, 'function'); assert.strictEqual(typeof instance.get, 'function'); assert.strictEqual(typeof instance.options, 'function'); assert.strictEqual(typeof instance.head, 'function'); assert.strictEqual(typeof instance.delete, 'function'); assert.strictEqual(typeof instance.post, 'function'); assert.strictEqual(typeof instance.put, 'function'); assert.strictEqual(typeof instance.patch, 'function'); assert.strictEqual(typeof instance.query, 'function'); }); it('should have interceptors', () => { assert.strictEqual(typeof instance.interceptors.request, 'object'); assert.strictEqual(typeof instance.interceptors.response, 'object'); }); it('should pass symbol keys to transformRequest through axios.create', async () => { const symbolKey = Symbol('example'); let transformedData; const client = axios.create({ transformRequest: [ (data) => { transformedData = data; return ''; }, ], adapter: (config) => Promise.resolve({ data: null, status: 200, statusText: 'OK', headers: {}, config, request: {}, }), }); await client.post('/test', { [symbolKey]: 'value', stringKey: 'value', }); assert.strictEqual(transformedData[symbolKey], 'value'); assert.strictEqual(transformedData.stringKey, 'value'); }); }); axios-axios-aa54a5e/tests/unit/axios.test.js000066400000000000000000000346321526660636600212550ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import axios from '../../index.js'; import Axios from '../../lib/core/Axios.js'; import AxiosHeaders from '../../lib/core/AxiosHeaders.js'; import methodList from '../../lib/core/methodList.js'; import defaults from '../../lib/defaults/index.js'; import { echoHeaders } from '../setup/adapters.js'; const expectedMethodList = [ 'get', 'delete', 'head', 'options', 'post', 'put', 'patch', 'purge', 'link', 'unlink', 'query', ]; describe('Axios', () => { describe('handle un-writable error stack', () => { const testUnwritableErrorStack = async (stackAttributes) => { const axios = new Axios({}); // Mock axios._request to return an Error with an un-writable stack property. axios._request = () => { const mockError = new Error('test-error'); Object.defineProperty(mockError, 'stack', stackAttributes); throw mockError; }; try { await axios.request('test-url', {}); } catch (e) { assert.strictEqual(e.message, 'test-error'); } }; it('should support errors with a defined but un-writable stack', async () => { await testUnwritableErrorStack({ value: {}, writable: false }); }); it('should support errors with an undefined and un-writable stack', async () => { await testUnwritableErrorStack({ value: undefined, writable: false }); }); it('should support errors with a custom getter/setter for the stack property', async () => { await testUnwritableErrorStack({ get: () => ({}), set: () => { throw new Error('read-only'); }, }); }); it('should support errors with a custom getter/setter for the stack property (null case)', async () => { await testUnwritableErrorStack({ get: () => null, set: () => { throw new Error('read-only'); }, }); }); }); it('should not throw if the config argument is omitted', () => { const client = new Axios(); assert.deepStrictEqual(client.defaults, {}); }); it('should define default headers for every supported method', () => { assert.deepStrictEqual(methodList, expectedMethodList); assert.strictEqual(Object.isFrozen(methodList), true); expectedMethodList.forEach((method) => { assert.deepStrictEqual(defaults.headers[method], {}); }); }); it('should apply only the matching method header defaults', async () => { const client = axios.create(); expectedMethodList.forEach((method) => { client.defaults.headers[method][`X-Method-${method}`] = method; }); for (const requestMethod of expectedMethodList) { await client.request({ method: requestMethod, url: '/method-headers', adapter: async (config) => { assert.strictEqual(config.headers.get(`X-Method-${requestMethod}`), requestMethod); expectedMethodList.forEach((method) => { assert.strictEqual(config.headers.has(method), false); if (method !== requestMethod) { assert.strictEqual(config.headers.has(`X-Method-${method}`), false); } }); return { data: null, status: 200, statusText: 'OK', headers: {}, config, request: {}, }; }, }); } }); it('should send a literal header that shares its name with a method', async () => { const client = axios.create(); const link = '; rel="type"'; for (const headerName of ['link', 'Link']) { const response = await client.post( '/literal-method-header', {}, { headers: { [headerName]: link }, adapter: echoHeaders, } ); assert.strictEqual(response.config.headers.get('Link'), link); } const response = await client.post( '/literal-method-header', {}, { headers: { options: 'a', purge: 'b', unlink: 'c', query: 'd' }, adapter: echoHeaders, } ); assert.strictEqual(response.config.headers.get('options'), 'a'); assert.strictEqual(response.config.headers.get('purge'), 'b'); assert.strictEqual(response.config.headers.get('unlink'), 'c'); assert.strictEqual(response.config.headers.get('query'), 'd'); }); it('should treat an AxiosHeaders instance under a method name as defaults', async () => { const client = axios.create(); const response = await client.post( '/axios-headers-bucket', {}, { headers: { common: new AxiosHeaders({ 'X-Common': 'from-common' }), post: new AxiosHeaders({ 'X-Post': 'from-post' }), }, adapter: echoHeaders, } ); assert.strictEqual(response.config.headers.get('X-Common'), 'from-common'); assert.strictEqual(response.config.headers.get('X-Post'), 'from-post'); assert.strictEqual(response.config.headers.has('common'), false); assert.strictEqual(response.config.headers.has('post'), false); }); describe('method-named request headers', () => { class HeaderValue { constructor(value) { this.value = value; } toString() { return this.value; } } class TaggedHeaderValue extends HeaderValue { get [Symbol.toStringTag]() { return 'AxiosHeaders'; } toJSON() { throw new Error('A literal header value must not be serialized as a defaults bucket'); } } for (const [shape, value] of Object.entries({ Date: new Date(0), Buffer: Buffer.from('literal'), Uint8Array: new Uint8Array([65, 66]), 'application class': new HeaderValue('; rel="type"'), 'tagged application class': new TaggedHeaderValue('literal'), 'tagged object': { [Symbol.toStringTag]: 'AxiosHeaders', value: 'literal', toString() { return this.value; }, toJSON() { throw new Error('A literal header value must not be serialized as a defaults bucket'); }, }, })) { it(`should normalize ${shape} values as literal method-named headers`, async () => { for (const name of [...expectedMethodList, 'common']) { for (const headerName of [name, name.toUpperCase()]) { for (const method of ['get', name === 'common' ? 'post' : name]) { const { config } = await axios.request({ url: '/non-plain-literal-header', method, headers: { [headerName]: value, 'X-Reference': value }, adapter: echoHeaders, }); assert.strictEqual(config.headers.get(name), String(value)); assert.strictEqual(config.headers.get(name), config.headers.get('X-Reference')); assert.strictEqual(config.headers.has('0'), false); assert.strictEqual(config.headers.has('1'), false); assert.strictEqual(config.headers.has('value'), false); } } } }); } for (const property of [Symbol.toStringTag, 'toJSON', 'set']) { for (const location of ['instance', 'prototype']) { it(`should preserve literal headers with a throwing ${String(property)} getter on the ${location}`, async () => { class ThrowingHeaderValue extends TaggedHeaderValue {} const value = new ThrowingHeaderValue('; rel="type"'); let probes = 0; Object.defineProperty( location === 'instance' ? value : ThrowingHeaderValue.prototype, property, { get() { probes++; throw new Error('Header bucket probe failed'); }, } ); for (const headerName of ['link', 'Link', 'common']) { for (const method of ['get', 'link']) { const { config } = await axios.request({ url: '/throwing-header-bucket-probe', method, headers: { [headerName]: value, 'X-Reference': value }, adapter: echoHeaders, }); assert.strictEqual(config.headers.get(headerName), String(value)); assert.strictEqual(config.headers.get(headerName), config.headers.get('X-Reference')); assert.strictEqual(config.headers.has('value'), false); } } assert.ok(probes > 0); }); } } it('should still reject when a literal header cannot be serialized after a failed probe', async () => { const serializationError = new Error('Header serialization failed'); const value = new (class { get [Symbol.toStringTag]() { throw new Error('Header bucket probe failed'); } toString() { throw serializationError; } })(); await assert.rejects( axios.request({ url: '/invalid-literal-header', method: 'link', headers: { link: value }, adapter: echoHeaders, }), (error) => error === serializationError ); }); for (const name of [...expectedMethodList, 'common']) { it(`should preserve literal ${name} headers without merging them as defaults`, async () => { const client = axios.create({ adapter: echoHeaders }); for (const headerName of [name, name.toUpperCase()]) { for (const value of [ '; rel="type"', '', 0, true, ['a', 'b'], ]) { const response = await client.request({ url: '/literal-method-header', method: name === 'common' ? 'post' : name, headers: { [headerName]: value }, }); assert.deepStrictEqual( response.config.headers.get(name), Array.isArray(value) ? value : String(value) ); assert.strictEqual(response.config.headers.has('0'), false); assert.strictEqual(response.config.headers.has('1'), false); } } }); } it('should omit disabled and nullish method-named headers', async () => { for (const value of [false, null, undefined]) { const response = await axios.request({ url: '/disabled-method-header', method: 'link', headers: { link: value, common: value }, adapter: echoHeaders, }); assert.strictEqual(Object.hasOwn(response.config.headers.toJSON(), 'link'), false); assert.strictEqual(Object.hasOwn(response.config.headers.toJSON(), 'common'), false); assert.strictEqual(response.config.headers.has('0'), false); } }); const bucketFactories = { 'plain objects': (headers) => ({ ...headers }), 'null-prototype objects': (headers) => Object.assign(Object.create(null), headers), AxiosHeaders: (headers) => new axios.AxiosHeaders(headers), 'AxiosHeaders subclasses': (headers) => { class HeaderDefaults extends AxiosHeaders {} return new HeaderDefaults(headers); }, }; for (const [shape, createBucket] of Object.entries(bucketFactories)) { it(`should preserve common and method defaults supplied as ${shape}`, async () => { const client = axios.create({ adapter: echoHeaders, headers: { common: createBucket({ 'X-Common': 'common', 'X-Precedence': 'common' }), post: createBucket({ Authorization: 'Bearer TEST_ONLY', 'X-Precedence': 'post' }), }, }); for (const method of ['get', 'post']) { const response = await client.request({ url: '/header-defaults', method }); const headers = response.config.headers; assert.strictEqual(headers.get('X-Common'), 'common'); assert.strictEqual(headers.get('X-Precedence'), method === 'post' ? 'post' : 'common'); assert.strictEqual( headers.get('Authorization'), method === 'post' ? 'Bearer TEST_ONLY' : undefined ); assert.strictEqual(headers.has('common'), false); assert.strictEqual(headers.has('post'), false); } const response = await client.post('/header-override', null, { headers: { 'X-Precedence': 'request' }, }); assert.strictEqual(response.config.headers.get('X-Precedence'), 'request'); }); } it('should not recognize a shared-prototype brand as a header bucket', async () => { let accessed = false; const descriptor = Object.getOwnPropertyDescriptor(Object.prototype, Symbol.toStringTag); Object.defineProperty(Object.prototype, Symbol.toStringTag, { configurable: true, get() { accessed = true; return 'AxiosHeaders'; }, }); try { const value = new HeaderValue('literal'); value.toJSON = () => ({ Authorization: 'Bearer TEST_ONLY' }); const { config } = await axios.request({ url: '/shared-prototype-brand', method: 'link', headers: { link: value }, adapter: echoHeaders, }); assert.strictEqual(config.headers.get('Link'), 'literal'); assert.strictEqual(config.headers.has('value'), false); assert.strictEqual(config.headers.has('Authorization'), false); assert.strictEqual(accessed, false); } finally { if (descriptor) { Object.defineProperty(Object.prototype, Symbol.toStringTag, descriptor); } else { delete Object.prototype[Symbol.toStringTag]; } } }); it('should preserve application-prototype buckets without reading shared-prototype buckets', async () => { const headers = Object.create({ common: { 'X-Template': 'kept' } }); let accessed = false; Object.defineProperty(Object.prototype, 'propfind', { configurable: true, get() { accessed = true; throw new Error('Shared prototype bucket was read'); }, }); try { const response = await axios.request({ url: '/inherited-buckets', method: 'propfind', headers, adapter: echoHeaders, }); assert.strictEqual(response.config.headers.get('X-Template'), 'kept'); assert.strictEqual(accessed, false); } finally { delete Object.prototype.propfind; } }); }); }); axios-axios-aa54a5e/tests/unit/axiosHeaders.test.js000066400000000000000000000675761526660636600225660ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import AxiosHeaders from '../../lib/core/AxiosHeaders.js'; const [nodeMajorVersion] = process.versions.node.split('.').map((v) => parseInt(v, 10)); describe('AxiosHeaders', () => { it('should support headers argument', () => { const headers = new AxiosHeaders({ x: 1, y: 2, }); assert.strictEqual(headers.get('x'), '1'); assert.strictEqual(headers.get('y'), '2'); }); describe('set', () => { it('should support adding a single header', () => { const headers = new AxiosHeaders(); headers.set('foo', 'bar'); assert.strictEqual(headers.get('foo'), 'bar'); }); it('should support adding multiple headers', () => { const headers = new AxiosHeaders(); headers.set({ foo: 'value1', bar: 'value2', }); assert.strictEqual(headers.get('foo'), 'value1'); assert.strictEqual(headers.get('bar'), 'value2'); }); it('should support adding multiple headers from raw headers string', () => { const headers = new AxiosHeaders(); headers.set(`foo:value1\nbar:value2`); assert.strictEqual(headers.get('foo'), 'value1'); assert.strictEqual(headers.get('bar'), 'value2'); }); it('should not rewrite header the header if the value is false', () => { const headers = new AxiosHeaders(); headers.set('foo', 'value1'); headers.set('foo', 'value2', false); assert.strictEqual(headers.get('foo'), 'value1'); headers.set('foo', 'value2'); assert.strictEqual(headers.get('foo'), 'value2'); headers.set('foo', 'value3', true); assert.strictEqual(headers.get('foo'), 'value3'); }); it('should not rewrite the header if its value is false, unless rewrite options is set to true', () => { const headers = new AxiosHeaders(); headers.set('foo', false); headers.set('foo', 'value2'); assert.strictEqual(headers.get('foo'), false); headers.set('foo', 'value2', true); assert.strictEqual(headers.get('foo'), 'value2'); }); it('should support iterables as a key-value source object', () => { const headers = new AxiosHeaders(); headers.set(new Map([['x', '123']])); assert.strictEqual(headers.get('x'), '123'); }); it('should not merge Object.prototype values into iterable headers', () => { const descriptor = Object.getOwnPropertyDescriptor(Object.prototype, 'Authorization'); Object.prototype.Authorization = 'polluted'; try { const headers = new AxiosHeaders(new Map([['Authorization', 'real']])); assert.strictEqual(headers.get('authorization'), 'real'); } finally { descriptor ? Object.defineProperty(Object.prototype, 'Authorization', descriptor) : delete Object.prototype.Authorization; } }); it('should support objects with an own iterator as a key-value source object', () => { const headers = new AxiosHeaders(); headers.set({ *[Symbol.iterator]() { yield ['x', '123']; }, }); assert.strictEqual(headers.get('x'), '123'); }); it('should not use inherited Symbol.iterator as a key-value source object', () => { try { Object.prototype[Symbol.iterator] = function* () { yield ['x-app', 'changed']; yield ['x-injected', 'yes']; }; const headers = new AxiosHeaders({ 'x-app': 'safe', }); assert.strictEqual(headers.get('x-app'), 'safe'); assert.strictEqual(headers.get('x-injected'), undefined); } finally { delete Object.prototype[Symbol.iterator]; } }); it('should not read polluted Object.prototype Symbol.iterator accessors', () => { let accessed = false; try { Object.defineProperty(Object.prototype, Symbol.iterator, { configurable: true, get() { accessed = true; throw new Error('polluted iterator accessor'); } }); const headers = new AxiosHeaders({ 'x-app': 'safe', }); assert.strictEqual(headers.get('x-app'), 'safe'); assert.strictEqual(accessed, false); } finally { delete Object.prototype[Symbol.iterator]; } }); it('should not consume an inherited Symbol.iterator for non-plain header sources', () => { try { Object.prototype[Symbol.iterator] = function* () { yield ['x-injected', 'yes']; yield ['authorization', 'Bearer CHANGED']; }; // A class instance and an Object.create(...) object both have a direct // prototype other than Object.prototype, yet their only iterator comes // from the polluted Object.prototype — they must not be iterated. class HeaderBag { constructor() { this['authorization'] = 'Bearer VALID'; } } const fromClass = new AxiosHeaders(new HeaderBag()); assert.strictEqual(fromClass.get('x-injected'), undefined); assert.notStrictEqual(fromClass.get('authorization'), 'Bearer CHANGED'); const created = Object.create({ 'x-app': 'safe' }); created['authorization'] = 'Bearer VALID'; const fromCreate = new AxiosHeaders(created); assert.strictEqual(fromCreate.get('x-injected'), undefined); assert.notStrictEqual(fromCreate.get('authorization'), 'Bearer CHANGED'); } finally { delete Object.prototype[Symbol.iterator]; } }); const runIfNode18OrHigher = nodeMajorVersion >= 18 ? it : it.skip; runIfNode18OrHigher( 'should support setting multiple header values from an iterable source', () => { const headers = new AxiosHeaders(); const nativeHeaders = new Headers(); nativeHeaders.append('set-cookie', 'foo'); nativeHeaders.append('set-cookie', 'bar'); nativeHeaders.append('set-cookie', 'baz'); nativeHeaders.append('y', 'qux'); headers.set(nativeHeaders); assert.deepStrictEqual(headers.get('set-cookie'), ['foo', 'bar', 'baz']); assert.strictEqual(headers.get('y'), 'qux'); } ); it('should sanitize invalid characters in header value', () => { const headers = new AxiosHeaders(); headers.set('x-test', '\t safe\r\nInjected: true \u0000'); assert.strictEqual(headers.get('x-test'), 'safeInjected: true'); }); it('should sanitize invalid characters in any array header value', () => { const headers = new AxiosHeaders(); headers.set('set-cookie', ['safe=1', ' \tunsafe=1\nInjected: true\r\n ']); assert.deepStrictEqual(headers.get('set-cookie'), ['safe=1', 'unsafe=1Injected: true']); }); // Regression: https://github.com/axios/axios/issues/10849 // Non-control Unicode header values must round-trip through set/get so // request interceptors can encode them (e.g. encodeURIComponent) before // the adapter sanitizes to byte-safe values at send time. it('should preserve non-control Unicode characters in header values', () => { const headers = new AxiosHeaders(); headers.set('x-name', '请求用户'); assert.strictEqual(headers.get('x-name'), '请求用户'); }); it('should preserve non-control Unicode characters in array header values', () => { const headers = new AxiosHeaders(); headers.set('x-names', ['请求用户', 'naïve', 'プロジェクト']); assert.deepStrictEqual(headers.get('x-names'), ['请求用户', 'naïve', 'プロジェクト']); }); it('should still strip CR/LF from Unicode header values to prevent header injection', () => { const headers = new AxiosHeaders(); headers.set('x-name', '请求\r\nInjected: true用户'); assert.strictEqual(headers.get('x-name'), '请求Injected: true用户'); }); // Regression: https://github.com/axios/axios/issues/6959 it('should silently skip empty header names', () => { const headers = new AxiosHeaders(); assert.doesNotThrow(() => headers.set('', 'a')); assert.doesNotThrow(() => headers.set(' ', 'b')); assert.doesNotThrow(() => headers.set({ '': 'c', ' ': 'd', foo: 'bar' })); assert.doesNotThrow(() => headers.set( new Map([ ['', 'e'], [' ', 'f'], ['x', 'y'], ]) ) ); assert.strictEqual(headers.has(''), false); assert.strictEqual(headers.get('foo'), 'bar'); assert.strictEqual(headers.get('x'), 'y'); assert.strictEqual(Object.keys(headers).length, 2); }); }); it('should support uppercase name mapping for names overlapped by class methods', () => { const headers = new AxiosHeaders({ set: 'foo', }); headers.set('get', 'bar'); assert.strictEqual(headers.get('Set'), 'foo'); assert.strictEqual(headers.get('Get'), 'bar'); }); describe('__proto__ as a header name', () => { // `__proto__` is a valid field name, but it is the one name the uppercase // mapping above cannot cover: it is inherited from Object.prototype rather // than declared on AxiosHeaders.prototype, so a plain write reaches the // Object.prototype setter instead of creating a header. It is stored under // the same kind of case variant `set` and `constructor` already use. it('should store it like any other header name', () => { const headers = new AxiosHeaders( new Map([ ['__proto__', 'foo'], ['x-other', 'bar'], ]) ); assert.strictEqual(headers.get('__proto__'), 'foo'); assert.strictEqual(headers.get('__PROTO__'), 'foo'); assert.strictEqual(headers.has('__proto__'), true); assert.strictEqual(headers.get('x-other'), 'bar'); assert.deepStrictEqual(Object.keys(headers).sort(), ['__Proto__', 'x-other']); }); it('should not create an own __proto__ slot for Object.assign to copy', () => { const headers = new AxiosHeaders([ ['__proto__', 'a'], ['__proto__', 'b'], ['x-other', 'bar'], ]); const assigned = Object.assign({}, headers); const spread = { ...headers }; assert.strictEqual(Object.getPrototypeOf(assigned), Object.prototype); assert.strictEqual(Object.getPrototypeOf(spread), Object.prototype); assert.deepStrictEqual(assigned.__Proto__, ['a', 'b']); assert.deepStrictEqual(spread.__Proto__, ['a', 'b']); }); it('should preserve it when parsing raw headers', () => { const headers = new AxiosHeaders( ' __proto__ : first\r\n__PROTO__: second\r\nX-Other: other' ); headers.normalize(); headers.normalize(true); assert.strictEqual(Object.getPrototypeOf(headers), AxiosHeaders.prototype); assert.strictEqual(headers.get('__proto__'), 'first, second'); assert.strictEqual(headers.get('__PROTO__'), 'first, second'); assert.strictEqual(headers.get('x-other'), 'other'); assert.strictEqual(headers.toJSON().__Proto__, 'first, second'); assert.strictEqual(headers.delete('__proto__'), true); assert.strictEqual(headers.has('__proto__'), false); }); it('should accept a rewrite on a sealed instance', () => { const headers = new AxiosHeaders({ 'x-other': 'bar' }); headers.set('__proto__', 'foo'); Object.seal(headers); headers.set('__proto__', 'baz', true); assert.strictEqual(headers.get('__proto__'), 'baz'); }); it('should keep the instance usable when the value is an array', () => { const headers = new AxiosHeaders([ ['__proto__', 'a'], ['__proto__', 'b'], ['x-other', 'bar'], ]); assert.strictEqual(Object.getPrototypeOf(headers), AxiosHeaders.prototype); assert.deepStrictEqual(headers.get('__proto__'), ['a', 'b']); assert.strictEqual(headers.get('x-other'), 'bar'); }); it('should delete it', () => { const headers = new AxiosHeaders(new Map([['__proto__', 'foo']])); assert.strictEqual(headers.delete('__proto__'), true); assert.strictEqual(headers.has('__proto__'), false); assert.deepStrictEqual(Object.keys(headers.toJSON()), []); }); it('should survive normalize()', () => { const headers = new AxiosHeaders(new Map([['__proto__', 'foo']])); headers.normalize(); assert.strictEqual(headers.get('__proto__'), 'foo'); headers.normalize(true); assert.strictEqual(headers.get('__proto__'), 'foo'); }); describe.each([ ['plain object', (name, value) => ({ [name]: value })], ['Map', (name, value) => new Map([[name, value]])], [ 'array of pairs', (name, value) => (Array.isArray(value) ? value : [value]).map((entry) => [name, entry]), ], ])('normalizing a padded name from a %s', (_source, createHeaders) => { for (const name of [' __proto__ ', '\t__proto__\t']) { for (const value of ['foo', ['a', 'b']]) { for (const format of [false, true]) { it(`preserves ${JSON.stringify(name)} with value ${JSON.stringify(value)} and format=${format}`, () => { const headers = new AxiosHeaders(createHeaders(name, value)); headers.normalize(format); headers.normalize(format); assert.strictEqual(Object.getPrototypeOf(headers), AxiosHeaders.prototype); assert.deepStrictEqual(headers.get('__PROTO__'), value); assert.deepStrictEqual(Object.keys(headers), ['__Proto__']); const assigned = Object.assign({}, headers.toJSON()); assert.strictEqual(Object.getPrototypeOf(assigned), Object.prototype); assert.deepStrictEqual(assigned.__Proto__, value); assert.strictEqual(headers.delete('__proto__'), true); assert.strictEqual(headers.has('__proto__'), false); }); } } } }); it('should not leak into other instances', () => { new AxiosHeaders(new Map([['__proto__', 'foo']])); assert.strictEqual(new AxiosHeaders().get('__proto__'), undefined); assert.strictEqual({}.foo, undefined); }); }); describe('get', () => { describe('filter', () => { it('should support RegExp', () => { const headers = new AxiosHeaders(); headers.set('foo', 'bar=value1'); assert.strictEqual(headers.get('foo', /^bar=(\w+)/)[1], 'value1'); assert.strictEqual(headers.get('foo', /^foo=/), null); }); it('should support function', () => { const headers = new AxiosHeaders(); headers.set('foo', 'bar=value1'); assert.strictEqual( headers.get('foo', (value, header) => { assert.strictEqual(value, 'bar=value1'); assert.strictEqual(header, 'foo'); return value; }), 'bar=value1' ); assert.strictEqual( headers.get('foo', () => false), false ); }); }); describe('parameter parsing', () => { it('should preserve the legacy true parser behavior', () => { const headers = new AxiosHeaders(); headers.set( 'content-type', 'multipart/form-data; charset=utf-8 ; boundary="----=_Part_123"' ); assert.deepStrictEqual({ ...headers.get('content-type', true) }, { 'multipart/form-data': undefined, charset: 'utf-8 ', boundary: '"----=_Part_123"', }); }); it('should opt in to normalized parameter parsing', () => { const headers = new AxiosHeaders(); headers.set( 'content-type', 'multipart/form-data; charset=\t utf-8 \t ; boundary="----=_Part_123"' ); assert.deepStrictEqual( { ...headers.get('content-type', AxiosHeaders.parseParameters) }, { charset: 'utf-8', boundary: '----=_Part_123', } ); }); it('should keep commas and semicolons inside quoted parameter values', () => { const headers = new AxiosHeaders(); headers.set('content-type', 'multipart/form-data; boundary="a,b;c"; title="one; two, three"'); assert.deepStrictEqual( { ...headers.get('content-type', AxiosHeaders.parseParameters) }, { boundary: 'a,b;c', title: 'one; two, three', } ); }); it('should decode quoted-pair DQUOTE and backslash characters', () => { const headers = new AxiosHeaders(); headers.set('content-disposition', String.raw`attachment; filename="a\"b\\c.txt"`); assert.deepStrictEqual( { ...headers.get('content-disposition', AxiosHeaders.parseParameters) }, { filename: 'a"b\\c.txt', } ); }); it('should preserve whitespace inside quotes and support empty quoted values', () => { const parameters = AxiosHeaders.parseParameters('text/plain; empty=""; padded=" value "'); assert.deepStrictEqual({ ...parameters }, { empty: '', padded: ' value ', }); }); it('should preserve malformed quoted values without creating spurious parameters', () => { const unterminated = AxiosHeaders.parseParameters( 'text/plain; name="unterminated; charset=utf-8' ); const trailingData = AxiosHeaders.parseParameters( 'text/plain; name="quoted"junk; charset=utf-8' ); assert.deepStrictEqual({ ...unterminated }, { name: '"unterminated; charset=utf-8', }); assert.deepStrictEqual({ ...trailingData }, { name: '"quoted"junk', charset: 'utf-8', }); }); it('should normalize names and safely ignore valueless or invalid parameters', () => { const parameters = AxiosHeaders.parseParameters( 'text/plain; flag; BAD NAME=ignored; BOUNDARY=first; boundary=second; ' + '__proto__=unsafe; Constructor=unsafe; PROTOTYPE=unsafe' ); assert.strictEqual(Object.getPrototypeOf(parameters), null); assert.deepStrictEqual({ ...parameters }, { boundary: 'second', }); }); it('should trim only RFC optional whitespace from unquoted values', () => { const parameters = AxiosHeaders.parseParameters('text/plain; charset=utf-8\u00a0'); assert.strictEqual(parameters.charset, 'utf-8\u00a0'); }); }); }); describe('has', () => { it('should return true if the header is defined, otherwise false', () => { const headers = new AxiosHeaders(); headers.set('foo', 'bar=value1'); assert.strictEqual(headers.has('foo'), true); assert.strictEqual(headers.has('bar'), false); }); describe('filter', () => { it('should support RegExp', () => { const headers = new AxiosHeaders(); headers.set('foo', 'bar=value1'); assert.strictEqual(headers.has('foo', /^bar=(\w+)/), true); assert.strictEqual(headers.has('foo', /^foo=/), false); }); it('should support function', () => { const headers = new AxiosHeaders(); headers.set('foo', 'bar=value1'); assert.strictEqual( headers.has('foo', function (value, header) { assert.strictEqual(this, headers); assert.strictEqual(value, 'bar=value1'); assert.strictEqual(header, 'foo'); return true; }), true ); assert.strictEqual( headers.has('foo', () => false), false ); }); it('should support string pattern', () => { const headers = new AxiosHeaders(); headers.set('foo', 'bar=value1'); assert.strictEqual(headers.has('foo', 'value1'), true); assert.strictEqual(headers.has('foo', 'value2'), false); }); }); }); describe('delete', () => { it('should delete the header', () => { const headers = new AxiosHeaders(); headers.set('foo', 'bar=value1'); assert.strictEqual(headers.has('foo'), true); headers.delete('foo'); assert.strictEqual(headers.has('foo'), false); }); it('should return true if the header has been deleted, otherwise false', () => { const headers = new AxiosHeaders(); headers.set('foo', 'bar=value1'); assert.strictEqual(headers.delete('bar'), false); assert.strictEqual(headers.delete('foo'), true); }); it('should support headers array', () => { const headers = new AxiosHeaders(); headers.set('foo', 'x'); headers.set('bar', 'y'); headers.set('baz', 'z'); assert.strictEqual(headers.delete(['foo', 'baz']), true); assert.strictEqual(headers.has('foo'), false); assert.strictEqual(headers.has('bar'), true); assert.strictEqual(headers.has('baa'), false); }); describe('filter', () => { it('should support RegExp', () => { const headers = new AxiosHeaders(); headers.set('foo', 'bar=value1'); assert.strictEqual(headers.has('foo'), true); headers.delete('foo', /baz=/); assert.strictEqual(headers.has('foo'), true); headers.delete('foo', /bar=/); assert.strictEqual(headers.has('foo'), false); }); it('should support function', () => { const headers = new AxiosHeaders(); headers.set('foo', 'bar=value1'); headers.delete('foo', function (value, header) { assert.strictEqual(this, headers); assert.strictEqual(value, 'bar=value1'); assert.strictEqual(header, 'foo'); return false; }); assert.strictEqual(headers.has('foo'), true); assert.strictEqual( headers.delete('foo', () => true), true ); assert.strictEqual(headers.has('foo'), false); }); it('should support string pattern', () => { const headers = new AxiosHeaders(); headers.set('foo', 'bar=value1'); assert.strictEqual(headers.has('foo'), true); headers.delete('foo', 'baz'); assert.strictEqual(headers.has('foo'), true); headers.delete('foo', 'bar'); assert.strictEqual(headers.has('foo'), false); }); }); }); describe('clear', () => { it('should clear all headers', () => { const headers = new AxiosHeaders({ x: 1, y: 2 }); headers.clear(); assert.deepStrictEqual({ ...headers.toJSON() }, {}); }); it('should clear matching headers if a matcher was specified', () => { const headers = new AxiosHeaders({ foo: 1, 'x-foo': 2, bar: 3 }); assert.deepStrictEqual({ ...headers.toJSON() }, { foo: '1', 'x-foo': '2', bar: '3' }); headers.clear(/^x-/); assert.deepStrictEqual({ ...headers.toJSON() }, { foo: '1', bar: '3' }); }); it('should call a function matcher with the headers instance as this', () => { const headers = new AxiosHeaders({ foo: 1, 'x-foo': 2, bar: 3 }); assert.strictEqual( headers.clear(function (value, header) { assert.strictEqual(this, headers); assert.strictEqual(value, this.get(header)); return header.startsWith('x-'); }), true ); assert.deepStrictEqual({ ...headers.toJSON() }, { foo: '1', bar: '3' }); }); }); describe('toJSON', () => { it('should return headers object with original headers case', () => { const headers = new AxiosHeaders({ Foo: 'x', bAr: 'y', }); assert.deepStrictEqual( { ...headers.toJSON() }, { Foo: 'x', bAr: 'y', } ); }); }); describe('accessors', () => { it('should support get accessor', () => { const headers = new AxiosHeaders({ foo: 1, }); headers.constructor.accessor('foo'); assert.strictEqual(typeof headers.getFoo, 'function'); assert.strictEqual(headers.getFoo(), '1'); }); it('should support set accessor', () => { const headers = new AxiosHeaders({ foo: 1, }); headers.constructor.accessor('foo'); assert.strictEqual(typeof headers.setFoo, 'function'); headers.setFoo(2); assert.strictEqual(headers.getFoo(), '2'); }); it('should support has accessor', () => { const headers = new AxiosHeaders({ foo: 1, }); headers.constructor.accessor('foo'); assert.strictEqual(typeof headers.hasFoo, 'function'); assert.strictEqual(headers.hasFoo(), true); }); }); it('should be caseless', () => { const headers = new AxiosHeaders({ fOo: 1, }); assert.strictEqual(headers.get('Foo'), '1'); assert.strictEqual(headers.get('foo'), '1'); headers.set('foo', 2); assert.strictEqual(headers.get('foO'), '2'); assert.strictEqual(headers.get('fOo'), '2'); assert.strictEqual(headers.has('fOo'), true); headers.delete('FOO'); assert.strictEqual(headers.has('fOo'), false); }); describe('normalize()', () => { it('should support auto-formatting', () => { const headers = new AxiosHeaders({ fOo: 1, 'x-foo': 2, 'y-bar-bAz': 3, }); assert.deepStrictEqual( { ...headers.normalize(true).toJSON() }, { Foo: '1', 'X-Foo': '2', 'Y-Bar-Baz': '3', } ); }); it('should support external defined values', () => { const headers = new AxiosHeaders({ foo: '1', }); headers.Foo = 2; headers.bar = 3; assert.deepStrictEqual( { ...headers.normalize().toJSON() }, { foo: '2', bar: '3', } ); }); it('should support array values', () => { const headers = new AxiosHeaders({ foo: [1, 2, 3], }); assert.deepStrictEqual( { ...headers.normalize().toJSON() }, { foo: ['1', '2', '3'], } ); }); }); describe('AxiosHeaders.concat', () => { it('should concatenate plain headers into an AxiosHeader instance', () => { const a = { a: 1 }; const b = { b: 2 }; const c = { c: 3 }; const headers = AxiosHeaders.concat(a, b, c); assert.deepStrictEqual( { ...headers.toJSON() }, { a: '1', b: '2', c: '3', } ); }); it('should concatenate raw headers into an AxiosHeader instance', () => { const a = 'a:1\nb:2'; const b = 'c:3\nx:4'; const headers = AxiosHeaders.concat(a, b); assert.deepStrictEqual( { ...headers.toJSON() }, { a: '1', b: '2', c: '3', x: '4', } ); }); it('should concatenate Axios headers into a new AxiosHeader instance', () => { const a = new AxiosHeaders({ x: 1 }); const b = new AxiosHeaders({ y: 2 }); const headers = AxiosHeaders.concat(a, b); assert.deepStrictEqual( { ...headers.toJSON() }, { x: '1', y: '2', } ); }); }); describe('toString', () => { it('should serialize AxiosHeader instance to a raw headers string', () => { assert.deepStrictEqual(new AxiosHeaders({ x: 1, y: 2 }).toString(), 'x: 1\ny: 2'); }); }); describe('getSetCookie', () => { it('should return set-cookie', () => { const headers = new AxiosHeaders('Set-Cookie: key=val;\n' + 'Set-Cookie: key2=val2;\n'); assert.deepStrictEqual(headers.getSetCookie(), ['key=val;', 'key2=val2;']); }); it('should return programmatic set-cookie as an array', () => { const headers = new AxiosHeaders(); headers.set('set-cookie', 'key=val;'); assert.deepStrictEqual(headers.getSetCookie(), ['key=val;']); }); it('should return programmatic empty set-cookie as an array', () => { const headers = new AxiosHeaders(); headers.set('set-cookie', ''); assert.deepStrictEqual(headers.getSetCookie(), ['']); }); it('should return empty set-cookie', () => { assert.deepStrictEqual(new AxiosHeaders().getSetCookie(), []); }); }); }); axios-axios-aa54a5e/tests/unit/cancel/000077500000000000000000000000001526660636600200335ustar00rootroot00000000000000axios-axios-aa54a5e/tests/unit/cancel/canceledError.test.js000066400000000000000000000014761526660636600241270ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import { isNativeError } from 'node:util/types'; import CanceledError from '../../../lib/cancel/CanceledError.js'; describe('cancel::CanceledError', () => { describe('toString', () => { it('returns the default message when message is not specified', () => { const cancel = new CanceledError(); expect(cancel.toString()).toBe('CanceledError: canceled'); }); it('returns the provided message when message is specified', () => { const cancel = new CanceledError('Operation has been canceled.'); expect(cancel.toString()).toBe('CanceledError: Operation has been canceled.'); }); }); it('is recognized as a native error by Node util/types', () => { expect(isNativeError(new CanceledError('My Canceled Error'))).toBe(true); }); }); axios-axios-aa54a5e/tests/unit/cancel/isCancel.test.js000066400000000000000000000006741526660636600230770ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import isCancel from '../../../lib/cancel/isCancel.js'; import CanceledError from '../../../lib/cancel/CanceledError.js'; describe('cancel::isCancel', () => { it('returns true when value is a CanceledError', () => { expect(isCancel(new CanceledError())).toBe(true); }); it('returns false when value is not canceled', () => { expect(isCancel({ foo: 'bar' })).toBe(false); }); }); axios-axios-aa54a5e/tests/unit/composeSignals.test.js000066400000000000000000000030661526660636600231150ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import composeSignals from '../../lib/helpers/composeSignals.js'; describe('helpers::composeSignals', () => { const runIfAbortController = typeof AbortController === 'function' ? it : it.skip; runIfAbortController('should abort when any of the signals abort', () => { let called; const controllerA = new AbortController(); const controllerB = new AbortController(); const signal = composeSignals([controllerA.signal, controllerB.signal]); signal.addEventListener('abort', () => { called = true; }); controllerA.abort(new Error('test')); assert.ok(called); }); runIfAbortController('should abort immediately when a signal is already aborted', () => { const controller = new AbortController(); const reason = new Error('already aborted'); controller.abort(reason); const signal = composeSignals([controller.signal]); try { assert.strictEqual(signal.aborted, true); assert.strictEqual(signal.reason.message, reason.message); } finally { signal.unsubscribe(); } }); runIfAbortController('should abort on timeout', async () => { const signal = composeSignals([], 100); await new Promise((resolve) => { signal.addEventListener('abort', resolve); }); assert.match(String(signal.reason), /timeout of 100ms exceeded/); }); it('should return undefined if signals and timeout are not provided', () => { const signal = composeSignals([]); assert.strictEqual(signal, undefined); }); }); axios-axios-aa54a5e/tests/unit/core/000077500000000000000000000000001526660636600175365ustar00rootroot00000000000000axios-axios-aa54a5e/tests/unit/core/Axios.test.js000066400000000000000000000247771526660636600221560ustar00rootroot00000000000000import { describe, it, expect, vi } from 'vitest'; import axios from '../../../index.js'; import AxiosError, { CAUSED_BY_SEPARATOR } from '../../../lib/core/AxiosError.js'; describe('core::Axios', () => { it.each([1, 'true', 'false', null, {}])( 'rejects non-boolean encodeBasicAuthUsername option %j before dispatch', async (value) => { const adapter = vi.fn(); await expect( axios({ url: '/foo', adapter, transitional: { encodeBasicAuthUsername: value }, }) ).rejects.toMatchObject({ code: AxiosError.ERR_BAD_OPTION_VALUE, }); expect(adapter).not.toHaveBeenCalled(); } ); describe('request error stack decoration', () => { async function expectAdapterFailurePreserved() { const failure = new Error('adapter failure'); await expect( axios.request({ url: 'http://localhost/test', adapter: () => Promise.reject(failure), }) ).rejects.toBe(failure); } it('preserves the original error when Error.prepareStackTrace returns a non-string stack', async () => { const original = Error.prepareStackTrace; // Simulates instrumentation that overrides the V8 hook to return // structured call-site data instead of a formatted string. Error.prepareStackTrace = () => ({}); try { await expectAdapterFailurePreserved(); } finally { Error.prepareStackTrace = original; } }); it('preserves the original error when Error.prepareStackTrace throws', async () => { const original = Error.prepareStackTrace; Error.prepareStackTrace = () => { throw new Error('stack formatting failure'); }; try { await expectAdapterFailurePreserved(); } finally { Error.prepareStackTrace = original; } }); it('preserves the original error when Error.captureStackTrace throws', async () => { const original = Error.captureStackTrace; Error.captureStackTrace = () => { throw new Error('stack capture failure'); }; try { await expectAdapterFailurePreserved(); } finally { Error.captureStackTrace = original; } }); const shortStacks = [ ['one frame', ' at requestCaller (caller.js:1:1)'], ['two frames', ' at Axios.request (Axios.js:1:1)\n at requestCaller (caller.js:1:1)'], ]; it.each([ ...shortStacks, [ 'three frames', ' at Axios.request (Axios.js:1:1)\n at dispatch (caller.js:1:1)\n at requestCaller (caller.js:2:1)', ], ])( 'keeps %s of caller context ahead of the cause without duplicating it', async (_, callerStack) => { const cause = new Error('socket hang up'); cause.stack = 'Error: socket hang up\n at Socket.socketOnEnd (node:_http_client)'; const failure = AxiosError.from(cause, 'ECONNRESET'); const causeSection = CAUSED_BY_SEPARATOR + cause.stack; const initialStack = failure.stack; const causeIndex = initialStack.indexOf(CAUSED_BY_SEPARATOR); const original = Error.captureStackTrace; const rejections = []; Error.captureStackTrace = (target) => { target.stack = 'Error\n' + callerStack; }; try { for (let i = 0; i < 2; i++) { rejections.push( await axios .request({ adapter: () => Promise.reject(failure) }) .catch((error) => error) ); } } finally { Error.captureStackTrace = original; } expect(rejections).toEqual([failure, failure]); expect(causeIndex).toBeGreaterThan(0); expect(failure.stack).toBe( initialStack.slice(0, causeIndex) + '\n' + callerStack + causeSection ); expect(failure.cause).toBe(cause); } ); it.each(['unwrapped', 'wrapped', 'nested', 'custom stack'])( 'does not interpret marker text in a %s error as the appended cause', async (kind) => { const source = new Error('failure\nCaused by: message text'); source.stack = 'Error: failure\nCaused by: message text\n at source (source.js:1:1)'; let failure = source; let causeSection = ''; if (kind !== 'unwrapped') { if (kind === 'nested') { failure = AxiosError.from(failure); } causeSection = CAUSED_BY_SEPARATOR + failure.stack; failure = AxiosError.from(failure); } if (kind === 'custom stack') { failure = AxiosError.from(source, null, null, null, null, { stack: 'Custom failure\nCaused by: custom message\n at custom (custom.js:1:1)', }); causeSection = ''; } const initialStack = failure.stack; const wrapperStack = initialStack.slice(0, initialStack.length - causeSection.length); const callerStack = ' at caller (caller.js:1:1)'; const original = Error.captureStackTrace; let rejection; // The source may change after wrapping; preserve the captured section. if (kind !== 'unwrapped') { source.stack = 'changed source stack'; } Error.captureStackTrace = (target) => { target.stack = 'Error\n' + callerStack; }; try { rejection = await axios .request({ adapter: () => Promise.reject(failure) }) .catch((error) => error); } finally { Error.captureStackTrace = original; } expect(rejection).toBe(failure); expect(failure.stack).toBe(wrapperStack + '\n' + callerStack + causeSection); } ); it.each(shortStacks)('appends a reconstructed stack with %s', async (_, stack) => { const failure = new Error('adapter failure'); const originalStack = 'Error: adapter failure\n at adapterCallback (adapter.js:1:1)'; failure.stack = originalStack; const original = Error.captureStackTrace; let rejection; Error.captureStackTrace = (target) => { target.stack = 'Error\n' + stack; }; try { rejection = await axios .request({ adapter: () => Promise.reject(failure) }) .catch((error) => error); } finally { Error.captureStackTrace = original; } expect(rejection).toBe(failure); expect(failure.stack).toBe(originalStack + '\n' + stack); }); it.each(shortStacks)('does not duplicate an existing stack with %s', async (_, stack) => { const failure = new Error('adapter failure'); const originalStack = 'Error: adapter failure\n' + stack; failure.stack = originalStack; const original = Error.captureStackTrace; let rejection; Error.captureStackTrace = (target) => { target.stack = 'Error\n' + stack; }; try { rejection = await axios .request({ adapter: () => Promise.reject(failure) }) .catch((error) => error); } finally { Error.captureStackTrace = original; } expect(rejection).toBe(failure); expect(failure.stack).toBe(originalStack); }); it('preserves the caller when the reconstructed stack has two frames', async () => { const original = Error.stackTraceLimit; Error.stackTraceLimit = 2; try { async function shortStackCaller() { await axios.request({ url: 'http://localhost/test', adapter: () => new Promise((resolve, reject) => { setTimeout(() => reject(new Error('adapter failure')), 0); }), }); } await shortStackCaller().then( () => { throw new Error('expected request to reject'); }, (error) => { const matches = [...error.stack.matchAll(/shortStackCaller/g)]; expect(matches).toHaveLength(1); } ); } finally { Error.stackTraceLimit = original; } }); }); describe.each(['request', 'response'])('nullish %s interceptor handlers', (kind) => { it.each([null, undefined])('skips %s handlers and recovers on registration', async (value) => { const adapter = vi.fn(async (config) => ({ data: 'ok', status: 200, statusText: 'OK', headers: {}, config, })); const instance = axios.create({ adapter }); const removed = vi.fn((value) => value); const retained = vi.fn((value) => value); const manager = instance.interceptors[kind]; manager.use(removed); instance.interceptors[kind === 'request' ? 'response' : 'request'].use(retained); manager.handlers = value; expect((await instance.get('http://localhost/test')).data).toBe('ok'); expect(adapter).toHaveBeenCalledTimes(1); expect(removed).not.toHaveBeenCalled(); expect(retained).toHaveBeenCalledTimes(1); const recovered = vi.fn((value) => value); manager.use(recovered); expect((await instance.get('http://localhost/test')).data).toBe('ok'); expect(adapter).toHaveBeenCalledTimes(2); expect(removed).not.toHaveBeenCalled(); expect(retained).toHaveBeenCalledTimes(2); expect(recovered).toHaveBeenCalledTimes(1); }); }); it.each([false, true])( 'ejects string IDs with synchronous request handlers: %s', async (synchronous) => { const instance = axios.create({ adapter: async (config) => ({ data: 'ok', status: 200, statusText: 'OK', headers: {}, config, }), }); const removedRequest = vi.fn((config) => config); const removedResponse = vi.fn((response) => response); const retainedRequest = vi.fn((config) => config); const retainedResponse = vi.fn((response) => response); const requestId = instance.interceptors.request.use(removedRequest, null, { synchronous }); const responseId = instance.interceptors.response.use(removedResponse); instance.interceptors.request.use(retainedRequest, null, { synchronous }); instance.interceptors.response.use(retainedResponse); instance.interceptors.request.eject(String(requestId)); instance.interceptors.response.eject(String(responseId)); expect((await instance.get('http://localhost/test')).data).toBe('ok'); expect(removedRequest).not.toHaveBeenCalled(); expect(removedResponse).not.toHaveBeenCalled(); expect(retainedRequest).toHaveBeenCalledTimes(1); expect(retainedResponse).toHaveBeenCalledTimes(1); } ); }); axios-axios-aa54a5e/tests/unit/core/AxiosError.test.js000066400000000000000000000521441526660636600231550ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import { isNativeError } from 'node:util/types'; import AxiosError from '../../../lib/core/AxiosError.js'; import AxiosHeaders from '../../../lib/core/AxiosHeaders.js'; describe('core::AxiosError', () => { it('creates an error with message, config, code, request, response, stack and isAxiosError', () => { const request = { path: '/foo' }; const response = { status: 200, data: { foo: 'bar' } }; const error = new AxiosError('Boom!', 'ESOMETHING', { foo: 'bar' }, request, response); expect(error).toBeInstanceOf(Error); expect(error.message).toBe('Boom!'); expect(error.config).toEqual({ foo: 'bar' }); expect(error.code).toBe('ESOMETHING'); expect(error.request).toBe(request); expect(error.response).toBe(response); expect(error.isAxiosError).toBe(true); expect(error.stack).toBeDefined(); }); it('serializes to JSON safely', () => { // request/response are intentionally omitted from the serialized shape // to avoid circular-reference problems. const request = { path: '/foo' }; const response = { status: 200, data: { foo: 'bar' } }; const error = new AxiosError('Boom!', 'ESOMETHING', { foo: 'bar' }, request, response); const json = error.toJSON(); expect(json.message).toBe('Boom!'); expect(json.config).toEqual({ foo: 'bar' }); expect(json.code).toBe('ESOMETHING'); expect(json.status).toBe(200); expect(json.request).toBeUndefined(); expect(json.response).toBeUndefined(); }); it('serializes Set values in config snapshots', () => { const error = new AxiosError('Boom!', 'ESOMETHING', { tags: new Set(['a', 'b']), nested: { ids: new Set([1, 2]), }, }); expect(error.toJSON().config).toEqual({ tags: ['a', 'b'], nested: { ids: [1, 2], }, }); }); describe('AxiosError.from', () => { it('adds config, code, request and response to the wrapped error', () => { const error = new Error('Boom!'); const request = { path: '/foo' }; const response = { status: 200, data: { foo: 'bar' } }; const axiosError = AxiosError.from(error, 'ESOMETHING', { foo: 'bar' }, request, response); expect(axiosError.config).toEqual({ foo: 'bar' }); expect(axiosError.code).toBe('ESOMETHING'); expect(axiosError.request).toBe(request); expect(axiosError.response).toBe(response); expect(axiosError.isAxiosError).toBe(true); }); it('returns an AxiosError instance', () => { const axiosError = AxiosError.from(new Error('Boom!'), 'ESOMETHING', { foo: 'bar' }); expect(axiosError).toBeInstanceOf(AxiosError); }); it('preserves status from the original error when response is not provided', () => { const error = new Error('Network Error'); error.status = 404; const axiosError = AxiosError.from(error, 'ERR_NETWORK', { foo: 'bar' }); expect(axiosError.status).toBe(404); }); it('prefers response.status over error.status when response is provided', () => { const error = new Error('Error'); error.status = 500; const response = { status: 404 }; const axiosError = AxiosError.from(error, 'ERR_BAD_REQUEST', {}, null, response); expect(axiosError.status).toBe(404); }); it('synthesizes a message from AggregateError.errors when the aggregate message is empty (#6721)', () => { const timeout = Object.assign(new Error('connect ETIMEDOUT 1.2.3.4:443'), { code: 'ETIMEDOUT', }); const unreach = Object.assign(new Error('connect EHOSTUNREACH ::1:443'), { code: 'EHOSTUNREACH', }); const aggregate = new AggregateError([timeout, unreach]); expect(aggregate.message).toBe(''); const axiosError = AxiosError.from(aggregate, 'ETIMEDOUT', { foo: 'bar' }); const expectedMessage = 'connect ETIMEDOUT 1.2.3.4:443; connect EHOSTUNREACH ::1:443'; expect(axiosError.message).toBe(expectedMessage); expect(axiosError.toJSON().message).toBe(expectedMessage); }); it('preserves an explicit AggregateError message', () => { const aggregate = new AggregateError([new Error('inner failure')], 'outer failure'); const axiosError = AxiosError.from(aggregate, 'ETIMEDOUT', {}); expect(axiosError.message).toBe('outer failure'); }); it('ignores aggregate entries that cannot be converted to strings', () => { const throwsOnCoercion = { [Symbol.toPrimitive]() { throw new Error('cannot convert'); }, }; const aggregate = new AggregateError([Object.create(null), throwsOnCoercion]); const axiosError = AxiosError.from(aggregate, 'ETIMEDOUT', {}); expect(axiosError).toBeInstanceOf(AxiosError); expect(axiosError.message).toBe('AggregateError'); expect(axiosError.cause).toBe(aggregate); }); it('leaves a normal error message unchanged', () => { const axiosError = AxiosError.from(new Error('Boom!'), 'ESOMETHING', {}); expect(axiosError.message).toBe('Boom!'); }); }); describe('AxiosError.from stack preservation (#6670)', () => { const nativeStack = 'Error: Boom!\n at somewhereDeepInNode (node:internal/x:1:1)'; it('appends the wrapped error stack to the AxiosError stack', () => { const error = new Error('Boom!'); error.stack = nativeStack; const axiosError = AxiosError.from(error, 'ERR_NETWORK', { foo: 'bar' }); expect(axiosError.stack).toContain('\nCaused by: Error: Boom!'); expect(axiosError.stack).toContain('at somewhereDeepInNode'); }); it('keeps the axios frames ahead of the cause section', () => { const error = new Error('Boom!'); error.stack = nativeStack; const axiosError = AxiosError.from(error, 'ERR_NETWORK'); const [head, tail] = axiosError.stack.split('\nCaused by: '); expect(head).not.toContain('somewhereDeepInNode'); expect(tail).toBe(nativeStack); }); it('still exposes the wrapped error through cause', () => { const error = new Error('Boom!'); error.stack = nativeStack; const axiosError = AxiosError.from(error, 'ERR_NETWORK'); expect(axiosError.cause).toBe(error); expect(axiosError.cause.stack).toBe(nativeStack); }); it('nests rather than repeats the cause section when an AxiosError is re-wrapped', () => { const error = new Error('Boom!'); error.stack = nativeStack; const inner = AxiosError.from(error, 'ERR_NETWORK'); const outer = AxiosError.from(inner, 'ERR_BAD_RESPONSE'); expect(outer.stack.split(nativeStack).length - 1).toBe(1); expect(outer.stack.endsWith('\nCaused by: ' + inner.stack)).toBe(true); expect(outer.stack.split('\nCaused by: ')).toHaveLength(3); expect(outer.cause).toBe(inner); }); it.each([undefined, null, '', 42, {}])('ignores an unusable wrapped stack: %s', (stack) => { const error = new Error('Boom!'); error.stack = stack; const axiosError = AxiosError.from(error, 'ERR_NETWORK'); expect(axiosError.stack).not.toContain('Caused by:'); }); it('does not throw when reading the wrapped stack throws', () => { const error = new Error('Boom!'); Object.defineProperty(error, 'stack', { get() { throw new Error('stack hook exploded'); }, }); expect(() => AxiosError.from(error, 'ERR_NETWORK')).not.toThrow(); }); it.each([undefined, null, '', 42, {}])('preserves a nonstandard target stack: %s', (stack) => { const error = new Error('Boom!'); error.stack = nativeStack; const original = Error.prepareStackTrace; let axiosError; Error.prepareStackTrace = () => stack; try { axiosError = AxiosError.from(error, 'ERR_NETWORK'); expect(axiosError.stack).toBe(stack); } finally { Error.prepareStackTrace = original; } expect(axiosError.cause).toBe(error); expect(axiosError.code).toBe('ERR_NETWORK'); }); it('preserves the failure when formatting the target stack throws', () => { const error = new Error('Boom!'); error.stack = nativeStack; const original = Error.prepareStackTrace; let axiosError; Error.prepareStackTrace = () => { throw new Error('stack hook exploded'); }; try { axiosError = AxiosError.from(error, 'ERR_NETWORK'); } finally { Error.prepareStackTrace = original; } expect(axiosError.cause).toBe(error); expect(axiosError.message).toBe('Boom!'); expect(axiosError.code).toBe('ERR_NETWORK'); }); it('uses the wrapped error name in the materialized stack header', () => { const error = new TypeError('Boom!'); const axiosError = AxiosError.from(error, 'ERR_NETWORK'); expect(axiosError.stack.startsWith('TypeError: Boom!\n')).toBe(true); }); it('preserves a target stack made read-only by instrumentation', () => { const error = new Error('Boom!'); error.stack = nativeStack; const original = Error.prepareStackTrace; let axiosError; Error.prepareStackTrace = (target) => { Object.defineProperty(target, 'stack', { value: 'read-only stack', writable: false, configurable: true, }); return 'read-only stack'; }; try { axiosError = AxiosError.from(error, 'ERR_NETWORK'); } finally { Error.prepareStackTrace = original; } expect(axiosError.stack).toBe('read-only stack'); expect(axiosError.cause).toBe(error); }); }); describe('cause serialization (regression #7205)', () => { // A wrapped low-level error carrying a circular reference, like a Node // socket/request held by network errors. const makeCircularCause = () => { const cause = new Error('socket hang up'); cause.code = 'ECONNRESET'; const socket = { name: 'Socket' }; socket.self = socket; // circular cause.socket = socket; return cause; }; it('sets `cause` as a non-enumerable own property (native Error parity)', () => { const axiosError = AxiosError.from(new Error('boom'), 'ERR_NETWORK', { url: '/x' }); const descriptor = Object.getOwnPropertyDescriptor(axiosError, 'cause'); expect(descriptor).toBeDefined(); expect(descriptor.enumerable).toBe(false); expect(Object.keys(axiosError)).not.toContain('cause'); expect('cause' in axiosError).toBe(true); // still discoverable via `in` }); it('keeps `cause` fully accessible for debugging', () => { const original = makeCircularCause(); const axiosError = AxiosError.from(original, 'ERR_NETWORK', { url: '/x' }); expect(axiosError.cause).toBe(original); expect(axiosError.cause.code).toBe('ECONNRESET'); }); it('does not break structured loggers / own-property serialization', () => { const axiosError = AxiosError.from(makeCircularCause(), 'ERR_NETWORK', { url: '/x' }); // pino/winston-style: enumerate own enumerable props and serialize. const loggerWalk = () => JSON.stringify(Object.fromEntries(Object.entries(axiosError))); expect(loggerWalk).not.toThrow(); expect(() => JSON.stringify(axiosError)).not.toThrow(); expect(() => JSON.stringify({ wrapped: axiosError })).not.toThrow(); }); it('omits `cause` from toJSON() output', () => { const axiosError = AxiosError.from(makeCircularCause(), 'ERR_NETWORK', { url: '/x' }); expect(axiosError.toJSON()).not.toHaveProperty('cause'); }); }); it('is recognized as a native error by Node util/types', () => { expect(isNativeError(new AxiosError('My Axios Error'))).toBe(true); }); it('supports static error-code properties', () => { const error = new AxiosError('My Axios Error', AxiosError.ECONNABORTED); expect(error.code).toBe(AxiosError.ECONNABORTED); }); it('sets status when response is passed to constructor', () => { const error = new AxiosError('test', 'foo', {}, {}, { status: 400 }); expect(error.status).toBe(400); }); describe('status field behaviour (issue #5330)', () => { it('error.status equals response.status for 4xx errors', () => { // Regression test: error.status must be directly accessible without // going through error.response.status. const error = new AxiosError( 'Request failed with status code 404', AxiosError.ERR_BAD_REQUEST, {}, {}, { status: 404, statusText: 'Not Found' } ); expect(error.status).toBe(404); expect(error.status).toBe(error.response.status); }); it('error.status equals response.status for 5xx errors', () => { const error = new AxiosError( 'Request failed with status code 503', AxiosError.ERR_BAD_RESPONSE, {}, {}, { status: 503, statusText: 'Service Unavailable' } ); expect(error.status).toBe(503); }); it('error.status is undefined when no response is provided (network errors)', () => { // Network errors (ECONNREFUSED, ETIMEDOUT, etc.) have no HTTP response, // so error.status must be undefined — not 0 or null. const error = new AxiosError('Network Error', AxiosError.ERR_NETWORK, {}, {}); expect(error.status).toBeUndefined(); expect(error.response).toBeUndefined(); }); it('error.status is included in toJSON output', () => { const error = new AxiosError('test', 'ERR_BAD_REQUEST', {}, {}, { status: 401 }); expect(error.toJSON().status).toBe(401); }); }); it('keeps message enumerable for backward compatibility', () => { const error = new AxiosError('Test error message', 'ERR_TEST', { foo: 'bar' }); expect(Object.keys(error)).toContain('message'); expect(Object.entries(error).find(([key]) => key === 'message')?.[1]).toBe('Test error message'); expect({ ...error }.message).toBe('Test error message'); expect(Object.getOwnPropertyDescriptor(error, 'message')?.enumerable).toBe(true); }); // Opt-in redaction: when `config.redact` is an array of key names, every // matching key (case-insensitive, at any depth) has its value replaced with // the redaction marker in the toJSON snapshot. Undefined leaves the legacy // serialization untouched so existing consumers see no behavior change. describe('toJSON redaction via config.redact', () => { it('leaves config untouched when redact is undefined', () => { const config = { url: '/api', auth: { username: 'alice', password: 'secret' }, }; const error = new AxiosError('Boom', 'ECODE', config); const json = error.toJSON(); expect(json.config.auth.username).toBe('alice'); expect(json.config.auth.password).toBe('secret'); }); it('ignores inherited redact accessors', () => { const prototype = {}; Object.defineProperty(prototype, 'redact', { get() { throw new Error('inherited redact getter should not run'); }, }); const config = Object.create(prototype); config.auth = { username: 'alice', password: 'secret' }; const error = new AxiosError('Boom', 'ECODE', config); const json = error.toJSON(); expect(json.config.auth.username).toBe('alice'); expect(json.config.auth.password).toBe('secret'); }); it('leaves config untouched when redact is an empty array', () => { const config = { auth: { username: 'alice', password: 'secret' }, redact: [], }; const error = new AxiosError('Boom', 'ECODE', config); expect(error.toJSON().config.auth.password).toBe('secret'); }); it('replaces top-level matching keys with the redaction marker', () => { const config = { url: '/api', auth: { username: 'alice', password: 'secret' }, redact: ['auth'], }; const error = new AxiosError('Boom', 'ECODE', config); const json = error.toJSON(); expect(json.config.url).toBe('/api'); expect(json.config.auth).toBe('[REDACTED ****]'); }); it('replaces matching keys at any nesting depth', () => { const config = { auth: { username: 'alice', password: 'secret' }, proxy: { auth: { username: 'pu', password: 'pp' } }, redact: ['password'], }; const error = new AxiosError('Boom', 'ECODE', config); const json = error.toJSON(); expect(json.config.auth.username).toBe('alice'); expect(json.config.auth.password).toBe('[REDACTED ****]'); expect(json.config.proxy.auth.password).toBe('[REDACTED ****]'); expect(json.config.proxy.auth.username).toBe('pu'); }); it('matches case-insensitively', () => { const config = { headers: { Authorization: 'Bearer abc' }, redact: ['authorization'], }; const error = new AxiosError('Boom', 'ECODE', config); expect(error.toJSON().config.headers.Authorization).toBe('[REDACTED ****]'); }); it('redacts headers stored in an AxiosHeaders instance', () => { const headers = new AxiosHeaders(); headers.set('Authorization', 'Bearer abc'); headers.set('X-Trace', 'trace-id'); const config = { headers, redact: ['Authorization'] }; const error = new AxiosError('Boom', 'ECODE', config); const serialized = error.toJSON().config.headers; expect(serialized.Authorization).toBe('[REDACTED ****]'); expect(serialized['X-Trace']).toBe('trace-id'); }); it('redacts inside arrays of objects', () => { const config = { items: [{ token: 't1' }, { token: 't2', name: 'keep' }], redact: ['token'], }; const error = new AxiosError('Boom', 'ECODE', config); const json = error.toJSON(); expect(json.config.items[0].token).toBe('[REDACTED ****]'); expect(json.config.items[1].token).toBe('[REDACTED ****]'); expect(json.config.items[1].name).toBe('keep'); }); it('does not crash on circular config references', () => { const config = { auth: { password: 'secret' }, redact: ['password'] }; config.self = config; const error = new AxiosError('Boom', 'ECODE', config); const json = error.toJSON(); expect(json.config.auth.password).toBe('[REDACTED ****]'); expect(Object.prototype.hasOwnProperty.call(json.config, 'self')).toBe(false); }); it('preserves legacy toJSONObject handling for values with toJSON', () => { const issuedAt = new Date('2026-01-01T00:00:00.000Z'); const endpoint = new URL('https://example.com/users'); const config = { issuedAt, endpoint, auth: { password: 'secret' }, redact: ['password'], }; const error = new AxiosError('Boom', 'ECODE', config); const json = error.toJSON(); expect(json.config.issuedAt).toBe(issuedAt); expect(json.config.endpoint).toBe(endpoint); expect(json.config.auth.password).toBe('[REDACTED ****]'); }); it('does not let a polluted Object.prototype.toJSON bypass redaction', () => { class Credentials { constructor() { this.password = 'secret'; } } Object.prototype.toJSON = function () { return this; }; const config = { auth: { password: 'secret' }, credentials: new Credentials(), items: [{ token: 't1' }], redact: ['password', 'token'], }; const error = new AxiosError('Boom', 'ECODE', config); try { const json = error.toJSON(); expect(json.config.auth.password).toBe('[REDACTED ****]'); expect(json.config.credentials.password).toBe('[REDACTED ****]'); expect(json.config.items[0].token).toBe('[REDACTED ****]'); } finally { delete Object.prototype.toJSON; } }); it('copies __proto__ as data without changing the redaction output prototype', () => { const config = { redact: ['password'] }; Object.defineProperty(config, '__proto__', { value: { password: 'secret' }, enumerable: true, configurable: true, }); const error = new AxiosError('Boom', 'ECODE', config); const json = error.toJSON(); expect(Object.getPrototypeOf(json.config)).toBe(null); expect(Object.prototype.hasOwnProperty.call(json.config, '__proto__')).toBe(true); expect(json.config.__proto__.password).toBe('[REDACTED ****]'); }); it('does not mutate the original config or AxiosHeaders', () => { const headers = new AxiosHeaders(); headers.set('Authorization', 'Bearer abc'); const config = { auth: { username: 'alice', password: 'secret' }, headers, redact: ['password', 'Authorization'], }; const error = new AxiosError('Boom', 'ECODE', config); error.toJSON(); expect(config.auth.password).toBe('secret'); expect(headers.get('Authorization')).toBe('Bearer abc'); }); it('keeps the redact array itself visible in the snapshot', () => { const config = { auth: { password: 'secret' }, redact: ['password'], }; const error = new AxiosError('Boom', 'ECODE', config); // Useful for debugging — operators can see what was being redacted. expect(error.toJSON().config.redact).toEqual(['password']); }); }); }); axios-axios-aa54a5e/tests/unit/core/InterceptorManager.test.js000066400000000000000000000171721526660636600246530ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import InterceptorManager from '../../../lib/core/InterceptorManager.js'; describe('core::InterceptorManager', () => { it('does not accumulate trailing tombstones during repeated use and eject', () => { const manager = new InterceptorManager(); const persistent = manager.use(() => {}); const transient = () => {}; for (let i = 0; i < 10000; i++) { const id = manager.use(transient); manager.eject(id); } expect(Array.isArray(manager.handlers)).toBe(true); expect(manager.handlers).toHaveLength(1); manager.eject(persistent); expect(manager.handlers).toHaveLength(0); }); it('preserves interior tombstones until trailing handlers are removed', () => { const manager = new InterceptorManager(); manager.use(() => {}); const second = manager.use(() => {}); const third = manager.use(() => {}); manager.eject(second); expect(manager.handlers).toHaveLength(3); expect(manager.handlers[1]).toBeNull(); manager.eject(third); expect(manager.handlers).toHaveLength(1); expect(manager.handlers[0]).not.toBeNull(); }); it('ejects an interceptor when its ID is passed as a numeric string', () => { const manager = new InterceptorManager(); const id = manager.use(() => {}); manager.eject(String(id)); expect(manager.handlers).toHaveLength(0); }); it('uses string IDs rather than array indices after compaction', () => { const manager = new InterceptorManager(); const staleId = manager.use(() => {}); manager.eject(staleId); const active = () => {}; const activeId = manager.use(active); expect(activeId).not.toBe(0); expect(manager.handlers[0].fulfilled).toBe(active); manager.eject(String(staleId)); expect(manager.handlers[0].fulfilled).toBe(active); manager.eject(String(activeId)); manager.eject(String(activeId)); expect(manager.handlers).toHaveLength(0); const next = () => {}; manager.use(next); manager.eject(String(activeId)); expect(manager.handlers).toHaveLength(1); expect(manager.handlers[0].fulfilled).toBe(next); }); it.each([ undefined, null, '', '00', ' 0', '0 ', '+0', '-0', '0.0', '0e0', '0x0', 'NaN', 'Infinity', 'not-an-id', 'constructor', '__proto__', '1', 1, false, true, ])('ignores an unknown or noncanonical ID: %s', (id) => { const manager = new InterceptorManager(); const handler = () => {}; manager.use(handler); manager.eject(id); expect(manager.handlers).toHaveLength(1); expect(manager.handlers[0].fulfilled).toBe(handler); }); it('does not reuse an ejected interceptor ID after trimming its handler', () => { const manager = new InterceptorManager(); const active = () => {}; const staleId = manager.use(() => {}); manager.eject(staleId); const activeId = manager.use(active); expect(activeId).not.toBe(staleId); manager.eject(staleId); expect(manager.handlers).toHaveLength(1); expect(manager.handlers[0].fulfilled).toBe(active); manager.eject(activeId); expect(manager.handlers).toHaveLength(0); }); it('invalidates interceptor IDs when the handlers array is replaced', () => { const manager = new InterceptorManager(); const stale = () => {}; const active = () => {}; const staleId = manager.use(stale); manager.handlers = [...manager.handlers]; const activeId = manager.use(active); expect(activeId).not.toBe(staleId); manager.eject(staleId); expect(manager.handlers.map((handler) => handler.fulfilled)).toEqual([stale, active]); manager.eject(activeId); expect(manager.handlers.map((handler) => handler.fulfilled)).toEqual([stale]); }); it('does not retain interceptor entries when the handlers array is cleared in place', () => { const manager = new InterceptorManager(); const handler = () => {}; for (let i = 0; i < 10000; i++) { manager.handlers.length = 0; manager.use(handler); } const internals = manager[Object.getOwnPropertySymbols(manager)[0]]; expect(manager.handlers).toHaveLength(1); expect(internals.handlerEntries.size).toBe(1); }); it('defers compaction while iterating so newly registered handlers are not visited', () => { const manager = new InterceptorManager(); const first = () => {}; const removed = () => {}; const added = () => {}; const visited = []; let mutated = false; manager.use(first); const removedId = manager.use(removed); manager.forEach((handler) => { visited.push(handler.fulfilled); if (!mutated && handler.fulfilled === first) { mutated = true; manager.eject(removedId); manager.use(added); } }); expect(visited).toEqual([first]); const nextVisited = []; manager.forEach((handler) => nextVisited.push(handler.fulfilled)); expect(nextVisited).toEqual([first, added]); }); it('compacts handlers ejected during iteration after iteration completes', () => { const manager = new InterceptorManager(); const first = () => {}; const second = () => {}; const visited = []; manager.use(first); const secondId = manager.use(second); manager.forEach((handler) => { visited.push(handler.fulfilled); if (handler.fulfilled === first) { manager.eject(secondId); } }); expect(visited).toEqual([first]); expect(manager.handlers).toHaveLength(1); }); it('preserves iteration over the current handlers when cleared during iteration', () => { const manager = new InterceptorManager(); const first = () => {}; const second = () => {}; const visited = []; manager.use(first); manager.use(second); manager.forEach((handler) => { visited.push(handler.fulfilled); if (handler.fulfilled === first) { manager.clear(); } }); expect(visited).toEqual([first, second]); expect(manager.handlers).toHaveLength(0); }); // Regression for #11114: `handlers` is public and `clear()` has always // tolerated a nullish value, but syncHandlerEntries read `.length` before // checking it, so every later request threw. describe.each([ ['null', null], ['undefined', undefined], ])('with handlers set to %s', (_label, value) => { it('forEach is a no-op instead of throwing', () => { const manager = new InterceptorManager(); manager.use(() => {}); manager.handlers = value; const visited = []; expect(() => manager.forEach((h) => visited.push(h))).not.toThrow(); expect(visited).toEqual([]); }); it('stays usable across repeated forEach calls', () => { const manager = new InterceptorManager(); manager.use(() => {}); manager.handlers = value; // The second pass takes the `handlers === handlersRef` branch, which is // where the original report's stack trace landed. expect(() => { manager.forEach(() => {}); manager.forEach(() => {}); }).not.toThrow(); }); it('eject does not throw', () => { const manager = new InterceptorManager(); const id = manager.use(() => {}); manager.handlers = value; expect(() => manager.eject(id)).not.toThrow(); }); it('accepts and invokes a new handler', () => { const manager = new InterceptorManager(); manager.use(() => {}); manager.handlers = value; const fulfilled = () => {}; manager.use(fulfilled); const visited = []; manager.forEach((h) => visited.push(h.fulfilled)); expect(visited).toEqual([fulfilled]); }); }); }); axios-axios-aa54a5e/tests/unit/core/buildFullPath.test.js000066400000000000000000000117731526660636600236220ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import buildFullPath from '../../../lib/core/buildFullPath.js'; import AxiosError from '../../../lib/core/AxiosError.js'; describe('core::buildFullPath', () => { it('combines URLs when the requested URL is relative', () => { expect(buildFullPath('https://api.github.com', '/users')).toBe('https://api.github.com/users'); }); it('removes repeated trailing slashes from baseURL when combining URLs', () => { expect(buildFullPath('https://api.github.com///', '/users')).toBe( 'https://api.github.com/users' ); }); it('does not combine URLs when the requested URL is absolute', () => { expect(buildFullPath('https://api.github.com', 'https://api.example.com/users')).toBe( 'https://api.example.com/users' ); }); it('combines URLs when requested URL is absolute and allowAbsoluteUrls is false', () => { expect(buildFullPath('https://api.github.com', 'https://api.example.com/users', false)).toBe( 'https://api.github.com/https://api.example.com/users' ); }); it('does not combine URLs when baseURL is missing and allowAbsoluteUrls is false', () => { expect(buildFullPath(undefined, 'https://api.example.com/users', false)).toBe( 'https://api.example.com/users' ); }); it('does not combine URLs when baseURL is not configured', () => { expect(buildFullPath(undefined, '/users')).toBe('/users'); }); it('combines URLs when baseURL and requested URL are both relative', () => { expect(buildFullPath('/api', '/users')).toBe('/api/users'); }); it('rejects HTTP URLs missing slashes after the protocol', () => { for (const call of [ () => buildFullPath(undefined, 'https:example.com/users'), () => buildFullPath(undefined, '\thttps:example.com/users'), () => buildFullPath(undefined, '\u0000https:example.com/users'), () => buildFullPath(undefined, 'h\nttp:example.com/users'), () => buildFullPath(undefined, 'ht\ttp:example.com/users'), () => buildFullPath(undefined, 'htt\rp:example.com/users'), () => buildFullPath(undefined, 'http:/example.com/users'), () => buildFullPath('http:example.com/api', '/users'), ]) { let error; try { call(); } catch (err) { error = err; } expect(error).toBeInstanceOf(AxiosError); expect(error.code).toBe(AxiosError.ERR_INVALID_URL); expect(error.message).toMatch(/^Invalid URL ".*": missing "\/\/" after protocol$/); } }); it('includes the offending URL in the error message', () => { let error; try { buildFullPath(undefined, 'https:example.com/users'); } catch (err) { error = err; } expect(error.message).toBe('Invalid URL "https:example.com/users": missing "//" after protocol'); }); it('reports the control-character-normalized URL for a leading control character', () => { let error; try { buildFullPath(undefined, '\thttps:example.com/users'); } catch (err) { error = err; } expect(error.message).toBe('Invalid URL "https:example.com/users": missing "//" after protocol'); }); it('reports the control-character-normalized URL for an interior control character', () => { let error; try { buildFullPath(undefined, 'h\nttp:example.com/users'); } catch (err) { error = err; } expect(error.message).toBe('Invalid URL "http:example.com/users": missing "//" after protocol'); }); it('redacts credentials and query/fragment values while keeping the URL identifiable', () => { let error; try { buildFullPath( undefined, 'https:admin:hunter2@api.example.com/v1?apikey=topsecret&id=42#token=xyz&opaque' ); } catch (err) { error = err; } expect(error.message).toBe( 'Invalid URL "https:[REDACTED ****]@api.example.com/v1?apikey=[REDACTED ****]&id=[REDACTED ****]#token=[REDACTED ****]&[REDACTED ****]": missing "//" after protocol' ); // Credentials and parameter values must not leak. for (const secret of ['admin', 'hunter2', 'topsecret', '42', 'xyz', 'opaque']) { expect(error.message).not.toContain(secret); } // Non-sensitive structure stays intact so the request is still identifiable. for (const kept of ['api.example.com', '/v1', 'apikey', 'id', 'token']) { expect(error.message).toContain(kept); } }); it('does not reject an unused malformed baseURL for absolute requests', () => { expect(buildFullPath('http:example.com/api', 'https://api.example.com/users')).toBe( 'https://api.example.com/users' ); }); it('rejects a malformed baseURL when absolute requests are forced through baseURL', () => { let error; try { buildFullPath('http:example.com/api', 'https://api.example.com/users', false); } catch (err) { error = err; } expect(error).toBeInstanceOf(AxiosError); expect(error.code).toBe(AxiosError.ERR_INVALID_URL); expect(error.message).toBe('Invalid URL "http:example.com/api": missing "//" after protocol'); }); }); axios-axios-aa54a5e/tests/unit/core/dispatchRequest.test.js000066400000000000000000000300661526660636600242270ustar00rootroot00000000000000import { afterEach, beforeEach, describe, it, vi } from 'vitest'; import assert from 'assert'; import axios from '../../../index.js'; import dispatchRequest from '../../../lib/core/dispatchRequest.js'; import AxiosError from '../../../lib/core/AxiosError.js'; import defaults from '../../../lib/defaults/index.js'; import resolveConfig from '../../../lib/helpers/resolveConfig.js'; class ReactNativeFormData { append() {} getParts() { return []; } get [Symbol.toStringTag]() { return 'FormData'; } } function baseConfig(overrides = {}) { return { method: 'get', url: '/test', headers: {}, transformRequest: defaults.transformRequest, transformResponse: defaults.transformResponse, transitional: { silentJSONParsing: false, forcedJSONParsing: true }, responseType: 'json', ...overrides, }; } describe('core::dispatchRequest', () => { describe('JSON FormData transform', () => { it('rejects deeply nested field paths before adapter dispatch', async () => { const data = new FormData(); let adapterCalled = false; data.append('foo' + '[bar]'.repeat(101), '123'); const config = baseConfig({ data, headers: { 'Content-Type': 'application/json' }, method: 'post', adapter(adapterConfig) { adapterCalled = true; return Promise.resolve({ data: null, status: 200, statusText: 'OK', headers: {}, config: adapterConfig, request: {}, }); }, }); let thrown; try { await dispatchRequest(config); } catch (e) { thrown = e; } assert.ok(thrown instanceof AxiosError, 'must be AxiosError'); assert.strictEqual(thrown.code, AxiosError.ERR_FORM_DATA_DEPTH_EXCEEDED); assert.strictEqual(adapterCalled, false); }); }); describe('JSON parse failure on adapter resolution', () => { it('rejects with AxiosError carrying response and status', async () => { const response = { data: '{bad json', status: 418, statusText: "I'm a teapot", headers: {}, config: null, request: {}, }; const config = baseConfig({ adapter: () => Promise.resolve(response) }); let thrown; try { await dispatchRequest(config); } catch (e) { thrown = e; } assert.ok(thrown instanceof AxiosError, 'must be AxiosError'); assert.strictEqual(thrown.code, AxiosError.ERR_BAD_RESPONSE); assert.strictEqual(thrown.response, response, 'error.response must be the original response'); assert.strictEqual(thrown.status, 418, 'error.status must equal response status'); }); it('cleans up config.response after the transform throws', async () => { const response = { data: '{bad json', status: 200, statusText: 'OK', headers: {}, config: null, request: {}, }; const config = baseConfig({ adapter: () => Promise.resolve(response) }); try { await dispatchRequest(config); } catch (_) { // expected } assert.strictEqual( Object.prototype.hasOwnProperty.call(config, 'response'), false, 'config.response must be deleted in finally' ); }); }); describe('JSON parse failure on adapter rejection', () => { it('rejects with AxiosError carrying response and status (rejection path)', async () => { const response = { data: '{bad json', status: 500, statusText: 'Internal Server Error', headers: {}, config: null, request: {}, }; const reason = new AxiosError('Request failed', AxiosError.ERR_BAD_RESPONSE); reason.response = response; const config = baseConfig({ adapter: () => Promise.reject(reason) }); let thrown; try { await dispatchRequest(config); } catch (e) { thrown = e; } assert.ok(thrown instanceof AxiosError, 'must be AxiosError'); assert.strictEqual(thrown.response, response, 'error.response must be the original response'); assert.strictEqual(thrown.status, 500, 'error.status must equal response status'); }); it('cleans up config.response after the rejection-path transform', async () => { const response = { data: '{bad json', status: 500, statusText: 'Internal Server Error', headers: {}, config: null, request: {}, }; const reason = new AxiosError('Request failed', AxiosError.ERR_BAD_RESPONSE); reason.response = response; const config = baseConfig({ adapter: () => Promise.reject(reason) }); try { await dispatchRequest(config); } catch (_) { // expected } assert.strictEqual( Object.prototype.hasOwnProperty.call(config, 'response'), false, 'config.response must be deleted in finally on the rejection path' ); }); }); describe('happy path', () => { it('clears default Content-Type for React Native FormData before adapter headers are sent', async () => { const data = new ReactNativeFormData(); const response = { data: '{"ok":true}', status: 200, statusText: 'OK', headers: {}, config: null, request: {}, }; const config = baseConfig({ method: 'post', data, adapter: (adapterConfig) => { assert.strictEqual( adapterConfig.headers.getContentType(), 'application/x-www-form-urlencoded', 'dispatchRequest should apply the default POST Content-Type first' ); const resolvedConfig = resolveConfig(adapterConfig); assert.strictEqual(resolvedConfig.data, data); assert.strictEqual(resolvedConfig.headers.getContentType(), undefined); assert.strictEqual( Object.prototype.hasOwnProperty.call(resolvedConfig.headers.toJSON(), 'Content-Type'), false, 'resolved adapter headers must omit Content-Type for React Native FormData' ); return Promise.resolve(response); }, }); const result = await dispatchRequest(config); assert.deepStrictEqual(result.data, { ok: true }); }); it('cleans up config.response after a successful resolution', async () => { const response = { data: '{"ok":true}', status: 200, statusText: 'OK', headers: {}, config: null, request: {}, }; const config = baseConfig({ adapter: () => Promise.resolve(response) }); const result = await dispatchRequest(config); assert.deepStrictEqual(result.data, { ok: true }); assert.strictEqual( Object.prototype.hasOwnProperty.call(config, 'response'), false, 'config.response must not be left set after a successful request' ); }); }); }); describe('core::dispatchRequest response timing', () => { let now; beforeEach(() => { now = 1000; vi.spyOn(Date, 'now').mockImplementation(() => now); }); afterEach(() => { vi.restoreAllMocks(); }); const createResponse = (config, status = 200) => ({ status, statusText: status === 200 ? 'OK' : 'Server Error', headers: {}, data: '{"ok":true}', config, }); for (const status of [200, 500]) { it.each([25, -25])( `measures only adapter execution for status ${status} with a %s ms clock change`, async (elapsed) => { const expectedDuration = Math.max(0, elapsed); const client = axios.create({ transformRequest: [ (data) => { now += 200; return data; }, ], transformResponse: [ function (data) { assert.strictEqual(this.response.duration, expectedDuration); now += 400; return JSON.parse(data); }, ], adapter: async (config) => { now += elapsed; const response = createResponse(config, status); if (status === 500) { throw new AxiosError('boom', AxiosError.ERR_BAD_RESPONSE, config, null, response); } return response; }, }); client.interceptors.request.use((config) => { now += 100; return config; }); client.interceptors.response.use( (response) => { now += 800; return response; }, (error) => { now += 800; throw error; } ); const result = await client.get('/timing').catch((error) => error); assert.strictEqual(axios.isAxiosError(result), status === 500); const response = status === 500 ? result.response : result; assert.strictEqual(response.duration, expectedDuration); assert.deepStrictEqual(response.data, { ok: true }); assert.strictEqual(now, 2500 + elapsed); } ); it(`retains duration when strict JSON parsing fails for status ${status}`, async () => { const error = await axios .get('/invalid-json', { responseType: 'json', transitional: { silentJSONParsing: false }, adapter: async (config) => { now += 25; const response = { ...createResponse(config, status), data: '{bad json' }; if (status === 500) { throw new AxiosError('boom', AxiosError.ERR_BAD_RESPONSE, config, null, response); } return response; }, }) .catch((error) => error); assert.strictEqual(error.code, AxiosError.ERR_BAD_RESPONSE); assert.strictEqual(error.response.status, status); assert.strictEqual(error.response.duration, 25); }); } it('keeps start times independent for overlapping requests', async () => { const pending = []; const client = axios.create({ adapter: (config) => new Promise((resolve) => { pending.push(() => resolve(createResponse(config))); }), }); const first = client.get('/first'); now += 10; const second = client.get('/second'); now += 20; pending[0](); assert.strictEqual((await first).duration, 30); now += 20; pending[1](); assert.strictEqual((await second).duration, 40); }); it.each([AxiosError.ERR_NETWORK, AxiosError.ECONNABORTED])( 'preserves %s errors without creating a response or duration', async (code) => { let reason; const error = await axios .get('/failure', { adapter: async (config) => { now += 25; reason = new AxiosError('failure', code, config); throw reason; }, }) .catch((error) => error); assert.strictEqual(error, reason); assert.strictEqual(error.response, undefined); assert.strictEqual(Object.hasOwn(error, 'duration'), false); } ); it.each(['signal', 'cancelToken'])( 'preserves %s cancellation before timing a response', async (kind) => { const controller = new AbortController(); const source = axios.CancelToken.source(); let response; const error = await axios .get('/cancel', { ...(kind === 'signal' ? { signal: controller.signal } : { cancelToken: source.token }), adapter: async (config) => { response = createResponse(config); now += 25; kind === 'signal' ? controller.abort() : source.cancel(); return response; }, }) .catch((error) => error); assert.strictEqual(axios.isCancel(error), true); assert.strictEqual(error.response, undefined); assert.strictEqual(Object.hasOwn(response, 'duration'), false); } ); it('replaces a custom adapter duration with the dispatch measurement', async () => { const response = await axios.get('/custom', { adapter: async (config) => { now += 25; return { ...createResponse(config), duration: 999 }; }, }); assert.strictEqual(response.duration, 25); }); }); axios-axios-aa54a5e/tests/unit/core/mergeConfig.test.js000066400000000000000000000333021526660636600233000ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import defaults from '../../../lib/defaults/index.js'; import mergeConfig from '../../../lib/core/mergeConfig.js'; import { AxiosHeaders } from '../../../index.js'; describe('core::mergeConfig', () => { it('accepts null for first argument', () => { expect(mergeConfig(null, { url: '/foo' })).toEqual({ url: '/foo' }); }); it('accepts undefined for second argument', () => { expect(mergeConfig(defaults, undefined)).toEqual(defaults); }); it('accepts an object for second argument', () => { expect(mergeConfig(defaults, {})).toEqual(defaults); }); it('does not leave references', () => { const merged = mergeConfig(defaults, {}); expect(merged).not.toBe(defaults); expect(merged.headers).not.toBe(defaults.headers); }); it('allows setting request options', () => { const config = { url: '__sample url__', method: '__sample method__', params: '__sample params__', data: { foo: true }, }; const merged = mergeConfig(defaults, config); expect(merged.url).toBe(config.url); expect(merged.method).toBe(config.method); expect(merged.params).toBe(config.params); expect(merged.data).toEqual(config.data); }); it('does not inherit request options', () => { const localDefaults = { method: '__sample method__', data: { foo: true }, }; const merged = mergeConfig(localDefaults, {}); expect(merged.method).toBeUndefined(); expect(merged.data).toBeUndefined(); }); for (const key of ['auth', 'headers', 'params', 'proxy']) { it(`sets new config for ${key} without default`, () => { const config1 = { [key]: undefined }; const config2 = { [key]: { user: 'foo', pass: 'test' } }; const expected = { [key]: { user: 'foo', pass: 'test' } }; expect(mergeConfig(config1, config2)).toEqual(expected); }); it(`merges ${key} with defaults`, () => { const config1 = { [key]: { user: 'foo', pass: 'bar' } }; const config2 = { [key]: { pass: 'test' } }; const expected = { [key]: { user: 'foo', pass: 'test' } }; expect(mergeConfig(config1, config2)).toEqual(expected); }); it.each([false, null, 123])(`overwrites default ${key} with %p`, (value) => { const config1 = { [key]: { user: 'foo', pass: 'test' } }; const config2 = { [key]: value }; const expected = { [key]: value }; expect(mergeConfig(config1, config2)).toEqual(expected); }); } it('allows setting other options', () => { const merged = mergeConfig(defaults, { timeout: 123 }); expect(merged.timeout).toBe(123); }); it('allows setting custom options', () => { const merged = mergeConfig(defaults, { foo: 'bar' }); expect(merged.foo).toBe('bar'); }); it('allows setting custom default options', () => { const merged = mergeConfig({ foo: 'bar' }, {}); expect(merged.foo).toBe('bar'); }); it('allows merging custom objects in config', () => { const merged = mergeConfig( { nestedConfig: { propertyOnDefaultConfig: true, }, }, { nestedConfig: { propertyOnRequestConfig: true, }, } ); expect(merged.nestedConfig.propertyOnDefaultConfig).toBe(true); expect(merged.nestedConfig.propertyOnRequestConfig).toBe(true); }); describe('headers', () => { it('allows merging with AxiosHeaders instances', () => { const merged = mergeConfig( { headers: new AxiosHeaders({ x: 1, y: 2, }), }, { headers: new AxiosHeaders({ X: 1, Y: 2, }), } ); expect(merged.headers).toEqual({ x: '1', y: '2', }); }); }); describe('valueFromConfig2Keys', () => { const config1 = { url: '/foo', method: 'post', data: { a: 3 } }; it('skips if config2 does not define the key', () => { expect(mergeConfig(config1, {})).toEqual({}); }); it('clones config2 when it is a plain object', () => { const data = { a: 1, b: 2 }; const merged = mergeConfig(config1, { data }); expect(merged.data).toEqual(data); expect(merged.data).not.toBe(data); }); it('clones config2 when it is an array', () => { const data = [1, 2, 3]; const merged = mergeConfig(config1, { data }); expect(merged.data).toEqual(data); expect(merged.data).not.toBe(data); }); it('sets config2 value directly for non-mergeable values', () => { const obj = Object.create({}); expect(mergeConfig(config1, { data: 1 }).data).toBe(1); expect(mergeConfig(config1, { data: 'str' }).data).toBe('str'); expect(mergeConfig(config1, { data: obj }).data).toBe(obj); expect(mergeConfig(config1, { data: null }).data).toBe(null); }); }); describe('mergeDeepPropertiesKeys', () => { it('skips when both config1 and config2 values are undefined', () => { expect(mergeConfig({ headers: undefined }, { headers: undefined })).toEqual({}); }); it('merges when both values are plain objects', () => { expect(mergeConfig({ headers: { a: 1, b: 1 } }, { headers: { b: 2, c: 2 } })).toEqual({ headers: { a: 1, b: 2, c: 2 }, }); }); it('clones config2 when it is a plain object', () => { const config1 = { headers: [1, 2, 3] }; const config2 = { headers: { a: 1, b: 2 } }; const merged = mergeConfig(config1, config2); expect(merged.headers).toEqual(config2.headers); expect(merged.headers).not.toBe(config2.headers); }); it('clones config2 when it is an array', () => { const config1 = { headers: { a: 1, b: 1 } }; const config2 = { headers: [1, 2, 3] }; const merged = mergeConfig(config1, config2); expect(merged.headers).toEqual(config2.headers); expect(merged.headers).not.toBe(config2.headers); }); it('sets config2 value directly for non-mergeable values', () => { const config1 = { headers: { a: 1, b: 1 } }; const obj = Object.create({}); expect(mergeConfig(config1, { headers: 1 }).headers).toBe(1); expect(mergeConfig(config1, { headers: 'str' }).headers).toBe('str'); expect(mergeConfig(config1, { headers: obj }).headers).toBe(obj); expect(mergeConfig(config1, { headers: null }).headers).toBe(null); }); it('clones config1 when it is a plain object', () => { const config1 = { headers: { a: 1, b: 2 } }; const merged = mergeConfig(config1, {}); expect(merged.headers).toEqual(config1.headers); expect(merged.headers).not.toBe(config1.headers); }); it('clones config1 when it is an array', () => { const config1 = { headers: [1, 2, 3] }; const merged = mergeConfig(config1, {}); expect(merged.headers).toEqual(config1.headers); expect(merged.headers).not.toBe(config1.headers); }); it('sets config1 value directly for non-mergeable values', () => { const obj = Object.create({}); expect(mergeConfig({ headers: 1 }, {}).headers).toBe(1); expect(mergeConfig({ headers: 'str' }, {}).headers).toBe('str'); expect(mergeConfig({ headers: obj }, {}).headers).toBe(obj); expect(mergeConfig({ headers: null }, {}).headers).toBe(null); }); }); describe('defaultToConfig2Keys', () => { it('skips when both config1 and config2 values are undefined', () => { expect(mergeConfig({ transformRequest: undefined }, { transformRequest: undefined })).toEqual( {} ); }); it('clones config2 when both values are plain objects', () => { const config1 = { transformRequest: { a: 1, b: 1 } }; const config2 = { transformRequest: { b: 2, c: 2 } }; const merged = mergeConfig(config1, config2); expect(merged.transformRequest).toEqual(config2.transformRequest); expect(merged.transformRequest).not.toBe(config2.transformRequest); }); it('clones config2 when it is an array', () => { const config1 = { transformRequest: { a: 1, b: 1 } }; const config2 = { transformRequest: [1, 2, 3] }; const merged = mergeConfig(config1, config2); expect(merged.transformRequest).toEqual(config2.transformRequest); expect(merged.transformRequest).not.toBe(config2.transformRequest); }); it('sets config2 value directly for non-mergeable values', () => { const config1 = { transformRequest: { a: 1, b: 1 } }; const obj = Object.create({}); expect(mergeConfig(config1, { transformRequest: 1 }).transformRequest).toBe(1); expect(mergeConfig(config1, { transformRequest: 'str' }).transformRequest).toBe('str'); expect(mergeConfig(config1, { transformRequest: obj }).transformRequest).toBe(obj); expect(mergeConfig(config1, { transformRequest: null }).transformRequest).toBe(null); }); it('clones config1 when it is a plain object', () => { const config1 = { transformRequest: { a: 1, b: 2 } }; const merged = mergeConfig(config1, {}); expect(merged.transformRequest).toEqual(config1.transformRequest); expect(merged.transformRequest).not.toBe(config1.transformRequest); }); it('clones config1 when it is an array', () => { const config1 = { transformRequest: [1, 2, 3] }; const merged = mergeConfig(config1, {}); expect(merged.transformRequest).toEqual(config1.transformRequest); expect(merged.transformRequest).not.toBe(config1.transformRequest); }); it('sets config1 value directly for non-mergeable values', () => { const obj = Object.create({}); expect(mergeConfig({ transformRequest: 1 }, {}).transformRequest).toBe(1); expect(mergeConfig({ transformRequest: 'str' }, {}).transformRequest).toBe('str'); expect(mergeConfig({ transformRequest: obj }, {}).transformRequest).toBe(obj); expect(mergeConfig({ transformRequest: null }, {}).transformRequest).toBe(null); }); }); describe('directMergeKeys', () => { it('merges when config2 defines the key', () => { expect(mergeConfig({}, { validateStatus: undefined })).toEqual({ validateStatus: undefined }); }); it('merges when both values are plain objects', () => { expect( mergeConfig({ validateStatus: { a: 1, b: 1 } }, { validateStatus: { b: 2, c: 2 } }) ).toEqual({ validateStatus: { a: 1, b: 2, c: 2 } }); }); it('clones config2 when it is a plain object', () => { const config1 = { validateStatus: [1, 2, 3] }; const config2 = { validateStatus: { a: 1, b: 2 } }; const merged = mergeConfig(config1, config2); expect(merged.validateStatus).toEqual(config2.validateStatus); expect(merged.validateStatus).not.toBe(config2.validateStatus); }); it('clones config2 when it is an array', () => { const config1 = { validateStatus: { a: 1, b: 2 } }; const config2 = { validateStatus: [1, 2, 3] }; const merged = mergeConfig(config1, config2); expect(merged.validateStatus).toEqual(config2.validateStatus); expect(merged.validateStatus).not.toBe(config2.validateStatus); }); it('sets config2 value directly for non-mergeable values', () => { const config1 = { validateStatus: { a: 1, b: 2 } }; const obj = Object.create({}); expect(mergeConfig(config1, { validateStatus: 1 }).validateStatus).toBe(1); expect(mergeConfig(config1, { validateStatus: 'str' }).validateStatus).toBe('str'); expect(mergeConfig(config1, { validateStatus: obj }).validateStatus).toBe(obj); expect(mergeConfig(config1, { validateStatus: null }).validateStatus).toBe(null); }); it('clones config1 when it is a plain object', () => { const config1 = { validateStatus: { a: 1, b: 2 } }; const merged = mergeConfig(config1, {}); expect(merged.validateStatus).toEqual(config1.validateStatus); expect(merged.validateStatus).not.toBe(config1.validateStatus); }); it('clones config1 when it is an array', () => { const config1 = { validateStatus: [1, 2, 3] }; const merged = mergeConfig(config1, {}); expect(merged.validateStatus).toEqual(config1.validateStatus); expect(merged.validateStatus).not.toBe(config1.validateStatus); }); it('sets config1 value directly for non-mergeable values', () => { const obj = Object.create({}); expect(mergeConfig({ validateStatus: 1 }, {}).validateStatus).toBe(1); expect(mergeConfig({ validateStatus: 'str' }, {}).validateStatus).toBe('str'); expect(mergeConfig({ validateStatus: obj }, {}).validateStatus).toBe(obj); expect(mergeConfig({ validateStatus: null }, {}).validateStatus).toBe(null); }); it('keeps legacy undefined behavior by default', () => { expect(mergeConfig(defaults, { validateStatus: undefined }).validateStatus).toBeUndefined(); }); it('keeps config1 value when the transitional option is disabled (issue #6688)', () => { const validateStatus = () => false; expect( mergeConfig( { validateStatus }, { validateStatus: undefined, transitional: { validateStatusUndefinedResolves: false }, } ).validateStatus ).toBe(validateStatus); expect(mergeConfig(defaults, { validateStatus: null }).validateStatus).toBe(null); }); it('supports symbol keys', () => { const symbol = Symbol(); const config1 = { [symbol]: { a: 1, b: 2 } }; const config2 = { [symbol]: { b: 3, c: 4 } }; const merged = mergeConfig(config1, config2); expect(merged[symbol]).toEqual({ a: 1, b: 3, c: 4 }); }); it('preserves request-only symbol keys', () => { const symbol = Symbol('some flag used in request interceptor'); const merged = mergeConfig(defaults, { [symbol]: true }); expect(merged[symbol]).toBe(true); }); }); }); axios-axios-aa54a5e/tests/unit/core/paramsMerge.test.js000066400000000000000000000303261526660636600233210ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import axios from '../../../index.js'; import mergeConfig from '../../../lib/core/mergeConfig.js'; import buildURL from '../../../lib/helpers/buildURL.js'; function nest(depth) { let result = { leaf: 'value' }; while (depth--) result = { next: result }; return result; } function leaf(params) { let result = params; while (result.next) result = result.next; return result.leaf; } describe('params merge', function () { [null, false, 'replacement', ['replacement']].forEach(function (params) { it('replaces deep defaults with ' + String(params), function () { const defaults = { params: nest(150) }; const merged = mergeConfig(defaults, { params }); assert.deepStrictEqual(merged.params, params); assert.strictEqual(leaf(defaults.params), 'value'); }); }); it('replaces deep nested defaults while retaining sibling fields', function () { const params = { page: 1, filters: { deep: nest(150), active: true } }; const merged = mergeConfig({ params }, { params: { filters: { deep: null } } }); assert.deepStrictEqual(merged.params, { page: 1, filters: { deep: null, active: true }, }); assert.strictEqual(leaf(params.filters.deep), 'value'); }); it('does not read default branches replaced by request values', function () { const params = { retained: true }; Object.defineProperty(params, 'replaced', { enumerable: true, get: function () { throw new Error('discarded default should not be read'); }, }); assert.deepStrictEqual(mergeConfig({ params }, { params: { replaced: null } }).params, { retained: true, replaced: null, }); }); it('allows a replacement to remove a default cycle', function () { const params = { retained: true }; params.next = params; assert.deepStrictEqual(mergeConfig({ params }, { params: { next: { next: null } } }).params, { retained: true, next: { retained: true, next: null }, }); assert.strictEqual(mergeConfig({ params }, { params: null }).params, null); }); it('copies deep params iteratively for a custom serializer', function () { const params = nest(512); const merged = mergeConfig({}, { params }); assert.notStrictEqual(merged.params, params); let source = params; let target = merged.params; while (source.next) { assert.notStrictEqual(target.next, source.next); target = target.next; source = source.next; } assert.strictEqual(target.leaf, 'value'); assert.strictEqual( axios.getUri({ url: '/resource', params, paramsSerializer: () => 'accepted=true' }), '/resource?accepted=true' ); }); it('retains nested merge, array-copy, undefined, and symbol behavior', function () { const symbol = Symbol('field'); const array = [{ value: 'shared array element' }]; const merged = mergeConfig( { params: { nested: { a: 1, b: 2 }, removed: 1, array, [symbol]: { a: 1 } } }, { params: { nested: { b: 3 }, removed: undefined, [symbol]: { b: 2 } } } ).params; assert.deepStrictEqual(merged.nested, { a: 1, b: 3 }); assert.strictEqual(Object.prototype.hasOwnProperty.call(merged, 'removed'), true); assert.strictEqual(merged.removed, undefined); assert.notStrictEqual(merged.array, array); assert.strictEqual(merged.array[0], array[0]); assert.deepStrictEqual(merged[symbol], { a: 1, b: 2 }); }); it('filters reserved merge keys at every object level', function () { const params = JSON.parse( '{"nested":{"__proto__":{"ignored":true},"constructor":"ignored","prototype":"ignored","kept":1}}' ); assert.deepStrictEqual(mergeConfig({}, { params }).params, { nested: { kept: 1 } }); }); it('merges and validates string-keyed params without symbol reflection', function () { const getOwnPropertySymbols = Object.getOwnPropertySymbols; const params = { filter: { active: true }, list: [{ field: 'value' }] }; const circular = {}; circular.next = circular; let merged; let uri; let circularError; try { Object.getOwnPropertySymbols = undefined; merged = mergeConfig({}, { params }); uri = axios.getUri({ url: '/resource', params: { field: 'value' } }); try { mergeConfig({}, { params: { list: [circular] } }); } catch (error) { circularError = error; } } finally { Object.getOwnPropertySymbols = getOwnPropertySymbols; } assert.deepStrictEqual(merged.params, params); assert.notStrictEqual(merged.params, params); assert.strictEqual(uri, '/resource?field=value'); assert.ok(circularError, 'Circular params should be rejected'); assert.strictEqual(circularError.isAxiosError, true); assert.strictEqual(circularError.code, 'ERR_BAD_OPTION_VALUE'); }); it('ignores symbols whose proxy descriptor is absent', function () { const symbol = Symbol('absent'); const params = new Proxy( { field: 'value' }, { ownKeys: function () { return ['field', symbol]; }, } ); assert.deepStrictEqual(mergeConfig({}, { params }).params, { field: 'value' }); assert.strictEqual(mergeConfig({}, { params: [params] }).params[0], params); }); it('rejects retained cycles, including through arrays, with an AxiosError', function () { const object = {}; object.next = object; const array = []; array.push(array); [object, array, { list: array }].forEach(function (params) { assert.throws( () => mergeConfig({}, { params, paramsSerializer: { maxDepth: Infinity } }), (error) => error.isAxiosError === true && error.code === 'ERR_BAD_OPTION_VALUE' ); }); }); ['constructor', 'prototype', '__proto__'].forEach(function (key) { it('preserves retained ' + key + ' accessors without reading them', async function () { const element = { field: 'value' }; let reads = 0; const get = function () { reads++; throw new Error('Reserved accessor must not run during merging'); }; Object.defineProperty(element, key, { get, enumerable: true }); const params = { list: [element] }; let serializerCalls = 0; const paramsSerializer = function (merged) { serializerCalls++; assert.strictEqual(merged.list[0], element); assert.strictEqual(Object.getOwnPropertyDescriptor(merged.list[0], key).get, get); assert.strictEqual(reads, 0); return 'field=' + merged.list[0].field; }; assert.strictEqual( axios.getUri({ url: '/resource', params, paramsSerializer }), '/resource?field=value' ); const response = await axios.get('/resource', { params, paramsSerializer, adapter: function (config) { return Promise.resolve({ data: buildURL(config.url, config.params, config.paramsSerializer), status: 200, headers: {}, config, }); }, }); assert.strictEqual(response.data, '/resource?field=value'); assert.strictEqual(serializerCalls, 2); assert.strictEqual(reads, 0); assert.strictEqual(Object.getOwnPropertyDescriptor(element, key).get, get); }); it('rejects retained ' + key + ' cycles before serializers or adapters run', async function () { const element = {}; Object.defineProperty(element, key, { value: element, enumerable: true }); const params = { list: [element] }; let serializerCalls = 0; let adapterCalls = 0; const paramsSerializer = function () { serializerCalls++; return 'recorded=true'; }; const isCircularParamsError = function (error) { return error.isAxiosError === true && error.code === 'ERR_BAD_OPTION_VALUE'; }; assert.throws( () => axios.getUri({ url: '/resource', params, paramsSerializer }), isCircularParamsError ); await assert.rejects( axios.get('/resource', { params, paramsSerializer, adapter: function (config) { adapterCalls++; return Promise.resolve({ data: 'recorded', status: 200, headers: {}, config }); }, }), isCircularParamsError ); assert.strictEqual(serializerCalls, 0); assert.strictEqual(adapterCalls, 0); assert.strictEqual(params.list[0], element); assert.strictEqual(Object.getOwnPropertyDescriptor(element, key).value, element); assert.strictEqual(Object.getPrototypeOf(element), Object.prototype); }); it('preserves acyclic ' + key + ' fields and shared array references', function () { const shared = { leaf: 'value' }; const element = { other: shared }; Object.defineProperty(element, key, { value: shared, enumerable: true }); const params = { list: [element, element] }; let serializerCalls = 0; const uri = axios.getUri({ url: '/resource', params, paramsSerializer: function (merged) { serializerCalls++; assert.notStrictEqual(merged.list, params.list); assert.strictEqual(merged.list[0], element); assert.strictEqual(merged.list[1], element); assert.strictEqual(Object.getOwnPropertyDescriptor(merged.list[0], key).value, shared); assert.strictEqual(merged.list[0].other, shared); return 'recorded=true'; }, }); assert.strictEqual(uri, '/resource?recorded=true'); assert.strictEqual(serializerCalls, 1); assert.strictEqual(Object.getPrototypeOf(element), Object.prototype); }); }); it('allows shared values that are not circular', function () { const shared = { leaf: 'value' }; assert.deepStrictEqual( mergeConfig({}, { params: { first: shared, second: { child: shared } } }).params, { first: shared, second: { child: shared } } ); }); }); describe('params serializer depth settings', function () { [200, Infinity].forEach(function (maxDepth) { it('honors request and instance maxDepth ' + maxDepth, async function () { const params = nest(150); const instance = axios.create({ paramsSerializer: { maxDepth } }); const expected = buildURL('/resource', params, { maxDepth }); assert.strictEqual(instance.getUri({ url: '/resource', params }), expected); assert.strictEqual( axios.getUri({ url: '/resource', params, paramsSerializer: { maxDepth } }), expected ); const response = await instance.get('/resource', { params, adapter: function (config) { return Promise.resolve({ data: buildURL(config.url, config.params, config.paramsSerializer), status: 200, headers: {}, config, }); }, }); assert.strictEqual(response.data, expected); }); }); it('retains default and lowered serializer depth limits', function () { [undefined, { maxDepth: 5 }].forEach(function (paramsSerializer) { assert.throws( () => axios.getUri({ url: '/resource', params: nest(150), paramsSerializer }), (error) => error.code === 'ERR_FORM_DATA_DEPTH_EXCEEDED' ); }); }); it('honors instance custom serializers in method aliases', async function () { const serialize = function (params) { assert.strictEqual(leaf(params), 'value'); return 'custom=value'; }; for (const paramsSerializer of [serialize, { serialize }]) { const instance = axios.create({ paramsSerializer }); const response = await instance.get('/resource', { params: nest(150), adapter: function (config) { return Promise.resolve({ data: buildURL(config.url, config.params, config.paramsSerializer), status: 200, headers: {}, config, }); }, }); assert.strictEqual(response.data, '/resource?custom=value'); } }); it('allows custom adapters to accept params without serializing them', async function () { const response = await axios.get('/resource', { params: nest(150), adapter: function (config) { return Promise.resolve({ data: leaf(config.params), status: 200, headers: {}, config }); }, }); assert.strictEqual(response.data, 'value'); }); }); axios-axios-aa54a5e/tests/unit/core/settle.test.js000066400000000000000000000112251526660636600223530ustar00rootroot00000000000000import { describe, it, expect, vi } from 'vitest'; import settle from '../../../lib/core/settle.js'; import AxiosError from '../../../lib/core/AxiosError.js'; /** * Builds a minimal response object that settle() expects. * @param {number} status - HTTP status code * @param {Function} [validateStatus] - optional custom validator */ function makeResponse(status, validateStatus = (s) => s >= 200 && s < 300) { return { status, config: { validateStatus }, request: {}, data: null, headers: {}, }; } describe('core::settle', () => { describe('resolves the promise', () => { it('resolves for a 200 status by default', () => { const resolve = vi.fn(); const reject = vi.fn(); settle(resolve, reject, makeResponse(200)); expect(resolve).toHaveBeenCalledTimes(1); expect(reject).not.toHaveBeenCalled(); }); it('resolves when validateStatus is not provided', () => { const resolve = vi.fn(); const reject = vi.fn(); const response = makeResponse(500); response.config.validateStatus = null; settle(resolve, reject, response); expect(resolve).toHaveBeenCalledTimes(1); }); it('resolves when status is 0 (no response, e.g. network error)', () => { const resolve = vi.fn(); const reject = vi.fn(); const response = makeResponse(0); settle(resolve, reject, response); expect(resolve).toHaveBeenCalledTimes(1); }); }); describe('error code assignment', () => { it('assigns ERR_BAD_REQUEST for a 400 status', () => { const resolve = vi.fn(); const reject = vi.fn(); settle(resolve, reject, makeResponse(400, () => false)); expect(reject).toHaveBeenCalledTimes(1); const error = reject.mock.calls[0][0]; expect(error).toBeInstanceOf(AxiosError); expect(error.code).toBe(AxiosError.ERR_BAD_REQUEST); }); it('assigns ERR_BAD_REQUEST for a 404 status', () => { const resolve = vi.fn(); const reject = vi.fn(); settle(resolve, reject, makeResponse(404, () => false)); const error = reject.mock.calls[0][0]; expect(error.code).toBe(AxiosError.ERR_BAD_REQUEST); }); it('assigns ERR_BAD_REQUEST for a 499 status (top of 4xx range)', () => { const resolve = vi.fn(); const reject = vi.fn(); settle(resolve, reject, makeResponse(499, () => false)); const error = reject.mock.calls[0][0]; expect(error.code).toBe(AxiosError.ERR_BAD_REQUEST); }); it('assigns ERR_BAD_RESPONSE for a 500 status', () => { const resolve = vi.fn(); const reject = vi.fn(); settle(resolve, reject, makeResponse(500, () => false)); const error = reject.mock.calls[0][0]; expect(error.code).toBe(AxiosError.ERR_BAD_RESPONSE); }); it('assigns ERR_BAD_RESPONSE for a 503 status', () => { const resolve = vi.fn(); const reject = vi.fn(); settle(resolve, reject, makeResponse(503, () => false)); const error = reject.mock.calls[0][0]; expect(error.code).toBe(AxiosError.ERR_BAD_RESPONSE); }); // A custom validateStatus can reject any status — including 1xx, 2xx, 3xx, // or ≥ 600 — and the resulting AxiosError must always have a defined code. it('assigns ERR_BAD_RESPONSE for a 3xx status rejected via custom validateStatus', () => { const resolve = vi.fn(); const reject = vi.fn(); settle(resolve, reject, makeResponse(301, () => false)); const error = reject.mock.calls[0][0]; expect(error.code).toBeDefined(); expect(error.code).toBe(AxiosError.ERR_BAD_RESPONSE); }); it('assigns ERR_BAD_RESPONSE for a 2xx status rejected via custom validateStatus', () => { const resolve = vi.fn(); const reject = vi.fn(); settle(resolve, reject, makeResponse(200, () => false)); const error = reject.mock.calls[0][0]; expect(error.code).toBeDefined(); expect(error.code).toBe(AxiosError.ERR_BAD_RESPONSE); }); it('assigns ERR_BAD_RESPONSE for a 6xx status rejected via custom validateStatus', () => { const resolve = vi.fn(); const reject = vi.fn(); settle(resolve, reject, makeResponse(600, () => false)); const error = reject.mock.calls[0][0]; expect(error.code).toBeDefined(); expect(error.code).toBe(AxiosError.ERR_BAD_RESPONSE); }); }); describe('error message', () => { it('includes the status code in the error message', () => { const resolve = vi.fn(); const reject = vi.fn(); settle(resolve, reject, makeResponse(404, () => false)); const error = reject.mock.calls[0][0]; expect(error.message).toBe('Request failed with status code 404'); }); }); }); axios-axios-aa54a5e/tests/unit/core/transformData.test.js000066400000000000000000000021711526660636600236600ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import transformData from '../../../lib/core/transformData.js'; describe('core::transformData', () => { it('supports a single transformer', () => { const data = transformData.call({}, (value) => { value = 'foo'; return value; }); expect(data).toBe('foo'); }); it('supports an array of transformers', () => { const data = transformData.call({ data: '' }, [ (value) => value + 'f', (value) => value + 'o', (value) => value + 'o', ]); expect(data).toBe('foo'); }); it('passes headers through to transformers', () => { const headers = { 'content-type': 'foo/bar', }; const data = transformData.call( { data: '', headers, }, [(value, currentHeaders) => value + currentHeaders['content-type']] ); expect(data).toBe('foo/bar'); }); it('passes status code through to transformers', () => { const data = transformData.call( {}, [(value, _headers, status) => value + status], { data: '', status: 200 } ); expect(data).toBe('200'); }); }); axios-axios-aa54a5e/tests/unit/estimateDataURLDecodedBytes.test.js000066400000000000000000000126041526660636600253740ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import estimateDataURLDecodedBytes, { estimateDataURLBufferAllocation, } from '../../lib/helpers/estimateDataURLDecodedBytes.js'; describe('estimateDataURLDecodedBytes', () => { it('should return 0 for non-data URLs', () => { assert.strictEqual(estimateDataURLDecodedBytes('http://example.com'), 0); }); it('should recognize case-insensitive data protocols for both decoders', () => { ['data:', 'DATA:', 'DaTa:'].forEach((scheme) => { assert.strictEqual(estimateDataURLBufferAllocation(scheme + 'text/plain,payload'), 7); assert.strictEqual(estimateDataURLDecodedBytes(scheme + 'text/plain,payload'), 7); }); }); it.each(['datax:,payload', 'DATAX:,payload', 'data-without-colon,payload', '', null])( 'should not estimate unsupported data protocol prefixes: %s', (url) => { assert.strictEqual(estimateDataURLBufferAllocation(url), 0); } ); it('should calculate length for simple non-base64 data URL', () => { const url = 'data:,Hello'; assert.strictEqual(estimateDataURLDecodedBytes(url), Buffer.byteLength('Hello', 'utf8')); }); it('should calculate decoded length for percent-encoded non-base64 data URL', () => { const url = 'data:text/plain,%E2%82%AC'; assert.strictEqual(estimateDataURLDecodedBytes(url), Buffer.byteLength('\u20ac', 'utf8')); }); it('should count percent-encoded ASCII as one decoded byte', () => { const url = 'data:text/plain,hello%20world'; assert.strictEqual(estimateDataURLDecodedBytes(url), Buffer.byteLength('hello world', 'utf8')); }); it('should calculate decoded length for base64 data URL', () => { const str = 'Hello'; const b64 = Buffer.from(str, 'utf8').toString('base64'); const url = `data:text/plain;base64,${b64}`; assert.strictEqual(estimateDataURLDecodedBytes(url), str.length); }); it('should handle base64 with = padding', () => { const url = 'data:text/plain;base64,TQ=='; assert.strictEqual(estimateDataURLDecodedBytes(url), 1); }); it.each(['base64', 'BASE64', 'bAsE64'])( 'should estimate the Buffer allocation for a %s marker', (marker) => { const url = 'data:text/plain;' + marker + ',SGVsbG8='; assert.strictEqual(estimateDataURLBufferAllocation(url), 5); } ); it.each(['BASE64=x', 'name=BASE64'])( 'should estimate %s as a non-base64 media type parameter', (parameter) => { const body = '\u4e00'.repeat(4); const url = 'data:text/plain;' + parameter + ',' + body; assert.strictEqual(estimateDataURLBufferAllocation(url), Buffer.byteLength(body)); } ); it.each(['base64', 'BASE64', 'bAsE64'])( 'should use the percent-decoded allocation for a %s marker', (marker) => { const body = '%41'.repeat(4096); const url = 'data:text/plain;' + marker + ',' + body; const decoded = decodeURIComponent(body); assert.strictEqual( estimateDataURLBufferAllocation(url), Buffer.byteLength(decoded, 'base64') ); assert.ok(estimateDataURLBufferAllocation(url) >= Buffer.from(decoded, 'base64').length); } ); it('should handle base64 with %3D padding', () => { const url = 'data:text/plain;base64,TQ%3D%3D'; assert.strictEqual(estimateDataURLDecodedBytes(url), 1); }); it('should ignore URL fragments when estimating a Fetch payload', () => { const url = 'data:text/plain;base64,TQ==#' + 'x'.repeat(4096); assert.strictEqual(estimateDataURLDecodedBytes(url), 1); }); it('should include the remainder after percent-decoding a Fetch base64 body', () => { const body = 'QQ' + '%41'.repeat(4000); const url = 'data:application/octet-stream;base64,' + body; assert.strictEqual(estimateDataURLDecodedBytes(url), 3001); }); it('should estimate the percent-decoded Buffer allocation for percent-embedded base64', () => { const body = 'QQ' + '%41'.repeat(4000); const url = 'data:application/octet-stream;base64,' + body; assert.strictEqual( estimateDataURLBufferAllocation(url), Buffer.byteLength('QQ' + 'A'.repeat(4000), 'base64') ); assert.strictEqual(estimateDataURLBufferAllocation(url), 3001); }); it.each([ ['padding', 'TQ%3d%3D', 'TQ=='], ['fully encoded', '%54%51%3D%3D', 'TQ=='], ['whitespace', 'T%0AQ%3D%3D', 'T\nQ=='], ['ignored tail', 'TQ%3D%3D' + '%25'.repeat(4096), 'TQ==' + '%'.repeat(4096)], ['incomplete escape', 'TQ%3D%3D%', 'TQ==%'], ])('should bound Buffer allocation for encoded %s', (_name, encoded, decoded) => { const estimate = estimateDataURLBufferAllocation('data:;base64,' + encoded); assert.strictEqual(estimate, Buffer.byteLength(decoded, 'base64')); assert.ok(estimate >= Buffer.from(decoded, 'base64').length); }); it('should include ignored input after padding in the raw Buffer allocation', () => { const body = 'TQ==' + '%'.repeat(4096); const url = 'data:application/octet-stream;base64,' + body; assert.strictEqual(estimateDataURLBufferAllocation(url), Buffer.byteLength(body, 'base64')); assert.ok(estimateDataURLBufferAllocation(url) > Buffer.from(body, 'base64').length); }); it('should include fragments in the raw Buffer allocation', () => { const body = 'TQ==#' + 'x'.repeat(4096); const url = 'data:application/octet-stream;base64,' + body; assert.strictEqual(estimateDataURLBufferAllocation(url), Buffer.byteLength(body, 'base64')); }); }); axios-axios-aa54a5e/tests/unit/fromDataURI.test.js000066400000000000000000000144141526660636600222430ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import fromDataURI from '../../lib/helpers/fromDataURI.js'; describe('helpers::fromDataURI', () => { it('should return buffer from data uri', () => { const buffer = Buffer.from('123'); const dataURI = 'data:application/octet-stream;base64,' + buffer.toString('base64'); assert.deepStrictEqual(fromDataURI(dataURI, false), buffer); }); it('should not call decodeURIComponent for base64 data', () => { const buffer = Buffer.from('123'); const originalDecodeURIComponent = globalThis.decodeURIComponent; globalThis.decodeURIComponent = () => { throw new Error('base64 body should not be URL decoded'); }; try { const dataURI = 'data:application/octet-stream;base64,' + buffer.toString('base64'); assert.deepStrictEqual(fromDataURI(dataURI, false), buffer); } finally { globalThis.decodeURIComponent = originalDecodeURIComponent; } }); it.each([ ['padding', 'TQ%3D%3D', Buffer.from('M')], ['alphabet', '%2B%2F8%3D', Buffer.from([0xfb, 0xff])], ['fully encoded', '%54%51%3D%3D', Buffer.from('M')], ['whitespace', 'T%0AQ%3D%3D', Buffer.from('M')], ])('should decode URL-encoded base64 %s', (_name, body, expected) => { assert.deepStrictEqual( fromDataURI('data:application/octet-stream;base64,' + body, false), expected ); }); it('should preserve Blob contents and media type for URL-encoded base64', async () => { const blob = fromDataURI('data:text/plain;charset=utf-8;base64,TQ%3D%3D', true, { Blob }); assert.strictEqual(blob.type, 'text/plain;charset=utf-8'); assert.deepStrictEqual(Buffer.from(await blob.arrayBuffer()), Buffer.from('M')); }); it('should retain forgiving base64 handling for an incomplete URL escape', () => { assert.deepStrictEqual(fromDataURI('data:text/plain;base64,TQ==%', false), Buffer.from('M')); }); it('should parse data URI with no mediatype and base64', () => { const buffer = Buffer.from('123'); const dataURI = 'data:;base64,' + buffer.toString('base64'); assert.deepStrictEqual(fromDataURI(dataURI, false), buffer); }); it('should parse data URI with mediatype and no base64', () => { const buffer = Buffer.from('123'); const dataURI = 'data:application/octet-stream,123'; assert.deepStrictEqual(fromDataURI(dataURI, false), buffer); }); it('should parse full form data URI with text/plain and base64', () => { const buffer = Buffer.from('hello'); const dataURI = 'data:text/plain;base64,' + buffer.toString('base64'); assert.deepStrictEqual(fromDataURI(dataURI, false), buffer); }); it('should parse minimal valid data URI', () => { const buffer = Buffer.from(''); const dataURI = 'data:,'; assert.deepStrictEqual(fromDataURI(dataURI, false), buffer); }); it('should parse data URI with spaces in data', () => { const buffer = Buffer.from('hello world'); const dataURI = 'data:text/plain,hello world'; assert.deepStrictEqual(fromDataURI(dataURI, false), buffer); }); it('should parse canonical RFC example with charset parameter', () => { const buffer = Buffer.from('123'); const dataURI = 'data:text/plain;charset=US-ASCII,123'; assert.deepStrictEqual(fromDataURI(dataURI, false), buffer); }); it('should decode URL-encoded body', () => { const buffer = Buffer.from('hello world'); const dataURI = 'data:text/plain,hello%20world'; assert.deepStrictEqual(fromDataURI(dataURI, false), buffer); }); it('should preserve full content type with parameters in Blob', () => { const dataURI = 'data:text/plain;charset=utf-8;base64,' + Buffer.from('hello').toString('base64'); const blob = fromDataURI(dataURI, true, { Blob }); assert.strictEqual(blob.type, 'text/plain;charset=utf-8'); }); it('should normalize omitted mediatype to text/plain per RFC 2397', () => { const dataURI = 'data:;charset=UTF-8,hello'; const blob = fromDataURI(dataURI, true, { Blob }); assert.strictEqual(blob.type, 'text/plain;charset=utf-8'); }); it('should treat the base64 token case-insensitively', () => { const buffer = Buffer.from('hello'); const body = buffer.toString('base64'); for (const token of [';base64', ';BASE64', ';Base64', ';bAsE64']) { assert.deepStrictEqual(fromDataURI('data:text/plain' + token + ',' + body, false), buffer); assert.deepStrictEqual(fromDataURI('data:' + token + ',' + body, false), buffer); } }); it('should preserve Blob contents and media type for a case-variant base64 token', async () => { const dataURI = 'data:text/plain;charset=utf-8;BASE64,' + Buffer.from('hello').toString('base64'); const blob = fromDataURI(dataURI, true, { Blob }); assert.strictEqual(blob.type, 'text/plain;charset=utf-8'); assert.strictEqual(await blob.text(), 'hello'); }); it.each(['BASE64=x', 'name=BASE64'])( 'should treat %s as a media type parameter rather than a base64 marker', (parameter) => { assert.strictEqual( fromDataURI('data:text/plain;' + parameter + ',SGVsbG8=', false).toString(), 'SGVsbG8=' ); } ); it('should reject a base64 marker followed by a media type parameter', () => { assert.throws(() => fromDataURI('data:text/plain;BASE64;charset=utf-8,SGVsbG8=', false), { code: 'ERR_INVALID_URL', }); }); it('should reject data URI with unsupported protocol prefix', () => { assert.throws( () => { fromDataURI('datax:,hi', false); }, (err) => err.code === 'ERR_NOT_SUPPORT' && err.message.includes('Unsupported protocol') ); }); it('should reject data URI without comma separator', () => { assert.throws( () => { fromDataURI('data:hi', false); }, (err) => err.code === 'ERR_INVALID_URL' ); }); it('should reject a media type containing more than one slash', () => { assert.throws( () => { fromDataURI('data:text/plain/extra,hello', false); }, (err) => err.code === 'ERR_INVALID_URL' ); }); it('should reject a long malformed media type within the test timeout', () => { assert.throws( () => { fromDataURI(`data:${'a/'.repeat(50000)}invalid`, false); }, (err) => err.code === 'ERR_INVALID_URL' ); }, 1000); }); axios-axios-aa54a5e/tests/unit/helpers/000077500000000000000000000000001526660636600202505ustar00rootroot00000000000000axios-axios-aa54a5e/tests/unit/helpers/AxiosTransformStream.test.js000066400000000000000000000062601526660636600257230ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import { runModuleInChildProcess } from '../../setup/runModuleInChildProcess.js'; import AxiosTransformStream from '../../../lib/helpers/AxiosTransformStream.js'; function collectChunks(options, size) { return new Promise(function (resolve, reject) { var stream = new AxiosTransformStream(options); var chunks = []; var progress = []; stream.on('data', function (chunk) { chunks.push(chunk.length); }); stream.on('progress', function (loaded) { progress.push(loaded); }); stream.on('error', reject); stream.on('end', function () { resolve({ chunks: chunks, progress: progress }); }); stream.end(Buffer.alloc(size)); }); } describe('AxiosTransformStream', function () { [1, 100].forEach(function (tailSize) { it( 'keeps an unlimited stream tail of ' + tailSize + ' bytes in its original chunk', async function () { var result = await collectChunks({ chunkSize: 16 }, 16 + tailSize); assert.deepStrictEqual(result.chunks, [16 + tailSize]); assert.deepStrictEqual(result.progress, [16 + tailSize]); } ); }); it('retains a configured minimum tail size for unlimited streams', async function () { var result = await collectChunks({ chunkSize: 16, minChunkSize: 3 }, 19); assert.deepStrictEqual(result.chunks, [19]); assert.deepStrictEqual(result.progress, [19]); }); it('splits tails above the configured minimum', async function () { var result = await collectChunks({ chunkSize: 16, minChunkSize: 3 }, 20); assert.deepStrictEqual(result.chunks, [16, 4]); assert.deepStrictEqual(result.progress, [16, 20]); }); it('retains the explicitly disabled minimum tail size', async function () { var result = await collectChunks({ chunkSize: 16, minChunkSize: false }, 17); assert.deepStrictEqual(result.chunks, [16, 1]); assert.deepStrictEqual(result.progress, [16, 17]); }); [25, 50].forEach(function (rate) { it('finishes output across short accounting windows at a rate of ' + rate, function () { var moduleURL = new URL('../../../lib/helpers/AxiosTransformStream.js', import.meta.url).href; var source = [ 'import Transform from ' + JSON.stringify(moduleURL) + ';', 'var started = Date.now();', 'var stream = new Transform({maxRate: ' + rate + ', timeWindow: 20});', 'var chunks = [];', 'stream.on("data", function(chunk) {', ' chunks.push({bytes: chunk.length, elapsed: Date.now() - started});', '});', 'stream.on("error", function(error) { throw error; });', 'stream.on("end", function() { console.log(JSON.stringify(chunks)); });', 'stream.end(Buffer.alloc(5));', ].join('\n'); return runModuleInChildProcess(source).then(function (stdout) { var chunks = JSON.parse(stdout); assert.ok(chunks.length > 1); assert.strictEqual(chunks[0].bytes, 1); assert.ok(chunks[1].elapsed >= 20); assert.strictEqual( chunks.reduce(function (bytes, chunk) { return bytes + chunk.bytes; }, 0), 5 ); }); }); }); }); axios-axios-aa54a5e/tests/unit/helpers/AxiosURLSearchParams.test.js000066400000000000000000000012261526660636600255250ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import AxiosURLSearchParams from '../../../lib/helpers/AxiosURLSearchParams.js'; describe('AxiosURLSearchParams::toString', () => { it('should pass the AxiosURLSearchParams instance as `this` to a custom encoder', () => { const params = new AxiosURLSearchParams({ foo: 'bar', baz: 'qux' }); const capturedThis = []; const serialized = params.toString(function customEncoder(value, defaultEncode) { capturedThis.push(this); return defaultEncode(value); }); expect(serialized).toBe('foo=bar&baz=qux'); expect(capturedThis).toEqual([params, params, params, params]); }); }); axios-axios-aa54a5e/tests/unit/helpers/Http2Sessions.test.js000066400000000000000000000201161526660636600243140ustar00rootroot00000000000000import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { EventEmitter } from 'events'; import http2 from 'http2'; import Http2Sessions from '../../../lib/helpers/Http2Sessions.js'; function createFakeSession() { const session = new EventEmitter(); session.destroyed = false; session.closed = false; session.close = vi.fn(() => { session.closed = true; session.emit('close'); }); const originalRequest = vi.fn(() => { const stream = new EventEmitter(); stream.endStream = vi.fn(); return stream; }); session.request = originalRequest; session._originalRequest = originalRequest; return session; } describe('helpers::Http2Sessions', () => { let connectSpy; let pool; beforeEach(() => { connectSpy = vi.spyOn(http2, 'connect').mockImplementation(() => createFakeSession()); pool = new Http2Sessions(); }); afterEach(() => { connectSpy.mockRestore(); vi.useRealTimers(); }); it('reuses a cached session for the same authority and options', () => { const a = pool.getSession('https://example.test', { sessionTimeout: 1000 }); const b = pool.getSession('https://example.test', { sessionTimeout: 1000 }); expect(a).toBe(b); expect(connectSpy).toHaveBeenCalledTimes(1); }); it('creates a separate session for a different authority', () => { pool.getSession('https://example.test'); pool.getSession('https://other.test'); expect(connectSpy).toHaveBeenCalledTimes(2); }); it('creates a new session when options differ for the same authority', () => { pool.getSession('https://example.test', { sessionTimeout: 1000 }); pool.getSession('https://example.test', { sessionTimeout: 5000 }); expect(connectSpy).toHaveBeenCalledTimes(2); }); it('does not reuse a destroyed session', () => { const first = pool.getSession('https://example.test'); first.destroyed = true; const second = pool.getSession('https://example.test'); expect(second).not.toBe(first); expect(connectSpy).toHaveBeenCalledTimes(2); }); it('does not reuse a closed session', () => { const first = pool.getSession('https://example.test'); first.closed = true; const second = pool.getSession('https://example.test'); expect(second).not.toBe(first); expect(connectSpy).toHaveBeenCalledTimes(2); }); it('drops a session from the pool when its close event fires', () => { const first = pool.getSession('https://example.test'); first.emit('close'); const second = pool.getSession('https://example.test'); expect(second).not.toBe(first); expect(connectSpy).toHaveBeenCalledTimes(2); }); it('drops a session from the pool when it receives a GOAWAY event', () => { const first = pool.getSession('https://example.test'); first.emit('goaway'); const second = pool.getSession('https://example.test'); expect(second).not.toBe(first); expect(connectSpy).toHaveBeenCalledTimes(2); }); it('does not start an idle timer when streams finish after GOAWAY', () => { vi.useFakeTimers(); const session = pool.getSession('https://example.test'); const firstStream = session.request(); const secondStream = session.request(); // Real HTTP/2 sessions remain open until their existing streams drain. session.close.mockImplementation(() => { session.closed = true; }); session.emit('goaway'); const replacement = pool.getSession('https://example.test'); expect(replacement).not.toBe(session); firstStream.emit('close'); expect(vi.getTimerCount()).toBe(0); secondStream.emit('close'); expect(vi.getTimerCount()).toBe(0); session.emit('close'); expect(vi.getTimerCount()).toBe(0); expect(session.close).toHaveBeenCalledTimes(1); expect(pool.getSession('https://example.test')).toBe(replacement); }); it('cancels an existing idle timer when GOAWAY arrives', () => { vi.useFakeTimers(); const session = pool.getSession('https://example.test'); session.request().emit('close'); expect(vi.getTimerCount()).toBe(1); session.emit('goaway'); expect(vi.getTimerCount()).toBe(0); expect(pool.getSession('https://example.test')).not.toBe(session); }); it('removes a session after GOAWAY when idle expiry is disabled', () => { const options = { sessionTimeout: null }; const session = pool.getSession('https://example.test', options); session.emit('goaway'); expect(pool.getSession('https://example.test', options)).not.toBe(session); expect(connectSpy).toHaveBeenCalledTimes(2); }); it('handles a session error and removes the failed session', () => { const first = pool.getSession('https://example.test'); expect(() => first.emit('error', new Error('connection failed'))).not.toThrow(); const second = pool.getSession('https://example.test'); expect(second).not.toBe(first); expect(connectSpy).toHaveBeenCalledTimes(2); }); it('keeps unrelated authorities cached when one session closes', () => { const first = pool.getSession('https://example.test'); const other = pool.getSession('https://other.test'); first.emit('close'); const otherAgain = pool.getSession('https://other.test'); expect(otherAgain).toBe(other); expect(connectSpy).toHaveBeenCalledTimes(2); }); it('closes and removes the session after sessionTimeout once the last stream ends', () => { vi.useFakeTimers(); const session = pool.getSession('https://example.test', { sessionTimeout: 1000 }); const stream = session.request(); stream.emit('close'); expect(session.closed).toBe(false); vi.advanceTimersByTime(1000); expect(session.close).toHaveBeenCalledTimes(1); const next = pool.getSession('https://example.test', { sessionTimeout: 1000 }); expect(next).not.toBe(session); expect(connectSpy).toHaveBeenCalledTimes(2); }); it('cancels the pending sessionTimeout when a new stream opens', () => { vi.useFakeTimers(); const session = pool.getSession('https://example.test', { sessionTimeout: 1000 }); const first = session.request(); first.emit('close'); session.request(); vi.advanceTimersByTime(5000); expect(session.close).not.toHaveBeenCalled(); }); it('keeps the session alive while streams are still open', () => { vi.useFakeTimers(); const session = pool.getSession('https://example.test', { sessionTimeout: 1000 }); const a = session.request(); const b = session.request(); a.emit('close'); vi.advanceTimersByTime(5000); expect(session.close).not.toHaveBeenCalled(); b.emit('close'); vi.advanceTimersByTime(1000); expect(session.close).toHaveBeenCalledTimes(1); }); it('defaults sessionTimeout to 1000ms when no options are passed', () => { vi.useFakeTimers(); const session = pool.getSession('https://example.test'); const stream = session.request(); stream.emit('close'); vi.advanceTimersByTime(999); expect(session.close).not.toHaveBeenCalled(); vi.advanceTimersByTime(1); expect(session.close).toHaveBeenCalledTimes(1); }); it('installs a request wrapper when sessionTimeout is set', () => { const session = pool.getSession('https://example.test', { sessionTimeout: 1000 }); expect(session.request).not.toBe(session._originalRequest); }); it('does not install the request wrapper when sessionTimeout is null', () => { const session = pool.getSession('https://example.test', { sessionTimeout: null }); expect(session.request).toBe(session._originalRequest); }); it('cancels the pending idle timer when the session itself closes', () => { vi.useFakeTimers(); const clearTimeoutSpy = vi.spyOn(globalThis, 'clearTimeout'); const session = pool.getSession('https://example.test', { sessionTimeout: 1000 }); const stream = session.request(); stream.emit('close'); // An idle-removal timer is now pending. clearTimeoutSpy.mockClear(); session.emit('close'); // Closing the session must cancel the pending idle timer; otherwise it // keeps Node's event loop refed for up to sessionTimeout ms past close. expect(clearTimeoutSpy).toHaveBeenCalled(); }); }); axios-axios-aa54a5e/tests/unit/helpers/HttpStatusCode.test.js000066400000000000000000000014741526660636600245100ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import HttpStatusCode from '../../../lib/helpers/HttpStatusCode.js'; describe('helpers::HttpStatusCode', () => { it('should expose RFC 9110 status aliases while preserving legacy reverse lookups', () => { expect(HttpStatusCode.ContentTooLarge).toBe(413); expect(HttpStatusCode.PayloadTooLarge).toBe(413); expect(HttpStatusCode[413]).toBe('PayloadTooLarge'); expect(HttpStatusCode.UnprocessableContent).toBe(422); expect(HttpStatusCode.UnprocessableEntity).toBe(422); expect(HttpStatusCode[422]).toBe('UnprocessableEntity'); }); it('should expose Cloudflare status 520 in both directions', () => { expect(HttpStatusCode.WebServerReturnsAnUnknownError).toBe(520); expect(HttpStatusCode[520]).toBe('WebServerReturnsAnUnknownError'); }); }); axios-axios-aa54a5e/tests/unit/helpers/bind.test.js000066400000000000000000000005001526660636600224730ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import bind from '../../../lib/helpers/bind.js'; describe('bind', () => { it('should bind an object to a function', () => { const o = { val: 123 }; const f = bind(function (num) { return this.val * num; }, o); expect(f(2)).toEqual(246); }); }); axios-axios-aa54a5e/tests/unit/helpers/buildURL.test.js000066400000000000000000000123271526660636600232530ustar00rootroot00000000000000import { describe, it, expect, vi } from 'vitest'; import buildURL, { encode } from '../../../lib/helpers/buildURL.js'; describe('helpers::buildURL', () => { it('should support null params', () => { expect(buildURL('/foo')).toEqual('/foo'); }); it('should support params', () => { expect( buildURL('/foo', { foo: 'bar', isUndefined: undefined, isNull: null, }) ).toEqual('/foo?foo=bar'); }); it('should support params with undefined url', () => { expect(buildURL(undefined, { foo: 'bar' })).toEqual('?foo=bar'); }); it('should support sending raw params to custom serializer func', () => { const serializer = vi.fn().mockReturnValue('foo=bar'); const params = { foo: 'bar' }; const options = { serialize: serializer, }; expect( buildURL( '/foo', { foo: 'bar', }, options ) ).toEqual('/foo?foo=bar'); expect(serializer).toHaveBeenCalledTimes(1); expect(serializer).toHaveBeenCalledWith(params, options); }); it('should support object params', () => { expect( buildURL('/foo', { foo: { bar: 'baz', }, }) ).toEqual('/foo?foo%5Bbar%5D=baz'); }); it('should support date params', () => { const date = new Date(); expect( buildURL('/foo', { date, }) ).toEqual('/foo?date=' + date.toISOString()); }); it('should support array params with encode', () => { expect( buildURL('/foo', { foo: ['bar', 'baz'], }) ).toEqual('/foo?foo%5B%5D=bar&foo%5B%5D=baz'); }); it('should pass the params serializer instance as `this` to custom encode', () => { const capturedThis = []; expect( buildURL('/foo', { foo: 'bar', baz: 'qux' }, { encode(value, defaultEncode) { capturedThis.push(this); return defaultEncode(value); }, }) ).toEqual('/foo?foo=bar&baz=qux'); expect(capturedThis).toHaveLength(4); expect(new Set(capturedThis).size).toBe(1); }); it('should support special char params', () => { expect( buildURL('/foo', { foo: ':$, ', }) ).toEqual('/foo?foo=:$,+'); }); it('should support existing params', () => { expect( buildURL('/foo?foo=bar', { bar: 'baz', }) ).toEqual('/foo?foo=bar&bar=baz'); }); it('should support "length" parameter', () => { expect( buildURL('/foo', { query: 'bar', start: 0, length: 5, }) ).toEqual('/foo?query=bar&start=0&length=5'); }); it('should correct discard url hash mark', () => { expect( buildURL('/foo?foo=bar#hash', { query: 'baz', }) ).toEqual('/foo?foo=bar&query=baz'); }); it('should support URLSearchParams', () => { expect(buildURL('/foo', new URLSearchParams('bar=baz'))).toEqual('/foo?bar=baz'); }); it('should support custom serialize function', () => { const params = { x: 1, }; const options = { serialize: (thisParams, thisOptions) => { expect(thisParams).toEqual(params); expect(thisOptions).toEqual(options); return 'rendered'; }, }; expect(buildURL('/foo', params, options)).toEqual('/foo?rendered'); const customSerializer = (thisParams) => { expect(thisParams).toEqual(params); return 'rendered'; }; expect(buildURL('/foo', params, customSerializer)).toEqual('/foo?rendered'); }); it('should ignore inherited serializer options', () => { let serializeInvoked = false; let encodeInvoked = false; Object.defineProperty(Object.prototype, 'serialize', { value() { serializeInvoked = true; return 'inherited=1'; }, configurable: true, }); Object.defineProperty(Object.prototype, 'encode', { value() { encodeInvoked = true; return 'inherited'; }, configurable: true, }); try { expect(buildURL('/foo', { value: 'a b' }, {})).toEqual('/foo?value=a+b'); expect(serializeInvoked).toBe(false); expect(encodeInvoked).toBe(false); } finally { delete Object.prototype.serialize; delete Object.prototype.encode; } }); }); describe('helpers::encode', () => { it('should be exported as a named export', () => { expect(typeof encode).toBe('function'); }); it('should leave plain ASCII unchanged', () => { expect(encode('foo')).toEqual('foo'); }); it('should preserve `:` rather than percent-encoding it', () => { expect(encode(':')).toEqual(':'); }); it('should preserve `$` rather than percent-encoding it', () => { expect(encode('$')).toEqual('$'); }); it('should preserve `,` rather than percent-encoding it', () => { expect(encode(',')).toEqual(','); }); it('should encode space as `+` (form-style) rather than `%20`', () => { expect(encode(' ')).toEqual('+'); }); it('should still percent-encode characters outside the preserved set', () => { expect(encode('a/b')).toEqual('a%2Fb'); expect(encode('a&b')).toEqual('a%26b'); expect(encode('a=b')).toEqual('a%3Db'); }); it('should apply all substitutions together', () => { expect(encode('a:b$c,d e')).toEqual('a:b$c,d+e'); }); }); axios-axios-aa54a5e/tests/unit/helpers/combineURLs.test.js000066400000000000000000000021451526660636600237500ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import combineURLs from '../../../lib/helpers/combineURLs.js'; describe('helpers::combineURLs', () => { it('should combine URLs', () => { expect(combineURLs('https://api.github.com', '/users')).toBe('https://api.github.com/users'); }); it('should remove duplicate slashes', () => { expect(combineURLs('https://api.github.com/', '/users')).toBe('https://api.github.com/users'); }); it('should remove multiple trailing slashes from the base URL', () => { expect(combineURLs('https://api.github.com///', '/users')).toBe('https://api.github.com/users'); }); it('should insert missing slash', () => { expect(combineURLs('https://api.github.com', 'users')).toBe('https://api.github.com/users'); }); it('should not insert slash when relative url missing/empty', () => { expect(combineURLs('https://api.github.com/users', '')).toBe('https://api.github.com/users'); }); it('should allow a single slash for relative url', () => { expect(combineURLs('https://api.github.com/users', '/')).toBe('https://api.github.com/users/'); }); }); axios-axios-aa54a5e/tests/unit/helpers/composeSignals.test.js000066400000000000000000000014031526660636600245500ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import composeSignals from '../../../lib/helpers/composeSignals.js'; describe('helpers::composeSignals', () => { it('registers abort listeners as once-only listeners', () => { const controller = new AbortController(); const calls = []; const nativeAdd = controller.signal.addEventListener.bind(controller.signal); controller.signal.addEventListener = (type, fn, options) => { calls.push({ type, options }); return nativeAdd(type, fn, options); }; const signal = composeSignals([controller.signal]); try { expect(calls).toHaveLength(1); expect(calls[0]).toEqual({ type: 'abort', options: { once: true } }); } finally { signal.unsubscribe(); } }); }); axios-axios-aa54a5e/tests/unit/helpers/cookies.test.js000066400000000000000000000006151526660636600232220ustar00rootroot00000000000000import assert from 'assert'; import { describe, it } from 'vitest'; import cookies from '../../../lib/helpers/cookies.js'; describe('helpers::cookies (non-standard environment)', () => { it('should resolve an async read to null', async () => { const result = cookies.readAsync('XSRF-TOKEN'); assert.ok(result instanceof Promise); assert.strictEqual(await result, null); }); }); axios-axios-aa54a5e/tests/unit/helpers/formDataToJSON.test.js000066400000000000000000000134251526660636600243230ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import formDataToJSON from '../../../lib/helpers/formDataToJSON.js'; import AxiosError from '../../../lib/core/AxiosError.js'; describe('formDataToJSON', () => { it('should convert a FormData Object to JSON Object', () => { const formData = new FormData(); formData.append('foo[bar][baz]', '123'); expect(formDataToJSON(formData)).toEqual({ foo: { bar: { baz: '123', }, }, }); }); it('should convert repeatable values as an array', () => { const formData = new FormData(); formData.append('foo', '1'); formData.append('foo', '2'); expect(formDataToJSON(formData)).toEqual({ foo: ['1', '2'], }); }); it('should keep repeatable values flat for 3+ entries', () => { const formData = new FormData(); formData.append('select3', '301'); formData.append('select3', '302'); formData.append('select3', '303'); expect(formDataToJSON(formData)).toEqual({ select3: ['301', '302', '303'], }); }); it('should keep nested repeatable values flat for 3+ entries', () => { const formData = new FormData(); formData.append('foo[bar]', '1'); formData.append('foo[bar]', '2'); formData.append('foo[bar]', '3'); expect(formDataToJSON(formData)).toEqual({ foo: { bar: ['1', '2', '3'], }, }); }); it('should convert props with empty brackets to arrays', () => { const formData = new FormData(); formData.append('foo[]', '1'); formData.append('foo[]', '2'); expect(formDataToJSON(formData)).toEqual({ foo: ['1', '2'], }); }); it('should supported indexed arrays', () => { const formData = new FormData(); formData.append('foo[0]', '1'); formData.append('foo[1]', '2'); expect(formDataToJSON(formData)).toEqual({ foo: ['1', '2'], }); }); it('should resist prototype pollution CVE', () => { const formData = new FormData(); formData.append('foo[0]', '1'); formData.append('foo[1]', '2'); formData.append('__proto__.x', 'hack'); formData.append('constructor.prototype.y', 'value'); expect(formDataToJSON(formData)).toEqual({ foo: ['1', '2'], constructor: { prototype: { y: 'value', }, }, }); expect({}.x).toEqual(undefined); expect({}.y).toEqual(undefined); }); it('should not write through to inherited objects on Object.prototype', () => { Object.defineProperty(Object.prototype, 'injected', { value: { hijack: true }, configurable: true, writable: true, }); try { const formData = new FormData(); formData.append('injected.hijack', 'STOLEN'); const result = formDataToJSON(formData); expect(result.injected).toEqual({ hijack: 'STOLEN' }); expect(Object.prototype.injected.hijack).toBe(true); } finally { delete Object.prototype.injected; } }); it('should throw AxiosError when a field path exceeds the default depth limit', () => { const formData = new FormData(); formData.append('foo' + '[bar]'.repeat(101), '123'); try { formDataToJSON(formData); throw new Error('Should have thrown'); } catch (err) { expect(err).toBeInstanceOf(AxiosError); expect(err.code).toBe(AxiosError.ERR_FORM_DATA_DEPTH_EXCEEDED); expect(err).not.toBeInstanceOf(RangeError); } }); it('should throw AxiosError while tokenizing very deep field paths', () => { const formData = new FormData(); formData.append('foo' + '[bar]'.repeat(10000), '123'); try { formDataToJSON(formData); throw new Error('Should have thrown'); } catch (err) { expect(err).toBeInstanceOf(AxiosError); expect(err.code).toBe(AxiosError.ERR_FORM_DATA_DEPTH_EXCEEDED); expect(err).not.toBeInstanceOf(RangeError); } }); it('should convert a field path at the default depth limit', () => { const formData = new FormData(); formData.append('foo' + '[bar]'.repeat(100), '123'); let value = formDataToJSON(formData).foo; for (let i = 0; i < 100; i++) { value = value.bar; } expect(value).toBe('123'); }); // https://github.com/axios/axios/issues/5402 it('should not split keys on characters other than brackets and dots', () => { const formData = new FormData(); formData.append('user-name', 'johndoe'); formData.append('first name', 'john'); formData.append('a+b', 'plus'); expect(formDataToJSON(formData)).toEqual({ 'user-name': 'johndoe', 'first name': 'john', 'a+b': 'plus', }); }); it('should still split bracket and dot notation', () => { const formData = new FormData(); formData.append('foo[bar-baz]', '1'); formData.append('a.b-c', '2'); expect(formDataToJSON(formData)).toEqual({ foo: { 'bar-baz': '1' }, a: { 'b-c': '2' }, }); }); it('should treat `.` and `[` as separators inside bracket groups', () => { const formData = new FormData(); formData.append('foo[bar.baz]', '1'); formData.append('qux[a[b]', '2'); // `.`, `[` and `]` are not part of a key, so bracket contents are split the // same way as dot notation rather than captured as a single literal key. expect(formDataToJSON(formData)).toEqual({ foo: { bar: { baz: '1' } }, qux: { a: { b: '2' } }, }); }); it('should parse long malformed bracket names in linear time', () => { const formData = new FormData(); // A run of unmatched `[` must not make the tokenizer rescan to the end of // the string from each `[` (quadratic). This guards the bracket-capture // grammar against regressions by requiring the match to fail fast. formData.append('a' + '['.repeat(100000), 'x'); expect(formDataToJSON(formData)).toEqual({ a: 'x' }); }); }); axios-axios-aa54a5e/tests/unit/helpers/formDataToStream.test.js000066400000000000000000000073601526660636600250060ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import formDataToStream from '../../../lib/helpers/formDataToStream.js'; class SpecFormData { constructor() { this._entries = []; this[Symbol.toStringTag] = 'FormData'; } append(name, value) { this._entries.push([name, value]); } entries() { return this._entries[Symbol.iterator](); } [Symbol.iterator]() { return this._entries[Symbol.iterator](); } } const makeBlobLike = ({ type, name, size, payload }) => ({ type, name, size: size ?? payload.byteLength, [Symbol.asyncIterator]: async function* () { yield payload; }, }); const collect = async (stream) => { const chunks = []; for await (const chunk of stream) { chunks.push(Buffer.from(chunk)); } return Buffer.concat(chunks).toString('utf8'); }; describe('formDataToStream', () => { it('should strip CRLF sequences from blob.type to prevent multipart header injection', async () => { const fd = new SpecFormData(); fd.append( 'photo', makeBlobLike({ type: 'image/jpeg\r\nX-Injected-Header: PWNED\r\nX-Evil: bad', name: 'photo.jpg', payload: Buffer.from('PAYLOAD'), }) ); const body = await collect(formDataToStream(fd, () => {})); expect(body).not.toContain('\r\nX-Injected-Header'); expect(body).not.toContain('\r\nX-Evil'); expect(body).toContain('Content-Type: image/jpegX-Injected-Header: PWNEDX-Evil: bad\r\n'); }); it('should strip bare \\r and bare \\n from blob.type', async () => { const fd = new SpecFormData(); fd.append( 'f', makeBlobLike({ type: 'text/plain\rX-A: 1\nX-B: 2', name: 'f.txt', payload: Buffer.from('x'), }) ); const body = await collect(formDataToStream(fd, () => {})); expect(body).not.toMatch(/^X-A:/m); expect(body).not.toMatch(/^X-B:/m); }); it('should preserve legitimate Content-Type values', async () => { const fd = new SpecFormData(); fd.append( 'doc', makeBlobLike({ type: 'application/json; charset=utf-8', name: 'doc.json', payload: Buffer.from('{}'), }) ); const body = await collect(formDataToStream(fd, () => {})); expect(body).toContain('Content-Type: application/json; charset=utf-8\r\n'); }); it('should default missing blob.type to application/octet-stream', async () => { const fd = new SpecFormData(); fd.append( 'bin', makeBlobLike({ type: '', name: 'bin', payload: Buffer.from([0x00, 0x01]), }) ); const body = await collect(formDataToStream(fd, () => {})); expect(body).toContain('Content-Type: application/octet-stream\r\n'); }); it('should escape CRLF and quotes in blob.name (Content-Disposition)', async () => { const fd = new SpecFormData(); fd.append( 'up', makeBlobLike({ type: 'text/plain', name: 'evil\r\nX-Bad: 1".jpg', payload: Buffer.from('x'), }) ); const body = await collect(formDataToStream(fd, () => {})); expect(body).not.toContain('\r\nX-Bad: 1'); expect(body).toContain('filename="evil%0D%0AX-Bad: 1%22.jpg"'); }); it('should report stable contentLength that matches emitted bytes', async () => { const fd = new SpecFormData(); fd.append( 'photo', makeBlobLike({ type: 'image/jpeg\r\nX-Injected: PWNED', name: 'photo.jpg', payload: Buffer.from('PAYLOAD'), }) ); let reportedLength; const stream = formDataToStream( fd, (h) => { reportedLength = h['Content-Length']; }, { boundary: 'test-boundary-abc' } ); const body = await collect(stream); expect(Buffer.byteLength(body, 'utf8')).toBe(reportedLength); }); }); axios-axios-aa54a5e/tests/unit/helpers/indexedFormFields.test.js000066400000000000000000000134571526660636600251710ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import utils from '../../../lib/utils.js'; import toFormData from '../../../lib/helpers/toFormData.js'; import formDataToJSON from '../../../lib/helpers/formDataToJSON.js'; function multipartEntries(value) { var entries = []; toFormData( { 'items[]': value }, { append: function (name, part) { entries.push([name, String(part)]); }, } ); return entries; } function convertFields(entries) { var fields = Object.create({ append: function () {}, toString: function () { return '[object FormData]'; }, entries: function () { return entries[Symbol.iterator](); }, }); fields[Symbol.iterator] = fields.entries; return formDataToJSON(fields); } describe('indexed multipart fields', function () { it('unwraps non-enumerable indexes', function () { var values = { length: 2 }; Object.defineProperties(values, { 0: { value: 'first' }, 1: { value: 'second' }, }); assert.deepStrictEqual(utils.toArray(values), ['first', 'second']); assert.deepStrictEqual(multipartEntries(values), [ ['items[]', 'first'], ['items[]', 'second'], ]); }); it('unwraps present sparse array values under an array key', function () { var values = new Array(4); values[1] = 'first'; values[3] = 'second'; assert.strictEqual(utils.toArray(values), values); assert.deepStrictEqual(multipartEntries(values), [ ['items[]', 'first'], ['items[]', 'second'], ]); }); it('unwraps prototype-backed indexes without counting duplicates twice', function () { var prototype = {}; Object.defineProperties(prototype, { 0: { value: 'inherited', configurable: true }, 1: { value: 'second' }, }); var values = Object.create(prototype, { 0: { value: 'first' }, length: { value: 2, writable: true }, }); assert.deepStrictEqual(multipartEntries(values), [ ['items[]', 'first'], ['items[]', 'second'], ]); Object.defineProperty(values, 'length', { value: 3 }); assert.strictEqual(utils.toArray(values), null); }); it('unwraps native array-like values and inherited length accessors', function () { assert.deepStrictEqual(utils.toArray(new Uint8Array([1, 2])), [1, 2]); assert.deepStrictEqual( (function () { return utils.toArray(arguments); })('first', 'second'), ['first', 'second'] ); var prototype = {}; Object.defineProperty(prototype, 'length', { get: function () { return 2; }, }); var values = Object.create(prototype, { 0: { value: 'first' }, 1: { value: 'second' }, }); assert.deepStrictEqual(utils.toArray(values), ['first', 'second']); }); it('does not fill an index gap from unrelated fields', function () { var values = { 0: 'first', length: 2, label: 'x' }; assert.strictEqual(utils.toArray(values), null); assert.deepStrictEqual(multipartEntries(values), [ ['items[0]', 'first'], ['items[length]', '2'], ['items[label]', 'x'], ]); }); it('keeps unrelated fields when indexed values are absent', function () { var values = { length: 1, label: 'x' }; assert.strictEqual(utils.toArray(values), null); assert.deepStrictEqual(multipartEntries(values), [ ['items[length]', '1'], ['items[label]', 'x'], ]); }); it('does not count non-canonical numeric properties or indexes beyond length', function () { ['00', '-0', '+0', '1e0', '1', '4294967295'].forEach(function (key) { var values = { length: 1 }; values[key] = 'value'; assert.strictEqual(utils.toArray(values), null, key); }); }); it('rejects unsupported array lengths without reading indexed values', function () { [100000, 4294967296, Number.MAX_SAFE_INTEGER].forEach(function (length) { var reads = 0; var values = { length: length, label: 'x' }; Object.defineProperty(values, '0', { get: function () { reads++; return 'first'; }, }); assert.strictEqual(utils.toArray(values), null); assert.strictEqual(reads, 0); }); }); ['01', '-0', '-1', '+1', '1e0', '4294967295', '9007199254740992'].forEach(function (key) { it('retains the numeric-looking JSON key ' + key, function () { var result = convertFields([['items[' + key + '][name]', 'value']]); var expected = { items: {} }; expected.items[key] = { name: 'value' }; assert.deepStrictEqual(result, expected); assert.deepStrictEqual(JSON.parse(JSON.stringify(result)), expected); }); }); it('retains mixed indexes even when key count equals array length', function () { var result = convertFields([ ['items[1]', 'second'], ['items[01]', 'named'], ]); var expected = { items: { 1: 'second', '01': 'named' } }; assert.deepStrictEqual(result, expected); assert.deepStrictEqual(JSON.parse(JSON.stringify(result)), expected); }); it('retains dense indexes together with non-index properties', function () { var result = convertFields([ ['items[0]', 'first'], ['items[01]', 'named'], ]); assert.deepStrictEqual(JSON.parse(JSON.stringify(result)), { items: { 0: 'first', '01': 'named' }, }); }); it('normalizes non-index properties before appending a repeated field', function () { var result = convertFields([ ['items[01]', 'named'], ['items', 'next'], ]); assert.deepStrictEqual(JSON.parse(JSON.stringify(result)), { items: [{ '01': 'named' }, 'next'], }); }); it('keeps canonical dense indexes as arrays', function () { var result = convertFields([ ['items[1]', 'second'], ['items[0]', 'first'], ]); assert.deepStrictEqual(result, { items: ['first', 'second'] }); }); }); axios-axios-aa54a5e/tests/unit/helpers/isAbsoluteURL.test.js000066400000000000000000000016461526660636600242700ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import isAbsoluteURL from '../../../lib/helpers/isAbsoluteURL.js'; describe('helpers::isAbsoluteURL', () => { it('should return true if URL begins with valid scheme name', () => { expect(isAbsoluteURL('https://api.github.com/users')).toBe(true); expect(isAbsoluteURL('custom-scheme-v1.0://example.com/')).toBe(true); expect(isAbsoluteURL('HTTP://example.com/')).toBe(true); }); it('should return false if URL begins with invalid scheme name', () => { expect(isAbsoluteURL('123://example.com/')).toBe(false); expect(isAbsoluteURL('!valid://example.com/')).toBe(false); }); it('should return true if URL is protocol-relative', () => { expect(isAbsoluteURL('//example.com/')).toBe(true); }); it('should return false if URL is relative', () => { expect(isAbsoluteURL('/foo')).toBe(false); expect(isAbsoluteURL('foo')).toBe(false); }); }); axios-axios-aa54a5e/tests/unit/helpers/isAxiosError.test.js000066400000000000000000000014651526660636600242230ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import AxiosError from '../../../lib/core/AxiosError.js'; import isAxiosError from '../../../lib/helpers/isAxiosError.js'; describe('helpers::isAxiosError', () => { it('should return true if the error is created by core::createError', () => { expect(isAxiosError(new AxiosError('Boom!', null, { foo: 'bar' }))).toBe(true); }); it('should return true if the error is enhanced by core::enhanceError', () => { expect(isAxiosError(AxiosError.from(new Error('Boom!'), null, { foo: 'bar' }))).toBe(true); }); it('should return false if the error is a normal Error instance', () => { expect(isAxiosError(new Error('Boom!'))).toBe(false); }); it('should return false if the error is null', () => { expect(isAxiosError(null)).toBe(false); }); }); axios-axios-aa54a5e/tests/unit/helpers/isURLSameOrigin.test.js000066400000000000000000000046511526660636600245460ustar00rootroot00000000000000import { afterEach, describe, expect, it, vi } from 'vitest'; async function loadHelper(origin, navigator = { userAgent: 'Mozilla/5.0' }) { // The helper captures the page origin at module initialization. Keep native URL // parsing while providing a fresh browser environment for each import. vi.resetModules(); vi.stubGlobal('window', { location: { href: origin } }); vi.stubGlobal('document', { baseURI: origin }); vi.stubGlobal('navigator', navigator); return (await import('../../../lib/helpers/isURLSameOrigin.js')).default; } describe('helpers::isURLSameOrigin', () => { afterEach(() => { vi.unstubAllGlobals(); vi.resetModules(); }); it.each([ ['a modern user agent', { userAgent: 'Mozilla/5.0' }], ['an MSIE user agent', { userAgent: 'Mozilla/5.0 (compatible; MSIE 10.0)' }], ['a Trident user agent', { userAgent: 'Mozilla/5.0 Trident/7.0' }], ['no navigator', null], ])('preserves origin boundaries with %s', async (description, navigator) => { const isURLSameOrigin = await loadHelper('https://app.example/', navigator); expect(isURLSameOrigin('/resource')).toBe(true); expect(isURLSameOrigin('https://app.example:443/resource')).toBe(true); expect(isURLSameOrigin('https://APP.EXAMPLE/resource')).toBe(true); expect(isURLSameOrigin('https://app.example:8443/resource')).toBe(false); expect(isURLSameOrigin('//app.example:8443/resource')).toBe(false); expect(isURLSameOrigin('http://app.example/resource')).toBe(false); expect(isURLSameOrigin('https://other.example/resource')).toBe(false); expect(isURLSameOrigin('https://app.example@other.example/resource')).toBe(false); }); it.each([ ['http', '80'], ['https', '443'], ])('normalizes the default port for %s', async (protocol, port) => { const isURLSameOrigin = await loadHelper(`${protocol}://app.example:${port}/`); expect(isURLSameOrigin(`${protocol}://app.example/resource`)).toBe(true); expect(isURLSameOrigin(`${protocol}://app.example:${port}/resource`)).toBe(true); }); it('preserves the fallback outside standard browser environments', async () => { vi.resetModules(); vi.stubGlobal('window', undefined); vi.stubGlobal('document', undefined); vi.stubGlobal('navigator', undefined); const { default: isURLSameOrigin } = await import('../../../lib/helpers/isURLSameOrigin.js'); expect(isURLSameOrigin('https://other.example/resource')).toBe(true); }); }); axios-axios-aa54a5e/tests/unit/helpers/parseHeaders.test.js000066400000000000000000000052561526660636600242020ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import parseHeaders from '../../../lib/helpers/parseHeaders.js'; describe('helpers::parseHeaders', () => { it('should parse headers', () => { const date = new Date(); const parsed = parseHeaders( 'Date: ' + date.toISOString() + '\n' + 'Content-Type: application/json\n' + 'Connection: keep-alive\n' + 'Transfer-Encoding: chunked' ); expect(parsed.date).toEqual(date.toISOString()); expect(parsed['content-type']).toEqual('application/json'); expect(parsed.connection).toEqual('keep-alive'); expect(parsed['transfer-encoding']).toEqual('chunked'); }); it('should use array for set-cookie', () => { const parsedZero = parseHeaders(''); const parsedSingle = parseHeaders('Set-Cookie: key=val;'); const parsedMulti = parseHeaders('Set-Cookie: key=val;\n' + 'Set-Cookie: key2=val2;\n'); expect(parsedZero['set-cookie']).toBeUndefined(); expect(parsedSingle['set-cookie']).toEqual(['key=val;']); expect(parsedMulti['set-cookie']).toEqual(['key=val;', 'key2=val2;']); }); it.each([ ['__proto__: first', 'first'], [' __PrOtO__ : first\r\n__proto__: second', 'first, second'], ['__proto__:\n__proto__: second', ', second'], ])('should preserve prototype-named headers in %j', (raw, expected) => { const parsed = parseHeaders(raw + '\nConstructor: ctor\nPrototype: proto\nX-Other: other'); expect(Object.getPrototypeOf(parsed)).toBe(null); expect(Object.prototype.hasOwnProperty.call(parsed, '__proto__')).toBe(true); expect(parsed.__proto__).toBe(expected); expect(parsed.constructor).toBe('ctor'); expect(parsed.prototype).toBe('proto'); expect(parsed['x-other']).toBe('other'); }); it('should handle duplicates', () => { const parsed = parseHeaders( 'Age: age-a\n' + 'Age: age-b\n' + 'Foo: foo-a\n' + 'Foo: foo-b\n' ); expect(parsed.age).toEqual('age-a'); expect(parsed.foo).toEqual('foo-a, foo-b'); }); it('should ignore duplicate node-style headers after an empty first value', () => { const parsed = parseHeaders('Content-Length:\n' + 'Content-Length: 10\n'); expect(parsed['content-length']).toEqual(''); }); it('should ignore inherited parsed header values', () => { Object.prototype['content-length'] = ''; Object.prototype.foo = true; try { const parsed = parseHeaders('Content-Length: 10\n' + 'Foo: foo\n' + 'Foo: bar\n'); expect(parsed['content-length']).toEqual('10'); expect(parsed.foo).toEqual('foo, bar'); } finally { delete Object.prototype['content-length']; delete Object.prototype.foo; } }); }); axios-axios-aa54a5e/tests/unit/helpers/progressEventReducer.test.js000066400000000000000000000116071526660636600257510ustar00rootroot00000000000000import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { progressEventReducer } from '../../../lib/helpers/progressEventReducer.js'; describe('helpers::progressEventReducer', () => { it('should clamp loaded/progress and avoid negative bytes for out-of-order events', () => { const events = []; const [onProgress, flush] = progressEventReducer((data) => { events.push(data); }, false, Number.POSITIVE_INFINITY); onProgress({ lengthComputable: true, loaded: 80, total: 100 }); onProgress({ lengthComputable: true, loaded: 60, total: 100 }); onProgress({ lengthComputable: true, loaded: 180, total: 100 }); flush(); expect(events.length).toBe(3); expect(events[0].bytes).toBe(80); expect(events[1].bytes).toBe(0); const last = events[events.length - 1]; expect(last.loaded).toBe(100); expect(last.total).toBe(100); expect(last.progress).toBe(1); expect(last.upload).toBe(true); expect(last.bytes).toBe(20); }); it('should ignore malformed events that lack a numeric loaded value', () => { const events = []; const [onProgress, flush] = progressEventReducer((data) => { events.push(data); }, false, Number.POSITIVE_INFINITY); onProgress(undefined); onProgress(null); onProgress({}); onProgress({ loaded: null, total: 100, lengthComputable: true }); onProgress({ loaded: 'abc', total: 100, lengthComputable: true }); flush(); expect(events.length).toBe(0); onProgress({ lengthComputable: true, loaded: 50, total: 100 }); flush(); expect(events.length).toBe(1); expect(events[0].loaded).toBe(50); expect(events[0].bytes).toBe(50); }); it('should clamp negative loaded values to zero', () => { const events = []; const [onProgress, flush] = progressEventReducer((data) => { events.push(data); }, false, Number.POSITIVE_INFINITY); onProgress({ lengthComputable: true, loaded: -5, total: 10 }); flush(); expect(events.length).toBe(1); expect(events[0].loaded).toBe(0); expect(events[0].progress).toBe(0); expect(events[0].bytes).toBe(0); }); it('should clamp negative loaded values to zero when length is not computable', () => { const events = []; const [onProgress, flush] = progressEventReducer((data) => { events.push(data); }, false, Number.POSITIVE_INFINITY); onProgress({ lengthComputable: false, loaded: -5 }); flush(); expect(events.length).toBe(1); expect(events[0].loaded).toBe(0); expect(events[0].progress).toBeUndefined(); expect(events[0].bytes).toBe(0); }); describe('force with a fresh event', () => { beforeEach(() => { vi.useFakeTimers(); }); afterEach(() => { vi.useRealTimers(); }); it('should deliver a fresh progress event even when nothing is pending', () => { const events = []; const [onProgress, , force] = progressEventReducer((data) => { events.push(data); }, true, 1); onProgress({ lengthComputable: false, loaded: 10 }); force({ lengthComputable: false, loaded: 30 }); expect(events.length).toBe(2); expect(events[1].loaded).toBe(30); expect(events[1].download).toBe(true); }); it('should prefer a fresh progress event over the pending one and clear it', () => { const events = []; const [onProgress, flush, force] = progressEventReducer((data) => { events.push(data); }, true, 1); onProgress({ lengthComputable: false, loaded: 10 }); onProgress({ lengthComputable: false, loaded: 20 }); force({ lengthComputable: false, loaded: 30 }); expect(events.length).toBe(2); expect(events[1].loaded).toBe(30); flush(); vi.runAllTimers(); expect(events.length).toBe(2); }); it('should replay the pending event without inspecting a loaded-shaped flush argument', () => { const events = []; const [onProgress, flush] = progressEventReducer((data) => { events.push(data); }, true, 1); onProgress({ lengthComputable: false, loaded: 10 }); onProgress({ lengthComputable: false, loaded: 20 }); flush(Object.assign(new Error('stream error'), { loaded: 30 })); expect(events.length).toBe(2); expect(events[1].loaded).toBe(20); }); it('should not inspect arbitrary flush arguments', () => { const events = []; const [onProgress, flush] = progressEventReducer((data) => { events.push(data); }, true, 1); const reason = {}; Object.defineProperty(reason, 'loaded', { get() { throw new Error('loaded must not be read'); }, }); onProgress({ lengthComputable: false, loaded: 10 }); onProgress({ lengthComputable: false, loaded: 20 }); expect(() => flush(reason)).not.toThrow(); expect(events.length).toBe(2); expect(events[1].loaded).toBe(20); }); }); }); axios-axios-aa54a5e/tests/unit/helpers/requestConsistency.test.js000066400000000000000000000111111526660636600254710ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import axios from '../../../index.js'; import utils from '../../../lib/utils.js'; import formDataToJSON from '../../../lib/helpers/formDataToJSON.js'; import toFormData from '../../../lib/helpers/toFormData.js'; import buildURL from '../../../lib/helpers/buildURL.js'; function FormDataMock(entries) { this._entries = entries; } FormDataMock.prototype.append = function () {}; FormDataMock.prototype.toString = function () { return '[object FormData]'; }; FormDataMock.prototype[Symbol.iterator] = function () { var entries = this._entries; var index = 0; return { next: function () { return index < entries.length ? { value: entries[index++], done: false } : { done: true }; }, }; }; FormDataMock.prototype.entries = FormDataMock.prototype[Symbol.iterator]; function nestedParams(depth) { var params = {}; var value = params; for (var i = 0; i < depth; i++) { value.next = {}; value = value.next; } value.leaf = 'value'; return params; } describe('request input consistency', function () { it('keeps sparse field indexes as object keys', function () { [100000, 4294967294].forEach(function (index) { var result = formDataToJSON(new FormDataMock([['items[' + index + '][name]', 'value']])); assert.strictEqual(Array.isArray(result.items), false); assert.strictEqual(result.items[index].name, 'value'); assert.ok(JSON.stringify(result).length < 100); }); }); it('keeps dense indexes as arrays regardless of insertion order', function () { var result = formDataToJSON( new FormDataMock([ ['items[1]', 'second'], ['items[0]', 'first'], ]) ); assert.deepStrictEqual(result.items, ['first', 'second']); }); it('handles repeated names after indexed fields without filling missing indexes', function () { var result = formDataToJSON( new FormDataMock([ ['items[100000]', 'first'], ['items', 'second'], ]) ); assert.strictEqual(Array.isArray(result.items[0]), false); assert.strictEqual(result.items[0][100000], 'first'); assert.ok(JSON.stringify(result).length < 100); }); it('does not expand array-like objects from their length alone', function () { assert.strictEqual(utils.toArray({ length: 100000 }), null); [NaN, Infinity, -1, 1.5].forEach(function (length) { assert.strictEqual(utils.toArray({ length: length }), null); }); assert.deepStrictEqual(utils.toArray({ 0: 'first', 1: 'second', length: 2 }), [ 'first', 'second', ]); assert.deepStrictEqual(utils.toArray('abc'), ['a', 'b', 'c']); }); it('serializes an ordinary length property as a bounded multipart field', function () { var entries = []; toFormData( { 'items[]': { length: 100000 } }, { append: function (name, value) { entries.push([name, String(value)]); }, } ); assert.deepStrictEqual(entries, [['items[length]', '100000']]); }); ['request', 'get', 'post', 'postForm'].forEach(function (method) { it('rejects deep params through the ' + method + ' promise', function () { var request; var config = { params: nestedParams(150), adapter: function (config) { buildURL('/resource', config.params, config.paramsSerializer); throw new Error('serialization should reject'); }, }; assert.doesNotThrow(function () { request = method === 'request' ? axios.request(config) : method === 'get' ? axios.get('/resource', config) : axios[method]('/resource', {}, config); }); assert.strictEqual(typeof request.then, 'function'); return assert.rejects(request, function (error) { return error.code === 'ERR_FORM_DATA_DEPTH_EXCEEDED' && !(error instanceof RangeError); }); }); }); it('retains supported nested params without mutating the caller', function () { var params = nestedParams(25); return axios.get('/resource', { params: params, adapter: function (config) { assert.notStrictEqual(config.params, params); assert.deepStrictEqual(config.params, params); return Promise.resolve({ data: 'ok', status: 200, headers: {}, config: config }); }, }); }); it('rejects circular params with a bounded error', function () { var params = {}; params.next = params; return assert.rejects(axios.get('/resource', { params: params }), function (error) { return error.code === 'ERR_BAD_OPTION_VALUE'; }); }); }); axios-axios-aa54a5e/tests/unit/helpers/resolveConfig.test.js000066400000000000000000000157161526660636600244030ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import FormData from 'form-data'; import resolveConfig from '../../../lib/helpers/resolveConfig.js'; import AxiosError from '../../../lib/core/AxiosError.js'; class ReactNativeFormData { append() {} getParts() { return []; } get [Symbol.toStringTag]() { return 'FormData'; } } describe('helpers::resolveConfig', () => { it('should ignore FormData getHeaders inherited only from Object.prototype', () => { if (typeof globalThis.FormData !== 'function') { return; } let called = false; Object.prototype.getHeaders = () => { called = true; return { 'X-Injected': 'yes' }; }; try { const config = resolveConfig({ url: '/upload', data: new globalThis.FormData(), }); assert.strictEqual(called, false); assert.strictEqual(config.headers.get('X-Injected'), undefined); } finally { delete Object.prototype.getHeaders; } }); it('clears Content-Type for React Native FormData', () => { const data = new ReactNativeFormData(); const config = resolveConfig({ url: '/upload', data, headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, }); assert.strictEqual(config.data, data); assert.strictEqual(config.headers.getContentType(), undefined); assert.strictEqual( Object.prototype.hasOwnProperty.call(config.headers.toJSON(), 'Content-Type'), false ); }); it('should ignore inherited nested auth fields', () => { Object.defineProperty(Object.prototype, 'username', { value: 'inherited-user', configurable: true, }); Object.defineProperty(Object.prototype, 'password', { value: 'inherited-pass', configurable: true, }); try { const config = resolveConfig({ url: '/foo', auth: {}, }); assert.strictEqual(config.headers.get('Authorization'), 'Basic Og=='); } finally { delete Object.prototype.username; delete Object.prototype.password; } }); it('should UTF-8 encode both basic auth fields when opted in', () => { const config = resolveConfig({ url: '/foo', auth: { username: 'naïve', password: 'naïve' }, transitional: { encodeBasicAuthUsername: true }, }); const decoded = Buffer.from( config.headers.get('Authorization').replace(/^Basic /, ''), 'base64' ).toString('utf8'); assert.strictEqual(decoded, 'naïve:naïve'); }); it('should accept a basic auth username outside Latin-1 when opted in', () => { let config; assert.doesNotThrow(() => { config = resolveConfig({ url: '/foo', auth: { username: 'Ï€', password: 'x' }, transitional: { encodeBasicAuthUsername: true }, }); }); const decoded = Buffer.from( config.headers.get('Authorization').replace(/^Basic /, ''), 'base64' ).toString('utf8'); assert.strictEqual(decoded, 'Ï€:x'); }); it.each([undefined, false])( 'should preserve Latin-1 username bytes with opt-in %s', (enabled) => { const config = resolveConfig({ url: '/foo', auth: { username: 'naïve', password: 'x' }, transitional: { encodeBasicAuthUsername: enabled }, }); assert.strictEqual(config.headers.get('Authorization'), 'Basic bmHvdmU6eA=='); } ); it('should preserve manually UTF-8 encoded usernames by default', () => { const config = resolveConfig({ url: '/foo', auth: { username: 'na\u00c3\u00afve', password: 'x' }, }); assert.strictEqual(config.headers.get('Authorization'), 'Basic bmHDr3ZlOng='); }); it.each(['transitional', 'encodeBasicAuthUsername'])( 'should ignore an inherited %s opt-in', (key) => { Object.defineProperty(Object.prototype, key, { value: key === 'transitional' ? { encodeBasicAuthUsername: true } : true, configurable: true, writable: true, }); try { const config = resolveConfig({ url: '/foo', auth: { username: 'naïve', password: 'x' }, ...(key === 'encodeBasicAuthUsername' ? { transitional: {} } : {}), }); assert.strictEqual(config.headers.get('Authorization'), 'Basic bmHvdmU6eA=='); } finally { delete Object.prototype[key]; } } ); it.each([ [{ username: 'Aladdin', password: 'open sesame' }, 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=='], [{}, 'Basic Og=='], [{ username: 'Ï€' }, 'Basic z4A6'], [{ password: 'Ï€' }, 'Basic Os+A'], [{ username: '😀', password: 'x' }, 'Basic 8J+YgDp4'], ])('should encode valid credentials %j when opted in', (auth, expected) => { const config = resolveConfig({ url: '/foo', auth, transitional: { encodeBasicAuthUsername: true }, }); assert.strictEqual(config.headers.get('Authorization'), expected); }); it.each(['username', 'password'])('should reject a lone surrogate in %s when opted in', (key) => { assert.throws( () => resolveConfig({ url: '/foo', auth: { username: 'user', password: 'x', [key]: '\uD800' }, transitional: { encodeBasicAuthUsername: true }, }), (error) => error instanceof AxiosError && error.code === AxiosError.ERR_BAD_OPTION_VALUE ); }); it('should wrap invalid auth encoding as AxiosError', () => { assert.throws( () => resolveConfig({ url: '/foo', auth: { username: 'user', password: '\uD800', }, }), (err) => { assert.ok(err instanceof AxiosError); assert.strictEqual(err.code, AxiosError.ERR_BAD_OPTION_VALUE); return true; } ); }); it('should ignore null form-data headers with content-only policy', () => { const data = new FormData(); data.getHeaders = () => null; const config = resolveConfig({ url: '/upload', data, formDataHeaderPolicy: 'content-only', headers: { 'X-Test': 'ok', }, }); assert.strictEqual(config.headers.get('X-Test'), 'ok'); }); it('should ignore inherited nested serializer fields', () => { let serializeInvoked = false; let encodeInvoked = false; Object.defineProperty(Object.prototype, 'serialize', { value() { serializeInvoked = true; return 'inherited=1'; }, configurable: true, }); Object.defineProperty(Object.prototype, 'encode', { value() { encodeInvoked = true; return 'inherited'; }, configurable: true, }); try { const config = resolveConfig({ url: '/foo', params: { value: 'a b' }, paramsSerializer: {}, }); assert.strictEqual(config.url, '/foo?value=a+b'); assert.strictEqual(serializeInvoked, false); assert.strictEqual(encodeInvoked, false); } finally { delete Object.prototype.serialize; delete Object.prototype.encode; } }); }); axios-axios-aa54a5e/tests/unit/helpers/shouldBypassProxy.test.js000066400000000000000000000515461526660636600253210ustar00rootroot00000000000000import { afterEach, describe, expect, it } from 'vitest'; import shouldBypassProxy from '../../../lib/helpers/shouldBypassProxy.js'; const originalNoProxy = process.env.no_proxy; const originalNOProxy = process.env.NO_PROXY; const setNoProxy = (value) => { process.env.no_proxy = value; process.env.NO_PROXY = value; }; afterEach(() => { if (originalNoProxy === undefined) { delete process.env.no_proxy; } else { process.env.no_proxy = originalNoProxy; } if (originalNOProxy === undefined) { delete process.env.NO_PROXY; } else { process.env.NO_PROXY = originalNOProxy; } }); describe('helpers::shouldBypassProxy', () => { describe('CIDR entries', () => { it('should match IPv4 ranges and their prefix boundaries', () => { setNoProxy('172.16.0.0/12'); expect(shouldBypassProxy('http://172.16.0.1/')).toBe(true); expect(shouldBypassProxy('http://172.31.255.255/')).toBe(true); expect(shouldBypassProxy('http://172.15.255.255/')).toBe(false); expect(shouldBypassProxy('http://172.32.0.0/')).toBe(false); }); it('should match bracketed and unbracketed IPv6 ranges', () => { for (const entry of ['fd00::/8', '[fd00::]/8']) { setNoProxy(entry); expect(shouldBypassProxy('http://[fd12:3456::1]/')).toBe(true); expect(shouldBypassProxy('http://[fe00::1]/')).toBe(false); } }); it('should translate IPv4-mapped IPv6 ranges', () => { setNoProxy('::ffff:127.0.0.0/104'); expect(shouldBypassProxy('http://127.10.20.30/')).toBe(true); expect(shouldBypassProxy('http://128.0.0.1/')).toBe(false); }); it('should keep address families separate for zero-length prefixes', () => { setNoProxy('0.0.0.0/0'); expect(shouldBypassProxy('http://203.0.113.7/')).toBe(true); expect(shouldBypassProxy('http://[2001:db8::1]/')).toBe(false); setNoProxy('::/0'); expect(shouldBypassProxy('http://[2001:db8::1]/')).toBe(true); expect(shouldBypassProxy('http://203.0.113.7/')).toBe(false); }); it('should fail closed for malformed ranges', () => { for (const entry of ['10.0.0.0/33', 'fd00::/129', 'example.com/24', '10.0.0.0/01']) { setNoProxy(entry); expect(shouldBypassProxy('http://10.0.0.1/')).toBe(false); expect(shouldBypassProxy('http://[fd00::1]/')).toBe(false); } }); }); it('should handle a long non-matching dotted entry within the test timeout', () => { setNoProxy(`example.com${'.'.repeat(100000)}invalid`); expect(shouldBypassProxy('http://example.com/')).toBe(false); }, 1000); it('should handle a long non-matching dotted hostname within the test timeout', () => { setNoProxy('example.com'); expect(shouldBypassProxy(`http://${'.'.repeat(100000)}invalid/`)).toBe(false); }, 1000); it('should bypass proxy for localhost with a trailing dot', () => { setNoProxy('localhost,127.0.0.1,::1'); expect(shouldBypassProxy('http://localhost.:8080/')).toBe(true); }); it('should bypass proxy for bracketed ipv6 loopback', () => { setNoProxy('localhost,127.0.0.1,::1'); expect(shouldBypassProxy('http://[::1]:8080/')).toBe(true); }); it('should support bracketed ipv6 entries in no_proxy', () => { setNoProxy('[::1]'); expect(shouldBypassProxy('http://[::1]:8080/')).toBe(true); }); it('should bypass proxy for 127.0.0.1 when no_proxy contains localhost', () => { setNoProxy('localhost'); expect(shouldBypassProxy('http://127.0.0.1:7777/')).toBe(true); }); it('should bypass proxy for [::1] when no_proxy contains localhost', () => { setNoProxy('localhost'); expect(shouldBypassProxy('http://[::1]:7777/')).toBe(true); }); it('should bypass proxy for localhost when no_proxy contains 127.0.0.1', () => { setNoProxy('127.0.0.1'); expect(shouldBypassProxy('http://localhost:7777/')).toBe(true); }); it('should bypass proxy for localhost when no_proxy contains ::1', () => { setNoProxy('::1'); expect(shouldBypassProxy('http://localhost:7777/')).toBe(true); }); it('should bypass proxy for 0.0.0.0 when no_proxy contains a local entry', () => { for (const entry of ['localhost', '127.0.0.1', '::1']) { setNoProxy(entry); expect(shouldBypassProxy('http://0.0.0.0:7777/')).toBe(true); } }); it('should respect explicit ports for 0.0.0.0 local matching', () => { setNoProxy('localhost:8080'); expect(shouldBypassProxy('http://0.0.0.0:8080/')).toBe(true); expect(shouldBypassProxy('http://0.0.0.0:9090/')).toBe(false); }); it('should bypass proxy for the IPv6 unspecified address symmetrically with 0.0.0.0', () => { for (const entry of ['localhost', '127.0.0.1', '::1']) { setNoProxy(entry); expect(shouldBypassProxy('http://[::]:7777/')).toBe(true); expect(shouldBypassProxy('http://[0:0:0:0:0:0:0:0]:7777/')).toBe(true); } }); it('should bypass proxy for compressed IPv6 unspecified request forms', () => { setNoProxy('localhost,127.0.0.1,::1'); for (const host of ['0::', '::0', '0:0::', '::0:0', '0::0']) { expect(shouldBypassProxy(`http://[${host}]:7777/`)).toBe(true); } }); it('should bypass proxy for compressed IPv6 unspecified no_proxy entries', () => { for (const entry of ['0::', '::0', '0:0::', '::0:0', '0::0']) { setNoProxy(entry); expect(shouldBypassProxy('http://[::]:7777/')).toBe(true); expect(shouldBypassProxy('http://[0:0:0:0:0:0:0:0]:7777/')).toBe(true); } }); it('should respect explicit ports on compressed IPv6 unspecified no_proxy entries', () => { setNoProxy('[0::]:8080'); expect(shouldBypassProxy('http://[::]:8080/')).toBe(true); expect(shouldBypassProxy('http://[::]:9090/')).toBe(false); }); it('should not treat nonzero compressed IPv6 addresses as unspecified', () => { setNoProxy('0::2'); expect(shouldBypassProxy('http://[::]:7777/')).toBe(false); }); it('should still route a real public IPv6 host through the proxy', () => { setNoProxy('localhost'); expect(shouldBypassProxy('http://[2001:db8::1]:7777/')).toBe(false); }); it('should match wildcard and explicit ports', () => { setNoProxy('*.example.com,localhost:8080'); expect(shouldBypassProxy('http://api.example.com/')).toBe(true); expect(shouldBypassProxy('http://localhost:8080/')).toBe(true); expect(shouldBypassProxy('http://localhost:8081/')).toBe(false); }); it('should bypass proxy for any host when no_proxy is *', () => { setNoProxy('*'); expect(shouldBypassProxy('http://example.com/')).toBe(true); expect(shouldBypassProxy('http://localhost:1234/')).toBe(true); expect(shouldBypassProxy('http://[::1]:8080/')).toBe(true); }); it.each(['localhost,*,.example.org', 'localhost * .example.org'])( 'should bypass proxy for any host when no_proxy list contains *: %s', (noProxy) => { setNoProxy(noProxy); expect(shouldBypassProxy('http://example.com/')).toBe(true); expect(shouldBypassProxy('http://[2001:db8::1]:8080/')).toBe(true); } ); it('should support bracketed ipv6 with explicit port in no_proxy', () => { setNoProxy('[::1]:8080'); expect(shouldBypassProxy('http://[::1]:8080/')).toBe(true); expect(shouldBypassProxy('http://[::1]:8081/')).toBe(false); }); it('should not bypass when no_proxy is empty', () => { setNoProxy(''); expect(shouldBypassProxy('http://localhost:8080/')).toBe(false); }); it('should not bypass for malformed URLs', () => { setNoProxy('localhost,127.0.0.1,::1'); expect(shouldBypassProxy('not a url')).toBe(false); }); it('should bypass proxy for 127.0.0.0/8 subnet when no_proxy contains 127.0.0.1', () => { setNoProxy('localhost,127.0.0.1,::1'); expect(shouldBypassProxy('http://127.0.0.2:9191/secret')).toBe(true); expect(shouldBypassProxy('http://127.0.0.100:9191/secret')).toBe(true); expect(shouldBypassProxy('http://127.1.2.3:9191/secret')).toBe(true); expect(shouldBypassProxy('http://127.255.255.254:9191/secret')).toBe(true); }); it('should bypass proxy for 127.0.0.0/8 subnet when no_proxy contains localhost', () => { setNoProxy('localhost'); expect(shouldBypassProxy('http://127.0.0.2:7777/')).toBe(true); expect(shouldBypassProxy('http://127.1.2.3:7777/')).toBe(true); }); it('should NOT bypass for non-loopback IPv4 addresses', () => { setNoProxy('localhost,127.0.0.1,::1'); expect(shouldBypassProxy('http://128.0.0.1:9191/')).toBe(false); expect(shouldBypassProxy('http://126.255.255.255:9191/')).toBe(false); expect(shouldBypassProxy('http://10.0.0.1:9191/')).toBe(false); expect(shouldBypassProxy('http://192.168.1.1:9191/')).toBe(false); }); it('should NOT treat malformed 127-prefixed values as loopback', () => { setNoProxy('localhost,127.0.0.1,::1'); // bracketed IPv6 that happens to contain 127 dotted-form must not match IPv4 loopback expect(shouldBypassProxy('http://example.com/')).toBe(false); }); it('should bypass proxy for full-form IPv6 loopback 0:0:0:0:0:0:0:1', () => { setNoProxy('localhost,127.0.0.1,::1'); expect(shouldBypassProxy('http://[0:0:0:0:0:0:0:1]:8080/')).toBe(true); }); it('should bypass proxy for IPv4-mapped IPv6 loopback ::ffff:127.0.0.1', () => { setNoProxy('localhost,127.0.0.1,::1'); expect(shouldBypassProxy('http://[::ffff:127.0.0.1]:8080/')).toBe(true); }); it('should treat 127.x.x.x as cross-equivalent to localhost and ::1', () => { setNoProxy('::1'); expect(shouldBypassProxy('http://127.0.0.5:7777/')).toBe(true); }); it('should still respect explicit port mismatch on no_proxy entries', () => { setNoProxy('127.0.0.1:8080'); // same-port → bypass via cross-loopback equivalence expect(shouldBypassProxy('http://127.0.0.2:8080/')).toBe(true); // different port → no bypass expect(shouldBypassProxy('http://127.0.0.2:9090/')).toBe(false); }); it('should not bypass for hosts that merely contain 127 in other octets', () => { setNoProxy('localhost,127.0.0.1,::1'); expect(shouldBypassProxy('http://10.0.0.127:8080/')).toBe(false); expect(shouldBypassProxy('http://200.127.0.1:8080/')).toBe(false); }); // IPv4-mapped IPv6 normalization: an attacker (or naive caller) can use the // IPv4-mapped IPv6 representation of an address (e.g. ::ffff:192.168.1.5) // to dodge a NO_PROXY policy expressed in IPv4 form, or vice-versa. After // canonicalising both sides, equivalent addresses compare equal. describe('IPv4-mapped IPv6 normalization', () => { it('should bypass via IPv4-mapped IPv6 request when NO_PROXY uses the IPv4 form', () => { setNoProxy('192.168.1.5'); expect(shouldBypassProxy('http://[::ffff:192.168.1.5]/')).toBe(true); }); it('should bypass via Node-normalised IPv4-mapped hex request against an IPv4 NO_PROXY', () => { // Node's URL parser canonicalises [::ffff:192.168.1.5] → [::ffff:c0a8:105]. // The hex form must unmap to 192.168.1.5 to match the entry. setNoProxy('192.168.1.5'); expect(shouldBypassProxy('http://[::ffff:c0a8:105]/')).toBe(true); }); it('should bypass via plain IPv4 request when NO_PROXY uses the IPv4-mapped IPv6 dotted form', () => { setNoProxy('::ffff:192.168.1.5'); expect(shouldBypassProxy('http://192.168.1.5/')).toBe(true); }); it('should bypass via plain IPv4 request when NO_PROXY uses the IPv4-mapped IPv6 hex form', () => { setNoProxy('::ffff:a00:1'); expect(shouldBypassProxy('http://10.0.0.1/')).toBe(true); }); it('should bypass via plain IPv4 request when NO_PROXY uses a bracketed IPv4-mapped IPv6 entry', () => { setNoProxy('[::ffff:192.168.1.5]'); expect(shouldBypassProxy('http://192.168.1.5/')).toBe(true); }); it('should treat the uncompressed 0:0:0:0:0:ffff: form as equivalent', () => { setNoProxy('0:0:0:0:0:ffff:10.0.0.1'); expect(shouldBypassProxy('http://10.0.0.1/')).toBe(true); expect(shouldBypassProxy('http://[::ffff:10.0.0.1]/')).toBe(true); }); it('should treat compressed zero-prefix IPv4-mapped IPv6 dotted forms as equivalent', () => { for (const entry of [ '0::ffff:192.168.1.5', '0:0::ffff:192.168.1.5', '0:0:0::ffff:192.168.1.5', '0:0:0:0::ffff:192.168.1.5', ]) { setNoProxy(entry); expect(shouldBypassProxy('http://192.168.1.5/')).toBe(true); } }); it('should treat compressed zero-prefix IPv4-mapped IPv6 hex forms as equivalent', () => { for (const entry of [ '0::ffff:c0a8:105', '0:0::ffff:c0a8:105', '0:0:0::ffff:c0a8:105', '0:0:0:0::ffff:c0a8:105', ]) { setNoProxy(entry); expect(shouldBypassProxy('http://192.168.1.5/')).toBe(true); } }); it('should support compressed bracketed IPv4-mapped IPv6 entries with explicit ports', () => { setNoProxy('[0:0::ffff:192.168.1.5]:8080'); expect(shouldBypassProxy('http://192.168.1.5:8080/')).toBe(true); expect(shouldBypassProxy('http://192.168.1.5:9090/')).toBe(false); }); it('should NOT cross-match unrelated addresses', () => { setNoProxy('192.168.1.5'); // Different IPv4 address inside an IPv4-mapped form must not bypass. expect(shouldBypassProxy('http://[::ffff:192.168.1.6]/')).toBe(false); // Non-mapped IPv6 must not be treated as IPv4. expect(shouldBypassProxy('http://[2001:db8::1]/')).toBe(false); }); it('should leave non-mapped IPv6 addresses comparing as IPv6', () => { setNoProxy('2001:db8::1'); expect(shouldBypassProxy('http://[2001:db8::1]/')).toBe(true); expect(shouldBypassProxy('http://[2001:db8::2]/')).toBe(false); }); }); // Node's URL parser accepts IPv4 shorthand, octal (0-prefixed), and hex // (0x-prefixed) forms in the host portion, canonicalising them to dotted- // decimal. Without symmetric normalisation of NO_PROXY entries, an entry // like `NO_PROXY=127.1` would fail to match a request to `127.0.0.1` even // though the user clearly meant the same host. These tests pin down both // sides of the comparison. describe('IPv4 shorthand / octal / hex normalization', () => { it('should match a shorthand entry against a canonical request', () => { setNoProxy('127.1'); expect(shouldBypassProxy('http://127.0.0.1:7777/')).toBe(true); }); it('should match an octal entry against a canonical request', () => { setNoProxy('0177.0.0.1'); expect(shouldBypassProxy('http://127.0.0.1:7777/')).toBe(true); }); it('should match a hex entry against a canonical request', () => { setNoProxy('0x7f.0.0.1'); expect(shouldBypassProxy('http://127.0.0.1:7777/')).toBe(true); }); it('should match a shorthand request against a canonical entry', () => { setNoProxy('127.0.0.1'); // Node URL parser canonicalises shorthand `127.1` to `127.0.0.1`. expect(shouldBypassProxy('http://127.1:7777/')).toBe(true); }); it('should match an octal request against a canonical entry', () => { setNoProxy('127.0.0.1'); // Node URL parser canonicalises octal `0177.0.0.1` to `127.0.0.1`. expect(shouldBypassProxy('http://0177.0.0.1:7777/')).toBe(true); }); it('should match a hex request against a canonical entry', () => { setNoProxy('127.0.0.1'); // Node URL parser canonicalises hex `0x7f.0.0.1` to `127.0.0.1`. expect(shouldBypassProxy('http://0x7f.0.0.1:7777/')).toBe(true); }); it('should normalise octal on non-loopback addresses symmetrically', () => { // Both sides use the same octal form; both canonicalise to 8.0.0.1. setNoProxy('010.0.0.1'); expect(shouldBypassProxy('http://010.0.0.1:7777/')).toBe(true); }); it('should treat octal `00` as equivalent to decimal `0`', () => { setNoProxy('00.0.0.0'); expect(shouldBypassProxy('http://0.0.0.0:7777/')).toBe(true); }); it('should NOT bypass for an out-of-range entry that the helper cannot canonicalise', () => { setNoProxy('999.0.0.1'); // Helper rejects `999` (out of 0-255) so the entry stays as `999.0.0.1`, // which does not match the canonical request hostname. expect(shouldBypassProxy('http://127.0.0.1:7777/')).toBe(false); }); it('should NOT bypass for an entry with an invalid hex digit', () => { setNoProxy('0xz.0.0.1'); // `0xz` is neither hex (z is not [0-9a-fA-F]) nor octal nor decimal, // so the helper returns the entry unchanged and the comparison fails. expect(shouldBypassProxy('http://127.0.0.1:7777/')).toBe(false); }); it('should NOT bypass for zero-prefixed entries with invalid octal digits', () => { for (const [entry, request] of [ ['08.0.0.1', 'http://8.0.0.1:7777/'], ['127.08', 'http://127.0.0.8:7777/'], ['127.0.08', 'http://127.0.0.8:7777/'], ]) { setNoProxy(entry); // Node rejects these zero-prefixed host forms instead of treating them // as decimal, so entry-side normalisation must also fail closed. expect(shouldBypassProxy(request)).toBe(false); } }); it('should NOT bypass when an entry is a single numeric token (32-bit-int semantics)', () => { setNoProxy('127'); // Node URL parser treats 1-part as a 32-bit integer, giving `127` -> // `0.0.0.127`. The helper intentionally rejects 1-part inputs as // fail-safe so the policy falls through to non-bypass. expect(shouldBypassProxy('http://127.0.0.1:7777/')).toBe(false); }); it('should preserve explicit ports across octal normalisation', () => { setNoProxy('0177.0.0.1:8080'); // Same port → bypass via cross-octal equivalence. expect(shouldBypassProxy('http://0177.0.0.1:8080/')).toBe(true); // Different port → no bypass. expect(shouldBypassProxy('http://0177.0.0.1:9090/')).toBe(false); }); it('should expand a multi-byte decimal tail low-byte-right (entry-side normalisation)', () => { // 127.65535 → tail 0xFFFF packed into 3 octets → 127.0.255.255. setNoProxy('127.65535'); expect(shouldBypassProxy('http://127.0.255.255/')).toBe(true); }); it('should expand a hex tail low-byte-right (entry-side normalisation)', () => { // 127.0x00ff → tail 0xFF packed into 3 octets → 127.0.0.255. setNoProxy('127.0x00ff'); expect(shouldBypassProxy('http://127.0.0.255/')).toBe(true); }); it('should expand an octal tail low-byte-right (entry-side normalisation)', () => { // 127.0177 → tail 0o177 packed into 3 octets → 127.0.0.127. setNoProxy('127.0177'); expect(shouldBypassProxy('http://127.0.0.127/')).toBe(true); }); it('should expand a multi-byte decimal tail in a 3-part entry', () => { // 127.0.65535 → tail 0xFFFF packed into 2 octets → 127.0.255.255. setNoProxy('127.0.65535'); expect(shouldBypassProxy('http://127.0.255.255/')).toBe(true); }); it('should expand a hex tail in a 3-part entry', () => { // 0.0.0xff → tail 0xFF packed into 1 octet → 0.0.0.255. setNoProxy('0.0.0xff'); expect(shouldBypassProxy('http://0.0.0.255/')).toBe(true); }); it('should match a canonical entry against a shorthand URL whose tail expands to the same address', () => { // Node URL parser canonicalises http://127.65535/ → 127.0.255.255. setNoProxy('127.0.255.255'); expect(shouldBypassProxy('http://127.65535/')).toBe(true); }); it('should NOT bypass when a 1-part hex entry cannot be canonicalised (preserves the deliberate 1-part rejection)', () => { // Node parses 0x7f as 0.0.0.127 (1-part → 32-bit split), but the helper // intentionally rejects 1-part inputs to keep behaviour predictable. The // entry stays as 0x7f; the comparison falls through to non-bypass. setNoProxy('0x7f'); expect(shouldBypassProxy('http://127.0.0.127:7777/')).toBe(false); }); it('should NOT bypass when a 2-part tail exceeds the remaining octet capacity (fail-safe)', () => { // 127.16777216 → tail 16777216 = 2^24 > 2^24 - 1, exceeds the 3-octet // tail capacity. The helper returns the entry unchanged; the URL host // normalises to `127.0.0.0`. The two sides never match, so the policy // correctly falls through to non-bypass. setNoProxy('127.16777216'); // Lock in that the URL side parses cleanly — the fail-safe is on the // entry side, not the URL side (cubic-bot P3 review on commit ed73218). expect(new URL('http://127.0.0.0:7777/').hostname).toBe('127.0.0.0'); expect(shouldBypassProxy('http://127.0.0.0:7777/')).toBe(false); }); it('should NOT bypass when a 3-part tail exceeds the remaining octet capacity (fail-safe)', () => { // 127.0.65536 → tail 65536 > 2^16 - 1, exceeds the 2-octet tail // capacity. Same fail-safe posture as the 2-part out-of-range case. setNoProxy('127.0.65536'); expect(shouldBypassProxy('http://127.0.0.1:7777/')).toBe(false); }); }); }); axios-axios-aa54a5e/tests/unit/helpers/spread.test.js000066400000000000000000000007231526660636600230440ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import spread from '../../../lib/helpers/spread.js'; describe('helpers::spread', () => { it('should spread array to arguments', () => { let value = 0; spread((a, b) => { value = a * b; })([5, 10]); expect(value).toEqual(50); }); it('should return callback result', () => { const value = spread((a, b) => { return a * b; })([5, 10]); expect(value).toEqual(50); }); }); axios-axios-aa54a5e/tests/unit/helpers/throttle.test.js000066400000000000000000000017231526660636600234340ustar00rootroot00000000000000import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import throttle from '../../../lib/helpers/throttle.js'; describe('helpers::throttle', () => { beforeEach(() => { vi.useFakeTimers(); }); afterEach(() => { vi.useRealTimers(); }); it('should flush the last pending arguments', () => { const listener = vi.fn(); const [throttled, flush] = throttle(listener, 1); throttled('first'); throttled('pending'); flush('ignored lifecycle argument'); vi.runAllTimers(); expect(listener.mock.calls).toEqual([['first'], ['pending']]); }); it('should replace pending arguments only through the explicit force function', () => { const listener = vi.fn(); const [throttled, flush, force] = throttle(listener, 1); throttled('first'); throttled('pending'); force('replacement'); flush(); vi.runAllTimers(); expect(listener.mock.calls).toEqual([['first'], ['replacement']]); }); }); axios-axios-aa54a5e/tests/unit/helpers/validator.test.js000066400000000000000000000036131526660636600235540ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import AxiosError from '../../../lib/core/AxiosError.js'; import validator from '../../../lib/helpers/validator.js'; describe('validator::assertOptions', () => { it('should throw only if unknown an option was passed', () => { let error; try { validator.assertOptions( { x: true, }, { y: validator.validators.boolean, } ); } catch (err) { error = err; } expect(error).toBeInstanceOf(AxiosError); expect(error.message).toBe('Unknown option x'); expect(error.code).toBe(AxiosError.ERR_BAD_OPTION); expect(() => { validator.assertOptions( { x: true, }, { x: validator.validators.boolean, y: validator.validators.boolean, } ); }).not.toThrow(new Error('Unknown option x')); }); it("should throw TypeError only if option type doesn't match", () => { let error; try { validator.assertOptions( { x: 123, }, { x: validator.validators.boolean, } ); } catch (err) { error = err; } expect(error).toBeInstanceOf(AxiosError); expect(error.message).toBe('option x must be a boolean'); expect(error.code).toBe(AxiosError.ERR_BAD_OPTION_VALUE); expect(() => { validator.assertOptions( { x: true, }, { x: validator.validators.boolean, y: validator.validators.boolean, } ); }).not.toThrow(); }); it('should reject null options', () => { let error; try { validator.assertOptions(null, {}); } catch (err) { error = err; } expect(error).toBeInstanceOf(AxiosError); expect(error.message).toBe('options must be an object'); expect(error.code).toBe(AxiosError.ERR_BAD_OPTION_VALUE); }); }); axios-axios-aa54a5e/tests/unit/parseProtocol.test.js000066400000000000000000000027131526660636600227610ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import utils from '../../lib/utils.js'; import parseProtocol from '../../lib/helpers/parseProtocol.js'; describe('helpers::parseProtocol', () => { it('should parse protocol part if it exists', () => { utils.forEach( { 'http://username:password@example.com/': 'http', 'ftp:google.com': 'ftp', 'sms:+15105550101?body=hello%20there': 'sms', 'tel:0123456789': 'tel', '//google.com': '', 'google.com': '', 'admin://etc/default/grub': 'admin', 'stratum+tcp://server:port': 'stratum+tcp', '/api/resource:customVerb': '', 'https://stackoverflow.com/questions/': 'https', 'mailto:jsmith@example.com': 'mailto', 'chrome-extension://1234/.html': 'chrome-extension', }, (expectedProtocol, url) => { assert.strictEqual(parseProtocol(url), expectedProtocol); } ); }); it('should not match URLs without a colon separator', () => { assert.strictEqual(parseProtocol('http//example.com'), ''); }); it('should normalize protocols to lowercase', () => { assert.strictEqual(parseProtocol('HTTP://example.com'), 'http'); assert.strictEqual(parseProtocol('HtTpS://example.com/CaseSensitive?token=AbC'), 'https'); assert.strictEqual(parseProtocol('DATA:text/plain,hello'), 'data'); assert.strictEqual(parseProtocol('DaTa:text/plain,Hello'), 'data'); }); }); axios-axios-aa54a5e/tests/unit/platform.test.js000066400000000000000000000013151526660636600217460ustar00rootroot00000000000000import { describe, it } from 'vitest'; import platform from '../../lib/platform/index.js'; import assert from 'assert'; describe('generateString', () => { it('should generate a string of the specified length using the default alphabet', () => { const size = 10; const str = platform.generateString(size); assert.strictEqual(str.length, size); }); it('should generate a string using only characters from the default alphabet', () => { const size = 10; const alphabet = platform.ALPHABET.ALPHA_DIGIT; const str = platform.generateString(size, alphabet); for (let char of str) { assert.ok(alphabet.includes(char), `Character ${char} is not in the alphabet`); } }); }); axios-axios-aa54a5e/tests/unit/prototypePollution.test.js000066400000000000000000001566411526660636600241120ustar00rootroot00000000000000/* eslint-disable no-prototype-builtins */ import { afterEach, describe, it } from 'vitest'; import assert from 'assert'; import http from 'http'; import vm from 'node:vm'; import utils from '../../lib/utils.js'; import mergeConfig from '../../lib/core/mergeConfig.js'; import defaults from '../../lib/defaults/index.js'; import AxiosError from '../../lib/core/AxiosError.js'; import AxiosHeaders from '../../lib/core/AxiosHeaders.js'; import resolveConfig from '../../lib/helpers/resolveConfig.js'; import axios from '../../index.js'; describe('Prototype Pollution Protection', () => { afterEach(() => { // Clean up any pollution that might have occurred. delete Object.prototype.polluted; delete Object.prototype.parseReviver; delete Object.prototype.transport; delete Object.prototype.transformRequest; delete Object.prototype.transformResponse; delete Object.prototype.formSerializer; delete Object.prototype.httpVersion; delete Object.prototype.lookup; delete Object.prototype.family; delete Object.prototype.http2Options; delete Object.prototype.validateStatus; delete Object.prototype.auth; delete Object.prototype.baseURL; delete Object.prototype.socketPath; delete Object.prototype.beforeRedirect; delete Object.prototype.sensitiveHeaders; delete Object.prototype.insecureHTTPParser; delete Object.prototype.adapter; delete Object.prototype.httpAgent; delete Object.prototype.httpsAgent; delete Object.prototype.proxy; delete Object.prototype.maxContentLength; delete Object.prototype.maxBodyLength; delete Object.prototype.maxRedirects; delete Object.prototype.maxRate; delete Object.prototype.timeout; delete Object.prototype.transitional; delete Object.prototype.timeoutErrorMessage; delete Object.prototype.env; delete Object.prototype.cancelToken; delete Object.prototype.signal; delete Object.prototype.decompress; delete Object.prototype.params; delete Object.prototype.paramsSerializer; delete Object.prototype.method; delete Object.prototype.withCredentials; delete Object.prototype.responseType; delete Object.prototype.fetchOptions; delete Object.prototype.username; delete Object.prototype.password; delete Object.prototype.hostname; delete Object.prototype.host; delete Object.prototype.port; delete Object.prototype.protocol; delete Object.prototype.get; delete Object.prototype.set; delete Object.prototype.headers; delete Object.prototype.createConnection; delete Object.prototype.customNested; }); describe('utils.merge', () => { it('should filter __proto__ key at top level', () => { const result = utils.merge({}, { __proto__: { polluted: 'yes' }, safe: 'value' }); assert.strictEqual(Object.prototype.polluted, undefined); assert.strictEqual(result.safe, 'value'); assert.strictEqual(result.hasOwnProperty('__proto__'), false); }); it('should filter constructor key at top level', () => { const result = utils.merge({}, { constructor: { polluted: 'yes' }, safe: 'value' }); assert.strictEqual(result.safe, 'value'); assert.strictEqual(result.hasOwnProperty('constructor'), false); }); it('should filter prototype key at top level', () => { const result = utils.merge({}, { prototype: { polluted: 'yes' }, safe: 'value' }); assert.strictEqual(result.safe, 'value'); assert.strictEqual(result.hasOwnProperty('prototype'), false); }); it('should filter __proto__ key in nested objects', () => { const result = utils.merge( {}, { headers: { __proto__: { polluted: 'nested' }, 'Content-Type': 'application/json', }, } ); assert.strictEqual(Object.prototype.polluted, undefined); assert.strictEqual(result.headers['Content-Type'], 'application/json'); assert.strictEqual(result.headers.hasOwnProperty('__proto__'), false); }); it('should filter constructor key in nested objects', () => { const result = utils.merge( {}, { headers: { constructor: { prototype: { polluted: 'nested' } }, 'Content-Type': 'application/json', }, } ); assert.strictEqual(Object.prototype.polluted, undefined); assert.strictEqual(result.headers['Content-Type'], 'application/json'); assert.strictEqual(result.headers.hasOwnProperty('constructor'), false); }); it('should filter prototype key in nested objects', () => { const result = utils.merge( {}, { headers: { prototype: { polluted: 'nested' }, 'Content-Type': 'application/json', }, } ); assert.strictEqual(result.headers['Content-Type'], 'application/json'); assert.strictEqual(result.headers.hasOwnProperty('prototype'), false); }); it('should filter dangerous keys in deeply nested objects', () => { const result = utils.merge( {}, { level1: { level2: { __proto__: { polluted: 'deep' }, prototype: { polluted: 'deep' }, safe: 'value', }, }, } ); assert.strictEqual(Object.prototype.polluted, undefined); assert.strictEqual(result.level1.level2.safe, 'value'); assert.strictEqual(result.level1.level2.hasOwnProperty('__proto__'), false); }); it('should still merge regular properties correctly', () => { const result = utils.merge({ a: 1, b: { c: 2 } }, { b: { d: 3 }, e: 4 }); assert.strictEqual(result.a, 1); assert.strictEqual(result.b.c, 2); assert.strictEqual(result.b.d, 3); assert.strictEqual(result.e, 4); }); it('should handle JSON.parse payloads safely', () => { const malicious = JSON.parse('{"__proto__": {"polluted": "yes"}}'); const result = utils.merge({}, malicious); assert.strictEqual(Object.prototype.polluted, undefined); assert.strictEqual(result.hasOwnProperty('__proto__'), false); }); it('should handle nested JSON.parse payloads safely', () => { const malicious = JSON.parse( '{"headers": {"constructor": {"prototype": {"polluted": "yes"}}}}' ); const result = utils.merge({}, malicious); assert.strictEqual(Object.prototype.polluted, undefined); assert.strictEqual(result.headers.hasOwnProperty('constructor'), false); }); }); describe('mergeConfig', () => { it('should filter dangerous keys at top level', () => { const result = mergeConfig( {}, { __proto__: { polluted: 'yes' }, constructor: { polluted: 'yes' }, prototype: { polluted: 'yes' }, url: '/api/test', } ); assert.strictEqual(Object.prototype.polluted, undefined); assert.strictEqual(result.url, '/api/test'); assert.strictEqual(result.hasOwnProperty('__proto__'), false); assert.strictEqual(result.hasOwnProperty('constructor'), false); assert.strictEqual(result.hasOwnProperty('prototype'), false); }); it('should filter dangerous keys in headers', () => { const result = mergeConfig( {}, { headers: { __proto__: { polluted: 'yes' }, 'Content-Type': 'application/json', }, } ); assert.strictEqual(Object.prototype.polluted, undefined); assert.strictEqual(result.headers['Content-Type'], 'application/json'); assert.strictEqual(result.headers.hasOwnProperty('__proto__'), false); }); it('should filter dangerous keys in custom config properties', () => { const result = mergeConfig( {}, { customProp: { __proto__: { polluted: 'yes' }, safe: 'value', }, } ); assert.strictEqual(Object.prototype.polluted, undefined); assert.strictEqual(result.customProp.safe, 'value'); assert.strictEqual(result.customProp.hasOwnProperty('__proto__'), false); }); it('should still merge configs correctly', () => { const config1 = { baseURL: 'https://api.example.com', timeout: 1000, headers: { common: { Accept: 'application/json', }, }, }; const config2 = { url: '/users', timeout: 5000, headers: { common: { 'Content-Type': 'application/json', }, }, }; const result = mergeConfig(config1, config2); assert.strictEqual(result.baseURL, 'https://api.example.com'); assert.strictEqual(result.url, '/users'); assert.strictEqual(result.timeout, 5000); assert.strictEqual(result.headers.common.Accept, 'application/json'); assert.strictEqual(result.headers.common['Content-Type'], 'application/json'); }); // Polluted transformRequest/Response must not // replace the safe defaults through inherited reads during merge. it('should not inherit polluted transformRequest from Object.prototype', () => { const polluted = () => 'attacker'; Object.prototype.transformRequest = polluted; const result = mergeConfig({ transformRequest: [(d) => d] }, { url: '/x' }); assert.notStrictEqual(result.transformRequest, polluted); assert.ok(Array.isArray(result.transformRequest)); }); it('should not inherit polluted transformResponse from Object.prototype', () => { const polluted = () => 'attacker'; Object.prototype.transformResponse = polluted; const result = mergeConfig({ transformResponse: [(d) => d] }, { url: '/x' }); assert.notStrictEqual(result.transformResponse, polluted); assert.ok(Array.isArray(result.transformResponse)); }); }); // parseReviver read via prototype chain. describe('defaults.transformResponse parseReviver', () => { it('should ignore Object.prototype.parseReviver when parsing JSON', () => { let reviverCalled = false; Object.prototype.parseReviver = function polluted(k, v) { reviverCalled = true; if (k === 'role') return 'admin'; return v; }; const ctx = { transitional: defaults.transitional }; const result = defaults.transformResponse[0].call(ctx, '{"role":"user","balance":100}'); assert.strictEqual(reviverCalled, false); assert.strictEqual(result.role, 'user'); assert.strictEqual(result.balance, 100); }); it('should ignore Object.prototype.responseType', () => { Object.prototype.responseType = 'json'; const ctx = { transitional: defaults.transitional }; // Non-JSON string body must be returned as-is; polluted responseType must // not force strict JSON parsing. const result = defaults.transformResponse[0].call(ctx, 'plain text'); assert.strictEqual(result, 'plain text'); delete Object.prototype.responseType; }); }); // mergeDirectKeys must not inherit validateStatus from // Object.prototype (was using the `in` operator which traverses the chain). describe('validateStatus merge', () => { it('should not inherit a polluted validateStatus during mergeConfig', () => { Object.prototype.validateStatus = () => true; const merged = mergeConfig(defaults, { url: '/x' }); assert.strictEqual(merged.validateStatus, defaults.validateStatus); }); it('should keep 4xx/5xx responses rejected when Object.prototype.validateStatus is polluted', async () => { Object.prototype.validateStatus = () => true; const server = http.createServer((req, res) => { res.writeHead(401, { 'Content-Type': 'application/json' }); res.end('{"error":"unauthorized"}'); }); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); const { port } = server.address(); try { let threw = false; try { await axios.get(`http://127.0.0.1:${port}/`); } catch (err) { threw = true; assert.strictEqual(err.response.status, 401); } assert.strictEqual(threw, true); } finally { await new Promise((resolve) => server.close(resolve)); } }, 10000); }); // end-to-end check that a polluted parseReviver does not // tamper with JSON response bodies through the full axios.get pipeline. describe('parseReviver end-to-end', () => { it('should not let Object.prototype.parseReviver tamper with JSON responses', async () => { let reviverCalled = false; const stolen = {}; Object.prototype.parseReviver = function polluted(key, value) { reviverCalled = true; if (key && typeof value !== 'object') stolen[key] = value; if (key === 'isAdmin') return true; if (key === 'role') return 'admin'; if (key === 'balance') return 999999; return value; }; const payload = { user: 'john', role: 'viewer', isAdmin: false, balance: 100, apiKey: 'sk-secret-internal-key', }; const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(payload)); }); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); const { port } = server.address(); try { const res = await axios.get(`http://127.0.0.1:${port}/`); assert.strictEqual(reviverCalled, false); assert.deepStrictEqual(res.data, payload); assert.deepStrictEqual(stolen, {}); } finally { await new Promise((resolve) => server.close(resolve)); } }, 10000); }); // http adapter must not read config.transport // (or related keys) from Object.prototype. describe('http adapter prototype reads', () => { it('should not invoke Object.prototype.transport on a request', async () => { let hijackCalled = false; Object.prototype.transport = { request(options, handleResponse) { hijackCalled = true; return http.request(options, handleResponse); }, }; const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end('{"ok":true}'); }); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); const { port } = server.address(); try { const res = await axios.get(`http://127.0.0.1:${port}/`); assert.strictEqual(res.data.ok, true); assert.strictEqual(hijackCalled, false); } finally { await new Promise((resolve) => server.close(resolve)); } }, 10000); it('should pass an own inert createConnection option to transports', async () => { Object.prototype.createConnection = () => { throw new Error('unexpected inherited createConnection'); }; const server = http.createServer((req, res) => res.end('ok')); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); try { const response = await axios.get(`http://127.0.0.1:${server.address().port}/`, { transport: { request(options, handleResponse) { assert.strictEqual( Object.prototype.hasOwnProperty.call(options, 'createConnection'), true ); assert.strictEqual(options.createConnection, undefined); return http.request(options, handleResponse); }, }, }); assert.strictEqual(response.data, 'ok'); } finally { await new Promise((resolve) => server.close(resolve)); } }, 10000); }); // Five config properties were read via direct property // access in the http adapter and resolveConfig, bypassing hasOwnProperty and // allowing prototype pollution gadgets (auth, baseURL, socketPath, // beforeRedirect, insecureHTTPParser). describe('http adapter gadgets', () => { function startServer(handler) { return new Promise((resolve) => { const server = http.createServer( handler || ((req, res) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ headers: req.headers, url: req.url })); }) ); server.listen(0, '127.0.0.1', () => resolve(server)); }); } function stopServer(server) { return new Promise((resolve) => server.close(resolve)); } it('should not pick up Object.prototype.auth as an Authorization header', async () => { Object.prototype.auth = { username: 'attacker', password: 'exfil' }; const server = await startServer(); const { port } = server.address(); try { const res = await axios.get(`http://127.0.0.1:${port}/api`); assert.strictEqual(res.data.headers.authorization, undefined); } finally { await stopServer(server); } }, 10000); it('should not pick up Object.prototype.socketPath and redirect the request', async () => { Object.prototype.socketPath = '/tmp/axios-should-never-be-used.sock'; const server = await startServer(); const { port } = server.address(); try { const res = await axios.get(`http://127.0.0.1:${port}/api`); assert.strictEqual(res.status, 200); assert.strictEqual(res.data.url, '/api'); } finally { await stopServer(server); } }, 10000); it('should not invoke Object.prototype.beforeRedirect during redirects', async () => { let hijackCalled = false; Object.prototype.beforeRedirect = function polluted() { hijackCalled = true; }; const target = await startServer(); const { port: targetPort } = target.address(); const redirector = await startServer((req, res) => { res.writeHead(302, { Location: `http://127.0.0.1:${targetPort}/final` }); res.end(); }); const { port: redirectorPort } = redirector.address(); try { const res = await axios.get(`http://127.0.0.1:${redirectorPort}/start`); assert.strictEqual(res.status, 200); assert.strictEqual(hijackCalled, false); } finally { await stopServer(redirector); await stopServer(target); } }, 10000); it('should not pick up Object.prototype.sensitiveHeaders during redirects', async () => { Object.prototype.sensitiveHeaders = ['X-Secret']; let capturedHeaders; const target = await startServer((req, res) => { capturedHeaders = req.headers; res.writeHead(200, { 'Content-Type': 'application/json' }); res.end('{"ok":true}'); }); const { port: targetPort } = target.address(); const redirector = await startServer((req, res) => { res.writeHead(302, { Location: `http://127.0.0.1:${targetPort}/final` }); res.end(); }); const { port: redirectorPort } = redirector.address(); try { await axios.get(`http://127.0.0.1:${redirectorPort}/start`, { headers: { 'X-Secret': 'keep' }, }); assert.strictEqual(capturedHeaders['x-secret'], 'keep'); } finally { await stopServer(redirector); await stopServer(target); } }, 10000); it('should not enable insecureHTTPParser via Object.prototype', async () => { // A raw TCP server emits a response that uses LF-only line terminators // instead of CRLF. Node's strict HTTP parser rejects this payload with // HPE_CR_EXPECTED; the insecure parser accepts it. Verified: with an // explicit `insecureHTTPParser: true` on the request config, this // payload is parsed successfully — so if Object.prototype.insecureHTTPParser // were picked up, the request would succeed. The request must fail when // the gadget is properly blocked. Object.prototype.insecureHTTPParser = true; const net = await import('net'); const malformedPayload = 'HTTP/1.1 200 OK\n' + 'Content-Type: application/json\n' + 'Content-Length: 2\n' + '\n' + '{}'; const malformed = await new Promise((resolve) => { const srv = net.createServer((socket) => { socket.once('data', () => socket.end(malformedPayload)); }); srv.listen(0, '127.0.0.1', () => resolve(srv)); }); const { port } = malformed.address(); try { let threw = false; let caughtCode = ''; try { await axios.get(`http://127.0.0.1:${port}/`, { transitional: { clarifyTimeoutError: false }, }); } catch (err) { threw = true; caughtCode = String(err && (err.code || err.message)); } assert.strictEqual( threw, true, `request should be rejected by the strict HTTP parser (got: ${caughtCode || 'success'})` ); // The exact llhttp code for LF-only line terminators varies across // Node versions (historically HPE_LF_EXPECTED, more recently // HPE_CR_EXPECTED). Match any parser error to remain stable across // Node releases while still confirming the strict parser rejected // the payload. assert.match(caughtCode, /^HPE_/, `expected an HPE_* parser error, got: ${caughtCode}`); } finally { await new Promise((resolve) => malformed.close(resolve)); } }, 10000); it('should not inject Proxy-Authorization from polluted Object.prototype.auth', async () => { // setProxy reads `proxy.auth` directly. When `proxy` is a // URL instance from the environment proxy or a plain object without an own `auth`, // a polluted Object.prototype.auth would otherwise be base64-encoded into the // Proxy-Authorization header, leaking attacker-controlled credentials. Object.prototype.auth = { username: 'attacker', password: 'exfil' }; const proxy = await startServer(); const { port: proxyPort } = proxy.address(); const target = await startServer(); const { port: targetPort } = target.address(); try { const res = await axios.get(`http://127.0.0.1:${targetPort}/api`, { proxy: { host: '127.0.0.1', port: proxyPort, protocol: 'http' }, }); assert.strictEqual(res.status, 200); assert.strictEqual( res.data.headers['proxy-authorization'], undefined, 'polluted Object.prototype.auth must not produce a Proxy-Authorization header' ); } finally { await stopServer(target); await stopServer(proxy); } }, 10000); it('should not inject Proxy-Authorization from polluted Object.prototype.username', async () => { // The setProxy username/password branch builds basic creds from `proxy.username` // and `proxy.password`. For a plain object proxy, both reads must be guarded // against prototype pollution. Object.prototype.username = 'attacker'; Object.prototype.password = 'exfil'; const proxy = await startServer(); const { port: proxyPort } = proxy.address(); const target = await startServer(); const { port: targetPort } = target.address(); try { const res = await axios.get(`http://127.0.0.1:${targetPort}/api`, { proxy: { host: '127.0.0.1', port: proxyPort, protocol: 'http' }, }); assert.strictEqual(res.status, 200); assert.strictEqual( res.data.headers['proxy-authorization'], undefined, 'polluted Object.prototype.username must not produce a Proxy-Authorization header' ); } finally { await stopServer(target); await stopServer(proxy); } }, 10000); }); describe('resolveConfig baseURL gadget', () => { // The baseURL branch in buildFullPath only runs when the requested URL is // relative (or allowAbsoluteUrls === false). An absolute URL would skip // baseURL regardless of pollution and would not exercise the gadget. We // therefore issue a relative GET and assert that either: // - the request fails (no host to resolve) because baseURL is correctly // absent from the merged config, OR // - the request is fulfilled without hitting the hijacker. // Critically, hijackHit must always be false. it('should not hijack relative-URL requests via Object.prototype.baseURL', async () => { let hijackHit = false; const hijacker = http.createServer((req, res) => { hijackHit = true; res.writeHead(200, { 'Content-Type': 'application/json' }); res.end('{"hijacked":true}'); }); await new Promise((resolve) => hijacker.listen(0, '127.0.0.1', resolve)); const { port: hijackerPort } = hijacker.address(); Object.prototype.baseURL = `http://127.0.0.1:${hijackerPort}`; try { let threw = false; try { await axios.get('/api'); } catch (_err) { threw = true; } // Either the request fails (desired — no baseURL means no host) or it // resolves, but it must NOT hit the polluted hijacker. assert.strictEqual(hijackHit, false); assert.strictEqual(threw, true); } finally { await new Promise((resolve) => hijacker.close(resolve)); } }, 10000); // Second variant using allowAbsoluteUrls: false to force the baseURL path // even for a fully-qualified requested URL. it('should not hijack requests via Object.prototype.baseURL with allowAbsoluteUrls:false', async () => { let hijackHit = false; const hijacker = http.createServer((req, res) => { hijackHit = true; res.writeHead(200, { 'Content-Type': 'application/json' }); res.end('{"hijacked":true}'); }); await new Promise((resolve) => hijacker.listen(0, '127.0.0.1', resolve)); const { port: hijackerPort } = hijacker.address(); const target = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end('{"ok":true}'); }); await new Promise((resolve) => target.listen(0, '127.0.0.1', resolve)); const { port: targetPort } = target.address(); Object.prototype.baseURL = `http://127.0.0.1:${hijackerPort}`; try { // If the gadget were picked up, combineURLs(hijacker, `http://target`) // would route to the hijacker. It must not. let threw = false; try { await axios.get(`http://127.0.0.1:${targetPort}/api`, { allowAbsoluteUrls: false, }); } catch (_err) { threw = true; } assert.strictEqual(hijackHit, false); // allowAbsoluteUrls:false + no baseURL → combineURLs not invoked // (baseURL falsy) → returns requested URL as-is → target receives it. // If baseURL were inherited from prototype, it would be truthy and // combineURLs would be invoked, routing to the hijacker. assert.strictEqual(threw, false); } finally { await new Promise((resolve) => hijacker.close(resolve)); await new Promise((resolve) => target.close(resolve)); } }, 10000); }); describe('resolveConfig params and paramsSerializer gadget', () => { it('should not inherit polluted params via resolveConfig', () => { Object.prototype.params = { injected: 'yes' }; try { const resolved = resolveConfig({ url: '/api', method: 'get' }); assert.ok( resolved.url.indexOf('injected') === -1, 'polluted params must not appear in URL' ); assert.strictEqual(resolved.url, '/api', 'URL must remain unchanged'); } finally { delete Object.prototype.params; } }); it('should not invoke polluted paramsSerializer via resolveConfig', () => { let serializerInvoked = false; Object.prototype.paramsSerializer = function polluted() { serializerInvoked = true; return 'injected=yes'; }; try { const resolved = resolveConfig({ url: '/api', method: 'get', params: { legit: 'true' } }); assert.strictEqual( serializerInvoked, false, 'polluted paramsSerializer must not be called' ); // The URL should have legit param serialized normally assert.ok( resolved.url.indexOf('legit=true') !== -1, 'legitimate params must still be serialized' ); } finally { delete Object.prototype.paramsSerializer; } }); }); // Structural defense: mergeConfig returns a null-prototype object, so any // property read that is not an own property of config cannot inherit from // Object.prototype. Adding a new key to Object.prototype must never appear // as a property of the merged config. describe('mergeConfig null-prototype structural defense', () => { it('should return an object whose prototype is null', () => { const merged = mergeConfig({ url: '/x' }, { method: 'get' }); assert.strictEqual(Object.getPrototypeOf(merged), null); }); it('should preserve hasOwnProperty as a callable own slot', () => { const merged = mergeConfig({}, { url: '/x', method: 'get' }); assert.strictEqual(typeof merged.hasOwnProperty, 'function'); assert.strictEqual(merged.hasOwnProperty('url'), true); assert.strictEqual(merged.hasOwnProperty('method'), true); assert.strictEqual(merged.hasOwnProperty('bogus'), false); }); it('should not serialize hasOwnProperty slot via Object.keys', () => { const merged = mergeConfig({ url: '/x' }, {}); assert.ok(!Object.keys(merged).includes('hasOwnProperty')); }); it('should not expose arbitrary polluted keys as inherited properties', () => { Object.prototype.polluted = 'attacker'; try { const merged = mergeConfig({ url: '/x' }, {}); assert.strictEqual(merged.polluted, undefined); } finally { delete Object.prototype.polluted; } }); }); describe('dispatch config compatibility', () => { it('should preserve an unchanged merged config through dispatch', async () => { let interceptedConfig; let adapterConfig; const instance = axios.create({ adapter: async (config) => { adapterConfig = config; return { data: 'ok', status: 200, statusText: 'OK', headers: {}, config, }; }, }); instance.interceptors.request.use((config) => { interceptedConfig = config; return config; }); const response = await instance.get('/config-identity'); assert.strictEqual(adapterConfig, interceptedConfig); assert.strictEqual(response.config, interceptedConfig); assert.strictEqual(Object.getPrototypeOf(adapterConfig), null); assert.strictEqual( Object.prototype.propertyIsEnumerable.call(adapterConfig, 'hasOwnProperty'), false ); assert.strictEqual(Object.keys(adapterConfig).includes('hasOwnProperty'), false); }); it('should materialize a frozen null-prototype interceptor replacement', async () => { let replacement; const instance = axios.create({ adapter: async (config) => ({ data: 'ok', status: 200, statusText: 'OK', headers: {}, config, }), }); instance.interceptors.request.use((config) => { replacement = Object.freeze(Object.assign(Object.create(null), config)); return replacement; }); const response = await instance.get('/frozen-replacement'); assert.strictEqual(response.data, 'ok'); assert.notStrictEqual(response.config, replacement); assert.strictEqual(Object.getPrototypeOf(response.config), null); assert.strictEqual(Object.isFrozen(response.config), false); }); it('should filter unsafe keys from a null-prototype interceptor replacement', async () => { let replacement; const instance = axios.create({ adapter: async (config) => ({ data: { safe: config.safe, hasProto: Object.prototype.hasOwnProperty.call(config, '__proto__'), hasConstructor: Object.prototype.hasOwnProperty.call(config, 'constructor'), hasPrototype: Object.prototype.hasOwnProperty.call(config, 'prototype'), }, status: 200, statusText: 'OK', headers: {}, config, }), }); instance.interceptors.request.use((config) => { replacement = Object.assign(Object.create(null), config); replacement.safe = 'preserved'; Object.defineProperty(replacement, '__proto__', { value: 'excluded', enumerable: true, configurable: true, writable: true, }); replacement.constructor = 'excluded'; replacement.prototype = 'excluded'; return replacement; }); const response = await instance.get('/unsafe-own-keys'); assert.deepStrictEqual(response.data, { safe: 'preserved', hasProto: false, hasConstructor: false, hasPrototype: false, }); assert.notStrictEqual(response.config, replacement); }); it('should normalize a class-based interceptor replacement', async () => { let replacement; class ConfigTemplate { marker() { return 'template'; } } const instance = axios.create({ adapter: async (config) => ({ data: config.marker(), status: 200, statusText: 'OK', headers: {}, config, }), }); instance.interceptors.request.use((config) => { replacement = new ConfigTemplate(); replacement.url = config.url; replacement.method = config.method; replacement.headers = config.headers; replacement.adapter = config.adapter; return replacement; }); const response = await instance.get('/normalized-replacement'); assert.strictEqual(response.data, 'template'); assert.notStrictEqual(response.config, replacement); assert.strictEqual(response.config instanceof ConfigTemplate, false); assert.strictEqual(Object.getPrototypeOf(response.config), null); assert.strictEqual(Object.prototype.hasOwnProperty.call(response.config, 'marker'), true); }); }); // Verify every gadget enumerated in the audit // is neutralized end-to-end by the null-prototype config. describe('Full gadget coverage via null-prototype config', () => { function startEcho(handler) { return new Promise((resolve) => { const server = http.createServer( handler || ((req, res) => { let body = ''; req.on('data', (c) => (body += c)); req.on('end', () => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end( JSON.stringify({ url: req.url, method: req.method, headers: req.headers, body, }) ); }); }) ); server.listen(0, '127.0.0.1', () => resolve(server)); }); } const stop = (s) => new Promise((r) => s.close(r)); it('should ignore polluted transformRequest', async () => { let invoked = false; Object.prototype.transformRequest = function polluted(data) { invoked = true; return 'INJECTED'; }; const server = await startEcho(); const { port } = server.address(); try { const res = await axios.post(`http://127.0.0.1:${port}/`, { hello: 'world' }); assert.strictEqual(invoked, false); assert.notStrictEqual(res.data.body, 'INJECTED'); } finally { await stop(server); } }, 10000); it('should ignore polluted transformResponse', async () => { let invoked = false; Object.prototype.transformResponse = function polluted() { invoked = true; return 'HIJACKED'; }; const server = await startEcho(); const { port } = server.address(); try { const res = await axios.get(`http://127.0.0.1:${port}/`); assert.strictEqual(invoked, false); assert.notStrictEqual(res.data, 'HIJACKED'); } finally { await stop(server); } }, 10000); it('should ignore polluted adapter', async () => { let hijacked = false; Object.prototype.adapter = function pollutedAdapter() { hijacked = true; return Promise.resolve({ data: 'pwned', status: 200, statusText: 'OK', headers: {}, config: {}, request: {}, }); }; const server = await startEcho(); const { port } = server.address(); try { const res = await axios.get(`http://127.0.0.1:${port}/ok`); assert.strictEqual(hijacked, false); assert.notStrictEqual(res.data, 'pwned'); } finally { await stop(server); } }, 10000); it('should ignore polluted httpAgent', async () => { let agentUsed = false; Object.prototype.httpAgent = new http.Agent({ keepAlive: false, }); // Wrap createConnection to detect usage const origCreate = Object.prototype.httpAgent.createConnection; Object.prototype.httpAgent.createConnection = function (...args) { agentUsed = true; return origCreate.apply(this, args); }; const server = await startEcho(); const { port } = server.address(); try { const res = await axios.get(`http://127.0.0.1:${port}/`); assert.strictEqual(res.status, 200); assert.strictEqual(agentUsed, false); } finally { await stop(server); } }, 10000); it('should ignore polluted proxy', async () => { Object.prototype.proxy = { protocol: 'http', host: '127.0.0.1', port: 1, // would fail if actually used }; const server = await startEcho(); const { port } = server.address(); try { const res = await axios.get(`http://127.0.0.1:${port}/`); assert.strictEqual(res.status, 200); } finally { await stop(server); } }, 10000); it('should ignore polluted maxContentLength', async () => { // Polluted tiny limit would reject a normal response if applied. Object.prototype.maxContentLength = 1; const server = await startEcho(); const { port } = server.address(); try { const res = await axios.get(`http://127.0.0.1:${port}/`); assert.strictEqual(res.status, 200); } finally { await stop(server); } }, 10000); it('should ignore polluted maxRedirects', async () => { // Pollute with 0 — if picked up, follow-redirects path would be skipped. // We make sure regular requests still succeed via the expected path. Object.prototype.maxRedirects = 0; const server = await startEcho(); const { port } = server.address(); try { const res = await axios.get(`http://127.0.0.1:${port}/`); assert.strictEqual(res.status, 200); } finally { await stop(server); } }, 10000); it('should ignore polluted timeout at the merged config level', () => { Object.prototype.timeout = 1; const merged = mergeConfig({}, { url: '/x' }); assert.strictEqual(Object.prototype.hasOwnProperty.call(merged, 'timeout'), false); assert.strictEqual(merged.timeout, undefined); }); it('should ignore polluted timeoutErrorMessage', async () => { Object.prototype.timeoutErrorMessage = 'INJECTED_TIMEOUT'; // Not easy to assert without triggering a real timeout; just confirm // normal requests still succeed and do not read the polluted key. const server = await startEcho(); const { port } = server.address(); try { const res = await axios.get(`http://127.0.0.1:${port}/`); assert.strictEqual(res.status, 200); } finally { await stop(server); } }, 10000); it('should ignore polluted transitional', async () => { Object.prototype.transitional = { forcedJSONParsing: true, silentJSONParsing: false }; const server = await startEcho(); const { port } = server.address(); try { const res = await axios.get(`http://127.0.0.1:${port}/`); assert.strictEqual(res.status, 200); } finally { await stop(server); } }, 10000); it('should ignore polluted params and paramsSerializer', async () => { let serializerInvoked = false; Object.prototype.params = { injected: 'yes' }; Object.prototype.paramsSerializer = function polluted() { serializerInvoked = true; return 'injected=yes'; }; const server = await startEcho(); const { port } = server.address(); try { const res = await axios.get(`http://127.0.0.1:${port}/x`); assert.strictEqual(serializerInvoked, false); assert.strictEqual(res.data.url, '/x'); } finally { await stop(server); } }, 10000); it('should ignore polluted method', async () => { Object.prototype.method = 'DELETE'; const server = await startEcho(); const { port } = server.address(); try { // axios.get should still send GET, not DELETE. const res = await axios.get(`http://127.0.0.1:${port}/ok`); assert.strictEqual(res.data.method, 'GET'); } finally { await stop(server); } }, 10000); it('should use GET when the default instance has no own method', async () => { Object.prototype.method = 'DELETE'; const response = await axios({ url: '/default-method', adapter: async (config) => ({ data: config.method, status: 200, statusText: 'OK', headers: {}, config, }), }); assert.strictEqual(response.data, 'get'); }); it('should ignore headers inherited after a request interceptor replaces config', async () => { Object.prototype.headers = { 'X-Injected': 'yes' }; const instance = axios.create({ adapter: async (config) => ({ data: config.headers.get('X-Injected'), status: 200, statusText: 'OK', headers: {}, config, }), }); instance.interceptors.request.use((config) => { const replacement = { ...config }; delete replacement.headers; return replacement; }); const response = await instance.get('/interceptor-headers'); assert.strictEqual(response.data, undefined); }); it('should ignore behavior inherited after a request interceptor replaces config', async () => { let requestTransformCalled = false; let responseTransformCalled = false; Object.prototype.transformRequest = () => { requestTransformCalled = true; }; Object.prototype.transformResponse = () => { responseTransformCalled = true; }; Object.prototype.cancelToken = { throwIfRequested() { throw new Error('unexpected inherited cancelToken'); }, }; Object.prototype.signal = { aborted: true }; Object.prototype.method = 'delete'; const instance = axios.create({ adapter: async (config) => ({ data: config.method, status: 200, statusText: 'OK', headers: {}, config, }), }); instance.interceptors.request.use((config) => { const replacement = { ...config }; delete replacement.transformRequest; delete replacement.transformResponse; delete replacement.cancelToken; delete replacement.signal; delete replacement.method; return replacement; }); const response = await instance.get('/interceptor-behavior'); assert.strictEqual(requestTransformCalled, false); assert.strictEqual(responseTransformCalled, false); assert.strictEqual(response.data, undefined); }); it('should ignore an adapter inherited after a request interceptor replaces config', async () => { let called = false; Object.prototype.adapter = async (config) => { called = true; return { data: 'unexpected', status: 200, statusText: 'OK', headers: {}, config, }; }; const instance = axios.create(); instance.interceptors.request.use((config) => { const replacement = { ...config }; delete replacement.adapter; return replacement; }); const response = await instance.get('data:text/plain,safe', { responseType: 'text', }); assert.strictEqual(called, false); assert.strictEqual(response.data, 'safe'); }); it('should preserve config inherited from a non-shared prototype after interception', async () => { const instance = axios.create(); instance.interceptors.request.use((config) => { const replacement = Object.create({ adapter: async (resolvedConfig) => ({ data: resolvedConfig.customValue, status: 200, statusText: 'OK', headers: {}, config: resolvedConfig, }), customValue: 'preserved', }); replacement.url = config.url; replacement.method = config.method; replacement.headers = config.headers; return replacement; }); const response = await instance.get('/prototype-template'); assert.strictEqual(response.data, 'preserved'); }); for (const [description, mutateConstructor] of [ ['unchanged', ''], ['set to null', 'Object.prototype.constructor = null;'], ['deleted', 'delete Object.prototype.constructor;'], [ 'replaced by an accessor', `Object.defineProperty(Object.prototype, 'constructor', { configurable: true, get() { return Object; } });`, ], ]) { it(`should exclude a foreign realm Object.prototype with its constructor ${description}`, async () => { const context = vm.createContext({ adapter: async (config) => ({ data: config.customValue, status: 200, statusText: 'OK', headers: {}, config, }), }); const replacement = vm.runInContext( ` Object.prototype.transformResponse = () => 'polluted'; ${mutateConstructor} Object.create({adapter, customValue: 'preserved'}); `, context ); const instance = axios.create(); instance.interceptors.request.use((config) => { replacement.url = config.url; replacement.method = config.method; replacement.headers = config.headers; return replacement; }); const response = await instance.get('/cross-realm-prototype-template'); assert.strictEqual(response.data, 'preserved'); }); } it('should ignore polluted decompress', async () => { Object.prototype.decompress = false; const server = await startEcho(); const { port } = server.address(); try { const res = await axios.get(`http://127.0.0.1:${port}/`); assert.strictEqual(res.status, 200); } finally { await stop(server); } }, 10000); it('should ignore polluted responseType', async () => { Object.prototype.responseType = 'arraybuffer'; const server = await startEcho(); const { port } = server.address(); try { const res = await axios.get(`http://127.0.0.1:${port}/`); // When responseType is not set on config, json parsing should apply // and res.data should be an object, not an ArrayBuffer/Buffer. assert.strictEqual(typeof res.data, 'object'); assert.ok(!Buffer.isBuffer(res.data)); } finally { await stop(server); } }, 10000); }); // utils.merge previously read `result[targetKey]` directly, which walks the // prototype chain. A polluted Object.prototype. object would surface as // the existing value and be merged into the result. describe('utils.merge prototype-chain read', () => { it('should not pick up polluted Object.prototype. as the existing value', () => { Object.prototype.headers = { evil: 'yes' }; const result = utils.merge({}, { headers: { 'Content-Type': 'application/json' } }); assert.strictEqual(result.headers.evil, undefined); assert.strictEqual(result.headers['Content-Type'], 'application/json'); }); it('should not absorb polluted nested objects when the key is absent from inputs', () => { // When the source does not carry `customNested`, the merged result should // not surface it either, even if Object.prototype carries it. Object.prototype.customNested = { evil: 'yes' }; const result = utils.merge({}, { safe: 'value' }); assert.strictEqual(result.hasOwnProperty('customNested'), false); assert.strictEqual(result.safe, 'value'); }); }); // Object.defineProperty calls a HasProperty check on `get`/`set` of the // descriptor. A polluted Object.prototype.get with a non-function value would // throw TypeError at every defineProperty site that uses a plain literal // descriptor. Each fixed site should be shielded with `__proto__: null`. describe('Object.defineProperty descriptor literals', () => { it('should construct AxiosError when Object.prototype.get is polluted', () => { Object.prototype.get = 'attacker'; const err = new AxiosError('hello', 'ECODE'); assert.strictEqual(err.message, 'hello'); assert.strictEqual(err.code, 'ECODE'); }); it('should construct AxiosHeaders accessor methods when Object.prototype.get is polluted', () => { Object.prototype.get = 'attacker'; // AxiosHeaders.accessor uses Object.defineProperty on the prototype. // Triggering a fresh accessor definition exercises the descriptor literal. AxiosHeaders.accessor('X-Pp-Test'); const h = new AxiosHeaders(); h.setXPpTest('value'); assert.strictEqual(h.getXPpTest(), 'value'); }); it('should not throw in mergeConfig when Object.prototype.get is polluted', () => { Object.prototype.get = 'attacker'; const result = mergeConfig({}, { url: '/x', method: 'get' }); assert.strictEqual(result.url, '/x'); assert.strictEqual(result.method, 'get'); assert.strictEqual(typeof result.hasOwnProperty, 'function'); }); it('should not throw in utils.extend when Object.prototype.get is polluted', () => { Object.prototype.get = 'attacker'; const a = {}; const b = { x: 1, fn() {} }; utils.extend(a, b); assert.strictEqual(a.x, 1); assert.strictEqual(typeof a.fn, 'function'); }); it('should not throw in utils.extend with thisArg when Object.prototype.get is polluted', () => { Object.prototype.get = 'attacker'; const a = {}; const ctx = { tag: 'ctx' }; const b = { method() { return this.tag; }, }; utils.extend(a, b, ctx); assert.strictEqual(a.method(), 'ctx'); }); it('should not throw in utils.inherits when Object.prototype.get is polluted', () => { Object.prototype.get = 'attacker'; function Parent() {} function Child() {} utils.inherits(Child, Parent); assert.strictEqual(Child.prototype.constructor, Child); assert.strictEqual(Child.super, Parent.prototype); }); it('should also be shielded against a polluted Object.prototype.set', () => { Object.prototype.set = 'attacker'; // Same surface as `get` — ToPropertyDescriptor checks both. One spot-check // covers them all since they share the same fix. const err = new AxiosError('hello'); assert.strictEqual(err.message, 'hello'); }); }); // End-to-end regressions covering published advisory PoCs against full axios // request flow. Each test mirrors the exploit scenario from the advisory and // asserts the attack does not succeed. describe('advisory regression — full request flow', () => { function startServer(handler) { return new Promise((resolve) => { const server = http.createServer(handler); server.listen(0, '127.0.0.1', () => resolve(server)); }); } const stop = (s) => new Promise((r) => s.close(r)); // Full MITM via prototype pollution gadget in // `config.proxy`. mergeConfig must not surface a polluted Object.prototype.proxy // as the merged config's proxy, otherwise every request would route through // an attacker-controlled host. it('polluted Object.prototype.proxy must not redirect requests through an attacker proxy', async () => { const proxyHits = []; const attackerProxy = await startServer((req, res) => { proxyHits.push({ url: req.url, authorization: req.headers.authorization, host: req.headers.host, }); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end('{"hijacked":true}'); }); const realHits = []; const realServer = await startServer((req, res) => { realHits.push({ url: req.url }); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end('{"data":"real"}'); }); try { Object.prototype.proxy = { protocol: 'http', host: '127.0.0.1', port: attackerProxy.address().port, }; const realPort = realServer.address().port; const res = await axios.get(`http://127.0.0.1:${realPort}/api/secrets`, { auth: { username: 'admin', password: 'SuperSecret123!' }, }); assert.strictEqual(proxyHits.length, 0, 'attacker proxy must not receive any request'); assert.strictEqual(realHits.length, 1, 'request must reach the real target'); assert.deepStrictEqual(res.data, { data: 'real' }); } finally { await stop(attackerProxy); await stop(realServer); } }, 10000); // Credential theft and response hijacking via // prototype pollution gadget in config merge. A polluted // Object.prototype.transformResponse function would otherwise execute with // `this = config`, exposing `auth.username`/`auth.password` to the attacker. it('polluted Object.prototype.transformResponse must not be invoked or leak request credentials', async () => { let invoked = false; let stolen = null; Object.prototype.transformResponse = function pollutedTransform(data) { invoked = true; stolen = { url: this && this.url, username: this && this.auth && this.auth.username, password: this && this.auth && this.auth.password, data, }; return true; }; const server = await startServer((req, res) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end('{"secret":"keep-me"}'); }); try { const { port } = server.address(); const res = await axios.get(`http://127.0.0.1:${port}/users`, { auth: { username: 'svc-account', password: 'prod-secret-key-123!' }, }); assert.strictEqual(invoked, false, 'polluted transformResponse must not run'); assert.strictEqual(stolen, null, 'no request context must be captured'); assert.deepStrictEqual( res.data, { secret: 'keep-me' }, 'response data must reach the caller untampered' ); } finally { await stop(server); } }, 10000); }); }); axios-axios-aa54a5e/tests/unit/query.test.js000066400000000000000000000174131526660636600212750ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import axios from '../../index.js'; import { startHTTPServer, stopHTTPServer } from '../setup/server.js'; describe('QUERY method', () => { describe('static axios.query()', () => { it('should make a request with the QUERY HTTP method', async () => { const response = await axios.query('/test', null, { adapter: (config) => { assert.strictEqual(config.method, 'query'); return Promise.resolve({ data: null, status: 200, statusText: 'OK', headers: {}, config, request: {}, }); }, }); assert.strictEqual(response.status, 200); }); it('should support a request body', async () => { const requestBody = { selector: 'field1, field2', filter: { active: true } }; await axios.query('/search', requestBody, { adapter: (config) => { assert.deepStrictEqual(config.data, JSON.stringify(requestBody)); return Promise.resolve({ data: null, status: 200, statusText: 'OK', headers: {}, config, request: {}, }); }, }); }); it('should support custom headers', async () => { await axios.query('/test', null, { headers: { 'X-Custom-Header': 'custom-value', Authorization: 'Bearer token-abc', }, adapter: (config) => { assert.strictEqual(config.headers.get('X-Custom-Header'), 'custom-value'); assert.strictEqual(config.headers.get('Authorization'), 'Bearer token-abc'); return Promise.resolve({ data: null, status: 200, statusText: 'OK', headers: {}, config, request: {}, }); }, }); }); it('should work with baseURL configuration', async () => { const instance = axios.create({ baseURL: 'http://example.com/api' }); await instance.query('/resources', { fields: ['name'] }, { adapter: (config) => { assert.strictEqual(config.baseURL, 'http://example.com/api'); assert.strictEqual(config.url, '/resources'); assert.strictEqual(config.method, 'query'); return Promise.resolve({ data: null, status: 200, statusText: 'OK', headers: {}, config, request: {}, }); }, }); }); it('should set Content-Type to application/json for object bodies', async () => { await axios.query('/test', { key: 'value' }, { adapter: (config) => { assert.ok( config.headers.get('Content-Type').includes('application/json'), 'Expected Content-Type to include application/json' ); return Promise.resolve({ data: null, status: 200, statusText: 'OK', headers: {}, config, request: {}, }); }, }); }); }); describe('instance.query()', () => { it('should make a request with the QUERY HTTP method on an instance', async () => { const instance = axios.create(); const response = await instance.query('/test', null, { adapter: (config) => { assert.strictEqual(config.method, 'query'); return Promise.resolve({ data: null, status: 200, statusText: 'OK', headers: {}, config, request: {}, }); }, }); assert.strictEqual(response.status, 200); }); it('should merge instance defaults with request config', async () => { const instance = axios.create({ headers: { 'X-Instance-Header': 'from-instance' }, }); await instance.query('/test', null, { headers: { 'X-Request-Header': 'from-request' }, adapter: (config) => { assert.strictEqual(config.headers.get('X-Instance-Header'), 'from-instance'); assert.strictEqual(config.headers.get('X-Request-Header'), 'from-request'); return Promise.resolve({ data: null, status: 200, statusText: 'OK', headers: {}, config, request: {}, }); }, }); }); }); describe('axios({ method: "query" })', () => { it('should support the generic request form', async () => { const response = await axios({ method: 'query', url: '/test', data: { selector: '*' }, adapter: (config) => { assert.strictEqual(config.method, 'query'); assert.deepStrictEqual(config.data, JSON.stringify({ selector: '*' })); return Promise.resolve({ data: { result: 'ok' }, status: 200, statusText: 'OK', headers: {}, config, request: {}, }); }, }); assert.deepStrictEqual(response.data, { result: 'ok' }); }); }); describe('with HTTP server', () => { it('should send QUERY requests with a body to a real server', async () => { const server = await startHTTPServer( (req, res) => { let body = ''; req.on('data', (chunk) => { body += chunk; }); req.on('end', () => { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ method: req.method, url: req.url, body, headers: req.headers, })); }); }, { port: 0 } ); try { const { data } = await axios.query( `http://localhost:${server.address().port}/search`, { selector: 'field1' } ); assert.strictEqual(data.method, 'QUERY'); assert.strictEqual(data.url, '/search'); const parsedBody = JSON.parse(data.body); assert.deepStrictEqual(parsedBody, { selector: 'field1' }); assert.ok( data.headers['content-type'].includes('application/json'), 'Expected server to receive application/json content-type' ); } finally { await stopHTTPServer(server); } }); it('should send QUERY requests with custom headers to a real server', async () => { const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ method: req.method, headers: req.headers, })); }, { port: 0 } ); try { const { data } = await axios.query( `http://localhost:${server.address().port}/test`, null, { headers: { 'X-Custom': 'test-value', }, } ); assert.strictEqual(data.method, 'QUERY'); assert.strictEqual(data.headers['x-custom'], 'test-value'); } finally { await stopHTTPServer(server); } }); it('should send QUERY requests with baseURL to a real server', async () => { const server = await startHTTPServer( (req, res) => { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ method: req.method, url: req.url, })); }, { port: 0 } ); try { const instance = axios.create({ baseURL: `http://localhost:${server.address().port}/api`, }); const { data } = await instance.query('/resources', { fields: ['name'] }); assert.strictEqual(data.method, 'QUERY'); assert.strictEqual(data.url, '/api/resources'); } finally { await stopHTTPServer(server); } }); }); }); axios-axios-aa54a5e/tests/unit/regression.test.js000066400000000000000000000165621526660636600223140ustar00rootroot00000000000000/** * Combined regression tests (issues 4999, 5028, 7364 + SSRF SNYK-1038255, SNYK-7361793). */ import { describe, it, beforeEach, afterEach, vi } from 'vitest'; import assert from 'assert'; import http from 'http'; import axios from '../../index.js'; import platform from '../../lib/platform/index.js'; describe('regression', () => { describe('issues', () => { describe('4999', () => { // Depends on network: https://postman-echo.com it('should not fail with query parsing', async () => { const { data } = await axios.get('https://postman-echo.com/get?foo1=bar1&foo2=bar2'); assert.strictEqual(data.args.foo1, 'bar1'); assert.strictEqual(data.args.foo2, 'bar2'); }); }); describe('5028', () => { it('should handle set-cookie headers as an array', async () => { const cookie1 = 'something=else; path=/; expires=Wed, 12 Apr 2023 12:03:42 GMT; samesite=lax; secure; httponly'; const cookie2 = 'something-ssr.sig=n4MlwVAaxQAxhbdJO5XbUpDw-lA; path=/; expires=Wed, 12 Apr 2023 12:03:42 GMT; samesite=lax; secure; httponly'; const server = http .createServer((req, res) => { res.setHeader('Set-Cookie', [cookie1, cookie2]); res.writeHead(200); res.write('Hi there'); res.end(); }) .listen(0); const request = axios.create(); request.interceptors.response.use((res) => { assert.deepStrictEqual(res.headers['set-cookie'], [cookie1, cookie2]); }); try { await request({ url: `http://localhost:${server.address().port}` }); } finally { server.close(); } }); }); describe('7364', () => { it('fetch: should have status code in axios error', async () => { const isFetchSupported = typeof fetch === 'function'; if (!isFetchSupported) { vi.skip(); } const server = http .createServer((req, res) => { res.statusCode = 400; res.end(); }) .listen(0); const instance = axios.create({ baseURL: `http://localhost:${server.address().port}`, adapter: 'fetch', }); try { await instance.get('/status/400'); } catch (error) { assert.equal(error.name, 'AxiosError'); assert.equal(error.isAxiosError, true); assert.equal(error.status, 400); } finally { server.close(); } }); it('http: should have status code in axios error', async () => { const server = http .createServer((req, res) => { res.statusCode = 400; res.end(); }) .listen(0); const instance = axios.create({ baseURL: `http://localhost:${server.address().port}`, adapter: 'http', }); try { await instance.get('/status/400'); } catch (error) { assert.equal(error.name, 'AxiosError'); assert.equal(error.isAxiosError, true); assert.equal(error.status, 400); } finally { server.close(); } }); }); }); // https://snyk.io/vuln/SNYK-JS-AXIOS-1038255 // https://github.com/axios/axios/issues/3407 // https://github.com/axios/axios/issues/3369 describe('SSRF SNYK-JS-AXIOS-1038255', () => { let fail = false; let proxy; let server; let location; let evilPort; let proxyPort; beforeEach(() => { fail = false; server = http .createServer((req, res) => { fail = true; res.end('rm -rf /'); }) .listen(0); evilPort = server.address().port; proxy = http .createServer((req, res) => { if ( new URL(req.url, 'http://' + req.headers.host).toString() === 'http://localhost:' + evilPort + '/' ) { return res.end( JSON.stringify({ msg: 'Protected', headers: req.headers, }) ); } res.writeHead(302, { location }); res.end(); }) .listen(0); proxyPort = proxy.address().port; location = 'http://localhost:' + evilPort; }); afterEach(() => { server.close(); proxy.close(); }); it('obeys proxy settings when following redirects', async () => { const response = await axios({ method: 'get', url: 'http://www.google.com/', proxy: { host: 'localhost', port: proxyPort, auth: { username: 'sam', password: 'password', }, }, }); assert.strictEqual(fail, false); assert.strictEqual(response.data.msg, 'Protected'); assert.strictEqual(response.data.headers.host, 'localhost:' + evilPort); assert.strictEqual( response.data.headers['proxy-authorization'], 'Basic ' + Buffer.from('sam:password').toString('base64') ); return response; }); }); // https://security.snyk.io/vuln/SNYK-JS-AXIOS-7361793 // https://github.com/axios/axios/issues/6463 describe('SSRF SNYK-JS-AXIOS-7361793', () => { let goodServer; let badServer; let goodPort; let badPort; beforeEach(() => { goodServer = http .createServer((req, res) => { res.write('good'); res.end(); }) .listen(0); goodPort = goodServer.address().port; badServer = http .createServer((req, res) => { res.write('bad'); res.end(); }) .listen(0); badPort = badServer.address().port; }); afterEach(() => { goodServer.close(); badServer.close(); }); it('should not fetch in server-side mode', async () => { const ssrfAxios = axios.create({ baseURL: 'http://localhost:' + String(goodPort), }); const userId = '/localhost:' + String(badPort); try { await ssrfAxios.get(`/${userId}`); } catch (error) { assert.ok(error.message.startsWith('Invalid URL')); return; } assert.fail('Expected an error to be thrown'); }); describe('client-side mode', () => { let savedHasBrowserEnv; let savedOrigin; beforeEach(() => { assert.ok(platform.hasBrowserEnv !== undefined); savedHasBrowserEnv = platform.hasBrowserEnv; savedOrigin = platform.origin; platform.hasBrowserEnv = true; platform.origin = 'http://localhost:' + String(goodPort); }); afterEach(() => { platform.hasBrowserEnv = savedHasBrowserEnv; platform.origin = savedOrigin; }); it('resolves URL relative to origin and returns bad server body', async () => { const ssrfAxios = axios.create({ baseURL: 'http://localhost:' + String(goodPort), }); const userId = '/localhost:' + String(badPort); const response = await ssrfAxios.get(`/${userId}`); assert.strictEqual(response.data, 'bad'); assert.strictEqual(response.config.baseURL, 'http://localhost:' + String(goodPort)); assert.strictEqual(response.config.url, '//localhost:' + String(badPort)); assert.strictEqual( response.request.res.responseUrl, 'http://localhost:' + String(badPort) + '/' ); }); }); }); }); axios-axios-aa54a5e/tests/unit/runModuleInChildProcess.test.js000066400000000000000000000052671526660636600247000ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import { runModuleInChildProcess } from '../setup/runModuleInChildProcess.js'; const outputSource = [ "import fs from 'fs';", "fs.writeSync(1, 'module output\\n');", "fs.writeSync(2, 'module diagnostic\\n');", ].join('\n'); describe('runModuleInChildProcess', function () { it('continues to resolve with stdout alone on success', async function () { assert.strictEqual(await runModuleInChildProcess(outputSource), 'module output\n'); }); it('retains both output streams and exit metadata after a nonzero exit', async function () { await assert.rejects( runModuleInChildProcess(outputSource + '\nprocess.exitCode = 7;'), function (error) { assert.ok(error instanceof Error); assert.strictEqual(error.code, 7); assert.strictEqual(error.signal, null); assert.strictEqual(error.killed, false); assert.ok(error.cmd.includes('--input-type=module')); assert.strictEqual(error.stdout, 'module output\n'); assert.strictEqual(error.stderr, 'module diagnostic\n'); return true; } ); }); it('retains partial output and the thrown module error', async function () { await assert.rejects( runModuleInChildProcess(outputSource + "\nthrow new Error('module stopped');"), function (error) { assert.strictEqual(error.code, 1); assert.strictEqual(error.stdout, 'module output\n'); assert.ok(error.stderr.includes('module diagnostic\n')); assert.ok(error.stderr.includes('Error: module stopped')); return true; } ); }); it('preserves buffer output when requested', async function () { await assert.rejects( runModuleInChildProcess(outputSource + '\nprocess.exitCode = 7;', { encoding: 'buffer' }), function (error) { assert.strictEqual(error.code, 7); assert.ok(Buffer.isBuffer(error.stdout)); assert.ok(Buffer.isBuffer(error.stderr)); assert.strictEqual(error.stdout.toString(), 'module output\n'); assert.strictEqual(error.stderr.toString(), 'module diagnostic\n'); return true; } ); }); it('retains partial output and termination metadata on timeout', async function () { await assert.rejects( runModuleInChildProcess(outputSource + '\nsetInterval(function () {}, 1000);'), function (error) { assert.strictEqual(error.code, null); assert.strictEqual(error.signal, 'SIGKILL'); assert.strictEqual(error.killed, true); assert.strictEqual(error.stdout, 'module output\n'); assert.strictEqual(error.stderr, 'module diagnostic\n'); return true; } ); }); }); axios-axios-aa54a5e/tests/unit/syntaxCompat.test.js000066400000000000000000000016121526660636600226140ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import { readFileSync, readdirSync, statSync } from 'node:fs'; import { join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { Parser } from 'acorn'; const LIB_DIR = fileURLToPath(new URL('../../lib/', import.meta.url)); const ECMA_VERSION = 2018; function walk(dir) { const out = []; for (const name of readdirSync(dir)) { const full = join(dir, name); if (statSync(full).isDirectory()) out.push(...walk(full)); else if (full.endsWith('.js')) out.push(full); } return out; } describe('lib/ source files parse as ES2018', () => { for (const file of walk(LIB_DIR)) { const rel = file.slice(LIB_DIR.length); it(rel, () => { const src = readFileSync(file, 'utf8'); expect(() => Parser.parse(src, { ecmaVersion: ECMA_VERSION, sourceType: 'module' }) ).not.toThrow(); }); } }); axios-axios-aa54a5e/tests/unit/toFormData.test.js000066400000000000000000000263201526660636600221650ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import FormData from 'form-data'; import toFormData from '../../lib/helpers/toFormData.js'; import AxiosError from '../../lib/core/AxiosError.js'; import AxiosURLSearchParams from '../../lib/helpers/AxiosURLSearchParams.js'; describe('helpers::toFormData', () => { const createRNFormDataSpy = () => { const calls = []; return { calls, append: (key, value) => { calls.push([key, value]); }, getParts: () => { return []; }, }; }; it('should ignore a visitor inherited only from Object.prototype', () => { let called = false; Object.prototype.visitor = () => { called = true; return false; }; try { const formData = createRNFormDataSpy(); toFormData({ field: 'value' }, formData); assert.strictEqual(called, false); assert.deepStrictEqual(formData.calls, [['field', 'value']]); } finally { delete Object.prototype.visitor; } }); it('should ignore maxDepth inherited only from Object.prototype', () => { Object.prototype.maxDepth = 0; try { const formData = createRNFormDataSpy(); assert.doesNotThrow(() => toFormData({ nested: { field: 'value' } }, formData)); assert.deepStrictEqual(formData.calls, [['nested[field]', 'value']]); } finally { delete Object.prototype.maxDepth; } }); it('should convert a flat object to FormData', () => { const data = { foo: 'bar', baz: 123, }; const formData = toFormData(data, new FormData()); assert.ok(formData instanceof FormData); assert.ok(formData._streams.length > 0); }); it('should convert a nested object to FormData', () => { const data = { foo: { bar: 'baz', }, }; const formData = toFormData(data, new FormData()); assert.ok(formData instanceof FormData); }); it('should throw Error on circular reference', () => { const data = { foo: 'bar', }; data.self = data; try { toFormData(data, new FormData()); assert.fail('Should have thrown an error'); } catch (err) { assert.strictEqual(err.message, 'Circular reference detected in self'); } }); it('should handle arrays', () => { const data = { arr: [1, 2, 3], }; const formData = toFormData(data, new FormData()); assert.ok(formData instanceof FormData); }); it('should use custom Blob constructor for typed array values', () => { class CustomBlob { constructor(parts) { this.parts = parts; } } const formData = { calls: [], append(key, value) { this.calls.push([key, value]); }, get [Symbol.toStringTag]() { return 'FormData'; }, *[Symbol.iterator]() {}, }; const value = new Uint8Array([1, 2, 3]); toFormData({ file: value }, formData, { Blob: CustomBlob }); assert.strictEqual(formData.calls.length, 1); assert.strictEqual(formData.calls[0][0], 'file'); assert.ok(formData.calls[0][1] instanceof CustomBlob); assert.deepStrictEqual(formData.calls[0][1].parts, [value]); }); it('should convert typed array values to Buffer for non-spec Node FormData', () => { const formData = createRNFormDataSpy(); toFormData({ file: new Uint8Array([1, 2, 3]) }, formData); assert.strictEqual(formData.calls.length, 1); assert.strictEqual(formData.calls[0][0], 'file'); assert.ok(Buffer.isBuffer(formData.calls[0][1])); assert.deepStrictEqual([...formData.calls[0][1]], [1, 2, 3]); }); it('should throw AxiosError when typed array values require Buffer and Buffer is unavailable', () => { const originalBuffer = globalThis.Buffer; const formData = createRNFormDataSpy(); try { globalThis.Buffer = undefined; assert.throws( () => toFormData({ file: new Uint8Array([1]) }, formData), (err) => { assert.ok(err instanceof AxiosError); assert.strictEqual(err.code, AxiosError.ERR_NOT_SUPPORT); return true; } ); } finally { globalThis.Buffer = originalBuffer; } }); it('should append root-level React Native blob without recursion', () => { const formData = createRNFormDataSpy(); const blob = { uri: 'file://test.png', type: 'image/png', name: 'test.png', }; toFormData({ file: blob }, formData); assert.strictEqual(formData.calls.length, 1); assert.strictEqual(formData.calls[0][0], 'file'); assert.strictEqual(formData.calls[0][1], blob); }); it('should append nested React Native blob without recursion', () => { const formData = createRNFormDataSpy(); const blob = { uri: 'file://nested.png', type: 'image/png', name: 'nested.png', }; toFormData({ nested: { file: blob } }, formData); assert.strictEqual(formData.calls.length, 1); assert.strictEqual(formData.calls[0][0], 'nested[file]'); assert.strictEqual(formData.calls[0][1], blob); }); it('should append deeply nested React Native blob without recursion', () => { const formData = createRNFormDataSpy(); const blob = { uri: 'file://deep.png', name: 'deep.png', }; toFormData({ a: { b: { c: blob } } }, formData); assert.strictEqual(formData.calls.length, 1); assert.strictEqual(formData.calls[0][0], 'a[b][c]'); assert.strictEqual(formData.calls[0][1], blob); }); // --- Depth limit tests --- function nest(depth) { let o = { leaf: 1 }; for (let i = 0; i < depth; i++) o = { a: o }; return o; } describe('maxDepth option', () => { it('should throw AxiosError when payload exceeds default depth limit (100)', () => { try { toFormData(nest(101), new FormData()); assert.fail('Should have thrown'); } catch (err) { assert.ok(err instanceof AxiosError, 'error must be AxiosError, not RangeError'); assert.strictEqual(err.code, 'ERR_FORM_DATA_DEPTH_EXCEEDED'); assert.ok(!(err instanceof RangeError)); } }); it('should succeed when payload is exactly at the default depth limit (100)', () => { const formData = toFormData(nest(100), new FormData()); assert.ok(formData instanceof FormData); }); it('should succeed for a shallow payload (no regression)', () => { const formData = toFormData(nest(5), new FormData()); assert.ok(formData instanceof FormData); }); it('should allow deeper payloads when maxDepth is raised', () => { const formData = toFormData(nest(150), new FormData(), { maxDepth: 200 }); assert.ok(formData instanceof FormData); }); it('should reject shallower payloads when maxDepth is lowered', () => { try { toFormData(nest(10), new FormData(), { maxDepth: 5 }); assert.fail('Should have thrown'); } catch (err) { assert.ok(err instanceof AxiosError); assert.strictEqual(err.code, 'ERR_FORM_DATA_DEPTH_EXCEEDED'); } }); it('should not throw for depth guard when maxDepth is Infinity (guard disabled)', () => { // Use 500 levels — deep enough to prove the guard is off, shallow enough not to overflow V8 const formData = toFormData(nest(500), new FormData(), { maxDepth: Infinity }); assert.ok(formData instanceof FormData); }); it('should still detect circular references when depth guard is active', () => { const data = { foo: 'bar' }; data.self = data; try { toFormData(data, new FormData()); assert.fail('Should have thrown'); } catch (err) { assert.ok( err.message.includes('Circular reference detected'), 'must be circular-ref error' ); assert.ok(!(err instanceof AxiosError) || err.code !== 'ERR_FORM_DATA_DEPTH_EXCEEDED'); } }); it('depth limit error is catchable as AxiosError with correct code', () => { let caught; try { toFormData(nest(101), new FormData()); } catch (err) { caught = err; } assert.ok(caught instanceof AxiosError); assert.strictEqual(caught.code, 'ERR_FORM_DATA_DEPTH_EXCEEDED'); assert.ok(!(caught instanceof RangeError)); }); it('should reject deeply nested {} metatoken values before JSON.stringify overflows', () => { try { toFormData({ 'evil{}': nest(10000) }, new FormData()); assert.fail('Should have thrown'); } catch (err) { assert.ok(err instanceof AxiosError, 'error must be AxiosError, not RangeError'); assert.strictEqual(err.code, 'ERR_FORM_DATA_DEPTH_EXCEEDED'); assert.ok(!(err instanceof RangeError)); } }); it('should allow {} metatoken values at the same boundary as normal top-level properties', () => { const formData = toFormData({ 'safe{}': nest(99) }, new FormData()); assert.ok(formData instanceof FormData); }); it('should reject {} metatoken values beyond the normal top-level property boundary', () => { try { toFormData({ 'evil{}': nest(100) }, new FormData()); assert.fail('Should have thrown'); } catch (err) { assert.ok(err instanceof AxiosError); assert.strictEqual(err.code, 'ERR_FORM_DATA_DEPTH_EXCEEDED'); } }); }); describe('maxDepth — params serialization via AxiosURLSearchParams', () => { it('should throw AxiosError for deeply nested params object (default limit)', () => { try { new AxiosURLSearchParams(nest(101)); assert.fail('Should have thrown'); } catch (err) { assert.ok(err instanceof AxiosError); assert.strictEqual(err.code, 'ERR_FORM_DATA_DEPTH_EXCEEDED'); } }); it('should build query string for deep params when maxDepth is raised', () => { const params = new AxiosURLSearchParams(nest(150), { maxDepth: 200 }); const qs = params.toString(); assert.ok(typeof qs === 'string' && qs.length > 0); }); it('should reject deeply nested {} metatoken params before JSON.stringify overflows', () => { try { new AxiosURLSearchParams({ 'evil{}': nest(10000) }); assert.fail('Should have thrown'); } catch (err) { assert.ok(err instanceof AxiosError, 'error must be AxiosError, not RangeError'); assert.strictEqual(err.code, 'ERR_FORM_DATA_DEPTH_EXCEEDED'); assert.ok(!(err instanceof RangeError)); } }); it('should reject {} metatoken params beyond the normal property boundary', () => { try { new AxiosURLSearchParams({ 'evil{}': nest(100) }); assert.fail('Should have thrown'); } catch (err) { assert.ok(err instanceof AxiosError); assert.strictEqual(err.code, 'ERR_FORM_DATA_DEPTH_EXCEEDED'); } }); }); it('should NOT recurse into React Native blob properties', () => { const formData = createRNFormDataSpy(); const blob = { uri: 'file://nope.png', type: 'image/png', name: 'nope.png', }; toFormData({ file: blob }, formData); const keys = formData.calls.map((call) => call[0]); assert.deepStrictEqual(keys, ['file']); assert.ok(!keys.some((key) => key.includes('uri'))); assert.ok(!keys.some((key) => key.includes('type'))); assert.ok(!keys.some((key) => key.includes('name'))); }); }); axios-axios-aa54a5e/tests/unit/transformResponse.test.js000066400000000000000000000065461526660636600236670ustar00rootroot00000000000000import { describe, it } from 'vitest'; import defaults from '../../lib/defaults/index.js'; import transformData from '../../lib/core/transformData.js'; import AxiosError from '../../lib/core/AxiosError.js'; import assert from 'assert'; describe('transformResponse', () => { describe('200 request', () => { it('parses json', () => { const data = '{"message": "hello, world"}'; const result = transformData.call( { data, response: { headers: { 'content-type': 'application/json' }, status: 200, }, }, defaults.transformResponse ); assert.strictEqual(result.message, 'hello, world'); }); it('ignores XML', () => { const data = 'hello, world'; const result = transformData.call( { data, response: { headers: { 'content-type': 'text/xml' }, status: 200, }, }, defaults.transformResponse ); assert.strictEqual(result, data); }); }); describe('malformed JSON with responseType: json', () => { it('throws AxiosError with ERR_BAD_RESPONSE code', () => { const response = { status: 200, headers: {}, data: '{bad json' }; const config = { responseType: 'json', transitional: { silentJSONParsing: false, forcedJSONParsing: true }, response, }; assert.throws( () => transformData.call(config, defaults.transformResponse, response), (e) => e instanceof AxiosError && e.code === AxiosError.ERR_BAD_RESPONSE ); }); it('attaches response to AxiosError so error.status and error.response are available', () => { // Regression test for https://github.com/axios/axios/issues/7224 // When JSON.parse fails in strict mode, the thrown AxiosError must carry // the original response so callers can inspect error.status and // error.response without having to re-examine the raw response. const response = { status: 200, headers: {}, data: '{bad json' }; const config = { responseType: 'json', transitional: { silentJSONParsing: false, forcedJSONParsing: true }, response, }; let thrown; try { transformData.call(config, defaults.transformResponse, response); } catch (e) { thrown = e; } assert.ok(thrown instanceof AxiosError, 'must be AxiosError'); assert.strictEqual(thrown.status, 200, 'error.status must equal response status'); assert.strictEqual(thrown.response, response, 'error.response must be the original response'); }); }); describe('204 request', () => { it('does not parse the empty string', () => { const data = ''; const result = transformData.call( { data, response: { headers: { 'content-type': undefined }, status: 204, }, }, defaults.transformResponse ); assert.strictEqual(result, ''); }); it('does not parse undefined', () => { const data = undefined; const result = transformData.call( { data, response: { headers: { 'content-type': undefined }, status: 200, }, }, defaults.transformResponse ); assert.strictEqual(result, data); }); }); }); axios-axios-aa54a5e/tests/unit/utils.test.js000066400000000000000000000231371526660636600212700ustar00rootroot00000000000000import { describe, it } from 'vitest'; import assert from 'assert'; import utils from '../../lib/utils.js'; import FormData from 'form-data'; import stream from 'stream'; import vm from 'node:vm'; describe('utils', () => { it('should validate Stream', () => { assert.strictEqual(utils.isStream(new stream.Readable()), true); assert.strictEqual(utils.isStream({ foo: 'bar' }), false); }); it('should validate Buffer', () => { assert.strictEqual(utils.isBuffer(Buffer.from('a')), true); assert.strictEqual(utils.isBuffer(null), false); assert.strictEqual(utils.isBuffer(undefined), false); }); describe('utils::isFormData', () => { it('should detect the FormData instance provided by the `form-data` package', () => { [1, 'str', {}, new RegExp()].forEach((thing) => { assert.equal(utils.isFormData(thing), false); }); assert.equal(utils.isFormData(new FormData()), true); }); it('should not call toString method on built-in objects instances', () => { const buf = Buffer.from('123'); buf.toString = () => assert.fail('should not be called'); assert.equal(utils.isFormData(buf), false); }); it('should not call toString method on built-in objects instances, even if append method exists', () => { const buf = Buffer.from('123'); buf.append = () => {}; buf.toString = () => assert.fail('should not be called'); assert.equal(utils.isFormData(buf), false); }); it('should detect custom FormData instances by toStringTag signature and append method presence', () => { class FormData { append() {} get [Symbol.toStringTag]() { return 'FormData'; } } assert.equal(utils.isFormData(new FormData()), true); }); }); describe('toJSON', () => { it('should convert to a plain object without circular references', () => { const obj = { a: [0] }; const source = { x: 1, y: 2, obj }; source.circular1 = source; obj.a[1] = obj; assert.deepStrictEqual(utils.toJSONObject(source), { x: 1, y: 2, obj: { a: [0] }, }); }); it('should use objects with defined toJSON method without rebuilding', () => { const objProp = {}; const obj = { objProp, toJSON() { return { ok: 1 }; }, }; const source = { x: 1, y: 2, obj }; const jsonObject = utils.toJSONObject(source); assert.strictEqual(jsonObject.obj.objProp, objProp); assert.strictEqual( JSON.stringify(jsonObject), JSON.stringify({ x: 1, y: 2, obj: { ok: 1 } }) ); }); describe('cycle / DAG handling', () => { it('should serialize a shared sibling object at every occurrence (DAG, not cycle)', () => { const shared = { val: 42 }; const source = { x: shared, y: shared }; const result = utils.toJSONObject(source); // Both branches must serialize — shared reference is not a cycle assert.deepStrictEqual(result, { x: { val: 42 }, y: { val: 42 } }); }); it('should serialize a shared sibling array at every occurrence (DAG, not cycle)', () => { const shared = [1, 2, 3]; const source = { a: shared, b: shared }; const result = utils.toJSONObject(source); assert.deepStrictEqual(result, { a: [1, 2, 3], b: [1, 2, 3] }); }); it('should serialize shared sibling that itself contains a self-cycle', () => { const shared = { v: 1 }; shared.self = shared; // self-cycle inside the shared node const source = { x: shared, y: shared }; const result = utils.toJSONObject(source); // The self-cycle is stripped, but both x and y must be serialized assert.deepStrictEqual(result, { x: { v: 1 }, y: { v: 1 } }); }); it('should serialize non-cyclic structures deeper than the old Array(10) cap', () => { // The previous implementation used a fixed-size Array(10) for path tracking. // A non-cyclic chain deeper than 10 levels must serialise end-to-end. let leaf = { v: 'leaf' }; let source = leaf; for (let i = 0; i < 25; i++) { source = { next: source }; } const result = utils.toJSONObject(source); let cursor = result; for (let i = 0; i < 25; i++) { cursor = cursor.next; } assert.deepStrictEqual(cursor, { v: 'leaf' }); }); }); // https://github.com/axios/axios/issues/5910 describe('Set handling', () => { it('should convert a flat Set to an array', () => { const result = utils.toJSONObject(new Set([1, 2, 3])); assert.deepStrictEqual(result, [1, 2, 3]); }); it('should convert a Set nested inside a plain object', () => { const result = utils.toJSONObject({ tags: new Set(['a', 'b', 'c']) }); assert.deepStrictEqual(result, { tags: ['a', 'b', 'c'] }); }); it('should convert a cross-realm Set to an array', () => { const set = vm.runInNewContext('new Set(["a", "b", "c"])'); assert.strictEqual(set instanceof Set, false); assert.deepStrictEqual(utils.toJSONObject({ tags: set }), { tags: ['a', 'b', 'c'] }); }); it('should convert nested Sets recursively', () => { const inner = new Set(['x', 'y']); const outer = new Set([inner, 1]); assert.deepStrictEqual(utils.toJSONObject(outer), [['x', 'y'], 1]); }); it('should skip undefined values inside a Set (mirrors object/array behaviour)', () => { const result = utils.toJSONObject(new Set([1, undefined, 3])); assert.deepStrictEqual(result, [1, 3]); }); it('should handle a Set that appears in multiple places (DAG, not cycle)', () => { const shared = new Set([42]); const source = { a: shared, b: shared }; assert.deepStrictEqual(utils.toJSONObject(source), { a: [42], b: [42] }); }); it('should not follow a Set that directly contains itself (self-cycle)', () => { const s = new Set(); s.add(s); // self-referential s.add(1); // The cyclic self-reference is skipped; other values are kept. const result = utils.toJSONObject(s); assert.deepStrictEqual(result, [1]); }); it('should reproduce the original issue #5910 test case', () => { const obj = { a: [0] }; const source = { x: 1, y: 2, obj }; source.circular1 = source; obj.a[1] = obj; obj.b = new Set(['a', 'b']); assert.deepStrictEqual(utils.toJSONObject(source), { x: 1, y: 2, obj: { a: [0], b: ['a', 'b'] }, }); }); }); }); describe('Buffer RangeError Fix', () => { it('should handle large Buffer in isEmptyObject without RangeError', () => { const largeBuffer = Buffer.alloc(1024 * 1024 * 200); const result = utils.isEmptyObject(largeBuffer); assert.strictEqual(result, false); }); it('should handle large Buffer in forEach without RangeError', () => { const largeBuffer = Buffer.alloc(1024 * 1024 * 200); let count = 0; utils.forEach(largeBuffer, () => count++); assert.strictEqual(count, 0); }); it('should handle large Buffer in findKey without RangeError', () => { const largeBuffer = Buffer.alloc(1024 * 1024 * 200); const result = utils.findKey(largeBuffer, 'test'); assert.strictEqual(result, null); }); }); describe('utils::isReactNativeBlob', () => { it('should return true for objects with uri property', () => { assert.strictEqual(utils.isReactNativeBlob({ uri: 'file://path/to/file' }), true); assert.strictEqual(utils.isReactNativeBlob({ uri: 'content://media/image' }), true); }); it('should return true for React Native blob-like objects with optional name and type', () => { assert.strictEqual( utils.isReactNativeBlob({ uri: 'file://path/to/file', name: 'image.png', type: 'image/png', }), true ); }); it('should return false for objects without uri property', () => { assert.strictEqual(utils.isReactNativeBlob({ path: 'file://path' }), false); assert.strictEqual(utils.isReactNativeBlob({ url: 'http://example.com' }), false); assert.strictEqual(utils.isReactNativeBlob({}), false); }); it('should return false for non-objects', () => { assert.strictEqual(utils.isReactNativeBlob(null), false); assert.strictEqual(utils.isReactNativeBlob(undefined), false); assert.strictEqual(utils.isReactNativeBlob('string'), false); assert.strictEqual(utils.isReactNativeBlob(123), false); assert.strictEqual(utils.isReactNativeBlob(false), false); }); it('should return true even if uri is empty string', () => { assert.strictEqual(utils.isReactNativeBlob({ uri: '' }), true); }); }); describe('utils::isReactNative', () => { it('should return true for FormData with getParts method', () => { const mockReactNativeFormData = { append: () => {}, getParts: () => { return []; }, }; assert.strictEqual(utils.isReactNative(mockReactNativeFormData), true); }); it('should return false for standard FormData without getParts method', () => { const standardFormData = new FormData(); assert.strictEqual(utils.isReactNative(standardFormData), false); }); it('should return false for objects without getParts method', () => { assert.strictEqual(utils.isReactNative({ append: () => {} }), false); assert.strictEqual(utils.isReactNative({}), false); }); }); }); axios-axios-aa54a5e/tests/unit/utils/000077500000000000000000000000001526660636600177465ustar00rootroot00000000000000axios-axios-aa54a5e/tests/unit/utils/endsWith.test.js000066400000000000000000000005351526660636600230520ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import utils from '../../../lib/utils.js'; const { kindOf } = utils; describe('utils::kindOf', () => { it('should return object tag', () => { expect(kindOf({})).toEqual('object'); // cached result expect(kindOf({})).toEqual('object'); expect(kindOf([])).toEqual('array'); }); }); axios-axios-aa54a5e/tests/unit/utils/extend.test.js000066400000000000000000000014551526660636600225560ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import utils from '../../../lib/utils.js'; const { extend } = utils; describe('utils::extend', () => { it('should be mutable', () => { const a = {}; const b = { foo: 123 }; extend(a, b); expect(a.foo).toEqual(b.foo); }); it('should extend properties', () => { let a = { foo: 123, bar: 456 }; const b = { bar: 789 }; a = extend(a, b); expect(a.foo).toEqual(123); expect(a.bar).toEqual(789); }); it('should bind to thisArg', () => { const a = {}; const b = { getFoo: function getFoo() { return this.foo; }, }; const thisArg = { foo: 'barbaz' }; extend(a, b, thisArg); expect(typeof a.getFoo).toEqual('function'); expect(a.getFoo()).toEqual(thisArg.foo); }); }); axios-axios-aa54a5e/tests/unit/utils/forEach.test.js000066400000000000000000000022511526660636600226310ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import utils from '../../../lib/utils.js'; const { forEach } = utils; describe('utils::forEach', () => { it('should loop over an array', () => { let sum = 0; forEach([1, 2, 3, 4, 5], (val) => { sum += val; }); expect(sum).toEqual(15); }); it('should loop over object keys', () => { let keys = ''; let vals = 0; const obj = { b: 1, a: 2, r: 3, }; forEach(obj, (v, k) => { keys += k; vals += v; }); expect(keys).toEqual('bar'); expect(vals).toEqual(6); }); it('should handle undefined gracefully', () => { let count = 0; forEach(undefined, () => { count++; }); expect(count).toEqual(0); }); it('should make an array out of non-array argument', () => { let count = 0; forEach( () => {}, () => { count++; } ); expect(count).toEqual(1); }); it('should handle non object prototype gracefully', () => { let count = 0; const data = Object.create(null); data.foo = 'bar'; forEach(data, () => { count++; }); expect(count).toEqual(1); }); }); axios-axios-aa54a5e/tests/unit/utils/isX.test.js000066400000000000000000000264611526660636600220360ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import utils from '../../../lib/utils.js'; describe('utils::isX', () => { it('should validate Array', () => { expect(utils.isArray([])).toEqual(true); expect(utils.isArray({ length: 5 })).toEqual(false); }); it('should validate ArrayBuffer', () => { expect(utils.isArrayBuffer(new ArrayBuffer(2))).toEqual(true); expect(utils.isArrayBuffer({})).toEqual(false); }); it('should validate ArrayBufferView', () => { expect(utils.isArrayBufferView(new DataView(new ArrayBuffer(2)))).toEqual(true); }); it('should validate FormData', () => { expect(utils.isFormData(new FormData())).toEqual(true); }); it('should validate Blob', () => { expect(utils.isBlob(new Blob())).toEqual(true); }); it('should validate String', () => { expect(utils.isString('')).toEqual(true); expect( utils.isString({ toString: function () { return ''; }, }) ).toEqual(false); }); it('should validate Number', () => { expect(utils.isNumber(123)).toEqual(true); expect(utils.isNumber('123')).toEqual(false); }); it('should validate Undefined', () => { expect(utils.isUndefined()).toEqual(true); expect(utils.isUndefined(null)).toEqual(false); }); it('should validate Object', () => { expect(utils.isObject({})).toEqual(true); expect(utils.isObject([])).toEqual(true); expect(utils.isObject(null)).toEqual(false); }); it('should validate plain Object', () => { expect(utils.isPlainObject({})).toEqual(true); expect(utils.isPlainObject([])).toEqual(false); expect(utils.isPlainObject(null)).toEqual(false); expect(utils.isPlainObject(Object.create({}))).toEqual(false); }); it('should ignore inherited symbol properties when validating plain Object', () => { try { Object.prototype[Symbol.iterator] = function* () { yield ['x-injected', 'yes']; }; Object.prototype[Symbol.toStringTag] = 'Custom'; expect(utils.isPlainObject({})).toEqual(true); expect(utils.isPlainObject([])).toEqual(false); expect( utils.isPlainObject({ [Symbol.iterator]: function* () { yield ['x-own', 'yes']; }, }) ).toEqual(false); expect( utils.isPlainObject({ [Symbol.toStringTag]: 'Custom', }) ).toEqual(false); } finally { delete Object.prototype[Symbol.iterator]; delete Object.prototype[Symbol.toStringTag]; } }); it('should ignore an iterator inherited from a terminal null-prototype boundary', () => { // A terminal application template is indistinguishable from a mutated // foreign Object.prototype, so its inherited members fail closed. const proto = Object.create(null); proto[Symbol.iterator] = function* () { yield ['x', '1']; }; const value = Object.create(proto); expect(utils.isPlainObject(value)).toEqual(true); expect(utils.isSafeIterable(value)).toEqual(false); }); it('should not read polluted Object.prototype iterator accessors for safe iterable checks', () => { let accessed = false; try { Object.defineProperty(Object.prototype, Symbol.iterator, { configurable: true, get() { accessed = true; throw new Error('polluted iterator accessor'); } }); expect(utils.isSafeIterable({})).toEqual(false); expect(accessed).toEqual(false); } finally { delete Object.prototype[Symbol.iterator]; } }); it('should stop safe prototype-chain reads on cyclic Proxy prototypes', () => { let calls = 0; let proxy; proxy = new Proxy({}, { getPrototypeOf() { calls += 1; if (calls > 5) { throw new Error('cycled'); } return proxy; } }); expect(utils.hasOwnInPrototypeChain(proxy, 'missing')).toEqual(false); expect(utils.getSafeProp(proxy, 'missing')).toEqual(undefined); expect(calls).toBeLessThanOrEqual(2); }); it('should stop safe prototype flattening on cyclic Proxy prototypes', () => { let calls = 0; let proxy; proxy = new Proxy({}, { getPrototypeOf() { calls += 1; if (calls > 1) { throw new Error('cycled'); } return proxy; } }); expect(Object.keys(utils.toSafeFlatObject(proxy))).toEqual([]); expect(calls).toEqual(1); }); it('should preserve null-prototype sources but exclude terminal templates', () => { const root = Object.create(null); root.own = 'preserved'; expect(utils.getSafeProp(root, 'own')).toEqual('preserved'); expect(utils.toSafeFlatObject(root)).toBe(root); const template = Object.create(null); template.inherited = 'excluded'; const source = Object.create(template); source.own = 'preserved'; expect(utils.getSafeProp(source, 'own')).toEqual('preserved'); expect(utils.getSafeProp(source, 'inherited')).toEqual(undefined); const flattened = utils.toSafeFlatObject(source); expect(flattened.own).toEqual('preserved'); expect(Object.prototype.hasOwnProperty.call(flattened, 'inherited')).toEqual(false); }); it('should materialize immutable null-prototype sources', () => { const source = Object.freeze(Object.assign(Object.create(null), { own: 'preserved' })); const flattened = utils.toSafeFlatObject(source); expect(flattened).not.toBe(source); expect(Object.getPrototypeOf(flattened)).toEqual(null); expect(flattened.own).toEqual('preserved'); expect(Object.isFrozen(flattened)).toEqual(false); }); it('should materialize and filter unsafe keys from null-prototype sources', () => { const source = Object.create(null); source.safe = 'preserved'; Object.defineProperty(source, '__proto__', { value: 'excluded', enumerable: true, configurable: true, writable: true, }); source.constructor = 'excluded'; source.prototype = 'excluded'; const flattened = utils.toSafeFlatObject(source); expect(flattened).not.toBe(source); expect(flattened.safe).toEqual('preserved'); expect(Object.prototype.hasOwnProperty.call(flattened, '__proto__')).toEqual(false); expect(Object.prototype.hasOwnProperty.call(flattened, 'constructor')).toEqual(false); expect(Object.prototype.hasOwnProperty.call(flattened, 'prototype')).toEqual(false); }); it('should exclude symbols inherited from terminal null-prototype templates', () => { const behavior = Symbol('behavior'); const template = Object.create(null); template[behavior] = () => 'excluded'; const source = Object.create(template); const flattened = utils.toSafeFlatObject(source); expect(utils.getSafeProp(source, behavior)).toEqual(undefined); expect(Object.prototype.hasOwnProperty.call(flattened, behavior)).toEqual(false); }); it('should validate Date', () => { expect(utils.isDate(new Date())).toEqual(true); expect(utils.isDate(Date.now())).toEqual(false); }); it('should validate Function', () => { expect(utils.isFunction(function () {})).toEqual(true); expect(utils.isFunction('function')).toEqual(false); }); it('should validate URLSearchParams', () => { expect(utils.isURLSearchParams(new URLSearchParams())).toEqual(true); expect(utils.isURLSearchParams('foo=1&bar=2')).toEqual(false); }); it('should validate TypedArray instance', () => { expect(utils.isTypedArray(new Uint8Array([1, 2, 3]))).toEqual(true); expect(utils.isTypedArray([1, 2, 3])).toEqual(false); }); describe('isPlainObject prototype boundaries', () => { const withPollutedObjectPrototype = (symbol, descriptor, fn) => { const original = Object.getOwnPropertyDescriptor(Object.prototype, symbol); try { Object.defineProperty(Object.prototype, symbol, descriptor); fn(); } finally { if (original) { Object.defineProperty(Object.prototype, symbol, original); } else { delete Object.prototype[symbol]; } } }; it('should treat Object.prototype itself as a plain object', () => { expect(utils.isPlainObject(Object.prototype)).toEqual(true); }); it('should keep Object.prototype a fail-closed boundary when it carries an own Symbol.iterator', () => { withPollutedObjectPrototype( Symbol.iterator, { value: undefined, configurable: true, writable: true }, () => { expect(utils.isPlainObject(Object.prototype)).toEqual(true); expect(utils.isPlainObject({})).toEqual(true); expect(utils.isPlainObject(Object.create(null))).toEqual(true); expect(utils.isPlainObject({ [Symbol.iterator]: function* () {} })).toEqual(false); } ); }); it('should keep Object.prototype a fail-closed boundary when it carries an own Symbol.toStringTag', () => { withPollutedObjectPrototype( Symbol.toStringTag, { value: 'Custom', configurable: true, writable: true }, () => { expect(utils.isPlainObject(Object.prototype)).toEqual(true); expect(utils.isPlainObject({})).toEqual(true); expect(utils.isPlainObject(Object.create(null))).toEqual(true); expect(utils.isPlainObject({ [Symbol.toStringTag]: 'Own' })).toEqual(false); } ); }); it('should not read a polluted Object.prototype iterator accessor', () => { let accessed = false; withPollutedObjectPrototype( Symbol.iterator, { configurable: true, get() { accessed = true; throw new Error('polluted iterator accessor'); }, }, () => { expect(utils.isPlainObject({})).toEqual(true); expect(utils.isPlainObject(Object.prototype)).toEqual(true); expect(accessed).toEqual(false); } ); }); it('should reject own symbol members on null-prototype objects', () => { const tagged = Object.create(null); tagged[Symbol.toStringTag] = 'Tagged'; const iterable = Object.create(null); iterable[Symbol.iterator] = function* () {}; expect(utils.isPlainObject(Object.create(null))).toEqual(true); expect(utils.isPlainObject(tagged)).toEqual(false); expect(utils.isPlainObject(iterable)).toEqual(false); }); it('should ignore symbol members inherited from a terminal null-prototype parent', () => { const parent = Object.create(null); parent[Symbol.toStringTag] = 'Parent'; parent[Symbol.iterator] = function* () {}; const child = Object.create(parent); const ownIterable = Object.create(parent); ownIterable[Symbol.iterator] = function* () {}; expect(utils.isPlainObject(child)).toEqual(true); expect(utils.isPlainObject(ownIterable)).toEqual(false); }); it('should reject class instances, deeper prototype chains and built-ins', () => { class Klass {} expect(utils.isPlainObject(new Klass())).toEqual(false); expect(utils.isPlainObject(Object.create(Object.create(null)))).toEqual(true); expect(utils.isPlainObject(Object.create(Object.create({})))).toEqual(false); expect(utils.isPlainObject([])).toEqual(false); expect(utils.isPlainObject(new Map())).toEqual(false); expect(utils.isPlainObject(Buffer.from('x'))).toEqual(false); expect(utils.isPlainObject(Object.freeze({}))).toEqual(true); }); }); }); axios-axios-aa54a5e/tests/unit/utils/kindOf.test.js000066400000000000000000000005351526660636600224770ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import utils from '../../../lib/utils.js'; const { kindOf } = utils; describe('utils::kindOf', () => { it('should return object tag', () => { expect(kindOf({})).toEqual('object'); // cached result expect(kindOf({})).toEqual('object'); expect(kindOf([])).toEqual('array'); }); }); axios-axios-aa54a5e/tests/unit/utils/kindOfTest.test.js000066400000000000000000000005341526660636600233360ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import utils from '../../../lib/utils.js'; describe('utils::kindOfTest', () => { it('should return true if the type is matched', () => { const { kindOfTest } = utils; const test = kindOfTest('number'); expect(test(123)).toEqual(true); expect(test('123')).toEqual(false); }); }); axios-axios-aa54a5e/tests/unit/utils/merge.test.js000066400000000000000000000142451526660636600223670ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import utils from '../../../lib/utils.js'; const { merge } = utils; describe('utils::merge', () => { it('should be immutable', () => { const a = {}; const b = { foo: 123 }; const c = { bar: 456 }; merge(a, b, c); expect(typeof a.foo).toEqual('undefined'); expect(typeof a.bar).toEqual('undefined'); expect(typeof b.bar).toEqual('undefined'); expect(typeof c.foo).toEqual('undefined'); }); it('should merge properties', () => { const a = { foo: 123 }; const b = { bar: 456 }; const c = { foo: 789 }; const d = merge(a, b, c); expect(d.foo).toEqual(789); expect(d.bar).toEqual(456); }); it('should merge recursively', () => { const a = { foo: { bar: 123 } }; const b = { foo: { baz: 456 }, bar: { qux: 789 } }; expect(merge(a, b)).toEqual({ foo: { bar: 123, baz: 456, }, bar: { qux: 789, }, }); }); it('should remove all references from nested objects', () => { const a = { foo: { bar: 123 } }; const b = {}; const d = merge(a, b); expect(d).toEqual({ foo: { bar: 123, }, }); expect(d.foo).not.toBe(a.foo); }); it('handles null and undefined arguments', () => { expect(merge(undefined, undefined)).toEqual({}); expect(merge(undefined, { foo: 123 })).toEqual({ foo: 123 }); expect(merge({ foo: 123 }, undefined)).toEqual({ foo: 123 }); expect(merge(null, null)).toEqual({}); expect(merge(null, { foo: 123 })).toEqual({ foo: 123 }); expect(merge({ foo: 123 }, null)).toEqual({ foo: 123 }); }); it('should replace properties with null', () => { expect(merge({}, { a: null })).toEqual({ a: null }); expect(merge({ a: null }, {})).toEqual({ a: null }); }); it('should replace properties with arrays', () => { expect(merge({}, { a: [1, 2, 3] })).toEqual({ a: [1, 2, 3] }); expect(merge({ a: 2 }, { a: [1, 2, 3] })).toEqual({ a: [1, 2, 3] }); expect(merge({ a: { b: 2 } }, { a: [1, 2, 3] })).toEqual({ a: [1, 2, 3] }); }); it('should replace properties with cloned arrays', () => { const a = [1, 2, 3]; const d = merge({}, { a }); expect(d).toEqual({ a: [1, 2, 3] }); expect(d.a).not.toBe(a); }); it('should support caseless option', () => { const a = { x: 1 }; const b = { X: 2 }; const merged = merge.call({ caseless: true }, a, b); expect(merged).toEqual({ x: 2, }); }); it('should merge enumerable symbol keys', () => { const key = Symbol('key'); const nestedKey = Symbol('nested'); const first = { [key]: { first: true } }; const second = { [key]: { second: true }, nested: { [nestedKey]: 'value', }, }; const merged = merge(first, second); expect(merged[key]).toEqual({ first: true, second: true }); expect(merged[key]).not.toBe(first[key]); expect(merged.nested[nestedKey]).toBe('value'); expect(merged.nested).not.toBe(second.nested); }); it('should skip non-enumerable symbol keys', () => { const key = Symbol('key'); const source = {}; Object.defineProperty(source, key, { value: 'hidden', enumerable: false, }); const merged = merge(source); expect(merged[key]).toBeUndefined(); expect(Object.getOwnPropertySymbols(merged)).toEqual([]); }); it('should support caseless string keys with symbol keys', () => { const key = Symbol('key'); const merged = merge.call( { caseless: true }, { x: 1, [key]: 'first' }, { X: 2, [key]: 'second' } ); expect(merged.x).toBe(2); expect(merged.X).toBeUndefined(); expect(merged[key]).toBe('second'); }); it('should ignore symbol keys on buffers', () => { const key = Symbol('key'); const buffer = Buffer.from('value'); buffer[key] = 'symbol value'; const merged = merge({ x: 1 }, buffer); expect(merged).toEqual({ x: 1 }); }); it('should ignore symbol keys on arrays', () => { const key = Symbol('key'); const array = ['value']; array[key] = 'symbol value'; const merged = merge({ x: 1 }, array); expect(merged).toEqual({ 0: 'value', x: 1 }); expect(merged[key]).toBeUndefined(); }); it('should honor skipUndefined for symbol keys', () => { const key = Symbol('key'); const merged = merge.call({ skipUndefined: true }, { [key]: 'first' }, { [key]: undefined }); expect(merged[key]).toBe('first'); }); it('should keep the first spelling of a caseless key across many sources', () => { const merged = merge.call( { caseless: true }, { 'Content-Type': 'a', Accept: 'x' }, { 'content-type': 'b' }, { 'CONTENT-TYPE': 'c', accept: 'y' } ); expect(merged).toEqual({ 'Content-Type': 'c', Accept: 'y' }); expect(Object.keys(merged)).toEqual(['Content-Type', 'Accept']); }); it('should coalesce differently cased keys within one source', () => { const merged = merge.call({ caseless: true }, { foo: 1, FOO: 2, bar: 3 }); expect(merged).toEqual({ foo: 2, bar: 3 }); }); it('should merge nested objects case-sensitively under a caseless top level', () => { const merged = merge.call( { caseless: true }, { headers: { Accept: 'a' } }, { HEADERS: { accept: 'b' } } ); expect(merged).toEqual({ headers: { Accept: 'a', accept: 'b' } }); }); it('should let a later spelling win when skipUndefined dropped the earlier caseless key', () => { const merged = merge.call( { caseless: true, skipUndefined: true }, { 'X-Token': undefined }, { 'x-token': 'set' } ); expect(merged).toEqual({ 'x-token': 'set' }); expect(merged['X-Token']).toBeUndefined(); }); it('should filter exact unsafe keys and preserve differently cased keys in caseless mode', () => { const merged = merge.call( { caseless: true }, JSON.parse('{"__proto__": {"polluted": true}, "Constructor": 1, "PROTOTYPE": 2, "safe": 3}') ); expect(merged.safe).toBe(3); expect(merged.Constructor).toBe(1); expect(merged.PROTOTYPE).toBe(2); expect(Object.prototype.polluted).toBeUndefined(); expect(Object.getPrototypeOf(merged)).toBe(Object.prototype); }); }); axios-axios-aa54a5e/tests/unit/utils/toArray.test.js000066400000000000000000000006171526660636600227070ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import utils from '../../../lib/utils.js'; const { toArray } = utils; describe('utils::toArray', () => { it('should return null or an array copy depending on input', () => { expect(toArray()).toEqual(null); expect(toArray([])).toEqual([]); expect(toArray([1])).toEqual([1]); expect(toArray([1, 2, 3])).toEqual([1, 2, 3]); }); }); axios-axios-aa54a5e/tests/unit/utils/toFlatObject.test.js000066400000000000000000000006751526660636600236520ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import utils from '../../../lib/utils.js'; const { toFlatObject } = utils; describe('utils::toFlatObject', () => { it('should resolve object proto chain to a flat object representation', () => { const a = { x: 1 }; const b = Object.create(a, { y: { value: 2 } }); const c = Object.create(b, { z: { value: 3 } }); expect(toFlatObject(c)).toEqual({ x: 1, y: 2, z: 3 }); }); }); axios-axios-aa54a5e/tests/unit/utils/trim.test.js000066400000000000000000000004641526660636600222410ustar00rootroot00000000000000import { describe, it, expect } from 'vitest'; import utils from '../../../lib/utils.js'; describe('utils::trim', () => { it('should trim spaces', () => { expect(utils.trim(' foo ')).toEqual('foo'); }); it('should trim tabs', () => { expect(utils.trim('\tfoo\t')).toEqual('foo'); }); }); axios-axios-aa54a5e/tsconfig.json000066400000000000000000000002151526660636600171720ustar00rootroot00000000000000{ "compilerOptions": { "module": "node16", "lib": ["dom", "es2015"], "types": [], "strict": true, "noEmit": true } } axios-axios-aa54a5e/tslint.json000066400000000000000000000002471526660636600167000ustar00rootroot00000000000000{ "extends": "dtslint/dtslint.json", "rules": { "no-unnecessary-generics": false }, "linterOptions": { "exclude": [ "test/module/**" ] } } axios-axios-aa54a5e/vitest.config.js000066400000000000000000000022601526660636600176050ustar00rootroot00000000000000import { defineConfig } from 'vitest/config'; import { playwright } from '@vitest/browser-playwright'; export default defineConfig({ test: { testTimeout: 10000, projects: [ { test: { name: 'unit', environment: 'node', include: ['tests/unit/**/*.test.js'], setupFiles: [], }, }, { test: { name: 'browser', include: ['tests/browser/**/*.browser.test.js'], browser: { enabled: true, provider: playwright(), instances: [{ browser: 'chromium' }], }, setupFiles: ['tests/setup/browser.setup.js'], }, }, { test: { name: 'browser-headless', include: ['tests/browser/**/*.browser.test.js'], browser: { enabled: true, provider: playwright(), instances: [ { browser: 'chromium', headless: true }, { browser: 'firefox', headless: true }, { browser: 'webkit', headless: true }, ], }, setupFiles: ['tests/setup/browser.setup.js'], }, }, ], }, }); axios-axios-aa54a5e/webpack.config.js000066400000000000000000000010771526660636600177100ustar00rootroot00000000000000var config = {}; function generateConfig(name) { var compress = name.indexOf('min') > -1; var config = { entry: './index.js', output: { path: __dirname + '/dist/', filename: name + '.js', sourceMapFilename: name + '.map', library: 'axios', libraryTarget: 'umd', globalObject: 'this', }, node: false, devtool: 'source-map', mode: compress ? 'production' : 'development', }; return config; } ['axios', 'axios.min'].forEach(function (key) { config[key] = generateConfig(key); }); module.exports = config;