From b00d8ce755ded47117e3162e634b7eb3b8b8d544 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 06:27:40 +0000 Subject: [PATCH 01/16] chore(deps): bump follow-redirects from 1.15.11 to 1.16.0 in /docs Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.11 to 1.16.0. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.11...v1.16.0) --- updated-dependencies: - dependency-name: follow-redirects dependency-version: 1.16.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- docs/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 0e947d0a..2704f38c 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -9615,9 +9615,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "funding": [ { "type": "individual", From 87351fe7dd75e17af035ec4141a7eb4b2ebeca95 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 9 May 2026 02:17:37 +0000 Subject: [PATCH 02/16] chore(deps): bump fast-uri from 3.1.0 to 3.1.2 in /docs Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.1.0 to 3.1.2. - [Release notes](https://github.com/fastify/fast-uri/releases) - [Commits](https://github.com/fastify/fast-uri/compare/v3.1.0...v3.1.2) --- updated-dependencies: - dependency-name: fast-uri dependency-version: 3.1.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- docs/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 8211c13f..58747164 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -9374,9 +9374,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", "funding": [ { "type": "github", From b4013f0c66eb6bc47caccf1ca72d97dcf2955d17 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 9 May 2026 13:11:39 +0000 Subject: [PATCH 03/16] chore(deps): bump @babel/plugin-transform-modules-systemjs in /docs Bumps [@babel/plugin-transform-modules-systemjs](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-modules-systemjs) from 7.29.0 to 7.29.4. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.29.4/packages/babel-plugin-transform-modules-systemjs) --- updated-dependencies: - dependency-name: "@babel/plugin-transform-modules-systemjs" dependency-version: 7.29.4 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- docs/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 8211c13f..804e0d3e 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -1216,9 +1216,9 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.0.tgz", - "integrity": "sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==", + "version": "7.29.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz", + "integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==", "license": "MIT", "dependencies": { "@babel/helper-module-transforms": "^7.28.6", From 11b684189dbfb8b3d6986233fe4940541a5bbca3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 May 2026 12:07:42 +0000 Subject: [PATCH 04/16] chore(deps): bump actions/labeler from 6.0.1 to 6.1.0 Bumps [actions/labeler](https://github.com/actions/labeler) from 6.0.1 to 6.1.0. - [Release notes](https://github.com/actions/labeler/releases) - [Commits](https://github.com/actions/labeler/compare/v6.0.1...v6.1.0) --- updated-dependencies: - dependency-name: actions/labeler dependency-version: 6.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 83c00a35..f5074bd9 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -12,7 +12,7 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@v6.0.1 + - uses: actions/labeler@v6.1.0 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" sync-labels: true From 05d636b43215eebe1dbf49bedd4d5a36e7b8140a Mon Sep 17 00:00:00 2001 From: Pete Crocker Date: Sat, 16 May 2026 15:45:55 +0100 Subject: [PATCH 05/16] fix(repository): detect existing repo when .git is a worktree gitlink MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GitRepoManager.initialize_repo() checked for `.git` with `.is_dir()`, which misses git worktrees (`.git` is a file containing `gitdir: ...`). The existing-repo branch was skipped and `Repo.init` then crashed with `FileExistsError` trying to mkdir the gitlink file. This made `infrahubctl transform` (and any other CLI relying on branch auto-detection) unusable from a worktree. Switch the check to `.exists()` — dulwich's `Repo(path)` already resolves the gitlink to the real controldir, and `porcelain.active_branch` returns the worktree's branch correctly. --- infrahub_sdk/repository.py | 3 ++- tests/unit/sdk/test_repository.py | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/infrahub_sdk/repository.py b/infrahub_sdk/repository.py index 2ad2080a..5bcd1387 100644 --- a/infrahub_sdk/repository.py +++ b/infrahub_sdk/repository.py @@ -17,7 +17,8 @@ def initialize_repo(self) -> Repo: root_path = Path(self.root_directory) - if root_path.exists() and (root_path / ".git").is_dir(): + if root_path.exists() and (root_path / ".git").exists(): + # `.git` is a directory in a regular clone and a gitlink file in a worktree. repo = Repo(self.root_directory) # Open existing repo else: repo = Repo.init(self.root_directory, default_branch=self.branch.encode("utf-8")) diff --git a/tests/unit/sdk/test_repository.py b/tests/unit/sdk/test_repository.py index 756b2e36..eca1c9a6 100644 --- a/tests/unit/sdk/test_repository.py +++ b/tests/unit/sdk/test_repository.py @@ -41,6 +41,26 @@ def test_initialize_repo_uses_existing_repo(temp_dir: str) -> None: assert (Path(temp_dir) / ".git").is_dir() +def test_initialize_repo_uses_existing_repo_in_worktree(temp_dir: str) -> None: + """A git worktree has a `.git` file (gitlink), not a directory. + + GitRepoManager must open the existing repo via the gitlink instead of trying + to re-initialize one, which would crash on `mkdir` because the file already exists. + """ + main_path = Path(temp_dir) / "main" + worktree_path = Path(temp_dir) / "worktree" + main_path.mkdir() + worktree_path.mkdir() + Repo.init(str(main_path), default_branch=b"main") + (worktree_path / ".git").write_text(f"gitdir: {main_path}/.git\n") + + manager = GitRepoManager(str(worktree_path)) + + assert manager.git is not None + assert isinstance(manager.git, Repo) + assert (worktree_path / ".git").is_file() + + def test_active_branch_returns_correct_branch(temp_dir: str) -> None: """Test that the active branch is correctly returned.""" manager = GitRepoManager(temp_dir, branch="develop") From 9c338be1b6bf7b5384d03a64faae885a18ec1c68 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jun 2026 16:54:22 +0000 Subject: [PATCH 06/16] chore(deps): bump starlette from 0.49.3 to 1.0.1 Bumps [starlette](https://github.com/Kludex/starlette) from 0.49.3 to 1.0.1. - [Release notes](https://github.com/Kludex/starlette/releases) - [Changelog](https://github.com/Kludex/starlette/blob/main/docs/release-notes.md) - [Commits](https://github.com/Kludex/starlette/compare/0.49.3...1.0.1) --- updated-dependencies: - dependency-name: starlette dependency-version: 1.0.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- uv.lock | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/uv.lock b/uv.lock index e66623aa..39a52a0f 100644 --- a/uv.lock +++ b/uv.lock @@ -568,17 +568,18 @@ wheels = [ [[package]] name = "fastapi" -version = "0.121.2" +version = "0.136.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "annotated-doc" }, { name = "pydantic" }, { name = "starlette" }, { name = "typing-extensions" }, + { name = "typing-inspection" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fb/48/f08f264da34cf160db82c62ffb335e838b1fc16cbcc905f474c7d4c815db/fastapi-0.121.2.tar.gz", hash = "sha256:ca8e932b2b823ec1721c641e3669472c855ad9564a2854c9899d904c2848b8b9", size = 342944, upload-time = "2025-11-13T17:05:54.692Z" } +sdist = { url = "https://files.pythonhosted.org/packages/81/2d/ff8d91d7b564d464629a0fd50a4489c97fcb836ac230bf3a7269232a9b1f/fastapi-0.136.3.tar.gz", hash = "sha256:e487fae93ad408e6f47641ee4dfe389864fd7bec92e547ea8498fc13f43e83ab", size = 396410, upload-time = "2026-05-23T18:53:15.192Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/eb/23/dfb161e91db7c92727db505dc72a384ee79681fe0603f706f9f9f52c2901/fastapi-0.121.2-py3-none-any.whl", hash = "sha256:f2d80b49a86a846b70cc3a03eb5ea6ad2939298bf6a7fe377aa9cd3dd079d358", size = 109201, upload-time = "2025-11-13T17:05:52.718Z" }, + { url = "https://files.pythonhosted.org/packages/e0/82/45359b62a067409bd929ae8a56b8ed13e5a8c8a61194b3c236920999ab83/fastapi-0.136.3-py3-none-any.whl", hash = "sha256:3d2a69bdf04b7e9f3afa292c3bc7a98816bbfafa10bc9b45f3f3700d2f761620", size = 117481, upload-time = "2026-05-23T18:53:16.924Z" }, ] [[package]] @@ -2658,15 +2659,15 @@ wheels = [ [[package]] name = "starlette" -version = "0.49.3" +version = "1.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/de/1a/608df0b10b53b0beb96a37854ee05864d182ddd4b1156a22f1ad3860425a/starlette-0.49.3.tar.gz", hash = "sha256:1c14546f299b5901a1ea0e34410575bc33bbd741377a10484a54445588d00284", size = 2655031, upload-time = "2025-11-01T15:12:26.13Z" } +sdist = { url = "https://files.pythonhosted.org/packages/08/a3/84e821cc54b4ab50ae6dbc6ac3800a651b65ec35f045cc73785380654057/starlette-1.0.1.tar.gz", hash = "sha256:512399c5f1de7fac99c88572212ded9ddeddef2fb32afa82d724000e88b38f4f", size = 2659596, upload-time = "2026-05-21T21:58:58.433Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/e0/021c772d6a662f43b63044ab481dc6ac7592447605b5b35a957785363122/starlette-0.49.3-py3-none-any.whl", hash = "sha256:b579b99715fdc2980cf88c8ec96d3bf1ce16f5a8051a7c2b84ef9b1cdecaea2f", size = 74340, upload-time = "2025-11-01T15:12:24.387Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e1/b2df4bc09a1e51ff664c1e17018a4274b42e5e9352e4a478ea540512dc88/starlette-1.0.1-py3-none-any.whl", hash = "sha256:7c0e69b2ee1c848bd54669d908500117a3ee13de603a21427e5c6fc1adf98dcd", size = 72802, upload-time = "2026-05-21T21:58:56.551Z" }, ] [[package]] From 6f8fb27850ef1b6a3aa9faca1b53611fdf4af008 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 23:45:10 +0000 Subject: [PATCH 07/16] chore(deps): bump pyarrow from 22.0.0 to 23.0.1 Bumps [pyarrow](https://github.com/apache/arrow) from 22.0.0 to 23.0.1. - [Release notes](https://github.com/apache/arrow/releases) - [Commits](https://github.com/apache/arrow/compare/apache-arrow-22.0.0...apache-arrow-23.0.1) --- updated-dependencies: - dependency-name: pyarrow dependency-version: 23.0.1 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- uv.lock | 106 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/uv.lock b/uv.lock index 70142375..80f4058f 100644 --- a/uv.lock +++ b/uv.lock @@ -1761,59 +1761,59 @@ wheels = [ [[package]] name = "pyarrow" -version = "22.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/53/04a7fdc63e6056116c9ddc8b43bc28c12cdd181b85cbeadb79278475f3ae/pyarrow-22.0.0.tar.gz", hash = "sha256:3d600dc583260d845c7d8a6db540339dd883081925da2bd1c5cb808f720b3cd9", size = 1151151, upload-time = "2025-10-24T12:30:00.762Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/9b/cb3f7e0a345353def531ca879053e9ef6b9f38ed91aebcf68b09ba54dec0/pyarrow-22.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:77718810bd3066158db1e95a63c160ad7ce08c6b0710bc656055033e39cdad88", size = 34223968, upload-time = "2025-10-24T10:03:31.21Z" }, - { url = "https://files.pythonhosted.org/packages/6c/41/3184b8192a120306270c5307f105b70320fdaa592c99843c5ef78aaefdcf/pyarrow-22.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:44d2d26cda26d18f7af7db71453b7b783788322d756e81730acb98f24eb90ace", size = 35942085, upload-time = "2025-10-24T10:03:38.146Z" }, - { url = "https://files.pythonhosted.org/packages/d9/3d/a1eab2f6f08001f9fb714b8ed5cfb045e2fe3e3e3c0c221f2c9ed1e6d67d/pyarrow-22.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:b9d71701ce97c95480fecb0039ec5bb889e75f110da72005743451339262f4ce", size = 44964613, upload-time = "2025-10-24T10:03:46.516Z" }, - { url = "https://files.pythonhosted.org/packages/46/46/a1d9c24baf21cfd9ce994ac820a24608decf2710521b29223d4334985127/pyarrow-22.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:710624ab925dc2b05a6229d47f6f0dac1c1155e6ed559be7109f684eba048a48", size = 47627059, upload-time = "2025-10-24T10:03:55.353Z" }, - { url = "https://files.pythonhosted.org/packages/3a/4c/f711acb13075c1391fd54bc17e078587672c575f8de2a6e62509af026dcf/pyarrow-22.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f963ba8c3b0199f9d6b794c90ec77545e05eadc83973897a4523c9e8d84e9340", size = 47947043, upload-time = "2025-10-24T10:04:05.408Z" }, - { url = "https://files.pythonhosted.org/packages/4e/70/1f3180dd7c2eab35c2aca2b29ace6c519f827dcd4cfeb8e0dca41612cf7a/pyarrow-22.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bd0d42297ace400d8febe55f13fdf46e86754842b860c978dfec16f081e5c653", size = 50206505, upload-time = "2025-10-24T10:04:15.786Z" }, - { url = "https://files.pythonhosted.org/packages/80/07/fea6578112c8c60ffde55883a571e4c4c6bc7049f119d6b09333b5cc6f73/pyarrow-22.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:00626d9dc0f5ef3a75fe63fd68b9c7c8302d2b5bbc7f74ecaedba83447a24f84", size = 28101641, upload-time = "2025-10-24T10:04:22.57Z" }, - { url = "https://files.pythonhosted.org/packages/2e/b7/18f611a8cdc43417f9394a3ccd3eace2f32183c08b9eddc3d17681819f37/pyarrow-22.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:3e294c5eadfb93d78b0763e859a0c16d4051fc1c5231ae8956d61cb0b5666f5a", size = 34272022, upload-time = "2025-10-24T10:04:28.973Z" }, - { url = "https://files.pythonhosted.org/packages/26/5c/f259e2526c67eb4b9e511741b19870a02363a47a35edbebc55c3178db22d/pyarrow-22.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:69763ab2445f632d90b504a815a2a033f74332997052b721002298ed6de40f2e", size = 35995834, upload-time = "2025-10-24T10:04:35.467Z" }, - { url = "https://files.pythonhosted.org/packages/50/8d/281f0f9b9376d4b7f146913b26fac0aa2829cd1ee7e997f53a27411bbb92/pyarrow-22.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:b41f37cabfe2463232684de44bad753d6be08a7a072f6a83447eeaf0e4d2a215", size = 45030348, upload-time = "2025-10-24T10:04:43.366Z" }, - { url = "https://files.pythonhosted.org/packages/f5/e5/53c0a1c428f0976bf22f513d79c73000926cb00b9c138d8e02daf2102e18/pyarrow-22.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:35ad0f0378c9359b3f297299c3309778bb03b8612f987399a0333a560b43862d", size = 47699480, upload-time = "2025-10-24T10:04:51.486Z" }, - { url = "https://files.pythonhosted.org/packages/95/e1/9dbe4c465c3365959d183e6345d0a8d1dc5b02ca3f8db4760b3bc834cf25/pyarrow-22.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8382ad21458075c2e66a82a29d650f963ce51c7708c7c0ff313a8c206c4fd5e8", size = 48011148, upload-time = "2025-10-24T10:04:59.585Z" }, - { url = "https://files.pythonhosted.org/packages/c5/b4/7caf5d21930061444c3cf4fa7535c82faf5263e22ce43af7c2759ceb5b8b/pyarrow-22.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1a812a5b727bc09c3d7ea072c4eebf657c2f7066155506ba31ebf4792f88f016", size = 50276964, upload-time = "2025-10-24T10:05:08.175Z" }, - { url = "https://files.pythonhosted.org/packages/ae/f3/cec89bd99fa3abf826f14d4e53d3d11340ce6f6af4d14bdcd54cd83b6576/pyarrow-22.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:ec5d40dd494882704fb876c16fa7261a69791e784ae34e6b5992e977bd2e238c", size = 28106517, upload-time = "2025-10-24T10:05:14.314Z" }, - { url = "https://files.pythonhosted.org/packages/af/63/ba23862d69652f85b615ca14ad14f3bcfc5bf1b99ef3f0cd04ff93fdad5a/pyarrow-22.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:bea79263d55c24a32b0d79c00a1c58bb2ee5f0757ed95656b01c0fb310c5af3d", size = 34211578, upload-time = "2025-10-24T10:05:21.583Z" }, - { url = "https://files.pythonhosted.org/packages/b1/d0/f9ad86fe809efd2bcc8be32032fa72e8b0d112b01ae56a053006376c5930/pyarrow-22.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:12fe549c9b10ac98c91cf791d2945e878875d95508e1a5d14091a7aaa66d9cf8", size = 35989906, upload-time = "2025-10-24T10:05:29.485Z" }, - { url = "https://files.pythonhosted.org/packages/b4/a8/f910afcb14630e64d673f15904ec27dd31f1e009b77033c365c84e8c1e1d/pyarrow-22.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:334f900ff08ce0423407af97e6c26ad5d4e3b0763645559ece6fbf3747d6a8f5", size = 45021677, upload-time = "2025-10-24T10:05:38.274Z" }, - { url = "https://files.pythonhosted.org/packages/13/95/aec81f781c75cd10554dc17a25849c720d54feafb6f7847690478dcf5ef8/pyarrow-22.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c6c791b09c57ed76a18b03f2631753a4960eefbbca80f846da8baefc6491fcfe", size = 47726315, upload-time = "2025-10-24T10:05:47.314Z" }, - { url = "https://files.pythonhosted.org/packages/bb/d4/74ac9f7a54cfde12ee42734ea25d5a3c9a45db78f9def949307a92720d37/pyarrow-22.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c3200cb41cdbc65156e5f8c908d739b0dfed57e890329413da2748d1a2cd1a4e", size = 47990906, upload-time = "2025-10-24T10:05:58.254Z" }, - { url = "https://files.pythonhosted.org/packages/2e/71/fedf2499bf7a95062eafc989ace56572f3343432570e1c54e6599d5b88da/pyarrow-22.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ac93252226cf288753d8b46280f4edf3433bf9508b6977f8dd8526b521a1bbb9", size = 50306783, upload-time = "2025-10-24T10:06:08.08Z" }, - { url = "https://files.pythonhosted.org/packages/68/ed/b202abd5a5b78f519722f3d29063dda03c114711093c1995a33b8e2e0f4b/pyarrow-22.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:44729980b6c50a5f2bfcc2668d36c569ce17f8b17bccaf470c4313dcbbf13c9d", size = 27972883, upload-time = "2025-10-24T10:06:14.204Z" }, - { url = "https://files.pythonhosted.org/packages/a6/d6/d0fac16a2963002fc22c8fa75180a838737203d558f0ed3b564c4a54eef5/pyarrow-22.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e6e95176209257803a8b3d0394f21604e796dadb643d2f7ca21b66c9c0b30c9a", size = 34204629, upload-time = "2025-10-24T10:06:20.274Z" }, - { url = "https://files.pythonhosted.org/packages/c6/9c/1d6357347fbae062ad3f17082f9ebc29cc733321e892c0d2085f42a2212b/pyarrow-22.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:001ea83a58024818826a9e3f89bf9310a114f7e26dfe404a4c32686f97bd7901", size = 35985783, upload-time = "2025-10-24T10:06:27.301Z" }, - { url = "https://files.pythonhosted.org/packages/ff/c0/782344c2ce58afbea010150df07e3a2f5fdad299cd631697ae7bd3bac6e3/pyarrow-22.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:ce20fe000754f477c8a9125543f1936ea5b8867c5406757c224d745ed033e691", size = 45020999, upload-time = "2025-10-24T10:06:35.387Z" }, - { url = "https://files.pythonhosted.org/packages/1b/8b/5362443737a5307a7b67c1017c42cd104213189b4970bf607e05faf9c525/pyarrow-22.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e0a15757fccb38c410947df156f9749ae4a3c89b2393741a50521f39a8cf202a", size = 47724601, upload-time = "2025-10-24T10:06:43.551Z" }, - { url = "https://files.pythonhosted.org/packages/69/4d/76e567a4fc2e190ee6072967cb4672b7d9249ac59ae65af2d7e3047afa3b/pyarrow-22.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cedb9dd9358e4ea1d9bce3665ce0797f6adf97ff142c8e25b46ba9cdd508e9b6", size = 48001050, upload-time = "2025-10-24T10:06:52.284Z" }, - { url = "https://files.pythonhosted.org/packages/01/5e/5653f0535d2a1aef8223cee9d92944cb6bccfee5cf1cd3f462d7cb022790/pyarrow-22.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:252be4a05f9d9185bb8c18e83764ebcfea7185076c07a7a662253af3a8c07941", size = 50307877, upload-time = "2025-10-24T10:07:02.405Z" }, - { url = "https://files.pythonhosted.org/packages/2d/f8/1d0bd75bf9328a3b826e24a16e5517cd7f9fbf8d34a3184a4566ef5a7f29/pyarrow-22.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:a4893d31e5ef780b6edcaf63122df0f8d321088bb0dee4c8c06eccb1ca28d145", size = 27977099, upload-time = "2025-10-24T10:08:07.259Z" }, - { url = "https://files.pythonhosted.org/packages/90/81/db56870c997805bf2b0f6eeeb2d68458bf4654652dccdcf1bf7a42d80903/pyarrow-22.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:f7fe3dbe871294ba70d789be16b6e7e52b418311e166e0e3cba9522f0f437fb1", size = 34336685, upload-time = "2025-10-24T10:07:11.47Z" }, - { url = "https://files.pythonhosted.org/packages/1c/98/0727947f199aba8a120f47dfc229eeb05df15bcd7a6f1b669e9f882afc58/pyarrow-22.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:ba95112d15fd4f1105fb2402c4eab9068f0554435e9b7085924bcfaac2cc306f", size = 36032158, upload-time = "2025-10-24T10:07:18.626Z" }, - { url = "https://files.pythonhosted.org/packages/96/b4/9babdef9c01720a0785945c7cf550e4acd0ebcd7bdd2e6f0aa7981fa85e2/pyarrow-22.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:c064e28361c05d72eed8e744c9605cbd6d2bb7481a511c74071fd9b24bc65d7d", size = 44892060, upload-time = "2025-10-24T10:07:26.002Z" }, - { url = "https://files.pythonhosted.org/packages/f8/ca/2f8804edd6279f78a37062d813de3f16f29183874447ef6d1aadbb4efa0f/pyarrow-22.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:6f9762274496c244d951c819348afbcf212714902742225f649cf02823a6a10f", size = 47504395, upload-time = "2025-10-24T10:07:34.09Z" }, - { url = "https://files.pythonhosted.org/packages/b9/f0/77aa5198fd3943682b2e4faaf179a674f0edea0d55d326d83cb2277d9363/pyarrow-22.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a9d9ffdc2ab696f6b15b4d1f7cec6658e1d788124418cb30030afbae31c64746", size = 48066216, upload-time = "2025-10-24T10:07:43.528Z" }, - { url = "https://files.pythonhosted.org/packages/79/87/a1937b6e78b2aff18b706d738c9e46ade5bfcf11b294e39c87706a0089ac/pyarrow-22.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ec1a15968a9d80da01e1d30349b2b0d7cc91e96588ee324ce1b5228175043e95", size = 50288552, upload-time = "2025-10-24T10:07:53.519Z" }, - { url = "https://files.pythonhosted.org/packages/60/ae/b5a5811e11f25788ccfdaa8f26b6791c9807119dffcf80514505527c384c/pyarrow-22.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:bba208d9c7decf9961998edf5c65e3ea4355d5818dd6cd0f6809bec1afb951cc", size = 28262504, upload-time = "2025-10-24T10:08:00.932Z" }, - { url = "https://files.pythonhosted.org/packages/bd/b0/0fa4d28a8edb42b0a7144edd20befd04173ac79819547216f8a9f36f9e50/pyarrow-22.0.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:9bddc2cade6561f6820d4cd73f99a0243532ad506bc510a75a5a65a522b2d74d", size = 34224062, upload-time = "2025-10-24T10:08:14.101Z" }, - { url = "https://files.pythonhosted.org/packages/0f/a8/7a719076b3c1be0acef56a07220c586f25cd24de0e3f3102b438d18ae5df/pyarrow-22.0.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:e70ff90c64419709d38c8932ea9fe1cc98415c4f87ea8da81719e43f02534bc9", size = 35990057, upload-time = "2025-10-24T10:08:21.842Z" }, - { url = "https://files.pythonhosted.org/packages/89/3c/359ed54c93b47fb6fe30ed16cdf50e3f0e8b9ccfb11b86218c3619ae50a8/pyarrow-22.0.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:92843c305330aa94a36e706c16209cd4df274693e777ca47112617db7d0ef3d7", size = 45068002, upload-time = "2025-10-24T10:08:29.034Z" }, - { url = "https://files.pythonhosted.org/packages/55/fc/4945896cc8638536ee787a3bd6ce7cec8ec9acf452d78ec39ab328efa0a1/pyarrow-22.0.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:6dda1ddac033d27421c20d7a7943eec60be44e0db4e079f33cc5af3b8280ccde", size = 47737765, upload-time = "2025-10-24T10:08:38.559Z" }, - { url = "https://files.pythonhosted.org/packages/cd/5e/7cb7edeb2abfaa1f79b5d5eb89432356155c8426f75d3753cbcb9592c0fd/pyarrow-22.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:84378110dd9a6c06323b41b56e129c504d157d1a983ce8f5443761eb5256bafc", size = 48048139, upload-time = "2025-10-24T10:08:46.784Z" }, - { url = "https://files.pythonhosted.org/packages/88/c6/546baa7c48185f5e9d6e59277c4b19f30f48c94d9dd938c2a80d4d6b067c/pyarrow-22.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:854794239111d2b88b40b6ef92aa478024d1e5074f364033e73e21e3f76b25e0", size = 50314244, upload-time = "2025-10-24T10:08:55.771Z" }, - { url = "https://files.pythonhosted.org/packages/3c/79/755ff2d145aafec8d347bf18f95e4e81c00127f06d080135dfc86aea417c/pyarrow-22.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:b883fe6fd85adad7932b3271c38ac289c65b7337c2c132e9569f9d3940620730", size = 28757501, upload-time = "2025-10-24T10:09:59.891Z" }, - { url = "https://files.pythonhosted.org/packages/0e/d2/237d75ac28ced3147912954e3c1a174df43a95f4f88e467809118a8165e0/pyarrow-22.0.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:7a820d8ae11facf32585507c11f04e3f38343c1e784c9b5a8b1da5c930547fe2", size = 34355506, upload-time = "2025-10-24T10:09:02.953Z" }, - { url = "https://files.pythonhosted.org/packages/1e/2c/733dfffe6d3069740f98e57ff81007809067d68626c5faef293434d11bd6/pyarrow-22.0.0-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:c6ec3675d98915bf1ec8b3c7986422682f7232ea76cad276f4c8abd5b7319b70", size = 36047312, upload-time = "2025-10-24T10:09:10.334Z" }, - { url = "https://files.pythonhosted.org/packages/7c/2b/29d6e3782dc1f299727462c1543af357a0f2c1d3c160ce199950d9ca51eb/pyarrow-22.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:3e739edd001b04f654b166204fc7a9de896cf6007eaff33409ee9e50ceaff754", size = 45081609, upload-time = "2025-10-24T10:09:18.61Z" }, - { url = "https://files.pythonhosted.org/packages/8d/42/aa9355ecc05997915af1b7b947a7f66c02dcaa927f3203b87871c114ba10/pyarrow-22.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:7388ac685cab5b279a41dfe0a6ccd99e4dbf322edfb63e02fc0443bf24134e91", size = 47703663, upload-time = "2025-10-24T10:09:27.369Z" }, - { url = "https://files.pythonhosted.org/packages/ee/62/45abedde480168e83a1de005b7b7043fd553321c1e8c5a9a114425f64842/pyarrow-22.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:f633074f36dbc33d5c05b5dc75371e5660f1dbf9c8b1d95669def05e5425989c", size = 48066543, upload-time = "2025-10-24T10:09:34.908Z" }, - { url = "https://files.pythonhosted.org/packages/84/e9/7878940a5b072e4f3bf998770acafeae13b267f9893af5f6d4ab3904b67e/pyarrow-22.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4c19236ae2402a8663a2c8f21f1870a03cc57f0bef7e4b6eb3238cc82944de80", size = 50288838, upload-time = "2025-10-24T10:09:44.394Z" }, - { url = "https://files.pythonhosted.org/packages/7b/03/f335d6c52b4a4761bcc83499789a1e2e16d9d201a58c327a9b5cc9a41bd9/pyarrow-22.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0c34fe18094686194f204a3b1787a27456897d8a2d62caf84b61e8dfbc0252ae", size = 29185594, upload-time = "2025-10-24T10:09:53.111Z" }, +version = "23.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/88/22/134986a4cc224d593c1afde5494d18ff629393d74cc2eddb176669f234a4/pyarrow-23.0.1.tar.gz", hash = "sha256:b8c5873e33440b2bc2f4a79d2b47017a89c5a24116c055625e6f2ee50523f019", size = 1167336, upload-time = "2026-02-16T10:14:12.39Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/a8/24e5dc6855f50a62936ceb004e6e9645e4219a8065f304145d7fb8a79d5d/pyarrow-23.0.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:3fab8f82571844eb3c460f90a75583801d14ca0cc32b1acc8c361650e006fd56", size = 34307390, upload-time = "2026-02-16T10:08:08.654Z" }, + { url = "https://files.pythonhosted.org/packages/bc/8e/4be5617b4aaae0287f621ad31c6036e5f63118cfca0dc57d42121ff49b51/pyarrow-23.0.1-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:3f91c038b95f71ddfc865f11d5876c42f343b4495535bd262c7b321b0b94507c", size = 35853761, upload-time = "2026-02-16T10:08:17.811Z" }, + { url = "https://files.pythonhosted.org/packages/2e/08/3e56a18819462210432ae37d10f5c8eed3828be1d6c751b6e6a2e93c286a/pyarrow-23.0.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:d0744403adabef53c985a7f8a082b502a368510c40d184df349a0a8754533258", size = 44493116, upload-time = "2026-02-16T10:08:25.792Z" }, + { url = "https://files.pythonhosted.org/packages/f8/82/c40b68001dbec8a3faa4c08cd8c200798ac732d2854537c5449dc859f55a/pyarrow-23.0.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:c33b5bf406284fd0bba436ed6f6c3ebe8e311722b441d89397c54f871c6863a2", size = 47564532, upload-time = "2026-02-16T10:08:34.27Z" }, + { url = "https://files.pythonhosted.org/packages/20/bc/73f611989116b6f53347581b02177f9f620efdf3cd3f405d0e83cdf53a83/pyarrow-23.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ddf743e82f69dcd6dbbcb63628895d7161e04e56794ef80550ac6f3315eeb1d5", size = 48183685, upload-time = "2026-02-16T10:08:42.889Z" }, + { url = "https://files.pythonhosted.org/packages/b0/cc/6c6b3ecdae2a8c3aced99956187e8302fc954cc2cca2a37cf2111dad16ce/pyarrow-23.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e052a211c5ac9848ae15d5ec875ed0943c0221e2fcfe69eee80b604b4e703222", size = 50605582, upload-time = "2026-02-16T10:08:51.641Z" }, + { url = "https://files.pythonhosted.org/packages/8d/94/d359e708672878d7638a04a0448edf7c707f9e5606cee11e15aaa5c7535a/pyarrow-23.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:5abde149bb3ce524782d838eb67ac095cd3fd6090eba051130589793f1a7f76d", size = 27521148, upload-time = "2026-02-16T10:08:58.077Z" }, + { url = "https://files.pythonhosted.org/packages/b0/41/8e6b6ef7e225d4ceead8459427a52afdc23379768f54dd3566014d7618c1/pyarrow-23.0.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:6f0147ee9e0386f519c952cc670eb4a8b05caa594eeffe01af0e25f699e4e9bb", size = 34302230, upload-time = "2026-02-16T10:09:03.859Z" }, + { url = "https://files.pythonhosted.org/packages/bf/4a/1472c00392f521fea03ae93408bf445cc7bfa1ab81683faf9bc188e36629/pyarrow-23.0.1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:0ae6e17c828455b6265d590100c295193f93cc5675eb0af59e49dbd00d2de350", size = 35850050, upload-time = "2026-02-16T10:09:11.877Z" }, + { url = "https://files.pythonhosted.org/packages/0c/b2/bd1f2f05ded56af7f54d702c8364c9c43cd6abb91b0e9933f3d77b4f4132/pyarrow-23.0.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:fed7020203e9ef273360b9e45be52a2a47d3103caf156a30ace5247ffb51bdbd", size = 44491918, upload-time = "2026-02-16T10:09:18.144Z" }, + { url = "https://files.pythonhosted.org/packages/0b/62/96459ef5b67957eac38a90f541d1c28833d1b367f014a482cb63f3b7cd2d/pyarrow-23.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:26d50dee49d741ac0e82185033488d28d35be4d763ae6f321f97d1140eb7a0e9", size = 47562811, upload-time = "2026-02-16T10:09:25.792Z" }, + { url = "https://files.pythonhosted.org/packages/7d/94/1170e235add1f5f45a954e26cd0e906e7e74e23392dcb560de471f7366ec/pyarrow-23.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c30143b17161310f151f4a2bcfe41b5ff744238c1039338779424e38579d701", size = 48183766, upload-time = "2026-02-16T10:09:34.645Z" }, + { url = "https://files.pythonhosted.org/packages/0e/2d/39a42af4570377b99774cdb47f63ee6c7da7616bd55b3d5001aa18edfe4f/pyarrow-23.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db2190fa79c80a23fdd29fef4b8992893f024ae7c17d2f5f4db7171fa30c2c78", size = 50607669, upload-time = "2026-02-16T10:09:44.153Z" }, + { url = "https://files.pythonhosted.org/packages/00/ca/db94101c187f3df742133ac837e93b1f269ebdac49427f8310ee40b6a58f/pyarrow-23.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:f00f993a8179e0e1c9713bcc0baf6d6c01326a406a9c23495ec1ba9c9ebf2919", size = 27527698, upload-time = "2026-02-16T10:09:50.263Z" }, + { url = "https://files.pythonhosted.org/packages/9a/4b/4166bb5abbfe6f750fc60ad337c43ecf61340fa52ab386da6e8dbf9e63c4/pyarrow-23.0.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:f4b0dbfa124c0bb161f8b5ebb40f1a680b70279aa0c9901d44a2b5a20806039f", size = 34214575, upload-time = "2026-02-16T10:09:56.225Z" }, + { url = "https://files.pythonhosted.org/packages/e1/da/3f941e3734ac8088ea588b53e860baeddac8323ea40ce22e3d0baa865cc9/pyarrow-23.0.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:7707d2b6673f7de054e2e83d59f9e805939038eebe1763fe811ee8fa5c0cd1a7", size = 35832540, upload-time = "2026-02-16T10:10:03.428Z" }, + { url = "https://files.pythonhosted.org/packages/88/7c/3d841c366620e906d54430817531b877ba646310296df42ef697308c2705/pyarrow-23.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:86ff03fb9f1a320266e0de855dee4b17da6794c595d207f89bba40d16b5c78b9", size = 44470940, upload-time = "2026-02-16T10:10:10.704Z" }, + { url = "https://files.pythonhosted.org/packages/2c/a5/da83046273d990f256cb79796a190bbf7ec999269705ddc609403f8c6b06/pyarrow-23.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:813d99f31275919c383aab17f0f455a04f5a429c261cc411b1e9a8f5e4aaaa05", size = 47586063, upload-time = "2026-02-16T10:10:17.95Z" }, + { url = "https://files.pythonhosted.org/packages/5b/3c/b7d2ebcff47a514f47f9da1e74b7949138c58cfeb108cdd4ee62f43f0cf3/pyarrow-23.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bf5842f960cddd2ef757d486041d57c96483efc295a8c4a0e20e704cbbf39c67", size = 48173045, upload-time = "2026-02-16T10:10:25.363Z" }, + { url = "https://files.pythonhosted.org/packages/43/b2/b40961262213beaba6acfc88698eb773dfce32ecdf34d19291db94c2bd73/pyarrow-23.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564baf97c858ecc03ec01a41062e8f4698abc3e6e2acd79c01c2e97880a19730", size = 50621741, upload-time = "2026-02-16T10:10:33.477Z" }, + { url = "https://files.pythonhosted.org/packages/f6/70/1fdda42d65b28b078e93d75d371b2185a61da89dda4def8ba6ba41ebdeb4/pyarrow-23.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:07deae7783782ac7250989a7b2ecde9b3c343a643f82e8a4df03d93b633006f0", size = 27620678, upload-time = "2026-02-16T10:10:39.31Z" }, + { url = "https://files.pythonhosted.org/packages/47/10/2cbe4c6f0fb83d2de37249567373d64327a5e4d8db72f486db42875b08f6/pyarrow-23.0.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6b8fda694640b00e8af3c824f99f789e836720aa8c9379fb435d4c4953a756b8", size = 34210066, upload-time = "2026-02-16T10:10:45.487Z" }, + { url = "https://files.pythonhosted.org/packages/cb/4f/679fa7e84dadbaca7a65f7cdba8d6c83febbd93ca12fa4adf40ba3b6362b/pyarrow-23.0.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:8ff51b1addc469b9444b7c6f3548e19dc931b172ab234e995a60aea9f6e6025f", size = 35825526, upload-time = "2026-02-16T10:10:52.266Z" }, + { url = "https://files.pythonhosted.org/packages/f9/63/d2747d930882c9d661e9398eefc54f15696547b8983aaaf11d4a2e8b5426/pyarrow-23.0.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:71c5be5cbf1e1cb6169d2a0980850bccb558ddc9b747b6206435313c47c37677", size = 44473279, upload-time = "2026-02-16T10:11:01.557Z" }, + { url = "https://files.pythonhosted.org/packages/b3/93/10a48b5e238de6d562a411af6467e71e7aedbc9b87f8d3a35f1560ae30fb/pyarrow-23.0.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:9b6f4f17b43bc39d56fec96e53fe89d94bac3eb134137964371b45352d40d0c2", size = 47585798, upload-time = "2026-02-16T10:11:09.401Z" }, + { url = "https://files.pythonhosted.org/packages/5c/20/476943001c54ef078dbf9542280e22741219a184a0632862bca4feccd666/pyarrow-23.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fc13fc6c403d1337acab46a2c4346ca6c9dec5780c3c697cf8abfd5e19b6b37", size = 48179446, upload-time = "2026-02-16T10:11:17.781Z" }, + { url = "https://files.pythonhosted.org/packages/4b/b6/5dd0c47b335fcd8edba9bfab78ad961bd0fd55ebe53468cc393f45e0be60/pyarrow-23.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5c16ed4f53247fa3ffb12a14d236de4213a4415d127fe9cebed33d51671113e2", size = 50623972, upload-time = "2026-02-16T10:11:26.185Z" }, + { url = "https://files.pythonhosted.org/packages/d5/09/a532297c9591a727d67760e2e756b83905dd89adb365a7f6e9c72578bcc1/pyarrow-23.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:cecfb12ef629cf6be0b1887f9f86463b0dd3dc3195ae6224e74006be4736035a", size = 27540749, upload-time = "2026-02-16T10:12:23.297Z" }, + { url = "https://files.pythonhosted.org/packages/a5/8e/38749c4b1303e6ae76b3c80618f84861ae0c55dd3c2273842ea6f8258233/pyarrow-23.0.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:29f7f7419a0e30264ea261fdc0e5fe63ce5a6095003db2945d7cd78df391a7e1", size = 34471544, upload-time = "2026-02-16T10:11:32.535Z" }, + { url = "https://files.pythonhosted.org/packages/a3/73/f237b2bc8c669212f842bcfd842b04fc8d936bfc9d471630569132dc920d/pyarrow-23.0.1-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:33d648dc25b51fd8055c19e4261e813dfc4d2427f068bcecc8b53d01b81b0500", size = 35949911, upload-time = "2026-02-16T10:11:39.813Z" }, + { url = "https://files.pythonhosted.org/packages/0c/86/b912195eee0903b5611bf596833def7d146ab2d301afeb4b722c57ffc966/pyarrow-23.0.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:cd395abf8f91c673dd3589cadc8cc1ee4e8674fa61b2e923c8dd215d9c7d1f41", size = 44520337, upload-time = "2026-02-16T10:11:47.764Z" }, + { url = "https://files.pythonhosted.org/packages/69/c2/f2a717fb824f62d0be952ea724b4f6f9372a17eed6f704b5c9526f12f2f1/pyarrow-23.0.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:00be9576d970c31defb5c32eb72ef585bf600ef6d0a82d5eccaae96639cf9d07", size = 47548944, upload-time = "2026-02-16T10:11:56.607Z" }, + { url = "https://files.pythonhosted.org/packages/84/a7/90007d476b9f0dc308e3bc57b832d004f848fd6c0da601375d20d92d1519/pyarrow-23.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c2139549494445609f35a5cda4eb94e2c9e4d704ce60a095b342f82460c73a83", size = 48236269, upload-time = "2026-02-16T10:12:04.47Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3f/b16fab3e77709856eb6ac328ce35f57a6d4a18462c7ca5186ef31b45e0e0/pyarrow-23.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7044b442f184d84e2351e5084600f0d7343d6117aabcbc1ac78eb1ae11eb4125", size = 50604794, upload-time = "2026-02-16T10:12:11.797Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a1/22df0620a9fac31d68397a75465c344e83c3dfe521f7612aea33e27ab6c0/pyarrow-23.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:a35581e856a2fafa12f3f54fce4331862b1cfb0bef5758347a858a4aa9d6bae8", size = 27660642, upload-time = "2026-02-16T10:12:17.746Z" }, + { url = "https://files.pythonhosted.org/packages/8d/1b/6da9a89583ce7b23ac611f183ae4843cd3a6cf54f079549b0e8c14031e73/pyarrow-23.0.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:5df1161da23636a70838099d4aaa65142777185cc0cdba4037a18cee7d8db9ca", size = 34238755, upload-time = "2026-02-16T10:12:32.819Z" }, + { url = "https://files.pythonhosted.org/packages/ae/b5/d58a241fbe324dbaeb8df07be6af8752c846192d78d2272e551098f74e88/pyarrow-23.0.1-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:fa8e51cb04b9f8c9c5ace6bab63af9a1f88d35c0d6cbf53e8c17c098552285e1", size = 35847826, upload-time = "2026-02-16T10:12:38.949Z" }, + { url = "https://files.pythonhosted.org/packages/54/a5/8cbc83f04aba433ca7b331b38f39e000efd9f0c7ce47128670e737542996/pyarrow-23.0.1-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:0b95a3994f015be13c63148fef8832e8a23938128c185ee951c98908a696e0eb", size = 44536859, upload-time = "2026-02-16T10:12:45.467Z" }, + { url = "https://files.pythonhosted.org/packages/36/2e/c0f017c405fcdc252dbccafbe05e36b0d0eb1ea9a958f081e01c6972927f/pyarrow-23.0.1-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:4982d71350b1a6e5cfe1af742c53dfb759b11ce14141870d05d9e540d13bc5d1", size = 47614443, upload-time = "2026-02-16T10:12:55.525Z" }, + { url = "https://files.pythonhosted.org/packages/af/6b/2314a78057912f5627afa13ba43809d9d653e6630859618b0fd81a4e0759/pyarrow-23.0.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c250248f1fe266db627921c89b47b7c06fee0489ad95b04d50353537d74d6886", size = 48232991, upload-time = "2026-02-16T10:13:04.729Z" }, + { url = "https://files.pythonhosted.org/packages/40/f2/1bcb1d3be3460832ef3370d621142216e15a2c7c62602a4ea19ec240dd64/pyarrow-23.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5f4763b83c11c16e5f4c15601ba6dfa849e20723b46aa2617cb4bffe8768479f", size = 50645077, upload-time = "2026-02-16T10:13:14.147Z" }, + { url = "https://files.pythonhosted.org/packages/eb/3f/b1da7b61cd66566a4d4c8383d376c606d1c34a906c3f1cb35c479f59d1aa/pyarrow-23.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:3a4c85ef66c134161987c17b147d6bffdca4566f9a4c1d81a0a01cdf08414ea5", size = 28234271, upload-time = "2026-02-16T10:14:09.397Z" }, + { url = "https://files.pythonhosted.org/packages/b5/78/07f67434e910a0f7323269be7bfbf58699bd0c1d080b18a1ab49ba943fe8/pyarrow-23.0.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:17cd28e906c18af486a499422740298c52d7c6795344ea5002a7720b4eadf16d", size = 34488692, upload-time = "2026-02-16T10:13:21.541Z" }, + { url = "https://files.pythonhosted.org/packages/50/76/34cf7ae93ece1f740a04910d9f7e80ba166b9b4ab9596a953e9e62b90fe1/pyarrow-23.0.1-cp314-cp314t-macosx_12_0_x86_64.whl", hash = "sha256:76e823d0e86b4fb5e1cf4a58d293036e678b5a4b03539be933d3b31f9406859f", size = 35964383, upload-time = "2026-02-16T10:13:28.63Z" }, + { url = "https://files.pythonhosted.org/packages/46/90/459b827238936d4244214be7c684e1b366a63f8c78c380807ae25ed92199/pyarrow-23.0.1-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a62e1899e3078bf65943078b3ad2a6ddcacf2373bc06379aac61b1e548a75814", size = 44538119, upload-time = "2026-02-16T10:13:35.506Z" }, + { url = "https://files.pythonhosted.org/packages/28/a1/93a71ae5881e99d1f9de1d4554a87be37da11cd6b152239fb5bd924fdc64/pyarrow-23.0.1-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:df088e8f640c9fae3b1f495b3c64755c4e719091caf250f3a74d095ddf3c836d", size = 47571199, upload-time = "2026-02-16T10:13:42.504Z" }, + { url = "https://files.pythonhosted.org/packages/88/a3/d2c462d4ef313521eaf2eff04d204ac60775263f1fb08c374b543f79f610/pyarrow-23.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:46718a220d64677c93bc243af1d44b55998255427588e400677d7192671845c7", size = 48259435, upload-time = "2026-02-16T10:13:49.226Z" }, + { url = "https://files.pythonhosted.org/packages/cc/f1/11a544b8c3d38a759eb3fbb022039117fd633e9a7b19e4841cc3da091915/pyarrow-23.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a09f3876e87f48bc2f13583ab551f0379e5dfb83210391e68ace404181a20690", size = 50629149, upload-time = "2026-02-16T10:13:57.238Z" }, + { url = "https://files.pythonhosted.org/packages/50/f2/c0e76a0b451ffdf0cf788932e182758eb7558953f4f27f1aff8e2518b653/pyarrow-23.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:527e8d899f14bd15b740cd5a54ad56b7f98044955373a17179d5956ddb93d9ce", size = 28365807, upload-time = "2026-02-16T10:14:03.892Z" }, ] [[package]] From 05ac99774b1e17c39d04ec07613a331115c5b528 Mon Sep 17 00:00:00 2001 From: Babatunde Olusola Date: Thu, 11 Jun 2026 18:00:39 +0100 Subject: [PATCH 08/16] IHS-119: Fix upsert not working for numberpool and attribute in hfid (#1014) --- changelog/339.fixed.md | 1 + .../python-sdk/guides/resource-manager.mdx | 55 ++++++++ .../sdk_ref/infrahub_sdk/node/attribute.mdx | 16 +++ infrahub_sdk/node/attribute.py | 14 ++ infrahub_sdk/node/node.py | 42 +++++- tests/unit/sdk/pool/conftest.py | 20 +++ .../unit/sdk/pool/test_attribute_from_pool.py | 129 ++++++++++++++++++ 7 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 changelog/339.fixed.md diff --git a/changelog/339.fixed.md b/changelog/339.fixed.md new file mode 100644 index 00000000..4543386c --- /dev/null +++ b/changelog/339.fixed.md @@ -0,0 +1 @@ +Calling `.save(allow_upsert=True)` on a node whose human-friendly identifier contains a CoreNumberPool-sourced attribute now raises a clear `ValidationError` instead of crashing with an opaque backend error. diff --git a/docs/docs/python-sdk/guides/resource-manager.mdx b/docs/docs/python-sdk/guides/resource-manager.mdx index 97cb4b90..8a42b6e9 100644 --- a/docs/docs/python-sdk/guides/resource-manager.mdx +++ b/docs/docs/python-sdk/guides/resource-manager.mdx @@ -309,3 +309,58 @@ query { ``` Notice that we have one IP address allocated by the Resource manager in the test branch. The query in the main branch shows us this allocation, indicating that it has been allocated and the resource cannot be allocated again. However, the IP address does not exist itself within the main branch. + +## CoreNumberPool and attribute allocation + +`CoreNumberPool` allocates integer values (such as VLAN IDs or AS numbers) directly to node attributes. The pool assigns the integer value at the moment the node is created on the server. + +```python +vlan = await client.create( + kind="InfraVLAN", + name="VLAN-100", + vlan_id={"from_pool": {"id": pool_id}}, +) +await vlan.save() +``` + +### Limitation: `allow_upsert=True` with a pool-sourced HFID attribute + +`CoreNumberPool` assigns the integer value at server creation time. The SDK does not know the assigned value before the node exists. When a node's human-friendly identifier (HFID) includes a pool-sourced attribute, the SDK cannot construct the HFID needed to look up an existing node for upsert. + +:::warning + +Calling `save(allow_upsert=True)` on a node whose HFID contains a `CoreNumberPool`-sourced attribute raises `ValidationError` before any network call is made. + +```python +# Schema has human_friendly_id: ["vlan_id__value"] +vlan = await client.create( + kind="InfraVLAN", + name="VLAN-100", + vlan_id={"from_pool": {"id": pool_id}}, +) + +# This raises ValidationError — the pool-assigned vlan_id is unknown client-side +await vlan.save(allow_upsert=True) +``` + +**Alternatives:** + +- **Two-step pattern** — create the node first, then update it in a separate call: + + ```python + await vlan.save() # creates node, pool assigns vlan_id + # later, if you need to update: + vlan.name.value = "VLAN-100-updated" + await vlan.save() # now _existing=True, calls update + ``` + +- **Explicit id** — if you already know the node's UUID, set `node.id` before saving. The upsert will use the UUID directly and skip HFID lookup: + + ```python + vlan.id = "known-uuid" + await vlan.save(allow_upsert=True) # guard bypassed + ``` + +- **Deterministic identifier** — if possible, design your schema so the HFID uses a non-pool attribute (for example, a human-assigned `name`) and keep `vlan_id` out of the HFID. + +::: diff --git a/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/attribute.mdx b/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/attribute.mdx index d08c7fc5..92a0ab98 100644 --- a/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/attribute.mdx +++ b/docs/docs/python-sdk/sdk_ref/infrahub_sdk/node/attribute.mdx @@ -36,3 +36,19 @@ Check whether this attribute's value is sourced from a resource pool. **Returns:** - True if the attribute value is a resource pool node or was explicitly allocated from a pool. + +#### `is_unresolved_pool_attribute` + +```python +is_unresolved_pool_attribute(self) -> bool +``` + +Return True when pool-backed but no concrete scalar value is available yet. + +A pool-backed attribute is unresolved when: + +- its value is a pool node object (the pool reference itself, not an allocated scalar), or +- its value is None and the from_pool allocation dict is set. + +An attribute whose _from_pool dict is set but whose value has already been populated +with the allocated scalar (e.g. after a prior save) is considered resolved. diff --git a/infrahub_sdk/node/attribute.py b/infrahub_sdk/node/attribute.py index c1a424a8..c72802d7 100644 --- a/infrahub_sdk/node/attribute.py +++ b/infrahub_sdk/node/attribute.py @@ -191,3 +191,17 @@ def is_from_pool_attribute(self) -> bool: """ return (isinstance(self.value, CoreNodeBase) and self.value.is_resource_pool()) or self._from_pool is not None + + def is_unresolved_pool_attribute(self) -> bool: + """Return True when pool-backed but no concrete scalar value is available yet. + + A pool-backed attribute is unresolved when: + - its value is a pool node object (the pool reference itself, not an allocated scalar), or + - its value is None and the from_pool allocation dict is set. + + An attribute whose _from_pool dict is set but whose value has already been populated + with the allocated scalar (e.g. after a prior save) is considered resolved. + """ + if isinstance(self.value, CoreNodeBase) and self.value.is_resource_pool(): + return True + return self._from_pool is not None and self.value is None diff --git a/infrahub_sdk/node/node.py b/infrahub_sdk/node/node.py index c39ac293..8213c686 100644 --- a/infrahub_sdk/node/node.py +++ b/infrahub_sdk/node/node.py @@ -7,7 +7,13 @@ from typing import TYPE_CHECKING, Any, BinaryIO, overload from ..constants import InfrahubClientMode -from ..exceptions import FeatureNotSupportedError, NodeNotFoundError, ResourceNotDefinedError, SchemaNotFoundError +from ..exceptions import ( + FeatureNotSupportedError, + NodeNotFoundError, + ResourceNotDefinedError, + SchemaNotFoundError, + ValidationError, +) from ..file_handler import FileHandler, FileHandlerBase, FileHandlerSync, PreparedFile, sha1_of_source from ..graphql import Mutation, Query from ..schema import ( @@ -591,6 +597,36 @@ def _get_attribute(self, name: str) -> Attribute: raise ResourceNotDefinedError(message=f"The node doesn't have an attribute for {name}") + def _validate_upsert(self, allow_upsert: bool) -> None: + """Ensure an upsert can resolve the HFID before attempting to save. + + An attribute sourced from a CoreNumberPool has no concrete value until the node is + created, so it cannot be used to look up an existing node by its human-friendly identifier. + + Raises: + ValidationError: If an HFID attribute is sourced from an unresolved CoreNumberPool. + + """ + if not (allow_upsert and not self.id): + return + + for hfid_path in self._schema.human_friendly_id or []: + attr_name = hfid_path.split("__")[0] + try: + attr = self._get_attribute(attr_name) + except ResourceNotDefinedError: + continue + if attr.is_unresolved_pool_attribute(): + raise ValidationError( + identifier=attr_name, + message=( + f"Attribute '{attr_name}' is sourced from a CoreNumberPool and is part of " + "this node's human-friendly identifier. Upsert cannot resolve the HFID " + "without a concrete value. Use an explicit id, or create the node first " + "and update it in a separate call." + ), + ) + @staticmethod def _build_rel_query_data( rel_schema: RelationshipSchemaAPI, @@ -1265,6 +1301,8 @@ async def _process_mutation_result( async def create( self, allow_upsert: bool = False, timeout: int | None = None, request_context: RequestContext | None = None ) -> None: + self._validate_upsert(allow_upsert=allow_upsert) + if self._file_object_support and self._file_content is None: raise ValueError( f"Cannot create {self._schema.kind} without file content. Use upload_from_path() or upload_from_bytes() to provide " @@ -2237,6 +2275,8 @@ def _process_mutation_result( def create( self, allow_upsert: bool = False, timeout: int | None = None, request_context: RequestContext | None = None ) -> None: + self._validate_upsert(allow_upsert=allow_upsert) + if self._file_object_support and self._file_content is None: raise ValueError( f"Cannot create {self._schema.kind} without file content. Use upload_from_path() or upload_from_bytes() to provide " diff --git a/tests/unit/sdk/pool/conftest.py b/tests/unit/sdk/pool/conftest.py index 9d0fd245..43ab113c 100644 --- a/tests/unit/sdk/pool/conftest.py +++ b/tests/unit/sdk/pool/conftest.py @@ -114,3 +114,23 @@ async def ipprefix_pool_schema() -> NodeSchemaAPI: ], } return NodeSchema(**data).convert_api() + + +@pytest.fixture +async def vlan_schema_with_pool_hfid() -> NodeSchemaAPI: + """VLAN schema where vlan_id (NumberPool-sourced) is part of the human_friendly_id.""" + data: dict[str, Any] = { + "name": "VLAN", + "namespace": "Infra", + "label": "VLAN", + "default_filter": "name__value", + "order_by": ["name__value"], + "display_labels": ["name__value"], + "human_friendly_id": ["vlan_id__value"], + "attributes": [ + {"name": "name", "kind": "Text", "unique": True}, + {"name": "vlan_id", "kind": "Number"}, + ], + "relationships": [], + } + return NodeSchema(**data).convert_api() diff --git a/tests/unit/sdk/pool/test_attribute_from_pool.py b/tests/unit/sdk/pool/test_attribute_from_pool.py index a11f2c63..28b00f81 100644 --- a/tests/unit/sdk/pool/test_attribute_from_pool.py +++ b/tests/unit/sdk/pool/test_attribute_from_pool.py @@ -11,11 +11,17 @@ from __future__ import annotations +import re from typing import TYPE_CHECKING, Any +import pytest + +from infrahub_sdk.exceptions import ValidationError from infrahub_sdk.node import InfrahubNode, InfrahubNodeSync if TYPE_CHECKING: + from pytest_httpx import HTTPXMock + from infrahub_sdk import InfrahubClient, InfrahubClientSync from infrahub_sdk.schema import NodeSchemaAPI @@ -204,3 +210,126 @@ async def test_attribute_with_pool_node_generates_mutation_query( mutation_query = vlan._generate_mutation_query() assert mutation_query["object"]["vlan_id"] == {"value": None} + + +UPSERT_MOCK_RESPONSE = { + "data": { + "InfraVLANUpsert": { + "ok": True, + "object": {"id": "mock-vlan-uuid", "vlan_id": {"value": 100}}, + } + } +} + + +async def test_save_upsert_raises_when_numberpool_attr_in_hfid( + client: InfrahubClient, + vlan_schema_with_pool_hfid: NodeSchemaAPI, +) -> None: + """save(allow_upsert=True) raises ValidationError naming the pool-sourced HFID attribute.""" + node = InfrahubNode( + client=client, + schema=vlan_schema_with_pool_hfid, + data={"name": "Test VLAN", "vlan_id": {"from_pool": {"id": POOL_ID}}}, + ) + + with pytest.raises(ValidationError, match=re.escape("Attribute 'vlan_id' is sourced from a CoreNumberPool")): + await node.save(allow_upsert=True) + + +async def test_save_upsert_proceeds_when_explicit_id_set( + client: InfrahubClient, + vlan_schema_with_pool_hfid: NodeSchemaAPI, + httpx_mock: HTTPXMock, +) -> None: + """Upsert proceeds when an explicit node id is already set.""" + httpx_mock.add_response(method="POST", json=UPSERT_MOCK_RESPONSE) + node = InfrahubNode( + client=client, + schema=vlan_schema_with_pool_hfid, + data={"name": "Test VLAN", "vlan_id": {"from_pool": {"id": POOL_ID}}}, + ) + node.id = "existing-node-uuid" + + await node.save(allow_upsert=True) + + assert node.id == "mock-vlan-uuid" + + +async def test_save_upsert_proceeds_when_numberpool_attr_not_in_hfid( + client: InfrahubClient, + vlan_schema: NodeSchemaAPI, + httpx_mock: HTTPXMock, +) -> None: + """Upsert proceeds when the pool-sourced attribute is not part of the HFID.""" + httpx_mock.add_response(method="POST", json=UPSERT_MOCK_RESPONSE) + node = InfrahubNode( + client=client, + schema=vlan_schema, + data={"name": "Test VLAN", "vlan_id": {"from_pool": {"id": POOL_ID}}}, + ) + + await node.save(allow_upsert=True) + + assert node.id == "mock-vlan-uuid" + + +async def test_save_upsert_raises_when_pool_node_object_in_hfid( + client: InfrahubClient, + vlan_schema_with_pool_hfid: NodeSchemaAPI, + ipaddress_pool_schema: NodeSchemaAPI, + ipam_ipprefix_schema: NodeSchemaAPI, + ipam_ipprefix_data: dict[str, Any], +) -> None: + """save(allow_upsert=True) raises ValidationError when vlan_id is set to a pool node object (not a from_pool dict).""" + ip_prefix = InfrahubNode(client=client, schema=ipam_ipprefix_schema, data=ipam_ipprefix_data) + ip_pool = InfrahubNode( + client=client, + schema=ipaddress_pool_schema, + data={ + "id": NODE_POOL_ID, + "name": "Core loopbacks", + "default_address_type": "IpamIPAddress", + "default_prefix_length": 32, + "ip_namespace": "ip_namespace", + "resources": [ip_prefix], + }, + ) + node = InfrahubNode( + client=client, + schema=vlan_schema_with_pool_hfid, + data={"name": "Test VLAN", "vlan_id": ip_pool}, + ) + + with pytest.raises(ValidationError, match=re.escape("Attribute 'vlan_id' is sourced from a CoreNumberPool")): + await node.save(allow_upsert=True) + + +async def test_create_upsert_raises_when_numberpool_attr_in_hfid( + client: InfrahubClient, + vlan_schema_with_pool_hfid: NodeSchemaAPI, +) -> None: + """create(allow_upsert=True) raises ValidationError directly, not only through save().""" + node = InfrahubNode( + client=client, + schema=vlan_schema_with_pool_hfid, + data={"name": "Test VLAN", "vlan_id": {"from_pool": {"id": POOL_ID}}}, + ) + + with pytest.raises(ValidationError, match=re.escape("Attribute 'vlan_id' is sourced from a CoreNumberPool")): + await node.create(allow_upsert=True) + + +def test_create_upsert_raises_when_numberpool_attr_in_hfid_sync( + client_sync: InfrahubClientSync, + vlan_schema_with_pool_hfid: NodeSchemaAPI, +) -> None: + """Sync create(allow_upsert=True) raises ValidationError directly, not only through save().""" + node = InfrahubNodeSync( + client=client_sync, + schema=vlan_schema_with_pool_hfid, + data={"name": "Test VLAN", "vlan_id": {"from_pool": {"id": POOL_ID}}}, + ) + + with pytest.raises(ValidationError, match=re.escape("Attribute 'vlan_id' is sourced from a CoreNumberPool")): + node.create(allow_upsert=True) From 73a5d35d22f817a51cea047bc00ccf2ec4292ba7 Mon Sep 17 00:00:00 2001 From: Iddo Date: Mon, 15 Jun 2026 07:10:26 +0200 Subject: [PATCH 09/16] fix(node): preserve unfetched optional one-cardinality relationships on upsert A node hydrated by `from_graphql` from a partial response would silently null-clear any optional cardinality-one relationship the response did not fetch, because the serialization gate could not distinguish "never loaded" from "explicitly cleared". Add `_peer_has_been_mutated` on `RelatedNodeBase`, flipped by `Node.__setattr__` on explicit assignment, and require it in the gate. Mirrors the existing `value_has_been_mutated` pattern on attributes. --- ...-graphql-preserves-unfetched-rels.fixed.md | 1 + infrahub_sdk/node/node.py | 16 ++++++---- infrahub_sdk/node/related_node.py | 4 +++ tests/unit/sdk/test_node.py | 30 +++++++++++++++---- 4 files changed, 40 insertions(+), 11 deletions(-) create mode 100644 changelog/+from-graphql-preserves-unfetched-rels.fixed.md diff --git a/changelog/+from-graphql-preserves-unfetched-rels.fixed.md b/changelog/+from-graphql-preserves-unfetched-rels.fixed.md new file mode 100644 index 00000000..70db340e --- /dev/null +++ b/changelog/+from-graphql-preserves-unfetched-rels.fixed.md @@ -0,0 +1 @@ +Calling `.save(allow_upsert=True)` on a node hydrated by `from_graphql` no longer silently clears optional one-cardinality relationships that the GraphQL response didn't fetch. Explicitly assigning `node.rel = None` still clears the relationship. diff --git a/infrahub_sdk/node/node.py b/infrahub_sdk/node/node.py index 8213c686..882ee39a 100644 --- a/infrahub_sdk/node/node.py +++ b/infrahub_sdk/node/node.py @@ -350,9 +350,11 @@ def _generate_input_data( # noqa: C901 rel: RelatedNodeBase | RelationshipManagerBase = getattr(self, item_name) if rel_schema.cardinality == RelationshipCardinality.ONE and rel_schema.optional and not rel.initialized: - # Only include None for existing nodes to allow clearing relationships - # For new nodes, omit the field to allow object template defaults to be applied - if self._existing: + # Emit `None` only when the caller has explicitly cleared the relationship + # (tracked by `_peer_has_been_mutated`). Without this guard, a node hydrated + # from a partial GraphQL payload — one that didn't fetch this relationship — + # would silently clear it on save. + if self._existing and isinstance(rel, RelatedNodeBase) and rel._peer_has_been_mutated: data[item_name] = None continue @@ -788,9 +790,11 @@ def __setattr__(self, name: str, value: Any) -> None: message=f"Unable to find relationship schema for '{name}' on {self._schema.kind}", ) rel_schema = rel_schemas[0] - self._relationship_cardinality_one_data[name] = RelatedNode( + new_rel = RelatedNode( name=rel_schema.name, branch=self._branch, client=self._client, schema=rel_schema, data=value ) + new_rel._peer_has_been_mutated = True + self._relationship_cardinality_one_data[name] = new_rel return super().__setattr__(name, value) @@ -1766,9 +1770,11 @@ def __setattr__(self, name: str, value: Any) -> None: message=f"Unable to find relationship schema for '{name}' on {self._schema.kind}", ) rel_schema = rel_schemas[0] - self._relationship_cardinality_one_data[name] = RelatedNodeSync( + new_rel = RelatedNodeSync( name=rel_schema.name, branch=self._branch, client=self._client, schema=rel_schema, data=value ) + new_rel._peer_has_been_mutated = True + self._relationship_cardinality_one_data[name] = new_rel return super().__setattr__(name, value) diff --git a/infrahub_sdk/node/related_node.py b/infrahub_sdk/node/related_node.py index 0e496f89..ec6cae51 100644 --- a/infrahub_sdk/node/related_node.py +++ b/infrahub_sdk/node/related_node.py @@ -44,6 +44,10 @@ def __init__(self, branch: str, schema: RelationshipSchemaAPI, data: Any | dict, self._kind: str | None = None self._source_typename: str | None = None self._relationship_metadata: RelationshipMetadata | None = None + # True once the user has assigned to this relationship via Node.__setattr__. + # Distinguishes "never loaded" (partial GraphQL payload) from "explicitly cleared" + # so we don't silently null-clear unfetched relationships on save. + self._peer_has_been_mutated: bool = False if isinstance(data, (CoreNodeBase)): self._peer = data diff --git a/tests/unit/sdk/test_node.py b/tests/unit/sdk/test_node.py index 18c4753a..ab0d72ec 100644 --- a/tests/unit/sdk/test_node.py +++ b/tests/unit/sdk/test_node.py @@ -1450,11 +1450,7 @@ async def test_create_input_data_with_dropdown( async def test_update_input_data_existing_node_with_optional_relationship( clients: BothClients, location_schema: NodeSchemaAPI, client_type: str ) -> None: - """Validate that existing nodes include None for uninitialized optional relationships. - - This ensures that we can explicitly clear optional relationships when updating existing nodes. - """ - # Simulate an existing node by including an id + """An explicit `node.rel = None` on an existing node still clears the relationship.""" data = { "id": "existing-node-id", "name": {"value": "JFK1"}, @@ -1467,7 +1463,8 @@ async def test_update_input_data_existing_node_with_optional_relationship( else: node = InfrahubNodeSync(client=clients.sync, schema=location_schema, data=data) - # For existing nodes, optional uninitialized relationships should include None + node.primary_tag = None + assert node._generate_input_data()["data"] == { "data": { "id": "existing-node-id", @@ -1479,6 +1476,27 @@ async def test_update_input_data_existing_node_with_optional_relationship( } +@pytest.mark.parametrize("client_type", client_types) +async def test_update_input_data_existing_node_preserves_untouched_optional_relationship( + clients: BothClients, location_schema: NodeSchemaAPI, client_type: str +) -> None: + """A partial payload that omits an optional one-cardinality rel must NOT silently clear it on save.""" + data = { + "id": "existing-node-id", + "name": {"value": "JFK1"}, + "description": {"value": "JFK Airport"}, + "type": {"value": "SITE"}, + } + + if client_type == "standard": + node = InfrahubNode(client=clients.standard, schema=location_schema, data=data) + else: + node = InfrahubNodeSync(client=clients.sync, schema=location_schema, data=data) + + payload = node._generate_input_data()["data"]["data"] + assert "primary_tag" not in payload + + @pytest.mark.parametrize("client_type", client_types) async def test_create_input_data__with_relationships_02( client: InfrahubClient, location_schema: NodeSchemaAPI, client_type: str From 2cd7f508acb071f8dd4dc897f74f853438427663 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2026 06:45:02 -0700 Subject: [PATCH 10/16] chore(deps-dev): bump infrahub-testcontainers from 1.9.7 to 1.9.8 (#1081) Bumps [infrahub-testcontainers](https://github.com/opsmill/infrahub) from 1.9.7 to 1.9.8. - [Release notes](https://github.com/opsmill/infrahub/releases) - [Changelog](https://github.com/opsmill/infrahub/blob/stable/CHANGELOG.md) - [Commits](https://github.com/opsmill/infrahub/compare/infrahub-v1.9.7...infrahub-v1.9.8) --- updated-dependencies: - dependency-name: infrahub-testcontainers dependency-version: 1.9.8 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uv.lock b/uv.lock index 297018ef..dcf4139b 100644 --- a/uv.lock +++ b/uv.lock @@ -895,7 +895,7 @@ types = [ [[package]] name = "infrahub-testcontainers" -version = "1.9.7" +version = "1.9.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -905,9 +905,9 @@ dependencies = [ { name = "pytest" }, { name = "testcontainers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ba/02/467481cbc12e0c841b155426e52567b443ab02928566dc0975c27f69f145/infrahub_testcontainers-1.9.7.tar.gz", hash = "sha256:87fbbaf64682ff07937543b2ef2335f8e250a8ecbd5420b525a629462a583b37", size = 17365, upload-time = "2026-06-03T16:07:03.507Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/24/e3be5eb74e157d6944c4f97eb009a4a44d83c16b91127a21d50859015548/infrahub_testcontainers-1.9.8.tar.gz", hash = "sha256:ebfdd5a163e97cca9b9ec24c89f4e6d4bab7b111643f3b210bde73c4b48d7463", size = 17380, upload-time = "2026-06-09T17:08:51.194Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1f/8b/3d60e3ad79ca9ec492c865f9464d0416ec7a008550df28fe0ced1108ef0c/infrahub_testcontainers-1.9.7-py3-none-any.whl", hash = "sha256:af29200fc4aedcbd432f53692573e3d8bac381642d89c9a56986bc6d6586e45b", size = 23199, upload-time = "2026-06-03T16:07:04.268Z" }, + { url = "https://files.pythonhosted.org/packages/c0/72/0cd541ff20074b3c179a8232162910946a3685f3035b06e6edfa8e11b6a2/infrahub_testcontainers-1.9.8-py3-none-any.whl", hash = "sha256:31da6008c0a5f4ffc8cf386210f012f83095ff3d189bd234f16853e7a6056da7", size = 23220, upload-time = "2026-06-09T17:08:50.14Z" }, ] [[package]] From d15468ddfae7447ef89e7cc0ef8ccf81e2defd3e Mon Sep 17 00:00:00 2001 From: Iddo Date: Tue, 16 Jun 2026 06:55:37 +0200 Subject: [PATCH 11/16] chore(changelog): rename fragment to issue number for towncrier autolinking --- ...om-graphql-preserves-unfetched-rels.fixed.md => 1080.fixed.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog/{+from-graphql-preserves-unfetched-rels.fixed.md => 1080.fixed.md} (100%) diff --git a/changelog/+from-graphql-preserves-unfetched-rels.fixed.md b/changelog/1080.fixed.md similarity index 100% rename from changelog/+from-graphql-preserves-unfetched-rels.fixed.md rename to changelog/1080.fixed.md From f7c5109330b9919dd866b7d7895c251175bb7ddd Mon Sep 17 00:00:00 2001 From: Pol Michel Date: Tue, 16 Jun 2026 08:31:52 +0200 Subject: [PATCH 12/16] Resolve attribute.py merge: port is_unresolved_pool_attribute to hasattr develop (94559a0) removed the CoreNodeBase import and switched to duck-typed hasattr(self.value, "is_resource_pool") checks. stable added the new is_unresolved_pool_attribute method still using isinstance(self.value, CoreNodeBase). Keep develop's refactored is_from_pool_attribute and port the new method to the hasattr style so it doesn't reference the now-unimported CoreNodeBase. Co-Authored-By: Claude Opus 4.8 (1M context) --- infrahub_sdk/node/attribute.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/infrahub_sdk/node/attribute.py b/infrahub_sdk/node/attribute.py index cc369e52..7e944e4d 100644 --- a/infrahub_sdk/node/attribute.py +++ b/infrahub_sdk/node/attribute.py @@ -189,12 +189,9 @@ def is_from_pool_attribute(self) -> bool: True if the attribute value is a resource pool node or was explicitly allocated from a pool. """ -<<<<<<< HEAD return ( hasattr(self.value, "is_resource_pool") and self.value.is_resource_pool() ) or self._from_pool is not None -======= - return (isinstance(self.value, CoreNodeBase) and self.value.is_resource_pool()) or self._from_pool is not None def is_unresolved_pool_attribute(self) -> bool: """Return True when pool-backed but no concrete scalar value is available yet. @@ -206,7 +203,6 @@ def is_unresolved_pool_attribute(self) -> bool: An attribute whose _from_pool dict is set but whose value has already been populated with the allocated scalar (e.g. after a prior save) is considered resolved. """ - if isinstance(self.value, CoreNodeBase) and self.value.is_resource_pool(): + if hasattr(self.value, "is_resource_pool") and self.value.is_resource_pool(): return True return self._from_pool is not None and self.value is None ->>>>>>> origin/stable From 9fc055b221167abe5573d3b87b2c124fcda09a4f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Jun 2026 15:48:31 +0000 Subject: [PATCH 13/16] chore(deps-dev): bump vite from 8.0.11 to 8.0.16 in /docs Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 8.0.11 to 8.0.16. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v8.0.16/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-version: 8.0.16 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- docs/package-lock.json | 168 ++++++++++++++++++++--------------------- 1 file changed, 84 insertions(+), 84 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 71bcc4a3..3359cb84 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -4805,14 +4805,14 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", + "integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.1" + "@tybys/wasm-util": "^0.10.2" }, "funding": { "type": "github", @@ -4871,9 +4871,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.128.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.128.0.tgz", - "integrity": "sha512-huv1Y/LzBJkBVHt3OlC7u0zHBW9qXf1FdD7sGmc1rXc2P1mTwHssYv7jyGx5KAACSCH+9B3Bhn6Z9luHRvf7pQ==", + "version": "0.133.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", + "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", "dev": true, "license": "MIT", "funding": { @@ -5085,9 +5085,9 @@ "license": "MIT" }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.18", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.18.tgz", - "integrity": "sha512-lIDyUAfD7U3+BWKzdxMbJcsYHuqXqmGz40aeRqvuAm3y5TkJSYTBW2RDrn65DJFPQqVjUAUqq5uz8urzQ8aBdQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", + "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", "cpu": [ "arm64" ], @@ -5102,9 +5102,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.18", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.18.tgz", - "integrity": "sha512-apJq2ktnGp27nSInMR5Vcj8kY6xJzDAvfdIFlpDcAK/w4cDO58qVoi1YQsES/SKiFNge/6e4CUzgjfHduYqWpQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", + "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", "cpu": [ "arm64" ], @@ -5119,9 +5119,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.18", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.18.tgz", - "integrity": "sha512-5Ofot8xbs+pxRHJqm9/9N/4sTQOvdrwEsmPE9pdLEEoAbdZtG6F2LMDfO1sp6ZAtXJuJV/21ew2srq3W8NXB5g==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", + "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", "cpu": [ "x64" ], @@ -5136,9 +5136,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.18", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.18.tgz", - "integrity": "sha512-7h8eeOTT1eyqJyx64BFCnWZpNm486hGWt2sqeLLgDxA0xI1oGZ9H7gK1S85uNGmBhkdPwa/6reTxfFFKvIsebw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", + "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", "cpu": [ "x64" ], @@ -5153,9 +5153,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.18", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.18.tgz", - "integrity": "sha512-eRcm/HVt9U/JFu5RKAEKwGQYtDCKWLiaH6wOnsSEp6NMBb/3Os8LgHZlNyzMpFVNmiiMFlfb2zEnebfzJrHFmg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", + "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", "cpu": [ "arm" ], @@ -5170,9 +5170,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.18", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.18.tgz", - "integrity": "sha512-SOrT/cT4ukTmgnrEz/Hg3m7LBnuCLW9psDeMKrimRWY4I8DmnO7Lco8W2vtqPmMkbVu8iJ+g4GFLVLLOVjJ9DQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", + "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", "cpu": [ "arm64" ], @@ -5190,9 +5190,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.18", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.18.tgz", - "integrity": "sha512-QWjdxN1HJCpBTAcZ5N5F7wju3gVPzRzSpmGzx7na0c/1qpN9CFil+xt+l9lV/1M6/gqHSNXCiqPfwhVJPeLnug==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", + "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", "cpu": [ "arm64" ], @@ -5210,9 +5210,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.18", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.18.tgz", - "integrity": "sha512-ugCOyj7a4d9h3q9B+wXmf6g3a68UsjGh6dob5DHevHGMwDUbhsYNbSPxJsENcIttJZ9jv7qGM2UesLw5jqIhdg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", + "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", "cpu": [ "ppc64" ], @@ -5230,9 +5230,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.18", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.18.tgz", - "integrity": "sha512-kKWRhbsotpXkGbcd5dllUWg5gEXcDAa8u5YnP9AV5DYNbvJHGzzuwv7dpmhc8NqKMJldl0a+x76IHbspEpEmdA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", + "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", "cpu": [ "s390x" ], @@ -5250,9 +5250,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.18", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.18.tgz", - "integrity": "sha512-uCo8ElcCIAMyYAZyuIZ81oFkhTSIllNvUCHCAlbhlN4ji3uC28h7IIdlXyIvGO7HsuqnV9p3rD/bpH7XhIyhRw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", + "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", "cpu": [ "x64" ], @@ -5270,9 +5270,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.18", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.18.tgz", - "integrity": "sha512-XNOQZtuE6yUIvx4rwGemwh8kpL1xvU41FXy/s9K7T/3JVcqGzo3NfKM2HrbrGgfPYGFW42f07Wk++aOC6B9NWA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", + "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", "cpu": [ "x64" ], @@ -5290,9 +5290,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.18", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.18.tgz", - "integrity": "sha512-tSn/kzrfa7tNOXr7sEacDBN4YsIqTyLqh45IO0nHDwtpKIDNDJr+VFojt+4klSpChxB29JLyduSsE0MKEwa65A==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", + "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", "cpu": [ "arm64" ], @@ -5307,9 +5307,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.18", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.18.tgz", - "integrity": "sha512-+J9YGmc+czgqlhYmwun3S3O0FIZhsH8ep2456xwjAdIOmuJxM7xz4P4PtrxU+Bz17a/5bqPA8o3HAAoX0teUdg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", + "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", "cpu": [ "wasm32" ], @@ -5326,9 +5326,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.18", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.18.tgz", - "integrity": "sha512-zsu47DgU0FQzSwi6sU9dZoEdUv7pc1AptSEz/Z8HBg54sV0Pbs3N0+CrIbTsgiu6EyoaNN9CHboqbLaz9lhOyQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", + "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", "cpu": [ "arm64" ], @@ -5343,9 +5343,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.18", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.18.tgz", - "integrity": "sha512-7H+3yqGgmnlDTRRhw/xpYY9J1kf4GC681nVc4GqKhExZTDrVVrV2tsOR9kso0fvgBdcTCcQShx4SLLoHgaLwhg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", + "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", "cpu": [ "x64" ], @@ -5360,9 +5360,9 @@ } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.18", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.18.tgz", - "integrity": "sha512-CUY5Mnhe64xQBGZEEXQ5WyZwsc1JU3vAZLIxtrsBt3LO6UOb+C8GunVKqe9sT8NeWb4lqSaoJtp2xo6GxT1MNw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", "dev": true, "license": "MIT" }, @@ -17861,14 +17861,14 @@ } }, "node_modules/rolldown": { - "version": "1.0.0-rc.18", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.18.tgz", - "integrity": "sha512-phmyKBpuBdRYDf4hgyynGAYn/rDDe+iZXKVJ7WX5b1zQzpLkP5oJRPGsfJuHdzPMlyyEO/4sPW6yfSx2gf7lVg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", + "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.128.0", - "@rolldown/pluginutils": "1.0.0-rc.18" + "@oxc-project/types": "=0.133.0", + "@rolldown/pluginutils": "^1.0.0" }, "bin": { "rolldown": "bin/cli.mjs" @@ -17877,21 +17877,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.18", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.18", - "@rolldown/binding-darwin-x64": "1.0.0-rc.18", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.18", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.18", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.18", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.18", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.18", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.18", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.18", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.18", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.18", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.18", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.18", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.18" + "@rolldown/binding-android-arm64": "1.0.3", + "@rolldown/binding-darwin-arm64": "1.0.3", + "@rolldown/binding-darwin-x64": "1.0.3", + "@rolldown/binding-freebsd-x64": "1.0.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", + "@rolldown/binding-linux-arm64-gnu": "1.0.3", + "@rolldown/binding-linux-arm64-musl": "1.0.3", + "@rolldown/binding-linux-ppc64-gnu": "1.0.3", + "@rolldown/binding-linux-s390x-gnu": "1.0.3", + "@rolldown/binding-linux-x64-gnu": "1.0.3", + "@rolldown/binding-linux-x64-musl": "1.0.3", + "@rolldown/binding-openharmony-arm64": "1.0.3", + "@rolldown/binding-wasm32-wasi": "1.0.3", + "@rolldown/binding-win32-arm64-msvc": "1.0.3", + "@rolldown/binding-win32-x64-msvc": "1.0.3" } }, "node_modules/rtlcss": { @@ -19045,9 +19045,9 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.16", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", "dependencies": { @@ -19826,17 +19826,17 @@ } }, "node_modules/vite": { - "version": "8.0.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.11.tgz", - "integrity": "sha512-Jz1mxtUBR5xTT65VOdJZUUeoyLtqljmFkiUXhPTLZka3RDc9vpi/xXkyrnsdRcm2lIi3l3GPMnAidTsEGIj3Ow==", + "version": "8.0.16", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", + "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", - "postcss": "^8.5.14", - "rolldown": "1.0.0-rc.18", - "tinyglobby": "^0.2.16" + "postcss": "^8.5.15", + "rolldown": "1.0.3", + "tinyglobby": "^0.2.17" }, "bin": { "vite": "bin/vite.js" From b889a0bda1c49aec20b3e0dccf2cdf971fb6e4c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2026 03:10:37 +0000 Subject: [PATCH 14/16] chore(deps): bump ws in /docs Bumps and [ws](https://github.com/websockets/ws). These dependencies needed to be updated together. Updates `ws` from 7.5.10 to 7.5.11 - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/7.5.10...7.5.11) Updates `ws` from 8.20.0 to 8.21.0 - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/7.5.10...7.5.11) --- updated-dependencies: - dependency-name: ws dependency-version: 7.5.11 dependency-type: indirect - dependency-name: ws dependency-version: 8.21.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- docs/package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 71bcc4a3..ef19caad 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -20293,9 +20293,9 @@ } }, "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", - "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -20532,9 +20532,9 @@ } }, "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "version": "7.5.11", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz", + "integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==", "license": "MIT", "engines": { "node": ">=8.3.0" From 9977473642ca8c9d9ddac6b698284c5dfa15b816 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2026 03:10:39 +0000 Subject: [PATCH 15/16] chore(deps): bump webpack-dev-server from 5.2.3 to 5.2.5 in /docs Bumps [webpack-dev-server](https://github.com/webpack/webpack-dev-server) from 5.2.3 to 5.2.5. - [Release notes](https://github.com/webpack/webpack-dev-server/releases) - [Changelog](https://github.com/webpack/webpack-dev-server/blob/main/CHANGELOG.md) - [Commits](https://github.com/webpack/webpack-dev-server/compare/v5.2.3...v5.2.5) --- updated-dependencies: - dependency-name: webpack-dev-server dependency-version: 5.2.5 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- docs/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 71bcc4a3..dc2b463a 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -20206,9 +20206,9 @@ } }, "node_modules/webpack-dev-server": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.3.tgz", - "integrity": "sha512-9Gyu2F7+bg4Vv+pjbovuYDhHX+mqdqITykfzdM9UyKqKHlsE5aAjRhR+oOEfXW5vBeu8tarzlJFIZva4ZjAdrQ==", + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.5.tgz", + "integrity": "sha512-4wZtCquSuv9CKX8oybo+mqxtxZqWz47uM1Ch94lxowBztOhWCbhqvRbfC/mODOwxgV2brY+JGZpHq58/SuVFYg==", "license": "MIT", "dependencies": { "@types/bonjour": "^3.5.13", From b656196d0e415771a489da4eb0b6132935251674 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2026 03:10:46 +0000 Subject: [PATCH 16/16] chore(deps): bump launch-editor from 2.13.2 to 2.14.1 in /docs Bumps [launch-editor](https://github.com/vitejs/launch-editor) from 2.13.2 to 2.14.1. - [Commits](https://github.com/vitejs/launch-editor/compare/v2.13.2...v2.14.1) --- updated-dependencies: - dependency-name: launch-editor dependency-version: 2.14.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- docs/package-lock.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index 71bcc4a3..c72c4202 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -11389,13 +11389,13 @@ } }, "node_modules/launch-editor": { - "version": "2.13.2", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.13.2.tgz", - "integrity": "sha512-4VVDnbOpLXy/s8rdRCSXb+zfMeFR0WlJWpET1iA9CQdlZDfwyLjUuGQzXU4VeOoey6AicSAluWan7Etga6Kcmg==", + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.14.1.tgz", + "integrity": "sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==", "license": "MIT", "dependencies": { "picocolors": "^1.1.1", - "shell-quote": "^1.8.3" + "shell-quote": "^1.8.4" } }, "node_modules/leven": { @@ -18327,9 +18327,9 @@ } }, "node_modules/shell-quote": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz", + "integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==", "license": "MIT", "engines": { "node": ">= 0.4"