diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 3536ffd1e949a..2b6318dc973e8 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -39,4 +39,4 @@ Generated-by: [Tool Name] following [the guidelines](https://github.com/apache/a
* Read the **[Pull Request Guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#pull-request-guidelines)** for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
* For fundamental code changes, an Airflow Improvement Proposal ([AIP](https://cwiki.apache.org/confluence/display/AIRFLOW/Airflow+Improvement+Proposals)) is needed.
* When adding dependency, check compliance with the [ASF 3rd Party License Policy](https://www.apache.org/legal/resolved.html#category-x).
-* For significant user-facing changes create newsfragment: `{pr_number}.significant.rst` or `{issue_number}.significant.rst`, in [airflow-core/newsfragments](https://github.com/apache/airflow/tree/main/airflow-core/newsfragments).
+* For significant user-facing changes create newsfragment: `{pr_number}.significant.rst`, in [airflow-core/newsfragments](https://github.com/apache/airflow/tree/main/airflow-core/newsfragments). You can add this file in a follow-up commit after the PR is created so you know the PR number.
diff --git a/.github/actions/breeze/action.yml b/.github/actions/breeze/action.yml
index eb0085390bf6b..9699f902e55f1 100644
--- a/.github/actions/breeze/action.yml
+++ b/.github/actions/breeze/action.yml
@@ -24,7 +24,7 @@ inputs:
default: "3.10"
uv-version:
description: 'uv version to use'
- default: "0.10.7" # Keep this comment to allow automatic replacement of uv version
+ default: "0.10.9" # Keep this comment to allow automatic replacement of uv version
outputs:
host-python-version:
description: Python version used in host
diff --git a/.github/actions/install-prek/action.yml b/.github/actions/install-prek/action.yml
index 79ba6a1db1671..d7d81e4d8ba7c 100644
--- a/.github/actions/install-prek/action.yml
+++ b/.github/actions/install-prek/action.yml
@@ -24,10 +24,10 @@ inputs:
default: "3.10"
uv-version:
description: 'uv version to use'
- default: "0.10.7" # Keep this comment to allow automatic replacement of uv version
+ default: "0.10.9" # Keep this comment to allow automatic replacement of uv version
prek-version:
description: 'prek version to use'
- default: "0.3.4" # Keep this comment to allow automatic replacement of prek version
+ default: "0.3.5" # Keep this comment to allow automatic replacement of prek version
save-cache:
description: "Whether to save prek cache"
required: true
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index af0b44bf8fbe4..021aff77f9542 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -48,6 +48,15 @@ updates:
core-ui-package-updates:
patterns:
- "*"
+ update-types:
+ - "minor"
+ - "patch"
+ major-version-updates:
+ patterns:
+ - "*"
+ applies-to: security-updates
+ update-types:
+ - "major"
- package-ecosystem: npm
cooldown:
@@ -60,6 +69,15 @@ updates:
core-ui-package-updates:
patterns:
- "*"
+ update-types:
+ - "minor"
+ - "patch"
+ major-version-updates:
+ patterns:
+ - "*"
+ applies-to: security-updates
+ update-types:
+ - "major"
- package-ecosystem: npm
cooldown:
@@ -72,6 +90,15 @@ updates:
ui-plugin-template-package-updates:
patterns:
- "*"
+ update-types:
+ - "minor"
+ - "patch"
+ ui-plugin-template-major-version-updates:
+ patterns:
+ - "*"
+ applies-to: security-updates
+ update-types:
+ - "major"
- package-ecosystem: npm
cooldown:
@@ -134,6 +161,9 @@ updates:
update-types:
- "minor"
- "patch"
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
- package-ecosystem: npm
cooldown:
@@ -150,6 +180,9 @@ updates:
update-types:
- "minor"
- "patch"
+ ignore:
+ - dependency-name: "*"
+ update-types: ["version-update:semver-major"]
# Repeat dependency updates on 2.11 branch as well
- package-ecosystem: pip
diff --git a/.github/workflows/additional-ci-image-checks.yml b/.github/workflows/additional-ci-image-checks.yml
index 0dfd92b793ee8..6a33cea24a937 100644
--- a/.github/workflows/additional-ci-image-checks.yml
+++ b/.github/workflows/additional-ci-image-checks.yml
@@ -135,7 +135,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Install Breeze"
diff --git a/.github/workflows/additional-prod-image-tests.yml b/.github/workflows/additional-prod-image-tests.yml
index c7d4bde61c8db..e37da4a47dc1e 100644
--- a/.github/workflows/additional-prod-image-tests.yml
+++ b/.github/workflows/additional-prod-image-tests.yml
@@ -107,7 +107,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 2
persist-credentials: false
@@ -145,7 +145,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 2
persist-credentials: false
@@ -176,7 +176,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 2
persist-credentials: false
@@ -207,7 +207,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 2
persist-credentials: false
diff --git a/.github/workflows/airflow-distributions-tests.yml b/.github/workflows/airflow-distributions-tests.yml
index db69ee894de6a..f35b8895054ff 100644
--- a/.github/workflows/airflow-distributions-tests.yml
+++ b/.github/workflows/airflow-distributions-tests.yml
@@ -89,7 +89,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Prepare breeze & CI image: ${{ matrix.python-version }}"
diff --git a/.github/workflows/automatic-backport.yml b/.github/workflows/automatic-backport.yml
index 986c31cae4a51..2abe2b8f3824b 100644
--- a/.github/workflows/automatic-backport.yml
+++ b/.github/workflows/automatic-backport.yml
@@ -45,7 +45,7 @@ jobs:
- name: Find PR information
id: pr-info
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
+ uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
diff --git a/.github/workflows/backport-cli.yml b/.github/workflows/backport-cli.yml
index 42f8178868267..9ed750a5fbc57 100644
--- a/.github/workflows/backport-cli.yml
+++ b/.github/workflows/backport-cli.yml
@@ -53,7 +53,7 @@ jobs:
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
id: checkout-for-backport
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: true
fetch-depth: 0
diff --git a/.github/workflows/basic-tests.yml b/.github/workflows/basic-tests.yml
index 34cf1bcb7ffc1..2f342dd7ac595 100644
--- a/.github/workflows/basic-tests.yml
+++ b/.github/workflows/basic-tests.yml
@@ -66,7 +66,7 @@ on: # yamllint disable-line rule:truthy
type: string
uv-version:
description: 'uv version to use'
- default: "0.10.7" # Keep this comment to allow automatic replacement of uv version
+ default: "0.10.9" # Keep this comment to allow automatic replacement of uv version
type: string
platform:
description: 'Platform for the build - linux/amd64 or linux/arm64'
@@ -83,7 +83,7 @@ jobs:
- name: "Cleanup repo"
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# Need to fetch all history for selective checks tests
fetch-depth: 0
@@ -102,7 +102,7 @@ jobs:
- name: "Cleanup repo"
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
persist-credentials: false
@@ -111,7 +111,7 @@ jobs:
- name: "Install SVN"
run: sudo apt-get update && sudo apt-get install -y subversion
- name: "Install Java (for Apache RAT)"
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
+ uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin'
java-version: '17'
@@ -130,7 +130,7 @@ jobs:
runs-on: ${{ fromJSON(inputs.runners) }}
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 1
persist-credentials: false
@@ -151,16 +151,16 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Setup pnpm
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0.0
+ uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
with:
version: 9
run_install: false
- name: "Setup node"
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
+ uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: 21
cache: 'pnpm'
@@ -213,7 +213,7 @@ jobs:
runs-on: ${{ fromJSON(inputs.runners) }}
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Install Breeze"
@@ -234,7 +234,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Install Breeze"
@@ -248,7 +248,7 @@ jobs:
platform: ${{ inputs.platform }}
save-cache: true
- name: Fetch incoming commit ${{ github.sha }} with its parent
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.sha }}
fetch-depth: 2
@@ -269,7 +269,7 @@ jobs:
runs-on: ["windows-2025"]
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 2
persist-credentials: false
@@ -286,7 +286,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Install Breeze"
@@ -371,7 +371,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Install Breeze"
@@ -418,7 +418,7 @@ jobs:
FORCE_COLOR: 1
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Install uv"
diff --git a/.github/workflows/ci-amd-arm.yml b/.github/workflows/ci-amd-arm.yml
index f64e2bbd90e91..99dbeaf15320a 100644
--- a/.github/workflows/ci-amd-arm.yml
+++ b/.github/workflows/ci-amd-arm.yml
@@ -137,11 +137,11 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Fetch incoming commit ${{ github.sha }} with its parent
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.sha }}
fetch-depth: 2
@@ -192,7 +192,7 @@ jobs:
if: needs.build-info.outputs.platform == 'linux/amd64'
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Install prek"
@@ -831,12 +831,12 @@ jobs:
VERBOSE: "true"
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
# keep this in sync with go.mod in go-sdk/
- name: Setup Go
- uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
+ uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0
with:
go-version: 1.24
cache-dependency-path: go-sdk/go.sum
@@ -932,7 +932,7 @@ jobs:
steps:
- name: Notify Slack
id: slack
- uses: slackapi/slack-github-action@485a9d42d3a73031f12ec201c457e2162c45d02d # v2.0.0
+ uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1
with:
method: chat.postMessage
token: ${{ env.SLACK_BOT_TOKEN }}
@@ -972,19 +972,19 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Free up disk space"
shell: bash
run: ./scripts/tools/free_up_disk_space.sh
- name: "Download all test warning artifacts from the current build"
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
+ uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
path: ./artifacts
pattern: test-warnings-*
- name: "Setup python"
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "${{ inputs.default-python-version }}"
- name: "Summarize all warnings"
@@ -993,7 +993,7 @@ jobs:
--pattern "**/warnings-*.txt" \
--output ./files
- name: "Upload artifact for summarized warnings"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: "test-summarized-warnings"
path: ./files/warn-summary-*.txt
diff --git a/.github/workflows/ci-image-build.yml b/.github/workflows/ci-image-build.yml
index 2083f7bfa28c7..d1415ed53fcf8 100644
--- a/.github/workflows/ci-image-build.yml
+++ b/.github/workflows/ci-image-build.yml
@@ -118,7 +118,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout target branch"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Free up disk space"
diff --git a/.github/workflows/ci-image-checks.yml b/.github/workflows/ci-image-checks.yml
index f4e302ab17036..7d23dce54a7d9 100644
--- a/.github/workflows/ci-image-checks.yml
+++ b/.github/workflows/ci-image-checks.yml
@@ -136,7 +136,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Prepare breeze & CI image: ${{ inputs.default-python-version }}"
@@ -184,7 +184,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Free up disk space"
@@ -238,7 +238,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Prepare breeze & CI image: ${{ inputs.default-python-version }}"
@@ -270,7 +270,7 @@ jobs:
# to be responsible for updating it. https://github.com/actions/upload-artifact/issues/506
if: steps.restore-docs-inventory-cache != 'true' && matrix.flag == '--docs-only'
- name: "Upload build docs"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: airflow-docs
path: './generated/_build'
@@ -301,7 +301,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Prepare breeze & CI image: ${{ inputs.default-python-version }}"
@@ -312,7 +312,7 @@ jobs:
use-uv: ${{ inputs.use-uv }}
make-mnt-writeable-and-cleanup: true
- name: "Download docs prepared as artifacts"
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
+ uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: airflow-docs
path: './generated/_build'
@@ -358,7 +358,7 @@ jobs:
inputs.canary-run == 'true' &&
(github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
- name: Configure AWS credentials
- uses: aws-actions/configure-aws-credentials@010d0da01d0b5a38af31e9c3470dbfdabdecca3a # v4.0.1
+ uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
with:
aws-access-key-id: ${{ secrets.DOCS_AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.DOCS_AWS_SECRET_ACCESS_KEY }}
@@ -393,12 +393,12 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 2
persist-credentials: false
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
repository: "apache/airflow-client-python"
fetch-depth: 1
diff --git a/.github/workflows/ci-notification.yml b/.github/workflows/ci-notification.yml
index 3b5fdfd31e5a5..e009e380e9277 100644
--- a/.github/workflows/ci-notification.yml
+++ b/.github/workflows/ci-notification.yml
@@ -41,7 +41,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
@@ -58,7 +58,7 @@ jobs:
- name: "Send Slack notification"
if: steps.find-workflow-run-status.outputs.conclusion == 'failure'
id: slack
- uses: slackapi/slack-github-action@485a9d42d3a73031f12ec201c457e2162c45d02d # v2.0.0
+ uses: slackapi/slack-github-action@91efab103c0de0a537f72a35f6b8cda0ee76bf0a # v2.1.1
with:
method: chat.postMessage
token: ${{ env.SLACK_BOT_TOKEN }}
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 28c8cfae81a07..97ca768720c9b 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -47,17 +47,17 @@ jobs:
security-events: write
steps:
- name: Checkout repository
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Initialize CodeQL
- uses: github/codeql-action/init@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
+ uses: github/codeql-action/init@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
with:
languages: ${{ matrix.language }}
- name: Autobuild
- uses: github/codeql-action/autobuild@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
+ uses: github/codeql-action/autobuild@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
+ uses: github/codeql-action/analyze@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
diff --git a/.github/workflows/finalize-tests.yml b/.github/workflows/finalize-tests.yml
index 6a6ae7d6b2e99..e814751f1cc79 100644
--- a/.github/workflows/finalize-tests.yml
+++ b/.github/workflows/finalize-tests.yml
@@ -99,7 +99,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# Needed to perform push action
persist-credentials: false
@@ -107,14 +107,14 @@ jobs:
id: constraints-branch
run: ./scripts/ci/constraints/ci_branch_constraints.sh >> ${GITHUB_OUTPUT}
- name: Checkout ${{ steps.constraints-branch.outputs.branch }}
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
path: "constraints"
ref: ${{ steps.constraints-branch.outputs.branch }}
persist-credentials: true
fetch-depth: 0
- name: "Download constraints from the constraints generated by build CI image"
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
+ uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
pattern: constraints-*
path: ./files
@@ -146,7 +146,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Prepare breeze & CI image: ${{ matrix.python-version }}"
diff --git a/.github/workflows/generate-constraints.yml b/.github/workflows/generate-constraints.yml
index 58b87a96b4f9e..acb854957453b 100644
--- a/.github/workflows/generate-constraints.yml
+++ b/.github/workflows/generate-constraints.yml
@@ -75,7 +75,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Prepare breeze & CI image: ${{ matrix.python-version }}"
@@ -123,7 +123,7 @@ jobs:
--answer yes --python "${PYTHON_VERSION}"
if: inputs.generate-pypi-constraints == 'true'
- name: "Upload constraint artifacts"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: constraints-${{ matrix.python-version }}
path: ./files/constraints-${{ matrix.python-version }}/constraints-*.txt
diff --git a/.github/workflows/helm-tests.yml b/.github/workflows/helm-tests.yml
index ae78d644e6a95..576abf71d1acd 100644
--- a/.github/workflows/helm-tests.yml
+++ b/.github/workflows/helm-tests.yml
@@ -68,7 +68,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Prepare breeze & CI image: ${{ inputs.default-python-version }}"
@@ -95,7 +95,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Install Breeze"
@@ -133,7 +133,7 @@ jobs:
breeze release-management generate-issue-content-helm-chart --limit-pr-count 2
--previous-release helm-chart/1.15.0 --current-release helm-chart/1.16.0 --verbose
- name: "Upload Helm artifacts"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: Helm artifacts
path: ./dist/airflow-*
diff --git a/.github/workflows/integration-system-tests.yml b/.github/workflows/integration-system-tests.yml
index 977243ecf75bb..d62a383a1e5c8 100644
--- a/.github/workflows/integration-system-tests.yml
+++ b/.github/workflows/integration-system-tests.yml
@@ -97,7 +97,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Prepare breeze & CI image: ${{ inputs.default-python-version }}"
@@ -147,7 +147,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Prepare breeze & CI image: ${{ inputs.default-python-version }}"
@@ -192,7 +192,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Prepare breeze & CI image: ${{ inputs.default-python-version }}"
diff --git a/.github/workflows/k8s-tests.yml b/.github/workflows/k8s-tests.yml
index 47bd7ba83211e..107a8757fe80f 100644
--- a/.github/workflows/k8s-tests.yml
+++ b/.github/workflows/k8s-tests.yml
@@ -80,7 +80,7 @@ jobs:
echo "PYTHON_MAJOR_MINOR_VERSION=${KUBERNETES_COMBO}" | sed 's/-.*//' >> $GITHUB_ENV
echo "KUBERNETES_VERSION=${KUBERNETES_COMBO}" | sed 's/=[^-]*-/=/' >> $GITHUB_ENV
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Free up disk space"
@@ -119,7 +119,7 @@ jobs:
- name: "\
Upload KinD logs ${{ matrix.executor }}-${{ matrix.kubernetes-combo }}-\
${{ matrix.use-standard-naming }}"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: "\
kind-logs-${{ matrix.kubernetes-combo }}-${{ matrix.executor }}-\
diff --git a/.github/workflows/news-fragment.yml b/.github/workflows/news-fragment.yml
index 45efd3364ea6d..6fd8c73d40ee7 100644
--- a/.github/workflows/news-fragment.yml
+++ b/.github/workflows/news-fragment.yml
@@ -30,7 +30,7 @@ jobs:
if: "contains(github.event.pull_request.labels.*.name, 'airflow3.0:breaking')"
steps:
- - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
# `towncrier check` runs `git diff --name-only origin/main...`, which
diff --git a/.github/workflows/prod-image-build.yml b/.github/workflows/prod-image-build.yml
index 93eb251cf0d63..0c9a4162b0436 100644
--- a/.github/workflows/prod-image-build.yml
+++ b/.github/workflows/prod-image-build.yml
@@ -125,7 +125,7 @@ jobs:
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
if: inputs.upload-package-artifact == 'true'
- name: "Checkout target branch"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Make /mnt writeable"
@@ -138,6 +138,13 @@ jobs:
shell: bash
run: rm -fv ./dist/* ./docker-context-files/*
if: inputs.upload-package-artifact == 'true'
+ - name: "Install prek"
+ uses: ./.github/actions/install-prek
+ id: prek
+ with:
+ python-version: ${{ matrix.python-version }}
+ platform: ${{ inputs.platform }}
+ save-cache: false
- name: "Install Breeze"
uses: ./.github/actions/breeze
if: inputs.upload-package-artifact == 'true'
@@ -173,7 +180,7 @@ jobs:
breeze release-management prepare-airflow-ctl-distributions --distribution-format wheel
if: inputs.upload-package-artifact == 'true'
- name: "Upload prepared packages as artifacts"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: prod-packages
path: ./dist
@@ -213,7 +220,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout target branch"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Make /mnt writeable"
@@ -224,7 +231,7 @@ jobs:
shell: bash
run: rm -fv ./dist/* ./docker-context-files/*
- name: "Download packages prepared as artifacts"
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
+ uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: prod-packages
path: ./docker-context-files
@@ -235,10 +242,12 @@ jobs:
- name: "Show downloaded packages"
run: ls -la ./docker-context-files
- name: "Download constraints"
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
+ uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
- pattern: constraints-*
- path: ./docker-context-files
+ name: constraints-${{ matrix.python-version }}
+ path: ./docker-context-files/constraints-${{ matrix.python-version }}
+ - name: "Show downloaded files"
+ run: ls -R ./docker-context-files
- name: "Show constraints"
run: |
for file in ./docker-context-files/constraints*/constraints*.txt
diff --git a/.github/workflows/publish-docs-to-s3.yml b/.github/workflows/publish-docs-to-s3.yml
index ddac8d7532598..ab10a4e2feccf 100644
--- a/.github/workflows/publish-docs-to-s3.yml
+++ b/.github/workflows/publish-docs-to-s3.yml
@@ -105,6 +105,7 @@ jobs:
"potiuk",
"utkarsharma2",
"vincbeck",
+ "vatsrahul1001",
]'), github.event.sender.login)
steps:
- name: "Input parameters summary"
@@ -177,7 +178,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout current version first to clean-up stuff"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
path: current-version
@@ -185,7 +186,7 @@ jobs:
# This will take longer as we need to rebuild CI image and it will not use cache
# but it will build the CI image from the version of Airflow that is used to check out things
- name: "Checkout ${{ inputs.ref }} "
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
ref: ${{ inputs.ref }}
@@ -242,7 +243,7 @@ jobs:
run: >
breeze build-docs ${INCLUDE_DOCS} --docs-only
- name: "Checkout current version to run SBOM generation"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
fetch-depth: 0
@@ -294,7 +295,7 @@ jobs:
run: >
breeze release-management publish-docs --override-versioned --run-in-parallel ${INCLUDE_DOCS}
- name: "Upload build docs"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: airflow-docs
path: /mnt/airflow-site
@@ -324,7 +325,7 @@ jobs:
# This will take longer as we need to rebuild CI image and it will not use cache
# but it will build the CI image from the version of Airflow that is used to check out things
- name: "Checkout ${{ inputs.ref }} "
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Make /mnt writeable and cleanup"
@@ -333,7 +334,7 @@ jobs:
- name: "Install Breeze"
uses: ./.github/actions/breeze
- name: "Download docs prepared as artifacts"
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
+ uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: airflow-docs
path: /mnt/airflow-site
@@ -360,7 +361,7 @@ jobs:
sudo /tmp/aws/install --update
rm -rf /tmp/aws/
- name: Configure AWS credentials
- uses: aws-actions/configure-aws-credentials@010d0da01d0b5a38af31e9c3470dbfdabdecca3a # v4.0.1
+ uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
with:
aws-access-key-id: ${{ secrets.DOCS_AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.DOCS_AWS_SECRET_ACCESS_KEY }}
diff --git a/.github/workflows/push-image-cache.yml b/.github/workflows/push-image-cache.yml
index 69ee1145361a0..1d06012e31034 100644
--- a/.github/workflows/push-image-cache.yml
+++ b/.github/workflows/push-image-cache.yml
@@ -113,7 +113,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Free up disk space"
@@ -182,7 +182,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Free up disk space"
@@ -192,7 +192,7 @@ jobs:
- name: "Cleanup dist and context file"
run: rm -fv ./dist/* ./docker-context-files/*
- name: "Download packages prepared as artifacts"
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
+ uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
name: prod-packages
path: ./docker-context-files
diff --git a/.github/workflows/recheck-old-bug-report.yml b/.github/workflows/recheck-old-bug-report.yml
index c245f73923216..907f2fda1c13f 100644
--- a/.github/workflows/recheck-old-bug-report.yml
+++ b/.github/workflows/recheck-old-bug-report.yml
@@ -28,7 +28,7 @@ jobs:
recheck-old-bug-report:
runs-on: ["ubuntu-22.04"]
steps:
- - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
+ - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
with:
only-issue-labels: 'kind:bug'
stale-issue-label: 'Stale Bug Report'
diff --git a/.github/workflows/release_dockerhub_image.yml b/.github/workflows/release_dockerhub_image.yml
index b6099163a84bc..0046ffdbca77b 100644
--- a/.github/workflows/release_dockerhub_image.yml
+++ b/.github/workflows/release_dockerhub_image.yml
@@ -58,7 +58,7 @@ jobs:
AIRFLOW_VERSION: ${{ github.event.inputs.airflowVersion }}
AMD_ONLY: ${{ github.event.inputs.amdOnly }}
LIMIT_PYTHON_VERSIONS: ${{ github.event.inputs.limitPythonVersions }}
- UV_VERSION: "0.10.7" # Keep this comment to allow automatic replacement of uv version
+ UV_VERSION: "0.10.9" # Keep this comment to allow automatic replacement of uv version
if: contains(fromJSON('[
"ashb",
"bugraoz93",
@@ -71,6 +71,7 @@ jobs:
"potiuk",
"utkarsharma2",
"vincbeck",
+ "vatsrahul1001",
]'), github.event.sender.login)
steps:
- name: "Input parameters summary"
@@ -87,7 +88,7 @@ jobs:
docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Install Breeze"
diff --git a/.github/workflows/release_single_dockerhub_image.yml b/.github/workflows/release_single_dockerhub_image.yml
index 5f62be975c4b8..d71a5a50d8b3b 100644
--- a/.github/workflows/release_single_dockerhub_image.yml
+++ b/.github/workflows/release_single_dockerhub_image.yml
@@ -76,7 +76,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Install Breeze"
@@ -144,7 +144,7 @@ jobs:
shell: bash
run: find ./dist -name '*.json'
- name: "Upload metadata artifact ${{ env.ARTIFACT_NAME }}"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: ${{ env.ARTIFACT_NAME }}
path: ./dist/metadata-*
@@ -170,7 +170,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Install Breeze"
@@ -189,7 +189,7 @@ jobs:
ACTOR: ${{ github.actor }}
run: echo "${GITHUB_TOKEN}" | docker login ghcr.io -u ${ACTOR} --password-stdin
- name: "Download metadata artifacts"
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
+ uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
with:
path: ./dist
pattern: metadata-${{ inputs.pythonVersion }}-*
diff --git a/.github/workflows/run-unit-tests.yml b/.github/workflows/run-unit-tests.yml
index 412e743146eb7..4a5789f44cf57 100644
--- a/.github/workflows/run-unit-tests.yml
+++ b/.github/workflows/run-unit-tests.yml
@@ -182,7 +182,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Make /mnt writeable"
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index 5724a17314aec..7e6d2a8726d8f 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -29,7 +29,7 @@ jobs:
stale:
runs-on: ["ubuntu-22.04"]
steps:
- - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
+ - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
with:
stale-pr-message: >
This pull request has been automatically marked as stale because it has not had
diff --git a/.github/workflows/test-providers.yml b/.github/workflows/test-providers.yml
index 264f75e759be6..36fa445b53f0c 100644
--- a/.github/workflows/test-providers.yml
+++ b/.github/workflows/test-providers.yml
@@ -89,7 +89,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Free up disk space"
@@ -188,7 +188,7 @@ jobs:
shell: bash
run: docker run -v "${GITHUB_WORKSPACE}:/workspace" -u 0:0 bash -c "rm -rf /workspace/*"
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Free up disk space"
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 79821d63f298f..74490ef0cc619 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -346,7 +346,7 @@ repos:
- --line-length
- '99999'
- repo: https://github.com/codespell-project/codespell
- rev: 63c8f8312b7559622c0d82815639671ae42132ac # frozen: v2.4.1
+ rev: 2ccb47ff45ad361a21071a7eedda4c37e6ae8c5a # frozen: v2.4.2
hooks:
- id: codespell
name: Run codespell
@@ -408,7 +408,7 @@ repos:
types_or: [python, pyi]
args: [--fix]
require_serial: true
- additional_dependencies: ['ruff==0.15.4']
+ additional_dependencies: ['ruff==0.15.5']
exclude: ^airflow-core/tests/unit/dags/test_imports\.py$|^performance/tests/test_.*\.py$
- id: ruff-format
name: Run 'ruff format'
diff --git a/Dockerfile b/Dockerfile
index e4fd516b5dc6f..4fc4ef3588294 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -58,7 +58,7 @@ ARG AIRFLOW_PYTHON_VERSION="3.12.13"
# Also use `force pip` label on your PR to swap all places we use `uv` to `pip`
ARG AIRFLOW_PIP_VERSION=26.0.1
# ARG AIRFLOW_PIP_VERSION="git+https://github.com/pypa/pip.git@main"
-ARG AIRFLOW_UV_VERSION=0.10.7
+ARG AIRFLOW_UV_VERSION=0.10.9
ARG AIRFLOW_USE_UV="false"
ARG UV_HTTP_TIMEOUT="300"
ARG AIRFLOW_IMAGE_REPOSITORY="https://github.com/apache/airflow"
@@ -993,10 +993,28 @@ function install_airflow_and_providers_from_docker_context_files(){
fi
set -x
- ${PACKAGING_TOOL_CMD} install ${EXTRA_INSTALL_FLAGS} \
+ if ! ${PACKAGING_TOOL_CMD} install ${EXTRA_INSTALL_FLAGS} \
${ADDITIONAL_PIP_INSTALL_FLAGS} \
"${flags[@]}" \
- "${install_airflow_distribution[@]}" "${install_airflow_core_distribution[@]}" "${airflow_distributions[@]}"
+ "${install_airflow_distribution[@]}" "${install_airflow_core_distribution[@]}" "${airflow_distributions[@]}"; then
+ set +x
+ if [[ ${AIRFLOW_FALLBACK_NO_CONSTRAINTS_INSTALLATION} != "true" ]]; then
+ echo
+ echo "${COLOR_RED}Failing because constraints installation failed and fallback is disabled.${COLOR_RESET}"
+ echo
+ exit 1
+ fi
+ echo
+ echo "${COLOR_YELLOW}Likely there are new dependencies conflicting with constraints.${COLOR_RESET}"
+ echo
+ echo "${COLOR_BLUE}Falling back to no-constraints installation.${COLOR_RESET}"
+ echo
+ set -x
+ ${PACKAGING_TOOL_CMD} install ${EXTRA_INSTALL_FLAGS} \
+ ${ADDITIONAL_PIP_INSTALL_FLAGS} \
+ "${install_airflow_distribution[@]}" "${install_airflow_core_distribution[@]}" \
+ "${airflow_distributions[@]}"
+ fi
set +x
common::install_packaging_tools
# We use pip check here to make sure that whatever `uv` installs, is also "correct" according to `pip`
@@ -1184,7 +1202,7 @@ function install_from_external_spec() {
installation_command_flags="apache-airflow[${AIRFLOW_EXTRAS}]${AIRFLOW_VERSION_SPECIFICATION}"
else
echo
- echo "${COLOR_RED}The '${INSTALLATION_METHOD}' installation method is not supported${COLOR_RESET}"
+ echo "${COLOR_RED}The '${AIRFLOW_INSTALLATION_METHOD}' installation method is not supported${COLOR_RESET}"
echo
echo "${COLOR_YELLOW}Supported methods are ('.', 'apache-airflow')${COLOR_RESET}"
echo
@@ -1653,7 +1671,8 @@ COPY <<"EOF" /clean-logs.sh
set -euo pipefail
readonly DIRECTORY="${AIRFLOW_HOME:-/usr/local/airflow}"
-readonly RETENTION="${AIRFLOW__LOG_RETENTION_DAYS:-15}"
+readonly RETENTION_DAYS="${AIRFLOW__LOG_RETENTION_DAYS:-15}"
+readonly RETENTION_MINUTES="${AIRFLOW__LOG_RETENTION_MINUTES:-0}"
readonly FREQUENCY="${AIRFLOW__LOG_CLEANUP_FREQUENCY_MINUTES:-15}"
readonly MAX_PERCENT="${AIRFLOW__LOG_MAX_SIZE_PERCENT:-0}"
@@ -1678,10 +1697,12 @@ fi
retention_days="${RETENTION}"
while true; do
- echo "Trimming airflow logs to ${retention_days} days."
+ total_retention_minutes=$(( (RETENTION_DAYS * 1440) + RETENTION_MINUTES ))
+ echo "Trimming airflow logs older than ${total_retention_minutes} minutes."
+
find "${DIRECTORY}"/logs \
-type d -name 'lost+found' -prune -o \
- -type f -mtime +"${retention_days}" -name '*.log' -print0 | \
+ -type f -mmin +"${total_retention_minutes}" -name '*.log' -print0 | \
xargs -0 rm -f || true
if [[ "$MAX_SIZE_BYTES" -gt 0 && "$retention_days" -ge 0 ]]; then
diff --git a/Dockerfile.ci b/Dockerfile.ci
index 0795a68222dce..421b654a343c5 100644
--- a/Dockerfile.ci
+++ b/Dockerfile.ci
@@ -937,7 +937,7 @@ function install_from_external_spec() {
installation_command_flags="apache-airflow[${AIRFLOW_EXTRAS}]${AIRFLOW_VERSION_SPECIFICATION}"
else
echo
- echo "${COLOR_RED}The '${INSTALLATION_METHOD}' installation method is not supported${COLOR_RESET}"
+ echo "${COLOR_RED}The '${AIRFLOW_INSTALLATION_METHOD}' installation method is not supported${COLOR_RESET}"
echo
echo "${COLOR_YELLOW}Supported methods are ('.', 'apache-airflow')${COLOR_RESET}"
echo
@@ -1579,7 +1579,7 @@ ENV DEV_APT_COMMAND=${DEV_APT_COMMAND} \
ARG AIRFLOW_PYTHON_VERSION="3.12.13"
ENV AIRFLOW_PYTHON_VERSION=${AIRFLOW_PYTHON_VERSION}
-ENV GOLANG_MAJOR_MINOR_VERSION="1.25.5"
+ENV GOLANG_MAJOR_MINOR_VERSION="1.26.1"
COPY --from=scripts install_os_dependencies.sh /scripts/docker/
@@ -1728,8 +1728,8 @@ COPY --from=scripts common.sh install_packaging_tools.sh install_additional_depe
# Also use `force pip` label on your PR to swap all places we use `uv` to `pip`
ARG AIRFLOW_PIP_VERSION=26.0.1
# ARG AIRFLOW_PIP_VERSION="git+https://github.com/pypa/pip.git@main"
-ARG AIRFLOW_UV_VERSION=0.10.7
-ARG AIRFLOW_PREK_VERSION="0.3.4"
+ARG AIRFLOW_UV_VERSION=0.10.9
+ARG AIRFLOW_PREK_VERSION="0.3.5"
# UV_LINK_MODE=copy is needed since we are using cache mounted from the host
ENV AIRFLOW_PIP_VERSION=${AIRFLOW_PIP_VERSION} \
diff --git a/airflow-core/docs/best-practices.rst b/airflow-core/docs/best-practices.rst
index cafd8d4895e55..6d11e0ccc0209 100644
--- a/airflow-core/docs/best-practices.rst
+++ b/airflow-core/docs/best-practices.rst
@@ -310,7 +310,7 @@ Installing and Using ruff
.. code-block:: bash
- pip install "ruff>=0.15.4"
+ pip install "ruff>=0.15.5"
2. **Running ruff**: Execute ``ruff`` to check your Dags for potential issues:
diff --git a/airflow-core/docs/img/airflow_erd.sha256 b/airflow-core/docs/img/airflow_erd.sha256
index 5b0452f2453de..ea41374282a5f 100644
--- a/airflow-core/docs/img/airflow_erd.sha256
+++ b/airflow-core/docs/img/airflow_erd.sha256
@@ -1 +1 @@
-a222f120b40cc9c82870945a44f9ba6a2aabce378716b5f0ced55c28e57c2bb6
\ No newline at end of file
+623beee8d78f46ec2160d0e9142ff89535b67cb1828c5df1f940f1a260ef8046
\ No newline at end of file
diff --git a/airflow-core/docs/img/airflow_erd.svg b/airflow-core/docs/img/airflow_erd.svg
index 00b4126c30b54..bfc639a1a3ee4 100644
--- a/airflow-core/docs/img/airflow_erd.svg
+++ b/airflow-core/docs/img/airflow_erd.svg
@@ -238,7 +238,7 @@
NOT NULL
-
+
dag_bundle:name--dag_bundle_team:dag_bundle_name
0..N
@@ -383,7 +383,7 @@
NOT NULL
-
+
team:id--dag_bundle_team:team_id
0..N
@@ -572,7 +572,7 @@
NOT NULL
-
+
asset_alias:id--asset_alias_asset:alias_id
0..N
@@ -595,7 +595,7 @@
NOT NULL
-
+
asset_alias:id--asset_alias_asset_event:alias_id
0..N
@@ -676,7 +676,7 @@
NOT NULL
-
+
asset:id--asset_alias_asset:asset_id
0..N
@@ -723,18 +723,18 @@
-asset:uri--asset_active:uri
-
-1
-1
-
-
-
asset:name--asset_active:name
1
1
+
+
+asset:uri--asset_active:uri
+
+1
+1
+
dag_schedule_asset_reference
@@ -762,7 +762,7 @@
NOT NULL
-
+
asset:id--dag_schedule_asset_reference:asset_id
0..N
@@ -838,7 +838,7 @@
NOT NULL
-
+
asset:id--task_inlet_asset_reference:asset_id
0..N
@@ -915,7 +915,7 @@
NOT NULL
-
+
asset_event:id--asset_alias_asset_event:event_id
0..N
@@ -1184,7 +1184,7 @@
[INTEGER]
-
+
trigger:id--deadline:trigger_id
0..N
@@ -1254,7 +1254,7 @@
1
-
+
dag:dag_id--dag_schedule_asset_reference:dag_id
0..N
@@ -1268,7 +1268,7 @@
1
-
+
dag:dag_id--task_inlet_asset_reference:dag_id
0..N
@@ -1552,7 +1552,7 @@
[TIMESTAMP]
-
+
dag_version:id--dag_run:created_dag_version_id
0..N
@@ -1696,7 +1696,7 @@
NOT NULL
-
+
log_template:id--dag_run:log_template_id
0..N
@@ -1724,7 +1724,7 @@
1
-
+
dag_run:id--deadline:dagrun_id
0..N
@@ -1765,7 +1765,7 @@
NOT NULL
-
+
dag_run:id--backfill_dag_run:dag_run_id
0..N
@@ -1878,7 +1878,7 @@
{0,1}
-
+
backfill:id--backfill_dag_run:backfill_id
0..N
@@ -1992,19 +1992,19 @@
-task_instance:run_id--task_map:run_id
-
-0..N
-1
-
-
-
task_instance:dag_id--task_map:dag_id
0..N
1
+
+task_instance:task_id--task_map:task_id
+
+0..N
+1
+
+
task_instance:map_index--task_map:map_index
@@ -2013,10 +2013,10 @@
-task_instance:task_id--task_map:task_id
-
-0..N
-1
+task_instance:run_id--task_map:run_id
+
+0..N
+1
@@ -2115,25 +2115,25 @@
+task_instance:run_id--xcom:run_id
+
+0..N
+1
+
+
+
task_instance:task_id--xcom:task_id
0..N
1
-
+
task_instance:map_index--xcom:map_index
0..N
1
-
-
-task_instance:run_id--xcom:run_id
-
-0..N
-1
-
task_instance_note
@@ -2338,19 +2338,19 @@
-task_instance:map_index--task_instance_history:map_index
-
-0..N
-1
-
-
-
task_instance:run_id--task_instance_history:run_id
0..N
1
+
+task_instance:map_index--task_instance_history:map_index
+
+0..N
+1
+
+
task_instance:dag_id--task_instance_history:dag_id
@@ -2394,19 +2394,19 @@
+task_instance:task_id--rendered_task_instance_fields:task_id
+
+0..N
+1
+
+
+
task_instance:map_index--rendered_task_instance_fields:map_index
0..N
1
-
-task_instance:dag_id--rendered_task_instance_fields:dag_id
-
-0..N
-1
-
-
task_instance:run_id--rendered_task_instance_fields:run_id
@@ -2415,10 +2415,10 @@
-task_instance:task_id--rendered_task_instance_fields:task_id
-
-0..N
-1
+task_instance:dag_id--rendered_task_instance_fields:dag_id
+
+0..N
+1
diff --git a/airflow-core/docs/tutorial/hitl.rst b/airflow-core/docs/tutorial/hitl.rst
index ab5c6ed0175b2..3a5e35c6f7918 100644
--- a/airflow-core/docs/tutorial/hitl.rst
+++ b/airflow-core/docs/tutorial/hitl.rst
@@ -159,7 +159,7 @@ The method ``HITLOperator.generate_link_to_ui_from_context`` can be used to gene
- ``context`` – automatically passed to ``notify`` by the notifier
- ``base_url`` – (optional) the base URL of the Airflow UI; if not provided, ``api.base_url`` in the configuration will be used
-- ``options`` – (optional) pre-selected options for the UI page
+- ``options`` – (optional) preselected options for the UI page
- ``params_inputs`` – (optional) pre-loaded inputs for the UI page
This makes it easy to include actionable links in notifications or logs.
diff --git a/airflow-core/newsfragments/62964.bugfix.rst b/airflow-core/newsfragments/62964.bugfix.rst
new file mode 100644
index 0000000000000..048ba18893be4
--- /dev/null
+++ b/airflow-core/newsfragments/62964.bugfix.rst
@@ -0,0 +1 @@
+Prevent JWT tokens from appearing in task logs by excluding the token field from workload object representations.
diff --git a/airflow-core/newsfragments/63205.bugfix.rst b/airflow-core/newsfragments/63205.bugfix.rst
new file mode 100644
index 0000000000000..7e1781bc8ed32
--- /dev/null
+++ b/airflow-core/newsfragments/63205.bugfix.rst
@@ -0,0 +1 @@
+Fix grid view URL for dynamic task groups producing 404 by not appending ``/mapped`` to group URLs.
diff --git a/airflow-core/pyproject.toml b/airflow-core/pyproject.toml
index d72eb3e14dfc4..f9b24eeb6ec4b 100644
--- a/airflow-core/pyproject.toml
+++ b/airflow-core/pyproject.toml
@@ -23,7 +23,7 @@ requires = [
"packaging==26.0",
"pathspec==1.0.4",
"pluggy==1.6.0",
- "smmap==5.0.2",
+ "smmap==5.0.3",
"tomli==2.4.0; python_version < '3.11'",
"trove-classifiers==2026.1.14.14",
]
diff --git a/airflow-core/src/airflow/api_fastapi/app.py b/airflow-core/src/airflow/api_fastapi/app.py
index 1720c9f08ecdf..c13ea9ed675a3 100644
--- a/airflow-core/src/airflow/api_fastapi/app.py
+++ b/airflow-core/src/airflow/api_fastapi/app.py
@@ -17,7 +17,9 @@
from __future__ import annotations
import logging
+import threading
from contextlib import AsyncExitStack, asynccontextmanager
+from functools import cache
from typing import TYPE_CHECKING, cast
from urllib.parse import urlsplit
@@ -64,8 +66,10 @@ def get_cookie_path() -> str:
log = logging.getLogger(__name__)
-app: FastAPI | None = None
-auth_manager: BaseAuthManager | None = None
+
+class _AuthManagerState:
+ instance: BaseAuthManager | None = None
+ _lock = threading.Lock()
@asynccontextmanager
@@ -117,19 +121,16 @@ def create_app(apps: str = "all") -> FastAPI:
return app
+@cache
def cached_app(config=None, testing=False, apps="all") -> FastAPI:
"""Return cached instance of Airflow API app."""
- global app
- if not app:
- app = create_app(apps=apps)
- return app
+ return create_app(apps=apps)
def purge_cached_app() -> None:
"""Remove the cached version of the app and auth_manager in global state."""
- global app, auth_manager
- app = None
- auth_manager = None
+ cached_app.cache_clear()
+ _AuthManagerState.instance = None
def get_auth_manager_cls() -> type[BaseAuthManager]:
@@ -149,11 +150,14 @@ def get_auth_manager_cls() -> type[BaseAuthManager]:
def create_auth_manager() -> BaseAuthManager:
- """Create the auth manager."""
- global auth_manager
+ """Create the auth manager, cached as a thread-safe singleton."""
auth_manager_cls = get_auth_manager_cls()
- auth_manager = auth_manager_cls()
- return auth_manager
+ if _AuthManagerState.instance is not None and isinstance(_AuthManagerState.instance, auth_manager_cls):
+ return _AuthManagerState.instance
+ with _AuthManagerState._lock:
+ if _AuthManagerState.instance is None or not isinstance(_AuthManagerState.instance, auth_manager_cls):
+ _AuthManagerState.instance = auth_manager_cls()
+ return _AuthManagerState.instance
def init_auth_manager(app: FastAPI | None = None) -> BaseAuthManager:
@@ -171,12 +175,12 @@ def init_auth_manager(app: FastAPI | None = None) -> BaseAuthManager:
def get_auth_manager() -> BaseAuthManager:
"""Return the auth manager, provided it's been initialized before."""
- if auth_manager is None:
+ if _AuthManagerState.instance is None:
raise RuntimeError(
"Auth Manager has not been initialized yet. "
"The `init_auth_manager` method needs to be called first."
)
- return auth_manager
+ return _AuthManagerState.instance
def init_plugins(app: FastAPI) -> None:
diff --git a/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/package-lock.json b/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/package-lock.json
index c93585c4a8345..59156ec013bdf 100644
--- a/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/package-lock.json
+++ b/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/package-lock.json
@@ -8,7 +8,7 @@
"name": "simple-auth-manager-ui",
"version": "0.0.0",
"dependencies": {
- "@chakra-ui/react": "^3.33.0",
+ "@chakra-ui/react": "^3.34.0",
"@tanstack/react-query": "^5.90.21",
"axios": "^1.13.5",
"next-themes": "^0.4.6",
@@ -19,7 +19,7 @@
"react-router-dom": "^7.13.1"
},
"devDependencies": {
- "@7nohe/openapi-react-query-codegen": "^2.0.0",
+ "@7nohe/openapi-react-query-codegen": "^1.6.2",
"@eslint/compat": "^2.0.2",
"@eslint/js": "^10.0.1",
"@stylistic/eslint-plugin": "^5.9.0",
@@ -28,9 +28,9 @@
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
- "@typescript-eslint/eslint-plugin": "8.50.0",
- "@typescript-eslint/parser": "8.50.0",
- "@typescript-eslint/utils": "^8.50.0",
+ "@typescript-eslint/eslint-plugin": "8.56.1",
+ "@typescript-eslint/parser": "8.56.1",
+ "@typescript-eslint/utils": "^8.56.1",
"@vitejs/plugin-react-swc": "^4.2.3",
"eslint": "^10.0.2",
"eslint-config-prettier": "^10.1.8",
@@ -41,8 +41,9 @@
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.2",
"eslint-plugin-unicorn": "^63.0.0",
- "happy-dom": "^20.7.0",
+ "happy-dom": "^20.8.3",
"prettier": "^3.8.1",
+ "ts-morph": "^27.0.2",
"typescript": "~5.9.3",
"typescript-eslint": "^8.56.1",
"vite": "^7.3.1",
@@ -51,18 +52,16 @@
}
},
"node_modules/@7nohe/openapi-react-query-codegen": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@7nohe/openapi-react-query-codegen/-/openapi-react-query-codegen-2.0.0.tgz",
- "integrity": "sha512-RZKQNK36xiziPzio9msj72gVuQq/d5WfiUI6cExCQuMOZUhdG2nEVdt1EunI3wc7msHX3oxVfxJGjMdn8ydz7w==",
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/@7nohe/openapi-react-query-codegen/-/openapi-react-query-codegen-1.6.2.tgz",
+ "integrity": "sha512-hiemrj2XDypRpsqBLjliIIhLxH5NzCsbUW5km7XVRvlC3J+oIc7EBG+LqMSGWj1T6zG1eqFFc+/qXXC3eKPptA==",
"dev": true,
"license": "MIT",
"workspaces": [
"examples/*"
],
"dependencies": {
- "@hey-api/client-fetch": "0.4.0",
- "@hey-api/openapi-ts": "0.53.8",
- "cross-spawn": "^7.0.3"
+ "@hey-api/openapi-ts": "0.52.0"
},
"bin": {
"openapi-rq": "dist/cli.mjs"
@@ -73,7 +72,8 @@
},
"peerDependencies": {
"commander": "12.x",
- "ts-morph": "23.x",
+ "glob": "10.x",
+ "ts-morph": "22.x",
"typescript": "5.x"
}
},
@@ -84,9 +84,9 @@
"dev": true
},
"node_modules/@apidevtools/json-schema-ref-parser": {
- "version": "11.7.0",
- "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.7.0.tgz",
- "integrity": "sha512-pRrmXMCwnmrkS3MLgAIW5dXRzeTv6GLjkjb4HmxNnvAKXN1Nfzp4KmGADBQvlVUcqi+a5D+hfGDLLnd5NnYxog==",
+ "version": "11.6.4",
+ "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.6.4.tgz",
+ "integrity": "sha512-9K6xOqeevacvweLGik6LnZCb1fBtCOSIWQs8d096XGeqoLKC33UVMGz9+77Gw44KvbH4pKcQPWo4ZpxkXYj05w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -448,12 +448,12 @@
}
},
"node_modules/@chakra-ui/react": {
- "version": "3.33.0",
- "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-3.33.0.tgz",
- "integrity": "sha512-HNbUFsFABjVL5IHBxsqtuT+AH/vQT1+xsEWrxnG0GBM2VjlzlMqlqCxNiDyQOsjLZXQC1ciCMbzPNcSCc63Y9w==",
+ "version": "3.34.0",
+ "resolved": "https://registry.npmjs.org/@chakra-ui/react/-/react-3.34.0.tgz",
+ "integrity": "sha512-VLhpVwv5IVxhwajO10KnS1VQT4hDqQMQP/A796Ya+uVu8AdoSX+5HHyTLTkYIeXIDMe0xLqJfov04OBKbBchJA==",
"license": "MIT",
"dependencies": {
- "@ark-ui/react": "^5.31.0",
+ "@ark-ui/react": "^5.34.1",
"@emotion/is-prop-valid": "^1.4.0",
"@emotion/serialize": "^1.3.3",
"@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
@@ -1237,23 +1237,16 @@
"integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==",
"license": "MIT"
},
- "node_modules/@hey-api/client-fetch": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/@hey-api/client-fetch/-/client-fetch-0.4.0.tgz",
- "integrity": "sha512-T8T3yCl2+AiVVDP6tvfnU/rXOkEHddMTOYCZXUVbydj7URVErh5BelIa8UWBkFYZBP2/mi2nViScNhe9eBolPw==",
- "deprecated": "Starting with v0.73.0, this package is bundled directly inside @hey-api/openapi-ts.",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@hey-api/openapi-ts": {
- "version": "0.53.8",
- "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.53.8.tgz",
- "integrity": "sha512-UbiaIq+JNgG00N/iWYk+LSivOBgWsfGxEHDleWEgQcQr3q7oZJTKL8oH87+KkFDDbUngm1g8lnKI/zLdu1aElQ==",
+ "version": "0.52.0",
+ "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.52.0.tgz",
+ "integrity": "sha512-DA3Zf5ONxMK1PUkK88lAuYbXMgn5BvU5sjJdTAO2YOn6Eu/9ovilBztMzvu8pyY44PmL3n4ex4+f+XIwvgfhvw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@apidevtools/json-schema-ref-parser": "11.7.0",
- "c12": "2.0.1",
+ "@apidevtools/json-schema-ref-parser": "11.6.4",
+ "c12": "1.11.1",
+ "camelcase": "8.0.0",
"commander": "12.1.0",
"handlebars": "4.7.8"
},
@@ -1347,6 +1340,25 @@
"@swc/helpers": "^0.5.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",
+ "peer": true,
+ "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/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
@@ -1400,50 +1412,24 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@nodelib/fs.scandir": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
- "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "@nodelib/fs.stat": "2.0.5",
- "run-parallel": "^1.1.9"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.stat": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
- "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nodelib/fs.walk": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
- "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "@nodelib/fs.scandir": "2.1.5",
- "fastq": "^1.6.0"
- },
- "engines": {
- "node": ">= 8"
- }
- },
"node_modules/@pandacss/is-valid-prop": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/@pandacss/is-valid-prop/-/is-valid-prop-1.8.2.tgz",
"integrity": "sha512-wfZ4kzvHXQ9pG3wuIGTUCcbC7Op8pqIQZNIRY/bqWQu67WTHxZUHUPSZgQMhguI8Tz4ot+DNf4Qhha0bhLvNEQ==",
"license": "MIT"
},
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/@pkgr/core": {
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz",
@@ -2188,59 +2174,51 @@
}
},
"node_modules/@ts-morph/common": {
- "version": "0.24.0",
- "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.24.0.tgz",
- "integrity": "sha512-c1xMmNHWpNselmpIqursHeOHHBTIsJLbB+NuovbTTRCNiTLEr/U9dbJ8qy0jd/O2x5pc3seWuOUN5R2IoOTp8A==",
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.28.1.tgz",
+ "integrity": "sha512-W74iWf7ILp1ZKNYXY5qbddNaml7e9Sedv5lvU1V8lftlitkc9Pq1A+jlH23ltDgWYeZFFEqGCD1Ies9hqu3O+g==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
- "fast-glob": "^3.3.2",
- "minimatch": "^9.0.4",
- "mkdirp": "^3.0.1",
- "path-browserify": "^1.0.1"
+ "minimatch": "^10.0.1",
+ "path-browserify": "^1.0.1",
+ "tinyglobby": "^0.2.14"
}
},
- "node_modules/@ts-morph/common/node_modules/brace-expansion": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
- "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "node_modules/@ts-morph/common/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",
- "peer": true,
- "dependencies": {
- "balanced-match": "^1.0.0"
+ "engines": {
+ "node": "18 || 20 || >=22"
}
},
- "node_modules/@ts-morph/common/node_modules/minimatch": {
- "version": "9.0.9",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
- "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+ "node_modules/@ts-morph/common/node_modules/brace-expansion": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
+ "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
"dev": true,
- "license": "ISC",
- "peer": true,
+ "license": "MIT",
"dependencies": {
- "brace-expansion": "^2.0.2"
+ "balanced-match": "^4.0.2"
},
"engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
+ "node": "18 || 20 || >=22"
}
},
- "node_modules/@ts-morph/common/node_modules/mkdirp": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
- "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
+ "node_modules/@ts-morph/common/node_modules/minimatch": {
+ "version": "10.2.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
+ "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"dev": true,
- "license": "MIT",
- "peer": true,
- "bin": {
- "mkdirp": "dist/cjs/src/bin.js"
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.2"
},
"engines": {
- "node": ">=10"
+ "node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
@@ -2354,20 +2332,20 @@
}
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.50.0.tgz",
- "integrity": "sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg==",
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz",
+ "integrity": "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.50.0",
- "@typescript-eslint/type-utils": "8.50.0",
- "@typescript-eslint/utils": "8.50.0",
- "@typescript-eslint/visitor-keys": "8.50.0",
- "ignore": "^7.0.0",
+ "@eslint-community/regexpp": "^4.12.2",
+ "@typescript-eslint/scope-manager": "8.56.1",
+ "@typescript-eslint/type-utils": "8.56.1",
+ "@typescript-eslint/utils": "8.56.1",
+ "@typescript-eslint/visitor-keys": "8.56.1",
+ "ignore": "^7.0.5",
"natural-compare": "^1.4.0",
- "ts-api-utils": "^2.1.0"
+ "ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2377,21 +2355,33 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "@typescript-eslint/parser": "^8.50.0",
- "eslint": "^8.57.0 || ^9.0.0",
+ "@typescript-eslint/parser": "^8.56.1",
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
- "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/project-service": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.50.0.tgz",
- "integrity": "sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ==",
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz",
+ "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.50.0",
- "@typescript-eslint/types": "^8.50.0",
- "debug": "^4.3.4"
+ "@typescript-eslint/scope-manager": "8.56.1",
+ "@typescript-eslint/types": "8.56.1",
+ "@typescript-eslint/typescript-estree": "8.56.1",
+ "@typescript-eslint/visitor-keys": "8.56.1",
+ "debug": "^4.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2401,15 +2391,21 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
- "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.50.0.tgz",
- "integrity": "sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w==",
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz",
+ "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.56.1",
+ "@typescript-eslint/types": "^8.56.1",
+ "debug": "^4.4.3"
+ },
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
@@ -2421,18 +2417,15 @@
"typescript": ">=4.8.4 <6.0.0"
}
},
- "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.50.0.tgz",
- "integrity": "sha512-7OciHT2lKCewR0mFoBrvZJ4AXTMe/sYOe87289WAViOocEmDjjv8MvIOT2XESuKj9jp8u3SZYUSh89QA4S1kQw==",
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz",
+ "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.50.0",
- "@typescript-eslint/typescript-estree": "8.50.0",
- "@typescript-eslint/utils": "8.50.0",
- "debug": "^4.3.4",
- "ts-api-utils": "^2.1.0"
+ "@typescript-eslint/types": "8.56.1",
+ "@typescript-eslint/visitor-keys": "8.56.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2440,16 +2433,12 @@
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <6.0.0"
}
},
- "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.50.0.tgz",
- "integrity": "sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==",
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz",
+ "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -2458,24 +2447,23 @@
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
}
},
- "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.50.0.tgz",
- "integrity": "sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ==",
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz",
+ "integrity": "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/project-service": "8.50.0",
- "@typescript-eslint/tsconfig-utils": "8.50.0",
- "@typescript-eslint/types": "8.50.0",
- "@typescript-eslint/visitor-keys": "8.50.0",
- "debug": "^4.3.4",
- "minimatch": "^9.0.4",
- "semver": "^7.6.0",
- "tinyglobby": "^0.2.15",
- "ts-api-utils": "^2.1.0"
+ "@typescript-eslint/types": "8.56.1",
+ "@typescript-eslint/typescript-estree": "8.56.1",
+ "@typescript-eslint/utils": "8.56.1",
+ "debug": "^4.4.3",
+ "ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2485,42 +2473,40 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
- "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.50.0.tgz",
- "integrity": "sha512-87KgUXET09CRjGCi2Ejxy3PULXna63/bMYv72tCAlDJC3Yqwln0HiFJ3VJMst2+mEtNtZu5oFvX4qJGjKsnAgg==",
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz",
+ "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.7.0",
- "@typescript-eslint/scope-manager": "8.50.0",
- "@typescript-eslint/types": "8.50.0",
- "@typescript-eslint/typescript-estree": "8.50.0"
- },
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <6.0.0"
}
},
- "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.50.0.tgz",
- "integrity": "sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==",
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz",
+ "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.50.0",
- "eslint-visitor-keys": "^4.2.1"
+ "@typescript-eslint/project-service": "8.56.1",
+ "@typescript-eslint/tsconfig-utils": "8.56.1",
+ "@typescript-eslint/types": "8.56.1",
+ "@typescript-eslint/visitor-keys": "8.56.1",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2528,56 +2514,61 @@
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
}
},
- "node_modules/@typescript-eslint/eslint-plugin/node_modules/brace-expansion": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
- "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "node_modules/@typescript-eslint/typescript-estree/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",
- "dependencies": {
- "balanced-match": "^1.0.0"
+ "engines": {
+ "node": "18 || 20 || >=22"
}
},
- "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
- "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
+ "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
"engines": {
- "node": ">= 4"
+ "node": "18 || 20 || >=22"
}
},
- "node_modules/@typescript-eslint/eslint-plugin/node_modules/minimatch": {
- "version": "9.0.9",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
- "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "10.2.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
+ "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
"dev": true,
- "license": "ISC",
+ "license": "BlueOak-1.0.0",
"dependencies": {
- "brace-expansion": "^2.0.2"
+ "brace-expansion": "^5.0.2"
},
"engines": {
- "node": ">=16 || 14 >=14.17"
+ "node": "18 || 20 || >=22"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/@typescript-eslint/parser": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.50.0.tgz",
- "integrity": "sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==",
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz",
+ "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/scope-manager": "8.50.0",
- "@typescript-eslint/types": "8.50.0",
- "@typescript-eslint/typescript-estree": "8.50.0",
- "@typescript-eslint/visitor-keys": "8.50.0",
- "debug": "^4.3.4"
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.56.1",
+ "@typescript-eslint/types": "8.56.1",
+ "@typescript-eslint/typescript-estree": "8.56.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2587,20 +2578,19 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
"typescript": ">=4.8.4 <6.0.0"
}
},
- "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/project-service": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.50.0.tgz",
- "integrity": "sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ==",
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz",
+ "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.50.0",
- "@typescript-eslint/types": "^8.50.0",
- "debug": "^4.3.4"
+ "@typescript-eslint/types": "8.56.1",
+ "eslint-visitor-keys": "^5.0.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2608,377 +2598,16 @@
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <6.0.0"
}
},
- "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.50.0.tgz",
- "integrity": "sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w==",
+ "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
"dev": true,
- "license": "MIT",
+ "license": "Apache-2.0",
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.50.0.tgz",
- "integrity": "sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.50.0.tgz",
- "integrity": "sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/project-service": "8.50.0",
- "@typescript-eslint/tsconfig-utils": "8.50.0",
- "@typescript-eslint/types": "8.50.0",
- "@typescript-eslint/visitor-keys": "8.50.0",
- "debug": "^4.3.4",
- "minimatch": "^9.0.4",
- "semver": "^7.6.0",
- "tinyglobby": "^0.2.15",
- "ts-api-utils": "^2.1.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.50.0.tgz",
- "integrity": "sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "8.50.0",
- "eslint-visitor-keys": "^4.2.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
- "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/@typescript-eslint/parser/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/@typescript-eslint/project-service": {
- "version": "8.56.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz",
- "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.56.1",
- "@typescript-eslint/types": "^8.56.1",
- "debug": "^4.4.3"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/scope-manager": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.50.0.tgz",
- "integrity": "sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "8.50.0",
- "@typescript-eslint/visitor-keys": "8.50.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/scope-manager/node_modules/@typescript-eslint/types": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.50.0.tgz",
- "integrity": "sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/scope-manager/node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.50.0.tgz",
- "integrity": "sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "8.50.0",
- "eslint-visitor-keys": "^4.2.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.56.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz",
- "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/type-utils": {
- "version": "8.56.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz",
- "integrity": "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "8.56.1",
- "@typescript-eslint/typescript-estree": "8.56.1",
- "@typescript-eslint/utils": "8.56.1",
- "debug": "^4.4.3",
- "ts-api-utils": "^2.4.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/types": {
- "version": "8.56.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz",
- "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.56.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz",
- "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/project-service": "8.56.1",
- "@typescript-eslint/tsconfig-utils": "8.56.1",
- "@typescript-eslint/types": "8.56.1",
- "@typescript-eslint/visitor-keys": "8.56.1",
- "debug": "^4.4.3",
- "minimatch": "^10.2.2",
- "semver": "^7.7.3",
- "tinyglobby": "^0.2.15",
- "ts-api-utils": "^2.4.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree/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/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
- "version": "5.0.4",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
- "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^4.0.2"
- },
- "engines": {
- "node": "18 || 20 || >=22"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
- "version": "10.2.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
- "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "brace-expansion": "^5.0.2"
- },
- "engines": {
- "node": "18 || 20 || >=22"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/@typescript-eslint/utils": {
- "version": "8.56.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz",
- "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.9.1",
- "@typescript-eslint/scope-manager": "8.56.1",
- "@typescript-eslint/types": "8.56.1",
- "@typescript-eslint/typescript-estree": "8.56.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": {
- "version": "8.56.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz",
- "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "8.56.1",
- "@typescript-eslint/visitor-keys": "8.56.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.56.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz",
- "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "8.56.1",
- "eslint-visitor-keys": "^5.0.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
- "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^20.19.0 || ^22.13.0 || >=24"
+ "node": "^20.19.0 || ^22.13.0 || >=24"
},
"funding": {
"url": "https://opencollective.com/eslint"
@@ -4116,11 +3745,26 @@
"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
+ "dev": true,
+ "license": "Python-2.0"
},
"node_modules/aria-query": {
"version": "5.3.0",
@@ -4381,6 +4025,19 @@
"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/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -4397,7 +4054,7 @@
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
- "peer": true,
+ "license": "MIT",
"dependencies": {
"fill-range": "^7.1.1"
},
@@ -4453,27 +4110,27 @@
}
},
"node_modules/c12": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/c12/-/c12-2.0.1.tgz",
- "integrity": "sha512-Z4JgsKXHG37C6PYUtIxCfLJZvo6FyhHJoClwwb9ftUkLpPSkuYqn6Tr+vnaN8hymm0kIbcg6Ey3kv/Q71k5w/A==",
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/c12/-/c12-1.11.1.tgz",
+ "integrity": "sha512-KDU0TvSvVdaYcQKQ6iPHATGz/7p/KiVjPg4vQrB6Jg/wX9R0yl5RZxWm9IoZqaIHD2+6PZd81+KMGwRr/lRIUg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "chokidar": "^4.0.1",
+ "chokidar": "^3.6.0",
"confbox": "^0.1.7",
"defu": "^6.1.4",
"dotenv": "^16.4.5",
"giget": "^1.2.3",
- "jiti": "^2.3.0",
+ "jiti": "^1.21.6",
"mlly": "^1.7.1",
- "ohash": "^1.1.4",
+ "ohash": "^1.1.3",
"pathe": "^1.1.2",
"perfect-debounce": "^1.0.0",
- "pkg-types": "^1.2.0",
+ "pkg-types": "^1.1.1",
"rc9": "^2.1.2"
},
"peerDependencies": {
- "magicast": "^0.3.5"
+ "magicast": "^0.3.4"
},
"peerDependenciesMeta": {
"magicast": {
@@ -4481,6 +4138,16 @@
}
}
},
+ "node_modules/c12/node_modules/jiti": {
+ "version": "1.21.7",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
+ "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "bin/jiti.js"
+ }
+ },
"node_modules/call-bind": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
@@ -4539,6 +4206,19 @@
"node": ">=6"
}
},
+ "node_modules/camelcase": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz",
+ "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/caniuse-lite": {
"version": "1.0.30001776",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001776.tgz",
@@ -4595,19 +4275,41 @@
"license": "MIT"
},
"node_modules/chokidar": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
- "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+ "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": {
- "readdirp": "^4.0.1"
+ "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": ">= 14.16.0"
+ "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/chownr": {
@@ -4674,8 +4376,7 @@
"resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz",
"integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==",
"dev": true,
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/color-convert": {
"version": "2.0.1",
@@ -5023,6 +4724,14 @@
"node": ">= 0.4"
}
},
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
"node_modules/electron-to-chromium": {
"version": "1.5.307",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.307.tgz",
@@ -5834,36 +5543,6 @@
"dev": true,
"license": "Apache-2.0"
},
- "node_modules/fast-glob": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
- "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "@nodelib/fs.stat": "^2.0.2",
- "@nodelib/fs.walk": "^1.2.3",
- "glob-parent": "^5.1.2",
- "merge2": "^1.3.0",
- "micromatch": "^4.0.8"
- },
- "engines": {
- "node": ">=8.6.0"
- }
- },
- "node_modules/fast-glob/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,
- "peer": true,
- "dependencies": {
- "is-glob": "^4.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
"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",
@@ -5877,16 +5556,6 @@
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
"dev": true
},
- "node_modules/fastq": {
- "version": "1.18.0",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz",
- "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==",
- "dev": true,
- "peer": true,
- "dependencies": {
- "reusify": "^1.0.4"
- }
- },
"node_modules/file-entry-cache": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
@@ -5904,7 +5573,7 @@
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
- "peer": true,
+ "license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
},
@@ -6002,6 +5671,24 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "dev": true,
+ "license": "ISC",
+ "peer": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/form-data": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
@@ -6188,6 +5875,29 @@
"dev": true,
"license": "MIT"
},
+ "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",
+ "peer": true,
+ "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/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@@ -6200,6 +5910,34 @@
"node": ">=10.13.0"
}
},
+ "node_modules/glob/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/glob/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",
+ "peer": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/globals": {
"version": "16.5.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz",
@@ -6275,9 +6013,9 @@
}
},
"node_modules/happy-dom": {
- "version": "20.7.0",
- "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-20.7.0.tgz",
- "integrity": "sha512-hR/uLYQdngTyEfxnOoa+e6KTcfBFyc1hgFj/Cc144A5JJUuHFYqIEBDcD4FeGqUeKLRZqJ9eN9u7/GDjYEgS1g==",
+ "version": "20.8.3",
+ "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-20.8.3.tgz",
+ "integrity": "sha512-lMHQRRwIPyJ70HV0kkFT7jH/gXzSI7yDkQFe07E2flwmNDFoWUTRMKpW2sglsnpeA7b6S2TJPp98EbQxai8eaQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6526,6 +6264,19 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "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-boolean-object": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
@@ -6646,6 +6397,17 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "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",
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-generator-function": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz",
@@ -6695,7 +6457,7 @@
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
- "peer": true,
+ "license": "MIT",
"engines": {
"node": ">=0.12.0"
}
@@ -6893,6 +6655,23 @@
"node": ">= 0.4"
}
},
+ "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",
+ "peer": true,
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
"node_modules/javascript-natural-sort": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
@@ -6900,26 +6679,17 @@
"dev": true,
"license": "MIT"
},
- "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=="
},
"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==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},
@@ -7121,30 +6891,6 @@
"node": ">= 0.4"
}
},
- "node_modules/merge2": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
- "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
- "dev": true,
- "peer": true,
- "engines": {
- "node": ">= 8"
- }
- },
- "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,
- "peer": true,
- "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",
@@ -7199,13 +6945,14 @@
}
},
"node_modules/minipass": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
- "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
"dev": true,
- "license": "ISC",
+ "license": "BlueOak-1.0.0",
+ "peer": true,
"engines": {
- "node": ">=8"
+ "node": ">=16 || 14 >=14.17"
}
},
"node_modules/minizlib": {
@@ -7339,6 +7086,16 @@
"dev": true,
"license": "MIT"
},
+ "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/nypm": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/nypm/-/nypm-0.5.4.tgz",
@@ -7558,6 +7315,14 @@
"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",
+ "peer": true
+ },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -7610,8 +7375,7 @@
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
"integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
"dev": true,
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/path-exists": {
"version": "4.0.0",
@@ -7636,6 +7400,32 @@
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
+ "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",
+ "peer": true,
+ "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/path-scurry/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",
+ "peer": true
+ },
"node_modules/path-type": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
@@ -7675,7 +7465,7 @@
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
- "peer": true,
+ "license": "MIT",
"engines": {
"node": ">=8.6"
},
@@ -7866,27 +7656,6 @@
"node": ">=6"
}
},
- "node_modules/queue-microtask": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
- "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
- "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"
- }
- ],
- "peer": true
- },
"node_modules/rc9": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz",
@@ -7993,17 +7762,16 @@
}
},
"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==",
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
"license": "MIT",
- "engines": {
- "node": ">= 14.18.0"
+ "dependencies": {
+ "picomatch": "^2.2.1"
},
- "funding": {
- "type": "individual",
- "url": "https://paulmillr.com/funding/"
+ "engines": {
+ "node": ">=8.10.0"
}
},
"node_modules/redent": {
@@ -8120,17 +7888,6 @@
"node": ">=4"
}
},
- "node_modules/reusify": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
- "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
- "dev": true,
- "peer": true,
- "engines": {
- "iojs": ">=1.0.0",
- "node": ">=0.10.0"
- }
- },
"node_modules/rollup": {
"version": "4.48.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.48.1.tgz",
@@ -8171,30 +7928,6 @@
"fsevents": "~2.3.2"
}
},
- "node_modules/run-parallel": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
- "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
- "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"
- }
- ],
- "peer": true,
- "dependencies": {
- "queue-microtask": "^1.2.2"
- }
- },
"node_modules/safe-array-concat": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
@@ -8427,6 +8160,20 @@
"integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
"dev": true
},
+ "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",
+ "peer": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
@@ -8459,6 +8206,64 @@
"dev": true,
"license": "MIT"
},
+ "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",
+ "peer": true,
+ "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",
+ "peer": true,
+ "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/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",
+ "peer": true
+ },
+ "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",
+ "peer": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/string.prototype.includes": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
@@ -8572,6 +8377,52 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "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",
+ "peer": true,
+ "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",
+ "peer": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi/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",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
"node_modules/strip-indent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
@@ -8649,6 +8500,16 @@
"node": ">=10"
}
},
+ "node_modules/tar/node_modules/minipass": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
+ "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/tinybench": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
@@ -8659,7 +8520,8 @@
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
"integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/tinyglobby": {
"version": "0.2.15",
@@ -8724,7 +8586,7 @@
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
- "peer": true,
+ "license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
},
@@ -8746,15 +8608,14 @@
}
},
"node_modules/ts-morph": {
- "version": "23.0.0",
- "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-23.0.0.tgz",
- "integrity": "sha512-FcvFx7a9E8TUe6T3ShihXJLiJOiqyafzFKUO4aqIHDUCIvADdGNShcbc2W5PMr3LerXRv7mafvFZ9lRENxJmug==",
+ "version": "27.0.2",
+ "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-27.0.2.tgz",
+ "integrity": "sha512-fhUhgeljcrdZ+9DZND1De1029PrE+cMkIP7ooqkLRTrRLTqcki2AstsyJm0vRNbTbVCNJ0idGlbBrfqc7/nA8w==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
- "@ts-morph/common": "~0.24.0",
- "code-block-writer": "^13.0.1"
+ "@ts-morph/common": "~0.28.1",
+ "code-block-writer": "^13.0.3"
}
},
"node_modules/tslib": {
@@ -8891,88 +8752,6 @@
"typescript": ">=4.8.4 <6.0.0"
}
},
- "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.56.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz",
- "integrity": "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/regexpp": "^4.12.2",
- "@typescript-eslint/scope-manager": "8.56.1",
- "@typescript-eslint/type-utils": "8.56.1",
- "@typescript-eslint/utils": "8.56.1",
- "@typescript-eslint/visitor-keys": "8.56.1",
- "ignore": "^7.0.5",
- "natural-compare": "^1.4.0",
- "ts-api-utils": "^2.4.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "@typescript-eslint/parser": "^8.56.1",
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": {
- "version": "8.56.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz",
- "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/scope-manager": "8.56.1",
- "@typescript-eslint/types": "8.56.1",
- "@typescript-eslint/typescript-estree": "8.56.1",
- "@typescript-eslint/visitor-keys": "8.56.1",
- "debug": "^4.4.3"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.0.0"
- }
- },
- "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": {
- "version": "8.56.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz",
- "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "8.56.1",
- "@typescript-eslint/visitor-keys": "8.56.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- }
- },
- "node_modules/typescript-eslint/node_modules/ignore": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
- "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 4"
- }
- },
"node_modules/ufo": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz",
@@ -9445,6 +9224,97 @@
"dev": true,
"license": "MIT"
},
+ "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",
+ "peer": true,
+ "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",
+ "peer": true,
+ "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/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",
+ "peer": true
+ },
+ "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",
+ "peer": true,
+ "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",
+ "peer": true,
+ "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",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
"node_modules/ws": {
"version": "8.19.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
diff --git a/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/package.json b/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/package.json
index bc466acd35812..53d8f9d98ab47 100644
--- a/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/package.json
+++ b/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/package.json
@@ -15,7 +15,7 @@
"coverage": "vitest run --coverage"
},
"dependencies": {
- "@chakra-ui/react": "^3.33.0",
+ "@chakra-ui/react": "^3.34.0",
"@tanstack/react-query": "^5.90.21",
"axios": "^1.13.5",
"next-themes": "^0.4.6",
@@ -48,7 +48,7 @@
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.2",
"eslint-plugin-unicorn": "^63.0.0",
- "happy-dom": "^20.7.0",
+ "happy-dom": "^20.8.3",
"prettier": "^3.8.1",
"ts-morph": "^27.0.2",
"typescript": "~5.9.3",
diff --git a/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/pnpm-lock.yaml b/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/pnpm-lock.yaml
index 89d4ad356c10d..3e4dcd2e75f43 100644
--- a/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/pnpm-lock.yaml
+++ b/airflow-core/src/airflow/api_fastapi/auth/managers/simple/ui/pnpm-lock.yaml
@@ -15,8 +15,8 @@ importers:
.:
dependencies:
'@chakra-ui/react':
- specifier: ^3.33.0
- version: 3.33.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
+ specifier: ^3.34.0
+ version: 3.34.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@tanstack/react-query':
specifier: ^5.90.21
version: 5.90.21(react@19.2.4)
@@ -80,7 +80,7 @@ importers:
version: 8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.3)
'@vitejs/plugin-react-swc':
specifier: ^4.2.3
- version: 4.2.3(@swc/helpers@0.5.19)(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1))
+ version: 4.2.3(@swc/helpers@0.5.19)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1))
eslint:
specifier: ^10.0.2
version: 10.0.2(jiti@2.6.1)
@@ -109,8 +109,8 @@ importers:
specifier: ^63.0.0
version: 63.0.0(eslint@10.0.2(jiti@2.6.1))
happy-dom:
- specifier: ^20.7.0
- version: 20.7.0
+ specifier: ^20.8.3
+ version: 20.8.3
prettier:
specifier: ^3.8.1
version: 3.8.1
@@ -125,13 +125,13 @@ importers:
version: 8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.3)
vite:
specifier: ^7.3.1
- version: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)
+ version: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)
vite-plugin-css-injected-by-js:
specifier: ^4.0.1
- version: 4.0.1(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1))
+ version: 4.0.1(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1))
vitest:
specifier: ^4.0.18
- version: 4.0.18(@types/node@25.3.3)(happy-dom@20.7.0)(jiti@2.6.1)
+ version: 4.0.18(@types/node@25.3.5)(happy-dom@20.8.3)(jiti@2.6.1)
packages:
@@ -229,8 +229,8 @@ packages:
resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
engines: {node: '>=6.9.0'}
- '@chakra-ui/react@3.33.0':
- resolution: {integrity: sha512-HNbUFsFABjVL5IHBxsqtuT+AH/vQT1+xsEWrxnG0GBM2VjlzlMqlqCxNiDyQOsjLZXQC1ciCMbzPNcSCc63Y9w==}
+ '@chakra-ui/react@3.34.0':
+ resolution: {integrity: sha512-VLhpVwv5IVxhwajO10KnS1VQT4hDqQMQP/A796Ya+uVu8AdoSX+5HHyTLTkYIeXIDMe0xLqJfov04OBKbBchJA==}
peerDependencies:
'@emotion/react': '>=11'
react: '>=18'
@@ -549,8 +549,8 @@ packages:
'@jsdevtools/ono@7.1.3':
resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==}
- '@pandacss/is-valid-prop@1.8.2':
- resolution: {integrity: sha512-wfZ4kzvHXQ9pG3wuIGTUCcbC7Op8pqIQZNIRY/bqWQu67WTHxZUHUPSZgQMhguI8Tz4ot+DNf4Qhha0bhLvNEQ==}
+ '@pandacss/is-valid-prop@1.9.0':
+ resolution: {integrity: sha512-AZvpXWGyjbHc8TC+YVloQ31Z2c4j2xMvYj6UfVxuZdB5w4c9+4N8wy5R7I/XswNh8e4cfUlkvsEGDXjhJRgypw==}
'@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
@@ -597,79 +597,66 @@ packages:
resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==}
cpu: [arm]
os: [linux]
- libc: [glibc]
'@rollup/rollup-linux-arm-musleabihf@4.59.0':
resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==}
cpu: [arm]
os: [linux]
- libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.59.0':
resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.59.0':
resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@rollup/rollup-linux-loong64-gnu@4.59.0':
resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==}
cpu: [loong64]
os: [linux]
- libc: [glibc]
'@rollup/rollup-linux-loong64-musl@4.59.0':
resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==}
cpu: [loong64]
os: [linux]
- libc: [musl]
'@rollup/rollup-linux-ppc64-gnu@4.59.0':
resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==}
cpu: [ppc64]
os: [linux]
- libc: [glibc]
'@rollup/rollup-linux-ppc64-musl@4.59.0':
resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==}
cpu: [ppc64]
os: [linux]
- libc: [musl]
'@rollup/rollup-linux-riscv64-gnu@4.59.0':
resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==}
cpu: [riscv64]
os: [linux]
- libc: [glibc]
'@rollup/rollup-linux-riscv64-musl@4.59.0':
resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==}
cpu: [riscv64]
os: [linux]
- libc: [musl]
'@rollup/rollup-linux-s390x-gnu@4.59.0':
resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==}
cpu: [s390x]
os: [linux]
- libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.59.0':
resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.59.0':
resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==}
cpu: [x64]
os: [linux]
- libc: [musl]
'@rollup/rollup-openbsd-x64@4.59.0':
resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==}
@@ -733,28 +720,24 @@ packages:
engines: {node: '>=10'}
cpu: [arm64]
os: [linux]
- libc: [glibc]
'@swc/core-linux-arm64-musl@1.15.18':
resolution: {integrity: sha512-0a+Lix+FSSHBSBOA0XznCcHo5/1nA6oLLjcnocvzXeqtdjnPb+SvchItHI+lfeiuj1sClYPDvPMLSLyXFaiIKw==}
engines: {node: '>=10'}
cpu: [arm64]
os: [linux]
- libc: [musl]
'@swc/core-linux-x64-gnu@1.15.18':
resolution: {integrity: sha512-wG9J8vReUlpaHz4KOD/5UE1AUgirimU4UFT9oZmupUDEofxJKYb1mTA/DrMj0s78bkBiNI+7Fo2EgPuvOJfuAA==}
engines: {node: '>=10'}
cpu: [x64]
os: [linux]
- libc: [glibc]
'@swc/core-linux-x64-musl@1.15.18':
resolution: {integrity: sha512-4nwbVvCphKzicwNWRmvD5iBaZj8JYsRGa4xOxJmOyHlMDpsvvJ2OR2cODlvWyGFH6BYL1MfIAK3qph3hp0Az6g==}
engines: {node: '>=10'}
cpu: [x64]
os: [linux]
- libc: [musl]
'@swc/core-win32-arm64-msvc@1.15.18':
resolution: {integrity: sha512-zk0RYO+LjiBCat2RTMHzAWaMky0cra9loH4oRrLKLLNuL+jarxKLFDA8xTZWEkCPLjUTwlRN7d28eDLLMgtUcQ==}
@@ -866,8 +849,8 @@ packages:
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
- '@types/node@25.3.3':
- resolution: {integrity: sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==}
+ '@types/node@25.3.5':
+ resolution: {integrity: sha512-oX8xrhvpiyRCQkG1MFchB09f+cXftgIXb3a7UUa4Y3wpmZPw5tyZGTLWhlESOLq1Rq6oDlc8npVU2/9xiCuXMA==}
'@types/parse-json@4.0.2':
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
@@ -1863,8 +1846,8 @@ packages:
engines: {node: '>=0.4.7'}
hasBin: true
- happy-dom@20.7.0:
- resolution: {integrity: sha512-hR/uLYQdngTyEfxnOoa+e6KTcfBFyc1hgFj/Cc144A5JJUuHFYqIEBDcD4FeGqUeKLRZqJ9eN9u7/GDjYEgS1g==}
+ happy-dom@20.8.3:
+ resolution: {integrity: sha512-lMHQRRwIPyJ70HV0kkFT7jH/gXzSI7yDkQFe07E2flwmNDFoWUTRMKpW2sglsnpeA7b6S2TJPp98EbQxai8eaQ==}
engines: {node: '>=20.0.0'}
has-bigints@1.1.0:
@@ -3073,7 +3056,7 @@ snapshots:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.28.5
- '@chakra-ui/react@3.33.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
+ '@chakra-ui/react@3.34.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)':
dependencies:
'@ark-ui/react': 5.34.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
'@emotion/is-prop-valid': 1.4.0
@@ -3081,7 +3064,7 @@ snapshots:
'@emotion/serialize': 1.3.3
'@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.4)
'@emotion/utils': 1.4.2
- '@pandacss/is-valid-prop': 1.8.2
+ '@pandacss/is-valid-prop': 1.9.0
csstype: 3.2.3
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
@@ -3347,7 +3330,7 @@ snapshots:
'@jsdevtools/ono@7.1.3': {}
- '@pandacss/is-valid-prop@1.8.2': {}
+ '@pandacss/is-valid-prop@1.9.0': {}
'@pkgjs/parseargs@0.11.0':
optional: true
@@ -3577,7 +3560,7 @@ snapshots:
'@types/json-schema@7.0.15': {}
- '@types/node@25.3.3':
+ '@types/node@25.3.5':
dependencies:
undici-types: 7.18.2
@@ -3595,7 +3578,7 @@ snapshots:
'@types/ws@8.18.1':
dependencies:
- '@types/node': 25.3.3
+ '@types/node': 25.3.5
'@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
@@ -3688,11 +3671,11 @@ snapshots:
'@typescript-eslint/types': 8.56.1
eslint-visitor-keys: 5.0.1
- '@vitejs/plugin-react-swc@4.2.3(@swc/helpers@0.5.19)(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1))':
+ '@vitejs/plugin-react-swc@4.2.3(@swc/helpers@0.5.19)(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1))':
dependencies:
'@rolldown/pluginutils': 1.0.0-rc.2
'@swc/core': 1.15.18(@swc/helpers@0.5.19)
- vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)
+ vite: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)
transitivePeerDependencies:
- '@swc/helpers'
@@ -3705,13 +3688,13 @@ snapshots:
chai: 6.2.2
tinyrainbow: 3.0.3
- '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1))':
+ '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1))':
dependencies:
'@vitest/spy': 4.0.18
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
- vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)
+ vite: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)
'@vitest/pretty-format@4.0.18':
dependencies:
@@ -5105,9 +5088,9 @@ snapshots:
optionalDependencies:
uglify-js: 3.19.3
- happy-dom@20.7.0:
+ happy-dom@20.8.3:
dependencies:
- '@types/node': 25.3.3
+ '@types/node': 25.3.5
'@types/whatwg-mimetype': 3.0.2
'@types/ws': 8.18.1
entities: 7.0.1
@@ -6030,11 +6013,11 @@ snapshots:
dependencies:
punycode: 2.3.1
- vite-plugin-css-injected-by-js@4.0.1(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)):
+ vite-plugin-css-injected-by-js@4.0.1(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1)):
dependencies:
- vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)
+ vite: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)
- vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1):
+ vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1):
dependencies:
esbuild: 0.27.3
fdir: 6.5.0(picomatch@4.0.3)
@@ -6043,14 +6026,14 @@ snapshots:
rollup: 4.59.0
tinyglobby: 0.2.15
optionalDependencies:
- '@types/node': 25.3.3
+ '@types/node': 25.3.5
fsevents: 2.3.3
jiti: 2.6.1
- vitest@4.0.18(@types/node@25.3.3)(happy-dom@20.7.0)(jiti@2.6.1):
+ vitest@4.0.18(@types/node@25.3.5)(happy-dom@20.8.3)(jiti@2.6.1):
dependencies:
'@vitest/expect': 4.0.18
- '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1))
+ '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.5)(jiti@2.6.1))
'@vitest/pretty-format': 4.0.18
'@vitest/runner': 4.0.18
'@vitest/snapshot': 4.0.18
@@ -6067,11 +6050,11 @@ snapshots:
tinyexec: 1.0.2
tinyglobby: 0.2.15
tinyrainbow: 3.0.3
- vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)
+ vite: 7.3.1(@types/node@25.3.5)(jiti@2.6.1)
why-is-node-running: 2.3.0
optionalDependencies:
- '@types/node': 25.3.3
- happy-dom: 20.7.0
+ '@types/node': 25.3.5
+ happy-dom: 20.8.3
transitivePeerDependencies:
- jiti
- less
diff --git a/airflow-core/src/airflow/api_fastapi/common/db/dag_runs.py b/airflow-core/src/airflow/api_fastapi/common/db/dag_runs.py
index 2a5fb76c8b850..a9ccf6031c786 100644
--- a/airflow-core/src/airflow/api_fastapi/common/db/dag_runs.py
+++ b/airflow-core/src/airflow/api_fastapi/common/db/dag_runs.py
@@ -41,13 +41,24 @@
def eager_load_dag_run_for_validation() -> tuple[LoaderOption, ...]:
- """Construct the eager loading options necessary for a DagRunResponse object."""
+ """
+ Construct the eager loading options necessary for a DagRunResponse object.
+
+ For the list endpoint (get_dag_runs), loading all task instance columns is
+ wasteful because we only need the dag_version_id FK to traverse to DagVersion.
+ Using load_only() on TaskInstance and TaskInstanceHistory restricts the SELECT
+ to just the identity columns and dag_version_id, avoiding large intermediate
+ result sets caused by loading heavyweight columns (executor_config, etc.) for
+ every task instance across every DAG run returned by the query.
+ """
return (
joinedload(DagRun.dag_model),
selectinload(DagRun.task_instances)
+ .load_only(TaskInstance.dag_version_id)
.joinedload(TaskInstance.dag_version)
.joinedload(DagVersion.bundle),
selectinload(DagRun.task_instances_histories)
+ .load_only(TaskInstanceHistory.dag_version_id)
.joinedload(TaskInstanceHistory.dag_version)
.joinedload(DagVersion.bundle),
joinedload(DagRun.dag_run_note),
diff --git a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dags.py b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dags.py
index 4cd3ed93817ea..294092c663995 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dags.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dags.py
@@ -20,6 +20,7 @@
import inspect
from collections import abc
from datetime import datetime, timedelta
+from functools import cache
from typing import Any
from itsdangerous import URLSafeSerializer
@@ -38,6 +39,19 @@
from airflow.configuration import conf
from airflow.models.dag_version import DagVersion
+
+@cache
+def _get_file_token_serializer() -> URLSafeSerializer:
+ """
+ Return a cached URLSafeSerializer instance.
+
+ Uses @cache for lazy initialization - the serializer is created on first
+ call rather than at module import time. This avoids issues if the module
+ is imported before configuration is fully loaded.
+ """
+ return URLSafeSerializer(conf.get_mandatory_value("api", "secret_key"))
+
+
DAG_ALIAS_MAPPING: dict[str, str] = {
# The keys are the names in the response, the values are the original names in the model
# This is used to map the names in the response to the names in the model
@@ -113,12 +127,11 @@ def get_timetable_summary(cls, tts: str | None) -> str | None:
@property
def file_token(self) -> str:
"""Return file token."""
- serializer = URLSafeSerializer(conf.get_mandatory_value("api", "secret_key"))
payload = {
"bundle_name": self.bundle_name,
"relative_fileloc": self.relative_fileloc,
}
- return serializer.dumps(payload)
+ return _get_file_token_serializer().dumps(payload)
class DAGPatchBody(StrictBaseModel):
diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/assets.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/assets.py
index b1bafae7766f8..714b77c65b97d 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/routes/public/assets.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/public/assets.py
@@ -25,6 +25,8 @@
from sqlalchemy.orm import joinedload, subqueryload
from airflow._shared.timezones import timezone
+from airflow.api_fastapi.app import get_auth_manager
+from airflow.api_fastapi.auth.managers.models.resource_details import DagAccessEntity, DagDetails
from airflow.api_fastapi.common.dagbag import DagBagDep, get_latest_version_of_dag
from airflow.api_fastapi.common.db.common import SessionDep, paginated_select
from airflow.api_fastapi.common.parameters import (
@@ -372,6 +374,17 @@ def materialize_asset(
f"More than one DAG materializes asset with ID: {asset_id}",
)
+ if not get_auth_manager().is_authorized_dag(
+ method="POST",
+ access_entity=DagAccessEntity.RUN,
+ details=DagDetails(id=dag_id),
+ user=user,
+ ):
+ raise HTTPException(
+ status.HTTP_403_FORBIDDEN,
+ f"User is not authorized to trigger a run for DAG: {dag_id} that materializes this asset",
+ )
+
dag = get_latest_version_of_dag(dag_bag, dag_id, session)
return dag.create_dagrun(
run_id=dag.timetable.generate_run_id(
diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/backfills.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/backfills.py
index 9d1630689f76e..753c5427253f0 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/backfills.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/backfills.py
@@ -36,7 +36,7 @@
from airflow.api_fastapi.core_api.openapi.exceptions import (
create_openapi_http_exception_doc,
)
-from airflow.api_fastapi.core_api.security import requires_access_backfill
+from airflow.api_fastapi.core_api.security import ReadableBackfillsFilterDep, requires_access_backfill
from airflow.models.backfill import Backfill
backfills_router = AirflowRouter(tags=["Backfill"], prefix="/backfills")
@@ -56,6 +56,7 @@ def list_backfills_ui(
SortParam,
Depends(SortParam(["id"], Backfill).dynamic_depends()),
],
+ readable_backfills_filter: ReadableBackfillsFilterDep,
session: SessionDep,
dag_id: Annotated[FilterParam[str | None], Depends(filter_param_factory(Backfill.dag_id, str | None))],
active: Annotated[
@@ -65,7 +66,7 @@ def list_backfills_ui(
) -> BackfillCollectionResponse:
select_stmt, total_entries = paginated_select(
statement=select(Backfill).options(joinedload(Backfill.dag_model)),
- filters=[dag_id, active],
+ filters=[dag_id, active, readable_backfills_filter],
order_by=order_by,
offset=offset,
limit=limit,
diff --git a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/dags.py b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/dags.py
index e1af93e746ebb..b404534cc7f47 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/dags.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/routes/ui/dags.py
@@ -52,7 +52,7 @@
filter_param_factory,
)
from airflow.api_fastapi.common.router import AirflowRouter
-from airflow.api_fastapi.core_api.datamodels.dags import DAGResponse
+from airflow.api_fastapi.core_api.datamodels.dags import DAG_ALIAS_MAPPING, DAGResponse
from airflow.api_fastapi.core_api.datamodels.ui.dag_runs import DAGRunLightResponse
from airflow.api_fastapi.core_api.datamodels.ui.dags import (
DAGWithLatestDagRunsCollectionResponse,
@@ -234,18 +234,25 @@ def get_dags(
pending_actions_by_dag_id[dag_id].append(hitl_detail)
# aggregate rows by dag_id
- dag_runs_by_dag_id: dict[str, DAGWithLatestDagRunsResponse] = {
- dag.dag_id: DAGWithLatestDagRunsResponse.model_validate(
+ # Build the dict dynamically from DAGResponse.model_fields so that new fields
+ # added to DAGResponse are picked up automatically without code changes here.
+ dag_runs_by_dag_id: dict[str, DAGWithLatestDagRunsResponse] = {}
+ for dag in dags:
+ dag_data = {
+ DAG_ALIAS_MAPPING.get(field_name, field_name): getattr(
+ dag, DAG_ALIAS_MAPPING.get(field_name, field_name)
+ )
+ for field_name in DAGResponse.model_fields
+ }
+ dag_data.update(
{
- **DAGResponse.model_validate(dag).model_dump(),
"asset_expression": dag.asset_expression,
"latest_dag_runs": [],
"pending_actions": pending_actions_by_dag_id[dag.dag_id],
"is_favorite": dag.dag_id in favorite_dag_ids,
}
)
- for dag in dags
- }
+ dag_runs_by_dag_id[dag.dag_id] = DAGWithLatestDagRunsResponse.model_validate(dag_data)
for row in recent_dag_runs:
dag_run_response = DAGRunLightResponse.model_validate(row)
diff --git a/airflow-core/src/airflow/api_fastapi/core_api/security.py b/airflow-core/src/airflow/api_fastapi/core_api/security.py
index 4215baaa7712f..26b0e5bae61f7 100644
--- a/airflow-core/src/airflow/api_fastapi/core_api/security.py
+++ b/airflow-core/src/airflow/api_fastapi/core_api/security.py
@@ -219,6 +219,13 @@ def to_orm(self, select: Select) -> Select:
return select.where(DagVersion.dag_id.in_(self.value or set()))
+class PermittedBackfillFilter(PermittedDagFilter):
+ """A parameter that filters the permitted backfills for the user."""
+
+ def to_orm(self, select: Select) -> Select:
+ return select.where(Backfill.dag_id.in_(self.value or set()))
+
+
def permitted_dag_filter_factory(
method: ResourceMethod, filter_class=PermittedDagFilter
) -> Callable[[Request, BaseUser], PermittedDagFilter]:
@@ -264,6 +271,9 @@ def depends_permitted_dags_filter(
ReadableDagVersionsFilterDep = Annotated[
PermittedDagVersionFilter, Depends(permitted_dag_filter_factory("GET", PermittedDagVersionFilter))
]
+ReadableBackfillsFilterDep = Annotated[
+ PermittedBackfillFilter, Depends(permitted_dag_filter_factory("GET", PermittedBackfillFilter))
+]
def requires_access_backfill(
diff --git a/airflow-core/src/airflow/api_fastapi/execution_api/routes/task_instances.py b/airflow-core/src/airflow/api_fastapi/execution_api/routes/task_instances.py
index 54c777fc59a44..93f8fc153414b 100644
--- a/airflow-core/src/airflow/api_fastapi/execution_api/routes/task_instances.py
+++ b/airflow-core/src/airflow/api_fastapi/execution_api/routes/task_instances.py
@@ -135,7 +135,7 @@ def ti_run(
TI.hostname,
TI.unixname,
TI.pid,
- # This selects the raw JSON value, by-passing the deserialization -- we want that to happen on the
+ # This selects the raw JSON value, bypassing the deserialization -- we want that to happen on the
# client
column("next_kwargs", JSON),
)
diff --git a/airflow-core/src/airflow/example_dags/example_xcom.py b/airflow-core/src/airflow/example_dags/example_xcom.py
index 8b8bc5bebd770..8843537964239 100644
--- a/airflow-core/src/airflow/example_dags/example_xcom.py
+++ b/airflow-core/src/airflow/example_dags/example_xcom.py
@@ -78,12 +78,22 @@ def pull_value_from_bash_push(ti=None):
'echo "value_by_return"',
)
+ # This example shows a safe way of passing XCom values to a BashOperator via environment variables.
+ # The values are templated into the bash_command and then set as environment variables for the
+ # command to use. This is a recommended pattern for passing XCom values to BashOperator, as it avoids
+ # issues with quoting and escaping that can arise when trying to directly template XCom values
+ # into.
bash_pull = BashOperator(
task_id="bash_pull",
bash_command='echo "bash pull demo" && '
- f'echo "The xcom pushed manually is {XComArg(bash_push, key="manually_pushed_value")}" && '
- f'echo "The returned_value xcom is {XComArg(bash_push)}" && '
+ "echo \"The xcom pushed manually is '$MANUALLY_PUSHED_VALUE'\" && "
+ "echo \"The returned_value xcom is '$RETURNED_VALUE'\" && "
'echo "finished"',
+ env={
+ "MANUALLY_PUSHED_VALUE": str(XComArg(bash_push, key="manually_pushed_value")),
+ "RETURNED_VALUE": str(XComArg(bash_push)),
+ },
+ append_env=True,
do_xcom_push=False,
)
diff --git a/airflow-core/src/airflow/executors/workloads.py b/airflow-core/src/airflow/executors/workloads.py
index 43a4aab1dbc47..bafb429dcc10c 100644
--- a/airflow-core/src/airflow/executors/workloads.py
+++ b/airflow-core/src/airflow/executors/workloads.py
@@ -40,7 +40,7 @@
class BaseWorkload(BaseModel):
- token: str
+ token: str = Field(repr=False)
"""The identity token for this workload"""
diff --git a/airflow-core/src/airflow/migrations/versions/0087_3_1_8_change_signed_url_template_from_varchar_.py b/airflow-core/src/airflow/migrations/versions/0087_3_1_8_change_signed_url_template_from_varchar_.py
index 9fae0722c8ca3..7966e10d1f2f2 100644
--- a/airflow-core/src/airflow/migrations/versions/0087_3_1_8_change_signed_url_template_from_varchar_.py
+++ b/airflow-core/src/airflow/migrations/versions/0087_3_1_8_change_signed_url_template_from_varchar_.py
@@ -30,6 +30,8 @@
import sqlalchemy as sa
from alembic import op
+from airflow.migrations.utils import disable_sqlite_fkeys
+
# revision identifiers, used by Alembic.
revision = "509b94a1042d"
down_revision = "82dbd68e6171"
@@ -40,27 +42,23 @@
def upgrade():
"""Apply Change signed_url_template from VARCHAR(200) to TEXT."""
- # ### commands auto generated by Alembic - please adjust! ###
- with op.batch_alter_table("dag_bundle", schema=None) as batch_op:
- batch_op.alter_column(
- "signed_url_template",
- existing_type=sa.VARCHAR(length=200),
- type_=sa.Text(),
- existing_nullable=True,
- )
-
- # ### end Alembic commands ###
+ with disable_sqlite_fkeys(op):
+ with op.batch_alter_table("dag_bundle", schema=None) as batch_op:
+ batch_op.alter_column(
+ "signed_url_template",
+ existing_type=sa.VARCHAR(length=200),
+ type_=sa.Text(),
+ existing_nullable=True,
+ )
def downgrade():
"""Unapply Change signed_url_template from VARCHAR(200) to TEXT."""
- # ### commands auto generated by Alembic - please adjust! ###
- with op.batch_alter_table("dag_bundle", schema=None) as batch_op:
- batch_op.alter_column(
- "signed_url_template",
- existing_type=sa.Text(),
- type_=sa.VARCHAR(length=200),
- existing_nullable=True,
- )
-
- # ### end Alembic commands ###
+ with disable_sqlite_fkeys(op):
+ with op.batch_alter_table("dag_bundle", schema=None) as batch_op:
+ batch_op.alter_column(
+ "signed_url_template",
+ existing_type=sa.Text(),
+ type_=sa.VARCHAR(length=200),
+ existing_nullable=True,
+ )
diff --git a/airflow-core/src/airflow/models/serialized_dag.py b/airflow-core/src/airflow/models/serialized_dag.py
index e714361df9d8f..401809c5087a7 100644
--- a/airflow-core/src/airflow/models/serialized_dag.py
+++ b/airflow-core/src/airflow/models/serialized_dag.py
@@ -432,7 +432,15 @@ def write_dag(
has_task_instances: bool = False
if dag_version:
has_task_instances = bool(
- session.scalar(select(exists().where(TaskInstance.dag_version_id == dag_version.id)))
+ session.scalar(
+ select(
+ exists().where(
+ # Using dag_id filter to speed up query via the composite index.
+ TaskInstance.dag_id == dag.dag_id,
+ TaskInstance.dag_version_id == dag_version.id,
+ )
+ )
+ )
)
if dag_version and not has_task_instances:
diff --git a/airflow-core/src/airflow/models/taskinstance.py b/airflow-core/src/airflow/models/taskinstance.py
index 7d58046d03ac2..95a4430f9acbf 100644
--- a/airflow-core/src/airflow/models/taskinstance.py
+++ b/airflow-core/src/airflow/models/taskinstance.py
@@ -1847,6 +1847,7 @@ def xcom_pull(
) -> Any:
""":meta private:""" # noqa: D400
# This is only kept for compatibility in tests for now while AIP-72 is in progress.
+
if dag_id is None:
dag_id = self.dag_id
if run_id is None:
@@ -1878,12 +1879,15 @@ def xcom_pull(
).first()
if first is None: # No matching XCom at all.
return default
+
if map_indexes is not None or first.map_index < 0:
return XComModel.deserialize_value(first)
- # raise RuntimeError("Nothing should hit this anymore")
-
- # TODO: TaskSDK: We should remove this, but many tests still currently call `ti.run()`. See #45549
+ return LazyXComSelectSequence.from_select(
+ query.with_only_columns(XComModel.value).order_by(None),
+ order_by=[XComModel.map_index.expression],
+ session=session,
+ )
# At this point either task_ids or map_indexes is explicitly multi-value.
# Order return values to match task_ids and map_indexes ordering.
diff --git a/airflow-core/src/airflow/ui/public/i18n/README.md b/airflow-core/src/airflow/ui/public/i18n/README.md
index e219db4e8302e..a58230a42eab1 100644
--- a/airflow-core/src/airflow/ui/public/i18n/README.md
+++ b/airflow-core/src/airflow/ui/public/i18n/README.md
@@ -332,16 +332,16 @@ Adding missing translations (with `TODO: translate` prefix):
breeze ui check-translation-completeness --language --add-missing
```
-You can also remove extra translations from the language of your choice:
+You can also remove unused translations from the language of your choice:
```bash
-breeze ui check-translation-completeness --language --remove-extra
+breeze ui check-translation-completeness --language --remove-unused
```
Or from all languages:
```bash
-breeze ui check-translation-completeness --remove-extra
+breeze ui check-translation-completeness --remove-unused
```
The script is also added as a prek hook (manual) so that it can be run from within `prek` and CI:
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/en/common.json b/airflow-core/src/airflow/ui/public/i18n/locales/en/common.json
index 0a59f20347358..e55c81ee5b009 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/en/common.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/en/common.json
@@ -288,6 +288,10 @@
"title": "Delete {{resourceName}} Request Submitted"
}
},
+ "forbidden": {
+ "description": "You do not have permission to perform this action.",
+ "title": "Access Denied"
+ },
"import": {
"error": "Import {{resourceName}} Request Failed",
"success": {
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/tr/common.json b/airflow-core/src/airflow/ui/public/i18n/locales/tr/common.json
index 32c66539b6473..1024676c131b6 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/tr/common.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/tr/common.json
@@ -145,15 +145,6 @@
"placeholder": "Not ekle...",
"taskInstance": "Görev Örneği Notu"
},
- "pools": {
- "deferred": "ErtelenmiÅŸ",
- "open": "Açık",
- "pools_one": "Havuz",
- "pools_other": "Havuzlar",
- "queued": "Kuyrukta",
- "running": "Çalışıyor",
- "scheduled": "Planlanmış"
- },
"reset": "Sıfırla",
"runId": "Çalıştırma Kimliği",
"runTypes": {
@@ -192,6 +183,7 @@
"failed": "Başarısız",
"no_status": "Durum Yok",
"none": "Durum Yok",
+ "open": "Açık",
"planned": "Planlanmış",
"queued": "Kuyrukta",
"removed": "Kaldırılmış",
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/tr/components.json b/airflow-core/src/airflow/ui/public/i18n/locales/tr/components.json
index b40523e33065e..67b2f0f2b954a 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/tr/components.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/tr/components.json
@@ -10,6 +10,7 @@
"maxRuns": "Maksimum Aktif Çalıştırma",
"missingAndErroredRuns": "Eksik ve Hata Almış Dag Çalıştırmaları",
"missingRuns": "Eksik Dag Çalıştırmaları",
+ "permissionDenied": "Ön çalıştırma başarısız oldu: İzin reddedildi. Lütfen gerekli izinlere sahip olduğunuzdan emin olun.",
"reprocessBehavior": "Yeniden İşleme Davranışı",
"run": "Geriye Dönük Çalıştır",
"selectDescription": "Bu Dag'ı bir tarih aralığı için çalıştır",
@@ -116,6 +117,9 @@
"selectLabel": "Tek Çalıştırma",
"title": "Dag Tetikle",
"toaster": {
+ "error": {
+ "title": "Dag tetikleme başarısız oldu."
+ },
"success": {
"description": "Dag çalıştırması başarıyla tetiklendi.",
"title": "Dag Çalıştırması Tetiklendi"
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/tr/dag.json b/airflow-core/src/airflow/ui/public/i18n/locales/tr/dag.json
index 987391a63da96..a4e04fd52e7d2 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/tr/dag.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/tr/dag.json
@@ -73,6 +73,12 @@
"navigation": "Gezin: {{arrow}}",
"toggleGroup": "Grubu aç/kapat: Space"
},
+ "notFound": {
+ "back": "Geri Dön",
+ "backToDags": "Dag'lere Geri Dön",
+ "message": "Dag \"{{dagId}}\" veri tabanında mevcut değil.",
+ "title": "Dag Bulunamadı"
+ },
"overview": {
"buttons": {
"failedRun_one": "Başarısız Çalıştırma",
diff --git a/airflow-core/src/airflow/ui/src/components/DurationChart.tsx b/airflow-core/src/airflow/ui/src/components/DurationChart.tsx
index 186ebb5f6f19e..8008307fabf40 100644
--- a/airflow-core/src/airflow/ui/src/components/DurationChart.tsx
+++ b/airflow-core/src/airflow/ui/src/components/DurationChart.tsx
@@ -35,8 +35,9 @@ import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom";
import type { TaskInstanceResponse, GridRunsResponse } from "openapi/requests/types.gen";
+import { useTimezone } from "src/context/timezone";
import { getComputedCSSVariableValue } from "src/theme";
-import { DEFAULT_DATETIME_FORMAT, renderDuration } from "src/utils/datetimeUtils";
+import { DEFAULT_DATETIME_FORMAT, formatDate, renderDuration } from "src/utils/datetimeUtils";
import { buildTaskInstanceUrl } from "src/utils/links";
ChartJS.register(
@@ -69,15 +70,35 @@ const getDuration = (start: string, end: string | null) => {
return dayjs.duration(endDate.diff(startDate)).asSeconds();
};
+const getTickLabelFormat = (entries: Array): string => {
+ if (entries.length < 2) {
+ return "HH:mm:ss";
+ }
+
+ const first = dayjs(entries[0]?.run_after);
+ const last = dayjs(entries[entries.length - 1]?.run_after);
+
+ if (!first.isValid() || !last.isValid()) {
+ return "MMM DD";
+ }
+
+ const diffInDays = Math.abs(last.diff(first, "day"));
+
+ return diffInDays < 1 ? "HH:mm:ss" : "MMM DD HH:mm";
+};
+
export const DurationChart = ({
entries,
+ isAutoRefreshing = false,
kind,
}: {
readonly entries: Array | undefined;
+ readonly isAutoRefreshing?: boolean;
readonly kind: "Dag Run" | "Task Instance";
}) => {
const { t: translate } = useTranslation(["components", "common"]);
const navigate = useNavigate();
+ const { selectedTimezone } = useTimezone();
const [queuedColorToken] = useToken("colors", ["queued.solid"]);
// Get states and create color tokens for them
@@ -175,6 +196,7 @@ export const DurationChart = ({
}}
datasetIdKey="id"
options={{
+ animation: isAutoRefreshing ? false : undefined,
onClick: (_event, elements) => {
const [element] = elements;
@@ -239,6 +261,8 @@ export const DurationChart = ({
x: {
stacked: true,
ticks: {
+ callback: (_value, index) =>
+ formatDate(entries[index]?.run_after, selectedTimezone, getTickLabelFormat(entries)),
maxTicksLimit: 3,
},
title: { align: "end", display: true, text: translate("common:dagRun.runAfter") },
diff --git a/airflow-core/src/airflow/ui/src/components/ui/createErrorToaster.ts b/airflow-core/src/airflow/ui/src/components/ui/createErrorToaster.ts
new file mode 100644
index 0000000000000..c59cabbf3b675
--- /dev/null
+++ b/airflow-core/src/airflow/ui/src/components/ui/createErrorToaster.ts
@@ -0,0 +1,40 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import type { TFunction } from "i18next";
+
+import type { ExpandedApiError } from "src/components/ErrorAlert";
+import { toaster } from "src/components/ui";
+
+type ErrorToastMessage = {
+ readonly description: string;
+ readonly title: string;
+};
+
+export const createErrorToaster =
+ (translate: TFunction, fallbackMessage: ErrorToastMessage) => (error: unknown) => {
+ const isForbidden = (error as ExpandedApiError).status === 403;
+
+ toaster.create({
+ description: isForbidden
+ ? translate("toaster.forbidden.description", { ns: "common" })
+ : fallbackMessage.description,
+ title: isForbidden ? translate("toaster.forbidden.title", { ns: "common" }) : fallbackMessage.title,
+ type: "error",
+ });
+ };
diff --git a/airflow-core/src/airflow/ui/src/pages/Dag/Overview/Overview.tsx b/airflow-core/src/airflow/ui/src/pages/Dag/Overview/Overview.tsx
index 8cebcc4ba0b8e..ba07cae494bcc 100644
--- a/airflow-core/src/airflow/ui/src/pages/Dag/Overview/Overview.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Dag/Overview/Overview.tsx
@@ -35,6 +35,7 @@ import TimeRangeSelector from "src/components/TimeRangeSelector";
import { TrendCountButton } from "src/components/TrendCountButton";
import { SearchParamsKeys } from "src/constants/searchParams";
import { useGridRuns } from "src/queries/useGridRuns.ts";
+import { isStatePending, useAutoRefresh } from "src/utils";
const FailedLogs = lazy(() => import("./FailedLogs"));
@@ -67,6 +68,9 @@ export const Overview = () => {
state: ["failed"],
});
const { data: gridRuns, isLoading: isLoadingRuns } = useGridRuns({ limit });
+ const refetchInterval = useAutoRefresh({ dagId });
+ const isAutoRefreshing =
+ Boolean(refetchInterval) && (gridRuns ?? []).some((run) => isStatePending(run.state));
const { data: assetEventsData, isLoading: isLoadingAssetEvents } = useAssetServiceGetAssetEvents({
limit,
orderBy: [assetSortBy],
@@ -124,7 +128,11 @@ export const Overview = () => {
{isLoadingRuns ? (
) : (
-
+
)}
{assetEventsData && assetEventsData.total_entries > 0 ? (
diff --git a/airflow-core/src/airflow/ui/src/queries/useDagParsing.ts b/airflow-core/src/airflow/ui/src/queries/useDagParsing.ts
index 914799dfc5499..2fc8c43c0e394 100644
--- a/airflow-core/src/airflow/ui/src/queries/useDagParsing.ts
+++ b/airflow-core/src/airflow/ui/src/queries/useDagParsing.ts
@@ -25,18 +25,15 @@ import {
UseDagSourceServiceGetDagSourceKeyFn,
} from "openapi/queries";
import { toaster } from "src/components/ui";
+import { createErrorToaster } from "src/components/ui/createErrorToaster";
export const useDagParsing = ({ dagId }: { readonly dagId: string }) => {
const queryClient = useQueryClient();
- const { t: translate } = useTranslation("dag");
-
- const onError = () => {
- toaster.create({
- description: translate("parse.toaster.error.description"),
- title: translate("parse.toaster.error.title"),
- type: "error",
- });
- };
+ const { t: translate } = useTranslation(["dag", "common"]);
+ const onError = createErrorToaster(translate, {
+ description: translate("parse.toaster.error.description"),
+ title: translate("parse.toaster.error.title"),
+ });
const onSuccess = async () => {
await queryClient.invalidateQueries({
diff --git a/airflow-core/src/airflow/ui/src/utils/links.test.ts b/airflow-core/src/airflow/ui/src/utils/links.test.ts
index 47dd032ebc519..75d175e22a599 100644
--- a/airflow-core/src/airflow/ui/src/utils/links.test.ts
+++ b/airflow-core/src/airflow/ui/src/utils/links.test.ts
@@ -243,7 +243,7 @@ describe("buildTaskInstanceUrl", () => {
}),
).toBe("/dags/new_dag/runs/new_run/tasks/group/new_group");
- // Groups should never preserve tabs even for mapped groups
+ // Groups should never get /mapped appended — no such route exists for task groups
expect(
buildTaskInstanceUrl({
currentPathname: "/dags/old/runs/old/tasks/group/old_group/events",
@@ -254,6 +254,21 @@ describe("buildTaskInstanceUrl", () => {
runId: "new_run",
taskId: "new_group",
}),
- ).toBe("/dags/new_dag/runs/new_run/tasks/group/new_group/mapped/3");
+ ).toBe("/dags/new_dag/runs/new_run/tasks/group/new_group");
+ });
+
+ it("should not append /mapped for dynamic task groups from grid view", () => {
+ // Regression test for https://github.com/apache/airflow/issues/63197
+ // Dynamic task groups have isMapped=true but no route exists for group/:groupId/mapped
+ expect(
+ buildTaskInstanceUrl({
+ currentPathname: "/dags/my_dag/runs/run_1/tasks/group/my_group",
+ dagId: "my_dag",
+ isGroup: true,
+ isMapped: true,
+ runId: "run_1",
+ taskId: "my_group",
+ }),
+ ).toBe("/dags/my_dag/runs/run_1/tasks/group/my_group");
});
});
diff --git a/airflow-core/src/airflow/ui/src/utils/links.ts b/airflow-core/src/airflow/ui/src/utils/links.ts
index 3beafb06afea1..23c438721a5b8 100644
--- a/airflow-core/src/airflow/ui/src/utils/links.ts
+++ b/airflow-core/src/airflow/ui/src/utils/links.ts
@@ -89,7 +89,7 @@ export const buildTaskInstanceUrl = (params: {
let basePath = `/dags/${dagId}/runs/${runId}/tasks/${groupPath}${taskId}`;
- if (isMapped) {
+ if (isMapped && !isGroup) {
basePath += `/mapped`;
if (mapIndex !== undefined && mapIndex !== "-1") {
basePath += `/${mapIndex}`;
diff --git a/airflow-core/src/airflow/utils/log/file_task_handler.py b/airflow-core/src/airflow/utils/log/file_task_handler.py
index 432ecffd46055..adc393c1689aa 100644
--- a/airflow-core/src/airflow/utils/log/file_task_handler.py
+++ b/airflow-core/src/airflow/utils/log/file_task_handler.py
@@ -895,9 +895,27 @@ def _read_from_logs_server(
"See more at https://airflow.apache.org/docs/apache-airflow/"
"stable/configurations-ref.html#secret-key"
)
+ elif response.status_code == 404:
+ # Log file not found on the worker's log server.
+ # This typically happens when a task retried on a different worker
+ # and the original worker's logs are no longer accessible.
+ # Fall back to local filesystem read if available.
+ worker_log_full_path = Path(self.local_base, worker_log_rel_path)
+ fallback_sources, fallback_streams = self._read_from_local(worker_log_full_path)
+ if fallback_sources:
+ sources.extend(fallback_sources)
+ log_streams.extend(fallback_streams)
+ else:
+ sources.append(
+ f"Log file not found on worker '{ti.hostname}'. "
+ f"This attempt may have run on a different worker whose logs "
+ f"are no longer accessible. "
+ f"Consider configuring remote logging (S3, GCS, etc.) for log persistence."
+ )
else:
# Check if the resource was properly fetched
response.raise_for_status()
+
if int(response.headers.get("Content-Length", 0)) > 0:
sources.append(url)
log_streams.append(
diff --git a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py
index 625b5abc9d708..451dbf22efda6 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_assets.py
@@ -24,6 +24,7 @@
import time_machine
from airflow._shared.timezones import timezone
+from airflow.api_fastapi.auth.managers.models.resource_details import DagAccessEntity, DagDetails
from airflow.models import DagModel
from airflow.models.asset import (
AssetActive,
@@ -1261,6 +1262,27 @@ def test_should_respond_404_on_multiple_dags(self, test_client):
assert response.status_code == 404
assert response.json()["detail"] == "No DAG materializes asset with ID: 3"
+ @pytest.mark.usefixtures("configure_git_connection_for_dag_bundle")
+ def test_should_respond_403_when_user_cannot_trigger_dag(self, test_client):
+ with mock.patch(
+ "airflow.api_fastapi.core_api.routes.public.assets.get_auth_manager",
+ autospec=True,
+ ) as mock_get_auth_manager:
+ mock_get_auth_manager.return_value.is_authorized_dag.return_value = False
+
+ response = test_client.post("/assets/1/materialize")
+
+ assert response.status_code == 403
+ assert response.json()["detail"] == (
+ f"User is not authorized to trigger a run for DAG: {self.DAG_ASSET1_ID} that materializes this asset"
+ )
+ mock_get_auth_manager.return_value.is_authorized_dag.assert_called_once_with(
+ method="POST",
+ access_entity=DagAccessEntity.RUN,
+ details=DagDetails(id=self.DAG_ASSET1_ID),
+ user=mock.ANY,
+ )
+
class TestGetAssetQueuedEvents(TestQueuedEventEndpoint):
@pytest.mark.usefixtures("time_freezer")
diff --git a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_backfills.py b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_backfills.py
index f7574936428f0..b397b06c9e2eb 100644
--- a/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_backfills.py
+++ b/airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_backfills.py
@@ -153,7 +153,7 @@ def test_should_response_200(
expected_response = []
for backfill in response_params:
expected_response.append(backfill_responses[backfill])
- with assert_queries_count(2 if test_params.get("dag_id") is None else 3):
+ with assert_queries_count(3 if test_params.get("dag_id") is None else 4):
response = test_client.get("/backfills", params=test_params)
assert response.status_code == 200
assert response.json() == {
@@ -168,3 +168,27 @@ def test_should_response_401(self, unauthenticated_test_client):
def test_should_response_403(self, unauthorized_test_client):
response = unauthorized_test_client.get("/backfills", params={})
assert response.status_code == 403
+
+ @mock.patch("airflow.api_fastapi.auth.managers.base_auth_manager.BaseAuthManager.get_authorized_dag_ids")
+ def test_should_only_return_authorized_dag_backfills(
+ self, mock_get_authorized_dag_ids, test_client, session, testing_dag_bundle
+ ):
+ dags = self._create_dag_models()
+ from_date = timezone.utcnow()
+ to_date = timezone.utcnow()
+ backfills = [
+ Backfill(dag_id=dags[0].dag_id, from_date=from_date, to_date=to_date),
+ Backfill(dag_id=dags[1].dag_id, from_date=from_date, to_date=to_date),
+ Backfill(dag_id=dags[2].dag_id, from_date=from_date, to_date=to_date),
+ ]
+ session.add_all(backfills)
+ session.commit()
+
+ mock_get_authorized_dag_ids.return_value = {"TEST_DAG_2", "TEST_DAG_3"}
+ response = test_client.get("/backfills")
+
+ mock_get_authorized_dag_ids.assert_called_once_with(user=mock.ANY, method="GET")
+ assert response.status_code == 200
+ body = response.json()
+ assert body["total_entries"] == 2
+ assert {b["dag_id"] for b in body["backfills"]} == {"TEST_DAG_2", "TEST_DAG_3"}
diff --git a/airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_xcoms.py b/airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_xcoms.py
index cf8f80642f7ca..cb84d1ace0a26 100644
--- a/airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_xcoms.py
+++ b/airflow-core/tests/unit/api_fastapi/execution_api/versions/head/test_xcoms.py
@@ -385,7 +385,7 @@ def test_xcom_round_trip(self, client, create_task_instance, session, orig_value
Test that deserialization works when XCom values are stored directly in the DB with API Server.
This tests the case where the XCom value is stored from the Task API where the value is serialized
- via Client SDK into JSON object and passed via the API Server to the DB. It by-passes
+ via Client SDK into JSON object and passed via the API Server to the DB. It bypasses
the XComModel.serialize_value and stores valid Python JSON compatible objects to DB.
This test is to ensure that the deserialization works correctly in this case as well as
diff --git a/airflow-core/tests/unit/api_fastapi/test_app.py b/airflow-core/tests/unit/api_fastapi/test_app.py
index cd43260211bc5..d4d3e8999cea7 100644
--- a/airflow-core/tests/unit/api_fastapi/test_app.py
+++ b/airflow-core/tests/unit/api_fastapi/test_app.py
@@ -16,6 +16,7 @@
# under the License.
from __future__ import annotations
+import threading
from unittest import mock
import pytest
@@ -140,3 +141,37 @@ def test_nested_subpath(self):
"""When base_url contains a nested subpath, get_cookie_path() should return it."""
with mock.patch.object(app_module, "API_ROOT_PATH", "/org/team-a/airflow/"):
assert app_module.get_cookie_path() == "/org/team-a/airflow/"
+
+
+def test_create_auth_manager_thread_safety():
+ """Concurrent calls to create_auth_manager must return the same singleton instance."""
+ call_count = 0
+ singleton = None
+
+ class FakeAuthManager:
+ def __init__(self):
+ nonlocal call_count, singleton
+ call_count += 1
+ singleton = self
+
+ app_module.purge_cached_app()
+
+ results = []
+ barrier = threading.Barrier(10)
+
+ def call_create_auth_manager():
+ barrier.wait()
+ results.append(app_module.create_auth_manager())
+
+ with mock.patch.object(app_module, "get_auth_manager_cls", return_value=FakeAuthManager):
+ threads = [threading.Thread(target=call_create_auth_manager) for _ in range(10)]
+ for t in threads:
+ t.start()
+ for t in threads:
+ t.join()
+
+ assert len(results) == 10
+ assert all(r is singleton for r in results)
+ assert call_count == 1
+
+ app_module.purge_cached_app()
diff --git a/airflow-core/tests/unit/executors/test_workloads.py b/airflow-core/tests/unit/executors/test_workloads.py
new file mode 100644
index 0000000000000..507204d41dc27
--- /dev/null
+++ b/airflow-core/tests/unit/executors/test_workloads.py
@@ -0,0 +1,53 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from pathlib import PurePosixPath
+from uuid import uuid4
+
+from airflow.executors.workloads import BundleInfo, ExecuteTask, TaskInstance
+
+
+def test_token_excluded_from_workload_repr():
+ """Ensure JWT tokens do not leak into log output via repr()."""
+ fake_token = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.secret_payload.signature"
+ ti = TaskInstance(
+ id=uuid4(),
+ dag_version_id=uuid4(),
+ task_id="test_task",
+ dag_id="test_dag",
+ run_id="test_run",
+ try_number=1,
+ map_index=-1,
+ pool_slots=1,
+ queue="default",
+ priority_weight=1,
+ )
+ workload = ExecuteTask(
+ ti=ti,
+ dag_rel_path=PurePosixPath("test_dag.py"),
+ token=fake_token,
+ bundle_info=BundleInfo(name="dags-folder", version=None),
+ log_path="test.log",
+ )
+
+ workload_repr = repr(workload)
+
+ # Token MUST NOT appear in repr (prevents leaking into logs)
+ assert fake_token not in workload_repr, f"JWT token leaked into repr! Found token in: {workload_repr}"
+ # But token should still be accessible as an attribute
+ assert workload.token == fake_token
diff --git a/airflow-core/tests/unit/models/test_taskinstance.py b/airflow-core/tests/unit/models/test_taskinstance.py
index f3ffcedf21893..615c40ac91864 100644
--- a/airflow-core/tests/unit/models/test_taskinstance.py
+++ b/airflow-core/tests/unit/models/test_taskinstance.py
@@ -3029,6 +3029,71 @@ def cmds():
out_lines = [line.strip() for line in f]
assert out_lines == ["hello FOO", "goodbye FOO", "hello BAR", "goodbye BAR"]
+ def test_xcom_pull_unmapped_task(self, dag_maker, session):
+ """
+ Test that xcom_pull from unmapped task returns single deserialized value.
+
+ For unmapped tasks with map_index < 0, xcom_pull should return the single value,
+ not a LazyXComSelectSequence.
+ """
+
+ with dag_maker(dag_id="test_xcom_unmapped", session=session):
+ upstream = PythonOperator(
+ task_id="unmapped_task",
+ python_callable=lambda: {"key": "value"},
+ )
+ downstream = PythonOperator(
+ task_id="downstream",
+ python_callable=lambda: None,
+ )
+ upstream >> downstream
+
+ dag_run = dag_maker.create_dagrun(logical_date=timezone.utcnow())
+
+ # Run upstream task to push xcom
+ dag_maker.run_ti("unmapped_task", dag_run=dag_run, session=session)
+
+ # Get downstream task instance
+ ti_downstream = dag_run.get_task_instance("downstream", session=session)
+ ti_downstream.task = dag_maker.dag.task_dict["downstream"]
+
+ # Pull xcom - should return single dict value, not LazyXComSelectSequence
+ result = ti_downstream.xcom_pull(task_ids="unmapped_task", session=session)
+ assert isinstance(result, dict), f"Expected dict for unmapped task, got {type(result)}"
+ assert result == {"key": "value"}
+
+ def test_xcom_pull_returns_lazy_sequence_for_mapped_xcom(self, dag_maker, session):
+ """
+ Test that xcom_pull returns LazyXComSelectSequence when XComs are mapped (map_index >= 0)
+ and map_indexes is not specified.
+ """
+ from airflow.models.xcom import LazyXComSelectSequence
+
+ with dag_maker(dag_id="test_xcom_mapped_values", session=session):
+
+ @task
+ def push_values(val):
+ return val
+
+ upstream = push_values.expand(val=[2, 4])
+ downstream = PythonOperator(
+ task_id="downstream",
+ python_callable=lambda: None,
+ )
+ upstream >> downstream
+
+ dag_run = dag_maker.create_dagrun(logical_date=timezone.utcnow())
+ dag_maker.run_ti(upstream.operator.task_id, map_index=0, dag_run=dag_run, session=session)
+ dag_maker.run_ti(upstream.operator.task_id, map_index=1, dag_run=dag_run, session=session)
+
+ ti_downstream = dag_run.get_task_instance("downstream", session=session)
+ ti_downstream.task = dag_maker.dag.task_dict["downstream"]
+
+ result = ti_downstream.xcom_pull(task_ids=upstream.operator.task_id, session=session)
+ assert isinstance(result, LazyXComSelectSequence), (
+ f"Expected LazyXComSelectSequence for mapped XComs, got {type(result)}"
+ )
+
def _get_lazy_xcom_access_expected_sql_lines() -> list[str]:
backend = os.environ.get("BACKEND")
diff --git a/airflow-core/tests/unit/utils/log/test_file_task_handler.py b/airflow-core/tests/unit/utils/log/test_file_task_handler.py
new file mode 100644
index 0000000000000..cdda0e66de84c
--- /dev/null
+++ b/airflow-core/tests/unit/utils/log/test_file_task_handler.py
@@ -0,0 +1,92 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from pathlib import Path
+from unittest.mock import MagicMock, patch
+
+from airflow.utils.log.file_task_handler import FileTaskHandler
+
+
+class TestFileTaskHandlerLogServer:
+ """Tests for _read_from_logs_server 404 handling."""
+
+ def setup_method(self):
+ self.handler = FileTaskHandler(base_log_folder="/tmp/test_logs")
+
+ self.ti = MagicMock()
+ self.ti.hostname = "worker-1"
+ self.ti.triggerer_job = None
+ self.ti.task = None
+
+ @patch("airflow.utils.log.file_task_handler._fetch_logs_from_service")
+ @patch.object(FileTaskHandler, "_get_log_retrieval_url")
+ @patch.object(FileTaskHandler, "_read_from_local")
+ def test_404_falls_back_to_local_when_available(self, mock_read_local, mock_get_url, mock_fetch):
+ """When log server returns 404 and local logs exist, use local logs."""
+ mock_get_url.return_value = ("http://worker-1/log", "dag/run/task/1.log")
+
+ mock_response = MagicMock()
+ mock_response.status_code = 404
+ mock_fetch.return_value = mock_response
+
+ mock_stream = MagicMock()
+ mock_read_local.return_value = (["/tmp/test_logs/dag/run/task/1.log"], [mock_stream])
+
+ sources, streams = self.handler._read_from_logs_server(self.ti, "dag/run/task/1.log")
+
+ assert sources == ["/tmp/test_logs/dag/run/task/1.log"]
+ assert streams == [mock_stream]
+ mock_read_local.assert_called_once_with(Path("/tmp/test_logs", "dag/run/task/1.log"))
+
+ @patch("airflow.utils.log.file_task_handler._fetch_logs_from_service")
+ @patch.object(FileTaskHandler, "_get_log_retrieval_url")
+ @patch.object(FileTaskHandler, "_read_from_local")
+ def test_404_shows_clear_message_when_no_local_fallback(self, mock_read_local, mock_get_url, mock_fetch):
+ """When log server returns 404 and no local logs exist, show helpful message."""
+ mock_get_url.return_value = ("http://worker-1/log", "dag/run/task/1.log")
+
+ mock_response = MagicMock()
+ mock_response.status_code = 404
+ mock_fetch.return_value = mock_response
+
+ mock_read_local.return_value = ([], [])
+
+ sources, streams = self.handler._read_from_logs_server(self.ti, "dag/run/task/1.log")
+
+ assert len(sources) == 1
+ assert "worker-1" in sources[0]
+ assert "no longer accessible" in sources[0]
+ assert "remote logging" in sources[0]
+ assert streams == []
+
+ @patch("airflow.utils.log.file_task_handler._fetch_logs_from_service")
+ @patch.object(FileTaskHandler, "_get_log_retrieval_url")
+ def test_403_shows_secret_key_message(self, mock_get_url, mock_fetch):
+ """When log server returns 403, show secret key configuration message."""
+ mock_get_url.return_value = ("http://worker-1/log", "dag/run/task/1.log")
+
+ mock_response = MagicMock()
+ mock_response.status_code = 403
+ mock_fetch.return_value = mock_response
+
+ sources, streams = self.handler._read_from_logs_server(self.ti, "dag/run/task/1.log")
+
+ assert len(sources) == 1
+ assert "secret_key" in sources[0]
+ assert streams == []
diff --git a/chart/templates/dag-processor/dag-processor-deployment.yaml b/chart/templates/dag-processor/dag-processor-deployment.yaml
index 3f029eebf9fd9..b8aba7d2a5810 100644
--- a/chart/templates/dag-processor/dag-processor-deployment.yaml
+++ b/chart/templates/dag-processor/dag-processor-deployment.yaml
@@ -218,6 +218,10 @@ spec:
- name: AIRFLOW__LOG_RETENTION_DAYS
value: "{{ .Values.dagProcessor.logGroomerSidecar.retentionDays }}"
{{- end }}
+ {{- if .Values.dagProcessor.logGroomerSidecar.retentionMinutes }}
+ - name: AIRFLOW__LOG_RETENTION_MINUTES
+ value: "{{ .Values.dagProcessor.logGroomerSidecar.retentionMinutes }}"
+ {{- end }}
{{- if .Values.dagProcessor.logGroomerSidecar.frequencyMinutes }}
- name: AIRFLOW__LOG_CLEANUP_FREQUENCY_MINUTES
value: "{{ .Values.dagProcessor.logGroomerSidecar.frequencyMinutes }}"
diff --git a/chart/templates/scheduler/scheduler-deployment.yaml b/chart/templates/scheduler/scheduler-deployment.yaml
index bb3670cecbebe..45ed2e2f36ceb 100644
--- a/chart/templates/scheduler/scheduler-deployment.yaml
+++ b/chart/templates/scheduler/scheduler-deployment.yaml
@@ -277,6 +277,10 @@ spec:
- name: AIRFLOW__LOG_RETENTION_DAYS
value: "{{ .Values.scheduler.logGroomerSidecar.retentionDays }}"
{{- end }}
+ {{- if .Values.scheduler.logGroomerSidecar.retentionMinutes }}
+ - name: AIRFLOW__LOG_RETENTION_MINUTES
+ value: "{{ .Values.scheduler.logGroomerSidecar.retentionMinutes }}"
+ {{- end }}
{{- if .Values.scheduler.logGroomerSidecar.frequencyMinutes }}
- name: AIRFLOW__LOG_CLEANUP_FREQUENCY_MINUTES
value: "{{ .Values.scheduler.logGroomerSidecar.frequencyMinutes }}"
diff --git a/chart/templates/triggerer/triggerer-deployment.yaml b/chart/templates/triggerer/triggerer-deployment.yaml
index 9010becf118c3..8a594105faba6 100644
--- a/chart/templates/triggerer/triggerer-deployment.yaml
+++ b/chart/templates/triggerer/triggerer-deployment.yaml
@@ -248,6 +248,10 @@ spec:
- name: AIRFLOW__LOG_RETENTION_DAYS
value: "{{ .Values.triggerer.logGroomerSidecar.retentionDays }}"
{{- end }}
+ {{- if .Values.triggerer.logGroomerSidecar.retentionMinutes }}
+ - name: AIRFLOW__LOG_RETENTION_MINUTES
+ value: "{{ .Values.triggerer.logGroomerSidecar.retentionMinutes }}"
+ {{- end }}
{{- if .Values.triggerer.logGroomerSidecar.frequencyMinutes }}
- name: AIRFLOW__LOG_CLEANUP_FREQUENCY_MINUTES
value: "{{ .Values.triggerer.logGroomerSidecar.frequencyMinutes }}"
diff --git a/chart/templates/workers/worker-deployment.yaml b/chart/templates/workers/worker-deployment.yaml
index a0d794137925d..3d450159483ea 100644
--- a/chart/templates/workers/worker-deployment.yaml
+++ b/chart/templates/workers/worker-deployment.yaml
@@ -344,6 +344,10 @@ spec:
- name: AIRFLOW__LOG_RETENTION_DAYS
value: "{{ .Values.workers.logGroomerSidecar.retentionDays }}"
{{- end }}
+ {{- if .Values.workers.logGroomerSidecar.retentionMinutes }}
+ - name: AIRFLOW__LOG_RETENTION_MINUTES
+ value: "{{ .Values.workers.logGroomerSidecar.retentionMinutes }}"
+ {{- end }}
{{- if .Values.workers.logGroomerSidecar.frequencyMinutes }}
- name: AIRFLOW__LOG_CLEANUP_FREQUENCY_MINUTES
value: "{{ .Values.workers.logGroomerSidecar.frequencyMinutes }}"
diff --git a/chart/values.schema.json b/chart/values.schema.json
index fd832f9b8974d..304c07b7ca818 100644
--- a/chart/values.schema.json
+++ b/chart/values.schema.json
@@ -12563,10 +12563,15 @@
]
},
"retentionDays": {
- "description": "Number of days to retain the logs when running the Airflow log groomer sidecar.",
+ "description": "Number of days to retain the logs when running the Airflow log groomer sidecar. Total retention time is retentionDays + retentionMinutes.",
"type": "integer",
"default": 15
},
+ "retentionMinutes": {
+ "description": "Number of minutes to retain the logs when running the Airflow log groomer sidecar. Total retention time is retentionDays + retentionMinutes.",
+ "type": "integer",
+ "default": 0
+ },
"frequencyMinutes": {
"description": "Number of minutes between attempts to groom the Airflow logs in log groomer sidecar.",
"type": "integer",
diff --git a/chart/values.yaml b/chart/values.yaml
index d4dde3fdf6941..57595632ca894 100644
--- a/chart/values.yaml
+++ b/chart/values.yaml
@@ -932,6 +932,12 @@ workers:
# Number of days to retain logs
retentionDays: 15
+ # Number of minutes to retain logs.
+ # This can be used for finer granularity than days.
+ # Total retention is retentionDays + retentionMinutes.
+ retentionMinutes: 0
+
+
# Frequency to attempt to groom logs (in minutes)
frequencyMinutes: 15
@@ -1181,6 +1187,12 @@ scheduler:
args: ["bash", "/clean-logs"]
# Number of days to retain logs
retentionDays: 15
+
+ # Number of minutes to retain logs.
+ # This can be used for finer granularity than days.
+ # Total retention is retentionDays + retentionMinutes.
+ retentionMinutes: 0
+
# frequency to attempt to groom logs, in minutes
frequencyMinutes: 15
# Max size of logs in bytes. 0 = disabled
@@ -1969,6 +1981,12 @@ triggerer:
args: ["bash", "/clean-logs"]
# Number of days to retain logs
retentionDays: 15
+
+ # Number of minutes to retain logs.
+ # This can be used for finer granularity than days.
+ # Total retention is retentionDays + retentionMinutes.
+ retentionMinutes: 0
+
# frequency to attempt to groom logs, in minutes
frequencyMinutes: 15
# Max size of logs in bytes. 0 = disabled
@@ -2161,6 +2179,12 @@ dagProcessor:
args: ["bash", "/clean-logs"]
# Number of days to retain logs
retentionDays: 15
+
+ # Number of minutes to retain logs.
+ # This can be used for finer granularity than days.
+ # Total retention is retentionDays + retentionMinutes.
+ retentionMinutes: 0
+
# frequency to attempt to groom logs, in minutes
frequencyMinutes: 15
# Max size of logs in bytes. 0 = disabled
diff --git a/contributing-docs/03_contributors_quick_start.rst b/contributing-docs/03_contributors_quick_start.rst
index b331b1dc610aa..b61b1989a8b9b 100644
--- a/contributing-docs/03_contributors_quick_start.rst
+++ b/contributing-docs/03_contributors_quick_start.rst
@@ -261,6 +261,14 @@ To avoid burden on our CI infrastructure and to save time, prek hooks can be run
Installing prek is best done with ``uv`` (recommended) or ``pipx``.
+You can also update the tools installed with UV, including ``prek``.
+
+ Run the following command to upgrade all UV-managed tools:
+
+ .. code-block:: bash
+
+ uv tool upgrade --all
+
1. Installing required packages
on Debian / Ubuntu, install via
@@ -285,6 +293,7 @@ on macOS, install via
uv tool install prek
+
or with pipx:
.. code-block:: bash
@@ -361,6 +370,7 @@ It will run prek hooks automatically before committing and stops the commit on f
cd ~/Projects/airflow
prek uninstall
+
- For more information on this visit |08_static_code_checks.rst|
.. |08_static_code_checks.rst| raw:: html
diff --git a/dev/breeze/doc/10_ui_tasks.rst b/dev/breeze/doc/10_ui_tasks.rst
index 0af6f739b2419..c1f7c04c3047c 100644
--- a/dev/breeze/doc/10_ui_tasks.rst
+++ b/dev/breeze/doc/10_ui_tasks.rst
@@ -62,11 +62,11 @@ Example usage:
# Add missing translations with TODO markers
breeze ui check-translation-completeness --add-missing
- # Remove extra translations not present in English
- breeze ui check-translation-completeness --remove-extra
+ # Remove unused translations (keys not required)
+ breeze ui check-translation-completeness --remove-unused
# Fix translations for a specific language
- breeze ui check-translation-completeness --language de --add-missing --remove-extra
+ breeze ui check-translation-completeness --language de --add-missing --remove-unused
-----
diff --git a/dev/breeze/doc/ci/02_images.md b/dev/breeze/doc/ci/02_images.md
index 4b184d6fbed28..1f8edf41b2aca 100644
--- a/dev/breeze/doc/ci/02_images.md
+++ b/dev/breeze/doc/ci/02_images.md
@@ -443,8 +443,8 @@ can be used for CI images:
| `ADDITIONAL_DEV_APT_DEPS` | | Additional apt dev dependencies installed in the first part of the image |
| `ADDITIONAL_DEV_APT_ENV` | | Additional env variables defined when installing dev deps |
| `AIRFLOW_PIP_VERSION` | `26.0.1` | `pip` version used. |
-| `AIRFLOW_UV_VERSION` | `0.10.7` | `uv` version used. |
-| `AIRFLOW_PREK_VERSION` | `0.3.4` | `prek` version used. |
+| `AIRFLOW_UV_VERSION` | `0.10.9` | `uv` version used. |
+| `AIRFLOW_PREK_VERSION` | `0.3.5` | `prek` version used. |
| `AIRFLOW_USE_UV` | `true` | Whether to use UV for installation. |
| `PIP_PROGRESS_BAR` | `on` | Progress bar for PIP installation |
diff --git a/dev/breeze/doc/images/output_ui_check-translation-completeness.svg b/dev/breeze/doc/images/output_ui_check-translation-completeness.svg
index c05a8d5f6ce0f..26aff21705c42 100644
--- a/dev/breeze/doc/images/output_ui_check-translation-completeness.svg
+++ b/dev/breeze/doc/images/output_ui_check-translation-completeness.svg
@@ -105,9 +105,9 @@
Check completeness of UI translations.
â•─ Translation options ────────────────────────────────────────────────────────────────────────────────────────────────╮
-│--language-lShow summary for a single language (e.g. en, de, pl, etc.)(TEXT)│
-│--add-missingAdd missing translations for all languages except English, prefixed with 'TODO: translate:'.│
-│--remove-extraRemove extra translations that are present in the language but missing in English.│
+│--language-lShow summary for a single language (e.g. en, de, pl, etc.)(TEXT)│
+│--add-missingAdd missing translations for all languages except English, prefixed with 'TODO: translate:'.│
+│--remove-unusedRemove unused translations (keys present in the language but not required).│
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
â•─ Common options ─────────────────────────────────────────────────────────────────────────────────────────────────────╮
│--verbose-vPrint verbose information about performed steps.│
diff --git a/dev/breeze/doc/images/output_ui_check-translation-completeness.txt b/dev/breeze/doc/images/output_ui_check-translation-completeness.txt
index 1c09634e8b5a9..ce06792e62e1e 100644
--- a/dev/breeze/doc/images/output_ui_check-translation-completeness.txt
+++ b/dev/breeze/doc/images/output_ui_check-translation-completeness.txt
@@ -1 +1 @@
-9cb14c5898ad3622ce8112dcca284dea
+a59814821bc3477be5f4451ce6e61290
diff --git a/dev/breeze/pyproject.toml b/dev/breeze/pyproject.toml
index 2d746401400e4..fe4c7218ef94f 100644
--- a/dev/breeze/pyproject.toml
+++ b/dev/breeze/pyproject.toml
@@ -59,7 +59,7 @@ dependencies = [
"jinja2>=3.1.5",
"jsonschema>=4.19.1",
"packaging>=25.0",
- "prek>=0.3.4",
+ "prek>=0.3.5",
"psutil>=5.9.6",
"pygithub>=2.1.1",
"pytest-xdist>=3.3.1",
diff --git a/dev/breeze/src/airflow_breeze/commands/release_management_commands.py b/dev/breeze/src/airflow_breeze/commands/release_management_commands.py
index 464472bfe136f..7db8e41dd48a2 100644
--- a/dev/breeze/src/airflow_breeze/commands/release_management_commands.py
+++ b/dev/breeze/src/airflow_breeze/commands/release_management_commands.py
@@ -254,11 +254,11 @@ class VersionedFile(NamedTuple):
AIRFLOW_PIP_VERSION = "26.0.1"
-AIRFLOW_UV_VERSION = "0.10.7"
+AIRFLOW_UV_VERSION = "0.10.9"
AIRFLOW_USE_UV = False
GITPYTHON_VERSION = "3.1.46"
RICH_VERSION = "14.3.3"
-PREK_VERSION = "0.3.4"
+PREK_VERSION = "0.3.5"
HATCH_VERSION = "1.16.5"
PYYAML_VERSION = "6.0.3"
diff --git a/dev/breeze/src/airflow_breeze/commands/ui_commands.py b/dev/breeze/src/airflow_breeze/commands/ui_commands.py
index 6faf576a484ae..44a7a4b41981c 100644
--- a/dev/breeze/src/airflow_breeze/commands/ui_commands.py
+++ b/dev/breeze/src/airflow_breeze/commands/ui_commands.py
@@ -96,15 +96,15 @@ def char_key(c: str) -> tuple:
class LocaleSummary(NamedTuple):
"""
- Summary of missing and extra translation keys for a file, per locale.
+ Summary of missing and unused translation keys for a file, per locale.
Attributes:
- missing_keys: A dictionary mapping locale codes to lists of missing translation keys.
- extra_keys: A dictionary mapping locale codes to lists of extra translation keys.
+ missing_keys: Required keys that the locale does not have.
+ unused_keys: Keys present in the locale that are not required (never used at runtime).
"""
missing_keys: dict[str, list[str]]
- extra_keys: dict[str, list[str]]
+ unused_keys: dict[str, list[str]]
class LocaleFiles(NamedTuple):
@@ -140,9 +140,17 @@ def get_plural_base(key: str, suffixes: list[str]) -> str | None:
return None
-def expand_plural_keys(keys: set[str], lang: str) -> set[str]:
+COUNT_PLACEHOLDER = "{{count}}"
+
+
+def expand_plural_keys(keys: set[str], lang: str, en_key_to_value: dict[str, str] | None = None) -> set[str]:
"""
- For a set of keys, expand all plural bases to include all required suffixes for the language.
+ For a set of keys, expand plural bases to include required suffixes for the language.
+
+ When en_key_to_value is provided, only expand a base to all plural forms when at least
+ one of the English values for that base contains {{count}}. Keys without {{count}}
+ (e.g. fixed "1 Error") are not expanded, so locales are not required to have
+ unused forms like error_other when the source only has error_one.
"""
console = get_console()
suffixes = PLURAL_SUFFIXES.get(lang)
@@ -160,6 +168,11 @@ def expand_plural_keys(keys: set[str], lang: str) -> set[str]:
base_to_suffixes.setdefault(base, set()).add(key[len(base) :])
expanded = set(keys)
for base in base_to_suffixes.keys():
+ if en_key_to_value is not None:
+ en_keys_for_base = [k for k in keys if get_plural_base(k, suffixes) == base]
+ any_has_count = any(COUNT_PLACEHOLDER in en_key_to_value.get(k, "") for k in en_keys_for_base)
+ if not any_has_count:
+ continue
for suffix in suffixes:
expanded.add(base + suffix)
return expanded
@@ -190,6 +203,18 @@ def flatten_keys(d: dict, prefix: str = "") -> list[str]:
return keys
+def flatten_keys_to_values(d: dict, prefix: str = "") -> dict[str, str]:
+ """Build a flat key -> value map for leaf string values (for i18n JSON)."""
+ result: dict[str, str] = {}
+ for k, v in d.items():
+ full_key = f"{prefix}.{k}" if prefix else k
+ if isinstance(v, dict):
+ result.update(flatten_keys_to_values(v, full_key))
+ elif isinstance(v, str):
+ result[full_key] = v
+ return result
+
+
def compare_keys(
locale_files: list[LocaleFiles],
) -> tuple[dict[str, LocaleSummary], dict[str, dict[str, int]]]:
@@ -197,7 +222,7 @@ def compare_keys(
Compare all non-English locales with English locale only.
Returns a tuple:
- - summary dict: filename -> LocaleSummary(missing, extra)
+ - summary dict: filename -> LocaleSummary(missing_keys, unused_keys)
- missing_counts dict: filename -> {locale: count_of_missing_keys}
"""
all_files: set[str] = set()
@@ -219,25 +244,32 @@ def compare_keys(
key_sets.append(LocaleKeySet(locale=lf.locale, keys=keys))
keys_by_locale = {ks.locale: ks.keys for ks in key_sets}
en_keys = keys_by_locale.get("en", set()) or set()
- # Expand English keys for all required plural forms in each language
- expanded_en_keys = {lang: expand_plural_keys(en_keys, lang) for lang in keys_by_locale.keys()}
+ en_key_to_value: dict[str, str] = {}
+ en_path = LOCALES_DIR / "en" / filename
+ if en_path.exists():
+ try:
+ en_data = load_json(en_path)
+ en_key_to_value = flatten_keys_to_values(en_data)
+ except Exception as e:
+ get_console().print(f"Error loading en values from {en_path}: {e}")
+ # Required = EN keys plus, for bases with {{count}} in EN, all plural forms for the locale
missing_keys: dict[str, list[str]] = {}
- extra_keys: dict[str, list[str]] = {}
+ unused_keys: dict[str, list[str]] = {}
missing_counts[filename] = {}
for ks in key_sets:
if ks.locale == "en":
continue
- required_keys = expanded_en_keys.get(ks.locale, en_keys)
+ required_keys = expand_plural_keys(en_keys, ks.locale, en_key_to_value)
if ks.keys is None:
missing_keys[ks.locale] = list(required_keys)
- extra_keys[ks.locale] = []
+ unused_keys[ks.locale] = []
missing_counts[filename][ks.locale] = len(required_keys)
else:
missing = list(required_keys - ks.keys)
missing_keys[ks.locale] = missing
- extra_keys[ks.locale] = list(ks.keys - required_keys)
+ unused_keys[ks.locale] = sorted(ks.keys - required_keys)
missing_counts[filename][ks.locale] = len(missing)
- summary[filename] = LocaleSummary(missing_keys=missing_keys, extra_keys=extra_keys)
+ summary[filename] = LocaleSummary(missing_keys=missing_keys, unused_keys=unused_keys)
return summary, missing_counts
@@ -294,15 +326,15 @@ def print_language_summary(locale_files: list[LocaleFiles], summary: dict[str, L
for lf in sorted(locale_files):
locale = lf.locale
file_missing: dict[str, list[str]] = {}
- file_extra: dict[str, list[str]] = {}
+ file_unused: dict[str, list[str]] = {}
for filename, diff in sorted(summary.items()):
missing_keys = diff.missing_keys.get(locale, [])
- extra_keys = diff.extra_keys.get(locale, [])
+ unused_keys = diff.unused_keys.get(locale, [])
if missing_keys:
file_missing[filename] = missing_keys
- if extra_keys:
- file_extra[filename] = extra_keys
- if file_missing or file_extra:
+ if unused_keys:
+ file_unused[filename] = unused_keys
+ if file_missing or file_unused:
if locale == "en":
continue
console.print(Panel(f"[bold yellow]{locale}[/bold yellow]", style="blue"))
@@ -313,34 +345,38 @@ def print_language_summary(locale_files: list[LocaleFiles], summary: dict[str, L
console.print(f" [magenta]{filename}[/magenta]:")
for k in keys:
console.print(f" [yellow]{k}[/yellow]")
- if file_extra:
- found_difference = True
- console.print("[red] Extra keys (need to be removed/updated):[/red]")
- for filename, keys in file_extra.items():
+ if file_unused:
+ console.print("[yellow] Unused keys (not required, can be removed):[/yellow]")
+ for filename, keys in file_unused.items():
console.print(f" [magenta]{filename}[/magenta]:")
for k in keys:
- console.print(f" [yellow]{k}[/yellow]")
+ console.print(f" [dim]{k}[/dim]")
return found_difference
+def is_todo_value(s: str) -> bool:
+ """Return True if the string is a TODO placeholder (e.g. 'TODO: translate: ...')."""
+ return isinstance(s, str) and s.strip().startswith("TODO: translate")
+
+
def count_todos(obj) -> int:
"""Count TODO: translate entries in a dict or list."""
if isinstance(obj, dict):
return sum(count_todos(v) for v in obj.values())
if isinstance(obj, list):
return sum(count_todos(v) for v in obj)
- if isinstance(obj, str) and obj.strip().startswith("TODO: translate"):
+ if is_todo_value(obj):
return 1
return 0
-def print_translation_progress(locale_files, missing_counts, summary):
+def print_translation_progress(locale_files, summary):
console = get_console()
from rich.table import Table
tables = defaultdict(lambda: Table(show_lines=True))
all_files = set()
- coverage_per_language = {} # Collect total coverage per language
+ coverage_per_language = {}
for lf in locale_files:
all_files.update(lf.files)
@@ -353,91 +389,94 @@ def print_translation_progress(locale_files, missing_counts, summary):
table = tables[lang]
table.title = f"Translation Progress: {lang}"
table.add_column("File", style="bold cyan")
- table.add_column("Missing", style="red")
- table.add_column("Extra", style="yellow")
- table.add_column("TODOs", style="magenta")
+ table.add_column("Required (base EN)", style="dim")
+ table.add_column("Required (plural)", style="dim")
+ table.add_column("Total required", style="bold")
table.add_column("Translated", style="green")
- table.add_column("Total", style="bold")
+ table.add_column("Missing", style="red")
table.add_column("Coverage", style="bold")
- table.add_column("Completed", style="bold")
+ table.add_column("TODOs", style="magenta")
+ table.add_column("Unused", style="yellow")
+ total_required_base = 0
+ total_required_plural = 0
+ total_required = 0
+ total_translated = 0
total_missing = 0
- total_extra = 0
total_todos = 0
- total_translated = 0
- total_total = 0
+ total_unused = 0
for filename in sorted(all_files):
- file_path = Path(LOCALES_DIR / lang / filename)
- # Always get total from English version
en_path = Path(LOCALES_DIR / "en" / filename)
- if en_path.exists():
- with open(en_path) as f:
- en_data = json.load(f)
- file_total = sum(1 for _ in flatten_keys(en_data))
- else:
- file_total = 0
+ file_path = Path(LOCALES_DIR / lang / filename)
+ diff = summary.get(filename, LocaleSummary({}, {}))
+ if not en_path.exists():
+ continue
+ with open(en_path) as f:
+ en_data = json.load(f)
+ en_keys = set(flatten_keys(en_data))
+ en_key_to_value = flatten_keys_to_values(en_data)
+ required_keys = expand_plural_keys(en_keys, lang, en_key_to_value)
+ required_base_en = len(en_keys)
+ required_plural = len(required_keys) - required_base_en
+ total_req = len(required_keys)
+ file_missing = len(diff.missing_keys.get(lang, []))
+ file_unused = len(diff.unused_keys.get(lang, []))
if file_path.exists():
with open(file_path) as f:
- data = json.load(f)
- file_missing = missing_counts.get(filename, {}).get(lang, 0)
- file_extra = len(summary.get(filename, LocaleSummary({}, {})).extra_keys.get(lang, []))
-
- file_todos = count_todos(data)
+ lang_data = json.load(f)
+ lang_key_to_value = flatten_keys_to_values(lang_data)
+ file_translated = sum(
+ 1
+ for k in required_keys
+ if k in lang_key_to_value and not is_todo_value(lang_key_to_value[k])
+ )
+ file_todos = sum(
+ 1 for k in required_keys if k in lang_key_to_value and is_todo_value(lang_key_to_value[k])
+ )
if file_todos > 0:
has_todos = True
- file_translated = file_total - file_missing
- # Coverage: translated / total
- file_coverage_percent = 100 * file_translated / file_total if file_total else 100
- # Complete percent: (translated - todos) / translated
- file_actual_translated = file_translated - file_todos
- complete_percent = 100 * file_actual_translated / file_translated if file_translated else 100
- style = (
- "bold green"
- if file_missing == 0 and file_extra == 0 and file_todos == 0
- else (
- "yellow" if file_missing < file_total or file_extra > 0 or file_todos > 0 else "red"
- )
- )
else:
- file_missing = file_total
- file_extra = len(summary.get(filename, LocaleSummary({}, {})).extra_keys.get(lang, []))
- file_todos = 0
file_translated = 0
- file_coverage_percent = 0
- complete_percent = 0
+ file_todos = 0
+ file_coverage = 100 * file_translated / total_req if total_req else 100.0
+ if file_missing > 0:
style = "red"
+ elif file_todos > 0 or file_unused > 0:
+ style = "yellow"
+ else:
+ style = "bold green"
table.add_row(
f"[bold reverse]{filename}[/bold reverse]",
+ str(required_base_en),
+ str(required_plural),
+ str(total_req),
+ str(file_translated),
str(file_missing),
- str(file_extra),
+ f"{file_coverage:.1f}%",
str(file_todos),
- str(file_translated),
- str(file_total),
- f"{file_coverage_percent:.1f}%",
- f"{complete_percent:.1f}%",
+ str(file_unused),
style=style,
)
+ total_required_base += required_base_en
+ total_required_plural += required_plural
+ total_required += total_req
+ total_translated += file_translated
total_missing += file_missing
- total_extra += file_extra
total_todos += file_todos
- total_translated += file_translated
- total_total += file_total
-
- # Calculate totals for this language
- total_coverage_percent = 100 * total_translated / total_total if total_total else 100
- total_actual_translated = total_translated - total_todos
- total_complete_percent = 100 * total_actual_translated / total_translated if total_translated else 100
+ total_unused += file_unused
- coverage_per_language[lang] = total_coverage_percent
+ total_coverage = 100 * total_translated / total_required if total_required else 100.0
+ coverage_per_language[lang] = total_coverage
table.add_row(
"All files",
+ str(total_required_base),
+ str(total_required_plural),
+ str(total_required),
+ str(total_translated),
str(total_missing),
- str(total_extra),
+ f"{total_coverage:.1f}%",
str(total_todos),
- str(total_translated),
- str(total_total),
- f"{total_coverage_percent:.1f}%",
- f"{total_complete_percent:.1f}%",
- style="red" if total_complete_percent < 100 else "bold green",
+ str(total_unused),
+ style="red" if total_missing > 0 or total_todos > 0 or total_unused > 0 else "bold green",
)
for _lang, table in tables.items():
@@ -510,16 +549,16 @@ def sort_dict_keys(obj):
console.print(f"[green]Added missing translations to {lang_path}[/green]")
-def remove_extra_translations(language: str, summary: dict[str, LocaleSummary]):
+def remove_unused_translations(language: str, summary: dict[str, LocaleSummary]):
"""
- Remove extra translations for the selected language.
+ Remove unused translations for the selected language.
- Removes keys that are present in the language file but missing in the English file.
+ Removes keys that are present in the language file but are not required.
"""
console = get_console()
for filename, diff in summary.items():
- extra_keys = set(diff.extra_keys.get(language, []))
- if not extra_keys:
+ unused_keys = set(diff.unused_keys.get(language, []))
+ if not unused_keys:
continue
lang_path = LOCALES_DIR / language / filename
try:
@@ -528,12 +567,12 @@ def remove_extra_translations(language: str, summary: dict[str, LocaleSummary]):
console.print(f"[yellow]Failed to load {language} file {lang_path}: {e}[/yellow]")
continue
- # Helper to recursively remove extra keys
+ # Helper to recursively remove unused keys
def remove_keys(dst, prefix=""):
keys_to_remove = []
for k, v in list(dst.items()):
full_key = f"{prefix}.{k}" if prefix else k
- if full_key in extra_keys:
+ if full_key in unused_keys:
keys_to_remove.append(k)
elif isinstance(v, dict):
remove_keys(v, full_key)
@@ -554,7 +593,7 @@ def sort_dict_keys(obj):
with open(lang_path, "w", encoding="utf-8") as f:
json.dump(lang_data, f, ensure_ascii=False, indent=2)
f.write("\n") # Ensure newline at the end of the file
- console.print(f"[green]Removed {len(extra_keys)} extra translations from {lang_path}[/green]")
+ console.print(f"[green]Removed {len(unused_keys)} unused translations from {lang_path}[/green]")
@click.group(cls=BreezeGroup, name="ui", help="Tools for UI development and maintenance")
@@ -579,15 +618,15 @@ def ui_group():
help="Add missing translations for all languages except English, prefixed with 'TODO: translate:'.",
)
@click.option(
- "--remove-extra",
+ "--remove-unused",
is_flag=True,
default=False,
- help="Remove extra translations that are present in the language but missing in English.",
+ help="Remove unused translations (keys present in the language but not required).",
)
@option_verbose
@option_dry_run
def check_translation_completeness(
- language: str | None = None, add_missing: bool = False, remove_extra: bool = False
+ language: str | None = None, add_missing: bool = False, remove_unused: bool = False
):
locale_files = get_locale_files()
console = get_console()
@@ -606,13 +645,13 @@ def check_translation_completeness(
for filename, diff in summary.items():
filtered_summary[filename] = LocaleSummary(
missing_keys={lf.locale: diff.missing_keys.get(lf.locale, [])},
- extra_keys={lf.locale: diff.extra_keys.get(lf.locale, [])},
+ unused_keys={lf.locale: diff.unused_keys.get(lf.locale, [])},
)
add_missing_translations(lf.locale, filtered_summary)
# After adding, re-run the summary for all languages
summary, missing_counts = compare_keys(get_locale_files())
- if remove_extra and language != "en":
- # Loop through all languages except 'en' and remove extra translations
+ if remove_unused and language != "en":
+ # Loop through all languages except 'en' and remove unused translations
if language:
language_files = [lf for lf in locale_files if lf.locale == language]
else:
@@ -622,9 +661,9 @@ def check_translation_completeness(
for filename, diff in summary.items():
filtered_summary[filename] = LocaleSummary(
missing_keys={lf.locale: diff.missing_keys.get(lf.locale, [])},
- extra_keys={lf.locale: diff.extra_keys.get(lf.locale, [])},
+ unused_keys={lf.locale: diff.unused_keys.get(lf.locale, [])},
)
- remove_extra_translations(lf.locale, filtered_summary)
+ remove_unused_translations(lf.locale, filtered_summary)
# After removing, re-run the summary for all languages
summary, missing_counts = compare_keys(get_locale_files())
if language:
@@ -640,7 +679,7 @@ def check_translation_completeness(
for filename, diff in summary.items():
filtered_summary[filename] = LocaleSummary(
missing_keys={language: diff.missing_keys.get(language, [])},
- extra_keys={language: diff.extra_keys.get(language, [])},
+ unused_keys={language: diff.unused_keys.get(language, [])},
)
lang_diff = print_language_summary(
[lf for lf in locale_files if lf.locale == language], filtered_summary
@@ -651,7 +690,6 @@ def check_translation_completeness(
found_difference = found_difference or lang_diff
has_todos, coverage_per_language = print_translation_progress(
[lf for lf in locale_files if language is None or lf.locale == language],
- missing_counts,
summary,
)
if not found_difference and not has_todos:
diff --git a/dev/breeze/src/airflow_breeze/commands/ui_commands_config.py b/dev/breeze/src/airflow_breeze/commands/ui_commands_config.py
index ea5b19a6808da..066005d3abf29 100644
--- a/dev/breeze/src/airflow_breeze/commands/ui_commands_config.py
+++ b/dev/breeze/src/airflow_breeze/commands/ui_commands_config.py
@@ -30,7 +30,7 @@
"options": [
"--language",
"--add-missing",
- "--remove-extra",
+ "--remove-unused",
],
},
],
diff --git a/dev/breeze/src/airflow_breeze/global_constants.py b/dev/breeze/src/airflow_breeze/global_constants.py
index 151ae6944ba99..35d526c84b293 100644
--- a/dev/breeze/src/airflow_breeze/global_constants.py
+++ b/dev/breeze/src/airflow_breeze/global_constants.py
@@ -213,7 +213,7 @@
ALLOWED_INSTALL_MYSQL_CLIENT_TYPES = ["mariadb", "mysql"]
PIP_VERSION = "26.0.1"
-UV_VERSION = "0.10.7"
+UV_VERSION = "0.10.9"
DEFAULT_UV_HTTP_TIMEOUT = 300
DEFAULT_WSL2_HTTP_TIMEOUT = 900
diff --git a/dev/breeze/src/airflow_breeze/params/shell_params.py b/dev/breeze/src/airflow_breeze/params/shell_params.py
index d38880b1ecabe..5565806efee75 100644
--- a/dev/breeze/src/airflow_breeze/params/shell_params.py
+++ b/dev/breeze/src/airflow_breeze/params/shell_params.py
@@ -103,6 +103,7 @@
SCRIPTS_CI_DOCKER_COMPOSE_PROVIDERS_AND_TESTS_SOURCES_PATH,
SCRIPTS_CI_DOCKER_COMPOSE_REMOVE_SOURCES_PATH,
SCRIPTS_CI_DOCKER_COMPOSE_TESTS_SOURCES_PATH,
+ get_main_git_dir_for_worktree,
)
from airflow_breeze.utils.run_utils import commit_sha, run_command
from airflow_breeze.utils.shared_options import get_forced_answer, get_verbose
@@ -385,6 +386,7 @@ def compose_file(self) -> str:
compose_file_list.append(SCRIPTS_CI_DOCKER_COMPOSE_BASE_PATH)
self.add_docker_in_docker(compose_file_list)
+ self.add_git_worktree_mount(compose_file_list)
compose_file_list.extend(backend_files)
compose_file_list.append(SCRIPTS_CI_DOCKER_COMPOSE_FILES_PATH)
if os.environ.get("CI", "false") == "true":
@@ -530,6 +532,26 @@ def add_docker_in_docker(self, compose_file_list: list[Path]):
# the /var/run/docker.sock is available. See https://github.com/docker/for-mac/issues/6545
compose_file_list.append(SCRIPTS_CI_DOCKER_COMPOSE_DOCKER_SOCKET_PATH)
+ def add_git_worktree_mount(self, compose_file_list: list[Path]):
+ main_git_directory = get_main_git_dir_for_worktree()
+ if main_git_directory:
+ get_console().print(
+ f"[info]Detected git worktree. Mounting main git directory: {main_git_directory}[/]"
+ )
+ generated_compose_file = SCRIPTS_CI_DOCKER_COMPOSE_PATH / "_generated_git_worktree_mount.yml"
+ generated_compose_file.write_text(
+ f"""---
+services:
+ airflow:
+ volumes:
+ - type: bind
+ source: "{main_git_directory}"
+ target: "{main_git_directory}"
+ read_only: true
+"""
+ )
+ compose_file_list.append(generated_compose_file)
+
@cached_property
def rootless_docker(self) -> bool:
return is_docker_rootless()
diff --git a/dev/breeze/src/airflow_breeze/utils/docker_command_utils.py b/dev/breeze/src/airflow_breeze/utils/docker_command_utils.py
index 3e717569ddb45..e5a49a94dc3ac 100644
--- a/dev/breeze/src/airflow_breeze/utils/docker_command_utils.py
+++ b/dev/breeze/src/airflow_breeze/utils/docker_command_utils.py
@@ -37,6 +37,7 @@
SCRIPTS_DOCKER_PATH,
cleanup_python_generated_files,
create_mypy_volume_if_needed,
+ get_main_git_dir_for_worktree,
)
from airflow_breeze.utils.shared_options import get_verbose
from airflow_breeze.utils.visuals import ASCIIART, ASCIIART_STYLE, CHEATSHEET, CHEATSHEET_STYLE
@@ -659,21 +660,28 @@ def fix_ownership_using_docker(quiet: bool = True):
"run",
"-v",
f"{AIRFLOW_ROOT_PATH}:/opt/airflow/",
- "-e",
- f"HOST_OS={get_host_os()}",
- "-e",
- f"HOST_USER_ID={get_host_user_id()}",
- "-e",
- f"HOST_GROUP_ID={get_host_group_id()}",
- "-e",
- f"VERBOSE={str(get_verbose()).lower()}",
- "-e",
- f"DOCKER_IS_ROOTLESS={is_docker_rootless()}",
- "--rm",
- "-t",
- OWNERSHIP_CLEANUP_DOCKER_TAG,
- "/opt/airflow/scripts/in_container/run_fix_ownership.py",
]
+ main_git_directory = get_main_git_dir_for_worktree()
+ if main_git_directory:
+ cmd.extend(["-v", f"{main_git_directory}:{main_git_directory}:ro"])
+ cmd.extend(
+ [
+ "-e",
+ f"HOST_OS={get_host_os()}",
+ "-e",
+ f"HOST_USER_ID={get_host_user_id()}",
+ "-e",
+ f"HOST_GROUP_ID={get_host_group_id()}",
+ "-e",
+ f"VERBOSE={str(get_verbose()).lower()}",
+ "-e",
+ f"DOCKER_IS_ROOTLESS={is_docker_rootless()}",
+ "--rm",
+ "-t",
+ OWNERSHIP_CLEANUP_DOCKER_TAG,
+ "/opt/airflow/scripts/in_container/run_fix_ownership.py",
+ ]
+ )
run_command(cmd, text=True, check=False, quiet=quiet)
diff --git a/dev/breeze/src/airflow_breeze/utils/path_utils.py b/dev/breeze/src/airflow_breeze/utils/path_utils.py
index 6ee4863a093d2..b7081dbae351c 100644
--- a/dev/breeze/src/airflow_breeze/utils/path_utils.py
+++ b/dev/breeze/src/airflow_breeze/utils/path_utils.py
@@ -422,3 +422,29 @@ def cleanup_python_generated_files():
get_console().print("You can also remove those files manually using sudo.")
if get_verbose():
get_console().print("[info]Cleaned")
+
+
+def get_main_git_dir_for_worktree() -> Path | None:
+ """
+ Detect if we are in a git worktree and return the main repository's .git directory.
+
+ Git worktrees store a ``.git`` *file* (not a directory) containing a ``gitdir:`` reference
+ pointing to ``/.git/worktrees/``. This helper resolves that reference
+ (handling both absolute and relative paths) and returns the main ``.git`` directory
+ (i.e. the grandparent of the ``gitdir`` path).
+
+ :return: Absolute path to the main repository's ``.git`` directory, or ``None``
+ if the current checkout is not a worktree.
+ """
+ git_path = AIRFLOW_ROOT_PATH / ".git"
+ if git_path.is_file():
+ git_content = git_path.read_text().strip()
+ if git_content.startswith("gitdir:"):
+ gitdir = Path(git_content.removeprefix("gitdir:").strip())
+ if not gitdir.is_absolute():
+ gitdir = (AIRFLOW_ROOT_PATH / gitdir).resolve()
+ # gitdir points to /.git/worktrees/
+ # .parent.parent gives us /.git
+ main_git_dir = gitdir.parent.parent
+ return main_git_dir if main_git_dir.is_dir() else None
+ return None
diff --git a/dev/breeze/tests/test_git_worktree.py b/dev/breeze/tests/test_git_worktree.py
new file mode 100644
index 0000000000000..a0c9d439bd259
--- /dev/null
+++ b/dev/breeze/tests/test_git_worktree.py
@@ -0,0 +1,106 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from pathlib import Path
+from unittest import mock
+
+import pytest
+
+from airflow_breeze.utils.path_utils import get_main_git_dir_for_worktree
+
+
+class TestGetMainGitDirForWorktree:
+ """Tests for get_main_git_dir_for_worktree detection."""
+
+ def test_returns_none_when_dot_git_is_directory(self, tmp_path):
+ """Standard clone: .git is a directory, not a worktree."""
+ git_dir = tmp_path / ".git"
+ git_dir.mkdir()
+ with mock.patch("airflow_breeze.utils.path_utils.AIRFLOW_ROOT_PATH", tmp_path):
+ assert get_main_git_dir_for_worktree() is None
+
+ def test_returns_none_when_dot_git_missing(self, tmp_path):
+ """No .git at all — not a git repo."""
+ with mock.patch("airflow_breeze.utils.path_utils.AIRFLOW_ROOT_PATH", tmp_path):
+ assert get_main_git_dir_for_worktree() is None
+
+ def test_returns_none_when_dot_git_file_without_gitdir_prefix(self, tmp_path):
+ """.git file exists but does not start with 'gitdir: '."""
+ git_file = tmp_path / ".git"
+ git_file.write_text("something unexpected\n")
+ with mock.patch("airflow_breeze.utils.path_utils.AIRFLOW_ROOT_PATH", tmp_path):
+ assert get_main_git_dir_for_worktree() is None
+
+ def test_absolute_gitdir_path(self, tmp_path):
+ """Worktree with an absolute gitdir path resolves to the main .git directory."""
+ # Simulate: /main-repo/.git/worktrees/my-worktree
+ main_repo = tmp_path / "main-repo"
+ main_git = main_repo / ".git"
+ worktree_gitdir = main_git / "worktrees" / "my-worktree"
+ worktree_gitdir.mkdir(parents=True)
+
+ worktree_dir = tmp_path / "my-worktree"
+ worktree_dir.mkdir()
+ (worktree_dir / ".git").write_text(f"gitdir: {worktree_gitdir}\n")
+
+ with mock.patch("airflow_breeze.utils.path_utils.AIRFLOW_ROOT_PATH", worktree_dir):
+ result = get_main_git_dir_for_worktree()
+ assert result is not None
+ assert result == main_git
+
+ def test_relative_gitdir_path(self, tmp_path):
+ """Worktree with a relative gitdir path is resolved correctly."""
+ # Simulate: main-repo/.git/worktrees/my-worktree
+ main_repo = tmp_path / "main-repo"
+ main_git = main_repo / ".git"
+ worktree_gitdir = main_git / "worktrees" / "my-worktree"
+ worktree_gitdir.mkdir(parents=True)
+
+ worktree_dir = tmp_path / "my-worktree"
+ worktree_dir.mkdir()
+ # Write a relative path from worktree_dir to worktree_gitdir
+ relative_gitdir = Path("../main-repo/.git/worktrees/my-worktree")
+ (worktree_dir / ".git").write_text(f"gitdir: {relative_gitdir}\n")
+
+ with mock.patch("airflow_breeze.utils.path_utils.AIRFLOW_ROOT_PATH", worktree_dir):
+ result = get_main_git_dir_for_worktree()
+ assert result is not None
+ assert result.resolve() == main_git.resolve()
+
+ @pytest.mark.parametrize(
+ "gitdir_content",
+ [
+ "gitdir: {path}/worktrees/wt\n",
+ "gitdir: {path}/worktrees/wt",
+ "gitdir: {path}/worktrees/wt \n",
+ ],
+ ids=["trailing-newline", "no-trailing-newline", "extra-whitespace"],
+ )
+ def test_strips_whitespace_from_gitdir(self, tmp_path, gitdir_content):
+ """Whitespace and trailing newlines are stripped from the gitdir content."""
+ main_repo = tmp_path / "main-repo"
+ main_git = main_repo / ".git"
+ (main_git / "worktrees" / "wt").mkdir(parents=True)
+
+ content = gitdir_content.format(path=main_git)
+ (tmp_path / ".git").write_text(content)
+
+ with mock.patch("airflow_breeze.utils.path_utils.AIRFLOW_ROOT_PATH", tmp_path):
+ result = get_main_git_dir_for_worktree()
+ assert result is not None
+ assert result == main_git
diff --git a/dev/breeze/tests/test_ui_commands.py b/dev/breeze/tests/test_ui_commands.py
index 843d985ed0281..8f7b844be796a 100644
--- a/dev/breeze/tests/test_ui_commands.py
+++ b/dev/breeze/tests/test_ui_commands.py
@@ -61,6 +61,25 @@ def test_expand_plural_keys_polish(self):
assert "message_many" in expanded
assert "message_other" in expanded
+ def test_expand_plural_keys_only_expands_when_en_has_count_placeholder(self):
+ # When en has error_one without {{count}}, we must not require error_other
+ keys = {"error_one"}
+ en_key_to_value = {"error_one": "1 Error"}
+ expanded = expand_plural_keys(keys, "pt", en_key_to_value)
+ assert "error_one" in expanded
+ assert "error_other" not in expanded
+
+ def test_expand_plural_keys_expands_when_en_has_count_placeholder(self):
+ # Polish has 4 plural forms (_one, _few, _many, _other). Input has only _one and _other;
+ # expansion must add _few and _many so the test verifies the expansion logic ran.
+ keys = {"warning_one", "warning_other"}
+ en_key_to_value = {"warning_one": "1 Warning", "warning_other": "{{count}} Warnings"}
+ expanded = expand_plural_keys(keys, "pl", en_key_to_value)
+ assert "warning_one" in expanded
+ assert "warning_other" in expanded
+ assert "warning_few" in expanded
+ assert "warning_many" in expanded
+
class TestFlattenKeys:
def test_flatten_simple_dict(self):
@@ -112,7 +131,7 @@ def test_compare_keys_identical(self, tmp_path):
assert "test.json" in summary
assert summary["test.json"].missing_keys.get("de", []) == []
- assert summary["test.json"].extra_keys.get("de", []) == []
+ assert summary["test.json"].unused_keys.get("de", []) == []
finally:
ui_commands.LOCALES_DIR = original_locales_dir
@@ -171,16 +190,50 @@ def test_compare_keys_with_extra(self, tmp_path):
summary, missing_counts = compare_keys(locale_files)
assert "test.json" in summary
- assert "extra" in summary["test.json"].extra_keys.get("de", [])
+ assert "extra" in summary["test.json"].unused_keys.get("de", [])
+ finally:
+ ui_commands.LOCALES_DIR = original_locales_dir
+
+ def test_compare_keys_optional_plural_unused_when_no_count(self, tmp_path):
+ """Plural variant of EN base with no {{count}} in EN is reported as unused."""
+ en_dir = tmp_path / "en"
+ en_dir.mkdir()
+ de_dir = tmp_path / "de"
+ de_dir.mkdir()
+
+ # EN has only error_one (no {{count}}), so error_other is never used at runtime
+ en_data = {"dagWarnings": {"error_one": "1 Error"}}
+ de_data = {"dagWarnings": {"error_one": "1 Fehler", "error_other": "{{count}} Fehler"}}
+
+ (en_dir / "test.json").write_text(json.dumps(en_data))
+ (de_dir / "test.json").write_text(json.dumps(de_data))
+
+ import airflow_breeze.commands.ui_commands as ui_commands
+
+ original_locales_dir = ui_commands.LOCALES_DIR
+ ui_commands.LOCALES_DIR = tmp_path
+
+ try:
+ locale_files = [
+ LocaleFiles(locale="en", files=["test.json"]),
+ LocaleFiles(locale="de", files=["test.json"]),
+ ]
+ summary, _ = compare_keys(locale_files)
+
+ # EN base has no {{count}}, so error_other is not required and is unused
+ assert "dagWarnings.error_other" in summary["test.json"].unused_keys.get("de", [])
finally:
ui_commands.LOCALES_DIR = original_locales_dir
class TestLocaleSummary:
def test_locale_summary_creation(self):
- summary = LocaleSummary(missing_keys={"de": ["key1", "key2"]}, extra_keys={"de": ["key3"]})
+ summary = LocaleSummary(
+ missing_keys={"de": ["key1", "key2"]},
+ unused_keys={"de": ["key3"]},
+ )
assert summary.missing_keys == {"de": ["key1", "key2"]}
- assert summary.extra_keys == {"de": ["key3"]}
+ assert summary.unused_keys == {"de": ["key3"]}
class TestLocaleFiles:
@@ -255,7 +308,7 @@ def test_add_missing_translations(self, tmp_path):
try:
summary = LocaleSummary(
missing_keys={"de": ["farewell"]},
- extra_keys={"de": []},
+ unused_keys={"de": []},
)
add_missing_translations("de", {"test.json": summary})
@@ -267,9 +320,9 @@ def test_add_missing_translations(self, tmp_path):
ui_commands.LOCALES_DIR = original_locales_dir
-class TestRemoveExtraTranslations:
- def test_remove_extra_translations(self, tmp_path):
- from airflow_breeze.commands.ui_commands import remove_extra_translations
+class TestRemoveUnusedTranslations:
+ def test_remove_unused_translations(self, tmp_path):
+ from airflow_breeze.commands.ui_commands import remove_unused_translations
de_dir = tmp_path / "de"
de_dir.mkdir()
@@ -285,11 +338,11 @@ def test_remove_extra_translations(self, tmp_path):
try:
summary = LocaleSummary(
missing_keys={"de": []},
- extra_keys={"de": ["extra"]},
+ unused_keys={"de": ["extra"]},
)
- remove_extra_translations("de", {"test.json": summary})
+ remove_unused_translations("de", {"test.json": summary})
- # Check that the extra key was removed
+ # Check that the unused key was removed
de_data_updated = json.loads((de_dir / "test.json").read_text())
assert "extra" not in de_data_updated
assert "greeting" in de_data_updated
@@ -330,7 +383,7 @@ def test_natural_sort_matches_eslint(self, tmp_path):
try:
summary = LocaleSummary(
missing_keys={"de": list(en_data.keys())},
- extra_keys={"de": []},
+ unused_keys={"de": []},
)
add_missing_translations("de", {"test.json": summary})
diff --git a/dev/breeze/uv.lock b/dev/breeze/uv.lock
index b848f8ab77791..71f1d6f21c7de 100644
--- a/dev/breeze/uv.lock
+++ b/dev/breeze/uv.lock
@@ -74,7 +74,7 @@ requires-dist = [
{ name = "jinja2", specifier = ">=3.1.5" },
{ name = "jsonschema", specifier = ">=4.19.1" },
{ name = "packaging", specifier = ">=25.0" },
- { name = "prek", specifier = ">=0.3.4" },
+ { name = "prek", specifier = ">=0.3.5" },
{ name = "psutil", specifier = ">=5.9.6" },
{ name = "pygithub", specifier = ">=2.1.1" },
{ name = "pytest", specifier = ">=9.0.0" },
@@ -216,7 +216,7 @@ wheels = [
[[package]]
name = "black"
-version = "26.1.0"
+version = "26.3.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
@@ -228,62 +228,62 @@ dependencies = [
{ name = "tomli", marker = "python_full_version < '3.11'" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/13/88/560b11e521c522440af991d46848a2bde64b5f7202ec14e1f46f9509d328/black-26.1.0.tar.gz", hash = "sha256:d294ac3340eef9c9eb5d29288e96dc719ff269a88e27b396340459dd85da4c58", size = 658785, upload-time = "2026-01-18T04:50:11.993Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/51/1b/523329e713f965ad0ea2b7a047eeb003007792a0353622ac7a8cb2ee6fef/black-26.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ca699710dece84e3ebf6e92ee15f5b8f72870ef984bf944a57a777a48357c168", size = 1849661, upload-time = "2026-01-18T04:59:12.425Z" },
- { url = "https://files.pythonhosted.org/packages/14/82/94c0640f7285fa71c2f32879f23e609dd2aa39ba2641f395487f24a578e7/black-26.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5e8e75dabb6eb83d064b0db46392b25cabb6e784ea624219736e8985a6b3675d", size = 1689065, upload-time = "2026-01-18T04:59:13.993Z" },
- { url = "https://files.pythonhosted.org/packages/f0/78/474373cbd798f9291ed8f7107056e343fd39fef42de4a51c7fd0d360840c/black-26.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb07665d9a907a1a645ee41a0df8a25ffac8ad9c26cdb557b7b88eeeeec934e0", size = 1751502, upload-time = "2026-01-18T04:59:15.971Z" },
- { url = "https://files.pythonhosted.org/packages/29/89/59d0e350123f97bc32c27c4d79563432d7f3530dca2bff64d855c178af8b/black-26.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:7ed300200918147c963c87700ccf9966dceaefbbb7277450a8d646fc5646bf24", size = 1400102, upload-time = "2026-01-18T04:59:17.8Z" },
- { url = "https://files.pythonhosted.org/packages/e1/bc/5d866c7ae1c9d67d308f83af5462ca7046760158bbf142502bad8f22b3a1/black-26.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:c5b7713daea9bf943f79f8c3b46f361cc5229e0e604dcef6a8bb6d1c37d9df89", size = 1207038, upload-time = "2026-01-18T04:59:19.543Z" },
- { url = "https://files.pythonhosted.org/packages/30/83/f05f22ff13756e1a8ce7891db517dbc06200796a16326258268f4658a745/black-26.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3cee1487a9e4c640dc7467aaa543d6c0097c391dc8ac74eb313f2fbf9d7a7cb5", size = 1831956, upload-time = "2026-01-18T04:59:21.38Z" },
- { url = "https://files.pythonhosted.org/packages/7d/f2/b2c570550e39bedc157715e43927360312d6dd677eed2cc149a802577491/black-26.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d62d14ca31c92adf561ebb2e5f2741bf8dea28aef6deb400d49cca011d186c68", size = 1672499, upload-time = "2026-01-18T04:59:23.257Z" },
- { url = "https://files.pythonhosted.org/packages/7a/d7/990d6a94dc9e169f61374b1c3d4f4dd3037e93c2cc12b6f3b12bc663aa7b/black-26.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb1dafbbaa3b1ee8b4550a84425aac8874e5f390200f5502cf3aee4a2acb2f14", size = 1735431, upload-time = "2026-01-18T04:59:24.729Z" },
- { url = "https://files.pythonhosted.org/packages/36/1c/cbd7bae7dd3cb315dfe6eeca802bb56662cc92b89af272e014d98c1f2286/black-26.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:101540cb2a77c680f4f80e628ae98bd2bd8812fb9d72ade4f8995c5ff019e82c", size = 1400468, upload-time = "2026-01-18T04:59:27.381Z" },
- { url = "https://files.pythonhosted.org/packages/59/b1/9fe6132bb2d0d1f7094613320b56297a108ae19ecf3041d9678aec381b37/black-26.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:6f3977a16e347f1b115662be07daa93137259c711e526402aa444d7a88fdc9d4", size = 1207332, upload-time = "2026-01-18T04:59:28.711Z" },
- { url = "https://files.pythonhosted.org/packages/f5/13/710298938a61f0f54cdb4d1c0baeb672c01ff0358712eddaf29f76d32a0b/black-26.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6eeca41e70b5f5c84f2f913af857cf2ce17410847e1d54642e658e078da6544f", size = 1878189, upload-time = "2026-01-18T04:59:30.682Z" },
- { url = "https://files.pythonhosted.org/packages/79/a6/5179beaa57e5dbd2ec9f1c64016214057b4265647c62125aa6aeffb05392/black-26.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dd39eef053e58e60204f2cdf059e2442e2eb08f15989eefe259870f89614c8b6", size = 1700178, upload-time = "2026-01-18T04:59:32.387Z" },
- { url = "https://files.pythonhosted.org/packages/8c/04/c96f79d7b93e8f09d9298b333ca0d31cd9b2ee6c46c274fd0f531de9dc61/black-26.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9459ad0d6cd483eacad4c6566b0f8e42af5e8b583cee917d90ffaa3778420a0a", size = 1777029, upload-time = "2026-01-18T04:59:33.767Z" },
- { url = "https://files.pythonhosted.org/packages/49/f9/71c161c4c7aa18bdda3776b66ac2dc07aed62053c7c0ff8bbda8c2624fe2/black-26.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a19915ec61f3a8746e8b10adbac4a577c6ba9851fa4a9e9fbfbcf319887a5791", size = 1406466, upload-time = "2026-01-18T04:59:35.177Z" },
- { url = "https://files.pythonhosted.org/packages/4a/8b/a7b0f974e473b159d0ac1b6bcefffeb6bec465898a516ee5cc989503cbc7/black-26.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:643d27fb5facc167c0b1b59d0315f2674a6e950341aed0fc05cf307d22bf4954", size = 1216393, upload-time = "2026-01-18T04:59:37.18Z" },
- { url = "https://files.pythonhosted.org/packages/79/04/fa2f4784f7237279332aa735cdfd5ae2e7730db0072fb2041dadda9ae551/black-26.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ba1d768fbfb6930fc93b0ecc32a43d8861ded16f47a40f14afa9bb04ab93d304", size = 1877781, upload-time = "2026-01-18T04:59:39.054Z" },
- { url = "https://files.pythonhosted.org/packages/cf/ad/5a131b01acc0e5336740a039628c0ab69d60cf09a2c87a4ec49f5826acda/black-26.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2b807c240b64609cb0e80d2200a35b23c7df82259f80bef1b2c96eb422b4aac9", size = 1699670, upload-time = "2026-01-18T04:59:41.005Z" },
- { url = "https://files.pythonhosted.org/packages/da/7c/b05f22964316a52ab6b4265bcd52c0ad2c30d7ca6bd3d0637e438fc32d6e/black-26.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1de0f7d01cc894066a1153b738145b194414cc6eeaad8ef4397ac9abacf40f6b", size = 1775212, upload-time = "2026-01-18T04:59:42.545Z" },
- { url = "https://files.pythonhosted.org/packages/a6/a3/e8d1526bea0446e040193185353920a9506eab60a7d8beb062029129c7d2/black-26.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:91a68ae46bf07868963671e4d05611b179c2313301bd756a89ad4e3b3db2325b", size = 1409953, upload-time = "2026-01-18T04:59:44.357Z" },
- { url = "https://files.pythonhosted.org/packages/c7/5a/d62ebf4d8f5e3a1daa54adaab94c107b57be1b1a2f115a0249b41931e188/black-26.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:be5e2fe860b9bd9edbf676d5b60a9282994c03fbbd40fe8f5e75d194f96064ca", size = 1217707, upload-time = "2026-01-18T04:59:45.719Z" },
- { url = "https://files.pythonhosted.org/packages/6a/83/be35a175aacfce4b05584ac415fd317dd6c24e93a0af2dcedce0f686f5d8/black-26.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:9dc8c71656a79ca49b8d3e2ce8103210c9481c57798b48deeb3a8bb02db5f115", size = 1871864, upload-time = "2026-01-18T04:59:47.586Z" },
- { url = "https://files.pythonhosted.org/packages/a5/f5/d33696c099450b1274d925a42b7a030cd3ea1f56d72e5ca8bbed5f52759c/black-26.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b22b3810451abe359a964cc88121d57f7bce482b53a066de0f1584988ca36e79", size = 1701009, upload-time = "2026-01-18T04:59:49.443Z" },
- { url = "https://files.pythonhosted.org/packages/1b/87/670dd888c537acb53a863bc15abbd85b22b429237d9de1b77c0ed6b79c42/black-26.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:53c62883b3f999f14e5d30b5a79bd437236658ad45b2f853906c7cbe79de00af", size = 1767806, upload-time = "2026-01-18T04:59:50.769Z" },
- { url = "https://files.pythonhosted.org/packages/fe/9c/cd3deb79bfec5bcf30f9d2100ffeec63eecce826eb63e3961708b9431ff1/black-26.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:f016baaadc423dc960cdddf9acae679e71ee02c4c341f78f3179d7e4819c095f", size = 1433217, upload-time = "2026-01-18T04:59:52.218Z" },
- { url = "https://files.pythonhosted.org/packages/4e/29/f3be41a1cf502a283506f40f5d27203249d181f7a1a2abce1c6ce188035a/black-26.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:66912475200b67ef5a0ab665011964bf924745103f51977a78b4fb92a9fc1bf0", size = 1245773, upload-time = "2026-01-18T04:59:54.457Z" },
- { url = "https://files.pythonhosted.org/packages/e4/3d/51bdb3ecbfadfaf825ec0c75e1de6077422b4afa2091c6c9ba34fbfc0c2d/black-26.1.0-py3-none-any.whl", hash = "sha256:1054e8e47ebd686e078c0bb0eaf31e6ce69c966058d122f2c0c950311f9f3ede", size = 204010, upload-time = "2026-01-18T04:50:09.978Z" },
+sdist = { url = "https://files.pythonhosted.org/packages/11/5f/25b7b149b8b7d3b958efa4faa56446560408c0f2651108a517526de0320a/black-26.3.0.tar.gz", hash = "sha256:4d438dfdba1c807c6c7c63c4f15794dda0820d2222e7c4105042ac9ddfc5dd0b", size = 664127, upload-time = "2026-03-06T17:42:33.7Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/e9/45/0df73428226c2197b8b1e2ca15654f85cece1efe5f060c910b641a35de4a/black-26.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:135bf8a352e35b3bfba4999c256063d8d86514654599eca7635e914a55d60ec3", size = 1866623, upload-time = "2026-03-06T17:46:07.622Z" },
+ { url = "https://files.pythonhosted.org/packages/40/e1/7467fcccf3532853b013bee22c9cdef6aa3314a58ccc73eb5a8a2750e50e/black-26.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6024a2959b6c62c311c564ce23ce0eaa977a50ed52a53f7abc83d2c9eb62b8d8", size = 1703733, upload-time = "2026-03-06T17:46:09.334Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/72/ceb0a5091b6dff654f77ee6488b91d45fbea1385338798935eb83090d27e/black-26.3.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:264144203ea3374542a1591b6fb317561662d074bce5d91ad6afa8d8d3e4ec3d", size = 1768094, upload-time = "2026-03-06T17:46:11.182Z" },
+ { url = "https://files.pythonhosted.org/packages/49/cc/6af7e15fb728f30f3e3d4257d2f3d3fe5c5f4ada30b0e8feb92f50118d5c/black-26.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:1a15d1386dce3af3993bf9baeb68d3e492cbb003dae05c3ecf8530a9b75edf85", size = 1413004, upload-time = "2026-03-06T17:46:12.867Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/04/7f5ffd40078ab54efa738797e1d547a3fce893f1de212a7a2e65b4a36254/black-26.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:d86a70bf048235aff62a79e229fe5d9e7809c7a05a3dd12982e7ccdc2678e096", size = 1219839, upload-time = "2026-03-06T17:46:14.133Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/ec/e4db9f2b2db8226ae20d48b589c69fd64477657bf241c8ccaea3bc4feafa/black-26.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3da07abe65732483e915ab7f9c7c50332c293056436e9519373775d62539607c", size = 1851905, upload-time = "2026-03-06T17:46:15.447Z" },
+ { url = "https://files.pythonhosted.org/packages/62/2c/ccecfcbd6a0610ecf554e852a146f053eaeb5b281dd9cb634338518c765e/black-26.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fc9fd683ccabc3dc9791b93db494d93b5c6c03b105453b76d71e5474e9dfa6e7", size = 1689299, upload-time = "2026-03-06T17:46:17.396Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/53/8dcb860242012d6da9c6b1b930c3e4c947eb42feb1fc70f2a4e7332c90c5/black-26.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e2c7e2c5ee09ff575869258b2c07064c952637918fc5e15f6ebd45e45eae0aa", size = 1753902, upload-time = "2026-03-06T17:46:19.592Z" },
+ { url = "https://files.pythonhosted.org/packages/5d/21/f37b3efcc8cf2d01ec9eb5466598aa53bed2292db236723ac4571e24c4de/black-26.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:a849286bfc3054eaeb233b6df9056fcf969ee18bf7ecb71b0257e838a0f05e6d", size = 1413841, upload-time = "2026-03-06T17:46:20.981Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/74/e70f5f2a74301d8f10276b90715699d51d7db1c3dd79cf13966d32ba7b18/black-26.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:c93c83af43cda73ed8265d001214779ab245fa7a861a75b3e43828f4fb1f5657", size = 1220105, upload-time = "2026-03-06T17:46:23.269Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/76/b21711045b7f4c4f1774048d0b34dd10a265c42255658b251ce3303ae3c7/black-26.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c2b1e5eec220b419e3591a0aaa6351bd3a9c01fe6291fbaf76d84308eb7a2ede", size = 1895944, upload-time = "2026-03-06T17:46:24.841Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/c3/8c56e73283326bc92a36101c660228fff09a2403a57a03cacf3f7f84cf62/black-26.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1bab64de70bccc992432bee56cdffbe004ceeaa07352127c386faa87e81f9261", size = 1718669, upload-time = "2026-03-06T17:46:26.639Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/8b/712a3ae8f17c1f3cd6f9ac2fffb167a27192f5c7aba68724e8c4ab8474ad/black-26.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b6c5f734290803b7b26493ffd734b02b72e6c90d82d45ac4d5b862b9bdf7720", size = 1794844, upload-time = "2026-03-06T17:46:28.334Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/5b/ee955040e446df86473287dd24dc69c80dd05e02cc358bca90e22059f7b1/black-26.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:7c767396af15b54e1a6aae99ddf241ae97e589f666b1d22c4b6618282a04e4ca", size = 1420461, upload-time = "2026-03-06T17:46:29.965Z" },
+ { url = "https://files.pythonhosted.org/packages/12/77/40b8bd44f032bb34c9ebf47ffc5bb47a2520d29e0a4b8a780ab515223b5a/black-26.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:765fd6ddd00f35c55250fdc6b790c272d54ac3f44da719cc42df428269b45980", size = 1229667, upload-time = "2026-03-06T17:46:31.654Z" },
+ { url = "https://files.pythonhosted.org/packages/28/c3/21a834ce3de02c64221243f2adac63fa3c3f441efdb3adbf4136b33dfeb0/black-26.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:59754fd8f43ef457be190594c07a52c999e22cb1534dc5344bff1d46fdf1027d", size = 1895195, upload-time = "2026-03-06T17:46:33.12Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/f9/212d9697dd78362dadb778d4616b74c8c2cf7f2e4a55aac2adeb0576f2e9/black-26.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1fd94cfee67b8d336761a0b08629a25938e4a491c440951ce517a7209c99b5ff", size = 1718472, upload-time = "2026-03-06T17:46:34.576Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/dd/da980b2f512441375b73cb511f38a2c3db4be83ccaa1302b8d39c9fa2dff/black-26.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f7b3e653a90ca1ef4e821c20f8edaee80b649c38d2532ed2e9073a9534b14a7", size = 1793741, upload-time = "2026-03-06T17:46:36.261Z" },
+ { url = "https://files.pythonhosted.org/packages/93/11/cd69ae8826fe3bc6eaf525c8c557266d522b258154a2968eb46d6d25fac7/black-26.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:f8fb9d7c2496adc83614856e1f6e55a9ce4b7ae7fc7f45b46af9189ddb493464", size = 1422522, upload-time = "2026-03-06T17:46:37.607Z" },
+ { url = "https://files.pythonhosted.org/packages/75/f5/647cf50255203eb286be197925e86eedc101d5409147505db3e463229228/black-26.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:e8618c1d06838f56afbcb3ffa1aa16436cec62b86b38c7b32ca86f53948ffb91", size = 1231807, upload-time = "2026-03-06T17:46:39.072Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/77/b197e701f15fd694d20d8ee0001efa2e29eba917aa7c3610ff7b10ae0f88/black-26.3.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d0c6f64ead44f4369c66f1339ecf68e99b40f2e44253c257f7807c5a3ef0ca32", size = 1889209, upload-time = "2026-03-06T17:46:40.453Z" },
+ { url = "https://files.pythonhosted.org/packages/93/85/b4d4924ac898adc2e39fc7a923bed99797535bc16dea4bc63944c3903c2b/black-26.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ed6f0809134e51ec4a7509e069cdfa42bf996bd0fd1df6d3146b907f36e28893", size = 1720830, upload-time = "2026-03-06T17:46:42.009Z" },
+ { url = "https://files.pythonhosted.org/packages/00/b1/5c0bf29fe5b43fcc6f3e8480c6566d21a02d4e702b3846944e7daa06dea9/black-26.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc6ac0ea5dd5fa6311ca82edfa3620cba0ed0426022d10d2d5d39aedbf3e1958", size = 1787676, upload-time = "2026-03-06T17:46:43.382Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/ce/cc8cf14806c144d6a16512272c537d5450f50675d3e8c038705430e90fd9/black-26.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:884bc0aefa96adabcba0b77b10e9775fd52d4b766e88c44dc6f41f7c82787fc8", size = 1445406, upload-time = "2026-03-06T17:46:44.948Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/bb/049ea0fad9f8bdec7b647948adcf74bb720bd71dcb213decd553e05b2699/black-26.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:be3bd02aab5c4ab03703172f5530ddc8fc8b5b7bb8786230e84c9e011cee9ca1", size = 1257945, upload-time = "2026-03-06T17:46:46.432Z" },
+ { url = "https://files.pythonhosted.org/packages/39/d7/7360654ba4f8b41afcaeb5aca973cfea5591da75aff79b0a8ae0bb8883f6/black-26.3.0-py3-none-any.whl", hash = "sha256:e825d6b121910dff6f04d7691f826d2449327e8e71c26254c030c4f3d2311985", size = 206848, upload-time = "2026-03-06T17:42:31.133Z" },
]
[[package]]
name = "boto3"
-version = "1.42.59"
+version = "1.42.64"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "botocore" },
{ name = "jmespath" },
{ name = "s3transfer" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/b0/4e/499cb52aaee9468c346bcc1158965e24e72b4e2a20052725b680e0ac949b/boto3-1.42.59.tar.gz", hash = "sha256:6c4a14a4eb37b58a9048901bdeefbe1c529638b73e8f55413319a25f010ca211", size = 112725, upload-time = "2026-02-27T20:25:33.228Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/27/3e/3f5f58100340f6576aa93da0fe46cabd91ea19baa746b80bd1d46498b0db/boto3-1.42.64.tar.gz", hash = "sha256:58d47897a26adbc22f6390d133dab772fb606ba72695291a8c9e20cba1c7fd23", size = 112773, upload-time = "2026-03-09T19:52:00.407Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/17/c0/22d868b9408dc5a33935a72896ec8d638b2766c459668d1b37c3e5ac2066/boto3-1.42.59-py3-none-any.whl", hash = "sha256:7a66e3e8e2087ea4403e135e9de592e6d63fc9a91080d8dac415bb74df873a72", size = 140557, upload-time = "2026-02-27T20:25:31.774Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/87/2f02a6db0828f4579aedef7e34ec15262e4aa402d31f31bdbc64ae8e471b/boto3-1.42.64-py3-none-any.whl", hash = "sha256:2ca6b472937a54ba74af0b4bede582ba98c070408db1061fc26d5c3aa8e6e7e6", size = 140557, upload-time = "2026-03-09T19:51:57.652Z" },
]
[[package]]
name = "botocore"
-version = "1.42.59"
+version = "1.42.64"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "jmespath" },
{ name = "python-dateutil" },
{ name = "urllib3" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/45/ae/50fb33bdf1911c216d50f98d989dd032a506f054cf829ebd737c6fa7e3e6/botocore-1.42.59.tar.gz", hash = "sha256:5314f19e1da8fc0ebc41bdb8bbe17c9a7397d87f4d887076ac8bdef972a34138", size = 14950271, upload-time = "2026-02-27T20:25:20.614Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/d3/3c/ac4bc939da695d2c648bf28f7b204ab741e4504e81749ccf943403cc07ca/botocore-1.42.64.tar.gz", hash = "sha256:4ee2aece227b9171ace8b749af694a77ab984fceab1639f2626bd0d6fb1aa69d", size = 14967869, upload-time = "2026-03-09T19:51:46.213Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/59/df/9d52819e0d804ead073d53ab1823bc0f0cb172a250fba31107b0b43fbb04/botocore-1.42.59-py3-none-any.whl", hash = "sha256:d2f2ff7ecc31e86ef46b5daee112cfbca052c13801285fb23af909f7bff5b657", size = 14619293, upload-time = "2026-02-27T20:25:17.455Z" },
+ { url = "https://files.pythonhosted.org/packages/33/0f/a0feb9a93da8f583217432dce71ce1940d6d8aa5884bad340872a504ba3f/botocore-1.42.64-py3-none-any.whl", hash = "sha256:f77c5cb76ed30576ed0bc73b591265d03dddffff02a9208d3ee0c790f43d3cd2", size = 14641339, upload-time = "2026-03-09T19:51:41.244Z" },
]
[[package]]
@@ -379,91 +379,91 @@ wheels = [
[[package]]
name = "charset-normalizer"
-version = "3.4.4"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709, upload-time = "2025-10-14T04:40:11.385Z" },
- { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814, upload-time = "2025-10-14T04:40:13.135Z" },
- { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467, upload-time = "2025-10-14T04:40:14.728Z" },
- { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280, upload-time = "2025-10-14T04:40:16.14Z" },
- { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454, upload-time = "2025-10-14T04:40:17.567Z" },
- { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609, upload-time = "2025-10-14T04:40:19.08Z" },
- { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849, upload-time = "2025-10-14T04:40:20.607Z" },
- { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586, upload-time = "2025-10-14T04:40:21.719Z" },
- { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290, upload-time = "2025-10-14T04:40:23.069Z" },
- { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663, upload-time = "2025-10-14T04:40:24.17Z" },
- { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964, upload-time = "2025-10-14T04:40:25.368Z" },
- { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064, upload-time = "2025-10-14T04:40:26.806Z" },
- { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015, upload-time = "2025-10-14T04:40:28.284Z" },
- { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792, upload-time = "2025-10-14T04:40:29.613Z" },
- { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198, upload-time = "2025-10-14T04:40:30.644Z" },
- { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262, upload-time = "2025-10-14T04:40:32.108Z" },
- { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" },
- { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" },
- { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" },
- { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" },
- { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" },
- { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" },
- { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" },
- { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" },
- { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" },
- { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" },
- { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" },
- { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" },
- { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" },
- { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" },
- { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" },
- { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" },
- { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" },
- { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" },
- { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" },
- { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" },
- { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" },
- { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" },
- { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" },
- { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" },
- { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" },
- { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" },
- { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" },
- { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" },
- { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" },
- { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" },
- { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" },
- { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" },
- { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" },
- { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" },
- { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" },
- { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" },
- { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" },
- { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" },
- { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" },
- { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" },
- { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" },
- { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" },
- { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" },
- { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" },
- { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" },
- { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" },
- { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" },
- { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" },
- { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" },
- { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" },
- { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" },
- { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" },
- { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" },
- { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" },
- { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" },
- { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" },
- { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" },
- { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" },
- { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" },
- { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" },
- { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" },
- { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" },
- { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" },
- { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" },
- { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" },
+version = "3.4.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/1d/35/02daf95b9cd686320bb622eb148792655c9412dbb9b67abb5694e5910a24/charset_normalizer-3.4.5.tar.gz", hash = "sha256:95adae7b6c42a6c5b5b559b1a99149f090a57128155daeea91732c8d970d8644", size = 134804, upload-time = "2026-03-06T06:03:19.46Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/a7/21/a2b1505639008ba2e6ef03733a81fc6cfd6a07ea6139a2b76421230b8dad/charset_normalizer-3.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4167a621a9a1a986c73777dbc15d4b5eac8ac5c10393374109a343d4013ec765", size = 283319, upload-time = "2026-03-06T06:00:26.433Z" },
+ { url = "https://files.pythonhosted.org/packages/70/67/df234c29b68f4e1e095885c9db1cb4b69b8aba49cf94fac041db4aaf1267/charset_normalizer-3.4.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3f64c6bf8f32f9133b668c7f7a7cbdbc453412bc95ecdbd157f3b1e377a92990", size = 189974, upload-time = "2026-03-06T06:00:28.222Z" },
+ { url = "https://files.pythonhosted.org/packages/df/7f/fc66af802961c6be42e2c7b69c58f95cbd1f39b0e81b3365d8efe2a02a04/charset_normalizer-3.4.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:568e3c34b58422075a1b49575a6abc616d9751b4d61b23f712e12ebb78fe47b2", size = 207866, upload-time = "2026-03-06T06:00:29.769Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/23/404eb36fac4e95b833c50e305bba9a241086d427bb2167a42eac7c4f7da4/charset_normalizer-3.4.5-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:036c079aa08a6a592b82487f97c60b439428320ed1b2ea0b3912e99d30c77765", size = 203239, upload-time = "2026-03-06T06:00:31.086Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/2f/8a1d989bfadd120c90114ab33e0d2a0cbde05278c1fc15e83e62d570f50a/charset_normalizer-3.4.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:340810d34ef83af92148e96e3e44cb2d3f910d2bf95e5618a5c467d9f102231d", size = 196529, upload-time = "2026-03-06T06:00:32.608Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/0c/c75f85ff7ca1f051958bb518cd43922d86f576c03947a050fbedfdfb4f15/charset_normalizer-3.4.5-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:cd2d0f0ec9aa977a27731a3209ebbcacebebaf41f902bd453a928bfd281cf7f8", size = 184152, upload-time = "2026-03-06T06:00:33.93Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/20/4ed37f6199af5dde94d4aeaf577f3813a5ec6635834cda1d957013a09c76/charset_normalizer-3.4.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0b362bcd27819f9c07cbf23db4e0e8cd4b44c5ecd900c2ff907b2b92274a7412", size = 195226, upload-time = "2026-03-06T06:00:35.469Z" },
+ { url = "https://files.pythonhosted.org/packages/28/31/7ba1102178cba7c34dcc050f43d427172f389729e356038f0726253dd914/charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:77be992288f720306ab4108fe5c74797de327f3248368dfc7e1a916d6ed9e5a2", size = 192933, upload-time = "2026-03-06T06:00:36.83Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/23/f86443ab3921e6a60b33b93f4a1161222231f6c69bc24fb18f3bee7b8518/charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:8b78d8a609a4b82c273257ee9d631ded7fac0d875bdcdccc109f3ee8328cfcb1", size = 185647, upload-time = "2026-03-06T06:00:38.367Z" },
+ { url = "https://files.pythonhosted.org/packages/82/44/08b8be891760f1f5a6d23ce11d6d50c92981603e6eb740b4f72eea9424e2/charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ba20bdf69bd127f66d0174d6f2a93e69045e0b4036dc1ca78e091bcc765830c4", size = 209533, upload-time = "2026-03-06T06:00:41.931Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/5f/df114f23406199f8af711ddccfbf409ffbc5b7cdc18fa19644997ff0c9bb/charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:76a9d0de4d0eab387822e7b35d8f89367dd237c72e82ab42b9f7bf5e15ada00f", size = 195901, upload-time = "2026-03-06T06:00:43.978Z" },
+ { url = "https://files.pythonhosted.org/packages/07/83/71ef34a76fe8aa05ff8f840244bda2d61e043c2ef6f30d200450b9f6a1be/charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8fff79bf5978c693c9b1a4d71e4a94fddfb5fe744eb062a318e15f4a2f63a550", size = 204950, upload-time = "2026-03-06T06:00:45.202Z" },
+ { url = "https://files.pythonhosted.org/packages/58/40/0253be623995365137d7dc68e45245036207ab2227251e69a3d93ce43183/charset_normalizer-3.4.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c7e84e0c0005e3bdc1a9211cd4e62c78ba80bc37b2365ef4410cd2007a9047f2", size = 198546, upload-time = "2026-03-06T06:00:46.481Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/5c/5f3cb5b259a130895ef5ae16b38eaf141430fa3f7af50cd06c5d67e4f7b2/charset_normalizer-3.4.5-cp310-cp310-win32.whl", hash = "sha256:58ad8270cfa5d4bef1bc85bd387217e14ff154d6630e976c6f56f9a040757475", size = 132516, upload-time = "2026-03-06T06:00:47.924Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/c3/84fb174e7770f2df2e1a2115090771bfbc2227fb39a765c6d00568d1aab4/charset_normalizer-3.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:02a9d1b01c1e12c27883b0c9349e0bcd9ae92e727ff1a277207e1a262b1cbf05", size = 142906, upload-time = "2026-03-06T06:00:49.389Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/b2/6f852f8b969f2cbd0d4092d2e60139ab1af95af9bb651337cae89ec0f684/charset_normalizer-3.4.5-cp310-cp310-win_arm64.whl", hash = "sha256:039215608ac7b358c4da0191d10fc76868567fbf276d54c14721bdedeb6de064", size = 133258, upload-time = "2026-03-06T06:00:51.051Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/9e/bcec3b22c64ecec47d39bf5167c2613efd41898c019dccd4183f6aa5d6a7/charset_normalizer-3.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:610f72c0ee565dfb8ae1241b666119582fdbfe7c0975c175be719f940e110694", size = 279531, upload-time = "2026-03-06T06:00:52.252Z" },
+ { url = "https://files.pythonhosted.org/packages/58/12/81fd25f7e7078ab5d1eedbb0fac44be4904ae3370a3bf4533c8f2d159acd/charset_normalizer-3.4.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:60d68e820af339df4ae8358c7a2e7596badeb61e544438e489035f9fbf3246a5", size = 188006, upload-time = "2026-03-06T06:00:53.8Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/6e/f2d30e8c27c1b0736a6520311982cf5286cfc7f6cac77d7bc1325e3a23f2/charset_normalizer-3.4.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b473fc8dca1c3ad8559985794815f06ca3fc71942c969129070f2c3cdf7281", size = 205085, upload-time = "2026-03-06T06:00:55.311Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/90/d12cefcb53b5931e2cf792a33718d7126efb116a320eaa0742c7059a95e4/charset_normalizer-3.4.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d4eb8ac7469b2a5d64b5b8c04f84d8bf3ad340f4514b98523805cbf46e3b3923", size = 200545, upload-time = "2026-03-06T06:00:56.532Z" },
+ { url = "https://files.pythonhosted.org/packages/03/f4/44d3b830a20e89ff82a3134912d9a1cf6084d64f3b95dcad40f74449a654/charset_normalizer-3.4.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bcb3227c3d9aaf73eaaab1db7ccd80a8995c509ee9941e2aae060ca6e4e5d81", size = 193863, upload-time = "2026-03-06T06:00:57.823Z" },
+ { url = "https://files.pythonhosted.org/packages/25/4b/f212119c18a6320a9d4a730d1b4057875cdeabf21b3614f76549042ef8a8/charset_normalizer-3.4.5-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:75ee9c1cce2911581a70a3c0919d8bccf5b1cbc9b0e5171400ec736b4b569497", size = 181827, upload-time = "2026-03-06T06:00:59.323Z" },
+ { url = "https://files.pythonhosted.org/packages/74/00/b26158e48b425a202a92965f8069e8a63d9af1481dfa206825d7f74d2a3c/charset_normalizer-3.4.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d1401945cb77787dbd3af2446ff2d75912327c4c3a1526ab7955ecf8600687c", size = 191085, upload-time = "2026-03-06T06:01:00.546Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/c2/1c1737bf6fd40335fe53d28fe49afd99ee4143cc57a845e99635ce0b9b6d/charset_normalizer-3.4.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a45e504f5e1be0bd385935a8e1507c442349ca36f511a47057a71c9d1d6ea9e", size = 190688, upload-time = "2026-03-06T06:01:02.479Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/3d/abb5c22dc2ef493cd56522f811246a63c5427c08f3e3e50ab663de27fcf4/charset_normalizer-3.4.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:e09f671a54ce70b79a1fc1dc6da3072b7ef7251fadb894ed92d9aa8218465a5f", size = 183077, upload-time = "2026-03-06T06:01:04.231Z" },
+ { url = "https://files.pythonhosted.org/packages/44/33/5298ad4d419a58e25b3508e87f2758d1442ff00c2471f8e0403dab8edad5/charset_normalizer-3.4.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d01de5e768328646e6a3fa9e562706f8f6641708c115c62588aef2b941a4f88e", size = 206706, upload-time = "2026-03-06T06:01:05.773Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/17/51e7895ac0f87c3b91d276a449ef09f5532a7529818f59646d7a55089432/charset_normalizer-3.4.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:131716d6786ad5e3dc542f5cc6f397ba3339dc0fb87f87ac30e550e8987756af", size = 191665, upload-time = "2026-03-06T06:01:07.473Z" },
+ { url = "https://files.pythonhosted.org/packages/90/8f/cce9adf1883e98906dbae380d769b4852bb0fa0004bc7d7a2243418d3ea8/charset_normalizer-3.4.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a374cc0b88aa710e8865dc1bd6edb3743c59f27830f0293ab101e4cf3ce9f85", size = 201950, upload-time = "2026-03-06T06:01:08.973Z" },
+ { url = "https://files.pythonhosted.org/packages/08/ca/bce99cd5c397a52919e2769d126723f27a4c037130374c051c00470bcd38/charset_normalizer-3.4.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d31f0d1671e1534e395f9eb84a68e0fb670e1edb1fe819a9d7f564ae3bc4e53f", size = 195830, upload-time = "2026-03-06T06:01:10.155Z" },
+ { url = "https://files.pythonhosted.org/packages/87/4f/2e3d023a06911f1281f97b8f036edc9872167036ca6f55cc874a0be6c12c/charset_normalizer-3.4.5-cp311-cp311-win32.whl", hash = "sha256:cace89841c0599d736d3d74a27bc5821288bb47c5441923277afc6059d7fbcb4", size = 132029, upload-time = "2026-03-06T06:01:11.706Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/1f/a853b73d386521fd44b7f67ded6b17b7b2367067d9106a5c4b44f9a34274/charset_normalizer-3.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:f8102ae93c0bc863b1d41ea0f4499c20a83229f52ed870850892df555187154a", size = 142404, upload-time = "2026-03-06T06:01:12.865Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/10/dba36f76b71c38e9d391abe0fd8a5b818790e053c431adecfc98c35cd2a9/charset_normalizer-3.4.5-cp311-cp311-win_arm64.whl", hash = "sha256:ed98364e1c262cf5f9363c3eca8c2df37024f52a8fa1180a3610014f26eac51c", size = 132796, upload-time = "2026-03-06T06:01:14.106Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/b6/9ee9c1a608916ca5feae81a344dffbaa53b26b90be58cc2159e3332d44ec/charset_normalizer-3.4.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed97c282ee4f994ef814042423a529df9497e3c666dca19be1d4cd1129dc7ade", size = 280976, upload-time = "2026-03-06T06:01:15.276Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/d8/a54f7c0b96f1df3563e9190f04daf981e365a9b397eedfdfb5dbef7e5c6c/charset_normalizer-3.4.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0294916d6ccf2d069727d65973c3a1ca477d68708db25fd758dd28b0827cff54", size = 189356, upload-time = "2026-03-06T06:01:16.511Z" },
+ { url = "https://files.pythonhosted.org/packages/42/69/2bf7f76ce1446759a5787cb87d38f6a61eb47dbbdf035cfebf6347292a65/charset_normalizer-3.4.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dc57a0baa3eeedd99fafaef7511b5a6ef4581494e8168ee086031744e2679467", size = 206369, upload-time = "2026-03-06T06:01:17.853Z" },
+ { url = "https://files.pythonhosted.org/packages/10/9c/949d1a46dab56b959d9a87272482195f1840b515a3380e39986989a893ae/charset_normalizer-3.4.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ed1a9a204f317ef879b32f9af507d47e49cd5e7f8e8d5d96358c98373314fc60", size = 203285, upload-time = "2026-03-06T06:01:19.473Z" },
+ { url = "https://files.pythonhosted.org/packages/67/5c/ae30362a88b4da237d71ea214a8c7eb915db3eec941adda511729ac25fa2/charset_normalizer-3.4.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ad83b8f9379176c841f8865884f3514d905bcd2a9a3b210eaa446e7d2223e4d", size = 196274, upload-time = "2026-03-06T06:01:20.728Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/07/c9f2cb0e46cb6d64fdcc4f95953747b843bb2181bda678dc4e699b8f0f9a/charset_normalizer-3.4.5-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:a118e2e0b5ae6b0120d5efa5f866e58f2bb826067a646431da4d6a2bdae7950e", size = 184715, upload-time = "2026-03-06T06:01:22.194Z" },
+ { url = "https://files.pythonhosted.org/packages/36/64/6b0ca95c44fddf692cd06d642b28f63009d0ce325fad6e9b2b4d0ef86a52/charset_normalizer-3.4.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:754f96058e61a5e22e91483f823e07df16416ce76afa4ebf306f8e1d1296d43f", size = 193426, upload-time = "2026-03-06T06:01:23.795Z" },
+ { url = "https://files.pythonhosted.org/packages/50/bc/a730690d726403743795ca3f5bb2baf67838c5fea78236098f324b965e40/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0c300cefd9b0970381a46394902cd18eaf2aa00163f999590ace991989dcd0fc", size = 191780, upload-time = "2026-03-06T06:01:25.053Z" },
+ { url = "https://files.pythonhosted.org/packages/97/4f/6c0bc9af68222b22951552d73df4532b5be6447cee32d58e7e8c74ecbb7b/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:c108f8619e504140569ee7de3f97d234f0fbae338a7f9f360455071ef9855a95", size = 185805, upload-time = "2026-03-06T06:01:26.294Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/b9/a523fb9b0ee90814b503452b2600e4cbc118cd68714d57041564886e7325/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d1028de43596a315e2720a9849ee79007ab742c06ad8b45a50db8cdb7ed4a82a", size = 208342, upload-time = "2026-03-06T06:01:27.55Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/61/c59e761dee4464050713e50e27b58266cc8e209e518c0b378c1580c959ba/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:19092dde50335accf365cce21998a1c6dd8eafd42c7b226eb54b2747cdce2fac", size = 193661, upload-time = "2026-03-06T06:01:29.051Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/43/729fa30aad69783f755c5ad8649da17ee095311ca42024742701e202dc59/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4354e401eb6dab9aed3c7b4030514328a6c748d05e1c3e19175008ca7de84fb1", size = 204819, upload-time = "2026-03-06T06:01:30.298Z" },
+ { url = "https://files.pythonhosted.org/packages/87/33/d9b442ce5a91b96fc0840455a9e49a611bbadae6122778d0a6a79683dd31/charset_normalizer-3.4.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a68766a3c58fde7f9aaa22b3786276f62ab2f594efb02d0a1421b6282e852e98", size = 198080, upload-time = "2026-03-06T06:01:31.478Z" },
+ { url = "https://files.pythonhosted.org/packages/56/5a/b8b5a23134978ee9885cee2d6995f4c27cc41f9baded0a9685eabc5338f0/charset_normalizer-3.4.5-cp312-cp312-win32.whl", hash = "sha256:1827734a5b308b65ac54e86a618de66f935a4f63a8a462ff1e19a6788d6c2262", size = 132630, upload-time = "2026-03-06T06:01:33.056Z" },
+ { url = "https://files.pythonhosted.org/packages/70/53/e44a4c07e8904500aec95865dc3f6464dc3586a039ef0df606eb3ac38e35/charset_normalizer-3.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:728c6a963dfab66ef865f49286e45239384249672cd598576765acc2a640a636", size = 142856, upload-time = "2026-03-06T06:01:34.489Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/aa/c5628f7cad591b1cf45790b7a61483c3e36cf41349c98af7813c483fd6e8/charset_normalizer-3.4.5-cp312-cp312-win_arm64.whl", hash = "sha256:75dfd1afe0b1647449e852f4fb428195a7ed0588947218f7ba929f6538487f02", size = 132982, upload-time = "2026-03-06T06:01:35.641Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/48/9f34ec4bb24aa3fdba1890c1bddb97c8a4be1bd84ef5c42ac2352563ad05/charset_normalizer-3.4.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ac59c15e3f1465f722607800c68713f9fbc2f672b9eb649fe831da4019ae9b23", size = 280788, upload-time = "2026-03-06T06:01:37.126Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/09/6003e7ffeb90cc0560da893e3208396a44c210c5ee42efff539639def59b/charset_normalizer-3.4.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:165c7b21d19365464e8f70e5ce5e12524c58b48c78c1f5a57524603c1ab003f8", size = 188890, upload-time = "2026-03-06T06:01:38.73Z" },
+ { url = "https://files.pythonhosted.org/packages/42/1e/02706edf19e390680daa694d17e2b8eab4b5f7ac285e2a51168b4b22ee6b/charset_normalizer-3.4.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:28269983f25a4da0425743d0d257a2d6921ea7d9b83599d4039486ec5b9f911d", size = 206136, upload-time = "2026-03-06T06:01:40.016Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/87/942c3def1b37baf3cf786bad01249190f3ca3d5e63a84f831e704977de1f/charset_normalizer-3.4.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d27ce22ec453564770d29d03a9506d449efbb9fa13c00842262b2f6801c48cce", size = 202551, upload-time = "2026-03-06T06:01:41.522Z" },
+ { url = "https://files.pythonhosted.org/packages/94/0a/af49691938dfe175d71b8a929bd7e4ace2809c0c5134e28bc535660d5262/charset_normalizer-3.4.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0625665e4ebdddb553ab185de5db7054393af8879fb0c87bd5690d14379d6819", size = 195572, upload-time = "2026-03-06T06:01:43.208Z" },
+ { url = "https://files.pythonhosted.org/packages/20/ea/dfb1792a8050a8e694cfbde1570ff97ff74e48afd874152d38163d1df9ae/charset_normalizer-3.4.5-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:c23eb3263356d94858655b3e63f85ac5d50970c6e8febcdde7830209139cc37d", size = 184438, upload-time = "2026-03-06T06:01:44.755Z" },
+ { url = "https://files.pythonhosted.org/packages/72/12/c281e2067466e3ddd0595bfaea58a6946765ace5c72dfa3edc2f5f118026/charset_normalizer-3.4.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e6302ca4ae283deb0af68d2fbf467474b8b6aedcd3dab4db187e07f94c109763", size = 193035, upload-time = "2026-03-06T06:01:46.051Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/4f/3792c056e7708e10464bad0438a44708886fb8f92e3c3d29ec5e2d964d42/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e51ae7d81c825761d941962450f50d041db028b7278e7b08930b4541b3e45cb9", size = 191340, upload-time = "2026-03-06T06:01:47.547Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/86/80ddba897127b5c7a9bccc481b0cd36c8fefa485d113262f0fe4332f0bf4/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:597d10dec876923e5c59e48dbd366e852eacb2b806029491d307daea6b917d7c", size = 185464, upload-time = "2026-03-06T06:01:48.764Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/00/b5eff85ba198faacab83e0e4b6f0648155f072278e3b392a82478f8b988b/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5cffde4032a197bd3b42fd0b9509ec60fb70918d6970e4cc773f20fc9180ca67", size = 208014, upload-time = "2026-03-06T06:01:50.371Z" },
+ { url = "https://files.pythonhosted.org/packages/c8/11/d36f70be01597fd30850dde8a1269ebc8efadd23ba5785808454f2389bde/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2da4eedcb6338e2321e831a0165759c0c620e37f8cd044a263ff67493be8ffb3", size = 193297, upload-time = "2026-03-06T06:01:51.933Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/1d/259eb0a53d4910536c7c2abb9cb25f4153548efb42800c6a9456764649c0/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:65a126fb4b070d05340a84fc709dd9e7c75d9b063b610ece8a60197a291d0adf", size = 204321, upload-time = "2026-03-06T06:01:53.887Z" },
+ { url = "https://files.pythonhosted.org/packages/84/31/faa6c5b9d3688715e1ed1bb9d124c384fe2fc1633a409e503ffe1c6398c1/charset_normalizer-3.4.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c7a80a9242963416bd81f99349d5f3fce1843c303bd404f204918b6d75a75fd6", size = 197509, upload-time = "2026-03-06T06:01:56.439Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/a5/c7d9dd1503ffc08950b3260f5d39ec2366dd08254f0900ecbcf3a6197c7c/charset_normalizer-3.4.5-cp313-cp313-win32.whl", hash = "sha256:f1d725b754e967e648046f00c4facc42d414840f5ccc670c5670f59f83693e4f", size = 132284, upload-time = "2026-03-06T06:01:57.812Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/0f/57072b253af40c8aa6636e6de7d75985624c1eb392815b2f934199340a89/charset_normalizer-3.4.5-cp313-cp313-win_amd64.whl", hash = "sha256:e37bd100d2c5d3ba35db9c7c5ba5a9228cbcffe5c4778dc824b164e5257813d7", size = 142630, upload-time = "2026-03-06T06:01:59.062Z" },
+ { url = "https://files.pythonhosted.org/packages/31/41/1c4b7cc9f13bd9d369ce3bc993e13d374ce25fa38a2663644283ecf422c1/charset_normalizer-3.4.5-cp313-cp313-win_arm64.whl", hash = "sha256:93b3b2cc5cf1b8743660ce77a4f45f3f6d1172068207c1defc779a36eea6bb36", size = 133254, upload-time = "2026-03-06T06:02:00.281Z" },
+ { url = "https://files.pythonhosted.org/packages/43/be/0f0fd9bb4a7fa4fb5067fb7d9ac693d4e928d306f80a0d02bde43a7c4aee/charset_normalizer-3.4.5-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8197abe5ca1ffb7d91e78360f915eef5addff270f8a71c1fc5be24a56f3e4873", size = 280232, upload-time = "2026-03-06T06:02:01.508Z" },
+ { url = "https://files.pythonhosted.org/packages/28/02/983b5445e4bef49cd8c9da73a8e029f0825f39b74a06d201bfaa2e55142a/charset_normalizer-3.4.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2aecdb364b8a1802afdc7f9327d55dad5366bc97d8502d0f5854e50712dbc5f", size = 189688, upload-time = "2026-03-06T06:02:02.857Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/88/152745c5166437687028027dc080e2daed6fe11cfa95a22f4602591c42db/charset_normalizer-3.4.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a66aa5022bf81ab4b1bebfb009db4fd68e0c6d4307a1ce5ef6a26e5878dfc9e4", size = 206833, upload-time = "2026-03-06T06:02:05.127Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/0f/ebc15c8b02af2f19be9678d6eed115feeeccc45ce1f4b098d986c13e8769/charset_normalizer-3.4.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d77f97e515688bd615c1d1f795d540f32542d514242067adcb8ef532504cb9ee", size = 202879, upload-time = "2026-03-06T06:02:06.446Z" },
+ { url = "https://files.pythonhosted.org/packages/38/9c/71336bff6934418dc8d1e8a1644176ac9088068bc571da612767619c97b3/charset_normalizer-3.4.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01a1ed54b953303ca7e310fafe0fe347aab348bd81834a0bcd602eb538f89d66", size = 195764, upload-time = "2026-03-06T06:02:08.763Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/95/ce92fde4f98615661871bc282a856cf9b8a15f686ba0af012984660d480b/charset_normalizer-3.4.5-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:b2d37d78297b39a9eb9eb92c0f6df98c706467282055419df141389b23f93362", size = 183728, upload-time = "2026-03-06T06:02:10.137Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/e7/f5b4588d94e747ce45ae680f0f242bc2d98dbd4eccfab73e6160b6893893/charset_normalizer-3.4.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e71bbb595973622b817c042bd943c3f3667e9c9983ce3d205f973f486fec98a7", size = 192937, upload-time = "2026-03-06T06:02:11.663Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/29/9d94ed6b929bf9f48bf6ede6e7474576499f07c4c5e878fb186083622716/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4cd966c2559f501c6fd69294d082c2934c8dd4719deb32c22961a5ac6db0df1d", size = 192040, upload-time = "2026-03-06T06:02:13.489Z" },
+ { url = "https://files.pythonhosted.org/packages/15/d2/1a093a1cf827957f9445f2fe7298bcc16f8fc5e05c1ed2ad1af0b239035e/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d5e52d127045d6ae01a1e821acfad2f3a1866c54d0e837828538fabe8d9d1bd6", size = 184107, upload-time = "2026-03-06T06:02:14.83Z" },
+ { url = "https://files.pythonhosted.org/packages/0f/7d/82068ce16bd36135df7b97f6333c5d808b94e01d4599a682e2337ed5fd14/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:30a2b1a48478c3428d047ed9690d57c23038dac838a87ad624c85c0a78ebeb39", size = 208310, upload-time = "2026-03-06T06:02:16.165Z" },
+ { url = "https://files.pythonhosted.org/packages/84/4e/4dfb52307bb6af4a5c9e73e482d171b81d36f522b21ccd28a49656baa680/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:d8ed79b8f6372ca4254955005830fd61c1ccdd8c0fac6603e2c145c61dd95db6", size = 192918, upload-time = "2026-03-06T06:02:18.144Z" },
+ { url = "https://files.pythonhosted.org/packages/08/a4/159ff7da662cf7201502ca89980b8f06acf3e887b278956646a8aeb178ab/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:c5af897b45fa606b12464ccbe0014bbf8c09191e0a66aab6aa9d5cf6e77e0c94", size = 204615, upload-time = "2026-03-06T06:02:19.821Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/62/0dd6172203cb6b429ffffc9935001fde42e5250d57f07b0c28c6046deb6b/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1088345bcc93c58d8d8f3d783eca4a6e7a7752bbff26c3eee7e73c597c191c2e", size = 197784, upload-time = "2026-03-06T06:02:21.86Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/5e/1aab5cb737039b9c59e63627dc8bbc0d02562a14f831cc450e5f91d84ce1/charset_normalizer-3.4.5-cp314-cp314-win32.whl", hash = "sha256:ee57b926940ba00bca7ba7041e665cc956e55ef482f851b9b65acb20d867e7a2", size = 133009, upload-time = "2026-03-06T06:02:23.289Z" },
+ { url = "https://files.pythonhosted.org/packages/40/65/e7c6c77d7aaa4c0d7974f2e403e17f0ed2cb0fc135f77d686b916bf1eead/charset_normalizer-3.4.5-cp314-cp314-win_amd64.whl", hash = "sha256:4481e6da1830c8a1cc0b746b47f603b653dadb690bcd851d039ffaefe70533aa", size = 143511, upload-time = "2026-03-06T06:02:26.195Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/91/52b0841c71f152f563b8e072896c14e3d83b195c188b338d3cc2e582d1d4/charset_normalizer-3.4.5-cp314-cp314-win_arm64.whl", hash = "sha256:97ab7787092eb9b50fb47fa04f24c75b768a606af1bcba1957f07f128a7219e4", size = 133775, upload-time = "2026-03-06T06:02:27.473Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/60/3a621758945513adfd4db86827a5bafcc615f913dbd0b4c2ed64a65731be/charset_normalizer-3.4.5-py3-none-any.whl", hash = "sha256:9db5e3fcdcee89a78c04dffb3fe33c79f77bd741a624946db2591c81b2fc85b0", size = 55455, upload-time = "2026-03-06T06:03:17.827Z" },
]
[[package]]
@@ -588,11 +588,11 @@ wheels = [
[[package]]
name = "filelock"
-version = "3.25.0"
+version = "3.25.1"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/77/18/a1fd2231c679dcb9726204645721b12498aeac28e1ad0601038f94b42556/filelock-3.25.0.tar.gz", hash = "sha256:8f00faf3abf9dc730a1ffe9c354ae5c04e079ab7d3a683b7c32da5dd05f26af3", size = 40158, upload-time = "2026-03-01T15:08:45.916Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/b3/8b/4c32ecde6bea6486a2a5d05340e695174351ff6b06cf651a74c005f9df00/filelock-3.25.1.tar.gz", hash = "sha256:b9a2e977f794ef94d77cdf7d27129ac648a61f585bff3ca24630c1629f701aa9", size = 40319, upload-time = "2026-03-09T19:38:47.309Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/f9/0b/de6f54d4a8bedfe8645c41497f3c18d749f0bd3218170c667bf4b81d0cdd/filelock-3.25.0-py3-none-any.whl", hash = "sha256:5ccf8069f7948f494968fc0713c10e5c182a9c9d9eef3a636307a20c2490f047", size = 26427, upload-time = "2026-03-01T15:08:44.593Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/b8/2f664b56a3b4b32d28d3d106c71783073f712ba43ff6d34b9ea0ce36dc7b/filelock-3.25.1-py3-none-any.whl", hash = "sha256:18972df45473c4aa2c7921b609ee9ca4925910cc3a0fb226c96b92fc224ef7bf", size = 26720, upload-time = "2026-03-09T19:38:45.718Z" },
]
[[package]]
@@ -662,7 +662,7 @@ wheels = [
[[package]]
name = "google-api-python-client"
-version = "2.191.0"
+version = "2.192.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "google-api-core" },
@@ -671,23 +671,23 @@ dependencies = [
{ name = "httplib2" },
{ name = "uritemplate" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/60/58/7d0843b7419a5ac51a27965e7233ef7c26ad693b490a74ab26548d0fd231/google_api_python_client-2.191.0.tar.gz", hash = "sha256:858c22fd46f51a65cee365a78aec2054e6d47b50434bee4ba62e91ac0944aea1", size = 14195310, upload-time = "2026-03-02T16:58:59.965Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/85/d8/489052a40935e45b9b5b3d6accc14b041360c1507bdc659c2e1a19aaa3ff/google_api_python_client-2.192.0.tar.gz", hash = "sha256:d48cfa6078fadea788425481b007af33fe0ab6537b78f37da914fb6fc112eb27", size = 14209505, upload-time = "2026-03-05T15:17:01.598Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/6e/a4/da3a66c90f94b9eb433f0844f1bf79b5789bfa0859e55c0cd02c1139fed8/google_api_python_client-2.191.0-py3-none-any.whl", hash = "sha256:0768dde3202121abb3e897c4ee5150e58a25f32ee843780f6bee636dccc7ef23", size = 14769138, upload-time = "2026-03-02T16:58:57.665Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/76/ec4128f00fefb9011635ae2abc67d7dacd05c8559378f8f05f0c907c38d8/google_api_python_client-2.192.0-py3-none-any.whl", hash = "sha256:63a57d4457cd97df1d63eb89c5fda03c5a50588dcbc32c0115dd1433c08f4b62", size = 14783267, upload-time = "2026-03-05T15:16:58.804Z" },
]
[[package]]
name = "google-auth"
-version = "2.48.0"
+version = "2.49.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cryptography" },
{ name = "pyasn1-modules" },
{ name = "rsa" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/0c/41/242044323fbd746615884b1c16639749e73665b718209946ebad7ba8a813/google_auth-2.48.0.tar.gz", hash = "sha256:4f7e706b0cd3208a3d940a19a822c37a476ddba5450156c3e6624a71f7c841ce", size = 326522, upload-time = "2026-01-26T19:22:47.157Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/7d/59/7371175bfd949abfb1170aa076352131d7281bd9449c0f978604fc4431c3/google_auth-2.49.0.tar.gz", hash = "sha256:9cc2d9259d3700d7a257681f81052db6737495a1a46b610597f4b8bafe5286ae", size = 333444, upload-time = "2026-03-06T21:53:06.07Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/83/1d/d6466de3a5249d35e832a52834115ca9d1d0de6abc22065f049707516d47/google_auth-2.48.0-py3-none-any.whl", hash = "sha256:2e2a537873d449434252a9632c28bfc268b0adb1e53f9fb62afc5333a975903f", size = 236499, upload-time = "2026-01-26T19:22:45.099Z" },
+ { url = "https://files.pythonhosted.org/packages/37/45/de64b823b639103de4b63dd193480dce99526bd36be6530c2dba85bf7817/google_auth-2.49.0-py3-none-any.whl", hash = "sha256:f893ef7307f19cf53700b7e2f61b5a6affe3aa0edf9943b13788920ab92d8d87", size = 240676, upload-time = "2026-03-06T21:52:38.304Z" },
]
[[package]]
@@ -718,14 +718,14 @@ wheels = [
[[package]]
name = "googleapis-common-protos"
-version = "1.72.0"
+version = "1.73.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "protobuf" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/e5/7b/adfd75544c415c487b33061fe7ae526165241c1ea133f9a9125a56b39fd8/googleapis_common_protos-1.72.0.tar.gz", hash = "sha256:e55a601c1b32b52d7a3e65f43563e2aa61bcd737998ee672ac9b951cd49319f5", size = 147433, upload-time = "2025-11-06T18:29:24.087Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/99/96/a0205167fa0154f4a542fd6925bdc63d039d88dab3588b875078107e6f06/googleapis_common_protos-1.73.0.tar.gz", hash = "sha256:778d07cd4fbeff84c6f7c72102f0daf98fa2bfd3fa8bea426edc545588da0b5a", size = 147323, upload-time = "2026-03-06T21:53:09.727Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c4/ab/09169d5a4612a5f92490806649ac8d41e3ec9129c636754575b3553f4ea4/googleapis_common_protos-1.72.0-py3-none-any.whl", hash = "sha256:4299c5a82d5ae1a9702ada957347726b167f9f8d1fc352477702a1e851ff4038", size = 297515, upload-time = "2025-11-06T18:29:13.14Z" },
+ { url = "https://files.pythonhosted.org/packages/69/28/23eea8acd65972bbfe295ce3666b28ac510dfcb115fac089d3edb0feb00a/googleapis_common_protos-1.73.0-py3-none-any.whl", hash = "sha256:dfdaaa2e860f242046be561e6d6cb5c5f1541ae02cfbcb034371aadb2942b4e8", size = 297578, upload-time = "2026-03-06T21:52:33.933Z" },
]
[[package]]
@@ -898,14 +898,14 @@ wheels = [
[[package]]
name = "jaraco-context"
-version = "6.1.0"
+version = "6.1.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "backports-tarfile", marker = "python_full_version < '3.12'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/cb/9c/a788f5bb29c61e456b8ee52ce76dbdd32fd72cd73dd67bc95f42c7a8d13c/jaraco_context-6.1.0.tar.gz", hash = "sha256:129a341b0a85a7db7879e22acd66902fda67882db771754574338898b2d5d86f", size = 15850, upload-time = "2026-01-13T02:53:53.847Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/27/7b/c3081ff1af947915503121c649f26a778e1a2101fd525f74aef997d75b7e/jaraco_context-6.1.1.tar.gz", hash = "sha256:bc046b2dc94f1e5532bd02402684414575cc11f565d929b6563125deb0a6e581", size = 15832, upload-time = "2026-03-07T15:46:04.63Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/8d/48/aa685dbf1024c7bd82bede569e3a85f82c32fd3d79ba5fea578f0159571a/jaraco_context-6.1.0-py3-none-any.whl", hash = "sha256:a43b5ed85815223d0d3cfdb6d7ca0d2bc8946f28f30b6f3216bda070f68badda", size = 7065, upload-time = "2026-01-13T02:53:53.031Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/49/c152890d49102b280ecf86ba5f80a8c111c3a155dafa3bd24aeb64fde9e1/jaraco_context-6.1.1-py3-none-any.whl", hash = "sha256:0df6a0287258f3e364072c3e40d5411b20cafa30cb28c4839d24319cecf9f808", size = 7005, upload-time = "2026-03-07T15:46:03.515Z" },
]
[[package]]
@@ -1203,11 +1203,11 @@ wheels = [
[[package]]
name = "platformdirs"
-version = "4.9.2"
+version = "4.9.4"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/1b/04/fea538adf7dbbd6d186f551d595961e564a3b6715bdf276b477460858672/platformdirs-4.9.2.tar.gz", hash = "sha256:9a33809944b9db043ad67ca0db94b14bf452cc6aeaac46a88ea55b26e2e9d291", size = 28394, upload-time = "2026-02-16T03:56:10.574Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/19/56/8d4c30c8a1d07013911a8fdbd8f89440ef9f08d07a1b50ab8ca8be5a20f9/platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", size = 28737, upload-time = "2026-03-05T18:34:13.271Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/48/31/05e764397056194206169869b50cf2fee4dbbbc71b344705b9c0d878d4d8/platformdirs-4.9.2-py3-none-any.whl", hash = "sha256:9170634f126f8efdae22fb58ae8a0eaa86f38365bc57897a6c4f781d1f5875bd", size = 21168, upload-time = "2026-02-16T03:56:08.891Z" },
+ { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216, upload-time = "2026-03-05T18:34:12.172Z" },
]
[[package]]
@@ -1221,26 +1221,26 @@ wheels = [
[[package]]
name = "prek"
-version = "0.3.4"
+version = "0.3.5"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/c6/51/2324eaad93a4b144853ca1c56da76f357d3a70c7b4fd6659e972d7bb8660/prek-0.3.4.tar.gz", hash = "sha256:56a74d02d8b7dfe3c774ecfcd8c1b4e5f1e1b84369043a8003e8e3a779fce72d", size = 356633, upload-time = "2026-02-28T03:47:13.452Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/46/d6/277e002e56eeab3a9d48f1ca4cc067d249d6326fc1783b770d70ad5ae2be/prek-0.3.5.tar.gz", hash = "sha256:ca40b6685a4192256bc807f32237af94bf9b8799c0d708b98735738250685642", size = 374806, upload-time = "2026-03-09T10:35:18.842Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/09/20/1a964cb72582307c2f1dc7f583caab90f42810ad41551e5220592406a4c3/prek-0.3.4-py3-none-linux_armv6l.whl", hash = "sha256:c35192d6e23fe7406bd2f333d1c7dab1a4b34ab9289789f453170f33550aa74d", size = 4641915, upload-time = "2026-02-28T03:47:03.772Z" },
- { url = "https://files.pythonhosted.org/packages/c5/cb/4a21f37102bac37e415b61818344aa85de8d29a581253afa7db8c08d5a33/prek-0.3.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6f784d78de72a8bbe58a5fe7bde787c364ae88f0aff5222c5c5c7287876c510a", size = 4649166, upload-time = "2026-02-28T03:47:06.164Z" },
- { url = "https://files.pythonhosted.org/packages/85/9c/a7c0d117a098d57931428bdb60fcb796e0ebc0478c59288017a2e22eca96/prek-0.3.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:50a43f522625e8c968e8c9992accf9e29017abad6c782d6d176b73145ad680b7", size = 4274422, upload-time = "2026-02-28T03:46:59.356Z" },
- { url = "https://files.pythonhosted.org/packages/59/84/81d06df1724d09266df97599a02543d82fde7dfaefd192f09d9b2ccb092f/prek-0.3.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:4bbb1d3912a88935f35c6ba4466b4242732e3e3a8c608623c708e83cea85de00", size = 4629873, upload-time = "2026-02-28T03:46:56.419Z" },
- { url = "https://files.pythonhosted.org/packages/09/cd/bb0aefa25cfacd8dbced75b9a9d9945707707867fa5635fb69ae1bbc2d88/prek-0.3.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ca4d4134db8f6e8de3c418317becdf428957e3cab271807f475318105fd46d04", size = 4552507, upload-time = "2026-02-28T03:47:05.004Z" },
- { url = "https://files.pythonhosted.org/packages/9b/c0/578a7af4861afb64ec81c03bfdcc1bb3341bb61f2fff8a094ecf13987a56/prek-0.3.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fb6395f6eb76133bb1e11fc718db8144522466cdc2e541d05e7813d1bbcae7d", size = 4865929, upload-time = "2026-02-28T03:47:09.231Z" },
- { url = "https://files.pythonhosted.org/packages/fc/48/f169406590028f7698ef2e1ff5bffd92ca05e017636c1163a2f5ef0f8275/prek-0.3.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aae17813239ddcb4ae7b38418de4d49afff740f48f8e0556029c96f58e350412", size = 5390286, upload-time = "2026-02-28T03:47:10.796Z" },
- { url = "https://files.pythonhosted.org/packages/05/c5/98a73fec052059c3ae06ce105bef67caca42334c56d84e9ef75df72ba152/prek-0.3.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10a621a690d9c127afc3d21c275030d364d1fbef3296c095068d3ae80a59546e", size = 4891028, upload-time = "2026-02-28T03:47:07.916Z" },
- { url = "https://files.pythonhosted.org/packages/a3/b4/029966e35e59b59c142be7e1d2208ad261709ac1a66aa4a3ce33c5b9f91f/prek-0.3.4-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:d978c31bc3b1f0b3d58895b7c6ac26f077e0ea846da54f46aeee4c7088b1b105", size = 4633986, upload-time = "2026-02-28T03:47:14.351Z" },
- { url = "https://files.pythonhosted.org/packages/1d/27/d122802555745b6940c99fcb41496001c192ddcdf56ec947ec10a0298e05/prek-0.3.4-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a8e089a030f0a023c22a4bb2ec4ff3fcc153585d701cff67acbfca2f37e173ae", size = 4680722, upload-time = "2026-02-28T03:47:12.224Z" },
- { url = "https://files.pythonhosted.org/packages/34/40/92318c96b3a67b4e62ed82741016ede34d97ea9579d3cc1332b167632222/prek-0.3.4-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:8060c72b764f0b88112616763da9dd3a7c293e010f8520b74079893096160a2f", size = 4535623, upload-time = "2026-02-28T03:46:52.221Z" },
- { url = "https://files.pythonhosted.org/packages/df/f5/6b383d94e722637da4926b4f609d36fe432827bb6f035ad46ee02bde66b6/prek-0.3.4-py3-none-musllinux_1_1_i686.whl", hash = "sha256:65b23268456b5a763278d4e1ec532f2df33918f13ded85869a1ddff761eb9697", size = 4729879, upload-time = "2026-02-28T03:46:57.886Z" },
- { url = "https://files.pythonhosted.org/packages/79/f8/fdc705b807d813fd713ffa4f67f96741542ed1dafbb221206078c06f3df4/prek-0.3.4-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:3975c61139c7b3200e38dc3955e050b0f2615701d3deb9715696a902e850509e", size = 5001569, upload-time = "2026-02-28T03:47:00.892Z" },
- { url = "https://files.pythonhosted.org/packages/84/92/b007a41f58e8192a1e611a21b396ad870d51d7873b7af12068ebae7fc15f/prek-0.3.4-py3-none-win32.whl", hash = "sha256:37449ae82f4dc08b72e542401e3d7318f05d1163e87c31ab260a40f425d6516e", size = 4297057, upload-time = "2026-02-28T03:47:02.219Z" },
- { url = "https://files.pythonhosted.org/packages/bb/dc/bcb02de9b11461e8e0c7d3c8fdf8cfa15ac6efe73472a4375549ba5defd2/prek-0.3.4-py3-none-win_amd64.whl", hash = "sha256:60e9aa86ca65de963510ae28c5d94b9d7a97bcbaa6e4cdb5bf5083ed4c45dc71", size = 4655174, upload-time = "2026-02-28T03:46:53.749Z" },
- { url = "https://files.pythonhosted.org/packages/0b/86/98f5598569f4cd3de7161e266fab6a8981e65555f79d4704810c1502ad0a/prek-0.3.4-py3-none-win_arm64.whl", hash = "sha256:486bdae8f4512d3b4f6eb61b83e5b7595da2adca385af4b2b7823c0ab38d1827", size = 4367817, upload-time = "2026-02-28T03:46:55.264Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/a9/16dd8d3a50362ebccffe58518af1f1f571c96f0695d7fcd8bbd386585f58/prek-0.3.5-py3-none-linux_armv6l.whl", hash = "sha256:44b3e12791805804f286d103682b42a84e0f98a2687faa37045e9d3375d3d73d", size = 5105604, upload-time = "2026-03-09T10:35:00.332Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/74/bc6036f5bf03860cda66ab040b32737e54802b71a81ec381839deb25df9e/prek-0.3.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e3cb451cc51ac068974557491beb4c7d2d41dfde29ed559c1694c8ce23bf53e8", size = 5506155, upload-time = "2026-03-09T10:35:17.64Z" },
+ { url = "https://files.pythonhosted.org/packages/02/d9/a3745c2a10509c63b6a118ada766614dd705efefd08f275804d5c807aa4a/prek-0.3.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:ad8f5f0d8da53dc94d00b76979af312b3dacccc9dcbc6417756c5dca3633c052", size = 5100383, upload-time = "2026-03-09T10:35:13.302Z" },
+ { url = "https://files.pythonhosted.org/packages/43/8e/de965fc515d39309a332789cd3778161f7bc80cde15070bedf17f9f8cb93/prek-0.3.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:4511e15d34072851ac88e4b2006868fbe13655059ad941d7a0ff9ee17138fd9f", size = 5334913, upload-time = "2026-03-09T10:35:14.813Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/8c/44f07e8940256059cfd82520e3cbe0764ab06ddb4aa43148465db00b39ad/prek-0.3.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcc0b63b8337e2046f51267facaac63ba755bc14aad53991840a5eccba3e5c28", size = 5033825, upload-time = "2026-03-09T10:35:06.976Z" },
+ { url = "https://files.pythonhosted.org/packages/94/85/3ff0f96881ff2360c212d310ff23c3cf5a15b223d34fcfa8cdcef203be69/prek-0.3.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5fc0d78c3896a674aeb8247a83bbda7efec85274dbdfbc978ceff8d37e4ed20", size = 5438586, upload-time = "2026-03-09T10:34:58.779Z" },
+ { url = "https://files.pythonhosted.org/packages/79/a5/c6d08d31293400fcb5d427f8e7e6bacfc959988e868ad3a9d97b4d87c4b7/prek-0.3.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64cad21cb9072d985179495b77b312f6b81e7b45357d0c68dc1de66e0408eabc", size = 6359714, upload-time = "2026-03-09T10:34:57.454Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/18/321dcff9ece8065d42c8c1c7a53a23b45d2b4330aa70993be75dc5f2822f/prek-0.3.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45ee84199bb48e013bdfde0c84352c17a44cc42d5792681b86d94e9474aab6f8", size = 5717632, upload-time = "2026-03-09T10:35:08.634Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/7f/1288226aa381d0cea403157f4e6b64b356e1a745f2441c31dd9d8a1d63da/prek-0.3.5-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:f43275e5d564e18e52133129ebeb5cb071af7ce4a547766c7f025aa0955dfbb6", size = 5339040, upload-time = "2026-03-09T10:35:03.665Z" },
+ { url = "https://files.pythonhosted.org/packages/22/94/cfec83df9c2b8e7ed1608087bcf9538a6a77b4c2e7365123e9e0a3162cd1/prek-0.3.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:abcee520d31522bcbad9311f21326b447694cd5edba33618c25fd023fc9865ec", size = 5162586, upload-time = "2026-03-09T10:35:11.564Z" },
+ { url = "https://files.pythonhosted.org/packages/13/b7/741d62132f37a5f7cc0fad1168bd31f20dea9628f482f077f569547e0436/prek-0.3.5-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:499c56a94a155790c75a973d351a33f8065579d9094c93f6d451ada5d1e469be", size = 5002933, upload-time = "2026-03-09T10:35:16.347Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/83/630a5671df6550fcfa67c54955e8a8174eb9b4d97ac38fb05a362029245b/prek-0.3.5-py3-none-musllinux_1_1_i686.whl", hash = "sha256:de1065b59f194624adc9dea269d4ff6b50e98a1b5bb662374a9adaa496b3c1eb", size = 5304934, upload-time = "2026-03-09T10:35:09.975Z" },
+ { url = "https://files.pythonhosted.org/packages/de/79/67a7afd0c0b6c436630b7dba6e586a42d21d5d6e5778fbd9eba7bbd3dd26/prek-0.3.5-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:a1c4869e45ee341735d07179da3a79fa2afb5959cef8b3c8a71906eb52dc6933", size = 5829914, upload-time = "2026-03-09T10:35:05.39Z" },
+ { url = "https://files.pythonhosted.org/packages/37/47/e2fe13b33e7b5fdd9dd1a312f5440208bfe1be6183e54c5c99c10f27d848/prek-0.3.5-py3-none-win32.whl", hash = "sha256:70b2152ecedc58f3f4f69adc884617b0cf44259f7414c44d6268ea6f107672eb", size = 4836910, upload-time = "2026-03-09T10:35:01.884Z" },
+ { url = "https://files.pythonhosted.org/packages/6b/ab/dc2a139fd4896d11f39631479ed385e86307af7f54059ebe9414bb0d00c6/prek-0.3.5-py3-none-win_amd64.whl", hash = "sha256:01d031b684f7e1546225393af1268d9b4451a44ef6cb9be4101c85c7862e08db", size = 5234234, upload-time = "2026-03-09T10:35:20.193Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/38/f7256b4b7581444f658e909c3b566f51bfabe56c03e80d107a6932d62040/prek-0.3.5-py3-none-win_arm64.whl", hash = "sha256:aa774168e3d868039ff79422bdef2df8d5a016ed804a9914607dcdd3d41da053", size = 5083330, upload-time = "2026-03-09T10:34:55.469Z" },
]
[[package]]
@@ -1474,15 +1474,15 @@ wheels = [
[[package]]
name = "python-discovery"
-version = "1.1.0"
+version = "1.1.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "filelock" },
{ name = "platformdirs" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/82/bb/93a3e83bdf9322c7e21cafd092e56a4a17c4d8ef4277b6eb01af1a540a6f/python_discovery-1.1.0.tar.gz", hash = "sha256:447941ba1aed8cc2ab7ee3cb91be5fc137c5bdbb05b7e6ea62fbdcb66e50b268", size = 55674, upload-time = "2026-02-26T09:42:49.668Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/7a/16/6f3f5e9258f0733aaca19aa18e298cb3a629ae49363573e78d241abeef59/python_discovery-1.1.2.tar.gz", hash = "sha256:c500bd2153e3afc5f48a61d33ff570b6f3e710d36ceaaf882fa9bbe5cc2cec49", size = 56928, upload-time = "2026-03-09T20:02:28.402Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/06/54/82a6e2ef37f0f23dccac604b9585bdcbd0698604feb64807dcb72853693e/python_discovery-1.1.0-py3-none-any.whl", hash = "sha256:a162893b8809727f54594a99ad2179d2ede4bf953e12d4c7abc3cc9cdbd1437b", size = 30687, upload-time = "2026-02-26T09:42:48.548Z" },
+ { url = "https://files.pythonhosted.org/packages/03/48/8bdfaec240edb1a79b79201eff38b737fc3c29ce59e2e71271bdd8bafdda/python_discovery-1.1.2-py3-none-any.whl", hash = "sha256:d18edd61b382d62f8bcd004a71ebaabc87df31dbefb30aeed59f4fc6afa005be", size = 31486, upload-time = "2026-03-09T20:02:27.277Z" },
]
[[package]]
@@ -1901,20 +1901,20 @@ wheels = [
[[package]]
name = "smmap"
-version = "5.0.2"
+version = "5.0.3"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329, upload-time = "2025-01-02T07:14:40.909Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/1f/ea/49c993d6dfdd7338c9b1000a0f36817ed7ec84577ae2e52f890d1a4ff909/smmap-5.0.3.tar.gz", hash = "sha256:4d9debb8b99007ae47165abc08670bd74cb74b5227dda7f643eccc4e9eb5642c", size = 22506, upload-time = "2026-03-09T03:43:26.1Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303, upload-time = "2025-01-02T07:14:38.724Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl", hash = "sha256:c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f", size = 24390, upload-time = "2026-03-09T03:43:24.361Z" },
]
[[package]]
name = "tabulate"
-version = "0.9.0"
+version = "0.10.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090, upload-time = "2022-10-06T17:21:48.54Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/46/58/8c37dea7bbf769b20d58e7ace7e5edfe65b849442b00ffcdd56be88697c6/tabulate-0.10.0.tar.gz", hash = "sha256:e2cfde8f79420f6deeffdeda9aaec3b6bc5abce947655d17ac662b126e48a60d", size = 91754, upload-time = "2026-03-04T18:55:34.402Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252, upload-time = "2022-10-06T17:21:44.262Z" },
+ { url = "https://files.pythonhosted.org/packages/99/55/db07de81b5c630da5cbf5c7df646580ca26dfaefa593667fc6f2fe016d2e/tabulate-0.10.0-py3-none-any.whl", hash = "sha256:f0b0622e567335c8fabaaa659f1b33bcb6ddfe2e496071b743aa113f8774f2d3", size = 39814, upload-time = "2026-03-04T18:55:31.284Z" },
]
[[package]]
@@ -2071,32 +2071,32 @@ wheels = [
[[package]]
name = "uv"
-version = "0.10.7"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/7c/ec/b324a43b55fe59577505478a396cb1d2758487a2e2270c81ccfa4ac6c96d/uv-0.10.7.tar.gz", hash = "sha256:7c3b0133c2d6bd725d5a35ec5e109ebf0d75389943abe826f3d9ea6d6667a375", size = 3922193, upload-time = "2026-02-27T12:33:58.525Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/f3/1b/decff24553325561850d70b75c737076e6fcbcfbf233011a27a33f06e4d9/uv-0.10.7-py3-none-linux_armv6l.whl", hash = "sha256:6a0af6c7a90fd2053edfa2c8ee719078ea906a2d9f4798d3fb3c03378726209a", size = 22497542, upload-time = "2026-02-27T12:33:39.425Z" },
- { url = "https://files.pythonhosted.org/packages/fc/b5/51152c87921bc2576fecb982df4a02ac9cfd7fc934e28114a1232b99eed4/uv-0.10.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3b7db0cab77232a7c8856062904fc3b9db22383f1dec7e97a9588fb6c8470f6a", size = 21558860, upload-time = "2026-02-27T12:34:03.362Z" },
- { url = "https://files.pythonhosted.org/packages/5e/15/8365dc2ded350a4ee5fcbbf9b15195cb2b45855114f2a154b5effb6fa791/uv-0.10.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d872d2ff9c9dfba989b5f05f599715bc0f19b94cd0dbf8ae4ad22f8879a66c8c", size = 20212775, upload-time = "2026-02-27T12:33:55.365Z" },
- { url = "https://files.pythonhosted.org/packages/53/a0/ccf25e897f3907b5a6fd899007ff9a80b5bbf151b3a75a375881005611fd/uv-0.10.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:d9b40d03693efda80a41e5d18ac997efdf1094b27fb75471c1a8f51a9ebeffb3", size = 22015584, upload-time = "2026-02-27T12:33:47.374Z" },
- { url = "https://files.pythonhosted.org/packages/fa/3a/5099747954e7774768572d30917bb6bda6b8d465d7a3c49c9bbf7af2a812/uv-0.10.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:e74fe4df9cf31fe84f20b84a0054874635077d31ce20e7de35ff0dd64d498d7b", size = 22100376, upload-time = "2026-02-27T12:34:06.169Z" },
- { url = "https://files.pythonhosted.org/packages/0c/1a/75897fd966b871803cf78019fa31757ced0d54af5ffd7f57bce8b01d64f3/uv-0.10.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9c76659fc8bb618dd35cd83b2f479c6f880555a16630a454a251045c4c118ea4", size = 22105202, upload-time = "2026-02-27T12:34:16.972Z" },
- { url = "https://files.pythonhosted.org/packages/b5/1e/0b8caedd66ca911533e18fd051da79a213c792404138812c66043d529b9e/uv-0.10.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d160cceb9468024ca40dc57a180289dfd2024d98e42f2284b9ec44355723b0a", size = 23335601, upload-time = "2026-02-27T12:34:11.161Z" },
- { url = "https://files.pythonhosted.org/packages/69/94/b741af277e39a92e0da07fe48c338eee1429c2607e7a192e41345208bb24/uv-0.10.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c775975d891cb60cf10f00953e61e643fcb9a9139e94c9ef5c805fe36e90477f", size = 24152851, upload-time = "2026-02-27T12:33:33.904Z" },
- { url = "https://files.pythonhosted.org/packages/27/b2/da351ccd02f0fb1aec5f992b886bea1374cce44276a78904348e2669dd78/uv-0.10.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a709e75583231cc1f39567fb3d8d9b4077ff94a64046eb242726300144ed1a4a", size = 23276444, upload-time = "2026-02-27T12:33:36.891Z" },
- { url = "https://files.pythonhosted.org/packages/71/a9/2735cc9dc39457c9cf64d1ce2ba5a9a8ecbb103d0fb64b052bf33ba3d669/uv-0.10.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89de2504407dcf04aece914c6ca3b9d8e60cf9ff39a13031c1df1f7c040cea81", size = 23218464, upload-time = "2026-02-27T12:34:00.904Z" },
- { url = "https://files.pythonhosted.org/packages/20/5f/5f204e9c3f04f5fc844d2f98d80a7de64b6b304af869644ab478d909f6ff/uv-0.10.7-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:9945de1d11c4a5ad77e9c4f36f8b5f9e7c9c3c32999b8bc0e7e579145c3b641c", size = 22092562, upload-time = "2026-02-27T12:34:14.155Z" },
- { url = "https://files.pythonhosted.org/packages/dd/a4/16bebf106e3289a29cc1e1482d551c49bd220983e9b4bc5960142389ad3f/uv-0.10.7-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:dbe43527f478e2ffa420516aa465f82057763936bbea56f814fd054a9b7f961f", size = 22851312, upload-time = "2026-02-27T12:34:08.651Z" },
- { url = "https://files.pythonhosted.org/packages/d1/7a/953b1da589225d98ca8668412f665c3192f6deed2a0f4bb782b0df18f611/uv-0.10.7-py3-none-musllinux_1_1_i686.whl", hash = "sha256:c0783f327631141501bdc5f31dd2b4c748df7e7f5dc5cdbfc0fbb82da86cc9ca", size = 22543775, upload-time = "2026-02-27T12:33:30.935Z" },
- { url = "https://files.pythonhosted.org/packages/8b/67/e133afdabf76e43989448be1c2ef607f13afc32aa1ee9f6897115dec8417/uv-0.10.7-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:eba438899010522812d3497af586e6eedc94fa2b0ced028f51812f0c10aafb30", size = 23431187, upload-time = "2026-02-27T12:33:42.131Z" },
- { url = "https://files.pythonhosted.org/packages/ba/40/6ffb58ec88a33d6cbe9a606966f9558807f37a50f7be7dc756824df2d04c/uv-0.10.7-py3-none-win32.whl", hash = "sha256:b56d1818aafb2701d92e94f552126fe71d30a13f28712d99345ef5cafc53d874", size = 21524397, upload-time = "2026-02-27T12:33:44.579Z" },
- { url = "https://files.pythonhosted.org/packages/e3/1f/74f4d625db838f716a555908d41777b6357bacc141ddef117a01855e5ef9/uv-0.10.7-py3-none-win_amd64.whl", hash = "sha256:ad0d0ddd9f5407ad8699e3b20fe6c18406cd606336743e246b16914801cfd8b0", size = 23999929, upload-time = "2026-02-27T12:33:49.839Z" },
- { url = "https://files.pythonhosted.org/packages/48/4e/20cbfbcb1a0f48c5c1ca94f6baa0fa00754aafda365da9160c15e3b9c277/uv-0.10.7-py3-none-win_arm64.whl", hash = "sha256:edf732de80c1a9701180ef8c7a2fa926a995712e4a34ae8c025e090f797c2e0b", size = 22353084, upload-time = "2026-02-27T12:33:52.792Z" },
+version = "0.10.9"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/f2/59/235fa08a6b56de82a45a385dc2bf724502f720f0a9692a1a8cb24aab3e6f/uv-0.10.9.tar.gz", hash = "sha256:31e76ae92e70fec47c3efab0c8094035ad7a578454482415b496fa39fc4d685c", size = 3945685, upload-time = "2026-03-06T21:21:16.219Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/2f/6d/f87f1530d5db4132776d49dddd88b1c77bc08fa7b32bf585b366204e6fc2/uv-0.10.9-py3-none-linux_armv6l.whl", hash = "sha256:0649f83fa0f44f18627c00b2a9a60e5c3486a34799b2c874f2b3945b76048a67", size = 22617914, upload-time = "2026-03-06T21:20:48.282Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/34/2e5cd576d312eb1131b615f49ee95ff6efb740965324843617adae729cf2/uv-0.10.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:880dd4cffe4bd184e8871ddf4c7d3c3b042e1f16d2682310644aa8d61eaea3e6", size = 21778779, upload-time = "2026-03-06T21:21:01.804Z" },
+ { url = "https://files.pythonhosted.org/packages/89/35/684f641de4de2b20db7d2163c735b2bb211e3b3c84c241706d6448e5e868/uv-0.10.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a7a784254380552398a6baf4149faf5b31a4003275f685c28421cf8197178a08", size = 20384301, upload-time = "2026-03-06T21:21:04.089Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/5c/7170cfd1b4af09b435abc5a89ff315af130cf4a5082e5eb1206ee46bba67/uv-0.10.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:5ea0e8598fa012cfa4480ecad4d112bc70f514157c3cc1555a7611c7b6b1ab0a", size = 22226893, upload-time = "2026-03-06T21:20:50.902Z" },
+ { url = "https://files.pythonhosted.org/packages/43/5c/68a17934dc8a2897fd7928b1c03c965373a820dc182aad96f1be6cce33a1/uv-0.10.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.musllinux_1_1_armv7l.whl", hash = "sha256:2d6b5367e9bf87eca51c0f2ecda26a1ff931e41409977b4f0a420de2f3e617cf", size = 22233832, upload-time = "2026-03-06T21:21:11.748Z" },
+ { url = "https://files.pythonhosted.org/packages/00/10/d262172ac59b669ca9c006bcbdb49c1a168cc314a5de576a4bb476dfab4c/uv-0.10.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd04e34db27f9a1d5a0871980edc9f910bb11afbc4abca8234d5a363cbe63c04", size = 22192193, upload-time = "2026-03-06T21:20:59.48Z" },
+ { url = "https://files.pythonhosted.org/packages/a2/e6/f75fef1e3e5b0cf3592a4c35ed5128164ef2e6bd6a2570a0782c0baf6d4b/uv-0.10.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:547deb57311fc64e4a6b8336228fca4cb4dcbeabdc6e85f14f7804dcd0bc8cd2", size = 23571687, upload-time = "2026-03-06T21:20:45.403Z" },
+ { url = "https://files.pythonhosted.org/packages/31/28/4b1ee6f4aa0e1b935e66b6018691258d1b702ef9c5d8c71e853564ad0a3a/uv-0.10.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0091b6d0b666640d7407a433860184f77667077b73564e86d49c2a851f073a8", size = 24418225, upload-time = "2026-03-06T21:21:09.459Z" },
+ { url = "https://files.pythonhosted.org/packages/39/a2/5e67987f8d55eeecca7d8f4e94ac3e973fa1e8aaf426fcb8f442e9f7e2bc/uv-0.10.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81b2286e6fd869e3507971f39d14829c03e2e31caa8ecc6347b0ffacabb95a5b", size = 23555724, upload-time = "2026-03-06T21:20:54.085Z" },
+ { url = "https://files.pythonhosted.org/packages/79/34/b104c413079874493eed7bf11838b47b697cf1f0ed7e9de374ea37b4e4e0/uv-0.10.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c9d6deb30edbc22123be75479f99fb476613eaf38a8034c0e98bba24a344179", size = 23438145, upload-time = "2026-03-06T21:21:26.866Z" },
+ { url = "https://files.pythonhosted.org/packages/27/8a/cad762b3e9bfb961b68b2ae43a258a92b522918958954b50b09dcb14bb4e/uv-0.10.9-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:24b1ce6d626e06c4582946b6af07b08a032fcccd81fe54c3db3ed2d1c63a97dc", size = 22326765, upload-time = "2026-03-06T21:21:14.283Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/62/7e066f197f3eb8f8f71e25d703a29c89849c9c047240c1223e29bc0a37e4/uv-0.10.9-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:fa3401780273d96a2960dbeab58452ce1b387ad8c5da25be6221c0188519e21d", size = 23215175, upload-time = "2026-03-06T21:21:29.673Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/06/51db93b5edb8b0202c0ec6caf3f24384f5abdfc180b6376a3710223fd56f/uv-0.10.9-py3-none-musllinux_1_1_i686.whl", hash = "sha256:8f94a31832d2b4c565312ea17a71b8dd2f971e5aa570c5b796a27b2c9fcdb163", size = 22784507, upload-time = "2026-03-06T21:21:20.676Z" },
+ { url = "https://files.pythonhosted.org/packages/96/34/1db511d9259c1f32e5e094133546e5723e183a9ba2c64f7ca6156badddee/uv-0.10.9-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:842c39c19d9072f1ad53c71bb4ecd1c9caa311d5de9d19e09a636274a6c95e2e", size = 23660703, upload-time = "2026-03-06T21:21:06.667Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/a0/58388abb252c7a37bc67422fce3a6b87404ea3fac44ca20132a4ba502235/uv-0.10.9-py3-none-win32.whl", hash = "sha256:ed44047c602449916ba18a8596715ef7edbbd00859f3db9eac010dc62a0edd30", size = 21524142, upload-time = "2026-03-06T21:21:18.246Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/e9/adf7a12136573937d12ac189569e2e90e7fad18b458192083df6986f3013/uv-0.10.9-py3-none-win_amd64.whl", hash = "sha256:af79552276d8bd622048ab2d67ec22120a6af64d83963c46b1482218c27b571f", size = 24103389, upload-time = "2026-03-06T21:20:56.495Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/49/4971affd9c62d26b3ff4a84dc6432275be72d9615d95f7bb9e027beeeed8/uv-0.10.9-py3-none-win_arm64.whl", hash = "sha256:47e18a0521d76293d4f60d129f520b18bddf1976b4a47b50f0fcb04fb6a9d40f", size = 22454171, upload-time = "2026-03-06T21:21:24.596Z" },
]
[[package]]
name = "virtualenv"
-version = "21.1.0"
+version = "21.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "distlib" },
@@ -2105,9 +2105,9 @@ dependencies = [
{ name = "python-discovery" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/2f/c9/18d4b36606d6091844daa3bd93cf7dc78e6f5da21d9f21d06c221104b684/virtualenv-21.1.0.tar.gz", hash = "sha256:1990a0188c8f16b6b9cf65c9183049007375b26aad415514d377ccacf1e4fb44", size = 5840471, upload-time = "2026-02-27T08:49:29.702Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/aa/92/58199fe10049f9703c2666e809c4f686c54ef0a68b0f6afccf518c0b1eb9/virtualenv-21.2.0.tar.gz", hash = "sha256:1720dc3a62ef5b443092e3f499228599045d7fea4c79199770499df8becf9098", size = 5840618, upload-time = "2026-03-09T17:24:38.013Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/78/55/896b06bf93a49bec0f4ae2a6f1ed12bd05c8860744ac3a70eda041064e4d/virtualenv-21.1.0-py3-none-any.whl", hash = "sha256:164f5e14c5587d170cf98e60378eb91ea35bf037be313811905d3a24ea33cc07", size = 5825072, upload-time = "2026-02-27T08:49:27.516Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/59/7d02447a55b2e55755011a647479041bc92a82e143f96a8195cb33bd0a1c/virtualenv-21.2.0-py3-none-any.whl", hash = "sha256:1bd755b504931164a5a496d217c014d098426cddc79363ad66ac78125f9d908f", size = 5825084, upload-time = "2026-03-09T17:24:35.378Z" },
]
[[package]]
diff --git a/devel-common/pyproject.toml b/devel-common/pyproject.toml
index bd658ba65b09c..f6d32fc99c6eb 100644
--- a/devel-common/pyproject.toml
+++ b/devel-common/pyproject.toml
@@ -35,7 +35,7 @@ dependencies = [
"kgb>=7.2.0",
"requests_mock>=1.11.0",
"rich>=13.6.0",
- "ruff==0.15.4",
+ "ruff==0.15.5",
"semver>=3.0.2",
"typer-slim>=0.15.1",
# limited due to changes needed https://github.com/apache/airflow/issues/58470
@@ -70,8 +70,7 @@ dependencies = [
"twine>=4.0.2",
]
"docs" = [
- # Astroid 4 released 5 Oct 2025 breaks autoapi https://github.com/apache/airflow/issues/56420
- "astroid>=3,<4",
+ "astroid>=4",
"checksumdir>=1.2.0",
"rich-click>=1.7.1",
"click>=8.1.8",
@@ -82,7 +81,7 @@ dependencies = [
"pagefind-bin>=1.5.0a3",
"sphinx-airflow-theme@https://airflow.apache.org/sphinx-airflow-theme/sphinx_airflow_theme-0.3.0-py3-none-any.whl",
"sphinx-argparse>=0.4.0",
- "sphinx-autoapi>=3",
+ "sphinx-autoapi>=3.8.0",
"sphinx-autobuild>=2024.10.2",
"sphinx-copybutton>=0.5.2",
"sphinx-design>=0.5.0",
@@ -99,7 +98,9 @@ dependencies = [
"sphinxcontrib-redoc>=1.6.0",
"sphinxcontrib-serializinghtml>=1.1.5",
"sphinxcontrib-spelling>=8.0.0",
- "setuptools!=82.0.0", # until https://github.com/sphinx-contrib/redoc/issues/53 is resolved
+ # setuptools 82.0.0+ causes redoc to fail due to pkg_resources removal
+ # until https://github.com/sphinx-contrib/redoc/issues/53 is resolved
+ "setuptools<82.0.0",
]
"docs-gen" = [
"diagrams>=0.24.4",
diff --git a/go-sdk/pkg/api/client.go b/go-sdk/pkg/api/client.go
index f8abbf99cbc18..fd7b77fd3954c 100644
--- a/go-sdk/pkg/api/client.go
+++ b/go-sdk/pkg/api/client.go
@@ -61,7 +61,7 @@ func (c *Client) WithBearerToken(token string) (ClientInterface, error) {
// We don't use SetAuthToken/SetAuthScheme, as that produces a (valid, but annoying) warning about using Auth
// over HTTP: "Using sensitive credentials in HTTP mode is not secure." It's a time-limited-token though, so we
- // can reasonably ignore that here and setting the header directly by-passes that
+ // can reasonably ignore that here and setting the header directly bypasses that
rc.SetHeader("Authorization", fmt.Sprintf("Bearer %s", token))
opts := []ClientOption{
diff --git a/providers/fab/src/airflow/providers/fab/www/extensions/init_appbuilder.py b/providers/fab/src/airflow/providers/fab/www/extensions/init_appbuilder.py
index 5c510984480fa..59b60c9ff6cae 100644
--- a/providers/fab/src/airflow/providers/fab/www/extensions/init_appbuilder.py
+++ b/providers/fab/src/airflow/providers/fab/www/extensions/init_appbuilder.py
@@ -19,6 +19,7 @@
from __future__ import annotations
import logging
+import threading
from functools import reduce
from typing import TYPE_CHECKING
@@ -57,6 +58,8 @@
# This module contains code imported from FlaskAppbuilder, so lets use _its_ logger name
log = logging.getLogger("flask_appbuilder.base")
+_init_app_lock = threading.Lock()
+
def dynamic_class_import(class_path):
"""
@@ -197,19 +200,23 @@ def init_app(self, app, session):
self._addon_managers = app.config["ADDON_MANAGERS"]
self.session = session
auth_manager = create_auth_manager()
- auth_manager.appbuilder = self
- if hasattr(auth_manager, "init_flask_resources"):
- auth_manager.init_flask_resources()
- if hasattr(auth_manager, "security_manager"):
- self.sm = auth_manager.security_manager
- else:
- self.sm = AirflowSecurityManagerV2(self)
- self.bm = BabelManager(self)
- self._add_global_static()
- self._add_global_filters()
- app.before_request(self.sm.before_request)
- self._add_admin_views()
- self._add_addon_views()
+ with _init_app_lock:
+ auth_manager.appbuilder = self
+ # Invalidate cached security_manager so it binds to the current Flask app.
+ if "security_manager" in auth_manager.__dict__:
+ del auth_manager.__dict__["security_manager"]
+ if hasattr(auth_manager, "init_flask_resources"):
+ auth_manager.init_flask_resources()
+ if hasattr(auth_manager, "security_manager"):
+ self.sm = auth_manager.security_manager
+ else:
+ self.sm = AirflowSecurityManagerV2(self)
+ self.bm = BabelManager(self)
+ self._add_global_static()
+ self._add_global_filters()
+ app.before_request(self.sm.before_request)
+ self._add_admin_views()
+ self._add_addon_views()
self._init_extension(app)
self._swap_url_filter()
diff --git a/pyproject.toml b/pyproject.toml
index 869bd0babf08c..08b0195a86234 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -530,7 +530,7 @@ packages = []
"apache-airflow-providers-amazon[s3fs]",
]
"uv" = [
- "uv>=0.10.7",
+ "uv>=0.10.9",
]
diff --git a/scripts/ci/docker-compose/integration-localstack.yml b/scripts/ci/docker-compose/integration-localstack.yml
index 68560bb047698..bb1ef2f5a3d86 100644
--- a/scripts/ci/docker-compose/integration-localstack.yml
+++ b/scripts/ci/docker-compose/integration-localstack.yml
@@ -27,8 +27,17 @@ services:
environment:
# LocalStack configuration: https://docs.localstack.cloud/references/configuration/
- DEBUG=${DEBUG:-0}
+ - AWS_ACCESS_KEY_ID=test
+ - AWS_SECRET_ACCESS_KEY=test
+ - AWS_DEFAULT_REGION=us-east-1
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:4566/_localstack/health"]
+ interval: 10s
+ timeout: 5s
+ retries: 10
+ start_period: 10s
airflow:
environment:
diff --git a/scripts/ci/prek/check_imports_in_providers.py b/scripts/ci/prek/check_imports_in_providers.py
index 44b1e0d67f40d..978aa990002c6 100755
--- a/scripts/ci/prek/check_imports_in_providers.py
+++ b/scripts/ci/prek/check_imports_in_providers.py
@@ -19,7 +19,7 @@
# requires-python = ">=3.10,<3.11"
# dependencies = [
# "rich>=13.6.0",
-# "ruff==0.15.4",
+# "ruff==0.15.5",
# ]
# ///
from __future__ import annotations
diff --git a/scripts/ci/prek/ruff_format.py b/scripts/ci/prek/ruff_format.py
index 951f82fff3e95..b089ff41a19ed 100755
--- a/scripts/ci/prek/ruff_format.py
+++ b/scripts/ci/prek/ruff_format.py
@@ -18,7 +18,7 @@
# /// script
# requires-python = ">=3.10,<3.11"
# dependencies = [
-# "ruff==0.15.4",
+# "ruff==0.15.5",
# ]
# ///
diff --git a/scripts/docker/clean-logs.sh b/scripts/docker/clean-logs.sh
index 1537036269538..7253424f461dd 100755
--- a/scripts/docker/clean-logs.sh
+++ b/scripts/docker/clean-logs.sh
@@ -20,7 +20,8 @@
set -euo pipefail
readonly DIRECTORY="${AIRFLOW_HOME:-/usr/local/airflow}"
-readonly RETENTION="${AIRFLOW__LOG_RETENTION_DAYS:-15}"
+readonly RETENTION_DAYS="${AIRFLOW__LOG_RETENTION_DAYS:-15}"
+readonly RETENTION_MINUTES="${AIRFLOW__LOG_RETENTION_MINUTES:-0}"
readonly FREQUENCY="${AIRFLOW__LOG_CLEANUP_FREQUENCY_MINUTES:-15}"
readonly MAX_PERCENT="${AIRFLOW__LOG_MAX_SIZE_PERCENT:-0}"
@@ -45,10 +46,12 @@ fi
retention_days="${RETENTION}"
while true; do
- echo "Trimming airflow logs to ${retention_days} days."
+ total_retention_minutes=$(( (RETENTION_DAYS * 1440) + RETENTION_MINUTES ))
+ echo "Trimming airflow logs older than ${total_retention_minutes} minutes."
+
find "${DIRECTORY}"/logs \
-type d -name 'lost+found' -prune -o \
- -type f -mtime +"${retention_days}" -name '*.log' -print0 | \
+ -type f -mmin +"${total_retention_minutes}" -name '*.log' -print0 | \
xargs -0 rm -f || true
if [[ "$MAX_SIZE_BYTES" -gt 0 && "$retention_days" -ge 0 ]]; then
diff --git a/scripts/docker/install_airflow_when_building_images.sh b/scripts/docker/install_airflow_when_building_images.sh
index 40f5b7cba1575..bec13ca2a5d39 100644
--- a/scripts/docker/install_airflow_when_building_images.sh
+++ b/scripts/docker/install_airflow_when_building_images.sh
@@ -147,7 +147,7 @@ function install_from_external_spec() {
installation_command_flags="apache-airflow[${AIRFLOW_EXTRAS}]${AIRFLOW_VERSION_SPECIFICATION}"
else
echo
- echo "${COLOR_RED}The '${INSTALLATION_METHOD}' installation method is not supported${COLOR_RESET}"
+ echo "${COLOR_RED}The '${AIRFLOW_INSTALLATION_METHOD}' installation method is not supported${COLOR_RESET}"
echo
echo "${COLOR_YELLOW}Supported methods are ('.', 'apache-airflow')${COLOR_RESET}"
echo
diff --git a/scripts/docker/install_from_docker_context_files.sh b/scripts/docker/install_from_docker_context_files.sh
index 987582f35b6ea..0dc4262aa1ac9 100644
--- a/scripts/docker/install_from_docker_context_files.sh
+++ b/scripts/docker/install_from_docker_context_files.sh
@@ -116,10 +116,28 @@ function install_airflow_and_providers_from_docker_context_files(){
fi
set -x
- ${PACKAGING_TOOL_CMD} install ${EXTRA_INSTALL_FLAGS} \
+ if ! ${PACKAGING_TOOL_CMD} install ${EXTRA_INSTALL_FLAGS} \
${ADDITIONAL_PIP_INSTALL_FLAGS} \
"${flags[@]}" \
- "${install_airflow_distribution[@]}" "${install_airflow_core_distribution[@]}" "${airflow_distributions[@]}"
+ "${install_airflow_distribution[@]}" "${install_airflow_core_distribution[@]}" "${airflow_distributions[@]}"; then
+ set +x
+ if [[ ${AIRFLOW_FALLBACK_NO_CONSTRAINTS_INSTALLATION} != "true" ]]; then
+ echo
+ echo "${COLOR_RED}Failing because constraints installation failed and fallback is disabled.${COLOR_RESET}"
+ echo
+ exit 1
+ fi
+ echo
+ echo "${COLOR_YELLOW}Likely there are new dependencies conflicting with constraints.${COLOR_RESET}"
+ echo
+ echo "${COLOR_BLUE}Falling back to no-constraints installation.${COLOR_RESET}"
+ echo
+ set -x
+ ${PACKAGING_TOOL_CMD} install ${EXTRA_INSTALL_FLAGS} \
+ ${ADDITIONAL_PIP_INSTALL_FLAGS} \
+ "${install_airflow_distribution[@]}" "${install_airflow_core_distribution[@]}" \
+ "${airflow_distributions[@]}"
+ fi
set +x
common::install_packaging_tools
# We use pip check here to make sure that whatever `uv` installs, is also "correct" according to `pip`
diff --git a/scripts/tools/setup_breeze b/scripts/tools/setup_breeze
index a277a9c2d6cec..17bdc220fcebf 100755
--- a/scripts/tools/setup_breeze
+++ b/scripts/tools/setup_breeze
@@ -27,7 +27,7 @@ COLOR_YELLOW=$'\e[33m'
COLOR_BLUE=$'\e[34m'
COLOR_RESET=$'\e[0m'
-UV_VERSION="0.10.7"
+UV_VERSION="0.10.9"
function manual_instructions() {
echo
diff --git a/task-sdk/src/airflow/sdk/__init__.py b/task-sdk/src/airflow/sdk/__init__.py
index 2eaa5ccd2a2e4..6386ec4f07d7d 100644
--- a/task-sdk/src/airflow/sdk/__init__.py
+++ b/task-sdk/src/airflow/sdk/__init__.py
@@ -30,6 +30,7 @@
"BaseOperator",
"BaseOperatorLink",
"BaseSensorOperator",
+ "BaseXCom",
"Connection",
"Context",
"DAG",
@@ -70,6 +71,7 @@
from airflow.sdk.bases.operator import BaseOperator, chain, chain_linear, cross_downstream
from airflow.sdk.bases.operatorlink import BaseOperatorLink
from airflow.sdk.bases.sensor import BaseSensorOperator, PokeReturnValue
+ from airflow.sdk.bases.xcom import BaseXCom
from airflow.sdk.definitions.asset import Asset, AssetAlias, AssetAll, AssetAny, AssetWatcher
from airflow.sdk.definitions.asset.decorators import asset
from airflow.sdk.definitions.asset.metadata import Metadata
@@ -97,6 +99,7 @@
"BaseOperator": ".bases.operator",
"BaseOperatorLink": ".bases.operatorlink",
"BaseSensorOperator": ".bases.sensor",
+ "BaseXCom": ".bases.xcom",
"Connection": ".definitions.connection",
"Context": ".definitions.context",
"DAG": ".definitions.dag",
diff --git a/task-sdk/src/airflow/sdk/__init__.pyi b/task-sdk/src/airflow/sdk/__init__.pyi
index bf7f63b209a6d..e36664594b425 100644
--- a/task-sdk/src/airflow/sdk/__init__.pyi
+++ b/task-sdk/src/airflow/sdk/__init__.pyi
@@ -34,6 +34,7 @@ from airflow.sdk.bases.sensor import (
BaseSensorOperator as BaseSensorOperator,
PokeReturnValue as PokeReturnValue,
)
+from airflow.sdk.bases.xcom import BaseXCom as BaseXCom
from airflow.sdk.definitions.asset import (
Asset as Asset,
AssetAlias as AssetAlias,
@@ -73,6 +74,7 @@ __all__ = [
"BaseOperator",
"BaseOperatorLink",
"BaseSensorOperator",
+ "BaseXCom",
"Connection",
"Context",
"DAG",
diff --git a/task-sdk/src/airflow/sdk/api/client.py b/task-sdk/src/airflow/sdk/api/client.py
index 2ef50a72ff796..8d9e48d612941 100644
--- a/task-sdk/src/airflow/sdk/api/client.py
+++ b/task-sdk/src/airflow/sdk/api/client.py
@@ -848,7 +848,8 @@ def __init__(self, *, base_url: str | None, dry_run: bool = False, token: str, *
kwargs.setdefault("base_url", "dry-run://server")
else:
kwargs["base_url"] = base_url
- kwargs["verify"] = self._get_ssl_context_cached(certifi.where(), API_SSL_CERT_PATH)
+ # Call via the class to avoid binding lru_cache wires to this instance.
+ kwargs["verify"] = type(self)._get_ssl_context_cached(certifi.where(), API_SSL_CERT_PATH)
# Set timeout if not explicitly provided
kwargs.setdefault("timeout", API_TIMEOUT)
diff --git a/task-sdk/src/airflow/sdk/definitions/dag.py b/task-sdk/src/airflow/sdk/definitions/dag.py
index e8d954e880a1a..ac96108f123f9 100644
--- a/task-sdk/src/airflow/sdk/definitions/dag.py
+++ b/task-sdk/src/airflow/sdk/definitions/dag.py
@@ -327,7 +327,7 @@ class DAG:
accessible in templates, namespaced under `params`. These
params can be overridden at the task level.
:param max_active_tasks: the number of task instances allowed to run
- concurrently
+ concurrently per Dag run. Note that in Airflow 2 this was a global limit on the Dag, since Airflow 3 it is per run.
:param max_active_runs: maximum number of active DAG runs, beyond this
number of DAG runs in a running state, the scheduler won't create
new active DAG runs
diff --git a/task-sdk/src/airflow/sdk/execution_time/context.py b/task-sdk/src/airflow/sdk/execution_time/context.py
index 379a13a930e55..512468bb48668 100644
--- a/task-sdk/src/airflow/sdk/execution_time/context.py
+++ b/task-sdk/src/airflow/sdk/execution_time/context.py
@@ -18,6 +18,7 @@
import collections
import contextlib
+import inspect
from collections.abc import Generator, Iterable, Iterator, Mapping, Sequence
from functools import cache
from typing import TYPE_CHECKING, Any, Generic, TypeVar, overload
@@ -197,8 +198,11 @@ async def _async_get_connection(conn_id: str) -> Connection:
for secrets_backend in backends:
try:
# Use async method if available, otherwise wrap sync method
- if hasattr(secrets_backend, "aget_connection"):
- conn = await secrets_backend.aget_connection(conn_id) # type: ignore[assignment]
+ # getattr avoids triggering AsyncMock coroutine creation under Python 3.13
+ async_method = getattr(secrets_backend, "aget_connection", None)
+ if async_method is not None:
+ maybe_awaitable = async_method(conn_id)
+ conn = await maybe_awaitable if inspect.isawaitable(maybe_awaitable) else maybe_awaitable
else:
conn = await sync_to_async(secrets_backend.get_connection)(conn_id) # type: ignore[assignment]
diff --git a/task-sdk/src/airflow/sdk/execution_time/supervisor.py b/task-sdk/src/airflow/sdk/execution_time/supervisor.py
index 206bf3b687802..0b02d215f27b0 100644
--- a/task-sdk/src/airflow/sdk/execution_time/supervisor.py
+++ b/task-sdk/src/airflow/sdk/execution_time/supervisor.py
@@ -36,14 +36,7 @@
from datetime import datetime, timezone
from http import HTTPStatus
from socket import socket, socketpair
-from typing import (
- TYPE_CHECKING,
- BinaryIO,
- ClassVar,
- NoReturn,
- TextIO,
- cast,
-)
+from typing import TYPE_CHECKING, BinaryIO, ClassVar, NoReturn, TextIO, cast
from urllib.parse import urlparse
from uuid import UUID
@@ -898,17 +891,29 @@ def _remote_logging_conn(client: Client):
# Fetch connection details on-demand without caching the entire API client instance
conn = _fetch_remote_logging_conn(conn_id, client)
- if conn:
- key = f"AIRFLOW_CONN_{conn_id.upper()}"
- old = os.getenv(key)
- os.environ[key] = conn.get_uri()
+ if not conn:
try:
yield
finally:
- if old is None:
- del os.environ[key]
- else:
- os.environ[key] = old
+ # Ensure we don't leak the caller's client when no connection was fetched.
+ del conn
+ del client
+ return
+
+ key = f"AIRFLOW_CONN_{conn_id.upper()}"
+ old = os.getenv(key)
+ os.environ[key] = conn.get_uri()
+ try:
+ yield
+ finally:
+ if old is None:
+ del os.environ[key]
+ else:
+ os.environ[key] = old
+
+ # Explicitly drop local references so the caller's client can be garbage collected.
+ del conn
+ del client
@attrs.define(kw_only=True)
diff --git a/task-sdk/tests/task_sdk/execution_time/test_secrets.py b/task-sdk/tests/task_sdk/execution_time/test_secrets.py
index b19ab3b1003ab..8f9745b0ffeca 100644
--- a/task-sdk/tests/task_sdk/execution_time/test_secrets.py
+++ b/task-sdk/tests/task_sdk/execution_time/test_secrets.py
@@ -144,9 +144,29 @@ def test_runtime_error_triggers_greenback_fallback(self, mocker, mock_supervisor
# Mock the greenback and asyncio modules that are imported inside the exception handler
mocker.patch("greenback.has_portal", return_value=True)
- mock_greenback_await = mocker.patch("greenback.await_", return_value=expected_conn)
mocker.patch("asyncio.current_task")
+ # Mock greenback.await_ to actually await the coroutine it receives.
+ # This prevents Python 3.13 RuntimeWarning about unawaited coroutines.
+ import asyncio
+
+ def greenback_await_side_effect(coro):
+ loop = asyncio.new_event_loop()
+ try:
+ return loop.run_until_complete(coro)
+ finally:
+ loop.close()
+
+ mock_greenback_await = mocker.patch("greenback.await_", side_effect=greenback_await_side_effect)
+
+ # Mock aget_connection to return the expected connection directly.
+ # We need to mock this because the real aget_connection would try to
+ # use SUPERVISOR_COMMS.asend which is not set up for this test.
+ async def mock_aget_connection(self, conn_id):
+ return expected_conn
+
+ mocker.patch.object(ExecutionAPISecretsBackend, "aget_connection", mock_aget_connection)
+
backend = ExecutionAPISecretsBackend()
conn = backend.get_connection("databricks_default")
diff --git a/task-sdk/tests/task_sdk/execution_time/test_supervisor.py b/task-sdk/tests/task_sdk/execution_time/test_supervisor.py
index ba3c51669311e..c7b54666a1e0c 100644
--- a/task-sdk/tests/task_sdk/execution_time/test_supervisor.py
+++ b/task-sdk/tests/task_sdk/execution_time/test_supervisor.py
@@ -225,8 +225,35 @@ class TestWatchedSubprocess:
def disable_log_upload(self, spy_agency):
spy_agency.spy_on(ActivitySubprocess._upload_logs, call_original=False)
- # TODO: Investigate and fix it after 3.1.0
- @pytest.mark.xfail(reason="Fails on Py 3.12 with multi-threading error only in tests.")
+ @pytest.fixture(autouse=True)
+ def use_real_secrets_backends(self, monkeypatch):
+ """
+ Ensure that real secrets backend instances are used instead of mocks.
+
+ This prevents Python 3.13 RuntimeWarning when hasattr checks async methods
+ on mocked backends. The warning occurs because hasattr on AsyncMock creates
+ unawaited coroutines.
+
+ This fixture ensures test isolation when running in parallel with pytest-xdist,
+ regardless of what other tests patch.
+ """
+ import importlib
+
+ import airflow.sdk.execution_time.secrets.execution_api as execution_api_module
+ from airflow.secrets.environment_variables import EnvironmentVariablesBackend
+
+ fresh_execution_backend = importlib.reload(execution_api_module).ExecutionAPISecretsBackend
+
+ # Ensure downstream imports see the restored class instead of any AsyncMock left by other tests
+ import airflow.sdk.execution_time.secrets as secrets_package
+
+ monkeypatch.setattr(secrets_package, "ExecutionAPISecretsBackend", fresh_execution_backend)
+
+ monkeypatch.setattr(
+ "airflow.sdk.execution_time.supervisor.ensure_secrets_backend_loaded",
+ lambda: [EnvironmentVariablesBackend(), fresh_execution_backend()],
+ )
+
def test_reading_from_pipes(self, captured_logs, time_machine, client_with_ti_start):
def subprocess_main():
# This is run in the subprocess!