diff --git a/.gitignore b/.gitignore index 11af4543bd..aa08e47aec 100644 --- a/.gitignore +++ b/.gitignore @@ -121,8 +121,6 @@ prime/ /.goosehints /.windsurfrules /.github/copilot-instructions.md -/AGENT.md -/CLAUDE.md /llms.txt # Ignore worktrees when working on multiple branches diff --git a/AGENTS.md b/AGENTS.md index f4414bfc8c..d0912c6bde 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,8 +1,6 @@ # Instructions for agents - Use `make help` to find available development targets -- Use the latest Golang stable release when working on Go code -- Use the latest Node.js LTS release when working on TypeScript code - Before committing `.go` changes, run `make fmt` to format, and run `make lint-go` to lint - Before committing `.ts` changes, run `make lint-js` to lint - Before committing `go.mod` changes, run `make tidy` diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..43c994c2d3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/Dockerfile b/Dockerfile index 7893db73e8..79f507dbc6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 # Build stage -FROM docker.io/library/golang:1.25-alpine3.23 AS build-env +FROM docker.io/library/golang:1.26-alpine3.23 AS build-env ARG GOPROXY=direct diff --git a/Dockerfile.rootless b/Dockerfile.rootless index 6640c2b2de..fe94774add 100644 --- a/Dockerfile.rootless +++ b/Dockerfile.rootless @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 # Build stage -FROM docker.io/library/golang:1.25-alpine3.23 AS build-env +FROM docker.io/library/golang:1.26-alpine3.23 AS build-env ARG GOPROXY=direct diff --git a/Makefile b/Makefile index 6054611580..93d87fc139 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ XGO_VERSION := go-1.25.x AIR_PACKAGE ?= github.com/air-verse/air@v1 EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3 GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.9.2 -GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.8.0 +GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.9.0 GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.15 MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.7.0 SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.33.1 diff --git a/assets/go-licenses.json b/assets/go-licenses.json index 5ee65f2c8a..bf0c174811 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -27,7 +27,7 @@ { "name": "connectrpc.com/connect", "path": "connectrpc.com/connect/LICENSE", - "licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright 2021-2024 The Connect Authors\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" + "licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright 2021-2025 The Connect Authors\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" }, { "name": "dario.cat/mergo", @@ -584,11 +584,6 @@ "path": "github.com/gogs/go-gogs-client/LICENSE", "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2014 Go Git Service\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n" }, - { - "name": "github.com/golang-jwt/jwt/v4", - "path": "github.com/golang-jwt/jwt/v4/LICENSE", - "licenseText": "Copyright (c) 2012 Dave Grijalva\nCopyright (c) 2021 golang-jwt maintainers\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n" - }, { "name": "github.com/golang-jwt/jwt/v5", "path": "github.com/golang-jwt/jwt/v5/LICENSE", @@ -609,11 +604,6 @@ "path": "github.com/golang/groupcache/lru/LICENSE", "licenseText": "Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n\"License\" shall mean the terms and conditions for use, reproduction, and\ndistribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by the copyright\nowner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all other entities\nthat control, are controlled by, or are under common control with that entity.\nFor the purposes of this definition, \"control\" means (i) the power, direct or\nindirect, to cause the direction or management of such entity, whether by\ncontract or otherwise, or (ii) ownership of fifty percent (50%) or more of the\noutstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity exercising\npermissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications, including\nbut not limited to software source code, documentation source, and configuration\nfiles.\n\n\"Object\" form shall mean any form resulting from mechanical transformation or\ntranslation of a Source form, including but not limited to compiled object code,\ngenerated documentation, and conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or Object form, made\navailable under the License, as indicated by a copyright notice that is included\nin or attached to the work (an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object form, that\nis based on (or derived from) the Work and for which the editorial revisions,\nannotations, elaborations, or other modifications represent, as a whole, an\noriginal work of authorship. For the purposes of this License, Derivative Works\nshall not include works that remain separable from, or merely link (or bind by\nname) to the interfaces of, the Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including the original version\nof the Work and any modifications or additions to that Work or Derivative Works\nthereof, that is intentionally submitted to Licensor for inclusion in the Work\nby the copyright owner or by an individual or Legal Entity authorized to submit\non behalf of the copyright owner. For the purposes of this definition,\n\"submitted\" means any form of electronic, verbal, or written communication sent\nto the Licensor or its representatives, including but not limited to\ncommunication on electronic mailing lists, source code control systems, and\nissue tracking systems that are managed by, or on behalf of, the Licensor for\nthe purpose of discussing and improving the Work, but excluding communication\nthat is conspicuously marked or otherwise designated in writing by the copyright\nowner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity on behalf\nof whom a Contribution has been received by Licensor and subsequently\nincorporated within the Work.\n\n2. Grant of Copyright License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable copyright license to reproduce, prepare Derivative Works of,\npublicly display, publicly perform, sublicense, and distribute the Work and such\nDerivative Works in Source or Object form.\n\n3. Grant of Patent License.\n\nSubject to the terms and conditions of this License, each Contributor hereby\ngrants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,\nirrevocable (except as stated in this section) patent license to make, have\nmade, use, offer to sell, sell, import, and otherwise transfer the Work, where\nsuch license applies only to those patent claims licensable by such Contributor\nthat are necessarily infringed by their Contribution(s) alone or by combination\nof their Contribution(s) with the Work to which such Contribution(s) was\nsubmitted. If You institute patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that the Work or a\nContribution incorporated within the Work constitutes direct or contributory\npatent infringement, then any patent licenses granted to You under this License\nfor that Work shall terminate as of the date such litigation is filed.\n\n4. Redistribution.\n\nYou may reproduce and distribute copies of the Work or Derivative Works thereof\nin any medium, with or without modifications, and in Source or Object form,\nprovided that You meet the following conditions:\n\nYou must give any other recipients of the Work or Derivative Works a copy of\nthis License; and\nYou must cause any modified files to carry prominent notices stating that You\nchanged the files; and\nYou must retain, in the Source form of any Derivative Works that You distribute,\nall copyright, patent, trademark, and attribution notices from the Source form\nof the Work, excluding those notices that do not pertain to any part of the\nDerivative Works; and\nIf the Work includes a \"NOTICE\" text file as part of its distribution, then any\nDerivative Works that You distribute must include a readable copy of the\nattribution notices contained within such NOTICE file, excluding those notices\nthat do not pertain to any part of the Derivative Works, in at least one of the\nfollowing places: within a NOTICE text file distributed as part of the\nDerivative Works; within the Source form or documentation, if provided along\nwith the Derivative Works; or, within a display generated by the Derivative\nWorks, if and wherever such third-party notices normally appear. The contents of\nthe NOTICE file are for informational purposes only and do not modify the\nLicense. You may add Your own attribution notices within Derivative Works that\nYou distribute, alongside or as an addendum to the NOTICE text from the Work,\nprovided that such additional attribution notices cannot be construed as\nmodifying the License.\nYou may add Your own copyright statement to Your modifications and may provide\nadditional or different license terms and conditions for use, reproduction, or\ndistribution of Your modifications, or for any such Derivative Works as a whole,\nprovided Your use, reproduction, and distribution of the Work otherwise complies\nwith the conditions stated in this License.\n\n5. Submission of Contributions.\n\nUnless You explicitly state otherwise, any Contribution intentionally submitted\nfor inclusion in the Work by You to the Licensor shall be under the terms and\nconditions of this License, without any additional terms or conditions.\nNotwithstanding the above, nothing herein shall supersede or modify the terms of\nany separate license agreement you may have executed with Licensor regarding\nsuch Contributions.\n\n6. Trademarks.\n\nThis License does not grant permission to use the trade names, trademarks,\nservice marks, or product names of the Licensor, except as required for\nreasonable and customary use in describing the origin of the Work and\nreproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty.\n\nUnless required by applicable law or agreed to in writing, Licensor provides the\nWork (and each Contributor provides its Contributions) on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,\nincluding, without limitation, any warranties or conditions of TITLE,\nNON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are\nsolely responsible for determining the appropriateness of using or\nredistributing the Work and assume any risks associated with Your exercise of\npermissions under this License.\n\n8. Limitation of Liability.\n\nIn no event and under no legal theory, whether in tort (including negligence),\ncontract, or otherwise, unless required by applicable law (such as deliberate\nand grossly negligent acts) or agreed to in writing, shall any Contributor be\nliable to You for damages, including any direct, indirect, special, incidental,\nor consequential damages of any character arising as a result of this License or\nout of the use or inability to use the Work (including but not limited to\ndamages for loss of goodwill, work stoppage, computer failure or malfunction, or\nany and all other commercial damages or losses), even if such Contributor has\nbeen advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability.\n\nWhile redistributing the Work or Derivative Works thereof, You may choose to\noffer, and charge a fee for, acceptance of support, warranty, indemnity, or\nother liability obligations and/or rights consistent with this License. However,\nin accepting such obligations, You may act only on Your own behalf and on Your\nsole responsibility, not on behalf of any other Contributor, and only if You\nagree to indemnify, defend, and hold each Contributor harmless for any liability\nincurred by, or claims asserted against, such Contributor by reason of your\naccepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work\n\nTo apply the Apache License to your work, attach the following boilerplate\nnotice, with the fields enclosed by brackets \"[]\" replaced with your own\nidentifying information. (Don't include the brackets!) The text should be\nenclosed in the appropriate comment syntax for the file format. We also\nrecommend that a file or class name and description of purpose be included on\nthe same \"printed page\" as the copyright notice for easier identification within\nthird-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" }, - { - "name": "github.com/golang/protobuf/proto", - "path": "github.com/golang/protobuf/proto/LICENSE", - "licenseText": "Copyright 2010 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n" - }, { "name": "github.com/golang/snappy", "path": "github.com/golang/snappy/LICENSE", @@ -707,7 +697,7 @@ { "name": "github.com/hashicorp/go-version", "path": "github.com/hashicorp/go-version/LICENSE", - "licenseText": "Mozilla Public License, version 2.0\n\n1. Definitions\n\n1.1. “Contributor”\n\n means each individual or legal entity that creates, contributes to the\n creation of, or owns Covered Software.\n\n1.2. “Contributor Version”\n\n means the combination of the Contributions of others (if any) used by a\n Contributor and that particular Contributor’s Contribution.\n\n1.3. “Contribution”\n\n means Covered Software of a particular Contributor.\n\n1.4. “Covered Software”\n\n means Source Code Form to which the initial Contributor has attached the\n notice in Exhibit A, the Executable Form of such Source Code Form, and\n Modifications of such Source Code Form, in each case including portions\n thereof.\n\n1.5. “Incompatible With Secondary Licenses”\n means\n\n a. that the initial Contributor has attached the notice described in\n Exhibit B to the Covered Software; or\n\n b. that the Covered Software was made available under the terms of version\n 1.1 or earlier of the License, but not also under the terms of a\n Secondary License.\n\n1.6. “Executable Form”\n\n means any form of the work other than Source Code Form.\n\n1.7. “Larger Work”\n\n means a work that combines Covered Software with other material, in a separate\n file or files, that is not Covered Software.\n\n1.8. “License”\n\n means this document.\n\n1.9. “Licensable”\n\n means having the right to grant, to the maximum extent possible, whether at the\n time of the initial grant or subsequently, any and all of the rights conveyed by\n this License.\n\n1.10. “Modifications”\n\n means any of the following:\n\n a. any file in Source Code Form that results from an addition to, deletion\n from, or modification of the contents of Covered Software; or\n\n b. any new file in Source Code Form that contains any Covered Software.\n\n1.11. “Patent Claims” of a Contributor\n\n means any patent claim(s), including without limitation, method, process,\n and apparatus claims, in any patent Licensable by such Contributor that\n would be infringed, but for the grant of the License, by the making,\n using, selling, offering for sale, having made, import, or transfer of\n either its Contributions or its Contributor Version.\n\n1.12. “Secondary License”\n\n means either the GNU General Public License, Version 2.0, the GNU Lesser\n General Public License, Version 2.1, the GNU Affero General Public\n License, Version 3.0, or any later versions of those licenses.\n\n1.13. “Source Code Form”\n\n means the form of the work preferred for making modifications.\n\n1.14. “You” (or “Your”)\n\n means an individual or a legal entity exercising rights under this\n License. For legal entities, “You” includes any entity that controls, is\n controlled by, or is under common control with You. For purposes of this\n definition, “control” means (a) the power, direct or indirect, to cause\n the direction or management of such entity, whether by contract or\n otherwise, or (b) ownership of more than fifty percent (50%) of the\n outstanding shares or beneficial ownership of such entity.\n\n\n2. License Grants and Conditions\n\n2.1. Grants\n\n Each Contributor hereby grants You a world-wide, royalty-free,\n non-exclusive license:\n\n a. under intellectual property rights (other than patent or trademark)\n Licensable by such Contributor to use, reproduce, make available,\n modify, display, perform, distribute, and otherwise exploit its\n Contributions, either on an unmodified basis, with Modifications, or as\n part of a Larger Work; and\n\n b. under Patent Claims of such Contributor to make, use, sell, offer for\n sale, have made, import, and otherwise transfer either its Contributions\n or its Contributor Version.\n\n2.2. Effective Date\n\n The licenses granted in Section 2.1 with respect to any Contribution become\n effective for each Contribution on the date the Contributor first distributes\n such Contribution.\n\n2.3. Limitations on Grant Scope\n\n The licenses granted in this Section 2 are the only rights granted under this\n License. No additional rights or licenses will be implied from the distribution\n or licensing of Covered Software under this License. Notwithstanding Section\n 2.1(b) above, no patent license is granted by a Contributor:\n\n a. for any code that a Contributor has removed from Covered Software; or\n\n b. for infringements caused by: (i) Your and any other third party’s\n modifications of Covered Software, or (ii) the combination of its\n Contributions with other software (except as part of its Contributor\n Version); or\n\n c. under Patent Claims infringed by Covered Software in the absence of its\n Contributions.\n\n This License does not grant any rights in the trademarks, service marks, or\n logos of any Contributor (except as may be necessary to comply with the\n notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\n No Contributor makes additional grants as a result of Your choice to\n distribute the Covered Software under a subsequent version of this License\n (see Section 10.2) or under the terms of a Secondary License (if permitted\n under the terms of Section 3.3).\n\n2.5. Representation\n\n Each Contributor represents that the Contributor believes its Contributions\n are its original creation(s) or it has sufficient rights to grant the\n rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\n This License is not intended to limit any rights You have under applicable\n copyright doctrines of fair use, fair dealing, or other equivalents.\n\n2.7. Conditions\n\n Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in\n Section 2.1.\n\n\n3. Responsibilities\n\n3.1. Distribution of Source Form\n\n All distribution of Covered Software in Source Code Form, including any\n Modifications that You create or to which You contribute, must be under the\n terms of this License. You must inform recipients that the Source Code Form\n of the Covered Software is governed by the terms of this License, and how\n they can obtain a copy of this License. You may not attempt to alter or\n restrict the recipients’ rights in the Source Code Form.\n\n3.2. Distribution of Executable Form\n\n If You distribute Covered Software in Executable Form then:\n\n a. such Covered Software must also be made available in Source Code Form,\n as described in Section 3.1, and You must inform recipients of the\n Executable Form how they can obtain a copy of such Source Code Form by\n reasonable means in a timely manner, at a charge no more than the cost\n of distribution to the recipient; and\n\n b. You may distribute such Executable Form under the terms of this License,\n or sublicense it under different terms, provided that the license for\n the Executable Form does not attempt to limit or alter the recipients’\n rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\n You may create and distribute a Larger Work under terms of Your choice,\n provided that You also comply with the requirements of this License for the\n Covered Software. If the Larger Work is a combination of Covered Software\n with a work governed by one or more Secondary Licenses, and the Covered\n Software is not Incompatible With Secondary Licenses, this License permits\n You to additionally distribute such Covered Software under the terms of\n such Secondary License(s), so that the recipient of the Larger Work may, at\n their option, further distribute the Covered Software under the terms of\n either this License or such Secondary License(s).\n\n3.4. Notices\n\n You may not remove or alter the substance of any license notices (including\n copyright notices, patent notices, disclaimers of warranty, or limitations\n of liability) contained within the Source Code Form of the Covered\n Software, except that You may alter any license notices to the extent\n required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\n You may choose to offer, and to charge a fee for, warranty, support,\n indemnity or liability obligations to one or more recipients of Covered\n Software. However, You may do so only on Your own behalf, and not on behalf\n of any Contributor. You must make it absolutely clear that any such\n warranty, support, indemnity, or liability obligation is offered by You\n alone, and You hereby agree to indemnify every Contributor for any\n liability incurred by such Contributor as a result of warranty, support,\n indemnity or liability terms You offer. You may include additional\n disclaimers of warranty and limitations of liability specific to any\n jurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n\n If it is impossible for You to comply with any of the terms of this License\n with respect to some or all of the Covered Software due to statute, judicial\n order, or regulation then You must: (a) comply with the terms of this License\n to the maximum extent possible; and (b) describe the limitations and the code\n they affect. Such description must be placed in a text file included with all\n distributions of the Covered Software under this License. Except to the\n extent prohibited by statute or regulation, such description must be\n sufficiently detailed for a recipient of ordinary skill to be able to\n understand it.\n\n5. Termination\n\n5.1. The rights granted under this License will terminate automatically if You\n fail to comply with any of its terms. However, if You become compliant,\n then the rights granted under this License from a particular Contributor\n are reinstated (a) provisionally, unless and until such Contributor\n explicitly and finally terminates Your grants, and (b) on an ongoing basis,\n if such Contributor fails to notify You of the non-compliance by some\n reasonable means prior to 60 days after You have come back into compliance.\n Moreover, Your grants from a particular Contributor are reinstated on an\n ongoing basis if such Contributor notifies You of the non-compliance by\n some reasonable means, this is the first time You have received notice of\n non-compliance with this License from such Contributor, and You become\n compliant prior to 30 days after Your receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\n infringement claim (excluding declaratory judgment actions, counter-claims,\n and cross-claims) alleging that a Contributor Version directly or\n indirectly infringes any patent, then the rights granted to You by any and\n all Contributors for the Covered Software under Section 2.1 of this License\n shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user\n license agreements (excluding distributors and resellers) which have been\n validly granted by You or Your distributors under this License prior to\n termination shall survive termination.\n\n6. Disclaimer of Warranty\n\n Covered Software is provided under this License on an “as is” basis, without\n warranty of any kind, either expressed, implied, or statutory, including,\n without limitation, warranties that the Covered Software is free of defects,\n merchantable, fit for a particular purpose or non-infringing. The entire\n risk as to the quality and performance of the Covered Software is with You.\n Should any Covered Software prove defective in any respect, You (not any\n Contributor) assume the cost of any necessary servicing, repair, or\n correction. This disclaimer of warranty constitutes an essential part of this\n License. No use of any Covered Software is authorized under this License\n except under this disclaimer.\n\n7. Limitation of Liability\n\n Under no circumstances and under no legal theory, whether tort (including\n negligence), contract, or otherwise, shall any Contributor, or anyone who\n distributes Covered Software as permitted above, be liable to You for any\n direct, indirect, special, incidental, or consequential damages of any\n character including, without limitation, damages for lost profits, loss of\n goodwill, work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses, even if such party shall have been\n informed of the possibility of such damages. This limitation of liability\n shall not apply to liability for death or personal injury resulting from such\n party’s negligence to the extent applicable law prohibits such limitation.\n Some jurisdictions do not allow the exclusion or limitation of incidental or\n consequential damages, so this exclusion and limitation may not apply to You.\n\n8. Litigation\n\n Any litigation relating to this License may be brought only in the courts of\n a jurisdiction where the defendant maintains its principal place of business\n and such litigation shall be governed by laws of that jurisdiction, without\n reference to its conflict-of-law provisions. Nothing in this Section shall\n prevent a party’s ability to bring cross-claims or counter-claims.\n\n9. Miscellaneous\n\n This License represents the complete agreement concerning the subject matter\n hereof. If any provision of this License is held to be unenforceable, such\n provision shall be reformed only to the extent necessary to make it\n enforceable. Any law or regulation which provides that the language of a\n contract shall be construed against the drafter shall not be used to construe\n this License against a Contributor.\n\n\n10. Versions of the License\n\n10.1. New Versions\n\n Mozilla Foundation is the license steward. Except as provided in Section\n 10.3, no one other than the license steward has the right to modify or\n publish new versions of this License. Each version will be given a\n distinguishing version number.\n\n10.2. Effect of New Versions\n\n You may distribute the Covered Software under the terms of the version of\n the License under which You originally received the Covered Software, or\n under the terms of any subsequent version published by the license\n steward.\n\n10.3. Modified Versions\n\n If you create software not governed by this License, and you want to\n create a new license for such software, you may create and use a modified\n version of this License if you rename the license and remove any\n references to the name of the license steward (except to note that such\n modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses\n If You choose to distribute Source Code Form that is Incompatible With\n Secondary Licenses under the terms of this version of the License, the\n notice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n\n This Source Code Form is subject to the\n terms of the Mozilla Public License, v.\n 2.0. If a copy of the MPL was not\n distributed with this file, You can\n obtain one at\n http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular file, then\nYou may include the notice in a location (such as a LICENSE file in a relevant\ndirectory) where a recipient would be likely to look for such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - “Incompatible With Secondary Licenses” Notice\n\n This Source Code Form is “Incompatible\n With Secondary Licenses”, as defined by\n the Mozilla Public License, v. 2.0.\n\n" + "licenseText": "Copyright IBM Corp. 2014, 2025\n\nMozilla Public License, version 2.0\n\n1. Definitions\n\n1.1. “Contributor”\n\n means each individual or legal entity that creates, contributes to the\n creation of, or owns Covered Software.\n\n1.2. “Contributor Version”\n\n means the combination of the Contributions of others (if any) used by a\n Contributor and that particular Contributor’s Contribution.\n\n1.3. “Contribution”\n\n means Covered Software of a particular Contributor.\n\n1.4. “Covered Software”\n\n means Source Code Form to which the initial Contributor has attached the\n notice in Exhibit A, the Executable Form of such Source Code Form, and\n Modifications of such Source Code Form, in each case including portions\n thereof.\n\n1.5. “Incompatible With Secondary Licenses”\n means\n\n a. that the initial Contributor has attached the notice described in\n Exhibit B to the Covered Software; or\n\n b. that the Covered Software was made available under the terms of version\n 1.1 or earlier of the License, but not also under the terms of a\n Secondary License.\n\n1.6. “Executable Form”\n\n means any form of the work other than Source Code Form.\n\n1.7. “Larger Work”\n\n means a work that combines Covered Software with other material, in a separate\n file or files, that is not Covered Software.\n\n1.8. “License”\n\n means this document.\n\n1.9. “Licensable”\n\n means having the right to grant, to the maximum extent possible, whether at the\n time of the initial grant or subsequently, any and all of the rights conveyed by\n this License.\n\n1.10. “Modifications”\n\n means any of the following:\n\n a. any file in Source Code Form that results from an addition to, deletion\n from, or modification of the contents of Covered Software; or\n\n b. any new file in Source Code Form that contains any Covered Software.\n\n1.11. “Patent Claims” of a Contributor\n\n means any patent claim(s), including without limitation, method, process,\n and apparatus claims, in any patent Licensable by such Contributor that\n would be infringed, but for the grant of the License, by the making,\n using, selling, offering for sale, having made, import, or transfer of\n either its Contributions or its Contributor Version.\n\n1.12. “Secondary License”\n\n means either the GNU General Public License, Version 2.0, the GNU Lesser\n General Public License, Version 2.1, the GNU Affero General Public\n License, Version 3.0, or any later versions of those licenses.\n\n1.13. “Source Code Form”\n\n means the form of the work preferred for making modifications.\n\n1.14. “You” (or “Your”)\n\n means an individual or a legal entity exercising rights under this\n License. For legal entities, “You” includes any entity that controls, is\n controlled by, or is under common control with You. For purposes of this\n definition, “control” means (a) the power, direct or indirect, to cause\n the direction or management of such entity, whether by contract or\n otherwise, or (b) ownership of more than fifty percent (50%) of the\n outstanding shares or beneficial ownership of such entity.\n\n\n2. License Grants and Conditions\n\n2.1. Grants\n\n Each Contributor hereby grants You a world-wide, royalty-free,\n non-exclusive license:\n\n a. under intellectual property rights (other than patent or trademark)\n Licensable by such Contributor to use, reproduce, make available,\n modify, display, perform, distribute, and otherwise exploit its\n Contributions, either on an unmodified basis, with Modifications, or as\n part of a Larger Work; and\n\n b. under Patent Claims of such Contributor to make, use, sell, offer for\n sale, have made, import, and otherwise transfer either its Contributions\n or its Contributor Version.\n\n2.2. Effective Date\n\n The licenses granted in Section 2.1 with respect to any Contribution become\n effective for each Contribution on the date the Contributor first distributes\n such Contribution.\n\n2.3. Limitations on Grant Scope\n\n The licenses granted in this Section 2 are the only rights granted under this\n License. No additional rights or licenses will be implied from the distribution\n or licensing of Covered Software under this License. Notwithstanding Section\n 2.1(b) above, no patent license is granted by a Contributor:\n\n a. for any code that a Contributor has removed from Covered Software; or\n\n b. for infringements caused by: (i) Your and any other third party’s\n modifications of Covered Software, or (ii) the combination of its\n Contributions with other software (except as part of its Contributor\n Version); or\n\n c. under Patent Claims infringed by Covered Software in the absence of its\n Contributions.\n\n This License does not grant any rights in the trademarks, service marks, or\n logos of any Contributor (except as may be necessary to comply with the\n notice requirements in Section 3.4).\n\n2.4. Subsequent Licenses\n\n No Contributor makes additional grants as a result of Your choice to\n distribute the Covered Software under a subsequent version of this License\n (see Section 10.2) or under the terms of a Secondary License (if permitted\n under the terms of Section 3.3).\n\n2.5. Representation\n\n Each Contributor represents that the Contributor believes its Contributions\n are its original creation(s) or it has sufficient rights to grant the\n rights to its Contributions conveyed by this License.\n\n2.6. Fair Use\n\n This License is not intended to limit any rights You have under applicable\n copyright doctrines of fair use, fair dealing, or other equivalents.\n\n2.7. Conditions\n\n Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in\n Section 2.1.\n\n\n3. Responsibilities\n\n3.1. Distribution of Source Form\n\n All distribution of Covered Software in Source Code Form, including any\n Modifications that You create or to which You contribute, must be under the\n terms of this License. You must inform recipients that the Source Code Form\n of the Covered Software is governed by the terms of this License, and how\n they can obtain a copy of this License. You may not attempt to alter or\n restrict the recipients’ rights in the Source Code Form.\n\n3.2. Distribution of Executable Form\n\n If You distribute Covered Software in Executable Form then:\n\n a. such Covered Software must also be made available in Source Code Form,\n as described in Section 3.1, and You must inform recipients of the\n Executable Form how they can obtain a copy of such Source Code Form by\n reasonable means in a timely manner, at a charge no more than the cost\n of distribution to the recipient; and\n\n b. You may distribute such Executable Form under the terms of this License,\n or sublicense it under different terms, provided that the license for\n the Executable Form does not attempt to limit or alter the recipients’\n rights in the Source Code Form under this License.\n\n3.3. Distribution of a Larger Work\n\n You may create and distribute a Larger Work under terms of Your choice,\n provided that You also comply with the requirements of this License for the\n Covered Software. If the Larger Work is a combination of Covered Software\n with a work governed by one or more Secondary Licenses, and the Covered\n Software is not Incompatible With Secondary Licenses, this License permits\n You to additionally distribute such Covered Software under the terms of\n such Secondary License(s), so that the recipient of the Larger Work may, at\n their option, further distribute the Covered Software under the terms of\n either this License or such Secondary License(s).\n\n3.4. Notices\n\n You may not remove or alter the substance of any license notices (including\n copyright notices, patent notices, disclaimers of warranty, or limitations\n of liability) contained within the Source Code Form of the Covered\n Software, except that You may alter any license notices to the extent\n required to remedy known factual inaccuracies.\n\n3.5. Application of Additional Terms\n\n You may choose to offer, and to charge a fee for, warranty, support,\n indemnity or liability obligations to one or more recipients of Covered\n Software. However, You may do so only on Your own behalf, and not on behalf\n of any Contributor. You must make it absolutely clear that any such\n warranty, support, indemnity, or liability obligation is offered by You\n alone, and You hereby agree to indemnify every Contributor for any\n liability incurred by such Contributor as a result of warranty, support,\n indemnity or liability terms You offer. You may include additional\n disclaimers of warranty and limitations of liability specific to any\n jurisdiction.\n\n4. Inability to Comply Due to Statute or Regulation\n\n If it is impossible for You to comply with any of the terms of this License\n with respect to some or all of the Covered Software due to statute, judicial\n order, or regulation then You must: (a) comply with the terms of this License\n to the maximum extent possible; and (b) describe the limitations and the code\n they affect. Such description must be placed in a text file included with all\n distributions of the Covered Software under this License. Except to the\n extent prohibited by statute or regulation, such description must be\n sufficiently detailed for a recipient of ordinary skill to be able to\n understand it.\n\n5. Termination\n\n5.1. The rights granted under this License will terminate automatically if You\n fail to comply with any of its terms. However, if You become compliant,\n then the rights granted under this License from a particular Contributor\n are reinstated (a) provisionally, unless and until such Contributor\n explicitly and finally terminates Your grants, and (b) on an ongoing basis,\n if such Contributor fails to notify You of the non-compliance by some\n reasonable means prior to 60 days after You have come back into compliance.\n Moreover, Your grants from a particular Contributor are reinstated on an\n ongoing basis if such Contributor notifies You of the non-compliance by\n some reasonable means, this is the first time You have received notice of\n non-compliance with this License from such Contributor, and You become\n compliant prior to 30 days after Your receipt of the notice.\n\n5.2. If You initiate litigation against any entity by asserting a patent\n infringement claim (excluding declaratory judgment actions, counter-claims,\n and cross-claims) alleging that a Contributor Version directly or\n indirectly infringes any patent, then the rights granted to You by any and\n all Contributors for the Covered Software under Section 2.1 of this License\n shall terminate.\n\n5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user\n license agreements (excluding distributors and resellers) which have been\n validly granted by You or Your distributors under this License prior to\n termination shall survive termination.\n\n6. Disclaimer of Warranty\n\n Covered Software is provided under this License on an “as is” basis, without\n warranty of any kind, either expressed, implied, or statutory, including,\n without limitation, warranties that the Covered Software is free of defects,\n merchantable, fit for a particular purpose or non-infringing. The entire\n risk as to the quality and performance of the Covered Software is with You.\n Should any Covered Software prove defective in any respect, You (not any\n Contributor) assume the cost of any necessary servicing, repair, or\n correction. This disclaimer of warranty constitutes an essential part of this\n License. No use of any Covered Software is authorized under this License\n except under this disclaimer.\n\n7. Limitation of Liability\n\n Under no circumstances and under no legal theory, whether tort (including\n negligence), contract, or otherwise, shall any Contributor, or anyone who\n distributes Covered Software as permitted above, be liable to You for any\n direct, indirect, special, incidental, or consequential damages of any\n character including, without limitation, damages for lost profits, loss of\n goodwill, work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses, even if such party shall have been\n informed of the possibility of such damages. This limitation of liability\n shall not apply to liability for death or personal injury resulting from such\n party’s negligence to the extent applicable law prohibits such limitation.\n Some jurisdictions do not allow the exclusion or limitation of incidental or\n consequential damages, so this exclusion and limitation may not apply to You.\n\n8. Litigation\n\n Any litigation relating to this License may be brought only in the courts of\n a jurisdiction where the defendant maintains its principal place of business\n and such litigation shall be governed by laws of that jurisdiction, without\n reference to its conflict-of-law provisions. Nothing in this Section shall\n prevent a party’s ability to bring cross-claims or counter-claims.\n\n9. Miscellaneous\n\n This License represents the complete agreement concerning the subject matter\n hereof. If any provision of this License is held to be unenforceable, such\n provision shall be reformed only to the extent necessary to make it\n enforceable. Any law or regulation which provides that the language of a\n contract shall be construed against the drafter shall not be used to construe\n this License against a Contributor.\n\n\n10. Versions of the License\n\n10.1. New Versions\n\n Mozilla Foundation is the license steward. Except as provided in Section\n 10.3, no one other than the license steward has the right to modify or\n publish new versions of this License. Each version will be given a\n distinguishing version number.\n\n10.2. Effect of New Versions\n\n You may distribute the Covered Software under the terms of the version of\n the License under which You originally received the Covered Software, or\n under the terms of any subsequent version published by the license\n steward.\n\n10.3. Modified Versions\n\n If you create software not governed by this License, and you want to\n create a new license for such software, you may create and use a modified\n version of this License if you rename the license and remove any\n references to the name of the license steward (except to note that such\n modified license differs from this License).\n\n10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses\n If You choose to distribute Source Code Form that is Incompatible With\n Secondary Licenses under the terms of this version of the License, the\n notice described in Exhibit B of this License must be attached.\n\nExhibit A - Source Code Form License Notice\n\n This Source Code Form is subject to the\n terms of the Mozilla Public License, v.\n 2.0. If a copy of the MPL was not\n distributed with this file, You can\n obtain one at\n http://mozilla.org/MPL/2.0/.\n\nIf it is not possible or desirable to put the notice in a particular file, then\nYou may include the notice in a location (such as a LICENSE file in a relevant\ndirectory) where a recipient would be likely to look for such a notice.\n\nYou may add additional accurate notices of copyright ownership.\n\nExhibit B - “Incompatible With Secondary Licenses” Notice\n\n This Source Code Form is “Incompatible\n With Secondary Licenses”, as defined by\n the Mozilla Public License, v. 2.0.\n\n" }, { "name": "github.com/hashicorp/golang-lru/v2", @@ -789,6 +779,11 @@ "path": "github.com/klauspost/cpuid/v2/LICENSE", "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Klaus Post\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n" }, + { + "name": "github.com/klauspost/crc32", + "path": "github.com/klauspost/crc32/LICENSE", + "licenseText": "Copyright (c) 2012 The Go Authors. All rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google Inc. nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" + }, { "name": "github.com/klauspost/pgzip", "path": "github.com/klauspost/pgzip/LICENSE", @@ -796,8 +791,8 @@ }, { "name": "github.com/lib/pq", - "path": "github.com/lib/pq/LICENSE.md", - "licenseText": "Copyright (c) 2011-2013, 'pq' Contributors\nPortions Copyright (C) 2011 Blake Mizerany\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" + "path": "github.com/lib/pq/LICENSE", + "licenseText": "MIT License\n\nCopyright (c) 2011-2013, 'pq' Contributors. Portions Copyright (c) 2011 Blake Mizerany\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" }, { "name": "github.com/libdns/libdns", @@ -1074,6 +1069,11 @@ "path": "github.com/sergi/go-diff/diffmatchpatch/LICENSE", "licenseText": "Copyright (c) 2012-2016 The go-diff Authors. All rights reserved.\n\nPermission is hereby granted, free of charge, to any person obtaining a\ncopy of this software and associated documentation files (the \"Software\"),\nto deal in the Software without restriction, including without limitation\nthe rights to use, copy, modify, merge, publish, distribute, sublicense,\nand/or sell copies of the Software, and to permit persons to whom the\nSoftware is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included\nin all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS\nOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\nFROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\nDEALINGS IN THE SOFTWARE.\n\n" }, + { + "name": "github.com/shopspring/decimal", + "path": "github.com/shopspring/decimal/LICENSE", + "licenseText": "The MIT License (MIT)\n\nCopyright (c) 2015 Spring, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\n- Based on https://github.com/oguzbilgic/fpd, which has the following license:\n\"\"\"\nThe MIT License (MIT)\n\nCopyright (c) 2013 Oguz Bilgic\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\"\"\"\n" + }, { "name": "github.com/sirupsen/logrus", "path": "github.com/sirupsen/logrus/LICENSE", @@ -1212,13 +1212,23 @@ { "name": "go.uber.org/zap", "path": "go.uber.org/zap/LICENSE", - "licenseText": "Copyright (c) 2016-2017 Uber Technologies, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" + "licenseText": "Copyright (c) 2016-2024 Uber Technologies, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" }, { "name": "go.uber.org/zap/exp/zapslog", "path": "go.uber.org/zap/exp/zapslog/LICENSE", "licenseText": "Copyright (c) 2016-2024 Uber Technologies, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" }, + { + "name": "go.yaml.in/yaml/v2", + "path": "go.yaml.in/yaml/v2/LICENSE", + "licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"{}\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright {yyyy} {name of copyright owner}\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" + }, + { + "name": "go.yaml.in/yaml/v3", + "path": "go.yaml.in/yaml/v3/LICENSE", + "licenseText": "\nThis project is covered by two different licenses: MIT and Apache.\n\n#### MIT License ####\n\nThe following files were ported to Go from C files of libyaml, and thus\nare still covered by their original MIT license, with the additional\ncopyright staring in 2011 when the project was ported over:\n\n apic.go emitterc.go parserc.go readerc.go scannerc.go\n writerc.go yamlh.go yamlprivateh.go\n\nCopyright (c) 2006-2010 Kirill Simonov\nCopyright (c) 2006-2011 Kirill Simonov\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n### Apache License ###\n\nAll the remaining project files are covered by the Apache license:\n\nCopyright (c) 2011-2019 Canonical Ltd\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n" + }, { "name": "go4.org/readerutil", "path": "go4.org/readerutil/LICENSE", diff --git a/cmd/admin.go b/cmd/admin.go index a01274b90e..dbd48e5727 100644 --- a/cmd/admin.go +++ b/cmd/admin.go @@ -134,7 +134,7 @@ func runRepoSyncReleases(ctx context.Context, _ *cli.Command) error { } log.Trace(" currentNumReleases is %d, running SyncReleasesWithTags", oldnum) - if err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil { + if _, err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil { log.Warn(" SyncReleasesWithTags: %v", err) gitRepo.Close() continue diff --git a/eslint.config.ts b/eslint.config.ts index 8d5b435397..5815702c89 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -342,7 +342,7 @@ export default defineConfig([ 'import-x/first': [2], 'import-x/group-exports': [0], 'import-x/max-dependencies': [0], - 'import-x/named': [2], + 'import-x/named': [0], 'import-x/namespace': [0], 'import-x/newline-after-import': [0], 'import-x/no-absolute-path': [0], @@ -987,7 +987,7 @@ export default defineConfig([ 'vitest/require-to-throw-message': [0], 'vitest/require-top-level-describe': [0], 'vitest/valid-describe-callback': [2], - 'vitest/valid-expect': [2], + 'vitest/valid-expect': [2, {maxArgs: 2}], 'vitest/valid-title': [2], }, }, diff --git a/go.mod b/go.mod index 26a2b818ef..f784ac2581 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module code.gitea.io/gitea -go 1.25.0 - -toolchain go1.25.6 +go 1.26.0 // rfc5280 said: "The serial number is an integer assigned by the CA to each certificate." // But some CAs use negative serial number, just relax the check. related: @@ -11,9 +9,9 @@ godebug x509negativeserial=1 require ( code.gitea.io/actions-proto-go v0.4.1 - code.gitea.io/sdk/gitea v0.22.0 + code.gitea.io/sdk/gitea v0.23.2 codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 - connectrpc.com/connect v1.18.1 + connectrpc.com/connect v1.19.1 gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed gitea.com/go-chi/cache v0.2.1 gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098 @@ -26,22 +24,22 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 github.com/ProtonMail/go-crypto v1.3.0 - github.com/PuerkitoBio/goquery v1.10.3 + github.com/PuerkitoBio/goquery v1.11.0 github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.8.0 github.com/alecthomas/chroma/v2 v2.23.1 - github.com/aws/aws-sdk-go-v2/credentials v1.18.10 - github.com/aws/aws-sdk-go-v2/service/codecommit v1.32.2 + github.com/aws/aws-sdk-go-v2/credentials v1.19.7 + github.com/aws/aws-sdk-go-v2/service/codecommit v1.33.8 github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb - github.com/blevesearch/bleve/v2 v2.5.3 + github.com/blevesearch/bleve/v2 v2.5.7 github.com/bohde/codel v0.2.0 github.com/buildkite/terminal-to-html/v3 v3.16.8 - github.com/caddyserver/certmagic v0.24.0 + github.com/caddyserver/certmagic v0.25.1 github.com/charmbracelet/git-lfs-transfer v0.1.1-0.20251013092601-6327009efd21 github.com/chi-middleware/proxy v1.1.1 github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21 github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 github.com/dustin/go-humanize v1.0.1 - github.com/editorconfig/editorconfig-core-go/v2 v2.6.3 + github.com/editorconfig/editorconfig-core-go/v2 v2.6.4 github.com/emersion/go-imap v1.2.1 github.com/emirpasic/gods v1.18.1 github.com/ethantkoenig/rupture v1.0.1 @@ -50,43 +48,43 @@ require ( github.com/gliderlabs/ssh v0.3.8 github.com/go-ap/activitypub v0.0.0-20250810115208-cb73b20a1742 github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 - github.com/go-chi/chi/v5 v5.2.4 + github.com/go-chi/chi/v5 v5.2.5 github.com/go-chi/cors v1.2.2 github.com/go-co-op/gocron v1.37.0 github.com/go-enry/go-enry/v2 v2.9.4 - github.com/go-git/go-billy/v5 v5.6.2 - github.com/go-git/go-git/v5 v5.16.3 - github.com/go-ldap/ldap/v3 v3.4.11 - github.com/go-redsync/redsync/v4 v4.13.0 + github.com/go-git/go-billy/v5 v5.7.0 + github.com/go-git/go-git/v5 v5.16.4 + github.com/go-ldap/ldap/v3 v3.4.12 + github.com/go-redsync/redsync/v4 v4.15.0 github.com/go-sql-driver/mysql v1.9.3 github.com/go-webauthn/webauthn v0.13.4 github.com/goccy/go-json v0.10.5 github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 - github.com/golang-jwt/jwt/v5 v5.3.0 + github.com/golang-jwt/jwt/v5 v5.3.1 github.com/google/go-github/v74 v74.0.0 github.com/google/licenseclassifier/v2 v2.0.0 - github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 + github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef github.com/google/uuid v1.6.0 github.com/gorilla/feeds v1.2.0 github.com/gorilla/sessions v1.4.0 - github.com/hashicorp/go-version v1.7.0 + github.com/hashicorp/go-version v1.8.0 github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/huandu/xstrings v1.5.0 github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 github.com/jhillyerd/enmime v1.3.0 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 - github.com/klauspost/compress v1.18.0 + github.com/klauspost/compress v1.18.3 github.com/klauspost/cpuid/v2 v2.3.0 - github.com/lib/pq v1.10.9 + github.com/lib/pq v1.11.1 github.com/markbates/goth v1.82.0 github.com/mattn/go-isatty v0.0.20 - github.com/mattn/go-sqlite3 v1.14.32 - github.com/meilisearch/meilisearch-go v0.33.2 - github.com/mholt/archives v0.0.0-20251009205813-e30ac6010726 + github.com/mattn/go-sqlite3 v1.14.33 + github.com/meilisearch/meilisearch-go v0.36.0 + github.com/mholt/archives v0.1.5 github.com/microcosm-cc/bluemonday v1.0.27 - github.com/microsoft/go-mssqldb v1.9.3 - github.com/minio/minio-go/v7 v7.0.95 + github.com/microsoft/go-mssqldb v1.9.6 + github.com/minio/minio-go/v7 v7.0.98 github.com/msteinert/pam v1.2.0 github.com/nektos/act v0.2.63 github.com/niklasfasching/go-org v1.9.1 @@ -94,9 +92,9 @@ require ( github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.1 github.com/pquerna/otp v1.5.0 - github.com/prometheus/client_golang v1.23.0 + github.com/prometheus/client_golang v1.23.2 github.com/quasoft/websspi v1.1.2 - github.com/redis/go-redis/v9 v9.12.1 + github.com/redis/go-redis/v9 v9.17.3 github.com/robfig/cron/v3 v3.0.1 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/sassoftware/go-rpmutils v0.4.0 @@ -114,25 +112,25 @@ require ( github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc github.com/yuin/goldmark-meta v1.1.0 gitlab.com/gitlab-org/api/client-go v0.142.4 - golang.org/x/crypto v0.45.0 - golang.org/x/image v0.30.0 - golang.org/x/net v0.47.0 - golang.org/x/oauth2 v0.30.0 - golang.org/x/sync v0.18.0 - golang.org/x/sys v0.38.0 - golang.org/x/text v0.31.0 - google.golang.org/grpc v1.75.0 - google.golang.org/protobuf v1.36.8 - gopkg.in/ini.v1 v1.67.0 + golang.org/x/crypto v0.47.0 + golang.org/x/image v0.35.0 + golang.org/x/net v0.49.0 + golang.org/x/oauth2 v0.34.0 + golang.org/x/sync v0.19.0 + golang.org/x/sys v0.40.0 + golang.org/x/text v0.33.0 + google.golang.org/grpc v1.78.0 + google.golang.org/protobuf v1.36.11 + gopkg.in/ini.v1 v1.67.1 gopkg.in/yaml.v3 v3.0.1 mvdan.cc/xurls/v2 v2.6.0 strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 xorm.io/builder v0.3.13 - xorm.io/xorm v1.3.10 + xorm.io/xorm v1.3.11 ) require ( - cloud.google.com/go/compute/metadata v0.8.0 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect code.gitea.io/gitea-vet v0.2.3 // indirect dario.cat/mergo v1.0.2 // indirect filippo.io/edwards25519 v1.1.0 // indirect @@ -145,20 +143,20 @@ require ( github.com/andybalholm/brotli v1.2.0 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect - github.com/aws/aws-sdk-go-v2 v1.38.3 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.6 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.6 // indirect - github.com/aws/smithy-go v1.23.0 // indirect + github.com/aws/aws-sdk-go-v2 v1.41.1 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 // indirect + github.com/aws/smithy-go v1.24.0 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.24.0 // indirect - github.com/blevesearch/bleve_index_api v1.2.9 // indirect + github.com/blevesearch/bleve_index_api v1.2.11 // indirect github.com/blevesearch/geo v0.2.4 // indirect - github.com/blevesearch/go-faiss v1.0.25 // indirect + github.com/blevesearch/go-faiss v1.0.26 // indirect github.com/blevesearch/go-porterstemmer v1.0.3 // indirect github.com/blevesearch/gtreap v0.1.1 // indirect github.com/blevesearch/mmap-go v1.0.4 // indirect - github.com/blevesearch/scorch_segment_api/v2 v2.3.11 // indirect + github.com/blevesearch/scorch_segment_api/v2 v2.3.13 // indirect github.com/blevesearch/segment v0.9.1 // indirect github.com/blevesearch/snowballstem v0.9.0 // indirect github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect @@ -168,14 +166,14 @@ require ( github.com/blevesearch/zapx/v13 v13.4.2 // indirect github.com/blevesearch/zapx/v14 v14.4.2 // indirect github.com/blevesearch/zapx/v15 v15.4.2 // indirect - github.com/blevesearch/zapx/v16 v16.2.4 // indirect + github.com/blevesearch/zapx/v16 v16.2.8 // indirect github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect github.com/bodgit/plumbing v1.3.0 // indirect github.com/bodgit/sevenzip v1.6.1 // indirect github.com/bodgit/windows v1.0.1 // indirect github.com/boombuler/barcode v1.1.0 // indirect github.com/bradfitz/gomemcache v0.0.0-20250403215159-8d39553ac7cf // indirect - github.com/caddyserver/zerossl v0.1.3 // indirect + github.com/caddyserver/zerossl v0.1.4 // indirect github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudflare/circl v1.6.1 // indirect @@ -199,11 +197,9 @@ require ( github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-webauthn/x v0.1.24 // indirect - github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect - github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v1.0.0 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/flatbuffers v25.2.10+incompatible // indirect @@ -220,6 +216,7 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v1.4.0 // indirect + github.com/klauspost/crc32 v1.3.0 // indirect github.com/klauspost/pgzip v1.2.6 // indirect github.com/libdns/libdns v1.1.1 // indirect github.com/mailru/easyjson v0.9.0 // indirect @@ -227,8 +224,8 @@ require ( github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mattn/go-shellwords v1.0.12 // indirect - github.com/mholt/acmez/v3 v3.1.2 // indirect - github.com/miekg/dns v1.1.68 // indirect + github.com/mholt/acmez/v3 v3.1.4 // indirect + github.com/miekg/dns v1.1.69 // indirect github.com/mikelolasagasti/xz v1.0.1 // indirect github.com/minio/crc64nvme v1.1.1 // indirect github.com/minio/md5-simd v1.1.2 // indirect @@ -251,18 +248,19 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/common v0.66.1 // indirect github.com/prometheus/procfs v0.17.0 // indirect github.com/rhysd/actionlint v1.7.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rs/xid v1.6.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/shopspring/decimal v1.4.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/skeema/knownhosts v1.3.1 // indirect github.com/sorairolake/lzip-go v0.3.8 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect - github.com/tinylib/msgp v1.4.0 // indirect + github.com/tinylib/msgp v1.6.1 // indirect github.com/unknwon/com v1.0.1 // indirect github.com/valyala/fastjson v1.6.4 // indirect github.com/x448/float16 v0.8.4 // indirect @@ -275,14 +273,16 @@ require ( go.etcd.io/bbolt v1.4.3 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect + go.uber.org/zap v1.27.1 // indirect go.uber.org/zap/exp v0.3.0 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect - golang.org/x/mod v0.29.0 // indirect + golang.org/x/mod v0.31.0 // indirect golang.org/x/time v0.12.0 // indirect - golang.org/x/tools v0.38.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 // indirect + golang.org/x/tools v0.40.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) @@ -294,8 +294,6 @@ ignore ( replace github.com/jaytaylor/html2text => github.com/Necoro/html2text v0.0.0-20250804200300-7bf1ce1c7347 -replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 - replace github.com/nektos/act => gitea.com/gitea/act v0.261.7-0.20251003180512-ac6e4b751763 replace git.sr.ht/~mariusor/go-xsd-duration => gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078 diff --git a/go.sum b/go.sum index 7490b3923c..b10e259c91 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,8 @@ cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6T cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA= -cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= @@ -20,12 +20,12 @@ code.gitea.io/actions-proto-go v0.4.1 h1:l0EYhjsgpUe/1VABo2eK7zcoNX2W44WOnb0MSLr code.gitea.io/actions-proto-go v0.4.1/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas= code.gitea.io/gitea-vet v0.2.3 h1:gdFmm6WOTM65rE8FUBTRzeQZYzXePKSSB1+r574hWwI= code.gitea.io/gitea-vet v0.2.3/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE= -code.gitea.io/sdk/gitea v0.22.0 h1:HCKq7bX/HQ85Nw7c/HAhWgRye+vBp5nQOE8Md1+9Ef0= -code.gitea.io/sdk/gitea v0.22.0/go.mod h1:yyF5+GhljqvA30sRDreoyHILruNiy4ASufugzYg0VHM= +code.gitea.io/sdk/gitea v0.23.2 h1:iJB1FDmLegwfwjX8gotBDHdPSbk/ZR8V9VmEJaVsJYg= +code.gitea.io/sdk/gitea v0.23.2/go.mod h1:yyF5+GhljqvA30sRDreoyHILruNiy4ASufugzYg0VHM= codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 h1:TXbikPqa7YRtfU9vS6QJBg77pUvbEb6StRdZO8t1bEY= codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570/go.mod h1:IIAjsijsd8q1isWX8MACefDEgTQslQ4stk2AeeTt3kM= -connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= -connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= +connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14= +connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -53,8 +53,6 @@ github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs= github.com/42wim/httpsig v1.2.3/go.mod h1:nZq9OlYKDrUBhptd77IHx4/sZZD+IxTBADvAPI9G/EM= github.com/42wim/sshsig v0.0.0-20250502153856-5100632e8920 h1:mWAVGlovzUfREJBhm0GwJnDNu21yRrL9QH9NIzAU3rg= github.com/42wim/sshsig v0.0.0-20250502153856-5100632e8920/go.mod h1:zWxcT7BIWOe05xVJL0VMvO/PJ6RpoCux10heb77H6Q8= -github.com/6543/go-version v1.3.1 h1:HvOp+Telns7HWJ2Xo/05YXQSB2bE0WmVgbHqwMPZT4U= -github.com/6543/go-version v1.3.1/go.mod h1:oqFAHCwtLVUTLdhQmVZWYvaHXTdsbB4SY85at64SQEo= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.0 h1:ci6Yd6nysBRLEodoziB6ah1+YOzZbZk+NYneoA6q+6E= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.0/go.mod h1:QyVsSSN64v5TGltphKLQ2sQxe4OBQg0J1eKRcVBnfgE= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4= @@ -85,8 +83,8 @@ github.com/Necoro/html2text v0.0.0-20250804200300-7bf1ce1c7347 h1:3JhDl+JysaO8nh github.com/Necoro/html2text v0.0.0-20250804200300-7bf1ce1c7347/go.mod h1:2ErI0aycD43Ufr6CFK5lT/NrHGmoZuVbn1nlPThw69o= github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= -github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo= -github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y= +github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw= +github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ= github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= github.com/RoaringBitmap/roaring v0.7.1/go.mod h1:jdT9ykXwHFNdJbEtxePexlFYH9LXucApeS0/+/g+p1I= github.com/RoaringBitmap/roaring/v2 v2.10.0 h1:HbJ8Cs71lfCJyvmSptxeMX2PtvOC8yonlU0GQcy2Ak0= @@ -103,8 +101,8 @@ github.com/alecthomas/chroma/v2 v2.23.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9c github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs= github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= -github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI= -github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= +github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktpoUAgOJK3OTFc/xug0PCXYCqU0FgDKI= +github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM= @@ -114,18 +112,18 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuW github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/aws/aws-sdk-go-v2 v1.38.3 h1:B6cV4oxnMs45fql4yRH+/Po/YU+597zgWqvDpYMturk= -github.com/aws/aws-sdk-go-v2 v1.38.3/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY= -github.com/aws/aws-sdk-go-v2/credentials v1.18.10 h1:xdJnXCouCx8Y0NncgoptztUocIYLKeQxrCgN6x9sdhg= -github.com/aws/aws-sdk-go-v2/credentials v1.18.10/go.mod h1:7tQk08ntj914F/5i9jC4+2HQTAuJirq7m1vZVIhEkWs= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.6 h1:uF68eJA6+S9iVr9WgX1NaRGyQ/6MdIyc4JNUo6TN1FA= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.6/go.mod h1:qlPeVZCGPiobx8wb1ft0GHT5l+dc6ldnwInDFaMvC7Y= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.6 h1:pa1DEC6JoI0zduhZePp3zmhWvk/xxm4NB8Hy/Tlsgos= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.6/go.mod h1:gxEjPebnhWGJoaDdtDkA0JX46VRg1wcTHYe63OfX5pE= -github.com/aws/aws-sdk-go-v2/service/codecommit v1.32.2 h1:qIySgaSYDLcInLpY0e7HPCi+AVeD/LTsl9EL1b692oA= -github.com/aws/aws-sdk-go-v2/service/codecommit v1.32.2/go.mod h1:SobWM1535Mn1WuThoIVLiLa/C1rRbxbbq5PZW2QFCIM= -github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE= -github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= +github.com/aws/aws-sdk-go-v2 v1.41.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6cerhU= +github.com/aws/aws-sdk-go-v2 v1.41.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= +github.com/aws/aws-sdk-go-v2/credentials v1.19.7 h1:tHK47VqqtJxOymRrNtUXN5SP/zUTvZKeLx4tH6PGQc8= +github.com/aws/aws-sdk-go-v2/credentials v1.19.7/go.mod h1:qOZk8sPDrxhf+4Wf4oT2urYJrYt3RejHSzgAquYeppw= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 h1:xOLELNKGp2vsiteLsvLPwxC+mYmO6OZ8PYgiuPJzF8U= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17/go.mod h1:5M5CI3D12dNOtH3/mk6minaRwI2/37ifCURZISxA/IQ= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 h1:WWLqlh79iO48yLkj1v3ISRNiv+3KdQoZ6JWyfcsyQik= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17/go.mod h1:EhG22vHRrvF8oXSTYStZhJc1aUgKtnJe+aOiFEV90cM= +github.com/aws/aws-sdk-go-v2/service/codecommit v1.33.8 h1:KxKGfYvkVOe/U/Z4yAd0ZySRJHavuL31VOC+fn7WEAs= +github.com/aws/aws-sdk-go-v2/service/codecommit v1.33.8/go.mod h1:cznnFD3BzYY+NB+4WoQ7SxdTACOsMqGCbQ5QaByPz4w= +github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk= +github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -138,15 +136,15 @@ github.com/bits-and-blooms/bitset v1.24.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6 github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4= github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= github.com/blevesearch/bleve/v2 v2.0.5/go.mod h1:ZjWibgnbRX33c+vBRgla9QhPb4QOjD6fdVJ+R1Bk8LM= -github.com/blevesearch/bleve/v2 v2.5.3 h1:9l1xtKaETv64SZc1jc4Sy0N804laSa/LeMbYddq1YEM= -github.com/blevesearch/bleve/v2 v2.5.3/go.mod h1:Z/e8aWjiq8HeX+nW8qROSxiE0830yQA071dwR3yoMzw= +github.com/blevesearch/bleve/v2 v2.5.7 h1:2d9YrL5zrX5EBBW++GOaEKjE+NPWeZGaX77IM26m1Z8= +github.com/blevesearch/bleve/v2 v2.5.7/go.mod h1:yj0NlS7ocGC4VOSAedqDDMktdh2935v2CSWOCDMHdSA= github.com/blevesearch/bleve_index_api v1.0.0/go.mod h1:fiwKS0xLEm+gBRgv5mumf0dhgFr2mDgZah1pqv1c1M4= -github.com/blevesearch/bleve_index_api v1.2.9 h1:WqD3kvYwnlYLv8sTdH+AF7n/L4v969Cek68+wZnYj4Q= -github.com/blevesearch/bleve_index_api v1.2.9/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0= +github.com/blevesearch/bleve_index_api v1.2.11 h1:bXQ54kVuwP8hdrXUSOnvTQfgK0KI1+f9A0ITJT8tX1s= +github.com/blevesearch/bleve_index_api v1.2.11/go.mod h1:rKQDl4u51uwafZxFrPD1R7xFOwKnzZW7s/LSeK4lgo0= github.com/blevesearch/geo v0.2.4 h1:ECIGQhw+QALCZaDcogRTNSJYQXRtC8/m8IKiA706cqk= github.com/blevesearch/geo v0.2.4/go.mod h1:K56Q33AzXt2YExVHGObtmRSFYZKYGv0JEN5mdacJJR8= -github.com/blevesearch/go-faiss v1.0.25 h1:lel1rkOUGbT1CJ0YgzKwC7k+XH0XVBHnCVWahdCXk4U= -github.com/blevesearch/go-faiss v1.0.25/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk= +github.com/blevesearch/go-faiss v1.0.26 h1:4dRLolFgjPyjkaXwff4NfbZFdE/dfywbzDqporeQvXI= +github.com/blevesearch/go-faiss v1.0.26/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk= github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo= github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M= github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y= @@ -155,8 +153,8 @@ github.com/blevesearch/mmap-go v1.0.2/go.mod h1:ol2qBqYaOUsGdm7aRMRrYGgPvnwLe6Y+ github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc= github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs= github.com/blevesearch/scorch_segment_api/v2 v2.0.1/go.mod h1:lq7yK2jQy1yQjtjTfU931aVqz7pYxEudHaDwOt1tXfU= -github.com/blevesearch/scorch_segment_api/v2 v2.3.11 h1:bYuEgsyGqgU/gy0/Vk6g1eCUqGBs2r+3bRCv+Cnq2kc= -github.com/blevesearch/scorch_segment_api/v2 v2.3.11/go.mod h1:aAWoeQ3DdoZ3Z5138jXVSd1T/klGwvg11z0pSxrJSEk= +github.com/blevesearch/scorch_segment_api/v2 v2.3.13 h1:ZPjv/4VwWvHJZKeMSgScCapOy8+DdmsmRyLmSB88UoY= +github.com/blevesearch/scorch_segment_api/v2 v2.3.13/go.mod h1:ENk2LClTehOuMS8XzN3UxBEErYmtwkE7MAArFTXs9Vc= github.com/blevesearch/segment v0.9.0/go.mod h1:9PfHYUdQCgHktBgvtUOF4x+pc4/l8rdH0u5spnW85UQ= github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU= github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw= @@ -184,8 +182,8 @@ github.com/blevesearch/zapx/v14 v14.4.2/go.mod h1:rz0XNb/OZSMjNorufDGSpFpjoFKhXm github.com/blevesearch/zapx/v15 v15.2.0/go.mod h1:MmQceLpWfME4n1WrBFIwplhWmaQbQqLQARpaKUEOs/A= github.com/blevesearch/zapx/v15 v15.4.2 h1:sWxpDE0QQOTjyxYbAVjt3+0ieu8NCE0fDRaFxEsp31k= github.com/blevesearch/zapx/v15 v15.4.2/go.mod h1:1pssev/59FsuWcgSnTa0OeEpOzmhtmr/0/11H0Z8+Nw= -github.com/blevesearch/zapx/v16 v16.2.4 h1:tGgfvleXTAkwsD5mEzgM3zCS/7pgocTCnO1oyAUjlww= -github.com/blevesearch/zapx/v16 v16.2.4/go.mod h1:Rti/REtuuMmzwsI8/C/qIzRaEoSK/wiFYw5e5ctUKKs= +github.com/blevesearch/zapx/v16 v16.2.8 h1:SlnzF0YGtSlrsOE3oE7EgEX6BIepGpeqxs1IjMbHLQI= +github.com/blevesearch/zapx/v16 v16.2.8/go.mod h1:murSoCJPCk25MqURrcJaBQ1RekuqSCSfMjXH4rHyA14= github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE= github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q= @@ -208,10 +206,10 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/buildkite/terminal-to-html/v3 v3.16.8 h1:QN/daUob6cmK8GcdKnwn9+YTlPr1vNj+oeAIiJK6fPc= github.com/buildkite/terminal-to-html/v3 v3.16.8/go.mod h1:+k1KVKROZocrTLsEQ9PEf9A+8+X8uaVV5iO1ZIOwKYM= -github.com/caddyserver/certmagic v0.24.0 h1:EfXTWpxHAUKgDfOj6MHImJN8Jm4AMFfMT6ITuKhrDF0= -github.com/caddyserver/certmagic v0.24.0/go.mod h1:xPT7dC1DuHHnS2yuEQCEyks+b89sUkMENh8dJF+InLE= -github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= -github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= +github.com/caddyserver/certmagic v0.25.1 h1:4sIKKbOt5pg6+sL7tEwymE1x2bj6CHr80da1CRRIPbY= +github.com/caddyserver/certmagic v0.25.1/go.mod h1:VhyvndxtVton/Fo/wKhRoC46Rbw1fmjvQ3GjHYSQTEY= +github.com/caddyserver/zerossl v0.1.4 h1:CVJOE3MZeFisCERZjkxIcsqIH4fnFdlYWnPYeFtBHRw= +github.com/caddyserver/zerossl v0.1.4/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI= github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a/go.mod h1:2GxOXOlEPAMFPfp014mK1SWq8G8BN8o7/dfYqJrVGn8= @@ -271,8 +269,8 @@ github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdf github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dvyukov/go-fuzz v0.0.0-20210429054444-fca39067bc72/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= -github.com/editorconfig/editorconfig-core-go/v2 v2.6.3 h1:XVUp6qW3BIkmM3/1EkrHpa6bL56APOynfXcZEmIgOhs= -github.com/editorconfig/editorconfig-core-go/v2 v2.6.3/go.mod h1:ThHVc+hqbUsmE1wmK/MASpQEhCleWu1JDJDNhUOMy0c= +github.com/editorconfig/editorconfig-core-go/v2 v2.6.4 h1:CHwUbBVVyKWRX9kt5A/OtwhYUJB32DrFp9xzmjR6cac= +github.com/editorconfig/editorconfig-core-go/v2 v2.6.4/go.mod h1:JWRVKHdVW+dkv6F8p+xGCa6a+TyMrqsFbFkSs/aQkrQ= github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= @@ -316,8 +314,8 @@ github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73/go.mod h1:jyveZeGw5La github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo= github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-chi/chi/v5 v5.2.4 h1:WtFKPHwlywe8Srng8j2BhOD9312j9cGUxG1SP4V2cR4= -github.com/go-chi/chi/v5 v5.2.4/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= +github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug= +github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE= github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0= @@ -330,26 +328,26 @@ github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e h1:oRq/fiirun5Hql github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= -github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= +github.com/go-git/go-billy/v5 v5.7.0 h1:83lBUJhGWhYp0ngzCMSgllhUSuoHP1iEWYjsPl9nwqM= +github.com/go-git/go-billy/v5 v5.7.0/go.mod h1:/1IUejTKH8xipsAcdfcSAlUlo2J7lkYV8GTKxAT/L3E= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.16.3 h1:Z8BtvxZ09bYm/yYNgPKCzgWtaRqDTgIKRgIRHBfU6Z8= -github.com/go-git/go-git/v5 v5.16.3/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= +github.com/go-git/go-git/v5 v5.16.4 h1:7ajIEZHZJULcyJebDLo99bGgS0jRrOxzZG4uCk2Yb2Y= +github.com/go-git/go-git/v5 v5.16.4/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-ldap/ldap/v3 v3.4.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU= -github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM= +github.com/go-ldap/ldap/v3 v3.4.12 h1:1b81mv7MagXZ7+1r7cLTWmyuTqVqdwbtJSjC0DAp9s4= +github.com/go-ldap/ldap/v3 v3.4.12/go.mod h1:+SPAGcTtOfmGsCb3h1RFiq4xpp4N636G75OEace8lNo= github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-redis/redis/v7 v7.4.1 h1:PASvf36gyUpr2zdOUS/9Zqc80GbM+9BDyiJSJDDOrTI= github.com/go-redis/redis/v7 v7.4.1/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRfnwdHj/Dcg= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= -github.com/go-redsync/redsync/v4 v4.13.0 h1:49X6GJfnbLGaIpBBREM/zA4uIMDXKAh1NDkvQ1EkZKA= -github.com/go-redsync/redsync/v4 v4.13.0/go.mod h1:HMW4Q224GZQz6x1Xc7040Yfgacukdzu7ifTDAKiyErQ= +github.com/go-redsync/redsync/v4 v4.15.0 h1:KH/XymuxSV7vyKs6z1Cxxj+N+N18JlPxgXeP6x4JY54= +github.com/go-redsync/redsync/v4 v4.15.0/go.mod h1:qNp+lLs3vkfZbtA/aM/OjlZHfEr5YTAYhRktFPKHC7s= github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= @@ -368,10 +366,8 @@ github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7w github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14= github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 h1:UjoPNDAQ5JPCjlxoJd6K8ALZqSDDhk2ymieAZOVaDg0= github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85/go.mod h1:fR6z1Ie6rtF7kl/vBYMfgD5/G5B1blui7z426/sj2DU= -github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= -github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= -github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= @@ -402,8 +398,8 @@ github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8l github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws= -github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE= +github.com/gomodule/redigo v1.9.3 h1:dNPSXeXv6HCq2jdyWfjgmhBdqnR6PRO3m/G05nvpPC8= +github.com/gomodule/redigo v1.9.3/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= @@ -436,8 +432,8 @@ github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OI github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= -github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY= -github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= +github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef h1:xpF9fUHpoIrrjX24DURVKiwHcFpw19ndIs+FwTSMbno= +github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -476,6 +472,8 @@ github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVU github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= +github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= @@ -520,12 +518,14 @@ github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PW github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw= +github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM= +github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/kljensen/snowball v0.6.0/go.mod h1:27N7E8fVU5H68RlUmnWwZCfxgt4POBJfENGMvNRhldw= @@ -541,8 +541,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.11.1 h1:wuChtj2hfsGmmx3nf1m7xC2XpK6OtelS2shMY+bGMtI= +github.com/lib/pq v1.11.1/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= github.com/libdns/libdns v1.1.1 h1:wPrHrXILoSHKWJKGd0EiAVmiJbFShguILTg9leS/P/U= github.com/libdns/libdns v1.1.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -561,28 +561,28 @@ github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6T github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= -github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= -github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/meilisearch/meilisearch-go v0.33.2 h1:YgsQSLYhAkRN2ias6I1KNRTjdYCN5w2uHbLUQ+xgrws= -github.com/meilisearch/meilisearch-go v0.33.2/go.mod h1:6eOPcQ+OAuwXvnONlfSgfgvr7TIAWM/6OdhcVHg8cF0= -github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc= -github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= -github.com/mholt/archives v0.0.0-20251009205813-e30ac6010726 h1:narluFTg20M5KBwKxedpFiSMkdjQRRNUlpY4uAsKMwk= -github.com/mholt/archives v0.0.0-20251009205813-e30ac6010726/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4= +github.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0= +github.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/meilisearch/meilisearch-go v0.36.0 h1:N1etykTektXt5KPcSbhBO0d5Xx5NaKj4pJWEM7WA5dI= +github.com/meilisearch/meilisearch-go v0.36.0/go.mod h1:HBfHzKMxcSbTOvqdfuRA/yf6Vk9IivcwKocWRuW7W78= +github.com/mholt/acmez/v3 v3.1.4 h1:DyzZe/RnAzT3rpZj/2Ii5xZpiEvvYk3cQEN/RmqxwFQ= +github.com/mholt/acmez/v3 v3.1.4/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= +github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ= +github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= -github.com/microsoft/go-mssqldb v1.9.3 h1:hy4p+LDC8LIGvI3JATnLVmBOLMJbmn5X400mr5j0lPs= -github.com/microsoft/go-mssqldb v1.9.3/go.mod h1:GBbW9ASTiDC+mpgWDGKdm3FnFLTUsLYN3iFL90lQ+PA= -github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= -github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= +github.com/microsoft/go-mssqldb v1.9.6 h1:1MNQg5UiSsokiPz3++K2KPx4moKrwIqly1wv+RyCKTw= +github.com/microsoft/go-mssqldb v1.9.6/go.mod h1:yYMPDufyoF2vVuVCUGtZARr06DKFIhMrluTcgWlXpr4= +github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc= +github.com/miekg/dns v1.1.69/go.mod h1:7OyjD9nEba5OkqQ/hB4fy3PIoxafSZJtducccIelz3g= github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0= github.com/mikelolasagasti/xz v1.0.1/go.mod h1:muAirjiOUxPRXwm9HdDtB3uoRPrGnL85XHtokL9Hcgc= github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI= github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.95 h1:ywOUPg+PebTMTzn9VDsoFJy32ZuARN9zhB+K3IYEvYU= -github.com/minio/minio-go/v7 v7.0.95/go.mod h1:wOOX3uxS334vImCNRVyIDdXX9OsXDm89ToynKgqUKlo= +github.com/minio/minio-go/v7 v7.0.98 h1:MeAVKjLVz+XJ28zFcuYyImNSAh8Mq725uNW4beRisi0= +github.com/minio/minio-go/v7 v7.0.98/go.mod h1:cY0Y+W7yozf0mdIclrttzo1Iiu7mEf9y7nk2uXqMOvM= github.com/minio/minlz v1.0.1 h1:OUZUzXcib8diiX+JYxyRLIdomyZYzHct6EShOKtQY2A= github.com/minio/minlz v1.0.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -603,6 +603,8 @@ github.com/msteinert/pam v1.2.0 h1:mYfjlvN2KYs2Pb9G6nb/1f/nPfAttT/Jee5Sq9r3bGE= github.com/msteinert/pam v1.2.0/go.mod h1:d2n0DCUK8rGecChV3JzvmsDjOY4R7AYbsNxAT+ftQl0= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/niklasfasching/go-org v1.9.1 h1:/3s4uTPOF06pImGa2Yvlp24yKXZoTYM+nsIlMzfpg/0= github.com/niklasfasching/go-org v1.9.1/go.mod h1:ZAGFFkWvUQcpazmi/8nHqwvARpr1xpb+Es67oUGX/48= github.com/nwaples/rardecode/v2 v2.2.0 h1:4ufPGHiNe1rYJxYfehALLjup4Ls3ck42CWwjKiOqu0A= @@ -653,24 +655,26 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs= github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= -github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= -github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= -github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= +github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= github.com/quasoft/websspi v1.1.2 h1:/mA4w0LxWlE3novvsoEL6BBA1WnjJATbjkh1kFrTidw= github.com/quasoft/websspi v1.1.2/go.mod h1:HmVdl939dQ0WIXZhyik+ARdI03M6bQzaSEKcgpFmewk= github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redis/go-redis/v9 v9.12.1 h1:k5iquqv27aBtnTm2tIkROUDp8JBXhXZIVu1InSgvovg= -github.com/redis/go-redis/v9 v9.12.1/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= -github.com/redis/rueidis v1.0.19 h1:s65oWtotzlIFN8eMPhyYwxlwLR1lUdhza2KtWprKYSo= -github.com/redis/rueidis v1.0.19/go.mod h1:8B+r5wdnjwK3lTFml5VtxjzGOQAC+5UmujoD12pDrEo= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/redis/go-redis/v9 v9.17.3 h1:fN29NdNrE17KttK5Ndf20buqfDZwGNgoUr9qjl1DQx4= +github.com/redis/go-redis/v9 v9.17.3/go.mod h1:u410H11HMLoB+TP67dz8rL9s6QW2j76l0//kSOd3370= +github.com/redis/rueidis v1.0.69 h1:WlUefRhuDekji5LsD387ys3UCJtSFeBVf0e5yI0B8b4= +github.com/redis/rueidis v1.0.69/go.mod h1:Lkhr2QTgcoYBhxARU7kJRO8SyVlgUuEkcJO1Y8MCluA= +github.com/redis/rueidis/rueidiscompat v1.0.69 h1:IWVYY9lXdjNO3do2VpJT7aDFi8zbCUuQxZB6E2Grahs= +github.com/redis/rueidis/rueidiscompat v1.0.69/go.mod h1:iC4Y8DoN0Uth0Uezg9e2trvNRC7QAgGeuP2OPLb5ccI= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rhysd/actionlint v1.7.7 h1:0KgkoNTrYY7vmOCs9BW2AHxLvvpoY9nEUzgBHiPUr0k= github.com/rhysd/actionlint v1.7.7/go.mod h1:AE6I6vJEkNaIfWqC2GNE5spIJNhxf8NCtLEKU4NnUXg= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -698,6 +702,8 @@ github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLS github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= @@ -746,8 +752,8 @@ github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKN github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= -github.com/tinylib/msgp v1.4.0 h1:SYOeDRiydzOw9kSiwdYp9UcBgPFtLU2WDHaJXyHruf8= -github.com/tinylib/msgp v1.4.0/go.mod h1:cvjFkb4RiC8qSBOPMGPSzSAx47nAsfhLVTCZZNuHv5o= +github.com/tinylib/msgp v1.6.1 h1:ESRv8eL3u+DNHUoSAAQRE50Hm162zqAnBoGv9PzScPY= +github.com/tinylib/msgp v1.6.1/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/tstranex/u2f v1.0.0 h1:HhJkSzDDlVSVIVt7pDJwCHQj67k7A5EeBgPmeD+pVsQ= github.com/tstranex/u2f v1.0.0/go.mod h1:eahSLaqAS0zsIEv80+vXT7WanXs7MQQDg3j3wGBSayo= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= @@ -815,10 +821,14 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U= go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= +go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= +go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc= go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -835,8 +845,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= +golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -849,8 +859,8 @@ golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11 golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.30.0 h1:jD5RhkmVAnjqaCUXfbGBrn3lpxbknfN9w2UhHHU+5B4= -golang.org/x/image v0.30.0/go.mod h1:SAEUTxCCMWSrJcCy/4HwavEsfZZJlYxeHLc6tTiAe/c= +golang.org/x/image v0.35.0 h1:LKjiHdgMtO8z7Fh18nGY6KDcoEtVfsgLDPeLyguqb7I= +golang.org/x/image v0.35.0/go.mod h1:MwPLTVgvxSASsxdLzKrl8BRFuyqMyGhLwmC+TO1Sybk= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -873,8 +883,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= +golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -903,15 +913,15 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= -golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -927,8 +937,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -970,8 +980,8 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -982,8 +992,8 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= +golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -997,8 +1007,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= @@ -1034,8 +1044,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= +golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1066,8 +1076,8 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1 h1:pmJpJEvT846VzausCQ5d7KreSROcDqmO388w5YbnltA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250826171959-ef028d996bc1/go.mod h1:GmFNa4BdJZ2a8G+wCe9Bg3wwThLrJun751XstdJt5Og= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda h1:i/Q+bfisr7gq6feoJnS/DlpdwEL4ihp41fvRiM3Ork0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1075,16 +1085,16 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= -google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc= -google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1092,8 +1102,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k= +gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= @@ -1120,20 +1130,20 @@ modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= -modernc.org/libc v1.22.2 h1:4U7v51GyhlWqQmwCHj28Rdq2Yzwk55ovjFrdPjs8Hb0= -modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug= -modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= -modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk= -modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U= +modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= +modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/sqlite v1.20.4 h1:J8+m2trkN+KKoE7jglyHYYYiaq5xmz2HoHJIiBlRzbE= modernc.org/sqlite v1.20.4/go.mod h1:zKcGyrICaxNTMEHSr1HQ2GUraP0j+845GYw37+EyT6A= -modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= -modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= -modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI= mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk= pgregory.net/rapid v0.4.2 h1:lsi9jhvZTYvzVpeG93WWgimPRmiJQfGFRNTEZh1dtY0= @@ -1145,5 +1155,5 @@ strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 h1:mUcz5b3 strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY= xorm.io/builder v0.3.13 h1:a3jmiVVL19psGeXx8GIurTp7p0IIgqeDmwhcR6BAOAo= xorm.io/builder v0.3.13/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= -xorm.io/xorm v1.3.10 h1:yR83hTT4mKIPyA/lvWFTzS35xjLwkiYnwdw0Qupeh0o= -xorm.io/xorm v1.3.10/go.mod h1:Lo7hmsFF0F0GbDE7ubX5ZKa+eCf0eCuiJAUG3oI5cxQ= +xorm.io/xorm v1.3.11 h1:i4tlVUASogb0ZZFJHA7dZqoRU2pUpUsutnNdaOlFyMI= +xorm.io/xorm v1.3.11/go.mod h1:cs0ePc8O4a0jD78cNvD+0VFwhqotTvLQZv372QsDw7Q= diff --git a/models/asymkey/gpg_key_test.go b/models/asymkey/gpg_key_test.go index 4621337f11..e6656cb70d 100644 --- a/models/asymkey/gpg_key_test.go +++ b/models/asymkey/gpg_key_test.go @@ -11,7 +11,6 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" "github.com/ProtonMail/go-crypto/openpgp" "github.com/ProtonMail/go-crypto/openpgp/packet" @@ -398,7 +397,7 @@ epiDVQ== func TestTryGetKeyIDFromSignature(t *testing.T) { assert.Empty(t, TryGetKeyIDFromSignature(&packet.Signature{})) assert.Equal(t, "038D1A3EADDBEA9C", TryGetKeyIDFromSignature(&packet.Signature{ - IssuerKeyId: util.ToPointer(uint64(0x38D1A3EADDBEA9C)), + IssuerKeyId: new(uint64(0x38D1A3EADDBEA9C)), })) assert.Equal(t, "038D1A3EADDBEA9C", TryGetKeyIDFromSignature(&packet.Signature{ IssuerFingerprint: []uint8{0xb, 0x23, 0x24, 0xc7, 0xe6, 0xfe, 0x4f, 0x3a, 0x6, 0x26, 0xc1, 0x21, 0x3, 0x8d, 0x1a, 0x3e, 0xad, 0xdb, 0xea, 0x9c}, @@ -419,7 +418,7 @@ func TestParseGPGKey(t *testing.T) { // then revoke the key for _, id := range e.Identities { - id.Revocations = append(id.Revocations, &packet.Signature{RevocationReason: util.ToPointer(packet.KeyCompromised)}) + id.Revocations = append(id.Revocations, &packet.Signature{RevocationReason: new(packet.KeyCompromised)}) } k, err = parseGPGKey(t.Context(), 1, e, true) require.NoError(t, err) diff --git a/models/git/branch.go b/models/git/branch.go index 7fef9f5ca3..e5b73fb3e7 100644 --- a/models/git/branch.go +++ b/models/git/branch.go @@ -490,12 +490,25 @@ func FindRecentlyPushedNewBranches(ctx context.Context, doer *user_model.User, o opts.CommitAfterUnix = time.Now().Add(-time.Hour * 2).Unix() } - baseBranch, err := GetBranch(ctx, opts.BaseRepo.ID, opts.BaseRepo.DefaultBranch) + var ignoredCommitIDs []string + baseDefaultBranch, err := GetBranch(ctx, opts.BaseRepo.ID, opts.BaseRepo.DefaultBranch) if err != nil { - return nil, err + log.Warn("GetBranch:DefaultBranch: %v", err) + } else { + ignoredCommitIDs = append(ignoredCommitIDs, baseDefaultBranch.CommitID) } - // find all related branches, these branches may already created PRs, we will check later + baseDefaultTargetBranchName := opts.BaseRepo.MustGetUnit(ctx, unit.TypePullRequests).PullRequestsConfig().DefaultTargetBranch + if baseDefaultTargetBranchName != "" && baseDefaultTargetBranchName != opts.BaseRepo.DefaultBranch { + baseDefaultTargetBranch, err := GetBranch(ctx, opts.BaseRepo.ID, baseDefaultTargetBranchName) + if err != nil { + log.Warn("GetBranch:DefaultTargetBranch: %v", err) + } else { + ignoredCommitIDs = append(ignoredCommitIDs, baseDefaultTargetBranch.CommitID) + } + } + + // find all related branches, these branches may already have PRs, we will check later var branches []*Branch if err := db.GetEngine(ctx). Where(builder.And( @@ -506,7 +519,7 @@ func FindRecentlyPushedNewBranches(ctx context.Context, doer *user_model.User, o builder.Gte{"commit_time": opts.CommitAfterUnix}, builder.In("repo_id", repoIDs), // newly created branch have no changes, so skip them - builder.Neq{"commit_id": baseBranch.CommitID}, + builder.NotIn("commit_id", ignoredCommitIDs), )). OrderBy(db.SearchOrderByRecentUpdated.String()). Find(&branches); err != nil { @@ -514,10 +527,8 @@ func FindRecentlyPushedNewBranches(ctx context.Context, doer *user_model.User, o } newBranches := make([]*RecentlyPushedNewBranch, 0, len(branches)) - if opts.MaxCount == 0 { - // by default we display 2 recently pushed new branch - opts.MaxCount = 2 - } + opts.MaxCount = util.IfZero(opts.MaxCount, 2) // by default, we display 2 recently pushed new branch + baseTargetBranchName := opts.BaseRepo.GetPullRequestTargetBranch(ctx) for _, branch := range branches { // whether the branch is protected protected, err := IsBranchProtected(ctx, branch.RepoID, branch.Name) @@ -555,7 +566,7 @@ func FindRecentlyPushedNewBranches(ctx context.Context, doer *user_model.User, o BranchDisplayName: branchDisplayName, BranchName: branch.Name, BranchLink: fmt.Sprintf("%s/src/branch/%s", branch.Repo.Link(), util.PathEscapeSegments(branch.Name)), - BranchCompareURL: branch.Repo.ComposeBranchCompareURL(opts.BaseRepo, branch.Name), + BranchCompareURL: branch.Repo.ComposeBranchCompareURL(opts.BaseRepo, baseTargetBranchName, branch.Name), CommitTime: branch.CommitTime, }) } diff --git a/models/issues/issue.go b/models/issues/issue.go index 053b96dceb..f6f27588b3 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -682,7 +682,7 @@ func (issue *Issue) GetParticipantIDsByIssue(ctx context.Context) ([]int64, erro } // BlockedByDependencies finds all Dependencies an issue is blocked by -func (issue *Issue) BlockedByDependencies(ctx context.Context, opts db.ListOptions) (issueDeps []*DependencyInfo, err error) { +func (issue *Issue) BlockedByDependencies(ctx context.Context, opts db.ListOptions) (issueDeps []*DependencyInfo, total int64, err error) { sess := db.GetEngine(ctx). Table("issue"). Join("INNER", "repository", "repository.id = issue.repo_id"). @@ -693,13 +693,13 @@ func (issue *Issue) BlockedByDependencies(ctx context.Context, opts db.ListOptio if opts.Page > 0 { sess = db.SetSessionPagination(sess, &opts) } - err = sess.Find(&issueDeps) + total, err = sess.FindAndCount(&issueDeps) for _, depInfo := range issueDeps { depInfo.Issue.Repo = &depInfo.Repository } - return issueDeps, err + return issueDeps, total, err } // BlockingDependencies returns all blocking dependencies, aka all other issues a given issue blocks diff --git a/models/issues/pull.go b/models/issues/pull.go index 18977ed212..9f180f9ac9 100644 --- a/models/issues/pull.go +++ b/models/issues/pull.go @@ -658,12 +658,18 @@ func (pr *PullRequest) IsWorkInProgress(ctx context.Context) bool { // HasWorkInProgressPrefix determines if the given PR title has a Work In Progress prefix func HasWorkInProgressPrefix(title string) bool { + _, ok := CutWorkInProgressPrefix(title) + return ok +} + +func CutWorkInProgressPrefix(title string) (origTitle string, ok bool) { for _, prefix := range setting.Repository.PullRequest.WorkInProgressPrefixes { - if strings.HasPrefix(strings.ToUpper(title), strings.ToUpper(prefix)) { - return true + prefixLen := len(prefix) + if prefixLen <= len(title) && util.AsciiEqualFold(title[:prefixLen], prefix) { + return title[len(prefix):], true } } - return false + return title, false } // IsFilesConflicted determines if the Pull Request has changes conflicting with the target branch. diff --git a/models/repo/pull_request_default.go b/models/repo/pull_request_default.go new file mode 100644 index 0000000000..89f8eb6a2c --- /dev/null +++ b/models/repo/pull_request_default.go @@ -0,0 +1,16 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repo + +import ( + "context" + + "code.gitea.io/gitea/models/unit" + "code.gitea.io/gitea/modules/util" +) + +func (repo *Repository) GetPullRequestTargetBranch(ctx context.Context) string { + unitPRConfig := repo.MustGetUnit(ctx, unit.TypePullRequests).PullRequestsConfig() + return util.IfZero(unitPRConfig.DefaultTargetBranch, repo.DefaultBranch) +} diff --git a/models/repo/pull_request_default_test.go b/models/repo/pull_request_default_test.go new file mode 100644 index 0000000000..1c4f585ed9 --- /dev/null +++ b/models/repo/pull_request_default_test.go @@ -0,0 +1,32 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repo + +import ( + "testing" + + "code.gitea.io/gitea/models/unit" + "code.gitea.io/gitea/models/unittest" + + "github.com/stretchr/testify/assert" +) + +func TestDefaultTargetBranchSelection(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + ctx := t.Context() + repo := unittest.AssertExistsAndLoadBean(t, &Repository{ID: 1}) + + assert.Equal(t, repo.DefaultBranch, repo.GetPullRequestTargetBranch(ctx)) + + repo.Units = nil + prUnit, err := repo.GetUnit(ctx, unit.TypePullRequests) + assert.NoError(t, err) + prConfig := prUnit.PullRequestsConfig() + prConfig.DefaultTargetBranch = "branch2" + prUnit.Config = prConfig + assert.NoError(t, UpdateRepoUnit(ctx, prUnit)) + repo.Units = nil + assert.Equal(t, "branch2", repo.GetPullRequestTargetBranch(ctx)) +} diff --git a/models/repo/repo.go b/models/repo/repo.go index 605a9e0f3f..0846dbcd05 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -613,16 +613,13 @@ func (repo *Repository) ComposeCompareURL(oldCommitID, newCommitID string) strin return fmt.Sprintf("%s/%s/compare/%s...%s", url.PathEscape(repo.OwnerName), url.PathEscape(repo.Name), util.PathEscapeSegments(oldCommitID), util.PathEscapeSegments(newCommitID)) } -func (repo *Repository) ComposeBranchCompareURL(baseRepo *Repository, branchName string) string { - if baseRepo == nil { - baseRepo = repo - } +func (repo *Repository) ComposeBranchCompareURL(baseRepo *Repository, baseBranch, branchName string) string { var cmpBranchEscaped string if repo.ID != baseRepo.ID { cmpBranchEscaped = fmt.Sprintf("%s/%s:", url.PathEscape(repo.OwnerName), url.PathEscape(repo.Name)) } cmpBranchEscaped = fmt.Sprintf("%s%s", cmpBranchEscaped, util.PathEscapeSegments(branchName)) - return fmt.Sprintf("%s/compare/%s...%s", baseRepo.Link(), util.PathEscapeSegments(baseRepo.DefaultBranch), cmpBranchEscaped) + return fmt.Sprintf("%s/compare/%s...%s", baseRepo.Link(), util.PathEscapeSegments(baseBranch), cmpBranchEscaped) } // IsOwnedBy returns true when user owns this repository diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go index ad0bb9d3f8..d03d5e1e6a 100644 --- a/models/repo/repo_unit.go +++ b/models/repo/repo_unit.go @@ -131,6 +131,7 @@ type PullRequestsConfig struct { DefaultDeleteBranchAfterMerge bool DefaultMergeStyle MergeStyle DefaultAllowMaintainerEdit bool + DefaultTargetBranch string } // FromDB fills up a PullRequestsConfig from serialized format. diff --git a/models/webhook/webhook_system.go b/models/webhook/webhook_system.go index 58d9d4a5c1..e8b5040c96 100644 --- a/models/webhook/webhook_system.go +++ b/models/webhook/webhook_system.go @@ -9,19 +9,32 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/optional" + + "xorm.io/builder" ) -// GetSystemOrDefaultWebhooks returns webhooks by given argument or all if argument is missing. -func GetSystemOrDefaultWebhooks(ctx context.Context, isSystemWebhook optional.Option[bool]) ([]*Webhook, error) { - webhooks := make([]*Webhook, 0, 5) - if !isSystemWebhook.Has() { - return webhooks, db.GetEngine(ctx).Where("repo_id=? AND owner_id=?", 0, 0). - Find(&webhooks) - } +// ListSystemWebhookOptions options for listing system or default webhooks +type ListSystemWebhookOptions struct { + db.ListOptions + IsActive optional.Option[bool] + IsSystem optional.Option[bool] +} - return webhooks, db.GetEngine(ctx). - Where("repo_id=? AND owner_id=? AND is_system_webhook=?", 0, 0, isSystemWebhook.Value()). - Find(&webhooks) +func (opts ListSystemWebhookOptions) ToConds() builder.Cond { + cond := builder.NewCond() + cond = cond.And(builder.Eq{"webhook.repo_id": 0}, builder.Eq{"webhook.owner_id": 0}) + if opts.IsActive.Has() { + cond = cond.And(builder.Eq{"webhook.is_active": opts.IsActive.Value()}) + } + if opts.IsSystem.Has() { + cond = cond.And(builder.Eq{"is_system_webhook": opts.IsSystem.Value()}) + } + return cond +} + +// GetGlobalWebhooks returns global (default and/or system) webhooks +func GetGlobalWebhooks(ctx context.Context, opts *ListSystemWebhookOptions) ([]*Webhook, int64, error) { + return db.FindAndCount[Webhook](ctx, opts) } // GetDefaultWebhooks returns all admin-default webhooks. diff --git a/models/webhook/webhook_system_test.go b/models/webhook/webhook_system_test.go index 8aac693995..d0013c6873 100644 --- a/models/webhook/webhook_system_test.go +++ b/models/webhook/webhook_system_test.go @@ -12,23 +12,24 @@ import ( "github.com/stretchr/testify/assert" ) -func TestGetSystemOrDefaultWebhooks(t *testing.T) { +func TestListSystemWebhookOptions(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - - hooks, err := GetSystemOrDefaultWebhooks(t.Context(), optional.None[bool]()) + opts := ListSystemWebhookOptions{IsSystem: optional.None[bool]()} + hooks, _, err := GetGlobalWebhooks(t.Context(), &opts) assert.NoError(t, err) if assert.Len(t, hooks, 2) { assert.Equal(t, int64(5), hooks[0].ID) assert.Equal(t, int64(6), hooks[1].ID) } - - hooks, err = GetSystemOrDefaultWebhooks(t.Context(), optional.Some(true)) + opts.IsSystem = optional.Some(true) + hooks, _, err = GetGlobalWebhooks(t.Context(), &opts) assert.NoError(t, err) if assert.Len(t, hooks, 1) { assert.Equal(t, int64(5), hooks[0].ID) } - hooks, err = GetSystemOrDefaultWebhooks(t.Context(), optional.Some(false)) + opts.IsSystem = optional.Some(false) + hooks, _, err = GetGlobalWebhooks(t.Context(), &opts) assert.NoError(t, err) if assert.Len(t, hooks, 1) { assert.Equal(t, int64(6), hooks[0].ID) diff --git a/modules/git/catfile_batch_reader.go b/modules/git/catfile_batch_reader.go index 7d2e496ace..8a0b342079 100644 --- a/modules/git/catfile_batch_reader.go +++ b/modules/git/catfile_batch_reader.go @@ -12,11 +12,15 @@ import ( "math" "strconv" "strings" + "sync/atomic" + "time" "code.gitea.io/gitea/modules/git/gitcmd" "code.gitea.io/gitea/modules/log" ) +var catFileBatchDebugWaitClose atomic.Int64 + type catFileBatchCommunicator struct { cancel context.CancelFunc reqWriter io.Writer @@ -36,7 +40,14 @@ func newCatFileBatch(ctx context.Context, repoPath string, cmdCatFile *gitcmd.Co ctx, ctxCancel := context.WithCancelCause(ctx) // We often want to feed the commits in order into cat-file --batch, followed by their trees and subtrees as necessary. - stdinWriter, stdoutReader, pipeClose := cmdCatFile.MakeStdinStdoutPipe() + stdinWriter, stdoutReader, stdPipeClose := cmdCatFile.MakeStdinStdoutPipe() + pipeClose := func() { + if delay := catFileBatchDebugWaitClose.Load(); delay > 0 { + time.Sleep(time.Duration(delay)) // for testing purpose only + } + stdPipeClose() + } + ret = &catFileBatchCommunicator{ debugGitCmd: cmdCatFile, cancel: func() { ctxCancel(nil) }, diff --git a/modules/git/catfile_batch_test.go b/modules/git/catfile_batch_test.go index 8f6b1f5eff..69662ffc1a 100644 --- a/modules/git/catfile_batch_test.go +++ b/modules/git/catfile_batch_test.go @@ -5,9 +5,11 @@ package git import ( "io" + "os" "path/filepath" "sync" "testing" + "time" "code.gitea.io/gitea/modules/test" @@ -37,6 +39,45 @@ func testCatFileBatch(t *testing.T) { require.Error(t, err) }) + simulateQueryTerminated := func(pipeCloseDelay, pipeReadDelay time.Duration) (errRead error) { + catFileBatchDebugWaitClose.Store(int64(pipeCloseDelay)) + defer catFileBatchDebugWaitClose.Store(0) + batch, err := NewBatch(t.Context(), filepath.Join(testReposDir, "repo1_bare")) + require.NoError(t, err) + defer batch.Close() + _, _ = batch.QueryInfo("e2129701f1a4d54dc44f03c93bca0a2aec7c5449") + var c *catFileBatchCommunicator + switch b := batch.(type) { + case *catFileBatchLegacy: + c = b.batchCheck + _, _ = c.reqWriter.Write([]byte("in-complete-line-")) + case *catFileBatchCommand: + c = b.batch + _, _ = c.reqWriter.Write([]byte("info")) + default: + t.FailNow() + } + + wg := sync.WaitGroup{} + wg.Go(func() { + time.Sleep(pipeReadDelay) + var n int + n, errRead = c.respReader.Read(make([]byte, 100)) + assert.Zero(t, n) + }) + time.Sleep(10 * time.Millisecond) + c.debugGitCmd.DebugKill() + wg.Wait() + return errRead + } + + t.Run("QueryTerminated", func(t *testing.T) { + err := simulateQueryTerminated(0, 20*time.Millisecond) + assert.ErrorIs(t, err, os.ErrClosed) // pipes are closed faster + err = simulateQueryTerminated(40*time.Millisecond, 20*time.Millisecond) + assert.ErrorIs(t, err, io.EOF) // reader is faster + }) + batch, err := NewBatch(t.Context(), filepath.Join(testReposDir, "repo1_bare")) require.NoError(t, err) defer batch.Close() @@ -60,30 +101,4 @@ func testCatFileBatch(t *testing.T) { require.NoError(t, err) require.Equal(t, "file1\n", string(content)) }) - - t.Run("QueryTerminated", func(t *testing.T) { - var c *catFileBatchCommunicator - switch b := batch.(type) { - case *catFileBatchLegacy: - c = b.batchCheck - _, _ = c.reqWriter.Write([]byte("in-complete-line-")) - case *catFileBatchCommand: - c = b.batch - _, _ = c.reqWriter.Write([]byte("info")) - default: - t.FailNow() - return - } - - wg := sync.WaitGroup{} - wg.Go(func() { - buf := make([]byte, 100) - _, _ = c.respReader.Read(buf) - n, errRead := c.respReader.Read(buf) - assert.Zero(t, n) - assert.ErrorIs(t, errRead, io.EOF) // the pipe is closed due to command being killed - }) - c.debugGitCmd.DebugKill() - wg.Wait() - }) } diff --git a/modules/git/git.go b/modules/git/git.go index 37371ac59f..932da1989b 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -88,12 +88,17 @@ func parseGitVersionLine(s string) (*version.Version, error) { return nil, fmt.Errorf("invalid git version: %q", s) } - // version string is like: "git version 2.29.3" or "git version 2.29.3.windows.1" + // version output is like: "git version {versionString}" + // versionString can be: + // * "2.5.3" + // * "2.29.3.windows.1" + // * "2.28.0.618.gf4bc123cb7": https://github.com/go-gitea/gitea/issues/12731 versionString := fields[2] - if pos := strings.Index(versionString, "windows"); pos >= 1 { - versionString = versionString[:pos-1] + versionFields := strings.Split(versionString, ".") + if len(versionFields) > 3 { + versionFields = versionFields[:3] } - return version.NewVersion(versionString) + return version.NewVersion(strings.Join(versionFields, ".")) } func checkGitVersionCompatibility(gitVer *version.Version) error { diff --git a/modules/git/git_test.go b/modules/git/git_test.go index 44c018dd74..e21cbe449a 100644 --- a/modules/git/git_test.go +++ b/modules/git/git_test.go @@ -23,6 +23,10 @@ func TestParseGitVersion(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "2.29.3", v.String()) + v, err = parseGitVersionLine("git version 2.28.0.618.gf4bc123cb7") + assert.NoError(t, err) + assert.Equal(t, "2.28.0", v.String()) + _, err = parseGitVersionLine("git version") assert.Error(t, err) diff --git a/modules/git/gitcmd/pipe.go b/modules/git/gitcmd/pipe.go index a1e91fdf8e..d0ce3e2dc6 100644 --- a/modules/git/gitcmd/pipe.go +++ b/modules/git/gitcmd/pipe.go @@ -9,6 +9,14 @@ import ( ) type PipeBufferReader interface { + // Read should be used in the same goroutine as command's Wait + // When Reader in one goroutine, command's Wait in another goroutine, then the command exits, the pipe will be closed: + // * If the Reader goroutine reads faster, it will read all remaining data and then get io.EOF + // * But this io.EOF doesn't mean the Reader has gotten complete data, the data might still be corrupted + // * If the Reader goroutine reads slower, it will get os.ErrClosed because the os.Pipe is closed ahead when the command exits + // + // When using 2 goroutines, no clear solution to distinguish these two cases or make Reader knows whether the data is complete + // It should avoid using Reader in a different goroutine than the command if the Read error needs to be handled. Read(p []byte) (n int, err error) Bytes() []byte } diff --git a/modules/highlight/highlight.go b/modules/highlight/highlight.go index 68f523c6ca..fc8699829c 100644 --- a/modules/highlight/highlight.go +++ b/modules/highlight/highlight.go @@ -98,6 +98,10 @@ func getChromaLexerByLanguage(fileName, lang string) chroma.Lexer { lang = "C++" } } + if lang == "" && util.AsciiEqualFold(ext, ".sql") { + // there is a bug when using MySQL lexer: "--\nSELECT", the second line will be rendered as comment incorrectly + lang = "SQL" + } // lexers.Get is slow if the language name can't be matched directly: it does extra "Match" call to iterate all lexers return lexers.Get(lang) } diff --git a/modules/highlight/highlight_test.go b/modules/highlight/highlight_test.go index f4bdedb2a0..69aff07b04 100644 --- a/modules/highlight/highlight_test.go +++ b/modules/highlight/highlight_test.go @@ -108,6 +108,12 @@ c=2 ), lexerName: "Python", }, + { + name: "test.sql", + code: "--\nSELECT", + want: []template.HTML{"--\n", `SELECT`}, + lexerName: "SQL", + }, } for _, tt := range tests { diff --git a/modules/indexer/issues/meilisearch/meilisearch.go b/modules/indexer/issues/meilisearch/meilisearch.go index b7fae6ee9a..5715cf4794 100644 --- a/modules/indexer/issues/meilisearch/meilisearch.go +++ b/modules/indexer/issues/meilisearch/meilisearch.go @@ -124,7 +124,7 @@ func (b *Indexer) Delete(_ context.Context, ids ...int64) error { } for _, id := range ids { - _, err := b.inner.Client.Index(b.inner.VersionedIndexName()).DeleteDocument(strconv.FormatInt(id, 10)) + _, err := b.inner.Client.Index(b.inner.VersionedIndexName()).DeleteDocument(strconv.FormatInt(id, 10), nil) if err != nil { return err } diff --git a/modules/packages/rubygems/metadata.go b/modules/packages/rubygems/metadata.go index 8a9794860e..5c01abc743 100644 --- a/modules/packages/rubygems/metadata.go +++ b/modules/packages/rubygems/metadata.go @@ -9,6 +9,7 @@ import ( "io" "regexp" "strings" + "sync" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/validation" @@ -25,7 +26,9 @@ var ( ErrInvalidVersion = util.NewInvalidArgumentErrorf("package version is invalid") ) -var versionMatcher = regexp.MustCompile(`\A[0-9]+(?:\.[0-9a-zA-Z]+)*(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?\z`) +var versionMatcher = sync.OnceValue(func() *regexp.Regexp { + return regexp.MustCompile(`\A[0-9]+(?:\.[0-9a-zA-Z]+)*(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?\z`) +}) // Package represents a RubyGems package type Package struct { @@ -128,7 +131,7 @@ func (r requirement) AsVersionRequirement() []VersionRequirement { continue } version, ok := versionInt.(string) - if !ok || version == "0" { + if !ok || (version == "0" && restriction == ">=") { continue } @@ -176,7 +179,7 @@ func parseMetadataFile(r io.Reader) (*Package, error) { return nil, ErrInvalidName } - if !versionMatcher.MatchString(spec.Version.Version) { + if !versionMatcher().MatchString(spec.Version.Version) { return nil, ErrInvalidVersion } diff --git a/modules/packages/rubygems/metadata_test.go b/modules/packages/rubygems/metadata_test.go index ec2fa08b6b..9a98c06653 100644 --- a/modules/packages/rubygems/metadata_test.go +++ b/modules/packages/rubygems/metadata_test.go @@ -4,42 +4,30 @@ package rubygems import ( - "archive/tar" - "bytes" - "encoding/base64" - "io" "testing" + "code.gitea.io/gitea/modules/test" + "github.com/stretchr/testify/assert" ) func TestParsePackageMetaData(t *testing.T) { - createArchive := func(filename string, content []byte) io.Reader { - var buf bytes.Buffer - tw := tar.NewWriter(&buf) - hdr := &tar.Header{ - Name: filename, - Mode: 0o600, - Size: int64(len(content)), - } - tw.WriteHeader(hdr) - tw.Write(content) - tw.Close() - return &buf - } - t.Run("MissingMetadataFile", func(t *testing.T) { - data := createArchive("dummy.txt", []byte{0}) - + data := test.WriteTarArchive(map[string]string{"dummy.txt": ""}) rp, err := ParsePackageMetaData(data) assert.ErrorIs(t, err, ErrMissingMetadataFile) assert.Nil(t, rp) }) t.Run("Valid", func(t *testing.T) { - content, _ := base64.StdEncoding.DecodeString("H4sICHC/I2EEAG1ldGFkYXRhAAEeAOH/bmFtZTogZwp2ZXJzaW9uOgogIHZlcnNpb246IDEKWw35Tx4AAAA=") - data := createArchive("metadata.gz", content) - + metadataContent := test.CompressGzip(` +name: g +version: + version: 1 +`) + data := test.WriteTarArchive(map[string]string{ + "metadata.gz": metadataContent.String(), + }) rp, err := ParsePackageMetaData(data) assert.NoError(t, err) assert.NotNil(t, rp) @@ -47,17 +35,86 @@ func TestParsePackageMetaData(t *testing.T) { } func TestParseMetadataFile(t *testing.T) { - content, _ := base64.StdEncoding.DecodeString(`H4sIAMe7I2ECA9VVTW/UMBC9+1eYXvaUbJpSQBZUHJAqDlwK4kCFIseZzZrGH9iTqisEv52Js9nd -0KqggiqRXWnX45n3ZuZ5nCzL+JPQ15ulq7+AQnEORoj3HpReaSVRO8usNCB4qxEku4YQySbuCPo4 -bjHOd07HeZGfMt9JXLlgBB9imOxx7UIULOPnCZMMLsDXXgeiYbW2jQ6C0y9TELBSa6kJ6/IzaySS -R1mUx1nxIitPeFGI9M2L6eGfWAMebANWaUgktzN9M3lsKNmxutBb1AYyCibbNhsDFu+q9GK/Tc4z -d2IcLBl9js5eHaXFsLyvXeNz0LQyL/YoLx8EsiCMBZlx46k6sS2PDD5AgA5kJPNKdhH2elWzOv7n -uv9Q9Aau/6ngP84elvNpXh5oRVlB5/yW7BH0+qu0G4gqaI/JdEHBFBS5l+pKtsARIjIwUnfj8Le0 -+TrdJLl2DG5A9SjrjgZ1mG+4QbAD+G4ZZBUap6qVnnzGf6Rwp+vliBRqtnYGPBEKvkb0USyXE8mS -dVoR6hj07u0HZgAl3SRS8G/fmXcRK20jyq6rDMSYQFgidamqkXbbuspLXE/0k7GphtKqe67GuRC/ -yjAbmt9LsOMp8xMamFkSQ38fP5EFjdz8LA4do2C69VvqWXAJgrPbKZb58/xZXrKoW6ttW13Bhvzi -4ftn7/yUxd4YGcglvTmmY8aGY3ZwRn4CqcWcidUGAAA=`) - rp, err := parseMetadataFile(bytes.NewReader(content)) + content := test.CompressGzip(`--- !ruby/object:Gem::Specification +name: gitea +version: !ruby/object:Gem::Version + version: 1.0.5 +platform: ruby +authors: +- Gitea +autorequire: +bindir: bin +cert_chain: [] +date: 2021-08-23 00:00:00.000000000 Z +dependencies: +- !ruby/object:Gem::Dependency + name: runtime-dep + requirement: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 1.2.0 + - - "<" + - !ruby/object:Gem::Version + version: '2.0' + type: :runtime + prerelease: false + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 1.2.0 + - - "<" + - !ruby/object:Gem::Version + version: '2.0' +- !ruby/object:Gem::Dependency + name: dev-dep + requirement: !ruby/object:Gem::Requirement + requirements: + - - "~>" + - !ruby/object:Gem::Version + version: '0' + type: :development + prerelease: false + version_requirements: !ruby/object:Gem::Requirement + requirements: + - - "~>" + - !ruby/object:Gem::Version + version: '5.2' +description: RubyGems package test +email: rubygems@gitea.io +executables: [] +extensions: [] +extra_rdoc_files: [] +files: +- lib/gitea.rb +homepage: https://gitea.io/ +licenses: +- MIT +metadata: {} +post_install_message: +rdoc_options: [] +require_paths: +- lib +required_ruby_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: 2.3.0 +required_rubygems_version: !ruby/object:Gem::Requirement + requirements: + - - ">=" + - !ruby/object:Gem::Version + version: '0' +requirements: [] +rubyforge_project: +rubygems_version: 2.7.6.2 +signing_key: +specification_version: 4 +summary: Gitea package +test_files: [] +`) + rp, err := parseMetadataFile(content) assert.NoError(t, err) assert.NotNil(t, rp) @@ -84,5 +141,5 @@ yjAbmt9LsOMp8xMamFkSQ38fP5EFjdz8LA4do2C69VvqWXAJgrPbKZb58/xZXrKoW6ttW13Bhvzi assert.Equal(t, "dev-dep", rp.Metadata.DevelopmentDependencies[0].Name) assert.Len(t, rp.Metadata.DevelopmentDependencies[0].Version, 1) assert.Equal(t, "~>", rp.Metadata.DevelopmentDependencies[0].Version[0].Restriction) - assert.Equal(t, "5.2", rp.Metadata.DevelopmentDependencies[0].Version[0].Version) + assert.Equal(t, "0", rp.Metadata.DevelopmentDependencies[0].Version[0].Version) } diff --git a/modules/repository/branch.go b/modules/repository/branch.go index 30aa0a6e85..0a8f7cc464 100644 --- a/modules/repository/branch.go +++ b/modules/repository/branch.go @@ -17,6 +17,13 @@ import ( "code.gitea.io/gitea/modules/timeutil" ) +// SyncResult describes a reference update detected during sync. +type SyncResult struct { + RefName git.RefName + OldCommitID string + NewCommitID string +} + // SyncRepoBranches synchronizes branch table with repository branches func SyncRepoBranches(ctx context.Context, repoID, doerID int64) (int64, error) { repo, err := repo_model.GetRepositoryByID(ctx, repoID) @@ -33,18 +40,19 @@ func SyncRepoBranches(ctx context.Context, repoID, doerID int64) (int64, error) } defer gitRepo.Close() - return SyncRepoBranchesWithRepo(ctx, repo, gitRepo, doerID) + count, _, err := SyncRepoBranchesWithRepo(ctx, repo, gitRepo, doerID) + return count, err } -func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, doerID int64) (int64, error) { +func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, doerID int64) (int64, []*SyncResult, error) { objFmt, err := gitRepo.GetObjectFormat() if err != nil { - return 0, fmt.Errorf("GetObjectFormat: %w", err) + return 0, nil, fmt.Errorf("GetObjectFormat: %w", err) } if objFmt.Name() != repo.ObjectFormatName { repo.ObjectFormatName = objFmt.Name() if err = repo_model.UpdateRepositoryColsWithAutoTime(ctx, repo, "object_format_name"); err != nil { - return 0, fmt.Errorf("UpdateRepositoryColsWithAutoTime: %w", err) + return 0, nil, fmt.Errorf("UpdateRepositoryColsWithAutoTime: %w", err) } } @@ -52,7 +60,7 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, { branches, _, err := gitRepo.GetBranchNames(0, 0) if err != nil { - return 0, err + return 0, nil, err } log.Trace("SyncRepoBranches[%s]: branches[%d]: %v", repo.FullName(), len(branches), branches) for _, branch := range branches { @@ -67,7 +75,7 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, RepoID: repo.ID, }) if err != nil { - return 0, err + return 0, nil, err } for _, branch := range branches { dbBranches[branch.Name] = branch @@ -77,11 +85,12 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, var toAdd []*git_model.Branch var toUpdate []*git_model.Branch var toRemove []int64 + var syncResults []*SyncResult for branch := range allBranches { dbb := dbBranches[branch] commit, err := gitRepo.GetBranchCommit(branch) if err != nil { - return 0, err + return 0, nil, err } if dbb == nil { toAdd = append(toAdd, &git_model.Branch{ @@ -92,7 +101,12 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, PusherID: doerID, CommitTime: timeutil.TimeStamp(commit.Committer.When.Unix()), }) - } else if commit.ID.String() != dbb.CommitID { + syncResults = append(syncResults, &SyncResult{ + RefName: git.RefNameFromBranch(branch), + OldCommitID: "", + NewCommitID: commit.ID.String(), + }) + } else if commit.ID.String() != dbb.CommitID || dbb.IsDeleted { toUpdate = append(toUpdate, &git_model.Branch{ ID: dbb.ID, RepoID: repo.ID, @@ -102,19 +116,29 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, PusherID: doerID, CommitTime: timeutil.TimeStamp(commit.Committer.When.Unix()), }) + syncResults = append(syncResults, &SyncResult{ + RefName: git.RefNameFromBranch(branch), + OldCommitID: dbb.CommitID, + NewCommitID: commit.ID.String(), + }) } } for _, dbBranch := range dbBranches { if !allBranches.Contains(dbBranch.Name) && !dbBranch.IsDeleted { toRemove = append(toRemove, dbBranch.ID) + syncResults = append(syncResults, &SyncResult{ + RefName: git.RefNameFromBranch(dbBranch.Name), + OldCommitID: dbBranch.CommitID, + NewCommitID: "", + }) } } log.Trace("SyncRepoBranches[%s]: toAdd: %v, toUpdate: %v, toRemove: %v", repo.FullName(), toAdd, toUpdate, toRemove) if len(toAdd) == 0 && len(toRemove) == 0 && len(toUpdate) == 0 { - return int64(len(allBranches)), nil + return int64(len(allBranches)), syncResults, nil } if err := db.WithTx(ctx, func(ctx context.Context) error { @@ -140,7 +164,7 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, return nil }); err != nil { - return 0, err + return 0, nil, err } - return int64(len(allBranches)), nil + return int64(len(allBranches)), syncResults, nil } diff --git a/modules/repository/repo.go b/modules/repository/repo.go index 9013590247..76125f5e61 100644 --- a/modules/repository/repo.go +++ b/modules/repository/repo.go @@ -53,7 +53,8 @@ func SyncRepoTags(ctx context.Context, repoID int64) error { } defer gitRepo.Close() - return SyncReleasesWithTags(ctx, repo, gitRepo) + _, err = SyncReleasesWithTags(ctx, repo, gitRepo) + return err } // StoreMissingLfsObjectsInRepository downloads missing LFS objects @@ -178,13 +179,14 @@ func (shortRelease) TableName() string { // upstream. Hence, after each sync we want the release set to be // identical to the upstream tag set. This is much more efficient for // repositories like https://github.com/vim/vim (with over 13000 tags). -func SyncReleasesWithTags(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository) error { +func SyncReleasesWithTags(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository) ([]*SyncResult, error) { log.Debug("SyncReleasesWithTags: in Repo[%d:%s/%s]", repo.ID, repo.OwnerName, repo.Name) tags, _, err := gitRepo.GetTagInfos(0, 0) if err != nil { - return fmt.Errorf("unable to GetTagInfos in pull-mirror Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err) + return nil, fmt.Errorf("unable to GetTagInfos in pull-mirror Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err) } var added, deleted, updated int + var syncResults []*SyncResult err = db.WithTx(ctx, func(ctx context.Context) error { dbReleases, err := db.Find[shortRelease](ctx, repo_model.FindReleasesOptions{ RepoID: repo.ID, @@ -195,7 +197,45 @@ func SyncReleasesWithTags(ctx context.Context, repo *repo_model.Repository, gitR return fmt.Errorf("unable to FindReleases in pull-mirror Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err) } + dbReleasesByID := make(map[int64]*shortRelease, len(dbReleases)) + dbReleasesByTag := make(map[string]*shortRelease, len(dbReleases)) + for _, release := range dbReleases { + dbReleasesByID[release.ID] = release + dbReleasesByTag[release.TagName] = release + } + inserts, deletes, updates := calcSync(tags, dbReleases) + syncResults = make([]*SyncResult, 0, len(inserts)+len(deletes)+len(updates)) + for _, tag := range inserts { + syncResults = append(syncResults, &SyncResult{ + RefName: git.RefNameFromTag(tag.Name), + OldCommitID: "", + NewCommitID: tag.Object.String(), + }) + } + for _, deleteID := range deletes { + release := dbReleasesByID[deleteID] + if release == nil { + continue + } + syncResults = append(syncResults, &SyncResult{ + RefName: git.RefNameFromTag(release.TagName), + OldCommitID: release.Sha1, + NewCommitID: "", + }) + } + for _, tag := range updates { + release := dbReleasesByTag[tag.Name] + oldSha := "" + if release != nil { + oldSha = release.Sha1 + } + syncResults = append(syncResults, &SyncResult{ + RefName: git.RefNameFromTag(tag.Name), + OldCommitID: oldSha, + NewCommitID: tag.Object.String(), + }) + } // // make release set identical to upstream tags // @@ -238,11 +278,11 @@ func SyncReleasesWithTags(ctx context.Context, repo *repo_model.Repository, gitR return nil }) if err != nil { - return fmt.Errorf("unable to rebuild release table for pull-mirror Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err) + return nil, fmt.Errorf("unable to rebuild release table for pull-mirror Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err) } log.Trace("SyncReleasesWithTags: %d tags added, %d tags deleted, %d tags updated", added, deleted, updated) - return nil + return syncResults, nil } func calcSync(destTags []*git.Tag, dbTags []*shortRelease) ([]*git.Tag, []int64, []*git.Tag) { diff --git a/modules/structs/repo.go b/modules/structs/repo.go index 47973a5f6a..765546a5aa 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -58,26 +58,27 @@ type Repository struct { Fork bool `json:"fork"` Template bool `json:"template"` // the original repository if this repository is a fork, otherwise null - Parent *Repository `json:"parent,omitempty"` - Mirror bool `json:"mirror"` - Size int `json:"size"` - Language string `json:"language"` - LanguagesURL string `json:"languages_url"` - HTMLURL string `json:"html_url"` - URL string `json:"url"` - Link string `json:"link"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - OriginalURL string `json:"original_url"` - Website string `json:"website"` - Stars int `json:"stars_count"` - Forks int `json:"forks_count"` - Watchers int `json:"watchers_count"` - OpenIssues int `json:"open_issues_count"` - OpenPulls int `json:"open_pr_counter"` - Releases int `json:"release_counter"` - DefaultBranch string `json:"default_branch"` - Archived bool `json:"archived"` + Parent *Repository `json:"parent,omitempty"` + Mirror bool `json:"mirror"` + Size int `json:"size"` + Language string `json:"language"` + LanguagesURL string `json:"languages_url"` + HTMLURL string `json:"html_url"` + URL string `json:"url"` + Link string `json:"link"` + SSHURL string `json:"ssh_url"` + CloneURL string `json:"clone_url"` + OriginalURL string `json:"original_url"` + Website string `json:"website"` + Stars int `json:"stars_count"` + Forks int `json:"forks_count"` + Watchers int `json:"watchers_count"` + OpenIssues int `json:"open_issues_count"` + OpenPulls int `json:"open_pr_counter"` + Releases int `json:"release_counter"` + DefaultBranch string `json:"default_branch"` + DefaultTargetBranch string `json:"default_target_branch,omitempty"` + Archived bool `json:"archived"` // swagger:strfmt date-time Created time.Time `json:"created_at"` // swagger:strfmt date-time diff --git a/modules/templates/util_render_comment.go b/modules/templates/util_render_comment.go new file mode 100644 index 0000000000..73f36ad21c --- /dev/null +++ b/modules/templates/util_render_comment.go @@ -0,0 +1,48 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package templates + +import ( + "html/template" + "strings" + + issues_model "code.gitea.io/gitea/models/issues" + "code.gitea.io/gitea/modules/htmlutil" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/svg" + "code.gitea.io/gitea/modules/translation" + "code.gitea.io/gitea/modules/util" +) + +func commentTimelineEventIsWipToggle(c *issues_model.Comment) (isToggle, isWip bool) { + title1, ok1 := issues_model.CutWorkInProgressPrefix(c.OldTitle) + title2, ok2 := issues_model.CutWorkInProgressPrefix(c.NewTitle) + return ok1 != ok2 && strings.TrimSpace(title1) == strings.TrimSpace(title2), ok2 +} + +func (ut *RenderUtils) RenderTimelineEventBadge(c *issues_model.Comment) template.HTML { + if c.Type == issues_model.CommentTypeChangeTitle { + isToggle, isWip := commentTimelineEventIsWipToggle(c) + if !isToggle { + return svg.RenderHTML("octicon-pencil") + } + return util.Iif(isWip, svg.RenderHTML("octicon-git-pull-request-draft"), svg.RenderHTML("octicon-eye")) + } + setting.PanicInDevOrTesting("unimplemented comment type %v: %v", c.Type, c) + return htmlutil.HTMLFormat("(CommentType:%v)", c.Type) +} + +func (ut *RenderUtils) RenderTimelineEventComment(c *issues_model.Comment, createdStr template.HTML) template.HTML { + if c.Type == issues_model.CommentTypeChangeTitle { + locale := ut.ctx.Value(translation.ContextKey).(translation.Locale) + isToggle, isWip := commentTimelineEventIsWipToggle(c) + if !isToggle { + return locale.Tr("repo.issues.change_title_at", ut.RenderEmoji(c.OldTitle), ut.RenderEmoji(c.NewTitle), createdStr) + } + trKey := util.Iif(isWip, "repo.pulls.marked_as_work_in_progress_at", "repo.pulls.marked_as_ready_for_review_at") + return locale.Tr(trKey, createdStr) + } + setting.PanicInDevOrTesting("unimplemented comment type %v: %v", c.Type, c) + return htmlutil.HTMLFormat("(Comment:%v,%v)", c.Type, c.Content) +} diff --git a/modules/templates/util_render_comment_test.go b/modules/templates/util_render_comment_test.go new file mode 100644 index 0000000000..27e67bd354 --- /dev/null +++ b/modules/templates/util_render_comment_test.go @@ -0,0 +1,31 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package templates + +import ( + "html/template" + "testing" + + issues_model "code.gitea.io/gitea/models/issues" + "code.gitea.io/gitea/modules/reqctx" + "code.gitea.io/gitea/modules/translation" + + "github.com/stretchr/testify/assert" +) + +func TestRenderTimelineEventComment(t *testing.T) { + ctx := reqctx.NewRequestContextForTest(t.Context()) + ctx.SetContextValue(translation.ContextKey, &translation.MockLocale{}) + ut := &RenderUtils{ctx: ctx} + var createdStr template.HTML = "(created-at)" + + c := &issues_model.Comment{Type: issues_model.CommentTypeChangeTitle, OldTitle: "WIP: title", NewTitle: "title"} + assert.Equal(t, "repo.pulls.marked_as_ready_for_review_at:(created-at)", string(ut.RenderTimelineEventComment(c, createdStr))) + + c = &issues_model.Comment{Type: issues_model.CommentTypeChangeTitle, OldTitle: "title", NewTitle: "WIP: title"} + assert.Equal(t, "repo.pulls.marked_as_work_in_progress_at:(created-at)", string(ut.RenderTimelineEventComment(c, createdStr))) + + c = &issues_model.Comment{Type: issues_model.CommentTypeChangeTitle, OldTitle: "title", NewTitle: "WIP: new title"} + assert.Equal(t, "repo.issues.change_title_at:title,WIP: new title,(created-at)", string(ut.RenderTimelineEventComment(c, createdStr))) +} diff --git a/modules/test/utils.go b/modules/test/utils.go index 193fee28c8..4996f5a13b 100644 --- a/modules/test/utils.go +++ b/modules/test/utils.go @@ -5,6 +5,7 @@ package test import ( "archive/tar" + "bytes" "compress/gzip" "io" "net/http" @@ -12,6 +13,7 @@ import ( "strings" "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/modules/util" ) // RedirectURL returns the redirect URL of a http response. @@ -82,3 +84,40 @@ func ReadAllTarGzContent(r io.Reader) (map[string]string, error) { } return content, nil } + +func WriteTarArchive(files map[string]string) *bytes.Buffer { + return WriteTarCompression(func(w io.Writer) io.WriteCloser { return util.NopCloser{Writer: w} }, files) +} + +func WriteTarCompression[F func(io.Writer) io.WriteCloser | func(io.Writer) (io.WriteCloser, error)](compression F, files map[string]string) *bytes.Buffer { + buf := &bytes.Buffer{} + var cw io.WriteCloser + switch compressFunc := any(compression).(type) { + case func(io.Writer) io.WriteCloser: + cw = compressFunc(buf) + case func(io.Writer) (io.WriteCloser, error): + cw, _ = compressFunc(buf) + } + tw := tar.NewWriter(cw) + + for name, content := range files { + hdr := &tar.Header{ + Name: name, + Mode: 0o600, + Size: int64(len(content)), + } + _ = tw.WriteHeader(hdr) + _, _ = tw.Write([]byte(content)) + } + _ = tw.Close() + _ = cw.Close() + return buf +} + +func CompressGzip(content string) *bytes.Buffer { + buf := &bytes.Buffer{} + cw := gzip.NewWriter(buf) + _, _ = cw.Write([]byte(content)) + _ = cw.Close() + return buf +} diff --git a/modules/util/util.go b/modules/util/util.go index dd8e073888..f197d4d6a4 100644 --- a/modules/util/util.go +++ b/modules/util/util.go @@ -197,11 +197,6 @@ func ToFloat64(number any) (float64, error) { return value, nil } -// ToPointer returns the pointer of a copy of any given value -func ToPointer[T any](val T) *T { - return &val -} - // Iif is an "inline-if", it returns "trueVal" if "condition" is true, otherwise "falseVal" func Iif[T any](condition bool, trueVal, falseVal T) T { if condition { diff --git a/modules/util/util_test.go b/modules/util/util_test.go index fe4125cdb5..38876276e3 100644 --- a/modules/util/util_test.go +++ b/modules/util/util_test.go @@ -212,15 +212,6 @@ func TestToTitleCase(t *testing.T) { assert.Equal(t, `Foo Bar Baz`, ToTitleCase(`FOO BAR BAZ`)) } -func TestToPointer(t *testing.T) { - assert.Equal(t, "abc", *ToPointer("abc")) - assert.Equal(t, 123, *ToPointer(123)) - abc := "abc" - assert.NotSame(t, &abc, ToPointer(abc)) - val123 := 123 - assert.NotSame(t, &val123, ToPointer(val123)) -} - func TestReserveLineBreakForTextarea(t *testing.T) { assert.Equal(t, "test\ndata", ReserveLineBreakForTextarea("test\r\ndata")) assert.Equal(t, "test\ndata\n", ReserveLineBreakForTextarea("test\r\ndata\r\n")) diff --git a/modules/web/router_test.go b/modules/web/router_test.go index f216aa6180..ab5fbb502c 100644 --- a/modules/web/router_test.go +++ b/modules/web/router_test.go @@ -69,7 +69,7 @@ func TestRouter(t *testing.T) { chiCtx := chi.RouteContext(req.Context()) res.method = req.Method res.pathParams = chiURLParamsToMap(chiCtx) - res.chiRoutePattern = util.ToPointer(chiCtx.RoutePattern()) + res.chiRoutePattern = new(chiCtx.RoutePattern()) if mark != "" { res.handlerMarks = append(res.handlerMarks, mark) } @@ -139,7 +139,7 @@ func TestRouter(t *testing.T) { testRoute(t, "GET /the-user/the-repo/other", resultStruct{ method: "GET", handlerMarks: []string{"not-found:/"}, - chiRoutePattern: util.ToPointer(""), + chiRoutePattern: new(""), }) testRoute(t, "GET /the-user/the-repo/pulls", resultStruct{ method: "GET", @@ -150,7 +150,7 @@ func TestRouter(t *testing.T) { method: "GET", pathParams: map[string]string{"username": "the-user", "reponame": "the-repo", "type": "issues", "index": "123"}, handlerMarks: []string{"view-issue"}, - chiRoutePattern: util.ToPointer("/{username}/{reponame}/{type:issues|pulls}/{index}"), + chiRoutePattern: new("/{username}/{reponame}/{type:issues|pulls}/{index}"), }) testRoute(t, "GET /the-user/the-repo/issues/123?stop=hijack", resultStruct{ method: "GET", @@ -228,7 +228,7 @@ func TestRouter(t *testing.T) { method: "GET", pathParams: map[string]string{"username": "the-user", "reponame": "the-repo", "*": "d1/d2/fn", "dir": "d1/d2", "file": "fn"}, handlerMarks: []string{"s1", "s2", "s3"}, - chiRoutePattern: util.ToPointer("/api/v1/repos/{username}/{reponame}/branches//"), + chiRoutePattern: new("/api/v1/repos/{username}/{reponame}/branches//"), }) }) } diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json index 417698544f..96fa878b5e 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -148,6 +148,13 @@ "filter.private": "Private", "no_results_found": "No results found.", "internal_error_skipped": "Internal error occurred but is skipped: %s", + "characters_spaces": "Spaces", + "characters_tabs": "Tabs", + "text_indent_style": "Indent style", + "text_indent_size": "Indent size", + "text_line_wrap": "Wrap", + "text_line_nowrap": "No wrap", + "text_line_wrap_mode": "Line wrap mode", "search.search": "Search…", "search.type_tooltip": "Search type", "search.fuzzy": "Fuzzy", @@ -1778,6 +1785,8 @@ "repo.pulls.title_desc": "wants to merge %[1]d commits from %[2]s into %[3]s", "repo.pulls.merged_title_desc": "merged %[1]d commits from %[2]s into %[3]s %[4]s", "repo.pulls.change_target_branch_at": "changed target branch from %s to %s %s", + "repo.pulls.marked_as_work_in_progress_at": "marked the pull request as work in progress %s", + "repo.pulls.marked_as_ready_for_review_at": "marked the pull request as ready for review %s", "repo.pulls.tab_conversation": "Conversation", "repo.pulls.tab_commits": "Commits", "repo.pulls.tab_files": "Files Changed", @@ -2122,6 +2131,8 @@ "repo.settings.pulls.ignore_whitespace": "Ignore Whitespace for Conflicts", "repo.settings.pulls.enable_autodetect_manual_merge": "Enable autodetect manual merge (Note: In some special cases, misjudgments can occur)", "repo.settings.pulls.allow_rebase_update": "Enable updating pull request branch by rebase", + "repo.settings.pulls.default_target_branch": "Default target branch for new pull requests", + "repo.settings.pulls.default_target_branch_default": "Default branch (%s)", "repo.settings.pulls.default_delete_branch_after_merge": "Delete pull request branch after merge by default", "repo.settings.pulls.default_allow_edits_from_maintainers": "Allow edits from maintainers by default", "repo.settings.releases_desc": "Enable Repository Releases", @@ -2434,9 +2445,10 @@ "repo.settings.block_outdated_branch_desc": "Merging will not be possible when head branch is behind base branch.", "repo.settings.block_admin_merge_override": "Administrators must follow branch protection rules", "repo.settings.block_admin_merge_override_desc": "Administrators must follow branch protection rules and cannot circumvent it.", - "repo.settings.default_branch_desc": "Select a default repository branch for pull requests and code commits:", + "repo.settings.default_branch_desc": "Select a default branch for code commits.", + "repo.settings.default_target_branch_desc": "Pull requests can use different default target branch if it is set in the Pull Requests section of Repository Advance Settings.", "repo.settings.merge_style_desc": "Merge Styles", - "repo.settings.default_merge_style_desc": "Default Merge Style", + "repo.settings.default_merge_style_desc": "Default merge style", "repo.settings.choose_branch": "Choose a branch…", "repo.settings.no_protected_branch": "There are no protected branches.", "repo.settings.edit_protected_branch": "Edit", @@ -2648,7 +2660,7 @@ "repo.branch.restore_success": "Branch \"%s\" has been restored.", "repo.branch.restore_failed": "Failed to restore branch \"%s\".", "repo.branch.protected_deletion_failed": "Branch \"%s\" is protected. It cannot be deleted.", - "repo.branch.default_deletion_failed": "Branch \"%s\" is the default branch. It cannot be deleted.", + "repo.branch.default_deletion_failed": "Branch \"%s\" is the default or pull request target branch. It cannot be deleted.", "repo.branch.default_branch_not_exist": "Default branch \"%s\" does not exist.", "repo.branch.restore": "Restore Branch \"%s\"", "repo.branch.download": "Download Branch \"%s\"", diff --git a/options/locale/locale_ga-IE.json b/options/locale/locale_ga-IE.json index 07f4a04a4d..14123c5002 100644 --- a/options/locale/locale_ga-IE.json +++ b/options/locale/locale_ga-IE.json @@ -1778,6 +1778,8 @@ "repo.pulls.title_desc": "ag iarraidh %[1]d gealltanas a chumasc ó %[2]s go %[3]s", "repo.pulls.merged_title_desc": "cumasc %[1]d tiomantas ó %[2]s go %[3]s %[4]s", "repo.pulls.change_target_branch_at": "athraigh an spriocbhrainse ó %s go %s %s", + "repo.pulls.marked_as_work_in_progress_at": "marcáladh an iarratas tarraingthe mar obair ar siúl %s", + "repo.pulls.marked_as_ready_for_review_at": "marcáladh an iarratas tarraingthe mar réidh le haghaidh athbhreithnithe %s", "repo.pulls.tab_conversation": "Comhrá", "repo.pulls.tab_commits": "Tiomáintí", "repo.pulls.tab_files": "Comhaid Athraithe", @@ -2122,6 +2124,8 @@ "repo.settings.pulls.ignore_whitespace": "Déan neamhaird de spás bán le haghaidh coinbhleachtaí", "repo.settings.pulls.enable_autodetect_manual_merge": "Cumasaigh cumasc láimhe autodetector (Nóta: I roinnt cásanna speisialta, is féidir míbhreithiúnais tarlú)", "repo.settings.pulls.allow_rebase_update": "Cumasaigh brainse iarratais tarraingthe a nuashonrú trí athbhunú", + "repo.settings.pulls.default_target_branch": "Brainse sprice réamhshocraithe le haghaidh iarratais tarraingthe nua", + "repo.settings.pulls.default_target_branch_default": "Brainse réamhshocraithe (%s)", "repo.settings.pulls.default_delete_branch_after_merge": "Scrios brainse an iarratais tarraingthe tar éis cumasc de réir réamhshocraithe", "repo.settings.pulls.default_allow_edits_from_maintainers": "Ceadaigh eagarthóirí ó chothabhálaí de réir réamhshocraithe", "repo.settings.releases_desc": "Cumasaigh Eisiúintí Stórais", @@ -2434,9 +2438,10 @@ "repo.settings.block_outdated_branch_desc": "Ní bheidh cumasc indéanta nuair a bhíonn ceannbhrainse taobh thiar de bhronnbhrainse.", "repo.settings.block_admin_merge_override": "Ní mór do riarthóirí rialacha cosanta brainse a leanúint", "repo.settings.block_admin_merge_override_desc": "Ní mór do riarthóirí rialacha cosanta brainse a leanúint agus ní féidir leo iad a sheachaint.", - "repo.settings.default_branch_desc": "Roghnaigh brainse stóras réamhshocraithe le haghaidh iarratas tarraingte agus geallann an cód:", + "repo.settings.default_branch_desc": "Roghnaigh brainse réamhshocraithe le haghaidh tiomnuithe cóid.", + "repo.settings.default_target_branch_desc": "Is féidir le hiarratais tarraingthe brainse sprice réamhshocraithe difriúil a úsáid má tá sé socraithe sa rannán Iarratais Tarraingthe de na Socruithe Ardleibhéil Stórála.", "repo.settings.merge_style_desc": "Stíleanna Cumaisc", - "repo.settings.default_merge_style_desc": "Stíl Cumaisc Réamhshocraithe", + "repo.settings.default_merge_style_desc": "Stíl chumasc réamhshocraithe", "repo.settings.choose_branch": "Roghnaigh brainse…", "repo.settings.no_protected_branch": "Níl aon bhrainsí cosanta ann.", "repo.settings.edit_protected_branch": "Cuir in eagar", @@ -2648,7 +2653,7 @@ "repo.branch.restore_success": "Tá brainse \"%s\" curtha ar ais.", "repo.branch.restore_failed": "Theip ar chur ar ais brainse \"%s\".", "repo.branch.protected_deletion_failed": "Tá brainse \"%s\" cosanta. Ní féidir é a scriosadh.", - "repo.branch.default_deletion_failed": "Is é brainse \"%s\" an brainse réamhshocraithe. Ní féidir é a scriosadh.", + "repo.branch.default_deletion_failed": "Is é brainse \"%s\" an brainse sprioc réamhshocraithe nó an brainse sprioc don iarratas tarraingthe. Ní féidir é a scriosadh.", "repo.branch.default_branch_not_exist": "Níl an brainse réamhshocraithe \"%s\" ann.", "repo.branch.restore": "Athchóirigh Brainse \"%s\"", "repo.branch.download": "Brainse Íosluchtaithe \"%s\"", diff --git a/package.json b/package.json index f50bdb0ffb..a01ece0379 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "type": "module", - "packageManager": "pnpm@10.28.2", + "packageManager": "pnpm@10.29.2", "engines": { "node": ">= 22.6.0", "pnpm": ">= 10.0.0" @@ -15,6 +15,7 @@ "@github/relative-time-element": "5.0.0", "@github/text-expander-element": "2.9.4", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", + "@mermaid-js/layout-elk": "0.2.0", "@primer/octicons": "19.21.2", "@resvg/resvg-wasm": "2.6.2", "@silverwind/vue3-calendar-heatmap": "2.1.1", @@ -25,7 +26,7 @@ "chart.js": "4.5.1", "chartjs-adapter-dayjs-4": "1.0.4", "chartjs-plugin-zoom": "2.2.0", - "clippie": "4.1.9", + "clippie": "4.1.10", "compare-versions": "6.1.1", "cropperjs": "1.6.2", "css-loader": "7.1.3", @@ -57,19 +58,19 @@ "tributejs": "5.1.3", "uint8-to-base64": "0.2.1", "vanilla-colorful": "0.7.2", - "vue": "3.5.27", + "vue": "3.5.28", "vue-bar-graph": "2.2.0", "vue-chartjs": "5.3.3", "vue-loader": "17.4.2", - "webpack": "5.104.1", + "webpack": "5.105.0", "webpack-cli": "6.0.1", "wrap-ansi": "9.0.2" }, "devDependencies": { "@eslint-community/eslint-plugin-eslint-comments": "4.6.0", "@eslint/json": "0.14.0", - "@playwright/test": "1.58.1", - "@stylistic/eslint-plugin": "5.7.1", + "@playwright/test": "1.58.2", + "@stylistic/eslint-plugin": "5.8.0", "@stylistic/stylelint-plugin": "5.0.1", "@types/codemirror": "5.60.17", "@types/dropzone": "5.7.9", @@ -82,9 +83,9 @@ "@types/throttle-debounce": "5.0.2", "@types/tinycolor2": "1.4.6", "@types/toastify-js": "1.12.4", - "@typescript-eslint/parser": "8.54.0", - "@vitejs/plugin-vue": "6.0.3", - "@vitest/eslint-plugin": "1.6.6", + "@typescript-eslint/parser": "8.55.0", + "@vitejs/plugin-vue": "6.0.4", + "@vitest/eslint-plugin": "1.6.7", "eslint": "9.39.2", "eslint-import-resolver-typescript": "4.4.4", "eslint-plugin-array-func": "5.1.0", @@ -96,25 +97,25 @@ "eslint-plugin-unicorn": "62.0.0", "eslint-plugin-vue": "10.7.0", "eslint-plugin-vue-scoped-css": "2.12.0", - "eslint-plugin-wc": "3.0.2", - "globals": "17.2.0", - "happy-dom": "20.4.0", + "eslint-plugin-wc": "3.1.0", + "globals": "17.3.0", + "happy-dom": "20.6.0", "jiti": "2.6.1", "markdownlint-cli": "0.47.0", "material-icon-theme": "5.31.0", "nolyfill": "1.0.44", "postcss-html": "1.8.1", - "spectral-cli-bundle": "1.0.3", - "stylelint": "17.1.0", + "spectral-cli-bundle": "1.0.4", + "stylelint": "17.1.1", "stylelint-config-recommended": "18.0.0", "stylelint-declaration-block-no-ignored-properties": "3.0.0", "stylelint-declaration-strict-value": "1.10.11", "stylelint-value-no-unknown-custom-properties": "6.1.1", "svgo": "4.0.0", "typescript": "5.9.3", - "typescript-eslint": "8.54.0", - "updates": "17.0.9", - "vite-string-plugin": "2.0.0", + "typescript-eslint": "8.55.0", + "updates": "17.4.0", + "vite-string-plugin": "2.0.1", "vitest": "4.0.18", "vue-tsc": "3.2.4" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7764e51399..7642d8dc82 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,6 +53,9 @@ importers: '@mcaptcha/vanilla-glue': specifier: 0.1.0-alpha-3 version: 0.1.0-alpha-3 + '@mermaid-js/layout-elk': + specifier: 0.2.0 + version: 0.2.0(mermaid@11.12.2) '@primer/octicons': specifier: 19.21.2 version: 19.21.2 @@ -61,13 +64,13 @@ importers: version: 2.6.2 '@silverwind/vue3-calendar-heatmap': specifier: 2.1.1 - version: 2.1.1(tippy.js@6.3.7)(vue@3.5.27(typescript@5.9.3)) + version: 2.1.1(tippy.js@6.3.7)(vue@3.5.28(typescript@5.9.3)) '@techknowlogick/license-checker-webpack-plugin': specifier: 0.3.0 - version: 0.3.0(webpack@5.104.1) + version: 0.3.0(webpack@5.105.0) add-asset-webpack-plugin: specifier: 3.1.1 - version: 3.1.1(webpack@5.104.1) + version: 3.1.1(webpack@5.105.0) ansi_up: specifier: 6.0.6 version: 6.0.6 @@ -84,8 +87,8 @@ importers: specifier: 2.2.0 version: 2.2.0(chart.js@4.5.1) clippie: - specifier: 4.1.9 - version: 4.1.9 + specifier: 4.1.10 + version: 4.1.10 compare-versions: specifier: 6.1.1 version: 6.1.1 @@ -94,7 +97,7 @@ importers: version: 1.6.2 css-loader: specifier: 7.1.3 - version: 7.1.3(webpack@5.104.1) + version: 7.1.3(webpack@5.105.0) dayjs: specifier: 1.11.19 version: 1.11.19 @@ -106,7 +109,7 @@ importers: version: 2.20.0 esbuild-loader: specifier: 4.4.2 - version: 4.4.2(webpack@5.104.1) + version: 4.4.2(webpack@5.105.0) htmx.org: specifier: 2.0.8 version: 2.0.8 @@ -127,13 +130,13 @@ importers: version: 11.12.2 mini-css-extract-plugin: specifier: 2.10.0 - version: 2.10.0(webpack@5.104.1) + version: 2.10.0(webpack@5.105.0) monaco-editor: specifier: 0.55.1 version: 0.55.1 monaco-editor-webpack-plugin: specifier: 7.1.1 - version: 7.1.1(monaco-editor@0.55.1)(webpack@5.104.1) + version: 7.1.1(monaco-editor@0.55.1)(webpack@5.105.0) online-3d-viewer: specifier: 0.18.0 version: 0.18.0 @@ -148,7 +151,7 @@ importers: version: 8.5.6 postcss-loader: specifier: 8.2.0 - version: 8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.104.1) + version: 8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.105.0) sortablejs: specifier: 1.15.6 version: 1.15.6 @@ -180,23 +183,23 @@ importers: specifier: 0.7.2 version: 0.7.2 vue: - specifier: 3.5.27 - version: 3.5.27(typescript@5.9.3) + specifier: 3.5.28 + version: 3.5.28(typescript@5.9.3) vue-bar-graph: specifier: 2.2.0 version: 2.2.0(typescript@5.9.3) vue-chartjs: specifier: 5.3.3 - version: 5.3.3(chart.js@4.5.1)(vue@3.5.27(typescript@5.9.3)) + version: 5.3.3(chart.js@4.5.1)(vue@3.5.28(typescript@5.9.3)) vue-loader: specifier: 17.4.2 - version: 17.4.2(vue@3.5.27(typescript@5.9.3))(webpack@5.104.1) + version: 17.4.2(vue@3.5.28(typescript@5.9.3))(webpack@5.105.0) webpack: - specifier: 5.104.1 - version: 5.104.1(webpack-cli@6.0.1) + specifier: 5.105.0 + version: 5.105.0(webpack-cli@6.0.1) webpack-cli: specifier: 6.0.1 - version: 6.0.1(webpack@5.104.1) + version: 6.0.1(webpack@5.105.0) wrap-ansi: specifier: 9.0.2 version: 9.0.2 @@ -208,14 +211,14 @@ importers: specifier: 0.14.0 version: 0.14.0 '@playwright/test': - specifier: 1.58.1 - version: 1.58.1 + specifier: 1.58.2 + version: 1.58.2 '@stylistic/eslint-plugin': - specifier: 5.7.1 - version: 5.7.1(eslint@9.39.2(jiti@2.6.1)) + specifier: 5.8.0 + version: 5.8.0(eslint@9.39.2(jiti@2.6.1)) '@stylistic/stylelint-plugin': specifier: 5.0.1 - version: 5.0.1(stylelint@17.1.0(typescript@5.9.3)) + version: 5.0.1(stylelint@17.1.1(typescript@5.9.3)) '@types/codemirror': specifier: 5.60.17 version: 5.60.17 @@ -250,20 +253,20 @@ importers: specifier: 1.12.4 version: 1.12.4 '@typescript-eslint/parser': - specifier: 8.54.0 - version: 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + specifier: 8.55.0 + version: 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@vitejs/plugin-vue': - specifier: 6.0.3 - version: 6.0.3(vite@7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.27(typescript@5.9.3)) + specifier: 6.0.4 + version: 6.0.4(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3)) '@vitest/eslint-plugin': - specifier: 1.6.6 - version: 1.6.6(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.18(@types/node@25.1.0)(happy-dom@20.4.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2)) + specifier: 1.6.7 + version: 1.6.7(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.18(@types/node@25.2.2)(happy-dom@20.6.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2)) eslint: specifier: 9.39.2 version: 9.39.2(jiti@2.6.1) eslint-import-resolver-typescript: specifier: 4.4.4 - version: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) + version: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-array-func: specifier: 5.1.0 version: 5.1.0(eslint@9.39.2(jiti@2.6.1)) @@ -272,7 +275,7 @@ importers: version: 6.0.0(@types/eslint@9.6.1)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-import-x: specifier: 4.16.1 - version: 4.16.1(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)) + version: 4.16.1(@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-playwright: specifier: 2.5.1 version: 2.5.1(eslint@9.39.2(jiti@2.6.1)) @@ -287,19 +290,19 @@ importers: version: 62.0.0(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-vue: specifier: 10.7.0 - version: 10.7.0(@stylistic/eslint-plugin@5.7.1(eslint@9.39.2(jiti@2.6.1)))(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1))) + version: 10.7.0(@stylistic/eslint-plugin@5.8.0(eslint@9.39.2(jiti@2.6.1)))(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1))) eslint-plugin-vue-scoped-css: specifier: 2.12.0 version: 2.12.0(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1))) eslint-plugin-wc: - specifier: 3.0.2 - version: 3.0.2(eslint@9.39.2(jiti@2.6.1)) + specifier: 3.1.0 + version: 3.1.0(eslint@9.39.2(jiti@2.6.1)) globals: - specifier: 17.2.0 - version: 17.2.0 + specifier: 17.3.0 + version: 17.3.0 happy-dom: - specifier: 20.4.0 - version: 20.4.0 + specifier: 20.6.0 + version: 20.6.0 jiti: specifier: 2.6.1 version: 2.6.1 @@ -316,23 +319,23 @@ importers: specifier: 1.8.1 version: 1.8.1 spectral-cli-bundle: - specifier: 1.0.3 - version: 1.0.3 + specifier: 1.0.4 + version: 1.0.4 stylelint: - specifier: 17.1.0 - version: 17.1.0(typescript@5.9.3) + specifier: 17.1.1 + version: 17.1.1(typescript@5.9.3) stylelint-config-recommended: specifier: 18.0.0 - version: 18.0.0(stylelint@17.1.0(typescript@5.9.3)) + version: 18.0.0(stylelint@17.1.1(typescript@5.9.3)) stylelint-declaration-block-no-ignored-properties: specifier: 3.0.0 - version: 3.0.0(stylelint@17.1.0(typescript@5.9.3)) + version: 3.0.0(stylelint@17.1.1(typescript@5.9.3)) stylelint-declaration-strict-value: specifier: 1.10.11 - version: 1.10.11(stylelint@17.1.0(typescript@5.9.3)) + version: 1.10.11(stylelint@17.1.1(typescript@5.9.3)) stylelint-value-no-unknown-custom-properties: specifier: 6.1.1 - version: 6.1.1(stylelint@17.1.0(typescript@5.9.3)) + version: 6.1.1(stylelint@17.1.1(typescript@5.9.3)) svgo: specifier: 4.0.0 version: 4.0.0 @@ -340,17 +343,17 @@ importers: specifier: 5.9.3 version: 5.9.3 typescript-eslint: - specifier: 8.54.0 - version: 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + specifier: 8.55.0 + version: 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) updates: - specifier: 17.0.9 - version: 17.0.9 + specifier: 17.4.0 + version: 17.4.0 vite-string-plugin: - specifier: 2.0.0 - version: 2.0.0(vite@7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2)) + specifier: 2.0.1 + version: 2.0.1(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2)) vitest: specifier: 4.0.18 - version: 4.0.18(@types/node@25.1.0)(happy-dom@20.4.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) + version: 4.0.18(@types/node@25.2.2)(happy-dom@20.6.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) vue-tsc: specifier: 3.2.4 version: 3.2.4(typescript@5.9.3) @@ -364,8 +367,8 @@ packages: '@antfu/install-pkg@1.1.0': resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} - '@babel/code-frame@7.28.6': - resolution: {integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==} + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} '@babel/helper-string-parser@7.27.1': @@ -376,8 +379,8 @@ packages: resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.6': - resolution: {integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==} + '@babel/parser@7.29.0': + resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} engines: {node: '>=6.0.0'} hasBin: true @@ -385,8 +388,8 @@ packages: resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.6': - resolution: {integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==} + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} '@braintree/sanitize-url@7.1.2': @@ -395,8 +398,8 @@ packages: '@cacheable/memory@2.0.7': resolution: {integrity: sha512-RbxnxAMf89Tp1dLhXMS7ceft/PGsDl1Ip7T20z5nZ+pwIAsQ1p2izPjVG69oCLv/jfQ7HDPHTWK0c9rcAWXN3A==} - '@cacheable/utils@2.3.3': - resolution: {integrity: sha512-JsXDL70gQ+1Vc2W/KUFfkAJzgb4puKwwKehNLuB+HrNKWf91O736kGfxn4KujXCCSuh6mRRL4XEB0PkAFjWS0A==} + '@cacheable/utils@2.3.4': + resolution: {integrity: sha512-knwKUJEYgIfwShABS1BX6JyJJTglAFcEU7EXqzTdiGCXur4voqkiJkdgZIQtWNFhynzDWERcTYv/sETMu3uJWA==} '@chevrotain/cst-dts-gen@11.0.3': resolution: {integrity: sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==} @@ -467,8 +470,8 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.0.26': - resolution: {integrity: sha512-6boXK0KkzT5u5xOgF6TKB+CLq9SOpEGmkZw0g5n9/7yg85wab3UzSxB8TxhLJ31L4SGJ6BCFRw/iftTha1CJXA==} + '@csstools/css-syntax-patches-for-csstree@1.0.27': + resolution: {integrity: sha512-sxP33Jwg1bviSUXAV43cVYdmjt2TLnLXNqCWl9xmxHawWVjGz/kEbdkr7F9pxJNBN2Mh+dq0crgItbW6tQvyow==} '@csstools/css-tokenizer@4.0.0': resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} @@ -506,158 +509,158 @@ packages: '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} - '@esbuild/aix-ppc64@0.27.2': - resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} + '@esbuild/aix-ppc64@0.27.3': + resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.27.2': - resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} + '@esbuild/android-arm64@0.27.3': + resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.27.2': - resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} + '@esbuild/android-arm@0.27.3': + resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.27.2': - resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} + '@esbuild/android-x64@0.27.3': + resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.27.2': - resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} + '@esbuild/darwin-arm64@0.27.3': + resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.27.2': - resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} + '@esbuild/darwin-x64@0.27.3': + resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.27.2': - resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} + '@esbuild/freebsd-arm64@0.27.3': + resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.2': - resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} + '@esbuild/freebsd-x64@0.27.3': + resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.27.2': - resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} + '@esbuild/linux-arm64@0.27.3': + resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.27.2': - resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} + '@esbuild/linux-arm@0.27.3': + resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.27.2': - resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} + '@esbuild/linux-ia32@0.27.3': + resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.27.2': - resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} + '@esbuild/linux-loong64@0.27.3': + resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.27.2': - resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} + '@esbuild/linux-mips64el@0.27.3': + resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.27.2': - resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} + '@esbuild/linux-ppc64@0.27.3': + resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.27.2': - resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} + '@esbuild/linux-riscv64@0.27.3': + resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.27.2': - resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} + '@esbuild/linux-s390x@0.27.3': + resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.27.2': - resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} + '@esbuild/linux-x64@0.27.3': + resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.27.2': - resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} + '@esbuild/netbsd-arm64@0.27.3': + resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.2': - resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} + '@esbuild/netbsd-x64@0.27.3': + resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.27.2': - resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} + '@esbuild/openbsd-arm64@0.27.3': + resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.2': - resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} + '@esbuild/openbsd-x64@0.27.3': + resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.27.2': - resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} + '@esbuild/openharmony-arm64@0.27.3': + resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.27.2': - resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} + '@esbuild/sunos-x64@0.27.3': + resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.27.2': - resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} + '@esbuild/win32-arm64@0.27.3': + resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.27.2': - resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} + '@esbuild/win32-ia32@0.27.3': + resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.27.2': - resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} + '@esbuild/win32-x64@0.27.3': + resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -767,8 +770,8 @@ packages: resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} engines: {node: 20 || >=22} - '@isaacs/brace-expansion@5.0.0': - resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} + '@isaacs/brace-expansion@5.0.1': + resolution: {integrity: sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==} engines: {node: 20 || >=22} '@jridgewell/gen-mapping@0.3.13': @@ -805,6 +808,11 @@ packages: '@mcaptcha/vanilla-glue@0.1.0-alpha-3': resolution: {integrity: sha512-GT6TJBgmViGXcXiT5VOr+h/6iOnThSlZuCoOWncubyTZU9R3cgU5vWPkF7G6Ob6ee2CBe3yqBxxk24CFVGTVXw==} + '@mermaid-js/layout-elk@0.2.0': + resolution: {integrity: sha512-vjjYGnCCjYlIA/rR7M//eFi0rHM6dsMyN1JQKfckpt30DTC/esrw36hcrvA2FNPHaqh3Q/SyBWzddyaky8EtUQ==} + peerDependencies: + mermaid: ^11.0.2 + '@mermaid-js/parser@0.6.3': resolution: {integrity: sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA==} @@ -890,8 +898,8 @@ packages: resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@playwright/test@1.58.1': - resolution: {integrity: sha512-6LdVIUERWxQMmUSSQi0I53GgCBYgM2RpGngCPY7hSeju+VrKjq3lvs7HpJoPbDiY5QM5EYRtRX5fvrinnMAz3w==} + '@playwright/test@1.58.2': + resolution: {integrity: sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==} engines: {node: '>=18'} hasBin: true @@ -905,8 +913,8 @@ packages: resolution: {integrity: sha512-FqALmHI8D4o6lk/LRWDnhw95z5eO+eAa6ORjVg09YRR7BkcM6oPHU9uyC0gtQG5vpFLvgpeU4+zEAz2H8APHNw==} engines: {node: '>= 10'} - '@rolldown/pluginutils@1.0.0-beta.53': - resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==} + '@rolldown/pluginutils@1.0.0-rc.2': + resolution: {integrity: sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==} '@rollup/rollup-android-arm-eabi@4.57.1': resolution: {integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==} @@ -1084,8 +1092,8 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@stylistic/eslint-plugin@5.7.1': - resolution: {integrity: sha512-zjTUwIsEfT+k9BmXwq1QEFYsb4afBlsI1AXFyWQBgggMzwBFOuu92pGrE5OFx90IOjNl+lUbQoTG7f8S0PkOdg==} + '@stylistic/eslint-plugin@5.8.0': + resolution: {integrity: sha512-WNPVF/FfBAjyi3OA7gok8swRiImNLKI4dmV3iK/GC/0xSJR7eCzBFsw9hLZVgb1+MYNLy7aDsjohxN1hA/FIfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: '>=9.0.0' @@ -1251,8 +1259,8 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@25.1.0': - resolution: {integrity: sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==} + '@types/node@25.2.2': + resolution: {integrity: sha512-BkmoP5/FhRYek5izySdkOneRyXYN35I860MFAGupTdebyE66uZaR+bXLHq8k4DirE5DwQi3NuhvRU1jqTVwUrQ==} '@types/pdfobject@2.2.5': resolution: {integrity: sha512-7gD5tqc/RUDq0PyoLemL0vEHxBYi+zY0WVaFAx/Y0jBsXFgot1vB9No1GhDZGwRGJMCIZbgAb74QG9MTyTNU/g==} @@ -1290,63 +1298,63 @@ packages: '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - '@typescript-eslint/eslint-plugin@8.54.0': - resolution: {integrity: sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ==} + '@typescript-eslint/eslint-plugin@8.55.0': + resolution: {integrity: sha512-1y/MVSz0NglV1ijHC8OT49mPJ4qhPYjiK08YUQVbIOyu+5k862LKUHFkpKHWu//zmr7hDR2rhwUm6gnCGNmGBQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.54.0 + '@typescript-eslint/parser': ^8.55.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.54.0': - resolution: {integrity: sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==} + '@typescript-eslint/parser@8.55.0': + resolution: {integrity: sha512-4z2nCSBfVIMnbuu8uinj+f0o4qOeggYJLbjpPHka3KH1om7e+H9yLKTYgksTaHcGco+NClhhY2vyO3HsMH1RGw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.54.0': - resolution: {integrity: sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==} + '@typescript-eslint/project-service@8.55.0': + resolution: {integrity: sha512-zRcVVPFUYWa3kNnjaZGXSu3xkKV1zXy8M4nO/pElzQhFweb7PPtluDLQtKArEOGmjXoRjnUZ29NjOiF0eCDkcQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.54.0': - resolution: {integrity: sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==} + '@typescript-eslint/scope-manager@8.55.0': + resolution: {integrity: sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.54.0': - resolution: {integrity: sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==} + '@typescript-eslint/tsconfig-utils@8.55.0': + resolution: {integrity: sha512-1R9cXqY7RQd7WuqSN47PK9EDpgFUK3VqdmbYrvWJZYDd0cavROGn+74ktWBlmJ13NXUQKlZ/iAEQHI/V0kKe0Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.54.0': - resolution: {integrity: sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA==} + '@typescript-eslint/type-utils@8.55.0': + resolution: {integrity: sha512-x1iH2unH4qAt6I37I2CGlsNs+B9WGxurP2uyZLRz6UJoZWDBx9cJL1xVN/FiOmHEONEg6RIufdvyT0TEYIgC5g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.54.0': - resolution: {integrity: sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==} + '@typescript-eslint/types@8.55.0': + resolution: {integrity: sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.54.0': - resolution: {integrity: sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==} + '@typescript-eslint/typescript-estree@8.55.0': + resolution: {integrity: sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.54.0': - resolution: {integrity: sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==} + '@typescript-eslint/utils@8.55.0': + resolution: {integrity: sha512-BqZEsnPGdYpgyEIkDC1BadNY8oMwckftxBT+C8W0g1iKPdeqKZBtTfnvcq0nf60u7MkjFO8RBvpRGZBPw4L2ow==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.54.0': - resolution: {integrity: sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==} + '@typescript-eslint/visitor-keys@8.55.0': + resolution: {integrity: sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@unrs/resolver-binding-android-arm-eabi@1.11.1': @@ -1452,15 +1460,15 @@ packages: cpu: [x64] os: [win32] - '@vitejs/plugin-vue@6.0.3': - resolution: {integrity: sha512-TlGPkLFLVOY3T7fZrwdvKpjprR3s4fxRln0ORDo1VQ7HHyxJwTlrjKU3kpVWTlaAjIEuCTokmjkZnr8Tpc925w==} + '@vitejs/plugin-vue@6.0.4': + resolution: {integrity: sha512-uM5iXipgYIn13UUQCZNdWkYk+sysBeA97d5mHsAoAt1u/wpN3+zxOmsVJWosuzX+IMGRzeYUNytztrYznboIkQ==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 vue: ^3.2.25 - '@vitest/eslint-plugin@1.6.6': - resolution: {integrity: sha512-bwgQxQWRtnTVzsUHK824tBmHzjV0iTx3tZaiQIYDjX3SA7TsQS8CuDVqxXrRY3FaOUMgbGavesCxI9MOfFLm7Q==} + '@vitest/eslint-plugin@1.6.7': + resolution: {integrity: sha512-sd2QJirEscSQk3Pywtelbs7z8RQp1gyF5BfeZVtTHE8y3suyzbAA71NuT9z01uTRMHoCf5p6M2t2WYNJ7m5FlA==} engines: {node: '>=18'} peerDependencies: eslint: '>=8.57.0' @@ -1510,37 +1518,37 @@ packages: '@volar/typescript@2.4.27': resolution: {integrity: sha512-eWaYCcl/uAPInSK2Lze6IqVWaBu/itVqR5InXcHXFyles4zO++Mglt3oxdgj75BDcv1Knr9Y93nowS8U3wqhxg==} - '@vue/compiler-core@3.5.27': - resolution: {integrity: sha512-gnSBQjZA+//qDZen+6a2EdHqJ68Z7uybrMf3SPjEGgG4dicklwDVmMC1AeIHxtLVPT7sn6sH1KOO+tS6gwOUeQ==} + '@vue/compiler-core@3.5.28': + resolution: {integrity: sha512-kviccYxTgoE8n6OCw96BNdYlBg2GOWfBuOW4Vqwrt7mSKWKwFVvI8egdTltqRgITGPsTFYtKYfxIG8ptX2PJHQ==} - '@vue/compiler-dom@3.5.27': - resolution: {integrity: sha512-oAFea8dZgCtVVVTEC7fv3T5CbZW9BxpFzGGxC79xakTr6ooeEqmRuvQydIiDAkglZEAd09LgVf1RoDnL54fu5w==} + '@vue/compiler-dom@3.5.28': + resolution: {integrity: sha512-/1ZepxAb159jKR1btkefDP+J2xuWL5V3WtleRmxaT+K2Aqiek/Ab/+Ebrw2pPj0sdHO8ViAyyJWfhXXOP/+LQA==} - '@vue/compiler-sfc@3.5.27': - resolution: {integrity: sha512-sHZu9QyDPeDmN/MRoshhggVOWE5WlGFStKFwu8G52swATgSny27hJRWteKDSUUzUH+wp+bmeNbhJnEAel/auUQ==} + '@vue/compiler-sfc@3.5.28': + resolution: {integrity: sha512-6TnKMiNkd6u6VeVDhZn/07KhEZuBSn43Wd2No5zaP5s3xm8IqFTHBj84HJah4UepSUJTro5SoqqlOY22FKY96g==} - '@vue/compiler-ssr@3.5.27': - resolution: {integrity: sha512-Sj7h+JHt512fV1cTxKlYhg7qxBvack+BGncSpH+8vnN+KN95iPIcqB5rsbblX40XorP+ilO7VIKlkuu3Xq2vjw==} + '@vue/compiler-ssr@3.5.28': + resolution: {integrity: sha512-JCq//9w1qmC6UGLWJX7RXzrGpKkroubey/ZFqTpvEIDJEKGgntuDMqkuWiZvzTzTA5h2qZvFBFHY7fAAa9475g==} '@vue/language-core@3.2.4': resolution: {integrity: sha512-bqBGuSG4KZM45KKTXzGtoCl9cWju5jsaBKaJJe3h5hRAAWpZUuj5G+L+eI01sPIkm4H6setKRlw7E85wLdDNew==} - '@vue/reactivity@3.5.27': - resolution: {integrity: sha512-vvorxn2KXfJ0nBEnj4GYshSgsyMNFnIQah/wczXlsNXt+ijhugmW+PpJ2cNPe4V6jpnBcs0MhCODKllWG+nvoQ==} + '@vue/reactivity@3.5.28': + resolution: {integrity: sha512-gr5hEsxvn+RNyu9/9o1WtdYdwDjg5FgjUSBEkZWqgTKlo/fvwZ2+8W6AfKsc9YN2k/+iHYdS9vZYAhpi10kNaw==} - '@vue/runtime-core@3.5.27': - resolution: {integrity: sha512-fxVuX/fzgzeMPn/CLQecWeDIFNt3gQVhxM0rW02Tvp/YmZfXQgcTXlakq7IMutuZ/+Ogbn+K0oct9J3JZfyk3A==} + '@vue/runtime-core@3.5.28': + resolution: {integrity: sha512-POVHTdbgnrBBIpnbYU4y7pOMNlPn2QVxVzkvEA2pEgvzbelQq4ZOUxbp2oiyo+BOtiYlm8Q44wShHJoBvDPAjQ==} - '@vue/runtime-dom@3.5.27': - resolution: {integrity: sha512-/QnLslQgYqSJ5aUmb5F0z0caZPGHRB8LEAQ1s81vHFM5CBfnun63rxhvE/scVb/j3TbBuoZwkJyiLCkBluMpeg==} + '@vue/runtime-dom@3.5.28': + resolution: {integrity: sha512-4SXxSF8SXYMuhAIkT+eBRqOkWEfPu6nhccrzrkioA6l0boiq7sp18HCOov9qWJA5HML61kW8p/cB4MmBiG9dSA==} - '@vue/server-renderer@3.5.27': - resolution: {integrity: sha512-qOz/5thjeP1vAFc4+BY3Nr6wxyLhpeQgAE/8dDtKo6a6xdk+L4W46HDZgNmLOBUDEkFXV3G7pRiUqxjX0/2zWA==} + '@vue/server-renderer@3.5.28': + resolution: {integrity: sha512-pf+5ECKGj8fX95bNincbzJ6yp6nyzuLDhYZCeFxUNp8EBrQpPpQaLX3nNCp49+UbgbPun3CeVE+5CXVV1Xydfg==} peerDependencies: - vue: 3.5.27 + vue: 3.5.28 - '@vue/shared@3.5.27': - resolution: {integrity: sha512-dXr/3CgqXsJkZ0n9F3I4elY8wM9jMJpP3pvRG52r6m0tu/MsAFIe6JpXVGeNMd/D9F4hQynWT8Rfuj0bdm9kFQ==} + '@vue/shared@3.5.28': + resolution: {integrity: sha512-cfWa1fCGBxrvaHRhvV3Is0MgmrbSCxYTXCSCau2I0a1Xw1N1pHAvkWCiXPRAqjvToILvguNyEwjevUqAuBQWvQ==} '@webassemblyjs/ast@1.14.1': resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} @@ -1800,8 +1808,8 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - caniuse-lite@1.0.30001766: - resolution: {integrity: sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==} + caniuse-lite@1.0.30001769: + resolution: {integrity: sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==} chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} @@ -1869,8 +1877,8 @@ packages: resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} engines: {node: '>=4'} - clippie@4.1.9: - resolution: {integrity: sha512-YaNJI8f2bPRVVfdKDUeqSPuQEztyOowee7DIc/DJ48qNJGq/SziipiWN6oWT6q9FR4QN0JzFDpP+fDtkSZyFHw==} + clippie@4.1.10: + resolution: {integrity: sha512-zUjK2fLH8/wju2lks5mH0u8wSRYCOJoHfT1KQ61+aCT5O1ouONnSrnKQ3BTKvIYLUYJarbLZo4FLHyce/SLF2g==} clone-deep@4.0.1: resolution: {integrity: sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==} @@ -1879,8 +1887,8 @@ packages: codemirror-spell-checker@1.1.2: resolution: {integrity: sha512-2Tl6n0v+GJRsC9K3MLCdLaMOmvWL0uukajNJseorZJsslaxZyZMgENocPU8R0DyoTAiKsyqiemSOZo7kjGV0LQ==} - codemirror@5.65.20: - resolution: {integrity: sha512-i5dLDDxwkFCbhjvL2pNjShsojoL3XHyDwsGv1jqETUoW+lzpBKKqNTUWgQwVAOa0tUm4BwekT455ujafi8payA==} + codemirror@5.65.21: + resolution: {integrity: sha512-6teYk0bA0nR3QP0ihGMoxuKzpl5W80FpnHpBJpgy66NK3cZv5b/d/HY8PnRvfSsCG1MTfr92u2WUl+wT0E40mQ==} color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} @@ -1903,8 +1911,8 @@ packages: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} - commander@14.0.2: - resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} engines: {node: '>=20'} commander@2.20.3: @@ -2254,8 +2262,11 @@ packages: easymde@2.20.0: resolution: {integrity: sha512-V1Z5f92TfR42Na852OWnIZMbM7zotWQYTddNaLYZFVKj7APBbyZ3FYJ27gBw2grMW3R6Qdv9J8n5Ij7XRSIgXQ==} - electron-to-chromium@1.5.283: - resolution: {integrity: sha512-3vifjt1HgrGW/h76UEeny+adYApveS9dH2h3p57JYzBSXJIKUJAvtmIytDKjcSCt9xHfrNCFJ7gts6vkhuq++w==} + electron-to-chromium@1.5.286: + resolution: {integrity: sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==} + + elkjs@0.9.3: + resolution: {integrity: sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -2270,14 +2281,18 @@ packages: resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} engines: {node: '>= 4'} - enhanced-resolve@5.18.4: - resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} + enhanced-resolve@5.19.0: + resolution: {integrity: sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==} engines: {node: '>=10.13.0'} entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + entities@7.0.1: resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} @@ -2305,8 +2320,8 @@ packages: peerDependencies: webpack: ^4.40.0 || ^5.0.0 - esbuild@0.27.2: - resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} + esbuild@0.27.3: + resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} engines: {node: '>=18'} hasBin: true @@ -2504,8 +2519,8 @@ packages: '@typescript-eslint/parser': optional: true - eslint-plugin-wc@3.0.2: - resolution: {integrity: sha512-siwTrxPTw6GU2JmP3faInw8nhi0ZCnKsiSRM3j7EAkZmBTGYdDAToeseLYsvPrc5Urp/vPz+g7Ewh7XcICLxww==} + eslint-plugin-wc@3.1.0: + resolution: {integrity: sha512-spvXHD2/GTTgYXxFB3xlMThnXGUeNJaiCwWuPGzjDOLXnVGLcQpDt0fyiN6yiLoaLs/yhsj+7G1FpBZKeigCSA==} peerDependencies: eslint: '>=8.40.0' @@ -2678,8 +2693,8 @@ packages: resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} engines: {node: '>=18'} - get-tsconfig@4.13.1: - resolution: {integrity: sha512-EoY1N2xCn44xU6750Sx7OjOIT59FkmstNc3X6y5xpz7D5cBtZRe/3pSlTkDJgqsOk3WwZPkWfonhhUJfttQo3w==} + get-tsconfig@4.13.6: + resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -2694,7 +2709,7 @@ packages: glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + 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 global-modules@2.0.0: resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} @@ -2712,8 +2727,8 @@ packages: resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} engines: {node: '>=18'} - globals@17.2.0: - resolution: {integrity: sha512-tovnCz/fEq+Ripoq+p/gN1u7l6A7wwkoBT9pRCzTHzsD/LvADIzXZdjmRymh5Ztf0DYC3Rwg5cZRYjxzBmzbWg==} + globals@17.3.0: + resolution: {integrity: sha512-yMqGUQVVCkD4tqjOJf3TnrvaaHDMYp4VlUSObbkIiuCPe/ofdMBFIAcBbCSRFWOnos6qRiTVStDwqPLUclaxIw==} engines: {node: '>=18'} globby@16.1.0: @@ -2733,8 +2748,8 @@ packages: resolution: {integrity: sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==} engines: {node: '>=0.8.0'} - happy-dom@20.4.0: - resolution: {integrity: sha512-RDeQm3dT9n0A5f/TszjUmNCLEuPnMGv3Tv4BmNINebz/h17PA6LMBcxJ5FrcqltNBMh9jA/8ufgDdBYUdBt+eg==} + happy-dom@20.6.0: + resolution: {integrity: sha512-a+Sz2bPai3rajDuE82Y4B0OxlXJ19ckUjyfWDmeCAs8ZbEbnqtwzV9d4CVhQjWIuOBTOw8rhlxNeaSCHeknXRQ==} engines: {node: '>=20.0.0'} has-flag@4.0.0: @@ -2752,8 +2767,8 @@ packages: resolution: {integrity: sha512-Wn2i1In6XFxl8Az55kkgnFRiAlIAushzh26PTjL2AKtQcEfXrcLa7Hn5QOWGZEf3LU057P9TwwZjFyxfS1VuvQ==} engines: {node: '>=20'} - hookified@1.15.0: - resolution: {integrity: sha512-51w+ZZGt7Zw5q7rM3nC4t3aLn/xvKDETsXqMczndvwyVQhAHfUmUuFBRFcos8Iyebtk7OAE9dL26wFNzZVVOkw==} + hookified@1.15.1: + resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==} html-tags@5.1.0: resolution: {integrity: sha512-n6l5uca7/y5joxZ3LUePhzmBFUJ+U2YWzhMa8XUTecSeSlQiZdF5XAd/Q3/WUl0VsXgUwWi8I7CNIwdI5WN1SQ==} @@ -2932,8 +2947,8 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true - jsdoc-type-pratt-parser@7.1.0: - resolution: {integrity: sha512-SX7q7XyCwzM/MEDCYz0l8GgGbJAACGFII9+WfNYr5SLEKukHWRy2Jk3iWRe7P+lpYJNs7oQ+OSei4JtKGUjd7A==} + jsdoc-type-pratt-parser@7.1.1: + resolution: {integrity: sha512-/2uqY7x6bsrpi3i9LVU6J89352C0rpMk0as8trXxCtvd4kPk1ke/Eyif6wqfSLvoNJqcDG9Vk4UsXgygzCt2xA==} engines: {node: '>=20.0.0'} jsesc@3.1.0: @@ -3242,6 +3257,10 @@ packages: resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} engines: {node: 20 || >=22} + minimatch@10.1.2: + resolution: {integrity: sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==} + engines: {node: 20 || >=22} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -3439,13 +3458,13 @@ packages: pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} - playwright-core@1.58.1: - resolution: {integrity: sha512-bcWzOaTxcW+VOOGBCQgnaKToLJ65d6AqfLVKEWvexyS3AS6rbXl+xdpYRMGSRBClPvyj44njOWoxjNdL/H9UNg==} + playwright-core@1.58.2: + resolution: {integrity: sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==} engines: {node: '>=18'} hasBin: true - playwright@1.58.1: - resolution: {integrity: sha512-+2uTZHxSCcxjvGc5C891LrS1/NlxglGxzrC4seZiVjcYVQfUa87wBL6rTDqzGjuoWNjnBzRqKmF6zRYGMvQUaQ==} + playwright@1.58.2: + resolution: {integrity: sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==} engines: {node: '>=18'} hasBin: true @@ -3702,6 +3721,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} @@ -3801,8 +3825,8 @@ packages: spdx-satisfies@5.0.1: resolution: {integrity: sha512-Nwor6W6gzFp8XX4neaKQ7ChV4wmpSh2sSDemMFSzHxpTw460jxFYeOn+jq4ybnSSw/5sc3pjka9MQPouksQNpw==} - spectral-cli-bundle@1.0.3: - resolution: {integrity: sha512-LUsOK0XKl/C2IhlDwBlXz+7qU2rnGbSlu8nqSFB/K+TbPjjmqoCYjG82YFJCmEHurbthvTJ8WRP735vl+3rY2Q==} + spectral-cli-bundle@1.0.4: + resolution: {integrity: sha512-DfArdHmRj8zMlDclROso5sWQzYzn2FZ616bYPstd/+SWdspP99YuUsWRePR32dd2Vj2ic9kfs2INlL749s9Puw==} engines: {node: '>=20'} hasBin: true @@ -3879,8 +3903,8 @@ packages: peerDependencies: stylelint: '>=16' - stylelint@17.1.0: - resolution: {integrity: sha512-+cUX1FxkkbLX5qJRAPapUv/+v+YU3pGbWu+pHVqTXpiY0mYh3Dxfxa0bLBtVtYgOC8hIWIyX2H/3Y3LWlAevDg==} + stylelint@17.1.1: + resolution: {integrity: sha512-SBHVcLEcRF1M9OkD3oT0hT2PayDNLw2hd+aovmzfNQ2ys4Xd3oS9ZNizILWqhQvW802AqKN/vUTMwJqQYMBlWw==} engines: {node: '>=20.19.0'} hasBin: true @@ -4049,8 +4073,8 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - typescript-eslint@8.54.0: - resolution: {integrity: sha512-CKsJ+g53QpsNPqbzUsfKVgd3Lny4yKZ1pP4qN3jdMOg/sisIDLGyDMezycquXLE5JsEU0wp3dGNdzig0/fmSVQ==} + typescript-eslint@8.55.0: + resolution: {integrity: sha512-HE4wj+r5lmDVS9gdaN0/+iqNvPZwGfnJ5lZuz7s5vLlg9ODw0bIiiETaios9LvFI1U94/VBXGm3CB2Y5cNFMpw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -4089,8 +4113,8 @@ packages: peerDependencies: browserslist: '>= 4.21.0' - updates@17.0.9: - resolution: {integrity: sha512-fl+hGvIH+oOBwqFWpkbX2XtZODooOqgr1jcjF/M3/RAVc0P7thuwaNoFpXUSpeJNuKQoyfqm2JFZ2i3ScgG7Lw==} + updates@17.4.0: + resolution: {integrity: sha512-7Lnof1TshAdMw9u/R3Deobcd53sYlaU3ZwULNhOtAVu6DMPEmdi2G7ERnyPZpFThbMLxNX2scCGz3wWTcNjY2Q==} engines: {node: '>=22'} hasBin: true @@ -4107,8 +4131,8 @@ packages: vanilla-colorful@0.7.2: resolution: {integrity: sha512-z2YZusTFC6KnLERx1cgoIRX2CjPRP0W75N+3CC6gbvdX5Ch47rZkEMGO2Xnf+IEmi3RiFLxS18gayMA27iU7Kg==} - vite-string-plugin@2.0.0: - resolution: {integrity: sha512-7p4aOvow1kphhZppt62A6Aj03NhUzOTiBgjHaTWDIdmtiUbjoJeVLarV52UjQNvLFC6BxJgV8r1AAwJfcxwZSQ==} + vite-string-plugin@2.0.1: + resolution: {integrity: sha512-L5B86yQkYrqH5d966w1vI91B0d+0vmICgB6tqjINvtBIGU9qhFY7izqjytED/ApggFC4QTDWNjfF6nWMqY/fQg==} peerDependencies: vite: '*' @@ -4242,8 +4266,8 @@ packages: peerDependencies: typescript: '>=5.0.0' - vue@3.5.27: - resolution: {integrity: sha512-aJ/UtoEyFySPBGarREmN4z6qNKpbEguYHMmXSiOGk69czc+zhs0NF6tEFrY8TZKAl8N/LYAkd4JHVd5E/AsSmw==} + vue@3.5.28: + resolution: {integrity: sha512-BRdrNfeoccSoIZeIhyPBfvWSLFP4q8J3u8Ju8Ug5vu3LdD+yTM13Sg4sKtljxozbnuMu1NB1X5HBHRYUzFocKg==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -4282,8 +4306,8 @@ packages: resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} engines: {node: '>=10.13.0'} - webpack@5.104.1: - resolution: {integrity: sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==} + webpack@5.105.0: + resolution: {integrity: sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -4375,7 +4399,7 @@ snapshots: package-manager-detector: 1.6.0 tinyexec: 1.0.2 - '@babel/code-frame@7.28.6': + '@babel/code-frame@7.29.0': dependencies: '@babel/helper-validator-identifier': 7.28.5 js-tokens: 4.0.0 @@ -4385,13 +4409,13 @@ snapshots: '@babel/helper-validator-identifier@7.28.5': {} - '@babel/parser@7.28.6': + '@babel/parser@7.29.0': dependencies: - '@babel/types': 7.28.6 + '@babel/types': 7.29.0 '@babel/runtime@7.28.6': {} - '@babel/types@7.28.6': + '@babel/types@7.29.0': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 @@ -4400,12 +4424,12 @@ snapshots: '@cacheable/memory@2.0.7': dependencies: - '@cacheable/utils': 2.3.3 + '@cacheable/utils': 2.3.4 '@keyv/bigmap': 1.3.1(keyv@5.6.0) - hookified: 1.15.0 + hookified: 1.15.1 keyv: 5.6.0 - '@cacheable/utils@2.3.3': + '@cacheable/utils@2.3.4': dependencies: hashery: 1.4.0 keyv: 5.6.0 @@ -4489,7 +4513,7 @@ snapshots: dependencies: '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.0.26': {} + '@csstools/css-syntax-patches-for-csstree@1.0.27': {} '@csstools/css-tokenizer@4.0.0': {} @@ -4524,82 +4548,82 @@ snapshots: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.27.2': + '@esbuild/aix-ppc64@0.27.3': optional: true - '@esbuild/android-arm64@0.27.2': + '@esbuild/android-arm64@0.27.3': optional: true - '@esbuild/android-arm@0.27.2': + '@esbuild/android-arm@0.27.3': optional: true - '@esbuild/android-x64@0.27.2': + '@esbuild/android-x64@0.27.3': optional: true - '@esbuild/darwin-arm64@0.27.2': + '@esbuild/darwin-arm64@0.27.3': optional: true - '@esbuild/darwin-x64@0.27.2': + '@esbuild/darwin-x64@0.27.3': optional: true - '@esbuild/freebsd-arm64@0.27.2': + '@esbuild/freebsd-arm64@0.27.3': optional: true - '@esbuild/freebsd-x64@0.27.2': + '@esbuild/freebsd-x64@0.27.3': optional: true - '@esbuild/linux-arm64@0.27.2': + '@esbuild/linux-arm64@0.27.3': optional: true - '@esbuild/linux-arm@0.27.2': + '@esbuild/linux-arm@0.27.3': optional: true - '@esbuild/linux-ia32@0.27.2': + '@esbuild/linux-ia32@0.27.3': optional: true - '@esbuild/linux-loong64@0.27.2': + '@esbuild/linux-loong64@0.27.3': optional: true - '@esbuild/linux-mips64el@0.27.2': + '@esbuild/linux-mips64el@0.27.3': optional: true - '@esbuild/linux-ppc64@0.27.2': + '@esbuild/linux-ppc64@0.27.3': optional: true - '@esbuild/linux-riscv64@0.27.2': + '@esbuild/linux-riscv64@0.27.3': optional: true - '@esbuild/linux-s390x@0.27.2': + '@esbuild/linux-s390x@0.27.3': optional: true - '@esbuild/linux-x64@0.27.2': + '@esbuild/linux-x64@0.27.3': optional: true - '@esbuild/netbsd-arm64@0.27.2': + '@esbuild/netbsd-arm64@0.27.3': optional: true - '@esbuild/netbsd-x64@0.27.2': + '@esbuild/netbsd-x64@0.27.3': optional: true - '@esbuild/openbsd-arm64@0.27.2': + '@esbuild/openbsd-arm64@0.27.3': optional: true - '@esbuild/openbsd-x64@0.27.2': + '@esbuild/openbsd-x64@0.27.3': optional: true - '@esbuild/openharmony-arm64@0.27.2': + '@esbuild/openharmony-arm64@0.27.3': optional: true - '@esbuild/sunos-x64@0.27.2': + '@esbuild/sunos-x64@0.27.3': optional: true - '@esbuild/win32-arm64@0.27.2': + '@esbuild/win32-arm64@0.27.3': optional: true - '@esbuild/win32-ia32@0.27.2': + '@esbuild/win32-ia32@0.27.3': optional: true - '@esbuild/win32-x64@0.27.2': + '@esbuild/win32-x64@0.27.3': optional: true '@eslint-community/eslint-plugin-eslint-comments@4.6.0(eslint@9.39.2(jiti@2.6.1))': @@ -4705,7 +4729,7 @@ snapshots: '@isaacs/balanced-match@4.0.1': {} - '@isaacs/brace-expansion@5.0.0': + '@isaacs/brace-expansion@5.0.1': dependencies: '@isaacs/balanced-match': 4.0.1 @@ -4731,7 +4755,7 @@ snapshots: '@keyv/bigmap@1.3.1(keyv@5.6.0)': dependencies: hashery: 1.4.0 - hookified: 1.15.0 + hookified: 1.15.1 keyv: 5.6.0 '@keyv/serialize@1.1.1': {} @@ -4744,6 +4768,12 @@ snapshots: dependencies: '@mcaptcha/core-glue': 0.1.0-alpha-5 + '@mermaid-js/layout-elk@0.2.0(mermaid@11.12.2)': + dependencies: + d3: 7.9.0 + elkjs: 0.9.3 + mermaid: 11.12.2 + '@mermaid-js/parser@0.6.3': dependencies: langium: 3.3.1 @@ -4821,9 +4851,9 @@ snapshots: '@pkgr/core@0.2.9': {} - '@playwright/test@1.58.1': + '@playwright/test@1.58.2': dependencies: - playwright: 1.58.1 + playwright: 1.58.2 '@popperjs/core@2.11.8': {} @@ -4833,7 +4863,7 @@ snapshots: '@resvg/resvg-wasm@2.6.2': {} - '@rolldown/pluginutils@1.0.0-beta.53': {} + '@rolldown/pluginutils@1.0.0-rc.2': {} '@rollup/rollup-android-arm-eabi@4.57.1': optional: true @@ -4914,10 +4944,10 @@ snapshots: '@scarf/scarf@1.4.0': {} - '@silverwind/vue3-calendar-heatmap@2.1.1(tippy.js@6.3.7)(vue@3.5.27(typescript@5.9.3))': + '@silverwind/vue3-calendar-heatmap@2.1.1(tippy.js@6.3.7)(vue@3.5.28(typescript@5.9.3))': dependencies: tippy.js: 6.3.7 - vue: 3.5.27(typescript@5.9.3) + vue: 3.5.28(typescript@5.9.3) '@simonwep/pickr@1.9.0': dependencies: @@ -4941,17 +4971,17 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@stylistic/eslint-plugin@5.7.1(eslint@9.39.2(jiti@2.6.1))': + '@stylistic/eslint-plugin@5.8.0(eslint@9.39.2(jiti@2.6.1))': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) - '@typescript-eslint/types': 8.54.0 + '@typescript-eslint/types': 8.55.0 eslint: 9.39.2(jiti@2.6.1) eslint-visitor-keys: 4.2.1 espree: 10.4.0 estraverse: 5.3.0 picomatch: 4.0.3 - '@stylistic/stylelint-plugin@5.0.1(stylelint@17.1.0(typescript@5.9.3))': + '@stylistic/stylelint-plugin@5.0.1(stylelint@17.1.1(typescript@5.9.3))': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 @@ -4960,11 +4990,11 @@ snapshots: postcss-selector-parser: 7.1.1 postcss-value-parser: 4.2.0 style-search: 0.1.0 - stylelint: 17.1.0(typescript@5.9.3) + stylelint: 17.1.1(typescript@5.9.3) '@swc/helpers@0.2.14': {} - '@techknowlogick/license-checker-webpack-plugin@0.3.0(webpack@5.104.1)': + '@techknowlogick/license-checker-webpack-plugin@0.3.0(webpack@5.105.0)': dependencies: glob: 7.2.3 lodash: 4.17.23 @@ -4973,7 +5003,7 @@ snapshots: spdx-expression-validate: 2.0.0 spdx-satisfies: 5.0.1 superstruct: 0.10.13 - webpack: 5.104.1(webpack-cli@6.0.1) + webpack: 5.105.0(webpack-cli@6.0.1) webpack-sources: 1.4.3 wrap-ansi: 6.2.0 @@ -5150,7 +5180,7 @@ snapshots: '@types/ms@2.1.0': {} - '@types/node@25.1.0': + '@types/node@25.2.2': dependencies: undici-types: 7.16.0 @@ -5181,16 +5211,16 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 25.1.0 + '@types/node': 25.2.2 - '@typescript-eslint/eslint-plugin@8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.55.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.54.0 - '@typescript-eslint/type-utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.54.0 + '@typescript-eslint/parser': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.55.0 + '@typescript-eslint/type-utils': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.55.0 eslint: 9.39.2(jiti@2.6.1) ignore: 7.0.5 natural-compare: 1.4.0 @@ -5199,41 +5229,41 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.54.0 - '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.54.0 + '@typescript-eslint/scope-manager': 8.55.0 + '@typescript-eslint/types': 8.55.0 + '@typescript-eslint/typescript-estree': 8.55.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.55.0 debug: 4.4.3 eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.54.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.55.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.54.0(typescript@5.9.3) - '@typescript-eslint/types': 8.54.0 + '@typescript-eslint/tsconfig-utils': 8.55.0(typescript@5.9.3) + '@typescript-eslint/types': 8.55.0 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.54.0': + '@typescript-eslint/scope-manager@8.55.0': dependencies: - '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/visitor-keys': 8.54.0 + '@typescript-eslint/types': 8.55.0 + '@typescript-eslint/visitor-keys': 8.55.0 - '@typescript-eslint/tsconfig-utils@8.54.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.55.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/types': 8.55.0 + '@typescript-eslint/typescript-estree': 8.55.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) debug: 4.4.3 eslint: 9.39.2(jiti@2.6.1) ts-api-utils: 2.4.0(typescript@5.9.3) @@ -5241,37 +5271,37 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.54.0': {} + '@typescript-eslint/types@8.55.0': {} - '@typescript-eslint/typescript-estree@8.54.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.55.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.54.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.54.0(typescript@5.9.3) - '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/visitor-keys': 8.54.0 + '@typescript-eslint/project-service': 8.55.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.55.0(typescript@5.9.3) + '@typescript-eslint/types': 8.55.0 + '@typescript-eslint/visitor-keys': 8.55.0 debug: 4.4.3 minimatch: 9.0.5 - semver: 7.7.3 + semver: 7.7.4 tinyglobby: 0.2.15 ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.54.0 - '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.55.0 + '@typescript-eslint/types': 8.55.0 + '@typescript-eslint/typescript-estree': 8.55.0(typescript@5.9.3) eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.54.0': + '@typescript-eslint/visitor-keys@8.55.0': dependencies: - '@typescript-eslint/types': 8.54.0 + '@typescript-eslint/types': 8.55.0 eslint-visitor-keys: 4.2.1 '@unrs/resolver-binding-android-arm-eabi@1.11.1': @@ -5333,20 +5363,20 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vitejs/plugin-vue@6.0.3(vite@7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.27(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.4(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.28(typescript@5.9.3))': dependencies: - '@rolldown/pluginutils': 1.0.0-beta.53 - vite: 7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) - vue: 3.5.27(typescript@5.9.3) + '@rolldown/pluginutils': 1.0.0-rc.2 + vite: 7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) + vue: 3.5.28(typescript@5.9.3) - '@vitest/eslint-plugin@1.6.6(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.18(@types/node@25.1.0)(happy-dom@20.4.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2))': + '@vitest/eslint-plugin@1.6.7(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)(vitest@4.0.18(@types/node@25.2.2)(happy-dom@20.6.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2))': dependencies: - '@typescript-eslint/scope-manager': 8.54.0 - '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.55.0 + '@typescript-eslint/utils': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.2(jiti@2.6.1) optionalDependencies: typescript: 5.9.3 - vitest: 4.0.18(@types/node@25.1.0)(happy-dom@20.4.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) + vitest: 4.0.18(@types/node@25.2.2)(happy-dom@20.6.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color @@ -5359,13 +5389,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2))': + '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.0.18 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) '@vitest/pretty-format@4.0.18': dependencies: @@ -5401,69 +5431,69 @@ snapshots: path-browserify: 1.0.1 vscode-uri: 3.1.0 - '@vue/compiler-core@3.5.27': + '@vue/compiler-core@3.5.28': dependencies: - '@babel/parser': 7.28.6 - '@vue/shared': 3.5.27 + '@babel/parser': 7.29.0 + '@vue/shared': 3.5.28 entities: 7.0.1 estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-dom@3.5.27': + '@vue/compiler-dom@3.5.28': dependencies: - '@vue/compiler-core': 3.5.27 - '@vue/shared': 3.5.27 + '@vue/compiler-core': 3.5.28 + '@vue/shared': 3.5.28 - '@vue/compiler-sfc@3.5.27': + '@vue/compiler-sfc@3.5.28': dependencies: - '@babel/parser': 7.28.6 - '@vue/compiler-core': 3.5.27 - '@vue/compiler-dom': 3.5.27 - '@vue/compiler-ssr': 3.5.27 - '@vue/shared': 3.5.27 + '@babel/parser': 7.29.0 + '@vue/compiler-core': 3.5.28 + '@vue/compiler-dom': 3.5.28 + '@vue/compiler-ssr': 3.5.28 + '@vue/shared': 3.5.28 estree-walker: 2.0.2 magic-string: 0.30.21 postcss: 8.5.6 source-map-js: 1.2.1 - '@vue/compiler-ssr@3.5.27': + '@vue/compiler-ssr@3.5.28': dependencies: - '@vue/compiler-dom': 3.5.27 - '@vue/shared': 3.5.27 + '@vue/compiler-dom': 3.5.28 + '@vue/shared': 3.5.28 '@vue/language-core@3.2.4': dependencies: '@volar/language-core': 2.4.27 - '@vue/compiler-dom': 3.5.27 - '@vue/shared': 3.5.27 + '@vue/compiler-dom': 3.5.28 + '@vue/shared': 3.5.28 alien-signals: 3.1.2 muggle-string: 0.4.1 path-browserify: 1.0.1 picomatch: 4.0.3 - '@vue/reactivity@3.5.27': + '@vue/reactivity@3.5.28': dependencies: - '@vue/shared': 3.5.27 + '@vue/shared': 3.5.28 - '@vue/runtime-core@3.5.27': + '@vue/runtime-core@3.5.28': dependencies: - '@vue/reactivity': 3.5.27 - '@vue/shared': 3.5.27 + '@vue/reactivity': 3.5.28 + '@vue/shared': 3.5.28 - '@vue/runtime-dom@3.5.27': + '@vue/runtime-dom@3.5.28': dependencies: - '@vue/reactivity': 3.5.27 - '@vue/runtime-core': 3.5.27 - '@vue/shared': 3.5.27 + '@vue/reactivity': 3.5.28 + '@vue/runtime-core': 3.5.28 + '@vue/shared': 3.5.28 csstype: 3.2.3 - '@vue/server-renderer@3.5.27(vue@3.5.27(typescript@5.9.3))': + '@vue/server-renderer@3.5.28(vue@3.5.28(typescript@5.9.3))': dependencies: - '@vue/compiler-ssr': 3.5.27 - '@vue/shared': 3.5.27 - vue: 3.5.27(typescript@5.9.3) + '@vue/compiler-ssr': 3.5.28 + '@vue/shared': 3.5.28 + vue: 3.5.28(typescript@5.9.3) - '@vue/shared@3.5.27': {} + '@vue/shared@3.5.28': {} '@webassemblyjs/ast@1.14.1': dependencies: @@ -5541,20 +5571,20 @@ snapshots: '@webassemblyjs/ast': 1.14.1 '@xtuc/long': 4.2.2 - '@webpack-cli/configtest@3.0.1(webpack-cli@6.0.1)(webpack@5.104.1)': + '@webpack-cli/configtest@3.0.1(webpack-cli@6.0.1)(webpack@5.105.0)': dependencies: - webpack: 5.104.1(webpack-cli@6.0.1) - webpack-cli: 6.0.1(webpack@5.104.1) + webpack: 5.105.0(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack@5.105.0) - '@webpack-cli/info@3.0.1(webpack-cli@6.0.1)(webpack@5.104.1)': + '@webpack-cli/info@3.0.1(webpack-cli@6.0.1)(webpack@5.105.0)': dependencies: - webpack: 5.104.1(webpack-cli@6.0.1) - webpack-cli: 6.0.1(webpack@5.104.1) + webpack: 5.105.0(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack@5.105.0) - '@webpack-cli/serve@3.0.1(webpack-cli@6.0.1)(webpack@5.104.1)': + '@webpack-cli/serve@3.0.1(webpack-cli@6.0.1)(webpack@5.105.0)': dependencies: - webpack: 5.104.1(webpack-cli@6.0.1) - webpack-cli: 6.0.1(webpack@5.104.1) + webpack: 5.105.0(webpack-cli@6.0.1) + webpack-cli: 6.0.1(webpack@5.105.0) '@xtuc/ieee754@1.2.0': {} @@ -5570,9 +5600,9 @@ snapshots: acorn@8.15.0: {} - add-asset-webpack-plugin@3.1.1(webpack@5.104.1): + add-asset-webpack-plugin@3.1.1(webpack@5.105.0): optionalDependencies: - webpack: 5.104.1(webpack-cli@6.0.1) + webpack: 5.105.0(webpack-cli@6.0.1) ajv-formats@2.1.1(ajv@8.17.1): optionalDependencies: @@ -5674,8 +5704,8 @@ snapshots: browserslist@4.28.1: dependencies: baseline-browser-mapping: 2.9.19 - caniuse-lite: 1.0.30001766 - electron-to-chromium: 1.5.283 + caniuse-lite: 1.0.30001769 + electron-to-chromium: 1.5.286 node-releases: 2.0.27 update-browserslist-db: 1.2.3(browserslist@4.28.1) @@ -5695,8 +5725,8 @@ snapshots: cacheable@2.3.2: dependencies: '@cacheable/memory': 2.0.7 - '@cacheable/utils': 2.3.3 - hookified: 1.15.0 + '@cacheable/utils': 2.3.4 + hookified: 1.15.1 keyv: 5.6.0 qified: 0.6.0 @@ -5704,7 +5734,7 @@ snapshots: camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001766: {} + caniuse-lite@1.0.30001769: {} chai@6.2.2: {} @@ -5774,7 +5804,7 @@ snapshots: dependencies: escape-string-regexp: 1.0.5 - clippie@4.1.9: {} + clippie@4.1.10: {} clone-deep@4.0.1: dependencies: @@ -5786,7 +5816,7 @@ snapshots: dependencies: typo-js: 1.3.1 - codemirror@5.65.20: {} + codemirror@5.65.21: {} color-convert@2.0.1: dependencies: @@ -5802,7 +5832,7 @@ snapshots: commander@12.1.0: {} - commander@14.0.2: {} + commander@14.0.3: {} commander@2.20.3: {} @@ -5853,7 +5883,7 @@ snapshots: css-functions-list@3.2.3: {} - css-loader@7.1.3(webpack@5.104.1): + css-loader@7.1.3(webpack@5.105.0): dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 @@ -5862,9 +5892,9 @@ snapshots: postcss-modules-scope: 3.2.1(postcss@8.5.6) postcss-modules-values: 4.0.0(postcss@8.5.6) postcss-value-parser: 4.2.0 - semver: 7.7.3 + semver: 7.7.4 optionalDependencies: - webpack: 5.104.1(webpack-cli@6.0.1) + webpack: 5.105.0(webpack-cli@6.0.1) css-select@5.2.2: dependencies: @@ -6166,11 +6196,13 @@ snapshots: dependencies: '@types/codemirror': 5.60.17 '@types/marked': 4.3.2 - codemirror: 5.65.20 + codemirror: 5.65.21 codemirror-spell-checker: 1.1.2 marked: 4.3.0 - electron-to-chromium@1.5.283: {} + electron-to-chromium@1.5.286: {} + + elkjs@0.9.3: {} emoji-regex@10.6.0: {} @@ -6180,13 +6212,15 @@ snapshots: emojis-list@3.0.0: {} - enhanced-resolve@5.18.4: + enhanced-resolve@5.19.0: dependencies: graceful-fs: 4.2.11 tapable: 2.3.0 entities@4.5.0: {} + entities@6.0.1: {} + entities@7.0.1: {} env-paths@2.2.1: {} @@ -6201,42 +6235,42 @@ snapshots: es-module-lexer@2.0.0: {} - esbuild-loader@4.4.2(webpack@5.104.1): + esbuild-loader@4.4.2(webpack@5.105.0): dependencies: - esbuild: 0.27.2 - get-tsconfig: 4.13.1 + esbuild: 0.27.3 + get-tsconfig: 4.13.6 loader-utils: 2.0.4 - webpack: 5.104.1(webpack-cli@6.0.1) + webpack: 5.105.0(webpack-cli@6.0.1) webpack-sources: 1.4.3 - esbuild@0.27.2: + esbuild@0.27.3: optionalDependencies: - '@esbuild/aix-ppc64': 0.27.2 - '@esbuild/android-arm': 0.27.2 - '@esbuild/android-arm64': 0.27.2 - '@esbuild/android-x64': 0.27.2 - '@esbuild/darwin-arm64': 0.27.2 - '@esbuild/darwin-x64': 0.27.2 - '@esbuild/freebsd-arm64': 0.27.2 - '@esbuild/freebsd-x64': 0.27.2 - '@esbuild/linux-arm': 0.27.2 - '@esbuild/linux-arm64': 0.27.2 - '@esbuild/linux-ia32': 0.27.2 - '@esbuild/linux-loong64': 0.27.2 - '@esbuild/linux-mips64el': 0.27.2 - '@esbuild/linux-ppc64': 0.27.2 - '@esbuild/linux-riscv64': 0.27.2 - '@esbuild/linux-s390x': 0.27.2 - '@esbuild/linux-x64': 0.27.2 - '@esbuild/netbsd-arm64': 0.27.2 - '@esbuild/netbsd-x64': 0.27.2 - '@esbuild/openbsd-arm64': 0.27.2 - '@esbuild/openbsd-x64': 0.27.2 - '@esbuild/openharmony-arm64': 0.27.2 - '@esbuild/sunos-x64': 0.27.2 - '@esbuild/win32-arm64': 0.27.2 - '@esbuild/win32-ia32': 0.27.2 - '@esbuild/win32-x64': 0.27.2 + '@esbuild/aix-ppc64': 0.27.3 + '@esbuild/android-arm': 0.27.3 + '@esbuild/android-arm64': 0.27.3 + '@esbuild/android-x64': 0.27.3 + '@esbuild/darwin-arm64': 0.27.3 + '@esbuild/darwin-x64': 0.27.3 + '@esbuild/freebsd-arm64': 0.27.3 + '@esbuild/freebsd-x64': 0.27.3 + '@esbuild/linux-arm': 0.27.3 + '@esbuild/linux-arm64': 0.27.3 + '@esbuild/linux-ia32': 0.27.3 + '@esbuild/linux-loong64': 0.27.3 + '@esbuild/linux-mips64el': 0.27.3 + '@esbuild/linux-ppc64': 0.27.3 + '@esbuild/linux-riscv64': 0.27.3 + '@esbuild/linux-s390x': 0.27.3 + '@esbuild/linux-x64': 0.27.3 + '@esbuild/netbsd-arm64': 0.27.3 + '@esbuild/netbsd-x64': 0.27.3 + '@esbuild/openbsd-arm64': 0.27.3 + '@esbuild/openbsd-x64': 0.27.3 + '@esbuild/openharmony-arm64': 0.27.3 + '@esbuild/sunos-x64': 0.27.3 + '@esbuild/win32-arm64': 0.27.3 + '@esbuild/win32-ia32': 0.27.3 + '@esbuild/win32-x64': 0.27.3 escalade@3.2.0: {} @@ -6247,7 +6281,7 @@ snapshots: eslint-compat-utils@0.6.5(eslint@9.39.2(jiti@2.6.1)): dependencies: eslint: 9.39.2(jiti@2.6.1) - semver: 7.7.3 + semver: 7.7.4 eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)): dependencies: @@ -6255,7 +6289,7 @@ snapshots: eslint-import-context@0.1.9(unrs-resolver@1.11.1): dependencies: - get-tsconfig: 4.13.1 + get-tsconfig: 4.13.6 stable-hash-x: 0.2.0 optionalDependencies: unrs-resolver: 1.11.1 @@ -6268,30 +6302,30 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)): + eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)): dependencies: debug: 4.4.3 eslint: 9.39.2(jiti@2.6.1) eslint-import-context: 0.1.9(unrs-resolver@1.11.1) - get-tsconfig: 4.13.1 + get-tsconfig: 4.13.6 is-bun-module: 2.0.0 stable-hash-x: 0.2.0 tinyglobby: 0.2.15 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)) - eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) + eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -6324,8 +6358,8 @@ snapshots: '@eslint/eslintrc': 3.3.3 '@eslint/js': 9.39.2 '@github/browserslist-config': 1.0.0 - '@typescript-eslint/eslint-plugin': 8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.55.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) aria-query: 5.3.2 eslint: 9.39.2(jiti@2.6.1) eslint-config-prettier: 10.1.8(eslint@9.39.2(jiti@2.6.1)) @@ -6333,7 +6367,7 @@ snapshots: eslint-plugin-eslint-comments: 3.2.0(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-filenames: 1.3.2(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-i18n-text: 1.0.1(eslint@9.39.2(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-no-only-tests: 3.3.0 eslint-plugin-prettier: 5.5.5(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1))(prettier@3.8.1) @@ -6343,7 +6377,7 @@ snapshots: prettier: 3.8.1 svg-element-attributes: 1.3.1 typescript: 5.9.3 - typescript-eslint: 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + typescript-eslint: 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) transitivePeerDependencies: - '@types/eslint' - eslint-import-resolver-typescript @@ -6354,25 +6388,25 @@ snapshots: dependencies: eslint: 9.39.2(jiti@2.6.1) - eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)): + eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@2.6.1)): dependencies: - '@typescript-eslint/types': 8.54.0 + '@typescript-eslint/types': 8.55.0 comment-parser: 1.4.5 debug: 4.4.3 eslint: 9.39.2(jiti@2.6.1) eslint-import-context: 0.1.9(unrs-resolver@1.11.1) is-glob: 4.0.3 - minimatch: 10.1.1 - semver: 7.7.3 + minimatch: 10.1.2 + semver: 7.7.4 stable-hash-x: 0.2.0 unrs-resolver: 1.11.1 optionalDependencies: - '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: '@nolyfill/array-includes@1.0.44' @@ -6383,7 +6417,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.2(jiti@2.6.1)) hasown: '@nolyfill/hasown@1.0.44' is-core-module: '@nolyfill/is-core-module@1.0.39' is-glob: 4.0.3 @@ -6395,7 +6429,7 @@ snapshots: string.prototype.trimend: '@nolyfill/string.prototype.trimend@1.0.44' tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -6443,7 +6477,7 @@ snapshots: '@eslint-community/regexpp': 4.12.2 comment-parser: 1.4.5 eslint: 9.39.2(jiti@2.6.1) - jsdoc-type-pratt-parser: 7.1.0 + jsdoc-type-pratt-parser: 7.1.1 refa: 0.12.1 regexp-ast-analysis: 0.7.1 scslre: 0.3.0 @@ -6481,7 +6515,7 @@ snapshots: pluralize: 8.0.0 regexp-tree: 0.1.27 regjsparser: 0.13.0 - semver: 7.7.3 + semver: 7.7.4 strip-indent: 4.1.1 eslint-plugin-vue-scoped-css@2.12.0(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1))): @@ -6499,21 +6533,21 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-vue@10.7.0(@stylistic/eslint-plugin@5.7.1(eslint@9.39.2(jiti@2.6.1)))(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1))): + eslint-plugin-vue@10.7.0(@stylistic/eslint-plugin@5.8.0(eslint@9.39.2(jiti@2.6.1)))(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1))): dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1)) eslint: 9.39.2(jiti@2.6.1) natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 7.1.1 - semver: 7.7.3 + semver: 7.7.4 vue-eslint-parser: 10.2.0(eslint@9.39.2(jiti@2.6.1)) xml-name-validator: 4.0.0 optionalDependencies: - '@stylistic/eslint-plugin': 5.7.1(eslint@9.39.2(jiti@2.6.1)) - '@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@stylistic/eslint-plugin': 5.8.0(eslint@9.39.2(jiti@2.6.1)) + '@typescript-eslint/parser': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - eslint-plugin-wc@3.0.2(eslint@9.39.2(jiti@2.6.1)): + eslint-plugin-wc@3.1.0(eslint@9.39.2(jiti@2.6.1)): dependencies: eslint: 9.39.2(jiti@2.6.1) is-valid-element-name: 1.0.0 @@ -6677,7 +6711,7 @@ snapshots: dependencies: cacheable: 2.3.2 flatted: 3.3.3 - hookified: 1.15.0 + hookified: 1.15.1 flat@5.0.2: {} @@ -6695,7 +6729,7 @@ snapshots: get-east-asian-width@1.4.0: {} - get-tsconfig@4.13.1: + get-tsconfig@4.13.6: dependencies: resolve-pkg-maps: 1.0.0 @@ -6732,7 +6766,7 @@ snapshots: globals@16.5.0: {} - globals@17.2.0: {} + globals@17.3.0: {} globby@16.1.0: dependencies: @@ -6751,12 +6785,12 @@ snapshots: hammerjs@2.0.8: {} - happy-dom@20.4.0: + happy-dom@20.6.0: dependencies: - '@types/node': 25.1.0 + '@types/node': 25.2.2 '@types/whatwg-mimetype': 3.0.2 '@types/ws': 8.18.1 - entities: 4.5.0 + entities: 6.0.1 whatwg-mimetype: 3.0.0 ws: 8.19.0 transitivePeerDependencies: @@ -6771,9 +6805,9 @@ snapshots: hashery@1.4.0: dependencies: - hookified: 1.15.0 + hookified: 1.15.1 - hookified@1.15.0: {} + hookified@1.15.1: {} html-tags@5.1.0: {} @@ -6856,7 +6890,7 @@ snapshots: is-bun-module@2.0.0: dependencies: - semver: 7.7.3 + semver: 7.7.4 is-decimal@2.0.1: {} @@ -6892,7 +6926,7 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 25.1.0 + '@types/node': 25.2.2 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -6912,7 +6946,7 @@ snapshots: dependencies: argparse: 2.0.1 - jsdoc-type-pratt-parser@7.1.0: {} + jsdoc-type-pratt-parser@7.1.1: {} jsesc@3.1.0: {} @@ -7051,7 +7085,7 @@ snapshots: markdownlint-cli@0.47.0: dependencies: - commander: 14.0.2 + commander: 14.0.3 deep-extend: 0.6.0 ignore: 7.0.5 js-yaml: 4.1.1 @@ -7059,7 +7093,7 @@ snapshots: jsonpointer: 5.0.1 markdown-it: 14.1.0 markdownlint: 0.40.0 - minimatch: 10.1.1 + minimatch: 10.1.2 run-con: 1.3.2 smol-toml: 1.5.2 tinyglobby: 0.2.15 @@ -7313,15 +7347,19 @@ snapshots: dependencies: mime-db: 1.52.0 - mini-css-extract-plugin@2.10.0(webpack@5.104.1): + mini-css-extract-plugin@2.10.0(webpack@5.105.0): dependencies: schema-utils: 4.3.3 tapable: 2.3.0 - webpack: 5.104.1(webpack-cli@6.0.1) + webpack: 5.105.0(webpack-cli@6.0.1) minimatch@10.1.1: dependencies: - '@isaacs/brace-expansion': 5.0.0 + '@isaacs/brace-expansion': 5.0.1 + + minimatch@10.1.2: + dependencies: + '@isaacs/brace-expansion': 5.0.1 minimatch@3.1.2: dependencies: @@ -7340,11 +7378,11 @@ snapshots: pkg-types: 1.3.1 ufo: 1.6.3 - monaco-editor-webpack-plugin@7.1.1(monaco-editor@0.55.1)(webpack@5.104.1): + monaco-editor-webpack-plugin@7.1.1(monaco-editor@0.55.1)(webpack@5.105.0): dependencies: loader-utils: 2.0.4 monaco-editor: 0.55.1 - webpack: 5.104.1(webpack-cli@6.0.1) + webpack: 5.105.0(webpack-cli@6.0.1) monaco-editor@0.55.1: dependencies: @@ -7452,7 +7490,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.28.6 + '@babel/code-frame': 7.29.0 error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -7495,11 +7533,11 @@ snapshots: mlly: 1.8.0 pathe: 2.0.3 - playwright-core@1.58.1: {} + playwright-core@1.58.2: {} - playwright@1.58.1: + playwright@1.58.2: dependencies: - playwright-core: 1.58.1 + playwright-core: 1.58.2 optionalDependencies: fsevents: 2.3.2 @@ -7538,14 +7576,14 @@ snapshots: optionalDependencies: postcss: 8.5.6 - postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.104.1): + postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.105.0): dependencies: cosmiconfig: 9.0.0(typescript@5.9.3) jiti: 2.6.1 postcss: 8.5.6 - semver: 7.7.3 + semver: 7.7.4 optionalDependencies: - webpack: 5.104.1(webpack-cli@6.0.1) + webpack: 5.105.0(webpack-cli@6.0.1) transitivePeerDependencies: - typescript @@ -7629,7 +7667,7 @@ snapshots: qified@0.6.0: dependencies: - hookified: 1.15.0 + hookified: 1.15.1 queue-microtask@1.2.3: {} @@ -7760,6 +7798,8 @@ snapshots: semver@7.7.3: {} + semver@7.7.4: {} + serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 @@ -7853,7 +7893,7 @@ snapshots: spdx-expression-parse: 3.0.1 spdx-ranges: 2.1.1 - spectral-cli-bundle@1.0.3: + spectral-cli-bundle@1.0.4: optionalDependencies: fsevents: 2.3.3 @@ -7901,28 +7941,28 @@ snapshots: style-search@0.1.0: {} - stylelint-config-recommended@18.0.0(stylelint@17.1.0(typescript@5.9.3)): + stylelint-config-recommended@18.0.0(stylelint@17.1.1(typescript@5.9.3)): dependencies: - stylelint: 17.1.0(typescript@5.9.3) + stylelint: 17.1.1(typescript@5.9.3) - stylelint-declaration-block-no-ignored-properties@3.0.0(stylelint@17.1.0(typescript@5.9.3)): + stylelint-declaration-block-no-ignored-properties@3.0.0(stylelint@17.1.1(typescript@5.9.3)): dependencies: - stylelint: 17.1.0(typescript@5.9.3) + stylelint: 17.1.1(typescript@5.9.3) - stylelint-declaration-strict-value@1.10.11(stylelint@17.1.0(typescript@5.9.3)): + stylelint-declaration-strict-value@1.10.11(stylelint@17.1.1(typescript@5.9.3)): dependencies: - stylelint: 17.1.0(typescript@5.9.3) + stylelint: 17.1.1(typescript@5.9.3) - stylelint-value-no-unknown-custom-properties@6.1.1(stylelint@17.1.0(typescript@5.9.3)): + stylelint-value-no-unknown-custom-properties@6.1.1(stylelint@17.1.1(typescript@5.9.3)): dependencies: postcss-value-parser: 4.2.0 resolve: 1.22.11 - stylelint: 17.1.0(typescript@5.9.3) + stylelint: 17.1.1(typescript@5.9.3) - stylelint@17.1.0(typescript@5.9.3): + stylelint@17.1.1(typescript@5.9.3): dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) - '@csstools/css-syntax-patches-for-csstree': 1.0.26 + '@csstools/css-syntax-patches-for-csstree': 1.0.27 '@csstools/css-tokenizer': 4.0.0 '@csstools/media-query-list-parser': 5.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/selector-resolve-nested': 4.0.0(postcss-selector-parser@7.1.1) @@ -8076,14 +8116,14 @@ snapshots: tapable@2.3.0: {} - terser-webpack-plugin@5.3.16(webpack@5.104.1): + terser-webpack-plugin@5.3.16(webpack@5.105.0): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 serialize-javascript: 6.0.2 terser: 5.46.0 - webpack: 5.104.1(webpack-cli@6.0.1) + webpack: 5.105.0(webpack-cli@6.0.1) terser@5.46.0: dependencies: @@ -8153,12 +8193,12 @@ snapshots: dependencies: prelude-ls: 1.2.1 - typescript-eslint@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): + typescript-eslint@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.55.0(@typescript-eslint/parser@8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.55.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.55.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.2(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: @@ -8208,7 +8248,7 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - updates@17.0.9: {} + updates@17.4.0: {} uri-js@4.4.1: dependencies: @@ -8220,30 +8260,30 @@ snapshots: vanilla-colorful@0.7.2: {} - vite-string-plugin@2.0.0(vite@7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2)): + vite-string-plugin@2.0.1(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2)): dependencies: - vite: 7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) - vite@7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2): + vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2): dependencies: - esbuild: 0.27.2 + esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 rollup: 4.57.1 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 25.1.0 + '@types/node': 25.2.2 fsevents: 2.3.3 jiti: 2.6.1 stylus: 0.57.0 terser: 5.46.0 yaml: 2.8.2 - vitest@4.0.18(@types/node@25.1.0)(happy-dom@20.4.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2): + vitest@4.0.18(@types/node@25.2.2)(happy-dom@20.6.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2): dependencies: '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2)) + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2)) '@vitest/pretty-format': 4.0.18 '@vitest/runner': 4.0.18 '@vitest/snapshot': 4.0.18 @@ -8260,11 +8300,11 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.3.1(@types/node@25.1.0)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) + vite: 7.3.1(@types/node@25.2.2)(jiti@2.6.1)(stylus@0.57.0)(terser@5.46.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 25.1.0 - happy-dom: 20.4.0 + '@types/node': 25.2.2 + happy-dom: 20.6.0 transitivePeerDependencies: - jiti - less @@ -8299,14 +8339,14 @@ snapshots: vue-bar-graph@2.2.0(typescript@5.9.3): dependencies: - vue: 3.5.27(typescript@5.9.3) + vue: 3.5.28(typescript@5.9.3) transitivePeerDependencies: - typescript - vue-chartjs@5.3.3(chart.js@4.5.1)(vue@3.5.27(typescript@5.9.3)): + vue-chartjs@5.3.3(chart.js@4.5.1)(vue@3.5.28(typescript@5.9.3)): dependencies: chart.js: 4.5.1 - vue: 3.5.27(typescript@5.9.3) + vue: 3.5.28(typescript@5.9.3) vue-eslint-parser@10.2.0(eslint@9.39.2(jiti@2.6.1)): dependencies: @@ -8316,18 +8356,18 @@ snapshots: eslint-visitor-keys: 4.2.1 espree: 10.4.0 esquery: 1.7.0 - semver: 7.7.3 + semver: 7.7.4 transitivePeerDependencies: - supports-color - vue-loader@17.4.2(vue@3.5.27(typescript@5.9.3))(webpack@5.104.1): + vue-loader@17.4.2(vue@3.5.28(typescript@5.9.3))(webpack@5.105.0): dependencies: chalk: 4.1.2 hash-sum: 2.0.0 watchpack: 2.5.1 - webpack: 5.104.1(webpack-cli@6.0.1) + webpack: 5.105.0(webpack-cli@6.0.1) optionalDependencies: - vue: 3.5.27(typescript@5.9.3) + vue: 3.5.28(typescript@5.9.3) vue-tsc@3.2.4(typescript@5.9.3): dependencies: @@ -8335,13 +8375,13 @@ snapshots: '@vue/language-core': 3.2.4 typescript: 5.9.3 - vue@3.5.27(typescript@5.9.3): + vue@3.5.28(typescript@5.9.3): dependencies: - '@vue/compiler-dom': 3.5.27 - '@vue/compiler-sfc': 3.5.27 - '@vue/runtime-dom': 3.5.27 - '@vue/server-renderer': 3.5.27(vue@3.5.27(typescript@5.9.3)) - '@vue/shared': 3.5.27 + '@vue/compiler-dom': 3.5.28 + '@vue/compiler-sfc': 3.5.28 + '@vue/runtime-dom': 3.5.28 + '@vue/server-renderer': 3.5.28(vue@3.5.28(typescript@5.9.3)) + '@vue/shared': 3.5.28 optionalDependencies: typescript: 5.9.3 @@ -8352,12 +8392,12 @@ snapshots: webidl-conversions@3.0.1: {} - webpack-cli@6.0.1(webpack@5.104.1): + webpack-cli@6.0.1(webpack@5.105.0): dependencies: '@discoveryjs/json-ext': 0.6.3 - '@webpack-cli/configtest': 3.0.1(webpack-cli@6.0.1)(webpack@5.104.1) - '@webpack-cli/info': 3.0.1(webpack-cli@6.0.1)(webpack@5.104.1) - '@webpack-cli/serve': 3.0.1(webpack-cli@6.0.1)(webpack@5.104.1) + '@webpack-cli/configtest': 3.0.1(webpack-cli@6.0.1)(webpack@5.105.0) + '@webpack-cli/info': 3.0.1(webpack-cli@6.0.1)(webpack@5.105.0) + '@webpack-cli/serve': 3.0.1(webpack-cli@6.0.1)(webpack@5.105.0) colorette: 2.0.20 commander: 12.1.0 cross-spawn: 7.0.6 @@ -8366,7 +8406,7 @@ snapshots: import-local: 3.2.0 interpret: 3.1.1 rechoir: 0.8.0 - webpack: 5.104.1(webpack-cli@6.0.1) + webpack: 5.105.0(webpack-cli@6.0.1) webpack-merge: 6.0.1 webpack-merge@6.0.1: @@ -8382,7 +8422,7 @@ snapshots: webpack-sources@3.3.3: {} - webpack@5.104.1(webpack-cli@6.0.1): + webpack@5.105.0(webpack-cli@6.0.1): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -8394,7 +8434,7 @@ snapshots: acorn-import-phases: 1.0.4(acorn@8.15.0) browserslist: 4.28.1 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.18.4 + enhanced-resolve: 5.19.0 es-module-lexer: 2.0.0 eslint-scope: 5.1.1 events: 3.3.0 @@ -8406,11 +8446,11 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.16(webpack@5.104.1) + terser-webpack-plugin: 5.3.16(webpack@5.105.0) watchpack: 2.5.1 webpack-sources: 3.3.3 optionalDependencies: - webpack-cli: 6.0.1(webpack@5.104.1) + webpack-cli: 6.0.1(webpack@5.105.0) transitivePeerDependencies: - '@swc/core' - esbuild diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index f6ee5958b5..71fee23c92 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -117,7 +117,7 @@ func CommonRoutes() *web.Router { &auth.OAuth2{}, &auth.Basic{}, &nuget.Auth{}, - &conan.Auth{}, + &Auth{}, &chef.Auth{}, }) @@ -537,7 +537,8 @@ func ContainerRoutes() *web.Router { verifyAuth(r, []auth.Method{ &auth.Basic{}, - &container.Auth{}, + // container auth requires an token, so container.Authenticate issues a Ghost user token for anonymous access + &Auth{AllowGhostUser: true}, }) // TODO: Content Discovery / References (not implemented yet) diff --git a/routers/api/packages/conan/auth.go b/routers/api/packages/auth.go similarity index 60% rename from routers/api/packages/conan/auth.go rename to routers/api/packages/auth.go index bce3235a2e..b7bf381241 100644 --- a/routers/api/packages/conan/auth.go +++ b/routers/api/packages/auth.go @@ -1,7 +1,7 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. +// Copyright 2026 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package conan +package packages import ( "net/http" @@ -14,10 +14,13 @@ import ( var _ auth.Method = &Auth{} -type Auth struct{} +// Auth is for conan and container +type Auth struct { + AllowGhostUser bool +} func (a *Auth) Name() string { - return "conan" + return "packages" } // Verify extracts the user from the Bearer token @@ -32,10 +35,22 @@ func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataS return nil, nil } - u, err := user_model.GetUserByID(req.Context(), packageMeta.UserID) - if err != nil { - return nil, err + var u *user_model.User + switch packageMeta.UserID { + case user_model.GhostUserID: + if !a.AllowGhostUser { + return nil, nil + } + u = user_model.NewGhostUser() + case user_model.ActionsUserID: + u = user_model.NewActionsUserWithTaskID(packageMeta.ActionsUserTaskID) + default: + u, err = user_model.GetUserByID(req.Context(), packageMeta.UserID) + if err != nil { + return nil, err + } } + if packageMeta.Scope != "" { store.GetData()["IsApiToken"] = true store.GetData()["ApiTokenScope"] = packageMeta.Scope diff --git a/routers/api/packages/container/auth.go b/routers/api/packages/container/auth.go deleted file mode 100644 index 19a931c405..0000000000 --- a/routers/api/packages/container/auth.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package container - -import ( - "net/http" - - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/services/auth" - "code.gitea.io/gitea/services/packages" -) - -var _ auth.Method = &Auth{} - -type Auth struct{} - -func (a *Auth) Name() string { - return "container" -} - -// Verify extracts the user from the Bearer token -// If it's an anonymous session, a ghost user is returned -func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataStore, sess auth.SessionStore) (*user_model.User, error) { - packageMeta, err := packages.ParseAuthorizationRequest(req) - if err != nil { - log.Trace("ParseAuthorizationToken: %v", err) - return nil, err - } - - if packageMeta == nil || packageMeta.UserID == 0 { - return nil, nil - } - - u, err := user_model.GetPossibleUserByID(req.Context(), packageMeta.UserID) - if err != nil { - return nil, err - } - - if packageMeta.Scope != "" { - store.GetData()["IsApiToken"] = true - store.GetData()["ApiTokenScope"] = packageMeta.Scope - } - - return u, nil -} diff --git a/routers/api/packages/nuget/auth.go b/routers/api/packages/nuget/auth.go index ce7df0ce0a..8248453e74 100644 --- a/routers/api/packages/nuget/auth.go +++ b/routers/api/packages/nuget/auth.go @@ -6,43 +6,21 @@ package nuget import ( "net/http" - auth_model "code.gitea.io/gitea/models/auth" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/services/auth" ) var _ auth.Method = &Auth{} -type Auth struct{} +type Auth struct { + basicAuth auth.Basic +} func (a *Auth) Name() string { return "nuget" } -// https://docs.microsoft.com/en-us/nuget/api/package-publish-resource#request-parameters func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataStore, sess auth.SessionStore) (*user_model.User, error) { - token, err := auth_model.GetAccessTokenBySHA(req.Context(), req.Header.Get("X-NuGet-ApiKey")) - if err != nil { - if !(auth_model.IsErrAccessTokenNotExist(err) || auth_model.IsErrAccessTokenEmpty(err)) { - return nil, err - } - return nil, nil - } - - u, err := user_model.GetUserByID(req.Context(), token.UID) - if err != nil { - return nil, err - } - - token.UpdatedUnix = timeutil.TimeStampNow() - if err := auth_model.UpdateAccessToken(req.Context(), token); err != nil { - log.Error("UpdateAccessToken: %v", err) - } - - store.GetData()["IsApiToken"] = true - store.GetData()["ApiToken"] = token - - return u, nil + // ref: https://docs.microsoft.com/en-us/nuget/api/package-publish-resource#request-parameters + return a.basicAuth.VerifyAuthToken(req, w, store, sess, req.Header.Get("X-NuGet-ApiKey")) } diff --git a/routers/api/v1/admin/hooks.go b/routers/api/v1/admin/hooks.go index a687541be5..6170e7343a 100644 --- a/routers/api/v1/admin/hooks.go +++ b/routers/api/v1/admin/hooks.go @@ -57,8 +57,13 @@ func ListHooks(ctx *context.APIContext) { case "all": isSystemWebhook = optional.None[bool]() } + listOptions := utils.GetListOptions(ctx) + opts := &webhook.ListSystemWebhookOptions{ + ListOptions: listOptions, + IsSystem: isSystemWebhook, + } - sysHooks, err := webhook.GetSystemOrDefaultWebhooks(ctx, isSystemWebhook) + sysHooks, total, err := webhook.GetGlobalWebhooks(ctx, opts) if err != nil { ctx.APIErrorInternal(err) return @@ -72,6 +77,8 @@ func ListHooks(ctx *context.APIContext) { } hooks[i] = h } + ctx.SetLinkHeader(int(total), listOptions.PageSize) + ctx.SetTotalCountHeader(total) ctx.JSON(http.StatusOK, hooks) } diff --git a/routers/api/v1/notify/repo.go b/routers/api/v1/notify/repo.go index e87054e26c..51695a52c8 100644 --- a/routers/api/v1/notify/repo.go +++ b/routers/api/v1/notify/repo.go @@ -125,8 +125,8 @@ func ListRepoNotifications(ctx *context.APIContext) { return } + ctx.SetLinkHeader(int(totalCount), opts.PageSize) ctx.SetTotalCountHeader(totalCount) - ctx.JSON(http.StatusOK, convert.ToNotifications(ctx, nl)) } diff --git a/routers/api/v1/notify/user.go b/routers/api/v1/notify/user.go index 3ebb678835..82cedd418b 100644 --- a/routers/api/v1/notify/user.go +++ b/routers/api/v1/notify/user.go @@ -86,6 +86,7 @@ func ListNotifications(ctx *context.APIContext) { return } + ctx.SetLinkHeader(int(totalCount), opts.PageSize) ctx.SetTotalCountHeader(totalCount) ctx.JSON(http.StatusOK, convert.ToNotifications(ctx, nl)) } diff --git a/routers/api/v1/org/action.go b/routers/api/v1/org/action.go index 3ae5e60585..59d8d3f2b4 100644 --- a/routers/api/v1/org/action.go +++ b/routers/api/v1/org/action.go @@ -67,6 +67,7 @@ func (Action) ListActionsSecrets(ctx *context.APIContext) { } } + ctx.SetLinkHeader(int(count), opts.PageSize) ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, apiSecrets) } @@ -240,9 +241,10 @@ func (Action) ListVariables(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" + listOptions := utils.GetListOptions(ctx) vars, count, err := db.FindAndCount[actions_model.ActionVariable](ctx, &actions_model.FindVariablesOpts{ OwnerID: ctx.Org.Organization.ID, - ListOptions: utils.GetListOptions(ctx), + ListOptions: listOptions, }) if err != nil { ctx.APIErrorInternal(err) @@ -259,7 +261,7 @@ func (Action) ListVariables(ctx *context.APIContext) { Description: v.Description, } } - + ctx.SetLinkHeader(int(count), listOptions.PageSize) ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, variables) } diff --git a/routers/api/v1/org/member.go b/routers/api/v1/org/member.go index 1c12b0cc94..b72cafee0c 100644 --- a/routers/api/v1/org/member.go +++ b/routers/api/v1/org/member.go @@ -20,11 +20,12 @@ import ( // listMembers list an organization's members func listMembers(ctx *context.APIContext, isMember bool) { + listOptions := utils.GetListOptions(ctx) opts := &organization.FindOrgMembersOpts{ Doer: ctx.Doer, IsDoerMember: isMember, OrgID: ctx.Org.Organization.ID, - ListOptions: utils.GetListOptions(ctx), + ListOptions: listOptions, } count, err := organization.CountOrgMembers(ctx, opts) @@ -44,6 +45,7 @@ func listMembers(ctx *context.APIContext, isMember bool) { apiMembers[i] = convert.ToUser(ctx, member, ctx.Doer) } + ctx.SetLinkHeader(int(count), listOptions.PageSize) ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, apiMembers) } diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index 1a1710750a..7d43db1e9b 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -54,8 +54,9 @@ func ListTeams(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" + listOptions := utils.GetListOptions(ctx) teams, count, err := organization.SearchTeam(ctx, &organization.SearchTeamOptions{ - ListOptions: utils.GetListOptions(ctx), + ListOptions: listOptions, OrgID: ctx.Org.Organization.ID, }) if err != nil { @@ -69,6 +70,7 @@ func ListTeams(ctx *context.APIContext) { return } + ctx.SetLinkHeader(int(count), listOptions.PageSize) ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, apiTeams) } @@ -93,8 +95,9 @@ func ListUserTeams(ctx *context.APIContext) { // "200": // "$ref": "#/responses/TeamList" + listOptions := utils.GetListOptions(ctx) teams, count, err := organization.SearchTeam(ctx, &organization.SearchTeamOptions{ - ListOptions: utils.GetListOptions(ctx), + ListOptions: listOptions, UserID: ctx.Doer.ID, }) if err != nil { @@ -108,6 +111,7 @@ func ListUserTeams(ctx *context.APIContext) { return } + ctx.SetLinkHeader(int(count), listOptions.PageSize) ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, apiTeams) } @@ -392,8 +396,9 @@ func GetTeamMembers(ctx *context.APIContext) { return } + listOptions := utils.GetListOptions(ctx) teamMembers, err := organization.GetTeamMembers(ctx, &organization.SearchMembersOptions{ - ListOptions: utils.GetListOptions(ctx), + ListOptions: listOptions, TeamID: ctx.Org.Team.ID, }) if err != nil { @@ -406,6 +411,7 @@ func GetTeamMembers(ctx *context.APIContext) { members[i] = convert.ToUser(ctx, member, ctx.Doer) } + ctx.SetLinkHeader(ctx.Org.Team.NumMembers, listOptions.PageSize) ctx.SetTotalCountHeader(int64(ctx.Org.Team.NumMembers)) ctx.JSON(http.StatusOK, members) } @@ -559,8 +565,9 @@ func GetTeamRepos(ctx *context.APIContext) { // "$ref": "#/responses/notFound" team := ctx.Org.Team + listOptions := utils.GetListOptions(ctx) teamRepos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{ - ListOptions: utils.GetListOptions(ctx), + ListOptions: listOptions, TeamID: team.ID, }) if err != nil { @@ -576,6 +583,7 @@ func GetTeamRepos(ctx *context.APIContext) { } repos[i] = convert.ToRepo(ctx, repo, permission) } + ctx.SetLinkHeader(team.NumRepos, listOptions.PageSize) ctx.SetTotalCountHeader(int64(team.NumRepos)) ctx.JSON(http.StatusOK, repos) } @@ -874,7 +882,7 @@ func ListTeamActivityFeeds(ctx *context.APIContext) { ctx.APIErrorInternal(err) return } + ctx.SetLinkHeader(int(count), listOptions.PageSize) ctx.SetTotalCountHeader(count) - ctx.JSON(http.StatusOK, convert.ToActivities(ctx, feeds, ctx.Doer)) } diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go index 25aabe6dd2..03ce0d3aab 100644 --- a/routers/api/v1/repo/action.go +++ b/routers/api/v1/repo/action.go @@ -69,10 +69,11 @@ func (Action) ListActionsSecrets(ctx *context.APIContext) { // "$ref": "#/responses/notFound" repo := ctx.Repo.Repository + listOptions := utils.GetListOptions(ctx) opts := &secret_model.FindSecretsOptions{ RepoID: repo.ID, - ListOptions: utils.GetListOptions(ctx), + ListOptions: listOptions, } secrets, count, err := db.FindAndCount[secret_model.Secret](ctx, opts) @@ -89,7 +90,7 @@ func (Action) ListActionsSecrets(ctx *context.APIContext) { Created: v.CreatedUnix.AsTime(), } } - + ctx.SetLinkHeader(int(count), listOptions.PageSize) ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, apiSecrets) } @@ -482,9 +483,11 @@ func (Action) ListVariables(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" + listOptions := utils.GetListOptions(ctx) + vars, count, err := db.FindAndCount[actions_model.ActionVariable](ctx, &actions_model.FindVariablesOpts{ RepoID: ctx.Repo.Repository.ID, - ListOptions: utils.GetListOptions(ctx), + ListOptions: listOptions, }) if err != nil { ctx.APIErrorInternal(err) @@ -502,6 +505,7 @@ func (Action) ListVariables(ctx *context.APIContext) { } } + ctx.SetLinkHeader(int(count), listOptions.PageSize) ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, variables) } @@ -807,9 +811,10 @@ func ListActionTasks(ctx *context.APIContext) { // "$ref": "#/responses/conflict" // "422": // "$ref": "#/responses/validationError" + listOptions := utils.GetListOptions(ctx) tasks, total, err := db.FindAndCount[actions_model.ActionTask](ctx, &actions_model.FindTaskOptions{ - ListOptions: utils.GetListOptions(ctx), + ListOptions: listOptions, RepoID: ctx.Repo.Repository.ID, }) if err != nil { @@ -830,6 +835,8 @@ func ListActionTasks(ctx *context.APIContext) { res.Entries[i] = convertedTask } + ctx.SetLinkHeader(int(total), listOptions.PageSize) + ctx.SetTotalCountHeader(total) // Duplicates api response field but it's better to set it for consistency ctx.JSON(http.StatusOK, &res) } diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index 9bdc0c76b8..82fd68bdec 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -155,7 +155,7 @@ func DeleteBranch(ctx *context.APIContext) { case git.IsErrBranchNotExist(err): ctx.APIErrorNotFound(err) case errors.Is(err, repo_service.ErrBranchIsDefault): - ctx.APIError(http.StatusForbidden, errors.New("can not delete default branch")) + ctx.APIError(http.StatusForbidden, errors.New("can not delete default or pull request target branch")) case errors.Is(err, git_model.ErrBranchIsProtected): ctx.APIError(http.StatusForbidden, errors.New("branch protected")) default: diff --git a/routers/api/v1/repo/issue_dependency.go b/routers/api/v1/repo/issue_dependency.go index b34e325e5d..6c66e719eb 100644 --- a/routers/api/v1/repo/issue_dependency.go +++ b/routers/api/v1/repo/issue_dependency.go @@ -7,13 +7,13 @@ package repo import ( "net/http" - "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) @@ -77,23 +77,14 @@ func GetIssueDependencies(ctx *context.APIContext) { return } - page := max(ctx.FormInt("page"), 1) - limit := ctx.FormInt("limit") - if limit == 0 { - limit = setting.API.DefaultPagingNum - } else if limit > setting.API.MaxResponseItems { - limit = setting.API.MaxResponseItems - } + listOptions := utils.GetListOptions(ctx) canWrite := ctx.Repo.Permission.CanWriteIssuesOrPulls(issue.IsPull) - blockerIssues := make([]*issues_model.Issue, 0, limit) + blockerIssues := make([]*issues_model.Issue, 0, listOptions.PageSize) // 2. Get the issues this issue depends on, i.e. the `<#b>`: ` <- <#b>` - blockersInfo, err := issue.BlockedByDependencies(ctx, db.ListOptions{ - Page: page, - PageSize: limit, - }) + blockersInfo, total, err := issue.BlockedByDependencies(ctx, listOptions) if err != nil { ctx.APIErrorInternal(err) return @@ -149,7 +140,8 @@ func GetIssueDependencies(ctx *context.APIContext) { } blockerIssues = append(blockerIssues, &blocker.Issue) } - + ctx.SetLinkHeader(int(total), listOptions.PageSize) + ctx.SetTotalCountHeader(total) ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, blockerIssues)) } diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 2f59bef7c7..e6f4dd62ce 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1138,7 +1138,7 @@ func parseCompareInfo(ctx *context.APIContext, compareParam string) (result *git return nil, nil } - baseRef := ctx.Repo.GitRepo.UnstableGuessRefByShortName(util.IfZero(compareReq.BaseOriRef, baseRepo.DefaultBranch)) + baseRef := ctx.Repo.GitRepo.UnstableGuessRefByShortName(util.IfZero(compareReq.BaseOriRef, baseRepo.GetPullRequestTargetBranch(ctx))) headRef := headGitRepo.UnstableGuessRefByShortName(util.IfZero(compareReq.HeadOriRef, headRepo.DefaultBranch)) log.Trace("Repo path: %q, base ref: %q->%q, head ref: %q->%q", ctx.Repo.Repository.RelativePath(), compareReq.BaseOriRef, baseRef, compareReq.HeadOriRef, headRef) diff --git a/routers/api/v1/repo/status.go b/routers/api/v1/repo/status.go index d632d5b5e1..e69d4468de 100644 --- a/routers/api/v1/repo/status.go +++ b/routers/api/v1/repo/status.go @@ -257,8 +257,8 @@ func GetCombinedCommitStatusByRef(ctx *context.APIContext) { } repo := ctx.Repo.Repository - - statuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, refCommit.Commit.ID.String(), utils.GetListOptions(ctx)) + listOptions := utils.GetListOptions(ctx) + statuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, refCommit.Commit.ID.String(), listOptions) if err != nil { ctx.APIErrorInternal(fmt.Errorf("GetLatestCommitStatus[%s, %s]: %w", repo.FullName(), refCommit.CommitID, err)) return @@ -269,6 +269,7 @@ func GetCombinedCommitStatusByRef(ctx *context.APIContext) { ctx.APIErrorInternal(fmt.Errorf("CountLatestCommitStatus[%s, %s]: %w", repo.FullName(), refCommit.CommitID, err)) return } + ctx.SetLinkHeader(int(count), listOptions.PageSize) ctx.SetTotalCountHeader(count) combiStatus := convert.ToCombinedStatus(ctx, refCommit.Commit.ID.String(), statuses, diff --git a/routers/api/v1/repo/wiki.go b/routers/api/v1/repo/wiki.go index baf5e0189f..90dd08394e 100644 --- a/routers/api/v1/repo/wiki.go +++ b/routers/api/v1/repo/wiki.go @@ -333,6 +333,7 @@ func ListWikiPages(ctx *context.APIContext) { pages = append(pages, wiki_service.ToWikiPageMetaData(wikiName, c, ctx.Repo.Repository)) } + ctx.SetLinkHeader(len(entries), limit) ctx.SetTotalCountHeader(int64(len(entries))) ctx.JSON(http.StatusOK, pages) } @@ -445,6 +446,7 @@ func ListPageRevisions(ctx *context.APIContext) { return } + // FIXME: SetLinkHeader missing ctx.SetTotalCountHeader(commitsCount) ctx.JSON(http.StatusOK, convert.ToWikiCommitList(commitsHistory, commitsCount)) } diff --git a/routers/api/v1/shared/action.go b/routers/api/v1/shared/action.go index c97e9419fd..108fca787b 100644 --- a/routers/api/v1/shared/action.go +++ b/routers/api/v1/shared/action.go @@ -32,11 +32,12 @@ func ListJobs(ctx *context.APIContext, ownerID, repoID, runID int64) { if ownerID != 0 && repoID != 0 { setting.PanicInDevOrTesting("ownerID and repoID should not be both set") } + listOptions := utils.GetListOptions(ctx) opts := actions_model.FindRunJobOptions{ OwnerID: ownerID, RepoID: repoID, RunID: runID, - ListOptions: utils.GetListOptions(ctx), + ListOptions: listOptions, } for _, status := range ctx.FormStrings("status") { values, err := convertToInternal(status) @@ -78,7 +79,8 @@ func ListJobs(ctx *context.APIContext, ownerID, repoID, runID int64) { } res.Entries[i] = convertedWorkflowJob } - + ctx.SetLinkHeader(int(total), listOptions.PageSize) + ctx.SetTotalCountHeader(total) ctx.JSON(http.StatusOK, &res) } @@ -120,10 +122,11 @@ func ListRuns(ctx *context.APIContext, ownerID, repoID int64) { if ownerID != 0 && repoID != 0 { setting.PanicInDevOrTesting("ownerID and repoID should not be both set") } + listOptions := utils.GetListOptions(ctx) opts := actions_model.FindRunOptions{ OwnerID: ownerID, RepoID: repoID, - ListOptions: utils.GetListOptions(ctx), + ListOptions: listOptions, } if event := ctx.FormString("event"); event != "" { @@ -182,6 +185,7 @@ func ListRuns(ctx *context.APIContext, ownerID, repoID int64) { } res.Entries[i] = convertedRun } - + ctx.SetLinkHeader(int(total), listOptions.PageSize) + ctx.SetTotalCountHeader(total) ctx.JSON(http.StatusOK, &res) } diff --git a/routers/api/v1/shared/block.go b/routers/api/v1/shared/block.go index b22f8a74fd..19ad552e20 100644 --- a/routers/api/v1/shared/block.go +++ b/routers/api/v1/shared/block.go @@ -16,8 +16,9 @@ import ( ) func ListBlocks(ctx *context.APIContext, blocker *user_model.User) { + listOptions := utils.GetListOptions(ctx) blocks, total, err := user_model.FindBlockings(ctx, &user_model.FindBlockingOptions{ - ListOptions: utils.GetListOptions(ctx), + ListOptions: listOptions, BlockerID: blocker.ID, }) if err != nil { @@ -35,6 +36,7 @@ func ListBlocks(ctx *context.APIContext, blocker *user_model.User) { users = append(users, convert.ToUser(ctx, b.Blockee, blocker)) } + ctx.SetLinkHeader(int(total), listOptions.PageSize) ctx.SetTotalCountHeader(total) ctx.JSON(http.StatusOK, &users) } diff --git a/routers/api/v1/user/action.go b/routers/api/v1/user/action.go index e934d02aa7..069d5e39b6 100644 --- a/routers/api/v1/user/action.go +++ b/routers/api/v1/user/action.go @@ -333,10 +333,10 @@ func ListVariables(ctx *context.APIContext) { // "$ref": "#/responses/error" // "404": // "$ref": "#/responses/notFound" - + listOptions := utils.GetListOptions(ctx) vars, count, err := db.FindAndCount[actions_model.ActionVariable](ctx, &actions_model.FindVariablesOpts{ OwnerID: ctx.Doer.ID, - ListOptions: utils.GetListOptions(ctx), + ListOptions: listOptions, }) if err != nil { ctx.APIErrorInternal(err) @@ -354,6 +354,7 @@ func ListVariables(ctx *context.APIContext) { } } + ctx.SetLinkHeader(int(count), listOptions.PageSize) ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, variables) } diff --git a/routers/api/v1/user/follower.go b/routers/api/v1/user/follower.go index 339b994af4..48c624ced9 100644 --- a/routers/api/v1/user/follower.go +++ b/routers/api/v1/user/follower.go @@ -24,12 +24,14 @@ func responseAPIUsers(ctx *context.APIContext, users []*user_model.User) { } func listUserFollowers(ctx *context.APIContext, u *user_model.User) { - users, count, err := user_model.GetUserFollowers(ctx, u, ctx.Doer, utils.GetListOptions(ctx)) + listOptions := utils.GetListOptions(ctx) + users, count, err := user_model.GetUserFollowers(ctx, u, ctx.Doer, listOptions) if err != nil { ctx.APIErrorInternal(err) return } + ctx.SetLinkHeader(int(count), listOptions.PageSize) ctx.SetTotalCountHeader(count) responseAPIUsers(ctx, users) } @@ -88,12 +90,14 @@ func ListFollowers(ctx *context.APIContext) { } func listUserFollowing(ctx *context.APIContext, u *user_model.User) { - users, count, err := user_model.GetUserFollowing(ctx, u, ctx.Doer, utils.GetListOptions(ctx)) + listOptions := utils.GetListOptions(ctx) + users, count, err := user_model.GetUserFollowing(ctx, u, ctx.Doer, listOptions) if err != nil { ctx.APIErrorInternal(err) return } + ctx.SetLinkHeader(int(count), listOptions.PageSize) ctx.SetTotalCountHeader(count) responseAPIUsers(ctx, users) } diff --git a/routers/api/v1/user/key.go b/routers/api/v1/user/key.go index 08aa182ca1..de0ac7b1e4 100644 --- a/routers/api/v1/user/key.go +++ b/routers/api/v1/user/key.go @@ -1,4 +1,5 @@ // Copyright 2015 The Gogs Authors. All rights reserved. +// Copyright 2026 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT package user @@ -53,11 +54,11 @@ func composePublicKeysAPILink() string { func listPublicKeys(ctx *context.APIContext, user *user_model.User) { var keys []*asymkey_model.PublicKey var err error - var count int + var count int64 fingerprint := ctx.FormString("fingerprint") username := ctx.PathParam("username") - + listOptions := utils.GetListOptions(ctx) if fingerprint != "" { var userID int64 // Unrestricted // Querying not just listing @@ -65,20 +66,18 @@ func listPublicKeys(ctx *context.APIContext, user *user_model.User) { // Restrict to provided uid userID = user.ID } - keys, err = db.Find[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{ + keys, count, err = db.FindAndCount[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{ + ListOptions: listOptions, OwnerID: userID, Fingerprint: fingerprint, }) - count = len(keys) } else { - var total int64 // Use ListPublicKeys - keys, total, err = db.FindAndCount[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{ - ListOptions: utils.GetListOptions(ctx), + keys, count, err = db.FindAndCount[asymkey_model.PublicKey](ctx, asymkey_model.FindPublicKeyOptions{ + ListOptions: listOptions, OwnerID: user.ID, NotKeytype: asymkey_model.KeyTypePrincipal, }) - count = int(total) } if err != nil { @@ -95,7 +94,8 @@ func listPublicKeys(ctx *context.APIContext, user *user_model.User) { } } - ctx.SetTotalCountHeader(int64(count)) + ctx.SetLinkHeader(int(count), listOptions.PageSize) + ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, &apiKeys) } diff --git a/routers/api/v1/user/star.go b/routers/api/v1/user/star.go index ee5d63063b..5c0d976527 100644 --- a/routers/api/v1/user/star.go +++ b/routers/api/v1/user/star.go @@ -76,6 +76,7 @@ func GetStarredRepos(ctx *context.APIContext) { return } + ctx.SetLinkHeader(ctx.ContextUser.NumStars, utils.GetListOptions(ctx).PageSize) ctx.SetTotalCountHeader(int64(ctx.ContextUser.NumStars)) ctx.JSON(http.StatusOK, &repos) } @@ -107,6 +108,7 @@ func GetMyStarredRepos(ctx *context.APIContext) { ctx.APIErrorInternal(err) } + ctx.SetLinkHeader(ctx.Doer.NumStars, utils.GetListOptions(ctx).PageSize) ctx.SetTotalCountHeader(int64(ctx.Doer.NumStars)) ctx.JSON(http.StatusOK, &repos) } diff --git a/routers/api/v1/user/watch.go b/routers/api/v1/user/watch.go index 844eac2c67..1ce0f3f529 100644 --- a/routers/api/v1/user/watch.go +++ b/routers/api/v1/user/watch.go @@ -71,6 +71,7 @@ func GetWatchedRepos(ctx *context.APIContext) { ctx.APIErrorInternal(err) } + ctx.SetLinkHeader(int(total), utils.GetListOptions(ctx).PageSize) ctx.SetTotalCountHeader(total) ctx.JSON(http.StatusOK, &repos) } @@ -99,7 +100,7 @@ func GetMyWatchedRepos(ctx *context.APIContext) { if err != nil { ctx.APIErrorInternal(err) } - + ctx.SetLinkHeader(int(total), utils.GetListOptions(ctx).PageSize) ctx.SetTotalCountHeader(total) ctx.JSON(http.StatusOK, &repos) } diff --git a/routers/api/v1/utils/hook.go b/routers/api/v1/utils/hook.go index 6f598f14c8..9f0447a80b 100644 --- a/routers/api/v1/utils/hook.go +++ b/routers/api/v1/utils/hook.go @@ -23,8 +23,9 @@ import ( // ListOwnerHooks lists the webhooks of the provided owner func ListOwnerHooks(ctx *context.APIContext, owner *user_model.User) { + listOptions := GetListOptions(ctx) opts := &webhook.ListWebhookOptions{ - ListOptions: GetListOptions(ctx), + ListOptions: listOptions, OwnerID: owner.ID, } @@ -42,7 +43,7 @@ func ListOwnerHooks(ctx *context.APIContext, owner *user_model.User) { return } } - + ctx.SetLinkHeader(int(count), listOptions.PageSize) ctx.SetTotalCountHeader(count) ctx.JSON(http.StatusOK, apiHooks) } diff --git a/routers/api/v1/utils/page.go b/routers/api/v1/utils/page.go index 024ba7b8d9..2c7b64967a 100644 --- a/routers/api/v1/utils/page.go +++ b/routers/api/v1/utils/page.go @@ -12,7 +12,7 @@ import ( // GetListOptions returns list options using the page and limit parameters func GetListOptions(ctx *context.APIContext) db.ListOptions { return db.ListOptions{ - Page: ctx.FormInt("page"), + Page: max(ctx.FormInt("page"), 1), PageSize: convert.ToCorrectPageSize(ctx.FormInt("limit")), } } diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go index 7c59132841..a5c379e01a 100644 --- a/routers/web/feed/convert.go +++ b/routers/web/feed/convert.go @@ -158,16 +158,16 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio if link.Href == "#" { link.Href = srcLink } - titleExtra = ctx.Locale.Tr("action.mirror_sync_push", act.GetRepoAbsoluteLink(ctx), srcLink, act.GetBranch(), act.ShortRepoPath(ctx)) + titleExtra = ctx.Locale.Tr("action.mirror_sync_push", act.GetRepoAbsoluteLink(ctx), srcLink, act.RefName, act.ShortRepoPath(ctx)) case activities_model.ActionMirrorSyncCreate: srcLink := toSrcLink(ctx, act) if link.Href == "#" { link.Href = srcLink } - titleExtra = ctx.Locale.Tr("action.mirror_sync_create", act.GetRepoAbsoluteLink(ctx), srcLink, act.GetBranch(), act.ShortRepoPath(ctx)) + titleExtra = ctx.Locale.Tr("action.mirror_sync_create", act.GetRepoAbsoluteLink(ctx), srcLink, act.RefName, act.ShortRepoPath(ctx)) case activities_model.ActionMirrorSyncDelete: link.Href = act.GetRepoAbsoluteLink(ctx) - titleExtra = ctx.Locale.Tr("action.mirror_sync_delete", act.GetRepoAbsoluteLink(ctx), act.GetBranch(), act.ShortRepoPath(ctx)) + titleExtra = ctx.Locale.Tr("action.mirror_sync_delete", act.GetRepoAbsoluteLink(ctx), act.RefName, act.ShortRepoPath(ctx)) case activities_model.ActionApprovePullRequest: pullLink := toPullLink(ctx, act) titleExtra = ctx.Locale.Tr("action.approve_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(ctx)) diff --git a/routers/web/repo/attachment.go b/routers/web/repo/attachment.go index ae52eb2ffa..bc14e42543 100644 --- a/routers/web/repo/attachment.go +++ b/routers/web/repo/attachment.go @@ -14,7 +14,6 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/common" "code.gitea.io/gitea/services/attachment" "code.gitea.io/gitea/services/context" @@ -200,7 +199,7 @@ func ServeAttachment(ctx *context.Context, uuid string) { } defer fr.Close() - common.ServeContentByReadSeeker(ctx.Base, attach.Name, util.ToPointer(attach.CreatedUnix.AsTime()), fr) + common.ServeContentByReadSeeker(ctx.Base, attach.Name, new(attach.CreatedUnix.AsTime()), fr) } // GetAttachment serve attachments diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go index 2b0ba9072d..f563035600 100644 --- a/routers/web/repo/branch.go +++ b/routers/web/repo/branch.go @@ -41,6 +41,7 @@ func Branches(ctx *context.Context) { ctx.Data["AllowsPulls"] = ctx.Repo.Repository.AllowsPulls(ctx) ctx.Data["IsWriter"] = ctx.Repo.CanWrite(unit.TypeCode) ctx.Data["IsMirror"] = ctx.Repo.Repository.IsMirror + // TODO: Can be replaced by ctx.Repo.PullRequestCtx.CanCreateNewPull() ctx.Data["CanPull"] = ctx.Repo.CanWrite(unit.TypeCode) || (ctx.IsSigned && repo_model.HasForkedRepo(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)) ctx.Data["PageIsViewCode"] = true diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 150a8583c8..1e2486f5f1 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -4,7 +4,6 @@ package repo import ( - "bufio" gocontext "context" "encoding/csv" "errors" @@ -247,7 +246,7 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { } // 4 get base and head refs - baseRefName := util.IfZero(compareReq.BaseOriRef, baseRepo.DefaultBranch) + baseRefName := util.IfZero(compareReq.BaseOriRef, baseRepo.GetPullRequestTargetBranch(ctx)) headRefName := util.IfZero(compareReq.HeadOriRef, headRepo.DefaultBranch) baseRef := ctx.Repo.GitRepo.UnstableGuessRefByShortName(baseRefName) @@ -276,10 +275,10 @@ func ParseCompareInfo(ctx *context.Context) *git_service.CompareInfo { ctx.Data["BaseBranch"] = baseRef.ShortName() // for legacy templates ctx.Data["HeadUser"] = headOwner ctx.Data["HeadBranch"] = headRef.ShortName() // for legacy templates - ctx.Repo.PullRequest.SameRepo = isSameRepo - ctx.Data["IsPull"] = true + context.InitRepoPullRequestCtx(ctx, baseRepo, headRepo) + // The current base and head repositories and branches may not // actually be the intended branches that the user wants to // create a pull-request from - but also determining the head @@ -744,13 +743,16 @@ func attachHiddenCommentIDs(section *gitdiff.DiffSection, lineComments map[int64 // ExcerptBlob render blob excerpt contents func ExcerptBlob(ctx *context.Context) { commitID := ctx.PathParam("sha") - lastLeft := ctx.FormInt("last_left") - lastRight := ctx.FormInt("last_right") - idxLeft := ctx.FormInt("left") - idxRight := ctx.FormInt("right") - leftHunkSize := ctx.FormInt("left_hunk_size") - rightHunkSize := ctx.FormInt("right_hunk_size") - direction := ctx.FormString("direction") + opts := gitdiff.BlobExcerptOptions{ + LastLeft: ctx.FormInt("last_left"), + LastRight: ctx.FormInt("last_right"), + LeftIndex: ctx.FormInt("left"), + RightIndex: ctx.FormInt("right"), + LeftHunkSize: ctx.FormInt("left_hunk_size"), + RightHunkSize: ctx.FormInt("right_hunk_size"), + Direction: ctx.FormString("direction"), + Language: ctx.FormString("filelang"), + } filePath := ctx.FormString("path") gitRepo := ctx.Repo.GitRepo @@ -770,61 +772,27 @@ func ExcerptBlob(ctx *context.Context) { diffBlobExcerptData.BaseLink = ctx.Repo.RepoLink + "/wiki/blob_excerpt" } - chunkSize := gitdiff.BlobExcerptChunkSize commit, err := gitRepo.GetCommit(commitID) if err != nil { - ctx.HTTPError(http.StatusInternalServerError, "GetCommit") + ctx.ServerError("GetCommit", err) return } - section := &gitdiff.DiffSection{ - FileName: filePath, - } - if direction == "up" && (idxLeft-lastLeft) > chunkSize { - idxLeft -= chunkSize - idxRight -= chunkSize - leftHunkSize += chunkSize - rightHunkSize += chunkSize - section.Lines, err = getExcerptLines(commit, filePath, idxLeft-1, idxRight-1, chunkSize) - } else if direction == "down" && (idxLeft-lastLeft) > chunkSize { - section.Lines, err = getExcerptLines(commit, filePath, lastLeft, lastRight, chunkSize) - lastLeft += chunkSize - lastRight += chunkSize - } else { - offset := -1 - if direction == "down" { - offset = 0 - } - section.Lines, err = getExcerptLines(commit, filePath, lastLeft, lastRight, idxRight-lastRight+offset) - leftHunkSize = 0 - rightHunkSize = 0 - idxLeft = lastLeft - idxRight = lastRight - } + blob, err := commit.Tree.GetBlobByPath(filePath) if err != nil { - ctx.HTTPError(http.StatusInternalServerError, "getExcerptLines") + ctx.ServerError("GetBlobByPath", err) return } - - newLineSection := &gitdiff.DiffLine{ - Type: gitdiff.DiffLineSection, - SectionInfo: &gitdiff.DiffLineSectionInfo{ - Path: filePath, - LastLeftIdx: lastLeft, - LastRightIdx: lastRight, - LeftIdx: idxLeft, - RightIdx: idxRight, - LeftHunkSize: leftHunkSize, - RightHunkSize: rightHunkSize, - }, + reader, err := blob.DataAsync() + if err != nil { + ctx.ServerError("DataAsync", err) + return } - if newLineSection.GetExpandDirection() != "" { - newLineSection.Content = fmt.Sprintf("@@ -%d,%d +%d,%d @@\n", idxLeft, leftHunkSize, idxRight, rightHunkSize) - switch direction { - case "up": - section.Lines = append([]*gitdiff.DiffLine{newLineSection}, section.Lines...) - case "down": - section.Lines = append(section.Lines, newLineSection) - } + defer reader.Close() + + section, err := gitdiff.BuildBlobExcerptDiffSection(filePath, reader, opts) + if err != nil { + ctx.ServerError("BuildBlobExcerptDiffSection", err) + return } diffBlobExcerptData.PullIssueIndex = ctx.FormInt64("pull_issue_index") @@ -865,37 +833,3 @@ func ExcerptBlob(ctx *context.Context) { ctx.HTML(http.StatusOK, tplBlobExcerpt) } - -func getExcerptLines(commit *git.Commit, filePath string, idxLeft, idxRight, chunkSize int) ([]*gitdiff.DiffLine, error) { - blob, err := commit.Tree.GetBlobByPath(filePath) - if err != nil { - return nil, err - } - reader, err := blob.DataAsync() - if err != nil { - return nil, err - } - defer reader.Close() - scanner := bufio.NewScanner(reader) - var diffLines []*gitdiff.DiffLine - for line := 0; line < idxRight+chunkSize; line++ { - if ok := scanner.Scan(); !ok { - break - } - if line < idxRight { - continue - } - lineText := scanner.Text() - diffLine := &gitdiff.DiffLine{ - LeftIdx: idxLeft + (line - idxRight) + 1, - RightIdx: line + 1, - Type: gitdiff.DiffLinePlain, - Content: " " + lineText, - } - diffLines = append(diffLines, diffLine) - } - if err = scanner.Err(); err != nil { - return nil, fmt.Errorf("getExcerptLines scan: %w", err) - } - return diffLines, nil -} diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index 048c9f3d4a..171ccd7719 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -18,7 +18,6 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/util" @@ -78,8 +77,6 @@ func prepareEditorPageFormOptions(ctx *context.Context, editorAction string) *co ctx.Data["CommitFormOptions"] = commitFormOptions // for online editor - ctx.Data["PreviewableExtensions"] = strings.Join(markup.PreviewableExtensions(), ",") - ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",") ctx.Data["IsEditingFileOnly"] = ctx.FormString("return_uri") != "" ctx.Data["ReturnURI"] = ctx.FormString("return_uri") @@ -321,7 +318,7 @@ func EditFile(ctx *context.Context) { } } - ctx.Data["EditorconfigJson"] = getContextRepoEditorConfig(ctx, ctx.Repo.TreePath) + ctx.Data["CodeEditorConfig"] = getCodeEditorConfig(ctx, ctx.Repo.TreePath) ctx.HTML(http.StatusOK, tplEditFile) } diff --git a/routers/web/repo/editor_apply_patch.go b/routers/web/repo/editor_apply_patch.go index 357c6f3a21..1a01bfd5cb 100644 --- a/routers/web/repo/editor_apply_patch.go +++ b/routers/web/repo/editor_apply_patch.go @@ -20,6 +20,7 @@ func NewDiffPatch(ctx *context.Context) { } ctx.Data["PageIsPatch"] = true + ctx.Data["CodeEditorConfig"] = CodeEditorConfig{} // not really editing a file, so no need to fill in the config ctx.HTML(http.StatusOK, tplPatchFile) } diff --git a/routers/web/repo/editor_preview.go b/routers/web/repo/editor_preview.go index 14be5b72b6..ec1f41a013 100644 --- a/routers/web/repo/editor_preview.go +++ b/routers/web/repo/editor_preview.go @@ -6,12 +6,13 @@ package repo import ( "net/http" + "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/services/context" files_service "code.gitea.io/gitea/services/repository/files" ) func DiffPreviewPost(ctx *context.Context) { - content := ctx.FormString("content") + newContent := ctx.FormString("content") treePath := files_service.CleanGitTreePath(ctx.Repo.TreePath) if treePath == "" { ctx.HTTPError(http.StatusBadRequest, "file name to diff is invalid") @@ -27,7 +28,12 @@ func DiffPreviewPost(ctx *context.Context) { return } - diff, err := files_service.GetDiffPreview(ctx, ctx.Repo.Repository, ctx.Repo.BranchName, treePath, content) + oldContent, err := entry.Blob().GetBlobContent(setting.UI.MaxDisplayFileSize) + if err != nil { + ctx.ServerError("GetBlobContent", err) + return + } + diff, err := files_service.GetDiffPreview(ctx, ctx.Repo.Repository, ctx.Repo.BranchName, treePath, oldContent, newContent) if err != nil { ctx.ServerError("GetDiffPreview", err) return diff --git a/routers/web/repo/editor_util.go b/routers/web/repo/editor_util.go index 07bcb474f0..aca732ac70 100644 --- a/routers/web/repo/editor_util.go +++ b/routers/web/repo/editor_util.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "path" + "strconv" "strings" git_model "code.gitea.io/gitea/models/git" @@ -14,9 +15,11 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" - "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/markup" repo_module "code.gitea.io/gitea/modules/repository" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" context_service "code.gitea.io/gitea/services/context" ) @@ -62,17 +65,33 @@ func getClosestParentWithFiles(gitRepo *git.Repository, branchName, originTreePa return f(originTreePath, commit) } -// getContextRepoEditorConfig returns the editorconfig JSON string for given treePath or "null" -func getContextRepoEditorConfig(ctx *context_service.Context, treePath string) string { +// CodeEditorConfig is also used by frontend, defined in "codeeditor.ts" +type CodeEditorConfig struct { + PreviewableExtensions []string `json:"previewable_extensions"` + LineWrapExtensions []string `json:"line_wrap_extensions"` + LineWrapOn bool `json:"line_wrap_on"` + + IndentStyle string `json:"indent_style"` + IndentSize int `json:"indent_size"` + TabWidth int `json:"tab_width"` + TrimTrailingWhitespace *bool `json:"trim_trailing_whitespace,omitempty"` +} + +func getCodeEditorConfig(ctx *context_service.Context, treePath string) (ret CodeEditorConfig) { + ret.PreviewableExtensions = markup.PreviewableExtensions() + ret.LineWrapExtensions = setting.Repository.Editor.LineWrapExtensions + ret.LineWrapOn = util.SliceContainsString(ret.LineWrapExtensions, path.Ext(treePath), true) ec, _, err := ctx.Repo.GetEditorconfig() if err == nil { def, err := ec.GetDefinitionForFilename(treePath) if err == nil { - jsonStr, _ := json.Marshal(def) - return string(jsonStr) + ret.IndentStyle = def.IndentStyle + ret.IndentSize, _ = strconv.Atoi(def.IndentSize) + ret.TabWidth = def.TabWidth + ret.TrimTrailingWhitespace = def.TrimTrailingWhitespace } } - return "null" + return ret } // getParentTreeFields returns list of parent tree names and corresponding tree paths based on given treePath. diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 54b7e5df2a..eaec3b5789 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -109,11 +109,6 @@ func MustAllowPulls(ctx *context.Context) { ctx.NotFound(nil) return } - - // User can send pull request if owns a forked repository. - if ctx.IsSigned && repo_model.HasForkedRepo(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID) { - ctx.Repo.PullRequest.Allowed = true - } } func retrieveProjectsInternal(ctx *context.Context, repo *repo_model.Repository) (open, closed []*project_model.Project) { diff --git a/routers/web/repo/issue_view.go b/routers/web/repo/issue_view.go index 7670660e31..1354c2d6f9 100644 --- a/routers/web/repo/issue_view.go +++ b/routers/web/repo/issue_view.go @@ -469,7 +469,7 @@ func prepareIssueViewSidebarDependency(ctx *context.Context, issue *issues_model ctx.Data["AllowCrossRepositoryDependencies"] = setting.Service.AllowCrossRepositoryDependencies // Get Dependencies - blockedBy, err := issue.BlockedByDependencies(ctx, db.ListOptions{}) + blockedBy, _, err := issue.BlockedByDependencies(ctx, db.ListOptions{}) if err != nil { ctx.ServerError("BlockedByDependencies", err) return diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index cff501ad71..d306927001 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -344,6 +344,35 @@ func (d *pullCommitStatusCheckData) CommitStatusCheckPrompt(locale translation.L return locale.TrString("repo.pulls.status_checking") } +func getViewPullHeadBranchInfo(ctx *context.Context, pull *issues_model.PullRequest, baseGitRepo *git.Repository) (headCommitID string, headCommitExists bool, err error) { + if pull.HeadRepo == nil { + return "", false, nil + } + headGitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, pull.HeadRepo) + if err != nil { + return "", false, util.Iif(errors.Is(err, util.ErrNotExist), nil, err) + } + defer closer.Close() + + if pull.Flow == issues_model.PullRequestFlowGithub { + headCommitExists, _ = git_model.IsBranchExist(ctx, pull.HeadRepo.ID, pull.HeadBranch) + } else { + headCommitExists = gitrepo.IsReferenceExist(ctx, pull.BaseRepo, pull.GetGitHeadRefName()) + } + + if headCommitExists { + if pull.Flow != issues_model.PullRequestFlowGithub { + headCommitID, err = baseGitRepo.GetRefCommitID(pull.GetGitHeadRefName()) + } else { + headCommitID, err = headGitRepo.GetBranchCommitID(pull.HeadBranch) + } + if err != nil { + return "", false, util.Iif(errors.Is(err, util.ErrNotExist), nil, err) + } + } + return headCommitID, headCommitExists, nil +} + // prepareViewPullInfo show meta information for a pull request preview page func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git_service.CompareInfo { ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes @@ -430,34 +459,10 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git_s return compareInfo } - var headBranchExist bool - var headBranchSha string - // HeadRepo may be missing - if pull.HeadRepo != nil { - headGitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, pull.HeadRepo) - if err != nil { - ctx.ServerError("RepositoryFromContextOrOpen", err) - return nil - } - defer closer.Close() - - if pull.Flow == issues_model.PullRequestFlowGithub { - headBranchExist, _ = git_model.IsBranchExist(ctx, pull.HeadRepo.ID, pull.HeadBranch) - } else { - headBranchExist = gitrepo.IsReferenceExist(ctx, pull.BaseRepo, pull.GetGitHeadRefName()) - } - - if headBranchExist { - if pull.Flow != issues_model.PullRequestFlowGithub { - headBranchSha, err = baseGitRepo.GetRefCommitID(pull.GetGitHeadRefName()) - } else { - headBranchSha, err = headGitRepo.GetBranchCommitID(pull.HeadBranch) - } - if err != nil { - ctx.ServerError("GetBranchCommitID", err) - return nil - } - } + headBranchSha, headBranchExist, err := getViewPullHeadBranchInfo(ctx, pull, baseGitRepo) + if err != nil { + ctx.ServerError("getViewPullHeadBranchInfo", err) + return nil } if headBranchExist { diff --git a/routers/web/repo/setting/git_hooks.go b/routers/web/repo/setting/git_hooks.go index ba4b5e85b6..1f542a3f9f 100644 --- a/routers/web/repo/setting/git_hooks.go +++ b/routers/web/repo/setting/git_hooks.go @@ -7,6 +7,7 @@ import ( "net/http" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/routers/web/repo" "code.gitea.io/gitea/services/context" ) @@ -41,6 +42,7 @@ func GitHooksEdit(ctx *context.Context) { return } ctx.Data["Hook"] = hook + ctx.Data["CodeEditorConfig"] = repo.CodeEditorConfig{} // not really editing a repo file, so no editor config ctx.HTML(http.StatusOK, tplGithookEdit) } diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index 0c73c1490f..f9e80a72e0 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -28,6 +28,7 @@ import ( "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/modules/web" + repo_router "code.gitea.io/gitea/routers/web/repo" actions_service "code.gitea.io/gitea/services/actions" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" @@ -88,6 +89,11 @@ func SettingsCtxData(ctx *context.Context) { return } ctx.Data["PushMirrors"] = pushMirrors + + repo_router.PrepareBranchList(ctx) + if ctx.Written() { + return + } } // Settings show a repository's settings page @@ -622,6 +628,7 @@ func handleSettingsPostAdvanced(ctx *context.Context) { DefaultDeleteBranchAfterMerge: form.DefaultDeleteBranchAfterMerge, DefaultMergeStyle: repo_model.MergeStyle(form.PullsDefaultMergeStyle), DefaultAllowMaintainerEdit: form.DefaultAllowMaintainerEdit, + DefaultTargetBranch: strings.TrimSpace(form.DefaultTargetBranch), })) } else if !unit_model.TypePullRequests.UnitGlobalDisabled() { deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePullRequests) diff --git a/services/auth/basic.go b/services/auth/basic.go index 51613870c9..de5c7730cc 100644 --- a/services/auth/basic.go +++ b/services/auth/basic.go @@ -40,25 +40,21 @@ func (b *Basic) Name() string { return BasicMethodName } -// Verify extracts and validates Basic data (username and password/token) from the -// "Authorization" header of the request and returns the corresponding user object for that -// name/token on successful validation. -// Returns nil if header is empty or validation fails. -func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) { +func (b *Basic) parseAuthBasic(req *http.Request) (ret struct{ authToken, uname, passwd string }) { // Basic authentication should only fire on API, Feed, Download, Archives or on Git or LFSPaths // Not all feed (rss/atom) clients feature the ability to add cookies or headers, so we need to allow basic auth for feeds detector := newAuthPathDetector(req) if !detector.isAPIPath() && !detector.isFeedRequest(req) && !detector.isContainerPath() && !detector.isAttachmentDownload() && !detector.isArchivePath() && !detector.isGitRawOrAttachOrLFSPath() { - return nil, nil + return ret } authHeader := req.Header.Get("Authorization") if authHeader == "" { - return nil, nil + return ret } parsed, ok := httpauth.ParseAuthorizationHeader(authHeader) if !ok || parsed.BasicAuth == nil { - return nil, nil + return ret } uname, passwd := parsed.BasicAuth.Username, parsed.BasicAuth.Password @@ -73,7 +69,12 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore } else { log.Trace("Basic Authorization: Attempting login with username as token") } + ret.authToken, ret.uname, ret.passwd = authToken, uname, passwd + return ret +} +// VerifyAuthToken only the access token provided as parameter, used by other auth methods that want to reuse access token verification logic +func (b *Basic) VerifyAuthToken(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore, authToken string) (*user_model.User, error) { // get oauth2 token's user's ID _, uid := GetOAuthAccessTokenScopeAndUserID(req.Context(), authToken) if uid != 0 { @@ -120,6 +121,23 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore store.GetData()["LoginMethod"] = ActionTokenMethodName return user_model.NewActionsUserWithTaskID(task.ID), nil } + return nil, nil +} + +// Verify extracts and validates Basic data (username and password/token) from the +// "Authorization" header of the request and returns the corresponding user object for that +// name/token on successful validation. +// Returns nil if header is empty or validation fails. +func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) { + parseBasicRet := b.parseAuthBasic(req) + authToken, uname, passwd := parseBasicRet.authToken, parseBasicRet.uname, parseBasicRet.passwd + if authToken == "" && uname == "" { + return nil, nil + } + u, err := b.VerifyAuthToken(req, w, store, sess, authToken) + if u != nil || err != nil { + return u, err + } if !setting.Service.EnableBasicAuth { return nil, nil diff --git a/services/context/api.go b/services/context/api.go index 591efadf37..a104d13588 100644 --- a/services/context/api.go +++ b/services/context/api.go @@ -221,7 +221,7 @@ func APIContexter() func(http.Handler) http.Handler { ctx := &APIContext{ Base: base, Cache: cache.GetCache(), - Repo: &Repository{PullRequest: &PullRequest{}}, + Repo: &Repository{}, Org: &APIOrganization{}, } diff --git a/services/context/context.go b/services/context/context.go index b19941cb8d..394a78aa9d 100644 --- a/services/context/context.go +++ b/services/context/context.go @@ -129,7 +129,7 @@ func NewWebContext(base *Base, render Render, session session.Store) *Context { Cache: cache.GetCache(), Link: setting.AppSubURL + strings.TrimSuffix(base.Req.URL.EscapedPath(), "/"), - Repo: &Repository{PullRequest: &PullRequest{}}, + Repo: &Repository{}, Org: &Organization{}, } ctx.TemplateContext = NewTemplateContextForWeb(ctx) diff --git a/services/context/repo.go b/services/context/repo.go index 3813335374..674da577b9 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -37,11 +37,46 @@ import ( "github.com/editorconfig/editorconfig-core-go/v2" ) -// PullRequest contains information to make a pull request -type PullRequest struct { - BaseRepo *repo_model.Repository - Allowed bool // it only used by the web tmpl: "PullRequestCtx.Allowed" - SameRepo bool // it only used by the web tmpl: "PullRequestCtx.SameRepo" +// PullRequestContext contains context information for making a new pull request +type PullRequestContext struct { + ctx *Context + + baseRepo, headRepo *repo_model.Repository + + canCreateNewPull *bool + defaultTargetBranch *string +} + +func (prc *PullRequestContext) SameRepo() bool { + return prc.baseRepo != nil && prc.headRepo != nil && prc.baseRepo.ID == prc.headRepo.ID +} + +func (prc *PullRequestContext) CanCreateNewPull() bool { + if prc.canCreateNewPull != nil { + return *prc.canCreateNewPull + } + ctx := prc.ctx + // People who have push access or have forked repository can propose a new pull request. + can := prc.baseRepo.CanContentChange() && + (ctx.Repo.CanWrite(unit_model.TypeCode) || (ctx.IsSigned && repo_model.HasForkedRepo(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID))) + prc.canCreateNewPull = &can + return can +} + +func (prc *PullRequestContext) MakeDefaultCompareLink(headBranch string) string { + return prc.baseRepo.Link() + "/compare/" + + util.PathEscapeSegments(prc.DefaultTargetBranch()) + "..." + + util.Iif(prc.SameRepo(), "", util.PathEscapeSegments(prc.headRepo.OwnerName)+":") + + util.PathEscapeSegments(headBranch) +} + +func (prc *PullRequestContext) DefaultTargetBranch() string { + if prc.defaultTargetBranch != nil { + return *prc.defaultTargetBranch + } + branchName := prc.baseRepo.GetPullRequestTargetBranch(prc.ctx) + prc.defaultTargetBranch = &branchName + return branchName } // Repository contains information to operate a repository @@ -64,7 +99,7 @@ type Repository struct { CommitID string CommitsCount int64 - PullRequest *PullRequest + PullRequestCtx *PullRequestContext } // CanWriteToBranch checks if the branch is writable by the user @@ -418,6 +453,12 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) { ctx.Data["IsEmptyRepo"] = ctx.Repo.Repository.IsEmpty } +func InitRepoPullRequestCtx(ctx *Context, base, head *repo_model.Repository) { + ctx.Repo.PullRequestCtx = &PullRequestContext{ctx: ctx} + ctx.Repo.PullRequestCtx.baseRepo, ctx.Repo.PullRequestCtx.headRepo = base, head + ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequestCtx +} + // RepoAssignment returns a middleware to handle repository assignment func RepoAssignment(ctx *Context) { if ctx.Data["Repository"] != nil { @@ -666,28 +707,16 @@ func RepoAssignment(ctx *Context) { ctx.Data["BranchesCount"] = branchesTotal - // People who have push access or have forked repository can propose a new pull request. - canPush := ctx.Repo.CanWrite(unit_model.TypeCode) || - (ctx.IsSigned && repo_model.HasForkedRepo(ctx, ctx.Doer.ID, ctx.Repo.Repository.ID)) - canCompare := false - - // Pull request is allowed if this is a fork repository - // and base repository accepts pull requests. + // Pull request is allowed if this is a fork repository, and base repository accepts pull requests. if repo.BaseRepo != nil && repo.BaseRepo.AllowsPulls(ctx) { - canCompare = true + // TODO: this (and below) "BaseRepo" var is not clear and should be removed in the future ctx.Data["BaseRepo"] = repo.BaseRepo - ctx.Repo.PullRequest.BaseRepo = repo.BaseRepo - ctx.Repo.PullRequest.Allowed = canPush + InitRepoPullRequestCtx(ctx, repo.BaseRepo, repo) } else if repo.AllowsPulls(ctx) { // Or, this is repository accepts pull requests between branches. - canCompare = true ctx.Data["BaseRepo"] = repo - ctx.Repo.PullRequest.BaseRepo = repo - ctx.Repo.PullRequest.Allowed = canPush - ctx.Repo.PullRequest.SameRepo = true + InitRepoPullRequestCtx(ctx, repo, repo) } - ctx.Data["CanCompareOrPull"] = canCompare - ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest if ctx.Repo.Repository.Status == repo_model.RepositoryPendingTransfer { repoTransfer, err := repo_model.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository) diff --git a/services/convert/repository.go b/services/convert/repository.go index da4e59d7a9..150c952b15 100644 --- a/services/convert/repository.go +++ b/services/convert/repository.go @@ -103,6 +103,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR defaultDeleteBranchAfterMerge := false defaultMergeStyle := repo_model.MergeStyleMerge defaultAllowMaintainerEdit := false + defaultTargetBranch := "" if unit, err := repo.GetUnit(ctx, unit_model.TypePullRequests); err == nil { config := unit.PullRequestsConfig() hasPullRequests = true @@ -118,6 +119,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR defaultDeleteBranchAfterMerge = config.DefaultDeleteBranchAfterMerge defaultMergeStyle = config.GetDefaultMergeStyle() defaultAllowMaintainerEdit = config.DefaultAllowMaintainerEdit + defaultTargetBranch = config.DefaultTargetBranch } hasProjects := false projectsMode := repo_model.ProjectsModeAll @@ -235,6 +237,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR DefaultDeleteBranchAfterMerge: defaultDeleteBranchAfterMerge, DefaultMergeStyle: string(defaultMergeStyle), DefaultAllowMaintainerEdit: defaultAllowMaintainerEdit, + DefaultTargetBranch: defaultTargetBranch, AvatarURL: repo.AvatarLink(ctx), Internal: !repo.IsPrivate && repo.Owner.Visibility == api.VisibleTypePrivate, MirrorInterval: mirrorInterval, diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index e4545570c8..765a723968 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -143,6 +143,7 @@ type RepoSettingForm struct { PullsAllowRebaseUpdate bool DefaultDeleteBranchAfterMerge bool DefaultAllowMaintainerEdit bool + DefaultTargetBranch string EnableTimetracker bool AllowOnlyContributorsToTrackTime bool EnableIssueDependencies bool diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 3728f50d21..6b29582208 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -16,6 +16,7 @@ import ( "path" "sort" "strings" + "time" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" @@ -81,6 +82,8 @@ type DiffLine struct { // DiffLineSectionInfo represents diff line section meta data type DiffLineSectionInfo struct { + language *diffVarMutable[string] + Path string // These line "idx" are 1-based line numbers @@ -121,8 +124,14 @@ type DiffHTMLOperation struct { // BlobExcerptChunkSize represent max lines of excerpt const BlobExcerptChunkSize = 20 -// MaxDiffHighlightEntireFileSize is the maximum file size that will be highlighted with "entire file diff" -const MaxDiffHighlightEntireFileSize = 1 * 1024 * 1024 +// Chroma seems extremely slow when highlighting large files, it might take dozens or hundreds of milliseconds. +// When fully highlighting a diff with a lot of large files, it would take many seconds or even dozens of seconds. +// So, don't highlight the entire file if it's too large, or highlighting takes too long. +// When there is no full-file highlighting, the legacy "line-by-line" highlighting is still applied as the fallback. +const ( + MaxFullFileHighlightSizeLimit = 256 * 1024 + MaxFullFileHighlightTimeLimit = 2 * time.Second +) // GetType returns the type of DiffLine. func (d *DiffLine) GetType() int { @@ -165,16 +174,19 @@ func (d *DiffLine) GetLineTypeMarker() string { } func (d *DiffLine) getBlobExcerptQuery() string { - query := fmt.Sprintf( + language := "" + if d.SectionInfo.language != nil { // for normal cases, it can't be nil, this check is only for some tests + language = d.SectionInfo.language.value + } + return fmt.Sprintf( "last_left=%d&last_right=%d&"+ "left=%d&right=%d&"+ "left_hunk_size=%d&right_hunk_size=%d&"+ - "path=%s", + "path=%s&filelang=%s", d.SectionInfo.LastLeftIdx, d.SectionInfo.LastRightIdx, d.SectionInfo.LeftIdx, d.SectionInfo.RightIdx, d.SectionInfo.LeftHunkSize, d.SectionInfo.RightHunkSize, - url.QueryEscape(d.SectionInfo.Path)) - return query + url.QueryEscape(d.SectionInfo.Path), url.QueryEscape(language)) } func (d *DiffLine) GetExpandDirection() string { @@ -266,11 +278,12 @@ func FillHiddenCommentIDsForDiffLine(line *DiffLine, lineComments map[int64][]*i line.SectionInfo.HiddenCommentIDs = hiddenCommentIDs } -func getDiffLineSectionInfo(treePath, line string, lastLeftIdx, lastRightIdx int) *DiffLineSectionInfo { +func newDiffLineSectionInfo(curFile *DiffFile, line string, lastLeftIdx, lastRightIdx int) *DiffLineSectionInfo { leftLine, leftHunk, rightLine, rightHunk := git.ParseDiffHunkString(line) return &DiffLineSectionInfo{ - Path: treePath, + Path: curFile.Name, + language: &curFile.language, LastLeftIdx: lastLeftIdx, LastRightIdx: lastRightIdx, LeftIdx: leftLine, @@ -290,7 +303,10 @@ func getLineContent(content string, locale translation.Locale) DiffInline { // DiffSection represents a section of a DiffFile. type DiffSection struct { - file *DiffFile + language *diffVarMutable[string] + highlightedLeftLines *diffVarMutable[map[int]template.HTML] + highlightedRightLines *diffVarMutable[map[int]template.HTML] + FileName string Lines []*DiffLine } @@ -339,9 +355,9 @@ func (diffSection *DiffSection) getDiffLineForRender(diffLineType DiffLineType, var fileLanguage string var highlightedLeftLines, highlightedRightLines map[int]template.HTML // when a "diff section" is manually prepared by ExcerptBlob, it doesn't have "file" information - if diffSection.file != nil { - fileLanguage = diffSection.file.Language - highlightedLeftLines, highlightedRightLines = diffSection.file.highlightedLeftLines, diffSection.file.highlightedRightLines + if diffSection.language != nil { + fileLanguage = diffSection.language.value + highlightedLeftLines, highlightedRightLines = diffSection.highlightedLeftLines.value, diffSection.highlightedRightLines.value } var lineHTML template.HTML @@ -392,6 +408,11 @@ func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine, loc } } +// diffVarMutable is a wrapper to make a variable mutable to be shared across structs +type diffVarMutable[T any] struct { + value T +} + // DiffFile represents a file diff. type DiffFile struct { // only used internally to parse Ambiguous filenames @@ -418,7 +439,6 @@ type DiffFile struct { IsIncompleteLineTooLong bool // will be filled by the extra loop in GitDiffForRender - Language string IsGenerated bool IsVendored bool SubmoduleDiffInfo *SubmoduleDiffInfo // IsSubmodule==true, then there must be a SubmoduleDiffInfo @@ -430,9 +450,10 @@ type DiffFile struct { IsViewed bool // User specific HasChangedSinceLastReview bool // User specific - // for render purpose only, will be filled by the extra loop in GitDiffForRender - highlightedLeftLines map[int]template.HTML - highlightedRightLines map[int]template.HTML + // for render purpose only, will be filled by the extra loop in GitDiffForRender, the maps of lines are 0-based + language diffVarMutable[string] + highlightedLeftLines diffVarMutable[map[int]template.HTML] + highlightedRightLines diffVarMutable[map[int]template.HTML] } // GetType returns type of diff file. @@ -469,6 +490,7 @@ func (diffFile *DiffFile) GetTailSectionAndLimitedContent(leftCommit, rightCommi Type: DiffLineSection, Content: " ", SectionInfo: &DiffLineSectionInfo{ + language: &diffFile.language, Path: diffFile.Name, LastLeftIdx: lastLine.LeftIdx, LastRightIdx: lastLine.RightIdx, @@ -549,7 +571,7 @@ func getCommitFileLineCountAndLimitedContent(commit *git.Commit, filePath string if err != nil { return 0, nil } - w := &limitByteWriter{limit: MaxDiffHighlightEntireFileSize + 1} + w := &limitByteWriter{limit: MaxFullFileHighlightSizeLimit + 1} lineCount, err = blob.GetBlobLineCount(w) if err != nil { return 0, nil @@ -907,6 +929,14 @@ func skipToNextDiffHead(input *bufio.Reader) (line string, err error) { return line, err } +func newDiffSectionForDiffFile(curFile *DiffFile) *DiffSection { + return &DiffSection{ + language: &curFile.language, + highlightedLeftLines: &curFile.highlightedLeftLines, + highlightedRightLines: &curFile.highlightedRightLines, + } +} + func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio.Reader) (lineBytes []byte, isFragment bool, err error) { sb := strings.Builder{} @@ -964,12 +994,12 @@ func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharact line := sb.String() // Create a new section to represent this hunk - curSection = &DiffSection{file: curFile} + curSection = newDiffSectionForDiffFile(curFile) lastLeftIdx = -1 curFile.Sections = append(curFile.Sections, curSection) // FIXME: the "-1" can't be right, these "line idx" are all 1-based, maybe there are other bugs that covers this bug. - lineSectionInfo := getDiffLineSectionInfo(curFile.Name, line, leftLine-1, rightLine-1) + lineSectionInfo := newDiffLineSectionInfo(curFile, line, leftLine-1, rightLine-1) diffLine := &DiffLine{ Type: DiffLineSection, Content: line, @@ -1004,7 +1034,7 @@ func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharact rightLine++ if curSection == nil { // Create a new section to represent this hunk - curSection = &DiffSection{file: curFile} + curSection = newDiffSectionForDiffFile(curFile) curFile.Sections = append(curFile.Sections, curSection) lastLeftIdx = -1 } @@ -1037,7 +1067,7 @@ func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharact } if curSection == nil { // Create a new section to represent this hunk - curSection = &DiffSection{file: curFile} + curSection = newDiffSectionForDiffFile(curFile) curFile.Sections = append(curFile.Sections, curSection) lastLeftIdx = -1 } @@ -1064,7 +1094,7 @@ func parseHunks(ctx context.Context, curFile *DiffFile, maxLines, maxLineCharact lastLeftIdx = -1 if curSection == nil { // Create a new section to represent this hunk - curSection = &DiffSection{file: curFile} + curSection = newDiffSectionForDiffFile(curFile) curFile.Sections = append(curFile.Sections, curSection) } curSection.Lines = append(curSection.Lines, diffLine) @@ -1294,6 +1324,8 @@ func GetDiffForRender(ctx context.Context, repoLink string, gitRepo *git.Reposit return nil, err } + startTime := time.Now() + checker, err := attribute.NewBatchChecker(gitRepo, opts.AfterCommitID, []string{attribute.LinguistVendored, attribute.LinguistGenerated, attribute.LinguistLanguage, attribute.GitlabLanguage, attribute.Diff}) if err != nil { return nil, err @@ -1309,7 +1341,7 @@ func GetDiffForRender(ctx context.Context, repoLink string, gitRepo *git.Reposit isVendored, isGenerated = attrs.GetVendored(), attrs.GetGenerated() language := attrs.GetLanguage() if language.Has() { - diffFile.Language = language.Value() + diffFile.language.value = language.Value() } attrDiff = attrs.Get(attribute.Diff).ToString() } @@ -1333,13 +1365,14 @@ func GetDiffForRender(ctx context.Context, repoLink string, gitRepo *git.Reposit diffFile.Sections = append(diffFile.Sections, tailSection) } - shouldFullFileHighlight := !setting.Git.DisableDiffHighlight && attrDiff.Value() == "" + shouldFullFileHighlight := attrDiff.Value() == "" // only do highlight if no custom diff command + shouldFullFileHighlight = shouldFullFileHighlight && time.Since(startTime) < MaxFullFileHighlightTimeLimit if shouldFullFileHighlight { - if limitedContent.LeftContent != nil && limitedContent.LeftContent.buf.Len() < MaxDiffHighlightEntireFileSize { - diffFile.highlightedLeftLines = highlightCodeLines(diffFile, true /* left */, limitedContent.LeftContent.buf.Bytes()) + if limitedContent.LeftContent != nil { + diffFile.highlightedLeftLines.value = highlightCodeLinesForDiffFile(diffFile, true /* left */, limitedContent.LeftContent.buf.Bytes()) } - if limitedContent.RightContent != nil && limitedContent.RightContent.buf.Len() < MaxDiffHighlightEntireFileSize { - diffFile.highlightedRightLines = highlightCodeLines(diffFile, false /* right */, limitedContent.RightContent.buf.Bytes()) + if limitedContent.RightContent != nil { + diffFile.highlightedRightLines.value = highlightCodeLinesForDiffFile(diffFile, false /* right */, limitedContent.RightContent.buf.Bytes()) } } } @@ -1347,13 +1380,26 @@ func GetDiffForRender(ctx context.Context, repoLink string, gitRepo *git.Reposit return diff, nil } -func highlightCodeLines(diffFile *DiffFile, isLeft bool, rawContent []byte) map[int]template.HTML { +func FillDiffFileHighlightLinesByContent(diffFile *DiffFile, left, right []byte) { + diffFile.highlightedLeftLines.value = highlightCodeLinesForDiffFile(diffFile, true /* left */, left) + diffFile.highlightedRightLines.value = highlightCodeLinesForDiffFile(diffFile, false /* right */, right) +} + +func highlightCodeLinesForDiffFile(diffFile *DiffFile, isLeft bool, rawContent []byte) map[int]template.HTML { + return highlightCodeLines(diffFile.Name, diffFile.language.value, diffFile.Sections, isLeft, rawContent) +} + +func highlightCodeLines(name, lang string, sections []*DiffSection, isLeft bool, rawContent []byte) map[int]template.HTML { + if setting.Git.DisableDiffHighlight || len(rawContent) > MaxFullFileHighlightSizeLimit { + return nil + } + content := util.UnsafeBytesToString(charset.ToUTF8(rawContent, charset.ConvertOpts{})) - highlightedNewContent, _ := highlight.RenderCodeFast(diffFile.Name, diffFile.Language, content) + highlightedNewContent, _ := highlight.RenderCodeFast(name, lang, content) unsafeLines := highlight.UnsafeSplitHighlightedLines(highlightedNewContent) lines := make(map[int]template.HTML, len(unsafeLines)) // only save the highlighted lines we need, but not the whole file, to save memory - for _, sec := range diffFile.Sections { + for _, sec := range sections { for _, ln := range sec.Lines { lineIdx := ln.LeftIdx if !isLeft { diff --git a/services/gitdiff/gitdiff_excerpt.go b/services/gitdiff/gitdiff_excerpt.go new file mode 100644 index 0000000000..be66d8e2af --- /dev/null +++ b/services/gitdiff/gitdiff_excerpt.go @@ -0,0 +1,121 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitdiff + +import ( + "bufio" + "bytes" + "fmt" + "html/template" + "io" + + "code.gitea.io/gitea/modules/setting" +) + +type BlobExcerptOptions struct { + LastLeft int + LastRight int + LeftIndex int + RightIndex int + LeftHunkSize int + RightHunkSize int + Direction string + Language string +} + +func fillExcerptLines(section *DiffSection, filePath string, reader io.Reader, lang string, idxLeft, idxRight, chunkSize int) error { + buf := &bytes.Buffer{} + scanner := bufio.NewScanner(reader) + var diffLines []*DiffLine + for line := 0; line < idxRight+chunkSize; line++ { + if ok := scanner.Scan(); !ok { + break + } + lineText := scanner.Text() + if buf.Len()+len(lineText) < int(setting.UI.MaxDisplayFileSize) { + buf.WriteString(lineText) + buf.WriteByte('\n') + } + if line < idxRight { + continue + } + diffLine := &DiffLine{ + LeftIdx: idxLeft + (line - idxRight) + 1, + RightIdx: line + 1, + Type: DiffLinePlain, + Content: " " + lineText, + } + diffLines = append(diffLines, diffLine) + } + if err := scanner.Err(); err != nil { + return fmt.Errorf("fillExcerptLines scan: %w", err) + } + section.Lines = diffLines + // DiffLinePlain always uses right lines + section.highlightedRightLines.value = highlightCodeLines(filePath, lang, []*DiffSection{section}, false /* right */, buf.Bytes()) + return nil +} + +func BuildBlobExcerptDiffSection(filePath string, reader io.Reader, opts BlobExcerptOptions) (*DiffSection, error) { + lastLeft, lastRight, idxLeft, idxRight := opts.LastLeft, opts.LastRight, opts.LeftIndex, opts.RightIndex + leftHunkSize, rightHunkSize, direction := opts.LeftHunkSize, opts.RightHunkSize, opts.Direction + language := opts.Language + + chunkSize := BlobExcerptChunkSize + section := &DiffSection{ + language: &diffVarMutable[string]{value: language}, + highlightedLeftLines: &diffVarMutable[map[int]template.HTML]{}, + highlightedRightLines: &diffVarMutable[map[int]template.HTML]{}, + FileName: filePath, + } + var err error + if direction == "up" && (idxLeft-lastLeft) > chunkSize { + idxLeft -= chunkSize + idxRight -= chunkSize + leftHunkSize += chunkSize + rightHunkSize += chunkSize + err = fillExcerptLines(section, filePath, reader, language, idxLeft-1, idxRight-1, chunkSize) + } else if direction == "down" && (idxLeft-lastLeft) > chunkSize { + err = fillExcerptLines(section, filePath, reader, language, lastLeft, lastRight, chunkSize) + lastLeft += chunkSize + lastRight += chunkSize + } else { + offset := -1 + if direction == "down" { + offset = 0 + } + err = fillExcerptLines(section, filePath, reader, language, lastLeft, lastRight, idxRight-lastRight+offset) + leftHunkSize = 0 + rightHunkSize = 0 + idxLeft = lastLeft + idxRight = lastRight + } + if err != nil { + return nil, err + } + + newLineSection := &DiffLine{ + Type: DiffLineSection, + SectionInfo: &DiffLineSectionInfo{ + language: &diffVarMutable[string]{value: opts.Language}, + Path: filePath, + LastLeftIdx: lastLeft, + LastRightIdx: lastRight, + LeftIdx: idxLeft, + RightIdx: idxRight, + LeftHunkSize: leftHunkSize, + RightHunkSize: rightHunkSize, + }, + } + if newLineSection.GetExpandDirection() != "" { + newLineSection.Content = fmt.Sprintf("@@ -%d,%d +%d,%d @@\n", idxLeft, leftHunkSize, idxRight, rightHunkSize) + switch direction { + case "up": + section.Lines = append([]*DiffLine{newLineSection}, section.Lines...) + case "down": + section.Lines = append(section.Lines, newLineSection) + } + } + return section, nil +} diff --git a/services/gitdiff/gitdiff_excerpt_test.go b/services/gitdiff/gitdiff_excerpt_test.go new file mode 100644 index 0000000000..cb71e66462 --- /dev/null +++ b/services/gitdiff/gitdiff_excerpt_test.go @@ -0,0 +1,39 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package gitdiff + +import ( + "bytes" + "strconv" + "testing" + + "code.gitea.io/gitea/modules/translation" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestBuildBlobExcerptDiffSection(t *testing.T) { + data := &bytes.Buffer{} + for i := range 100 { + data.WriteString("a = " + strconv.Itoa(i+1) + "\n") + } + + locale := translation.MockLocale{} + lineMiddle := 50 + diffSection, err := BuildBlobExcerptDiffSection("a.py", bytes.NewReader(data.Bytes()), BlobExcerptOptions{ + LeftIndex: lineMiddle, + RightIndex: lineMiddle, + LeftHunkSize: 10, + RightHunkSize: 10, + Direction: "up", + }) + require.NoError(t, err) + assert.Len(t, diffSection.highlightedRightLines.value, BlobExcerptChunkSize) + assert.NotEmpty(t, diffSection.highlightedRightLines.value[lineMiddle-BlobExcerptChunkSize-1]) + assert.NotEmpty(t, diffSection.highlightedRightLines.value[lineMiddle-2]) // 0-based + + diffInline := diffSection.GetComputedInlineDiffFor(diffSection.Lines[1], locale) + assert.Equal(t, `a = 30`+"\n", string(diffInline.Content)) +} diff --git a/services/gitdiff/gitdiff_test.go b/services/gitdiff/gitdiff_test.go index a94dad8b63..62b17c223c 100644 --- a/services/gitdiff/gitdiff_test.go +++ b/services/gitdiff/gitdiff_test.go @@ -1111,22 +1111,20 @@ func TestDiffLine_GetExpandDirection(t *testing.T) { func TestHighlightCodeLines(t *testing.T) { t.Run("CharsetDetecting", func(t *testing.T) { diffFile := &DiffFile{ - Name: "a.c", - Language: "c", + Name: "a.c", Sections: []*DiffSection{ { Lines: []*DiffLine{{LeftIdx: 1}}, }, }, } - ret := highlightCodeLines(diffFile, true, []byte("// abc\xcc def\xcd")) // ISO-8859-1 bytes + ret := highlightCodeLinesForDiffFile(diffFile, true, []byte("// abc\xcc def\xcd")) // ISO-8859-1 bytes assert.Equal(t, "// abcÌ defÍ\n", string(ret[0])) }) t.Run("LeftLines", func(t *testing.T) { diffFile := &DiffFile{ - Name: "a.c", - Language: "c", + Name: "a.c", Sections: []*DiffSection{ { Lines: []*DiffLine{ @@ -1138,7 +1136,7 @@ func TestHighlightCodeLines(t *testing.T) { }, } const nl = "\n" - ret := highlightCodeLines(diffFile, true, []byte("a\nb\n")) + ret := highlightCodeLinesForDiffFile(diffFile, true, []byte("a\nb\n")) assert.Equal(t, map[int]template.HTML{ 0: `a` + nl, 1: `b`, diff --git a/services/gitdiff/highlightdiff.go b/services/gitdiff/highlightdiff.go index e8be063e69..1de3963788 100644 --- a/services/gitdiff/highlightdiff.go +++ b/services/gitdiff/highlightdiff.go @@ -7,12 +7,45 @@ import ( "bytes" "html/template" "strings" + "unicode/utf8" + + "code.gitea.io/gitea/modules/util" "github.com/sergi/go-diff/diffmatchpatch" ) -// token is a html tag or entity, eg: "", "", "<" -func extractHTMLToken(s string) (before, token, after string, valid bool) { +// extractDiffTokenRemainingFullTag tries to extract full tag with content from the remaining string +// e.g. for input: "contentthe-rest...", it returns "content", "the-rest...", true +func extractDiffTokenRemainingFullTag(s string) (token, after string, valid bool) { + pos := 0 + for ; pos < len(s); pos++ { + c := s[pos] + if c == '<' { + break + } + // keep in mind: even if we'd like to relax this check, + // we should never ignore "&" because it is for HTML entity and can't be safely used in the diff algorithm, + // because diff between "<" and ">" will generate broken result. + isSymbolChar := 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z' || '0' <= c && c <= '9' || c == '_' || c == '-' || c == '.' + if !isSymbolChar { + return "", s, false + } + } + if pos+1 >= len(s) || s[pos+1] != '/' { + return "", s, false + } + pos2 := strings.IndexByte(s[pos:], '>') + if pos2 == -1 { + return "", s, false + } + return s[:pos+pos2+1], s[pos+pos2+1:], true +} + +// Returned token: +// * full tag with content: "<content>", it is used to optimize diff results to highlight the whole changed symbol +// * opening/closing tag: "" or "" +// * HTML entity: "<" +func extractDiffToken(s string) (before, token, after string, valid bool) { for pos1 := 0; pos1 < len(s); pos1++ { switch s[pos1] { case '<': @@ -20,7 +53,15 @@ func extractHTMLToken(s string) (before, token, after string, valid bool) { if pos2 == -1 { return "", "", s, false } - return s[:pos1], s[pos1 : pos1+pos2+1], s[pos1+pos2+1:], true + before, token, after = s[:pos1], s[pos1:pos1+pos2+1], s[pos1+pos2+1:] + + if !strings.HasPrefix(token, "content>`, to optimize diff results + if fullTokenRemaining, fullTokenAfter, ok := extractDiffTokenRemainingFullTag(after); ok { + return before, "<" + token + fullTokenRemaining + ">", fullTokenAfter, true + } + } + return before, token, after, true case '&': pos2 := strings.IndexByte(s[pos1:], ';') if pos2 == -1 { @@ -47,7 +88,9 @@ type highlightCodeDiff struct { placeholderOverflowCount int - lineWrapperTags []string + diffCodeAddedOpen rune + diffCodeRemovedOpen rune + diffCodeClose rune } func newHighlightCodeDiff() *highlightCodeDiff { @@ -87,11 +130,26 @@ func (hcd *highlightCodeDiff) collectUsedRunes(code template.HTML) { } } -func (hcd *highlightCodeDiff) diffLineWithHighlight(lineType DiffLineType, codeA, codeB template.HTML) template.HTML { - return hcd.diffLineWithHighlightWrapper(nil, lineType, codeA, codeB) +func (hcd *highlightCodeDiff) diffEqualPartIsSpaceOnly(s string) bool { + for _, r := range s { + if r >= hcd.placeholderBegin { + recovered := hcd.placeholderTokenMap[r] + if strings.HasPrefix(recovered, "<<") { + return false // a full tag with content, it can't be space-only + } else if strings.HasPrefix(recovered, "<") { + continue // a single opening/closing tag, skip the tag and continue to check the content + } + return false // otherwise, it must be an HTML entity, it can't be space-only + } + isSpace := r == ' ' || r == '\t' || r == '\n' || r == '\r' + if !isSpace { + return false + } + } + return true } -func (hcd *highlightCodeDiff) diffLineWithHighlightWrapper(lineWrapperTags []string, lineType DiffLineType, codeA, codeB template.HTML) template.HTML { +func (hcd *highlightCodeDiff) diffLineWithHighlight(lineType DiffLineType, codeA, codeB template.HTML) template.HTML { hcd.collectUsedRunes(codeA) hcd.collectUsedRunes(codeB) @@ -104,32 +162,44 @@ func (hcd *highlightCodeDiff) diffLineWithHighlightWrapper(lineWrapperTags []str buf := bytes.NewBuffer(nil) - // restore the line wrapper tags and , if necessary - for _, tag := range lineWrapperTags { - buf.WriteString(tag) + if hcd.diffCodeClose == 0 { + // tests can pre-set the placeholders + hcd.diffCodeAddedOpen = hcd.registerTokenAsPlaceholder(``) + hcd.diffCodeRemovedOpen = hcd.registerTokenAsPlaceholder(``) + hcd.diffCodeClose = hcd.registerTokenAsPlaceholder(``) } - addedCodePrefix := hcd.registerTokenAsPlaceholder(``) - removedCodePrefix := hcd.registerTokenAsPlaceholder(``) - codeTagSuffix := hcd.registerTokenAsPlaceholder(``) + equalPartSpaceOnly := true + for _, diff := range diffs { + if diff.Type != diffmatchpatch.DiffEqual { + continue + } + if equalPartSpaceOnly = hcd.diffEqualPartIsSpaceOnly(diff.Text); !equalPartSpaceOnly { + break + } + } - if codeTagSuffix != 0 { + // only add "added"/"removed" tags when needed: + // * non-space contents appear in the DiffEqual parts (not a full-line add/del) + // * placeholder map still works (not exhausted, can get the closing tag placeholder) + addDiffTags := !equalPartSpaceOnly && hcd.diffCodeClose != 0 + if addDiffTags { for _, diff := range diffs { switch { case diff.Type == diffmatchpatch.DiffEqual: buf.WriteString(diff.Text) case diff.Type == diffmatchpatch.DiffInsert && lineType == DiffLineAdd: - buf.WriteRune(addedCodePrefix) + buf.WriteRune(hcd.diffCodeAddedOpen) buf.WriteString(diff.Text) - buf.WriteRune(codeTagSuffix) + buf.WriteRune(hcd.diffCodeClose) case diff.Type == diffmatchpatch.DiffDelete && lineType == DiffLineDel: - buf.WriteRune(removedCodePrefix) + buf.WriteRune(hcd.diffCodeRemovedOpen) buf.WriteString(diff.Text) - buf.WriteRune(codeTagSuffix) + buf.WriteRune(hcd.diffCodeClose) } } } else { - // placeholder map space is exhausted + // the caller will still add added/removed backgrounds for the whole line for _, diff := range diffs { take := diff.Type == diffmatchpatch.DiffEqual || (diff.Type == diffmatchpatch.DiffInsert && lineType == DiffLineAdd) || (diff.Type == diffmatchpatch.DiffDelete && lineType == DiffLineDel) if take { @@ -137,19 +207,21 @@ func (hcd *highlightCodeDiff) diffLineWithHighlightWrapper(lineWrapperTags []str } } } - for range lineWrapperTags { - buf.WriteString("") - } return hcd.recoverOneDiff(buf.String()) } func (hcd *highlightCodeDiff) registerTokenAsPlaceholder(token string) rune { + recovered := token + if token[0] == '<' && token[1] != '<' { + // when recovering a single tag, only use the tag itself, ignore the trailing comment (for how the comment is generated, see the code in `convert` function) + recovered = token[:strings.IndexByte(token, '>')+1] + } placeholder, ok := hcd.tokenPlaceholderMap[token] if !ok { placeholder = hcd.nextPlaceholder() if placeholder != 0 { hcd.tokenPlaceholderMap[token] = placeholder - hcd.placeholderTokenMap[placeholder] = token + hcd.placeholderTokenMap[placeholder] = recovered } } return placeholder @@ -160,44 +232,42 @@ func (hcd *highlightCodeDiff) convertToPlaceholders(htmlContent template.HTML) s var tagStack []string res := strings.Builder{} - firstRunForLineTags := hcd.lineWrapperTags == nil - + htmlCode := string(htmlContent) var beforeToken, token string var valid bool - - htmlCode := string(htmlContent) - // the standard chroma highlight HTML is " ... " for { - beforeToken, token, htmlCode, valid = extractHTMLToken(htmlCode) + beforeToken, token, htmlCode, valid = extractDiffToken(htmlCode) if !valid || token == "" { break } // write the content before the token into result string, and consume the token in the string res.WriteString(beforeToken) + // the standard chroma highlight HTML is ` ... ` // the line wrapper tags should be removed before diff if strings.HasPrefix(token, `") continue } var tokenInMap string - if strings.HasSuffix(token, "" for "" tokenInMap = token + "" tagStack = tagStack[:len(tagStack)-1] - } else if token[0] == '<' { // for opening tag - tokenInMap = token - tagStack = append(tagStack, token) - } else if token[0] == '&' { // for html entity + } else if token[0] == '<' { + if token[1] == '<' { + // full tag `<content>`, recover to `content` + tokenInMap = token + } else { + // opening tag + tokenInMap = token + tagStack = append(tagStack, token) + } + } else if token[0] == '&' { // for HTML entity tokenInMap = token } // else: impossible @@ -210,8 +280,13 @@ func (hcd *highlightCodeDiff) convertToPlaceholders(htmlContent template.HTML) s // unfortunately, all private use runes has been exhausted, no more placeholder could be used, no more converting // usually, the exhausting won't occur in real cases, the magnitude of used placeholders is not larger than that of the CSS classes outputted by chroma. hcd.placeholderOverflowCount++ + if strings.HasPrefix(token, "<<") { + pos1 := strings.IndexByte(token, '>') + pos2 := strings.LastIndexByte(token, '<') + res.WriteString(token[pos1+1 : pos2]) // recover to `content` from "<content>" + } if strings.HasPrefix(token, "&") { - // when the token is a html entity, something must be outputted even if there is no placeholder. + // when the token is an HTML entity, something must be outputted even if there is no placeholder. res.WriteRune(0xFFFD) // replacement character TODO: how to handle this case more gracefully? res.WriteString(token[1:]) // still output the entity code part, otherwise there will be no diff result. } @@ -223,43 +298,99 @@ func (hcd *highlightCodeDiff) convertToPlaceholders(htmlContent template.HTML) s return res.String() } +// recoverOneRune tries to recover one rune +// * if the rune is a placeholder, it will be recovered to the corresponding content +// * otherwise it will be returned as is +func (hcd *highlightCodeDiff) recoverOneRune(buf []byte) (r rune, runeLen int, isSingleTag bool, recovered string) { + r, runeLen = utf8.DecodeRune(buf) + token := hcd.placeholderTokenMap[r] + if token == "" { + return r, runeLen, false, "" // rune itself, not a placeholder + } else if token[0] == '<' { + if token[1] == '<' { + return 0, runeLen, false, token[1 : len(token)-1] // full tag `<content>`, recover to `content` + } + return r, runeLen, true, token // single tag + } + return 0, runeLen, false, token // HTML entity +} + func (hcd *highlightCodeDiff) recoverOneDiff(str string) template.HTML { sb := strings.Builder{} var tagStack []string + var diffCodeOpenTag string + diffCodeCloseTag := hcd.placeholderTokenMap[hcd.diffCodeClose] + strBytes := util.UnsafeStringToBytes(str) - for _, r := range str { - token, ok := hcd.placeholderTokenMap[r] - if !ok || token == "" { - sb.WriteRune(r) // if the rune is not a placeholder, write it as it is - continue - } - var tokenToRecover string - if strings.HasPrefix(token, "')+1] - if len(tagStack) == 0 { - continue // if no opening tag in stack yet, skip the closing tag + // this loop is slightly longer than expected, for performance consideration + for idx := 0; idx < len(strBytes); { + // take a look at the next rune + r, runeLen, isSingleTag, recovered := hcd.recoverOneRune(strBytes[idx:]) + idx += runeLen + + // loop section 1: if it isn't a single tag, then try to find the following runes until the next single tag, and recover them together + if !isSingleTag { + if diffCodeOpenTag != "" { + // start the "added/removed diff tag" if the current token is in the diff part + sb.WriteString(diffCodeOpenTag) } - tagStack = tagStack[:len(tagStack)-1] - } else if token[0] == '<' { // for opening tag - tokenToRecover = token - tagStack = append(tagStack, token) - } else if token[0] == '&' { // for html entity - tokenToRecover = token - } // else: impossible - sb.WriteString(tokenToRecover) + if recovered != "" { + sb.WriteString(recovered) + } else { + sb.WriteRune(r) + } + // inner loop to recover following runes until the next single tag + for idx < len(strBytes) { + r, runeLen, isSingleTag, recovered = hcd.recoverOneRune(strBytes[idx:]) + idx += runeLen + if isSingleTag { + break + } + if recovered != "" { + sb.WriteString(recovered) + } else { + sb.WriteRune(r) + } + } + if diffCodeOpenTag != "" { + // end the "added/removed diff tag" if the current token is in the diff part + sb.WriteString(diffCodeCloseTag) + } + } + + if !isSingleTag { + break // the inner loop has already consumed all remaining runes, no more single tag found + } + + // loop section 2: for opening/closing HTML tags + placeholder := r + if recovered[1] != '/' { // opening tag + if placeholder == hcd.diffCodeAddedOpen || placeholder == hcd.diffCodeRemovedOpen { + diffCodeOpenTag = recovered + recovered = "" + } else { + tagStack = append(tagStack, recovered) + } + } else { // closing tag + if placeholder == hcd.diffCodeClose { + diffCodeOpenTag = "" // the highlighted diff is closed, no more diff + recovered = "" + } else if len(tagStack) != 0 { + tagStack = tagStack[:len(tagStack)-1] + } else { + recovered = "" + } + } + sb.WriteString(recovered) } - if len(tagStack) > 0 { - // close all opening tags - for i := len(tagStack) - 1; i >= 0; i-- { - tagToClose := tagStack[i] - // get the closing tag "" from "" or "" - pos := strings.IndexAny(tagToClose, " >") - if pos != -1 { - sb.WriteString("") - } // else: impossible. every tag was pushed into the stack by the code above and is valid HTML opening tag - } + // close all opening tags + for i := len(tagStack) - 1; i >= 0; i-- { + tagToClose := tagStack[i] + // get the closing tag "" from "" or "" + pos := strings.IndexAny(tagToClose, " >") + // pos must be positive, because the tags were pushed by us + sb.WriteString("") } return template.HTML(sb.String()) } diff --git a/services/gitdiff/highlightdiff_test.go b/services/gitdiff/highlightdiff_test.go index 0df2e29d13..b99b7e3675 100644 --- a/services/gitdiff/highlightdiff_test.go +++ b/services/gitdiff/highlightdiff_test.go @@ -9,28 +9,62 @@ import ( "strings" "testing" + "code.gitea.io/gitea/modules/highlight" + "github.com/stretchr/testify/assert" ) -func TestDiffWithHighlight(t *testing.T) { - t.Run("DiffLineAddDel", func(t *testing.T) { +func BenchmarkHighlightDiff(b *testing.B) { + for b.Loop() { + // still fast enough: BenchmarkHighlightDiff-12 1000000 1027 ns/op + // TODO: the real bottleneck is that "diffLineWithHighlight" is called twice when rendering "added" and "removed" lines by the caller + // Ideally the caller should cache the diff result, and then use the diff result to render "added" and "removed" lines separately hcd := newHighlightCodeDiff() codeA := template.HTML(`x foo y`) codeB := template.HTML(`x bar y`) - outDel := hcd.diffLineWithHighlight(DiffLineDel, codeA, codeB) - assert.Equal(t, `x foo y`, string(outDel)) - outAdd := hcd.diffLineWithHighlight(DiffLineAdd, codeA, codeB) - assert.Equal(t, `x bar y`, string(outAdd)) + hcd.diffLineWithHighlight(DiffLineDel, codeA, codeB) + } +} + +func TestDiffWithHighlight(t *testing.T) { + t.Run("DiffLineAddDel", func(t *testing.T) { + t.Run("WithDiffTags", func(t *testing.T) { + hcd := newHighlightCodeDiff() + codeA := template.HTML(`x foo y`) + codeB := template.HTML(`x bar y`) + outDel := hcd.diffLineWithHighlight(DiffLineDel, codeA, codeB) + assert.Equal(t, `x foo y`, string(outDel)) + outAdd := hcd.diffLineWithHighlight(DiffLineAdd, codeA, codeB) + assert.Equal(t, `x bar y`, string(outAdd)) + }) + t.Run("NoRedundantTags", func(t *testing.T) { + // the equal parts only contain spaces, in this case, don't use "added/removed" tags + // because the diff lines already have a background color to indicate the change + hcd := newHighlightCodeDiff() + codeA := template.HTML(" \tfoo ") + codeB := template.HTML(" bar \n") + outDel := hcd.diffLineWithHighlight(DiffLineDel, codeA, codeB) + assert.Equal(t, string(codeA), string(outDel)) + outAdd := hcd.diffLineWithHighlight(DiffLineAdd, codeA, codeB) + assert.Equal(t, string(codeB), string(outAdd)) + }) }) t.Run("CleanUp", func(t *testing.T) { hcd := newHighlightCodeDiff() - codeA := template.HTML(`this is a comment`) - codeB := template.HTML(`this is updated comment`) + codeA := template.HTML(` this is a comment`) + codeB := template.HTML(` this is updated comment`) outDel := hcd.diffLineWithHighlight(DiffLineDel, codeA, codeB) - assert.Equal(t, `this is a comment`, string(outDel)) + assert.Equal(t, ` this is a comment`, string(outDel)) outAdd := hcd.diffLineWithHighlight(DiffLineAdd, codeA, codeB) - assert.Equal(t, `this is updated comment`, string(outAdd)) + assert.Equal(t, ` this is updated comment`, string(outAdd)) + + codeA = `line1` + "\n" + `line2` + codeB = `line1` + "\n" + `line!` + outDel = hcd.diffLineWithHighlight(DiffLineDel, codeA, codeB) + assert.Equal(t, `line1`+"\n"+`line2`, string(outDel)) + outAdd = hcd.diffLineWithHighlight(DiffLineAdd, codeA, codeB) + assert.Equal(t, `line1`+"\n"+`line!`, string(outAdd)) }) t.Run("OpenCloseTags", func(t *testing.T) { @@ -40,6 +74,55 @@ func TestDiffWithHighlight(t *testing.T) { assert.Equal(t, "", string(hcd.recoverOneDiff("O"))) assert.Empty(t, string(hcd.recoverOneDiff("C"))) }) + + t.Run("ComplexDiff1", func(t *testing.T) { + oldCode, _ := highlight.RenderCodeFast("a.go", "Go", `xxx || yyy`) + newCode, _ := highlight.RenderCodeFast("a.go", "Go", `bot&xxx || bot&yyy`) + hcd := newHighlightCodeDiff() + out := hcd.diffLineWithHighlight(DiffLineAdd, oldCode, newCode) + assert.Equal(t, strings.ReplaceAll(` +bot& +xxx || +bot& +yyy`, "\n", ""), string(out)) + }) + + forceTokenAsPlaceholder := func(hcd *highlightCodeDiff, r rune, token string) rune { + // for testing purpose only + hcd.tokenPlaceholderMap[token] = r + hcd.placeholderTokenMap[r] = token + return r + } + + t.Run("ComplexDiff2", func(t *testing.T) { + // When running "diffLineWithHighlight", the newly inserted "added-code", and "removed-code" tags may break the original layout. + // The newly inserted tags can appear in any position, because the "diff" algorithm can make outputs like: + // * Equal: + // * Insert: xxyy + // * Equal: zz + // Then the newly inserted tags will make this output, the tags mismatch. + // * xxyy zz + // So we need to fix it to: + // * xx yy zz + hcd := newHighlightCodeDiff() + hcd.diffCodeAddedOpen = forceTokenAsPlaceholder(hcd, '[', "") + hcd.diffCodeClose = forceTokenAsPlaceholder(hcd, ']', "") + forceTokenAsPlaceholder(hcd, '{', "") + forceTokenAsPlaceholder(hcd, '}', "") + assert.Equal(t, `aaxxyyzzbb`, string(hcd.recoverOneDiff("aa{xx[yy]zz}bb"))) + assert.Equal(t, `aaxxyyzzbb`, string(hcd.recoverOneDiff("aa[xx{yy}zz]bb"))) + assert.Equal(t, `aaxxyyzzbb`, string(hcd.recoverOneDiff("aa{xx[yy}zz]bb"))) + assert.Equal(t, `aaxxyyzzbb`, string(hcd.recoverOneDiff("aa[xx{yy]zz}bb"))) + assert.Equal(t, `aaxxyyzzbbcc`, string(hcd.recoverOneDiff("aa[xx{yy][zz}bb]cc"))) + + // And do a simple test for "diffCodeRemovedOpen", it shares the same logic as "diffCodeAddedOpen" + hcd = newHighlightCodeDiff() + hcd.diffCodeRemovedOpen = forceTokenAsPlaceholder(hcd, '[', "") + hcd.diffCodeClose = forceTokenAsPlaceholder(hcd, ']', "") + forceTokenAsPlaceholder(hcd, '{', "") + forceTokenAsPlaceholder(hcd, '}', "") + assert.Equal(t, `aaxxyyzzbbcc`, string(hcd.recoverOneDiff("aa[xx{yy][zz}bb]cc"))) + }) } func TestDiffWithHighlightPlaceholder(t *testing.T) { @@ -64,6 +147,11 @@ func TestDiffWithHighlightPlaceholderExhausted(t *testing.T) { assert.Equal(t, placeHolderAmp+"lt;", string(output)) output = hcd.diffLineWithHighlight(DiffLineAdd, `<`, `>`) assert.Equal(t, placeHolderAmp+"gt;", string(output)) + + output = hcd.diffLineWithHighlight(DiffLineDel, `foo`, `bar`) + assert.Equal(t, "foo", string(output)) + output = hcd.diffLineWithHighlight(DiffLineAdd, `foo`, `bar`) + assert.Equal(t, "bar", string(output)) } func TestDiffWithHighlightTagMatch(t *testing.T) { diff --git a/services/migrations/codebase_test.go b/services/migrations/codebase_test.go index 6cd52e5e59..dabe7e1ac9 100644 --- a/services/migrations/codebase_test.go +++ b/services/migrations/codebase_test.go @@ -54,12 +54,12 @@ func TestCodebaseDownloadRepo(t *testing.T) { assertMilestonesEqual(t, []*base.Milestone{ { Title: "Milestone1", - Deadline: timePtr(time.Date(2021, time.September, 16, 0, 0, 0, 0, time.UTC)), + Deadline: new(time.Date(2021, time.September, 16, 0, 0, 0, 0, time.UTC)), }, { Title: "Milestone2", - Deadline: timePtr(time.Date(2021, time.September, 17, 0, 0, 0, 0, time.UTC)), - Closed: timePtr(time.Date(2021, time.September, 17, 0, 0, 0, 0, time.UTC)), + Deadline: new(time.Date(2021, time.September, 17, 0, 0, 0, 0, time.UTC)), + Closed: new(time.Date(2021, time.September, 17, 0, 0, 0, 0, time.UTC)), State: "closed", }, }, milestones) diff --git a/services/migrations/codecommit.go b/services/migrations/codecommit.go index d08b2e6d4a..188280273f 100644 --- a/services/migrations/codecommit.go +++ b/services/migrations/codecommit.go @@ -14,7 +14,6 @@ import ( "code.gitea.io/gitea/modules/log" base "code.gitea.io/gitea/modules/migration" "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/service/codecommit" @@ -87,7 +86,7 @@ type CodeCommitDownloader struct { // GetRepoInfo returns a repository information func (c *CodeCommitDownloader) GetRepoInfo(ctx context.Context) (*base.Repository, error) { output, err := c.codeCommitClient.GetRepository(ctx, &codecommit.GetRepositoryInput{ - RepositoryName: util.ToPointer(c.repoName), + RepositoryName: new(c.repoName), }) if err != nil { return nil, err @@ -119,7 +118,7 @@ func (c *CodeCommitDownloader) GetComments(ctx context.Context, commentable base for { resp, err := c.codeCommitClient.GetCommentsForPullRequest(ctx, &codecommit.GetCommentsForPullRequestInput{ NextToken: nextToken, - PullRequestId: util.ToPointer(strconv.FormatInt(commentable.GetForeignIndex(), 10)), + PullRequestId: new(strconv.FormatInt(commentable.GetForeignIndex(), 10)), }) if err != nil { return nil, false, err @@ -161,7 +160,7 @@ func (c *CodeCommitDownloader) GetPullRequests(ctx context.Context, page, perPag prs := make([]*base.PullRequest, 0, len(batch)) for _, id := range batch { output, err := c.codeCommitClient.GetPullRequest(ctx, &codecommit.GetPullRequestInput{ - PullRequestId: util.ToPointer(id), + PullRequestId: new(id), }) if err != nil { return nil, false, err @@ -241,7 +240,7 @@ func (c *CodeCommitDownloader) getAllPullRequestIDs(ctx context.Context) ([]stri for { output, err := c.codeCommitClient.ListPullRequests(ctx, &codecommit.ListPullRequestsInput{ - RepositoryName: util.ToPointer(c.repoName), + RepositoryName: new(c.repoName), NextToken: nextToken, }) if err != nil { diff --git a/services/migrations/gitea_downloader.go b/services/migrations/gitea_downloader.go index 5d48d2f003..242873e551 100644 --- a/services/migrations/gitea_downloader.go +++ b/services/migrations/gitea_downloader.go @@ -345,25 +345,43 @@ func (g *GiteaDownloader) GetReleases(ctx context.Context) ([]*base.Release, err return releases, nil } -func (g *GiteaDownloader) getIssueReactions(index int64) ([]*base.Reaction, error) { - var reactions []*base.Reaction +func (g *GiteaDownloader) getIssueReactions(ctx context.Context, index int64) ([]*base.Reaction, error) { if err := g.client.CheckServerVersionConstraint(">=1.11"); err != nil { log.Info("GiteaDownloader: instance to old, skip getIssueReactions") - return reactions, nil - } - rl, _, err := g.client.GetIssueReactions(g.repoOwner, g.repoName, index) - if err != nil { - return nil, err + return nil, nil } - for _, reaction := range rl { - reactions = append(reactions, &base.Reaction{ - UserID: reaction.User.ID, - UserName: reaction.User.UserName, - Content: reaction.Reaction, - }) + allReactions := make([]*base.Reaction, 0, g.maxPerPage) + + for i := 1; ; i++ { + // make sure gitea can shutdown gracefully + select { + case <-ctx.Done(): + return nil, nil + default: + } + + reactions, _, err := g.client.ListIssueReactions(g.repoOwner, g.repoName, index, gitea_sdk.ListIssueReactionsOptions{ListOptions: gitea_sdk.ListOptions{ + PageSize: g.maxPerPage, + Page: i, + }}) + if err != nil { + return nil, err + } + + for _, reaction := range reactions { + allReactions = append(allReactions, &base.Reaction{ + UserID: reaction.User.ID, + UserName: reaction.User.UserName, + Content: reaction.Reaction, + }) + } + + if !g.pagination || len(reactions) < g.maxPerPage { + break + } } - return reactions, nil + return allReactions, nil } func (g *GiteaDownloader) getCommentReactions(commentID int64) ([]*base.Reaction, error) { @@ -388,7 +406,7 @@ func (g *GiteaDownloader) getCommentReactions(commentID int64) ([]*base.Reaction } // GetIssues returns issues according start and limit -func (g *GiteaDownloader) GetIssues(_ context.Context, page, perPage int) ([]*base.Issue, bool, error) { +func (g *GiteaDownloader) GetIssues(ctx context.Context, page, perPage int) ([]*base.Issue, bool, error) { if perPage > g.maxPerPage { perPage = g.maxPerPage } @@ -413,7 +431,7 @@ func (g *GiteaDownloader) GetIssues(_ context.Context, page, perPage int) ([]*ba milestone = issue.Milestone.Title } - reactions, err := g.getIssueReactions(issue.Index) + reactions, err := g.getIssueReactions(ctx, issue.Index) if err != nil { WarnAndNotice("Unable to load reactions during migrating issue #%d in %s. Error: %v", issue.Index, g, err) } @@ -497,7 +515,7 @@ func (g *GiteaDownloader) GetComments(ctx context.Context, commentable base.Comm } // GetPullRequests returns pull requests according page and perPage -func (g *GiteaDownloader) GetPullRequests(_ context.Context, page, perPage int) ([]*base.PullRequest, bool, error) { +func (g *GiteaDownloader) GetPullRequests(ctx context.Context, page, perPage int) ([]*base.PullRequest, bool, error) { if perPage > g.maxPerPage { perPage = g.maxPerPage } @@ -546,7 +564,7 @@ func (g *GiteaDownloader) GetPullRequests(_ context.Context, page, perPage int) mergeCommitSHA = *pr.MergedCommitID } - reactions, err := g.getIssueReactions(pr.Index) + reactions, err := g.getIssueReactions(ctx, pr.Index) if err != nil { WarnAndNotice("Unable to load reactions during migrating pull #%d in %s. Error: %v", pr.Index, g, err) } diff --git a/services/migrations/gitea_downloader_test.go b/services/migrations/gitea_downloader_test.go index 1a9094f6f2..cf727b44c7 100644 --- a/services/migrations/gitea_downloader_test.go +++ b/services/migrations/gitea_downloader_test.go @@ -86,16 +86,16 @@ func TestGiteaDownloadRepo(t *testing.T) { { Title: "V2 Finalize", Created: time.Unix(0, 0), - Deadline: timePtr(time.Unix(1599263999, 0)), - Updated: timePtr(time.Unix(0, 0)), + Deadline: new(time.Unix(1599263999, 0)), + Updated: new(time.Unix(0, 0)), State: "open", }, { Title: "V1", Description: "Generate Content", Created: time.Unix(0, 0), - Updated: timePtr(time.Unix(0, 0)), - Closed: timePtr(time.Unix(1598985406, 0)), + Updated: new(time.Unix(0, 0)), + Closed: new(time.Unix(1598985406, 0)), State: "closed", }, }, milestones) @@ -171,7 +171,7 @@ func TestGiteaDownloadRepo(t *testing.T) { Content: "laugh", }, }, - Closed: timePtr(time.Date(2020, 9, 1, 15, 49, 34, 0, time.UTC)), + Closed: new(time.Date(2020, 9, 1, 15, 49, 34, 0, time.UTC)), }, { Number: 2, @@ -190,7 +190,7 @@ func TestGiteaDownloadRepo(t *testing.T) { Color: "d4c5f9", Description: "", }}, - Closed: timePtr(time.Unix(1598969497, 0)), + Closed: new(time.Unix(1598969497, 0)), }, }, issues) @@ -237,7 +237,7 @@ func TestGiteaDownloadRepo(t *testing.T) { IsLocked: false, Created: time.Unix(1598982759, 0), Updated: time.Unix(1599023425, 0), - Closed: timePtr(time.Unix(1598982934, 0)), + Closed: new(time.Unix(1598982934, 0)), Assignees: []string{"techknowlogick"}, Base: base.PullRequestBranch{ CloneURL: "", @@ -254,7 +254,7 @@ func TestGiteaDownloadRepo(t *testing.T) { OwnerName: "6543-forks", }, Merged: true, - MergedTime: timePtr(time.Unix(1598982934, 0)), + MergedTime: new(time.Unix(1598982934, 0)), MergeCommitSHA: "827aa28a907853e5ddfa40c8f9bc52471a2685fd", PatchURL: "https://gitea.com/gitea/test_repo/pulls/12.patch", }, prs[1]) diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index 6f5b9bb33a..af9a0e0eaf 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -364,11 +364,12 @@ func (g *GiteaLocalUploader) CreateReleases(ctx context.Context, releases ...*ba // SyncTags syncs releases with tags in the database func (g *GiteaLocalUploader) SyncTags(ctx context.Context) error { - return repo_module.SyncReleasesWithTags(ctx, g.repo, g.gitRepo) + _, err := repo_module.SyncReleasesWithTags(ctx, g.repo, g.gitRepo) + return err } func (g *GiteaLocalUploader) SyncBranches(ctx context.Context) error { - _, err := repo_module.SyncRepoBranchesWithRepo(ctx, g.repo, g.gitRepo, g.doer.ID) + _, _, err := repo_module.SyncRepoBranchesWithRepo(ctx, g.repo, g.gitRepo, g.doer.ID) return err } diff --git a/services/migrations/github_test.go b/services/migrations/github_test.go index 6d1a5378b9..198062f7cf 100644 --- a/services/migrations/github_test.go +++ b/services/migrations/github_test.go @@ -47,19 +47,19 @@ func TestGitHubDownloadRepo(t *testing.T) { { Title: "1.0.0", Description: "Milestone 1.0.0", - Deadline: timePtr(time.Date(2019, 11, 11, 8, 0, 0, 0, time.UTC)), + Deadline: new(time.Date(2019, 11, 11, 8, 0, 0, 0, time.UTC)), Created: time.Date(2019, 11, 12, 19, 37, 8, 0, time.UTC), - Updated: timePtr(time.Date(2019, 11, 12, 21, 56, 17, 0, time.UTC)), - Closed: timePtr(time.Date(2019, 11, 12, 19, 45, 49, 0, time.UTC)), + Updated: new(time.Date(2019, 11, 12, 21, 56, 17, 0, time.UTC)), + Closed: new(time.Date(2019, 11, 12, 19, 45, 49, 0, time.UTC)), State: "closed", }, { Title: "1.1.0", Description: "Milestone 1.1.0", - Deadline: timePtr(time.Date(2019, 11, 12, 8, 0, 0, 0, time.UTC)), + Deadline: new(time.Date(2019, 11, 12, 8, 0, 0, 0, time.UTC)), Created: time.Date(2019, 11, 12, 19, 37, 25, 0, time.UTC), - Updated: timePtr(time.Date(2019, 11, 12, 21, 39, 27, 0, time.UTC)), - Closed: timePtr(time.Date(2019, 11, 12, 19, 45, 46, 0, time.UTC)), + Updated: new(time.Date(2019, 11, 12, 21, 39, 27, 0, time.UTC)), + Closed: new(time.Date(2019, 11, 12, 19, 45, 46, 0, time.UTC)), State: "closed", }, }, milestones) @@ -163,7 +163,7 @@ func TestGitHubDownloadRepo(t *testing.T) { Content: "+1", }, }, - Closed: timePtr(time.Date(2019, 11, 12, 20, 22, 22, 0, time.UTC)), + Closed: new(time.Date(2019, 11, 12, 20, 22, 22, 0, time.UTC)), }, { Number: 2, @@ -214,7 +214,7 @@ func TestGitHubDownloadRepo(t *testing.T) { Content: "+1", }, }, - Closed: timePtr(time.Date(2019, 11, 12, 21, 1, 31, 0, time.UTC)), + Closed: new(time.Date(2019, 11, 12, 21, 1, 31, 0, time.UTC)), }, }, issues) @@ -284,9 +284,9 @@ func TestGitHubDownloadRepo(t *testing.T) { OwnerName: "go-gitea", RepoName: "test_repo", }, - Closed: timePtr(time.Date(2019, 11, 12, 21, 39, 27, 0, time.UTC)), + Closed: new(time.Date(2019, 11, 12, 21, 39, 27, 0, time.UTC)), Merged: true, - MergedTime: timePtr(time.Date(2019, 11, 12, 21, 39, 27, 0, time.UTC)), + MergedTime: new(time.Date(2019, 11, 12, 21, 39, 27, 0, time.UTC)), MergeCommitSHA: "f32b0a9dfd09a60f616f29158f772cedd89942d2", ForeignIndex: 3, }, diff --git a/services/migrations/gitlab_test.go b/services/migrations/gitlab_test.go index fef1053ec8..9e4050289d 100644 --- a/services/migrations/gitlab_test.go +++ b/services/migrations/gitlab_test.go @@ -59,14 +59,14 @@ func TestGitlabDownloadRepo(t *testing.T) { { Title: "1.1.0", Created: time.Date(2019, 11, 28, 8, 42, 44, 575000000, time.UTC), - Updated: timePtr(time.Date(2019, 11, 28, 8, 42, 44, 575000000, time.UTC)), + Updated: new(time.Date(2019, 11, 28, 8, 42, 44, 575000000, time.UTC)), State: "active", }, { Title: "1.0.0", Created: time.Date(2019, 11, 28, 8, 42, 30, 301000000, time.UTC), - Updated: timePtr(time.Date(2019, 11, 28, 15, 57, 52, 401000000, time.UTC)), - Closed: timePtr(time.Date(2019, 11, 28, 15, 57, 52, 401000000, time.UTC)), + Updated: new(time.Date(2019, 11, 28, 15, 57, 52, 401000000, time.UTC)), + Closed: new(time.Date(2019, 11, 28, 15, 57, 52, 401000000, time.UTC)), State: "closed", }, }, milestones) @@ -161,7 +161,7 @@ func TestGitlabDownloadRepo(t *testing.T) { Content: "open_mouth", }, }, - Closed: timePtr(time.Date(2019, 11, 28, 8, 46, 23, 275000000, time.UTC)), + Closed: new(time.Date(2019, 11, 28, 8, 46, 23, 275000000, time.UTC)), }, { Number: 2, @@ -210,7 +210,7 @@ func TestGitlabDownloadRepo(t *testing.T) { Content: "hearts", }, }, - Closed: timePtr(time.Date(2019, 11, 28, 8, 45, 44, 959000000, time.UTC)), + Closed: new(time.Date(2019, 11, 28, 8, 45, 44, 959000000, time.UTC)), }, }, issues) diff --git a/services/migrations/main_test.go b/services/migrations/main_test.go index 581af614f9..9893b4e7d1 100644 --- a/services/migrations/main_test.go +++ b/services/migrations/main_test.go @@ -18,10 +18,6 @@ func TestMain(m *testing.M) { unittest.MainTest(m) } -func timePtr(t time.Time) *time.Time { - return &t -} - func assertTimeEqual(t *testing.T, expected, actual time.Time) { assert.Equal(t, expected.UTC(), actual.UTC()) } diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 3a1ed2b7d9..576dcb95f6 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -29,9 +29,6 @@ import ( repo_service "code.gitea.io/gitea/services/repository" ) -// gitShortEmptySha Git short empty SHA -const gitShortEmptySha = "0000000" - // UpdateAddress writes new address to Git repository and database func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error { u, err := giturl.ParseGitURL(addr) @@ -72,127 +69,6 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error return repo_model.UpdateRepositoryColsNoAutoTime(ctx, m.Repo, "original_url") } -// mirrorSyncResult contains information of a updated reference. -// If the oldCommitID is "0000000", it means a new reference, the value of newCommitID is empty. -// If the newCommitID is "0000000", it means the reference is deleted, the value of oldCommitID is empty. -type mirrorSyncResult struct { - refName git.RefName - oldCommitID string - newCommitID string -} - -// parseRemoteUpdateOutput detects create, update and delete operations of references from upstream. -// possible output example: -/* -// * [new tag] v0.1.8 -> v0.1.8 -// * [new branch] master -> origin/master -// * [new ref] refs/pull/2/head -> refs/pull/2/head" -// - [deleted] (none) -> origin/test // delete a branch -// - [deleted] (none) -> 1 // delete a tag -// 957a993..a87ba5f test -> origin/test -// + f895a1e...957a993 test -> origin/test (forced update) -*/ -// TODO: return whether it's a force update -func parseRemoteUpdateOutput(output, remoteName string) []*mirrorSyncResult { - results := make([]*mirrorSyncResult, 0, 3) - lines := strings.Split(output, "\n") - for i := range lines { - // Make sure reference name is presented before continue - idx := strings.Index(lines[i], "-> ") - if idx == -1 { - continue - } - - refName := strings.TrimSpace(lines[i][idx+3:]) - - switch { - case strings.HasPrefix(lines[i], " * [new tag]"): // new tag - results = append(results, &mirrorSyncResult{ - refName: git.RefNameFromTag(refName), - oldCommitID: gitShortEmptySha, - }) - case strings.HasPrefix(lines[i], " * [new branch]"): // new branch - refName = strings.TrimPrefix(refName, remoteName+"/") - results = append(results, &mirrorSyncResult{ - refName: git.RefNameFromBranch(refName), - oldCommitID: gitShortEmptySha, - }) - case strings.HasPrefix(lines[i], " * [new ref]"): // new reference - results = append(results, &mirrorSyncResult{ - refName: git.RefName(refName), - oldCommitID: gitShortEmptySha, - }) - case strings.HasPrefix(lines[i], " - "): // Delete reference - isTag := !strings.HasPrefix(refName, remoteName+"/") - var refFullName git.RefName - if strings.HasPrefix(refName, "refs/") { - refFullName = git.RefName(refName) - } else if isTag { - refFullName = git.RefNameFromTag(refName) - } else { - refFullName = git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/")) - } - results = append(results, &mirrorSyncResult{ - refName: refFullName, - newCommitID: gitShortEmptySha, - }) - case strings.HasPrefix(lines[i], " + "): // Force update - if idx := strings.Index(refName, " "); idx > -1 { - refName = refName[:idx] - } - delimIdx := strings.Index(lines[i][3:], " ") - if delimIdx == -1 { - log.Error("SHA delimiter not found: %q", lines[i]) - continue - } - shas := strings.Split(lines[i][3:delimIdx+3], "...") - if len(shas) != 2 { - log.Error("Expect two SHAs but not what found: %q", lines[i]) - continue - } - var refFullName git.RefName - if strings.HasPrefix(refName, "refs/") { - refFullName = git.RefName(refName) - } else { - refFullName = git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/")) - } - - results = append(results, &mirrorSyncResult{ - refName: refFullName, - oldCommitID: shas[0], - newCommitID: shas[1], - }) - case strings.HasPrefix(lines[i], " "): // New commits of a reference - delimIdx := strings.Index(lines[i][3:], " ") - if delimIdx == -1 { - log.Error("SHA delimiter not found: %q", lines[i]) - continue - } - shas := strings.Split(lines[i][3:delimIdx+3], "..") - if len(shas) != 2 { - log.Error("Expect two SHAs but not what found: %q", lines[i]) - continue - } - var refFullName git.RefName - if strings.HasPrefix(refName, "refs/") { - refFullName = git.RefName(refName) - } else { - refFullName = git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/")) - } - - results = append(results, &mirrorSyncResult{ - refName: refFullName, - oldCommitID: shas[0], - newCommitID: shas[1], - }) - - default: - log.Warn("parseRemoteUpdateOutput: unexpected update line %q", lines[i]) - } - } - return results -} - func pruneBrokenReferences(ctx context.Context, m *repo_model.Mirror, gitRepo gitrepo.Repository, timeout time.Duration) error { cmd := gitcmd.NewCommand("remote", "prune").AddDynamicArguments(m.GetRemoteName()).WithTimeout(timeout) stdout, _, pruneErr := gitrepo.RunCmdString(ctx, gitRepo, cmd) @@ -229,7 +105,7 @@ func checkRecoverableSyncError(stderrMessage string) bool { } // runSync returns true if sync finished without error. -func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bool) { +func runSync(ctx context.Context, m *repo_model.Mirror) ([]*repo_module.SyncResult, bool) { log.Trace("SyncMirrors [repo: %-v]: running git remote update...", m.Repo) remoteURL, remoteErr := gitrepo.GitRemoteGetURL(ctx, m.Repo, m.GetRemoteName()) @@ -250,7 +126,6 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo } var err error - var fetchOutput string // it is from fetch's stderr fetchStdout, fetchStderr, err := gitrepo.RunCmdString(ctx, m.Repo, cmdFetch()) if err != nil { // sanitize the output, since it may contain the remote address, which may contain a password @@ -284,8 +159,6 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo return nil, false } } - fetchOutput = fetchStderr // the result of "git fetch" is in stderr - if err := gitrepo.WriteCommitGraph(ctx, m.Repo); err != nil { log.Error("SyncMirrors [repo: %-v]: %v", m.Repo, err) } @@ -306,14 +179,17 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo } log.Trace("SyncMirrors [repo: %-v]: syncing branches...", m.Repo) - if _, err = repo_module.SyncRepoBranchesWithRepo(ctx, m.Repo, gitRepo, 0); err != nil { + _, results, err := repo_module.SyncRepoBranchesWithRepo(ctx, m.Repo, gitRepo, 0) + if err != nil { log.Error("SyncMirrors [repo: %-v]: failed to synchronize branches: %v", m.Repo, err) } log.Trace("SyncMirrors [repo: %-v]: syncing releases with tags...", m.Repo) - if err = repo_module.SyncReleasesWithTags(ctx, m.Repo, gitRepo); err != nil { + tagResults, err := repo_module.SyncReleasesWithTags(ctx, m.Repo, gitRepo) + if err != nil { log.Error("SyncMirrors [repo: %-v]: failed to synchronize tags to releases: %v", m.Repo, err) } + results = append(results, tagResults...) gitRepo.Close() log.Trace("SyncMirrors [repo: %-v]: updating size of repository", m.Repo) @@ -381,7 +257,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo } m.UpdatedUnix = timeutil.TimeStampNow() - return parseRemoteUpdateOutput(fetchOutput, m.GetRemoteName()), true + return results, true } func getRepoPullMirrorLockKey(repoID int64) string { @@ -450,42 +326,42 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { for _, result := range results { // Discard GitHub pull requests, i.e. refs/pull/* - if result.refName.IsPull() { + if result.RefName.IsPull() { continue } // Create reference - if result.oldCommitID == gitShortEmptySha { - commitID, err := gitRepo.GetRefCommitID(result.refName.String()) + if result.OldCommitID == "" { + commitID, err := gitRepo.GetRefCommitID(result.RefName.String()) if err != nil { - log.Error("SyncMirrors [repo: %-v]: unable to GetRefCommitID [ref_name: %s]: %v", m.Repo, result.refName, err) + log.Error("SyncMirrors [repo: %-v]: unable to GetRefCommitID [ref_name: %s]: %v", m.Repo, result.RefName, err) continue } objectFormat := git.ObjectFormatFromName(m.Repo.ObjectFormatName) notify_service.SyncPushCommits(ctx, m.Repo.MustOwner(ctx), m.Repo, &repo_module.PushUpdateOptions{ - RefFullName: result.refName, + RefFullName: result.RefName, OldCommitID: objectFormat.EmptyObjectID().String(), NewCommitID: commitID, }, repo_module.NewPushCommits()) - notify_service.SyncCreateRef(ctx, m.Repo.MustOwner(ctx), m.Repo, result.refName, commitID) + notify_service.SyncCreateRef(ctx, m.Repo.MustOwner(ctx), m.Repo, result.RefName, commitID) continue } // Delete reference - if result.newCommitID == gitShortEmptySha { - notify_service.SyncDeleteRef(ctx, m.Repo.MustOwner(ctx), m.Repo, result.refName) + if result.NewCommitID == "" { + notify_service.SyncDeleteRef(ctx, m.Repo.MustOwner(ctx), m.Repo, result.RefName) continue } // Push commits - oldCommitID, err := gitrepo.GetFullCommitID(ctx, repo, result.oldCommitID) + oldCommitID, err := gitrepo.GetFullCommitID(ctx, repo, result.OldCommitID) if err != nil { - log.Error("SyncMirrors [repo: %-v]: unable to get GetFullCommitID[%s]: %v", m.Repo, result.oldCommitID, err) + log.Error("SyncMirrors [repo: %-v]: unable to get GetFullCommitID[%s]: %v", m.Repo, result.OldCommitID, err) continue } - newCommitID, err := gitrepo.GetFullCommitID(ctx, repo, result.newCommitID) + newCommitID, err := gitrepo.GetFullCommitID(ctx, repo, result.NewCommitID) if err != nil { - log.Error("SyncMirrors [repo: %-v]: unable to get GetFullCommitID [%s]: %v", m.Repo, result.newCommitID, err) + log.Error("SyncMirrors [repo: %-v]: unable to get GetFullCommitID [%s]: %v", m.Repo, result.NewCommitID, err) continue } commits, err := gitRepo.CommitsBetweenIDs(newCommitID, oldCommitID) @@ -509,7 +385,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { theCommits.CompareURL = m.Repo.ComposeCompareURL(oldCommitID, newCommitID) notify_service.SyncPushCommits(ctx, m.Repo.MustOwner(ctx), m.Repo, &repo_module.PushUpdateOptions{ - RefFullName: result.refName, + RefFullName: result.RefName, OldCommitID: oldCommitID, NewCommitID: newCommitID, }, theCommits) @@ -548,7 +424,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { return true } -func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, results []*mirrorSyncResult) bool { +func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, results []*repo_module.SyncResult) bool { if !m.Repo.IsEmpty { return true } @@ -562,11 +438,11 @@ func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, re } firstName := "" for _, result := range results { - if !result.refName.IsBranch() { + if !result.RefName.IsBranch() { continue } - name := result.refName.BranchName() + name := result.RefName.BranchName() if len(firstName) == 0 { firstName = name } diff --git a/services/mirror/mirror_pull_test.go b/services/mirror/mirror_pull_test.go index 97859be5b0..58b4da51a2 100644 --- a/services/mirror/mirror_pull_test.go +++ b/services/mirror/mirror_pull_test.go @@ -9,62 +9,6 @@ import ( "github.com/stretchr/testify/assert" ) -func Test_parseRemoteUpdateOutput(t *testing.T) { - output := ` - * [new tag] v0.1.8 -> v0.1.8 - * [new branch] master -> origin/master - - [deleted] (none) -> origin/test1 - - [deleted] (none) -> tag1 - + f895a1e...957a993 test2 -> origin/test2 (forced update) - 957a993..a87ba5f test3 -> origin/test3 - * [new ref] refs/pull/26595/head -> refs/pull/26595/head - * [new ref] refs/pull/26595/merge -> refs/pull/26595/merge - e0639e38fb..6db2410489 refs/pull/25873/head -> refs/pull/25873/head - + 1c97ebc746...976d27d52f refs/pull/25873/merge -> refs/pull/25873/merge (forced update) -` - results := parseRemoteUpdateOutput(output, "origin") - assert.Len(t, results, 10) - assert.Equal(t, "refs/tags/v0.1.8", results[0].refName.String()) - assert.Equal(t, gitShortEmptySha, results[0].oldCommitID) - assert.Empty(t, results[0].newCommitID) - - assert.Equal(t, "refs/heads/master", results[1].refName.String()) - assert.Equal(t, gitShortEmptySha, results[1].oldCommitID) - assert.Empty(t, results[1].newCommitID) - - assert.Equal(t, "refs/heads/test1", results[2].refName.String()) - assert.Empty(t, results[2].oldCommitID) - assert.Equal(t, gitShortEmptySha, results[2].newCommitID) - - assert.Equal(t, "refs/tags/tag1", results[3].refName.String()) - assert.Empty(t, results[3].oldCommitID) - assert.Equal(t, gitShortEmptySha, results[3].newCommitID) - - assert.Equal(t, "refs/heads/test2", results[4].refName.String()) - assert.Equal(t, "f895a1e", results[4].oldCommitID) - assert.Equal(t, "957a993", results[4].newCommitID) - - assert.Equal(t, "refs/heads/test3", results[5].refName.String()) - assert.Equal(t, "957a993", results[5].oldCommitID) - assert.Equal(t, "a87ba5f", results[5].newCommitID) - - assert.Equal(t, "refs/pull/26595/head", results[6].refName.String()) - assert.Equal(t, gitShortEmptySha, results[6].oldCommitID) - assert.Empty(t, results[6].newCommitID) - - assert.Equal(t, "refs/pull/26595/merge", results[7].refName.String()) - assert.Equal(t, gitShortEmptySha, results[7].oldCommitID) - assert.Empty(t, results[7].newCommitID) - - assert.Equal(t, "refs/pull/25873/head", results[8].refName.String()) - assert.Equal(t, "e0639e38fb", results[8].oldCommitID) - assert.Equal(t, "6db2410489", results[8].newCommitID) - - assert.Equal(t, "refs/pull/25873/merge", results[9].refName.String()) - assert.Equal(t, "1c97ebc746", results[9].oldCommitID) - assert.Equal(t, "976d27d52f", results[9].newCommitID) -} - func Test_checkRecoverableSyncError(t *testing.T) { cases := []struct { recoverable bool diff --git a/services/packages/auth.go b/services/packages/auth.go index 6e87643e29..6fcc408adc 100644 --- a/services/packages/auth.go +++ b/services/packages/auth.go @@ -23,21 +23,24 @@ type packageClaims struct { PackageMeta } type PackageMeta struct { - UserID int64 - Scope auth_model.AccessTokenScope + UserID int64 + Scope auth_model.AccessTokenScope + ActionsUserTaskID int64 } func CreateAuthorizationToken(u *user_model.User, packageScope auth_model.AccessTokenScope) (string, error) { now := time.Now() + actionsUserTaskID, _ := user_model.GetActionsUserTaskID(u) claims := packageClaims{ RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(now.Add(24 * time.Hour)), NotBefore: jwt.NewNumericDate(now), }, PackageMeta: PackageMeta{ - UserID: u.ID, - Scope: packageScope, + UserID: u.ID, + Scope: packageScope, + ActionsUserTaskID: actionsUserTaskID, }, } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) diff --git a/services/repository/adopt.go b/services/repository/adopt.go index 13218193d5..64e7f3f02b 100644 --- a/services/repository/adopt.go +++ b/services/repository/adopt.go @@ -147,11 +147,11 @@ func adoptRepository(ctx context.Context, repo *repo_model.Repository, defaultBr } defer gitRepo.Close() - if _, err = repo_module.SyncRepoBranchesWithRepo(ctx, repo, gitRepo, 0); err != nil { + if _, _, err = repo_module.SyncRepoBranchesWithRepo(ctx, repo, gitRepo, 0); err != nil { return fmt.Errorf("SyncRepoBranchesWithRepo: %w", err) } - if err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil { + if _, err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil { return fmt.Errorf("SyncReleasesWithTags: %w", err) } diff --git a/services/repository/branch.go b/services/repository/branch.go index a580208af6..b3310b2e68 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -547,10 +547,11 @@ func UpdateBranch(ctx context.Context, repo *repo_model.Repository, gitRepo *git return gitrepo.Push(ctx, repo, repo, pushOpts) } -var ErrBranchIsDefault = util.ErrorWrap(util.ErrPermissionDenied, "branch is default") +var ErrBranchIsDefault = util.ErrorWrap(util.ErrPermissionDenied, "branch is default or pull request target") func CanDeleteBranch(ctx context.Context, repo *repo_model.Repository, branchName string, doer *user_model.User) error { - if branchName == repo.DefaultBranch { + unitPRConfig := repo.MustGetUnit(ctx, unit.TypePullRequests).PullRequestsConfig() + if branchName == repo.DefaultBranch || branchName == unitPRConfig.DefaultTargetBranch { return ErrBranchIsDefault } diff --git a/services/repository/files/content.go b/services/repository/files/content.go index d32d3041c2..fc0e00a1a7 100644 --- a/services/repository/files/content.go +++ b/services/repository/files/content.go @@ -173,18 +173,18 @@ func getFileContentsByEntryInternal(ctx context.Context, repo *repo_model.Reposi } if opts.IncludeCommitMetadata { - contentsResponse.LastCommitSHA = util.ToPointer(lastCommit.ID.String()) + contentsResponse.LastCommitSHA = new(lastCommit.ID.String()) // GitHub doesn't have these fields in the response, but we could follow other similar APIs to name them // https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28#list-commits if lastCommit.Committer != nil { - contentsResponse.LastCommitterDate = util.ToPointer(lastCommit.Committer.When) + contentsResponse.LastCommitterDate = new(lastCommit.Committer.When) } if lastCommit.Author != nil { - contentsResponse.LastAuthorDate = util.ToPointer(lastCommit.Author.When) + contentsResponse.LastAuthorDate = new(lastCommit.Author.When) } } if opts.IncludeCommitMessage { - contentsResponse.LastCommitMessage = util.ToPointer(lastCommit.Message()) + contentsResponse.LastCommitMessage = new(lastCommit.Message()) } } @@ -281,7 +281,7 @@ func GetBlobBySHA(repo *repo_model.Repository, gitRepo *git.Repository, sha stri return nil, err } - ret.Encoding, ret.Content = util.ToPointer("base64"), &content + ret.Encoding, ret.Content = new("base64"), &content if originContent != nil { ret.LfsOid, ret.LfsSize = parsePossibleLfsPointerBuffer(strings.NewReader(originContent.String())) } diff --git a/services/repository/files/content_test.go b/services/repository/files/content_test.go index d72f918074..dda5572ad0 100644 --- a/services/repository/files/content_test.go +++ b/services/repository/files/content_test.go @@ -8,7 +8,6 @@ import ( "code.gitea.io/gitea/models/unittest" api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/contexttest" _ "code.gitea.io/gitea/models/actions" @@ -37,8 +36,8 @@ func TestGetContents(t *testing.T) { ctx.SetPathParam("sha", sha) gbr, err := GetBlobBySHA(ctx.Repo.Repository, ctx.Repo.GitRepo, ctx.PathParam("sha")) expectedGBR := &api.GitBlobResponse{ - Content: util.ToPointer("dHJlZSAyYTJmMWQ0NjcwNzI4YTJlMTAwNDllMzQ1YmQ3YTI3NjQ2OGJlYWI2CmF1dGhvciB1c2VyMSA8YWRkcmVzczFAZXhhbXBsZS5jb20+IDE0ODk5NTY0NzkgLTA0MDAKY29tbWl0dGVyIEV0aGFuIEtvZW5pZyA8ZXRoYW50a29lbmlnQGdtYWlsLmNvbT4gMTQ4OTk1NjQ3OSAtMDQwMAoKSW5pdGlhbCBjb21taXQK"), - Encoding: util.ToPointer("base64"), + Content: new("dHJlZSAyYTJmMWQ0NjcwNzI4YTJlMTAwNDllMzQ1YmQ3YTI3NjQ2OGJlYWI2CmF1dGhvciB1c2VyMSA8YWRkcmVzczFAZXhhbXBsZS5jb20+IDE0ODk5NTY0NzkgLTA0MDAKY29tbWl0dGVyIEV0aGFuIEtvZW5pZyA8ZXRoYW50a29lbmlnQGdtYWlsLmNvbT4gMTQ4OTk1NjQ3OSAtMDQwMAoKSW5pdGlhbCBjb21taXQK"), + Encoding: new("base64"), URL: "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/65f1bf27bc3bf70f64657658635e66094edbcb4d", SHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d", Size: 180, diff --git a/services/repository/files/diff.go b/services/repository/files/diff.go index 50d01f9d7c..aa4b55a307 100644 --- a/services/repository/files/diff.go +++ b/services/repository/files/diff.go @@ -12,7 +12,7 @@ import ( ) // GetDiffPreview produces and returns diff result of a file which is not yet committed. -func GetDiffPreview(ctx context.Context, repo *repo_model.Repository, branch, treePath, content string) (*gitdiff.Diff, error) { +func GetDiffPreview(ctx context.Context, repo *repo_model.Repository, branch, treePath, oldContent, newContent string) (*gitdiff.Diff, error) { if branch == "" { branch = repo.DefaultBranch } @@ -29,7 +29,7 @@ func GetDiffPreview(ctx context.Context, repo *repo_model.Repository, branch, tr } // Add the object to the database - objectHash, err := t.HashObjectAndWrite(ctx, strings.NewReader(content)) + objectHash, err := t.HashObjectAndWrite(ctx, strings.NewReader(newContent)) if err != nil { return nil, err } @@ -38,5 +38,5 @@ func GetDiffPreview(ctx context.Context, repo *repo_model.Repository, branch, tr if err := t.AddObjectToIndex(ctx, "100644", objectHash, treePath); err != nil { return nil, err } - return t.DiffIndex(ctx) + return t.DiffIndex(ctx, oldContent, newContent) } diff --git a/services/repository/files/diff_test.go b/services/repository/files/diff_test.go index ae702e4189..5295879621 100644 --- a/services/repository/files/diff_test.go +++ b/services/repository/files/diff_test.go @@ -27,8 +27,30 @@ func TestGetDiffPreview(t *testing.T) { branch := ctx.Repo.Repository.DefaultBranch treePath := "README.md" + oldContent := "# repo1\n\nDescription for repo1" content := "# repo1\n\nDescription for repo1\nthis is a new line" + t.Run("Errors", func(t *testing.T) { + t.Run("empty repo", func(t *testing.T) { + diff, err := GetDiffPreview(ctx, &repo_model.Repository{}, branch, treePath, oldContent, content) + assert.Nil(t, diff) + assert.EqualError(t, err, "repository does not exist [id: 0, uid: 0, owner_name: , name: ]") + }) + + t.Run("bad branch", func(t *testing.T) { + badBranch := "bad_branch" + diff, err := GetDiffPreview(ctx, ctx.Repo.Repository, badBranch, treePath, oldContent, content) + assert.Nil(t, diff) + assert.EqualError(t, err, "branch does not exist [name: "+badBranch+"]") + }) + + t.Run("empty treePath", func(t *testing.T) { + diff, err := GetDiffPreview(ctx, ctx.Repo.Repository, branch, "", oldContent, content) + assert.Nil(t, diff) + assert.EqualError(t, err, "path is invalid [path: ]") + }) + }) + expectedDiff := &gitdiff.Diff{ Files: []*gitdiff.DiffFile{ { @@ -112,56 +134,22 @@ func TestGetDiffPreview(t *testing.T) { } t.Run("with given branch", func(t *testing.T) { - diff, err := GetDiffPreview(ctx, ctx.Repo.Repository, branch, treePath, content) + diff, err := GetDiffPreview(ctx, ctx.Repo.Repository, branch, treePath, oldContent, content) assert.NoError(t, err) expectedBs, err := json.Marshal(expectedDiff) assert.NoError(t, err) bs, err := json.Marshal(diff) assert.NoError(t, err) - assert.Equal(t, string(expectedBs), string(bs)) + assert.JSONEq(t, string(expectedBs), string(bs)) }) t.Run("empty branch, same results", func(t *testing.T) { - diff, err := GetDiffPreview(ctx, ctx.Repo.Repository, "", treePath, content) + diff, err := GetDiffPreview(ctx, ctx.Repo.Repository, "", treePath, oldContent, content) assert.NoError(t, err) expectedBs, err := json.Marshal(expectedDiff) assert.NoError(t, err) bs, err := json.Marshal(diff) assert.NoError(t, err) - assert.Equal(t, expectedBs, bs) - }) -} - -func TestGetDiffPreviewErrors(t *testing.T) { - unittest.PrepareTestEnv(t) - ctx, _ := contexttest.MockContext(t, "user2/repo1") - ctx.SetPathParam("id", "1") - contexttest.LoadRepo(t, ctx, 1) - contexttest.LoadRepoCommit(t, ctx) - contexttest.LoadUser(t, ctx, 2) - contexttest.LoadGitRepo(t, ctx) - defer ctx.Repo.GitRepo.Close() - - branch := ctx.Repo.Repository.DefaultBranch - treePath := "README.md" - content := "# repo1\n\nDescription for repo1\nthis is a new line" - - t.Run("empty repo", func(t *testing.T) { - diff, err := GetDiffPreview(ctx, &repo_model.Repository{}, branch, treePath, content) - assert.Nil(t, diff) - assert.EqualError(t, err, "repository does not exist [id: 0, uid: 0, owner_name: , name: ]") - }) - - t.Run("bad branch", func(t *testing.T) { - badBranch := "bad_branch" - diff, err := GetDiffPreview(ctx, ctx.Repo.Repository, badBranch, treePath, content) - assert.Nil(t, diff) - assert.EqualError(t, err, "branch does not exist [name: "+badBranch+"]") - }) - - t.Run("empty treePath", func(t *testing.T) { - diff, err := GetDiffPreview(ctx, ctx.Repo.Repository, branch, "", content) - assert.Nil(t, diff) - assert.EqualError(t, err, "path is invalid [path: ]") + assert.JSONEq(t, string(expectedBs), string(bs)) }) } diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go index 63f4f06d25..68d1df24b7 100644 --- a/services/repository/files/temp_repo.go +++ b/services/repository/files/temp_repo.go @@ -259,7 +259,7 @@ func (t *TemporaryUploadRepository) CommitTree(ctx context.Context, opts *Commit authorDate := opts.AuthorTime committerDate := opts.CommitterTime if authorDate == nil && committerDate == nil { - authorDate = util.ToPointer(time.Now()) + authorDate = new(time.Now()) committerDate = authorDate } else if authorDate == nil { authorDate = committerDate @@ -361,7 +361,7 @@ func (t *TemporaryUploadRepository) Push(ctx context.Context, doer *user_model.U } // DiffIndex returns a Diff of the current index to the head -func (t *TemporaryUploadRepository) DiffIndex(ctx context.Context) (*gitdiff.Diff, error) { +func (t *TemporaryUploadRepository) DiffIndex(ctx context.Context, oldContent, newContent string) (*gitdiff.Diff, error) { var diff *gitdiff.Diff cmd := gitcmd.NewCommand("diff-index", "--src-prefix=\\a/", "--dst-prefix=\\b/", "--cached", "-p", "HEAD") stdoutReader, stdoutReaderClose := cmd.MakeStdoutPipe() @@ -383,6 +383,9 @@ func (t *TemporaryUploadRepository) DiffIndex(ctx context.Context) (*gitdiff.Dif return nil, fmt.Errorf("unable to run diff-index pipeline in temporary repo: %w", err) } + if len(diff.Files) > 0 { + gitdiff.FillDiffFileHighlightLinesByContent(diff.Files[0], util.UnsafeStringToBytes(oldContent), util.UnsafeStringToBytes(newContent)) + } return diff, nil } diff --git a/services/repository/fork.go b/services/repository/fork.go index 9c360c2f82..8cf41d5654 100644 --- a/services/repository/fork.go +++ b/services/repository/fork.go @@ -177,10 +177,10 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork } defer gitRepo.Close() - if _, err = repo_module.SyncRepoBranchesWithRepo(ctx, repo, gitRepo, doer.ID); err != nil { + if _, _, err = repo_module.SyncRepoBranchesWithRepo(ctx, repo, gitRepo, doer.ID); err != nil { return nil, fmt.Errorf("SyncRepoBranchesWithRepo: %w", err) } - if err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil { + if _, err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil { return nil, fmt.Errorf("Sync releases from git tags failed: %v", err) } diff --git a/services/repository/migrate.go b/services/repository/migrate.go index bc46c5e09b..a51791ed29 100644 --- a/services/repository/migrate.go +++ b/services/repository/migrate.go @@ -145,7 +145,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User, } } - if _, err := repo_module.SyncRepoBranchesWithRepo(ctx, repo, gitRepo, u.ID); err != nil { + if _, _, err := repo_module.SyncRepoBranchesWithRepo(ctx, repo, gitRepo, u.ID); err != nil { return repo, fmt.Errorf("SyncRepoBranchesWithRepo: %v", err) } @@ -153,7 +153,7 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User, // otherwise, the releases sync will be done out of this function if !opts.Releases { repo.IsMirror = opts.Mirror - if err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil { + if _, err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil { log.Error("Failed to synchronize tags to releases for repository: %v", err) } } diff --git a/tailwind.config.ts b/tailwind.config.ts index 8693208e13..7aaea687eb 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -80,10 +80,10 @@ export default { semibold: 'var(--font-weight-semibold)', bold: 'var(--font-weight-bold)', }, - fontSize: { // not using `rem` units because our root is currently 14px - 'xs': '12px', - 'sm': '14px', - 'base': '16px', + fontSize: { // rarely used, but "text-base" (matching body's 1em=14px) is useful to reset font-size in a header container + 'xs': '11px', + 'sm': '12px', + 'base': '14px', 'lg': '18px', 'xl': '20px', '2xl': '24px', diff --git a/templates/admin/auth/list.tmpl b/templates/admin/auth/list.tmpl index a1e72b742f..7296e95558 100644 --- a/templates/admin/auth/list.tmpl +++ b/templates/admin/auth/list.tmpl @@ -7,7 +7,7 @@
- +
diff --git a/templates/admin/cron.tmpl b/templates/admin/cron.tmpl index 4d01ce51eb..8e44614d97 100644 --- a/templates/admin/cron.tmpl +++ b/templates/admin/cron.tmpl @@ -5,7 +5,7 @@
-
ID
+
diff --git a/templates/admin/emails/list.tmpl b/templates/admin/emails/list.tmpl index 50e453916d..d597a2753f 100644 --- a/templates/admin/emails/list.tmpl +++ b/templates/admin/emails/list.tmpl @@ -24,7 +24,7 @@
-
+
diff --git a/templates/admin/notice.tmpl b/templates/admin/notice.tmpl index 0499b0adbb..7e08946f23 100644 --- a/templates/admin/notice.tmpl +++ b/templates/admin/notice.tmpl @@ -3,7 +3,7 @@

{{ctx.Locale.Tr "admin.notices.system_notice_list"}} ({{ctx.Locale.Tr "admin.total" .Total}})

- +
diff --git a/templates/admin/org/list.tmpl b/templates/admin/org/list.tmpl index 137c42b45d..c2c0280e4c 100644 --- a/templates/admin/org/list.tmpl +++ b/templates/admin/org/list.tmpl @@ -29,7 +29,7 @@
-
+
diff --git a/templates/admin/packages/list.tmpl b/templates/admin/packages/list.tmpl index 4d458f48fb..553130ae09 100644 --- a/templates/admin/packages/list.tmpl +++ b/templates/admin/packages/list.tmpl @@ -26,7 +26,7 @@
-
ID{{SortArrow "oldest" "newest" $.SortType false}}
+
diff --git a/templates/admin/queue.tmpl b/templates/admin/queue.tmpl index 1be35cf765..e6abd1e794 100644 --- a/templates/admin/queue.tmpl +++ b/templates/admin/queue.tmpl @@ -4,7 +4,7 @@ {{ctx.Locale.Tr "admin.monitor.queues"}}
-
ID
+
diff --git a/templates/admin/queue_manage.tmpl b/templates/admin/queue_manage.tmpl index a793fe1350..73da9164cc 100644 --- a/templates/admin/queue_manage.tmpl +++ b/templates/admin/queue_manage.tmpl @@ -4,7 +4,7 @@ {{ctx.Locale.Tr "admin.monitor.queue" .Queue.GetName}}
-
{{ctx.Locale.Tr "admin.monitor.queue.name"}}
+
diff --git a/templates/admin/repo/list.tmpl b/templates/admin/repo/list.tmpl index f21e5bfef2..0af9df2551 100644 --- a/templates/admin/repo/list.tmpl +++ b/templates/admin/repo/list.tmpl @@ -10,7 +10,7 @@ {{template "shared/repo/search" .}}
-
{{ctx.Locale.Tr "admin.monitor.queue.name"}}
+
diff --git a/templates/admin/stats.tmpl b/templates/admin/stats.tmpl index 04fa862a85..bb6a86ac30 100644 --- a/templates/admin/stats.tmpl +++ b/templates/admin/stats.tmpl @@ -4,7 +4,7 @@ {{ctx.Locale.Tr "admin.dashboard.statistic"}}
-
ID{{SortArrow "oldest" "newest" $.SortType false}}
+
{{range $statsKey := .StatsKeys}} diff --git a/templates/admin/user/list.tmpl b/templates/admin/user/list.tmpl index 49f62dda74..168603354d 100644 --- a/templates/admin/user/list.tmpl +++ b/templates/admin/user/list.tmpl @@ -56,7 +56,7 @@
-
{{$statsKey}}
+
diff --git a/templates/package/content/container.tmpl b/templates/package/content/container.tmpl index 1a1335aaa6..a69b0477d2 100644 --- a/templates/package/content/container.tmpl +++ b/templates/package/content/container.tmpl @@ -78,7 +78,7 @@ {{ctx.Locale.Tr "packages.container.layers"}} {{/* only show the platform if the image metadata is not the package's, which means that it is a sub manifest */}} {{if ne .ContainerImageMetadata .PackageDescriptor.Metadata}} - + ({{svg "octicon-cpu" 12}} {{.ContainerImageMetadata.Platform}}) {{end}} diff --git a/templates/package/content/rpm.tmpl b/templates/package/content/rpm.tmpl index 3faa8a0dc7..8aebe62806 100644 --- a/templates/package/content/rpm.tmpl +++ b/templates/package/content/rpm.tmpl @@ -14,6 +14,12 @@ dnf config-manager --add-repo {{- end}} +# Fedora 41+ (DNF5) +{{- range $group := .Groups}} + {{- if $group}}{{$group = print "/" $group}}{{end}} +dnf config-manager addrepo --from-repofile= +{{- end}} + # {{ctx.Locale.Tr "packages.rpm.distros.suse"}} {{- range $group := .Groups}} {{- if $group}}{{$group = print "/" $group}}{{end}} diff --git a/templates/package/shared/cleanup_rules/preview.tmpl b/templates/package/shared/cleanup_rules/preview.tmpl index bb2354c944..0991a07fbc 100644 --- a/templates/package/shared/cleanup_rules/preview.tmpl +++ b/templates/package/shared/cleanup_rules/preview.tmpl @@ -3,7 +3,7 @@

{{ctx.Locale.Tr "packages.owner.settings.cleanuprules.preview.overview" (len .VersionsToRemove)}}

-
ID{{SortArrow "oldest" "newest" .SortType false}}
+
diff --git a/templates/repo/branch/list.tmpl b/templates/repo/branch/list.tmpl index aa9e856fcf..5b56b3ed42 100644 --- a/templates/repo/branch/list.tmpl +++ b/templates/repo/branch/list.tmpl @@ -15,7 +15,7 @@
-
{{ctx.Locale.Tr "admin.packages.type"}}
+
@@ -83,7 +83,7 @@
- +
{{range .Branches}} @@ -133,14 +133,14 @@ {{svg "octicon-git-pull-request"}} {{ctx.Locale.Tr "repo.branch.included"}} - {{else if and (not .DBBranch.IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}} - + {{else if and (not .DBBranch.IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}} + {{end}} {{else if and .LatestPullRequest.HasMerged .MergeMovedOn}} {{if and (not .DBBranch.IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}} - + {{end}} diff --git a/templates/repo/commits_list.tmpl b/templates/repo/commits_list.tmpl index 959f2a9398..1a236582a2 100644 --- a/templates/repo/commits_list.tmpl +++ b/templates/repo/commits_list.tmpl @@ -1,5 +1,5 @@
-
+
diff --git a/templates/repo/editor/common_breadcrumb.tmpl b/templates/repo/editor/common_breadcrumb.tmpl index 8cfbe09d3e..44c9058457 100644 --- a/templates/repo/editor/common_breadcrumb.tmpl +++ b/templates/repo/editor/common_breadcrumb.tmpl @@ -5,7 +5,7 @@ {{range $i, $v := .TreeNames}} {{if eq $i $l}} - + {{svg "octicon-info"}} {{else}} {{$v}} diff --git a/templates/repo/editor/edit.tmpl b/templates/repo/editor/edit.tmpl index 008376ce16..8a5ed4a39c 100644 --- a/templates/repo/editor/edit.tmpl +++ b/templates/repo/editor/edit.tmpl @@ -18,19 +18,22 @@ {{if not .NotEditableReason}}
-
+ data-previewable-extensions="{{StringUtils.Join $.CodeEditorConfig.PreviewableExtensions ","}}" + data-line-wrap-extensions="{{StringUtils.Join $.CodeEditorConfig.LineWrapExtensions ","}}">{{.FileContent}}
diff --git a/templates/repo/editor/options.tmpl b/templates/repo/editor/options.tmpl new file mode 100644 index 0000000000..04cbd44cd6 --- /dev/null +++ b/templates/repo/editor/options.tmpl @@ -0,0 +1,30 @@ +{{$indentStyle := $.CodeEditorConfig.IndentStyle}} +{{$indentSize := or $.CodeEditorConfig.IndentSize 4}} +{{$lineWrapOn := $.CodeEditorConfig.LineWrapOn}} +
+
+ +
+
+ +
+
+ +
+
diff --git a/templates/repo/editor/patch.tmpl b/templates/repo/editor/patch.tmpl index 9b82bb855f..a70464dacb 100644 --- a/templates/repo/editor/patch.tmpl +++ b/templates/repo/editor/patch.tmpl @@ -20,15 +20,20 @@
-
diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl index 1fe220e1b8..9aa75500e0 100644 --- a/templates/repo/issue/list.tmpl +++ b/templates/repo/issue/list.tmpl @@ -24,11 +24,12 @@ {{if .PageIsIssueList}} {{ctx.Locale.Tr "repo.issues.new"}} {{else}} - {{ctx.Locale.Tr "repo.pulls.new"}} + {{ctx.Locale.Tr "repo.pulls.new"}} {{end}} {{else}} + {{/* archived, view compare page only */}} {{if not .PageIsIssueList}} - {{ctx.Locale.Tr "action.compare_commits_general"}} + {{ctx.Locale.Tr "action.compare_commits_general"}} {{end}} {{end}}
diff --git a/templates/repo/issue/sidebar/assignee_list.tmpl b/templates/repo/issue/sidebar/assignee_list.tmpl index 1d2279a45d..f66a38d182 100644 --- a/templates/repo/issue/sidebar/assignee_list.tmpl +++ b/templates/repo/issue/sidebar/assignee_list.tmpl @@ -1,5 +1,7 @@ {{$pageMeta := .}} {{$data := .AssigneesData}} +{{$listBaseLink := print $pageMeta.RepoLink (Iif $pageMeta.IsPullRequest "/pulls" "/issues")}} +{{/* TODO: it seems that the code keeps checking $pageMeta.Issue and assumes that it might not exist, need to figure out why */}} {{$issueAssignees := NIL}}{{if $pageMeta.Issue}}{{$issueAssignees = $pageMeta.Issue.Assignees}}{{end}}
{{ctx.Locale.Tr "repo.issues.new.clear_assignees"}}
{{range $data.CandidateAssignees}} - + {{svg "octicon-check"}} {{ctx.AvatarUtils.Avatar . 20}} {{template "repo/search_name" .}} @@ -30,8 +32,8 @@ diff --git a/templates/repo/issue/sidebar/label_list.tmpl b/templates/repo/issue/sidebar/label_list.tmpl index 15c8760d1a..d30196326b 100644 --- a/templates/repo/issue/sidebar/label_list.tmpl +++ b/templates/repo/issue/sidebar/label_list.tmpl @@ -1,5 +1,6 @@ {{$pageMeta := .}} {{$data := .LabelsData}} +{{$listBaseLink := print $pageMeta.RepoLink (Iif $pageMeta.IsPullRequest "/pulls" "/issues")}}
@@ -26,7 +27,7 @@
{{end}} {{$previousExclusiveScope = $exclusiveScope}} - {{template "repo/issue/sidebar/label_list_item" dict "Label" .}} + {{template "repo/issue/sidebar/label_list_item" dict "Label" . "LabelLink" (print $listBaseLink "?labels=" .ID)}} {{end}} {{if and $data.RepoLabels $data.OrgLabels}}
{{end}} {{$previousExclusiveScope = "_no_scope"}} @@ -36,7 +37,7 @@
{{end}} {{$previousExclusiveScope = $exclusiveScope}} - {{template "repo/issue/sidebar/label_list_item" dict "Label" .}} + {{template "repo/issue/sidebar/label_list_item" dict "Label" . "LabelLink" (print $listBaseLink "?labels=" .ID)}} {{end}}
{{end}} @@ -47,7 +48,7 @@ {{ctx.Locale.Tr "repo.issues.new.no_label"}} {{range $data.AllLabels}} {{if .IsChecked}} - + {{- ctx.RenderUtils.RenderLabel . -}} {{end}} diff --git a/templates/repo/issue/sidebar/label_list_item.tmpl b/templates/repo/issue/sidebar/label_list_item.tmpl index 5c6808d95b..0c32d99bf9 100644 --- a/templates/repo/issue/sidebar/label_list_item.tmpl +++ b/templates/repo/issue/sidebar/label_list_item.tmpl @@ -1,5 +1,6 @@ {{$label := .Label}} - {{svg (Iif $label.ExclusiveScope "octicon-dot-fill" "octicon-check")}} diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index 0eeb10cba7..e7b4c8758d 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -1,5 +1,5 @@ {{template "base/alert"}} -{{range .Issue.Comments}} +{{range $comment := .Issue.Comments}} {{if call $.ShouldShowCommentType .Type}} {{$createdStr:= DateUtils.TimeSince .CreatedUnix}} @@ -220,11 +220,11 @@
{{else if eq .Type 10}}
- {{svg "octicon-pencil"}} + {{ctx.RenderUtils.RenderTimelineEventBadge $comment}} {{template "shared/user/avatarlink" dict "user" .Poster}} {{template "shared/user/authorlink" .Poster}} - {{ctx.Locale.Tr "repo.issues.change_title_at" (.OldTitle|ctx.RenderUtils.RenderEmoji) (.NewTitle|ctx.RenderUtils.RenderEmoji) $createdStr}} + {{ctx.RenderUtils.RenderTimelineEventComment $comment $createdStr}}
{{else if eq .Type 11}} diff --git a/templates/repo/settings/branches.tmpl b/templates/repo/settings/branches.tmpl index 37ebab2a72..8488174ab6 100644 --- a/templates/repo/settings/branches.tmpl +++ b/templates/repo/settings/branches.tmpl @@ -9,22 +9,23 @@ {{ctx.Locale.Tr "repo.default_branch"}}
-

- {{ctx.Locale.Tr "repo.settings.default_branch_desc"}} -

-
+

{{ctx.Locale.Tr "repo.settings.default_branch_desc"}}

+ - {{end}} + {{/* FIXME: need to split the "Advance Settings" by units, there are too many options here */}}

{{ctx.Locale.Tr "repo.settings.advanced_settings"}}

@@ -594,6 +595,20 @@
+
+ + +
{{ctx.Locale.Tr "repo.commits.author"}}
+
diff --git a/templates/shared/actions/runner_list.tmpl b/templates/shared/actions/runner_list.tmpl index 43321a8dc5..9c541c95ee 100644 --- a/templates/shared/actions/runner_list.tmpl +++ b/templates/shared/actions/runner_list.tmpl @@ -41,7 +41,7 @@
-
{{ctx.Locale.Tr "actions.runners.task_list.run"}}
+
@@ -72,7 +72,7 @@ {{if .Version}}{{.Version}}{{else}}{{ctx.Locale.Tr "unknown"}}{{end}} {{.BelongsToOwnerType.LocaleString ctx.Locale}} - {{range .AgentLabels}}{{.}}{{end}} + {{range .AgentLabels}}{{.}}{{end}} {{if .LastOnline}}{{DateUtils.TimeSince .LastOnline}}{{else}}{{ctx.Locale.Tr "never"}}{{end}} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 45f4a84316..570747ca57 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -28147,6 +28147,10 @@ "type": "string", "x-go-name": "DefaultMergeStyle" }, + "default_target_branch": { + "type": "string", + "x-go-name": "DefaultTargetBranch" + }, "description": { "type": "string", "x-go-name": "Description" diff --git a/templates/user/dashboard/feeds.tmpl b/templates/user/dashboard/feeds.tmpl index d525ce78ff..c3c042dc38 100644 --- a/templates/user/dashboard/feeds.tmpl +++ b/templates/user/dashboard/feeds.tmpl @@ -56,11 +56,11 @@ {{$index := index .GetIssueInfos 0}} {{ctx.Locale.Tr "action.delete_branch" (.GetRepoLink ctx) .GetBranch (.ShortRepoPath ctx)}} {{else if .GetOpType.InActions "mirror_sync_push"}} - {{ctx.Locale.Tr "action.mirror_sync_push" (.GetRepoLink ctx) (.GetRefLink ctx) .GetBranch (.ShortRepoPath ctx)}} + {{ctx.Locale.Tr "action.mirror_sync_push" (.GetRepoLink ctx) (.GetRefLink ctx) .RefName (.ShortRepoPath ctx)}} {{else if .GetOpType.InActions "mirror_sync_create"}} - {{ctx.Locale.Tr "action.mirror_sync_create" (.GetRepoLink ctx) (.GetRefLink ctx) .GetBranch (.ShortRepoPath ctx)}} + {{ctx.Locale.Tr "action.mirror_sync_create" (.GetRepoLink ctx) (.GetRefLink ctx) .RefName (.ShortRepoPath ctx)}} {{else if .GetOpType.InActions "mirror_sync_delete"}} - {{ctx.Locale.Tr "action.mirror_sync_delete" (.GetRepoLink ctx) .GetBranch (.ShortRepoPath ctx)}} + {{ctx.Locale.Tr "action.mirror_sync_delete" (.GetRepoLink ctx) .RefName (.ShortRepoPath ctx)}} {{else if .GetOpType.InActions "approve_pull_request"}} {{$index := index .GetIssueInfos 0}} {{ctx.Locale.Tr "action.approve_pull_request" (printf "%s/pulls/%s" (.GetRepoLink ctx) $index) $index (.ShortRepoPath ctx)}} diff --git a/tests/integration/actions_approve_test.go b/tests/integration/actions_approve_test.go index f2d6f75f88..3f2c02f77a 100644 --- a/tests/integration/actions_approve_test.go +++ b/tests/integration/actions_approve_test.go @@ -17,7 +17,6 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" ) @@ -68,7 +67,7 @@ jobs: // user4 forks the repo req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/forks", baseRepo.OwnerName, baseRepo.Name), &api.CreateForkOption{ - Name: util.ToPointer("approve-all-runs-fork"), + Name: new("approve-all-runs-fork"), }).AddTokenAuth(user4Token) resp := MakeRequest(t, req, http.StatusAccepted) var apiForkRepo api.Repository diff --git a/tests/integration/actions_concurrency_test.go b/tests/integration/actions_concurrency_test.go index 653376d554..b904230a95 100644 --- a/tests/integration/actions_concurrency_test.go +++ b/tests/integration/actions_concurrency_test.go @@ -19,7 +19,6 @@ import ( "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" webhook_module "code.gitea.io/gitea/modules/webhook" actions_service "code.gitea.io/gitea/services/actions" @@ -420,7 +419,7 @@ jobs: // user4 forks the repo req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/forks", baseRepo.OwnerName, baseRepo.Name), &api.CreateForkOption{ - Name: util.ToPointer("actions-concurrency-fork"), + Name: new("actions-concurrency-fork"), }).AddTokenAuth(user4Token) resp := MakeRequest(t, req, http.StatusAccepted) var apiForkRepo api.Repository diff --git a/tests/integration/actions_schedule_test.go b/tests/integration/actions_schedule_test.go index dda4f1421e..43c44ede55 100644 --- a/tests/integration/actions_schedule_test.go +++ b/tests/integration/actions_schedule_test.go @@ -19,7 +19,6 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/migration" api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" mirror_service "code.gitea.io/gitea/services/mirror" repo_service "code.gitea.io/gitea/services/repository" files_service "code.gitea.io/gitea/services/repository/files" @@ -103,8 +102,8 @@ jobs: doTestScheduleUpdate(t, func(t *testing.T, u *url.URL, testContext APITestContext, user *user_model.User, repo *repo_model.Repository) (commitID, expectedSpec string) { // enable manual-merge doAPIEditRepository(testContext, &api.EditRepoOption{ - HasPullRequests: util.ToPointer(true), - AllowManualMerge: util.ToPointer(true), + HasPullRequests: new(true), + AllowManualMerge: new(true), })(t) // update workflow file @@ -170,7 +169,7 @@ func testScheduleUpdateMirrorSync(t *testing.T) { // enable actions unit for mirror repo assert.False(t, mirrorRepo.UnitEnabled(t.Context(), unit_model.TypeActions)) doAPIEditRepository(mirrorContext, &api.EditRepoOption{ - HasActions: util.ToPointer(true), + HasActions: new(true), })(t) actionSchedule := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionSchedule{RepoID: mirrorRepo.ID}) scheduleSpec := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionScheduleSpec{RepoID: mirrorRepo.ID, ScheduleID: actionSchedule.ID}) @@ -201,11 +200,11 @@ func testScheduleUpdateMirrorSync(t *testing.T) { func testScheduleUpdateArchiveAndUnarchive(t *testing.T) { doTestScheduleUpdate(t, func(t *testing.T, u *url.URL, testContext APITestContext, user *user_model.User, repo *repo_model.Repository) (commitID, expectedSpec string) { doAPIEditRepository(testContext, &api.EditRepoOption{ - Archived: util.ToPointer(true), + Archived: new(true), })(t) assert.Zero(t, unittest.GetCount(t, &actions_model.ActionSchedule{RepoID: repo.ID})) doAPIEditRepository(testContext, &api.EditRepoOption{ - Archived: util.ToPointer(false), + Archived: new(false), })(t) branch, err := git_model.GetBranch(t.Context(), repo.ID, repo.DefaultBranch) assert.NoError(t, err) @@ -216,11 +215,11 @@ func testScheduleUpdateArchiveAndUnarchive(t *testing.T) { func testScheduleUpdateDisableAndEnableActionsUnit(t *testing.T) { doTestScheduleUpdate(t, func(t *testing.T, u *url.URL, testContext APITestContext, user *user_model.User, repo *repo_model.Repository) (commitID, expectedSpec string) { doAPIEditRepository(testContext, &api.EditRepoOption{ - HasActions: util.ToPointer(false), + HasActions: new(false), })(t) assert.Zero(t, unittest.GetCount(t, &actions_model.ActionSchedule{RepoID: repo.ID})) doAPIEditRepository(testContext, &api.EditRepoOption{ - HasActions: util.ToPointer(true), + HasActions: new(true), })(t) branch, err := git_model.GetBranch(t.Context(), repo.ID, repo.DefaultBranch) assert.NoError(t, err) diff --git a/tests/integration/actions_trigger_test.go b/tests/integration/actions_trigger_test.go index fd3f2a0294..b0eabdd432 100644 --- a/tests/integration/actions_trigger_test.go +++ b/tests/integration/actions_trigger_test.go @@ -29,7 +29,6 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" webhook_module "code.gitea.io/gitea/modules/webhook" issue_service "code.gitea.io/gitea/services/issue" pull_service "code.gitea.io/gitea/services/pull" @@ -1406,7 +1405,7 @@ jobs: // user4 forks the repo req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/forks", baseRepo.OwnerName, baseRepo.Name), &api.CreateForkOption{ - Name: util.ToPointer("close-pull-request-with-path-fork"), + Name: new("close-pull-request-with-path-fork"), }).AddTokenAuth(user4Token) resp := MakeRequest(t, req, http.StatusAccepted) var apiForkRepo api.Repository diff --git a/tests/integration/api_pull_test.go b/tests/integration/api_pull_test.go index 0e1a88dcee..61a37ddd01 100644 --- a/tests/integration/api_pull_test.go +++ b/tests/integration/api_pull_test.go @@ -304,7 +304,7 @@ func TestAPICreatePullBasePermission(t *testing.T) { Base: "master", Title: prTitle, - AllowMaintainerEdit: util.ToPointer(true), + AllowMaintainerEdit: new(true), } req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner10.Name, repo10.Name), &opts).AddTokenAuth(token) MakeRequest(t, req, http.StatusForbidden) diff --git a/tests/integration/api_repo_file_create_test.go b/tests/integration/api_repo_file_create_test.go index 7cf1083248..5bc920a274 100644 --- a/tests/integration/api_repo_file_create_test.go +++ b/tests/integration/api_repo_file_create_test.go @@ -19,7 +19,6 @@ import ( "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/context" "github.com/stretchr/testify/assert" @@ -53,8 +52,8 @@ func getCreateFileOptions() api.CreateFileOptions { func normalizeFileContentResponseCommitTime(c *api.ContentsResponse) { // decoded JSON response may contain different timezone from the one parsed by git commit // so we need to normalize the time to UTC to make "assert.Equal" pass - c.LastCommitterDate = util.ToPointer(c.LastCommitterDate.UTC()) - c.LastAuthorDate = util.ToPointer(c.LastAuthorDate.UTC()) + c.LastCommitterDate = new(c.LastCommitterDate.UTC()) + c.LastAuthorDate = new(c.LastAuthorDate.UTC()) } type apiFileResponseInfo struct { @@ -75,9 +74,9 @@ func getExpectedFileResponseForCreate(info apiFileResponseInfo) *api.FileRespons Name: path.Base(info.treePath), Path: info.treePath, SHA: sha, - LastCommitSHA: util.ToPointer(info.lastCommitSHA), - LastCommitterDate: util.ToPointer(info.lastCommitterWhen), - LastAuthorDate: util.ToPointer(info.lastAuthorWhen), + LastCommitSHA: new(info.lastCommitSHA), + LastCommitterDate: new(info.lastCommitterWhen), + LastAuthorDate: new(info.lastAuthorWhen), Size: 16, Type: "file", Encoding: &encoding, diff --git a/tests/integration/api_repo_file_update_test.go b/tests/integration/api_repo_file_update_test.go index 6e6aae389f..2847e01791 100644 --- a/tests/integration/api_repo_file_update_test.go +++ b/tests/integration/api_repo_file_update_test.go @@ -18,7 +18,6 @@ import ( "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/context" "github.com/stretchr/testify/assert" @@ -59,9 +58,9 @@ func getExpectedFileResponseForUpdate(info apiFileResponseInfo) *api.FileRespons Name: path.Base(info.treePath), Path: info.treePath, SHA: sha, - LastCommitSHA: util.ToPointer(info.lastCommitSHA), - LastCommitterDate: util.ToPointer(info.lastCommitterWhen), - LastAuthorDate: util.ToPointer(info.lastAuthorWhen), + LastCommitSHA: new(info.lastCommitSHA), + LastCommitterDate: new(info.lastCommitterWhen), + LastAuthorDate: new(info.lastAuthorWhen), Type: "file", Size: 20, Encoding: &encoding, diff --git a/tests/integration/api_repo_get_contents_list_test.go b/tests/integration/api_repo_get_contents_list_test.go index 4984559f0c..679ad37bef 100644 --- a/tests/integration/api_repo_get_contents_list_test.go +++ b/tests/integration/api_repo_get_contents_list_test.go @@ -17,7 +17,6 @@ import ( "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" repo_service "code.gitea.io/gitea/services/repository" "github.com/stretchr/testify/assert" @@ -35,9 +34,9 @@ func getExpectedContentsListResponseForContents(ref, refType, lastCommitSHA stri Name: path.Base(treePath), Path: treePath, SHA: sha, - LastCommitSHA: util.ToPointer(lastCommitSHA), - LastCommitterDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))), - LastAuthorDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))), + LastCommitSHA: new(lastCommitSHA), + LastCommitterDate: new(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))), + LastAuthorDate: new(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))), Type: "file", Size: 30, URL: &selfURL, diff --git a/tests/integration/api_repo_get_contents_test.go b/tests/integration/api_repo_get_contents_test.go index 6225c96bc6..33960b1ea3 100644 --- a/tests/integration/api_repo_get_contents_test.go +++ b/tests/integration/api_repo_get_contents_test.go @@ -18,7 +18,6 @@ import ( "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" repo_service "code.gitea.io/gitea/services/repository" "github.com/stretchr/testify/assert" @@ -34,17 +33,17 @@ func getExpectedContentsResponseForContents(ref, refType, lastCommitSHA string) Name: treePath, Path: treePath, SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f", - LastCommitSHA: util.ToPointer(lastCommitSHA), - LastCommitterDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))), - LastAuthorDate: util.ToPointer(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))), + LastCommitSHA: new(lastCommitSHA), + LastCommitterDate: new(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))), + LastAuthorDate: new(time.Date(2017, time.March, 19, 16, 47, 59, 0, time.FixedZone("", -14400))), Type: "file", Size: 30, - Encoding: util.ToPointer("base64"), - Content: util.ToPointer("IyByZXBvMQoKRGVzY3JpcHRpb24gZm9yIHJlcG8x"), + Encoding: new("base64"), + Content: new("IyByZXBvMQoKRGVzY3JpcHRpb24gZm9yIHJlcG8x"), URL: &selfURL, HTMLURL: &htmlURL, GitURL: &gitURL, - DownloadURL: util.ToPointer(setting.AppURL + "user2/repo1/raw/" + refType + "/" + ref + "/" + treePath), + DownloadURL: new(setting.AppURL + "user2/repo1/raw/" + refType + "/" + ref + "/" + treePath), Links: &api.FileLinksResponse{ Self: &selfURL, GitURL: &gitURL, @@ -256,8 +255,8 @@ func testAPIGetContentsExt(t *testing.T) { assert.Equal(t, "jpeg.jpg", respFile.Name) assert.Nil(t, respFile.Encoding) assert.Nil(t, respFile.Content) - assert.Equal(t, util.ToPointer(int64(107)), respFile.LfsSize) - assert.Equal(t, util.ToPointer("0b8d8b5f15046343fd32f451df93acc2bdd9e6373be478b968e4cad6b6647351"), respFile.LfsOid) + assert.Equal(t, new(int64(107)), respFile.LfsSize) + assert.Equal(t, new("0b8d8b5f15046343fd32f451df93acc2bdd9e6373be478b968e4cad6b6647351"), respFile.LfsOid) }) t.Run("FileContents", func(t *testing.T) { // by default, no file content or commit info is returned @@ -296,7 +295,7 @@ func testAPIGetContentsExt(t *testing.T) { assert.NotNil(t, respFile.Content) assert.Nil(t, contentsResponse.FileContents.LastCommitSHA) assert.Nil(t, contentsResponse.FileContents.LastCommitMessage) - assert.Equal(t, util.ToPointer(int64(107)), respFile.LfsSize) - assert.Equal(t, util.ToPointer("0b8d8b5f15046343fd32f451df93acc2bdd9e6373be478b968e4cad6b6647351"), respFile.LfsOid) + assert.Equal(t, new(int64(107)), respFile.LfsSize) + assert.Equal(t, new("0b8d8b5f15046343fd32f451df93acc2bdd9e6373be478b968e4cad6b6647351"), respFile.LfsOid) }) } diff --git a/tests/integration/editor_test.go b/tests/integration/editor_test.go index d4681f1699..c70bb061c9 100644 --- a/tests/integration/editor_test.go +++ b/tests/integration/editor_test.go @@ -148,10 +148,10 @@ func testEditFileToNewBranch(t *testing.T, session *TestSession, user, repo, bra func testEditorDiffPreview(t *testing.T) { session := loginUser(t, "user2") req := NewRequestWithValues(t, "POST", "/user2/repo1/_preview/master/README.md", map[string]string{ - "content": "Hello, World (Edited)\n", + "content": "# repo1 (Edited)", }) resp := session.MakeRequest(t, req, http.StatusOK) - assert.Contains(t, resp.Body.String(), `Hello, World (Edited)`) + assert.Contains(t, resp.Body.String(), ` (Edited)`) } func testEditorPatchFile(t *testing.T) { diff --git a/tests/integration/pull_commit_test.go b/tests/integration/pull_commit_test.go index 9f3b1a9ef5..01b8ec1ff4 100644 --- a/tests/integration/pull_commit_test.go +++ b/tests/integration/pull_commit_test.go @@ -36,6 +36,6 @@ func TestListPullCommits(t *testing.T) { defer tests.PrintCurrentTest(t)() req = NewRequest(t, "GET", "/user2/repo1/blob_excerpt/985f0301dba5e7b34be866819cd15ad3d8f508ee?last_left=0&last_right=0&left=2&right=2&left_hunk_size=2&right_hunk_size=2&path=README.md&style=split&direction=up") resp = session.MakeRequest(t, req, http.StatusOK) - assert.Contains(t, resp.Body.String(), `# repo1`) + assert.Contains(t, resp.Body.String(), `# repo1`+"\n"+``) }) } diff --git a/tests/integration/pull_create_test.go b/tests/integration/pull_create_test.go index 518f452e28..3db335fc3f 100644 --- a/tests/integration/pull_create_test.go +++ b/tests/integration/pull_create_test.go @@ -20,7 +20,6 @@ import ( "code.gitea.io/gitea/modules/git/gitcmd" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" @@ -330,7 +329,7 @@ func TestCreatePullRequestFromNestedOrgForks(t *testing.T) { forkIntoOrg := func(srcOrg, dstOrg string) api.Repository { req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/forks", srcOrg, repoName), &api.CreateForkOption{ - Organization: util.ToPointer(dstOrg), + Organization: new(dstOrg), }).AddTokenAuth(token) resp := MakeRequest(t, req, http.StatusAccepted) var forkRepo api.Repository diff --git a/tests/integration/repo_merge_upstream_test.go b/tests/integration/repo_merge_upstream_test.go index 84c87066cb..fcc6078fcd 100644 --- a/tests/integration/repo_merge_upstream_test.go +++ b/tests/integration/repo_merge_upstream_test.go @@ -18,7 +18,6 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -42,7 +41,7 @@ func TestRepoMergeUpstream(t *testing.T) { // create a fork req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/forks", baseUser.Name, baseRepo.Name), &api.CreateForkOption{ - Name: util.ToPointer("test-repo-fork"), + Name: new("test-repo-fork"), }).AddTokenAuth(token) MakeRequest(t, req, http.StatusAccepted) forkRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: forkUser.ID, Name: "test-repo-fork"}) diff --git a/tests/integration/repofiles_change_test.go b/tests/integration/repofiles_change_test.go index 6fd42401c5..442959b8a5 100644 --- a/tests/integration/repofiles_change_test.go +++ b/tests/integration/repofiles_change_test.go @@ -17,7 +17,6 @@ import ( "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/contexttest" files_service "code.gitea.io/gitea/services/repository/files" @@ -107,9 +106,9 @@ func getExpectedFileResponseForRepoFilesCreate(commitID string, lastCommit *git. Name: path.Base(treePath), Path: treePath, SHA: "103ff9234cefeee5ec5361d22b49fbb04d385885", - LastCommitSHA: util.ToPointer(lastCommit.ID.String()), - LastCommitterDate: util.ToPointer(lastCommit.Committer.When), - LastAuthorDate: util.ToPointer(lastCommit.Author.When), + LastCommitSHA: new(lastCommit.ID.String()), + LastCommitterDate: new(lastCommit.Committer.When), + LastAuthorDate: new(lastCommit.Author.When), Type: "file", Size: 18, Encoding: &encoding, @@ -177,9 +176,9 @@ func getExpectedFileResponseForRepoFilesUpdate(commitID, filename, lastCommitSHA Name: filename, Path: filename, SHA: "dbf8d00e022e05b7e5cf7e535de857de57925647", - LastCommitSHA: util.ToPointer(lastCommitSHA), - LastCommitterDate: util.ToPointer(lastCommitterWhen), - LastAuthorDate: util.ToPointer(lastAuthorWhen), + LastCommitSHA: new(lastCommitSHA), + LastCommitterDate: new(lastCommitterWhen), + LastAuthorDate: new(lastAuthorWhen), Type: "file", Size: 43, Encoding: &encoding, @@ -253,8 +252,8 @@ func getExpectedFileResponseForRepoFilesUpdateRename(commitID, lastCommitSHA str sha: "d4a41a0d4db4949e129bd22f871171ea988103ef", size: 129, content: "dmVyc2lvbiBodHRwczovL2dpdC1sZnMuZ2l0aHViLmNvbS9zcGVjL3YxCm9pZCBzaGEyNTY6MmVjY2RiNDM4MjVkMmE0OWQ5OWQ1NDJkYWEyMDA3NWNmZjFkOTdkOWQyMzQ5YTg5NzdlZmU5YzAzNjYxNzM3YwpzaXplIDIwNDgK", - lfsOid: util.ToPointer("2eccdb43825d2a49d99d542daa20075cff1d97d9d2349a8977efe9c03661737c"), - lfsSize: util.ToPointer(int64(2048)), + lfsOid: new("2eccdb43825d2a49d99d542daa20075cff1d97d9d2349a8977efe9c03661737c"), + lfsSize: new(int64(2048)), }, { filename: "jpeg.jpeg", @@ -267,8 +266,8 @@ func getExpectedFileResponseForRepoFilesUpdateRename(commitID, lastCommitSHA str sha: "2b6c6c4eaefa24b22f2092c3d54b263ff26feb58", size: 127, content: "dmVyc2lvbiBodHRwczovL2dpdC1sZnMuZ2l0aHViLmNvbS9zcGVjL3YxCm9pZCBzaGEyNTY6N2I2YjJjODhkYmE5Zjc2MGExYTU4NDY5YjY3ZmVlMmI2OThlZjdlOTM5OWM0Y2E0ZjM0YTE0Y2NiZTM5ZjYyMwpzaXplIDI3Cg==", - lfsOid: util.ToPointer("7b6b2c88dba9f760a1a58469b67fee2b698ef7e9399c4ca4f34a14ccbe39f623"), - lfsSize: util.ToPointer(int64(27)), + lfsOid: new("7b6b2c88dba9f760a1a58469b67fee2b698ef7e9399c4ca4f34a14ccbe39f623"), + lfsSize: new(int64(27)), }, } @@ -283,10 +282,10 @@ func getExpectedFileResponseForRepoFilesUpdateRename(commitID, lastCommitSHA str Name: detail.filename, Path: detail.filename, SHA: detail.sha, - LastCommitSHA: util.ToPointer(lastCommitSHA), + LastCommitSHA: new(lastCommitSHA), Type: "file", Size: detail.size, - Encoding: util.ToPointer("base64"), + Encoding: new("base64"), Content: &detail.content, URL: &selfURL, HTMLURL: &htmlURL, diff --git a/updates.config.ts b/updates.config.ts index 7bf680bbde..bc9e368fb4 100644 --- a/updates.config.ts +++ b/updates.config.ts @@ -4,6 +4,7 @@ export default { exclude: [ '@mcaptcha/vanilla-glue', // breaking changes in rc versions need to be handled 'cropperjs', // need to migrate to v2 but v2 is not compatible with v1 + 'eslint', // need to migrate to v10 'tailwindcss', // need to migrate '@eslint/json', // needs eslint 10 ], diff --git a/uv.lock b/uv.lock index 33e9b64b2c..10fdbf2bf3 100644 --- a/uv.lock +++ b/uv.lock @@ -127,11 +127,11 @@ wheels = [ [[package]] name = "pathspec" -version = "1.0.3" +version = "1.0.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/b2/bb8e495d5262bfec41ab5cb18f522f1012933347fb5d9e62452d446baca2/pathspec-1.0.3.tar.gz", hash = "sha256:bac5cf97ae2c2876e2d25ebb15078eb04d76e4b98921ee31c6f85ade8b59444d", size = 130841, upload-time = "2026-01-09T15:46:46.009Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/2b/121e912bd60eebd623f873fd090de0e84f322972ab25a7f9044c056804ed/pathspec-1.0.3-py3-none-any.whl", hash = "sha256:e80767021c1cc524aa3fb14bedda9c34406591343cc42797b386ce7b9354fb6c", size = 55021, upload-time = "2026-01-09T15:46:44.652Z" }, + { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, ] [[package]] @@ -384,14 +384,14 @@ wheels = [ [[package]] name = "tqdm" -version = "4.67.1" +version = "4.67.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } +sdist = { url = "https://files.pythonhosted.org/packages/09/a9/6ba95a270c6f1fbcd8dac228323f2777d886cb206987444e4bce66338dd4/tqdm-4.67.3.tar.gz", hash = "sha256:7d825f03f89244ef73f1d4ce193cb1774a8179fd96f31d7e1dcde62092b960bb", size = 169598, upload-time = "2026-02-03T17:35:53.048Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, + { url = "https://files.pythonhosted.org/packages/16/e1/3079a9ff9b8e11b846c6ac5c8b5bfb7ff225eee721825310c91b3b50304f/tqdm-4.67.3-py3-none-any.whl", hash = "sha256:ee1e4c0e59148062281c49d80b25b67771a127c85fc9676d3be5f243206826bf", size = 78374, upload-time = "2026-02-03T17:35:50.982Z" }, ] [[package]] diff --git a/web_src/css/base.css b/web_src/css/base.css index 227a59b9cd..8d4977cf41 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -18,6 +18,7 @@ --checkbox-mask-checked: url('data:image/svg+xml;utf8,'); --checkbox-mask-indeterminate: url('data:image/svg+xml;utf8,'); --octicon-chevron-right: url('data:image/svg+xml;utf8,'); + --select-arrows: url('data:image/svg+xml;utf8,'); /* other variables */ --border-radius: 4px; --border-radius-medium: 6px; @@ -30,6 +31,7 @@ --page-spacing: 16px; /* space between page elements */ --page-margin-x: 32px; /* minimum space on left and right side of page */ --page-space-bottom: 64px; /* space between last page element and footer */ + --transition-hover-fade: opacity 0.2s ease; /* fade transition for elements that show on hover */ /* z-index */ --z-index-modal: 1001; /* modal dialog, hard-coded from Fomantic modal.css */ @@ -306,6 +308,34 @@ a.label, text-decoration-line: none !important; } +.native-select { + position: relative; +} + +.native-select > select { + appearance: none; /* hide default triangle */ + padding: 6px 26px 6px 10px; + border-radius: var(--border-radius); +} + +/* ::before and ::after pseudo elements don't work on select elements, + so we need to put it on the parent wrapper element. */ +.native-select::after { + position: absolute; + pointer-events: none; /* make the click event can pass through the svg to the select element */ + top: 50%; + transform: translateY(-50%); + right: 5px; + content: ""; + width: 16px; + height: 16px; + mask-size: cover; + -webkit-mask-size: cover; + mask-image: var(--select-arrows); + -webkit-mask-image: var(--select-arrows); + background: currentcolor; +} + .ui.search > .results { background: var(--color-body); border-color: var(--color-secondary); diff --git a/web_src/css/index.css b/web_src/css/index.css index 7ada257d33..61af31171f 100644 --- a/web_src/css/index.css +++ b/web_src/css/index.css @@ -47,7 +47,7 @@ @import "./features/captcha.css"; @import "./markup/content.css"; -@import "./markup/codecopy.css"; +@import "./markup/codeblock.css"; @import "./markup/codepreview.css"; @import "./markup/asciicast.css"; diff --git a/web_src/css/markup/codeblock.css b/web_src/css/markup/codeblock.css new file mode 100644 index 0000000000..e7744391f0 --- /dev/null +++ b/web_src/css/markup/codeblock.css @@ -0,0 +1,10 @@ +.markup .ui.button.code-copy { + top: 8px; + right: 6px; + margin: 0; +} + +.markup .mermaid-block .view-controller { + right: 6px; + bottom: 5px; +} diff --git a/web_src/css/markup/codecopy.css b/web_src/css/markup/codecopy.css deleted file mode 100644 index 5a7b9955e7..0000000000 --- a/web_src/css/markup/codecopy.css +++ /dev/null @@ -1,30 +0,0 @@ -.markup .code-copy { - position: absolute; - top: 8px; - right: 6px; - padding: 9px; - visibility: hidden; - animation: fadeout 0.2s both; -} - -/* adjustments for comment content having only 14px font size */ -.repository.view.issue .comment-list .comment .markup .code-copy { - right: 5px; - padding: 8px; -} - -/* can not use regular transparent button colors for hover and active states because - we need opaque colors here as code can appear behind the button */ -.markup .code-copy:hover { - background: var(--color-secondary) !important; -} - -.markup .code-copy:active { - background: var(--color-secondary-dark-1) !important; -} - -.markup .code-block-container:hover .code-copy, -.markup .code-block:hover .code-copy { - visibility: visible; - animation: fadein 0.2s both; -} diff --git a/web_src/css/markup/content.css b/web_src/css/markup/content.css index 0e3aeb2ffe..34e7de8c47 100644 --- a/web_src/css/markup/content.css +++ b/web_src/css/markup/content.css @@ -601,3 +601,40 @@ In markup content, we always use bottom margin for all elements */ .file-view.markup.orgmode li.indeterminate > p { display: inline-block; } + +/* auto-hide-control is a control element or a container for control elements, + it floats over the code-block and only shows when the code-block is hovered. */ +.markup .auto-hide-control { + position: absolute; + z-index: 1; + visibility: hidden; /* prevent from click events even opacity=0 */ + opacity: 0; + transition: var(--transition-hover-fade); +} + +/* all rendered code-block elements are in their container, +the manually written code-block elements on "packages" pages don't have the container */ +.markup .code-block-container:hover .auto-hide-control, +.markup .code-block:hover .auto-hide-control { + visibility: visible; + opacity: 1; +} + +@media (hover: none) { + .markup .auto-hide-control { + visibility: visible; + opacity: 1; + } +} + +/* can not use regular transparent button colors for hover and active states + because we need opaque colors here as code can appear behind the button */ +.markup .auto-hide-control.ui.button:hover, +.markup .auto-hide-control .ui.button:hover { + background: var(--color-secondary) !important; +} + +.markup .auto-hide-control.ui.button:active, +.markup .auto-hide-control .ui.button:active { + background: var(--color-secondary-dark-1) !important; +} diff --git a/web_src/css/modules/animations.css b/web_src/css/modules/animations.css index aedf53569a..65d7a90e97 100644 --- a/web_src/css/modules/animations.css +++ b/web_src/css/modules/animations.css @@ -82,15 +82,6 @@ code.language-math.is-loading::after { } } -@keyframes fadeout { - 0% { - opacity: 1; - } - 100% { - opacity: 0; - } -} - /* 1p5 means 1-point-5. it can't use "pulse" here, otherwise the animation is not right (maybe due to some conflicts */ @keyframes pulse-1p5 { 0% { diff --git a/web_src/css/modules/dropdown.css b/web_src/css/modules/dropdown.css index f67705bf37..f8a4b74316 100644 --- a/web_src/css/modules/dropdown.css +++ b/web_src/css/modules/dropdown.css @@ -298,6 +298,10 @@ select.ui.dropdown { overflow-wrap: normal; } +.ui.selection.dropdown .menu .item:first-of-type { + border-radius: 0; +} + .ui.selection.dropdown:hover { border-color: var(--color-input-border-hover); box-shadow: none; diff --git a/web_src/css/modules/form.css b/web_src/css/modules/form.css index 09d247cf17..56d4f9ba61 100644 --- a/web_src/css/modules/form.css +++ b/web_src/css/modules/form.css @@ -142,14 +142,6 @@ textarea:focus, margin-top: 0.7em; } -.ui.form select { - display: block; - height: auto; - width: 100%; - border-radius: 0.28571429rem; - padding: 0.62em 1em; -} - .ui.form .field > .selection.dropdown { min-width: 14em; /* matches the default min width */ width: 100%; diff --git a/web_src/css/modules/table.css b/web_src/css/modules/table.css index 6298471d47..c7d6cb7a48 100644 --- a/web_src/css/modules/table.css +++ b/web_src/css/modules/table.css @@ -196,11 +196,6 @@ margin-bottom: 0; } -.ui.striped.table > tr:nth-child(2n), -.ui.striped.table > tbody > tr:nth-child(2n) { - background: var(--color-light); -} - .ui.table[class*="single line"], .ui.table [class*="single line"] { white-space: nowrap; @@ -291,37 +286,10 @@ .ui.basic.table > tr > td { background: transparent; } -.ui.basic.striped.table > tbody > tr:nth-child(2n) { - background: var(--color-light); -} -.ui.basic.striped.selectable.table > tbody > tr:nth-child(2n):hover { - background: var(--color-hover); -} .ui[class*="very basic"].table { border: none; } -.ui[class*="very basic"].table:not(.striped) > tr > th:first-child, -.ui[class*="very basic"].table:not(.striped) > thead > tr > th:first-child, -.ui[class*="very basic"].table:not(.striped) > tbody > tr > th:first-child, -.ui[class*="very basic"].table:not(.striped) > tfoot > tr > th:first-child, -.ui[class*="very basic"].table:not(.striped) > tr > td:first-child, -.ui[class*="very basic"].table:not(.striped) > tbody > tr > td:first-child, -.ui[class*="very basic"].table:not(.striped) > tfoot > tr > td:first-child { - padding-left: 0; -} -.ui[class*="very basic"].table:not(.striped) > tr > th:last-child, -.ui[class*="very basic"].table:not(.striped) > thead > tr > th:last-child, -.ui[class*="very basic"].table:not(.striped) > tbody > tr > th:last-child, -.ui[class*="very basic"].table:not(.striped) > tfoot > tr > th:last-child, -.ui[class*="very basic"].table:not(.striped) > tr > td:last-child, -.ui[class*="very basic"].table:not(.striped) > tbody > tr > td:last-child, -.ui[class*="very basic"].table:not(.striped) > tfoot > tr > td:last-child { - padding-right: 0; -} -.ui[class*="very basic"].table:not(.striped) > thead > tr:first-child > th { - padding-top: 0; -} .ui.celled.table > tr > th, .ui.celled.table > thead > tr > th, diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 16fbfaec4a..83df3e5c29 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -774,10 +774,6 @@ td .commit-summary { width: 200px; } -.repository #commits-table.ui.basic.striped.table tbody tr:nth-child(2n) { - background-color: var(--color-light) !important; -} - .repository .data-table { width: 100%; } diff --git a/web_src/css/repo/commit-sign.css b/web_src/css/repo/commit-sign.css index 56eee62ffc..101eb6d528 100644 --- a/web_src/css/repo/commit-sign.css +++ b/web_src/css/repo/commit-sign.css @@ -7,6 +7,10 @@ flex-shrink: 0; } +.ui.label.commit-sign-badge > * { + display: flex; +} + .ui.label.commit-id-short { font-family: var(--fonts-monospace); height: 24px; diff --git a/web_src/fomantic/build/components/dropdown.js b/web_src/fomantic/build/components/dropdown.js index f40d77df13..47d815490d 100644 --- a/web_src/fomantic/build/components/dropdown.js +++ b/web_src/fomantic/build/components/dropdown.js @@ -407,6 +407,9 @@ $.fn.dropdown = function(parameters) { .html( templates.dropdown(selectValues, fields, settings.preserveHTML, settings.className) ) .insertBefore($input) ; + + $module.attr('data-tooltip-content', $input.attr('data-tooltip-content') ?? null); // GITEA-PATCH: convert "select" to "dropdown" with attrs + if($input.hasClass(className.multiple) && $input.prop('multiple') === false) { module.error(error.missingMultiple); $input.prop('multiple', true); diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue index e1f8475ea8..82e972ceaf 100644 --- a/web_src/js/components/DashboardRepoList.vue +++ b/web_src/js/components/DashboardRepoList.vue @@ -307,6 +307,7 @@ export default defineComponent({ }, async reposFilterKeyControl(e: KeyboardEvent) { + if (e.isComposing) return; switch (e.key) { case 'Enter': document.querySelector('.repo-owner-name-list li.active a')?.click(); diff --git a/web_src/js/components/RepoActionView.test.ts b/web_src/js/components/RepoActionView.test.ts new file mode 100644 index 0000000000..a0855ecf24 --- /dev/null +++ b/web_src/js/components/RepoActionView.test.ts @@ -0,0 +1,26 @@ +import {createLogLineMessage, parseLogLineCommand} from './RepoActionView.vue'; + +test('LogLineMessage', () => { + const cases = { + 'normal message': 'normal message', + '##[group] foo': ' foo', + '::group::foo': 'foo', + '##[endgroup]': '', + '::endgroup::': '', + + // parser shouldn't do any trim, keep origin output as-is + '##[error] foo': ' foo', + '[command] foo': ' foo', + + // hidden is special, it is actually skipped before creating + '##[add-matcher]foo': 'foo', + '::add-matcher::foo': 'foo', + '::remove-matcher foo::': ' foo::', // not correctly parsed, but we don't need it + }; + for (const [input, html] of Object.entries(cases)) { + const line = {index: 0, timestamp: 0, message: input}; + const cmd = parseLogLineCommand(line); + const el = createLogLineMessage(line, cmd); + expect(el.outerHTML).toBe(html); + } +}); diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index 69579d3687..a5275c99e9 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -13,22 +13,48 @@ import {localUserSettings} from '../modules/user-settings.ts'; // see "models/actions/status.go", if it needs to be used somewhere else, move it to a shared file like "types/actions.ts" type RunStatus = 'unknown' | 'waiting' | 'running' | 'success' | 'failure' | 'cancelled' | 'skipped' | 'blocked'; -type StepContainerElement = HTMLElement & {_stepLogsActiveContainer?: HTMLElement} +type StepContainerElement = HTMLElement & { + // To remember the last active logs container, for example: a batch of logs only starts a group but doesn't end it, + // then the following batches of logs should still use the same group (active logs container). + // maybe it can be refactored to decouple from the HTML element in the future. + _stepLogsActiveContainer?: HTMLElement; +} -type LogLine = { +export type LogLine = { index: number; timestamp: number; message: string; }; -const LogLinePrefixesGroup = ['::group::', '##[group]']; -const LogLinePrefixesEndGroup = ['::endgroup::', '##[endgroup]']; +type LogLineCommandName = 'group' | 'endgroup' | 'command' | 'error' | 'hidden'; type LogLineCommand = { - name: 'group' | 'endgroup', + name: LogLineCommandName, prefix: string, } +// How GitHub Actions logs work: +// * Workflow command outputs log commands like "::group::the-title", "::add-matcher::...." +// * Workflow runner parses and processes the commands to "##[group]", apply "matchers", hide secrets, etc. +// * The reported logs are the processed logs. +// HOWEVER: Gitea runner does not completely process those commands. Many works are done by the frontend at the moment. +const LogLinePrefixCommandMap: Record = { + '::group::': 'group', + '##[group]': 'group', + '::endgroup::': 'endgroup', + '##[endgroup]': 'endgroup', + + '##[error]': 'error', + '[command]': 'command', + + // https://github.com/actions/toolkit/blob/master/docs/commands.md + // https://github.com/actions/runner/blob/main/docs/adrs/0276-problem-matchers.md#registration + '::add-matcher::': 'hidden', + '##[add-matcher]': 'hidden', + '::remove-matcher': 'hidden', // it has arguments +}; + + type Job = { id: number; name: string; @@ -49,20 +75,29 @@ type JobStepState = { manuallyCollapsed: boolean, // whether the user manually collapsed the step, used to avoid auto-expanding it again } -function parseLineCommand(line: LogLine): LogLineCommand | null { - for (const prefix of LogLinePrefixesGroup) { +export function parseLogLineCommand(line: LogLine): LogLineCommand | null { + // TODO: in the future it can be refactored to be a general parser that can parse arguments, drop the "prefix match" + for (const prefix in LogLinePrefixCommandMap) { if (line.message.startsWith(prefix)) { - return {name: 'group', prefix}; - } - } - for (const prefix of LogLinePrefixesEndGroup) { - if (line.message.startsWith(prefix)) { - return {name: 'endgroup', prefix}; + return {name: LogLinePrefixCommandMap[prefix], prefix}; } } return null; } +export function createLogLineMessage(line: LogLine, cmd: LogLineCommand | null) { + const logMsgAttrs = {class: 'log-msg'}; + if (cmd?.name) logMsgAttrs.class += ` log-cmd-${cmd?.name}`; // make it easier to add styles to some commands like "error" + + // TODO: for some commands (::group::), the "prefix removal" works well, for some commands with "arguments" (::remove-matcher ...::), + // it needs to do further processing in the future (fortunately, at the moment we don't need to handle these commands) + const msgContent = cmd ? line.message.substring(cmd.prefix.length) : line.message; + + const logMsg = createElementFromAttrs('span', logMsgAttrs); + logMsg.innerHTML = renderAnsi(msgContent); + return logMsg; +} + function isLogElementInViewport(el: Element, {extraViewPortHeight}={extraViewPortHeight: 0}): boolean { const rect = el.getBoundingClientRect(); // only check whether bottom is in viewport, because the log element can be a log group which is usually tall @@ -236,11 +271,7 @@ export default defineComponent({ beginLogGroup(stepIndex: number, startTime: number, line: LogLine, cmd: LogLineCommand) { const el = (this.$refs.logs as any)[stepIndex] as StepContainerElement; const elJobLogGroupSummary = createElementFromAttrs('summary', {class: 'job-log-group-summary'}, - this.createLogLine(stepIndex, startTime, { - index: line.index, - timestamp: line.timestamp, - message: line.message.substring(cmd.prefix.length), - }), + this.createLogLine(stepIndex, startTime, line, cmd), ); const elJobLogList = createElementFromAttrs('div', {class: 'job-log-list'}); const elJobLogGroup = createElementFromAttrs('details', {class: 'job-log-group'}, @@ -254,11 +285,7 @@ export default defineComponent({ endLogGroup(stepIndex: number, startTime: number, line: LogLine, cmd: LogLineCommand) { const el = (this.$refs.logs as any)[stepIndex]; el._stepLogsActiveContainer = null; - el.append(this.createLogLine(stepIndex, startTime, { - index: line.index, - timestamp: line.timestamp, - message: line.message.substring(cmd.prefix.length), - })); + el.append(this.createLogLine(stepIndex, startTime, line, cmd)); }, // show/hide the step logs for a step @@ -279,7 +306,7 @@ export default defineComponent({ POST(`${this.run.link}/approve`); }, - createLogLine(stepIndex: number, startTime: number, line: LogLine) { + createLogLine(stepIndex: number, startTime: number, line: LogLine, cmd: LogLineCommand | null) { const lineNum = createElementFromAttrs('a', {class: 'line-num muted', href: `#jobstep-${stepIndex}-${line.index}`}, String(line.index), ); @@ -288,9 +315,7 @@ export default defineComponent({ formatDatetime(new Date(line.timestamp * 1000)), // for "Show timestamps" ); - const logMsg = createElementFromAttrs('span', {class: 'log-msg'}); - logMsg.innerHTML = renderAnsi(line.message); - + const logMsg = createLogLineMessage(line, cmd); const seconds = Math.floor(line.timestamp - startTime); const logTimeSeconds = createElementFromAttrs('span', {class: 'log-time-seconds'}, `${seconds}s`, // for "Show seconds" @@ -315,16 +340,20 @@ export default defineComponent({ appendLogs(stepIndex: number, startTime: number, logLines: LogLine[]) { for (const line of logLines) { - const el = this.getActiveLogsContainer(stepIndex); - const cmd = parseLineCommand(line); - if (cmd?.name === 'group') { - this.beginLogGroup(stepIndex, startTime, line, cmd); - continue; - } else if (cmd?.name === 'endgroup') { - this.endLogGroup(stepIndex, startTime, line, cmd); - continue; + const cmd = parseLogLineCommand(line); + switch (cmd?.name) { + case 'hidden': + continue; + case 'group': + this.beginLogGroup(stepIndex, startTime, line, cmd); + continue; + case 'endgroup': + this.endLogGroup(stepIndex, startTime, line, cmd); + continue; } - el.append(this.createLogLine(stepIndex, startTime, line)); + // the active logs container may change during the loop, for example: entering and leaving a group + const el = this.getActiveLogsContainer(stepIndex); + el.append(this.createLogLine(stepIndex, startTime, line, cmd)); } }, @@ -976,6 +1005,14 @@ export default defineComponent({ overflow-wrap: anywhere; } +.job-step-logs .job-log-line .log-cmd-command { + color: var(--color-ansi-blue); +} + +.job-step-logs .job-log-line .log-cmd-error { + color: var(--color-ansi-red); +} + /* selectors here are intentionally exact to only match fullscreen */ .full.height > .action-view-right { diff --git a/web_src/js/components/RepoBranchTagSelector.vue b/web_src/js/components/RepoBranchTagSelector.vue index a83227d43b..ab32baed37 100644 --- a/web_src/js/components/RepoBranchTagSelector.vue +++ b/web_src/js/components/RepoBranchTagSelector.vue @@ -159,6 +159,7 @@ export default defineComponent({ return el?.length ? el[0] : null; }, keydown(e: KeyboardEvent) { + if (e.isComposing) return; if (e.key === 'ArrowUp' || e.key === 'ArrowDown') { e.preventDefault(); diff --git a/web_src/js/components/RepoFileSearch.vue b/web_src/js/components/RepoFileSearch.vue index dd93de9850..f0c63267bc 100644 --- a/web_src/js/components/RepoFileSearch.vue +++ b/web_src/js/components/RepoFileSearch.vue @@ -42,6 +42,8 @@ const handleSearchInput = () => { }; const handleKeyDown = (e: KeyboardEvent) => { + if (e.isComposing) return; + if (e.key === 'Escape') { e.preventDefault(); clearSearch(); diff --git a/web_src/js/features/codeeditor.ts b/web_src/js/features/codeeditor.ts index e47a678bda..94c7e18234 100644 --- a/web_src/js/features/codeeditor.ts +++ b/web_src/js/features/codeeditor.ts @@ -10,15 +10,11 @@ type IGlobalEditorOptions = MonacoNamespace.editor.IGlobalEditorOptions; type ITextModelUpdateOptions = MonacoNamespace.editor.ITextModelUpdateOptions; type MonacoOpts = IEditorOptions & IGlobalEditorOptions & ITextModelUpdateOptions; -type EditorConfig = { +type CodeEditorConfig = { indent_style?: 'tab' | 'space', - indent_size?: string | number, // backend emits this as string + indent_size?: number, tab_width?: string | number, // backend emits this as string - end_of_line?: 'lf' | 'cr' | 'crlf', - charset?: 'latin1' | 'utf-8' | 'utf-8-bom' | 'utf-16be' | 'utf-16le', trim_trailing_whitespace?: boolean, - insert_final_newline?: boolean, - root?: boolean, }; const languagesByFilename: Record = {}; @@ -38,14 +34,15 @@ const baseOptions: MonacoOpts = { scrollbar: {horizontalScrollbarSize: 6, verticalScrollbarSize: 6, alwaysConsumeMouseWheel: false}, scrollBeyondLastLine: false, automaticLayout: true, + indentSize: 'tabSize', wrappingIndent: 'none', wordWrapBreakAfterCharacters: '', wordWrapBreakBeforeCharacters: '', matchBrackets: 'never', }; -function getEditorconfig(input: HTMLInputElement): EditorConfig | null { - const json = input.getAttribute('data-editorconfig'); +function getCodeEditorConfig(input: HTMLInputElement): CodeEditorConfig | null { + const json = input.getAttribute('data-code-editor-config'); if (!json) return null; try { return JSON.parse(json); @@ -152,6 +149,7 @@ export async function createMonaco(textarea: HTMLTextAreaElement, filename: stri const editor = monaco.editor.create(container, { model, theme: 'gitea', + ...baseOptions, ...other, }); @@ -167,6 +165,22 @@ export async function createMonaco(textarea: HTMLTextAreaElement, filename: stri textarea.dispatchEvent(new Event('change')); // seems to be needed for jquery-are-you-sure }); + const elEditorOptions = textarea.closest('form')?.querySelector('.code-editor-options'); + if (elEditorOptions) { + elEditorOptions.querySelector('.js-indent-style-select')!.addEventListener('change', (e) => { + const insertSpaces = (e.target as HTMLSelectElement).value === 'space'; + editor.updateOptions({insertSpaces, useTabStops: !insertSpaces}); + }); + elEditorOptions.querySelector('.js-indent-size-select')!.addEventListener('change', (e) => { + const tabSize = Number((e.target as HTMLSelectElement).value); + editor.updateOptions({tabSize}); + }); + elEditorOptions.querySelector('.js-line-wrap-select')!.addEventListener('change', (e) => { + const wordWrap = (e.target as HTMLSelectElement).value as IEditorOptions['wordWrap']; + editor.updateOptions({wordWrap}); + }); + } + exportEditor(editor); const loading = document.querySelector('.editor-loading'); @@ -203,14 +217,13 @@ export async function createCodeEditor(textarea: HTMLTextAreaElement, filenameIn const previewableExts = new Set((textarea.getAttribute('data-previewable-extensions') || '').split(',')); const lineWrapExts = (textarea.getAttribute('data-line-wrap-extensions') || '').split(','); const isPreviewable = previewableExts.has(extname(filename)); - const editorConfig = getEditorconfig(filenameInput); + const editorConfig = getCodeEditorConfig(filenameInput); togglePreviewDisplay(isPreviewable); const {monaco, editor} = await createMonaco(textarea, filename, { - ...baseOptions, ...getFileBasedOptions(filenameInput.value, lineWrapExts), - ...getEditorConfigOptions(editorConfig), + ...getMonacoOptsByCodeEditorConfig(editorConfig), }); filenameInput.addEventListener('input', onInputDebounce(() => { @@ -223,20 +236,15 @@ export async function createCodeEditor(textarea: HTMLTextAreaElement, filenameIn return editor; } -function getEditorConfigOptions(ec: EditorConfig | null): MonacoOpts { +function getMonacoOptsByCodeEditorConfig(ec: CodeEditorConfig | null): MonacoOpts { if (!ec || !isObject(ec)) return {}; const opts: MonacoOpts = {}; - opts.detectIndentation = !('indent_style' in ec) || !('indent_size' in ec); + opts.detectIndentation = !ec.indent_style || !ec.indent_size; - if ('indent_size' in ec) { - opts.indentSize = Number(ec.indent_size); - } - if ('tab_width' in ec) { - opts.tabSize = Number(ec.tab_width) || Number(ec.indent_size); - } - if ('max_line_length' in ec) { - opts.rulers = [Number(ec.max_line_length)]; + // with indentSize='tabSize', this also controls the `indentSize` option + if (!opts.detectIndentation) { + opts.tabSize = Number(ec.tab_width) || Number(ec.indent_size) || 4; } opts.trimAutoWhitespace = ec.trim_trailing_whitespace === true; diff --git a/web_src/js/features/common-form.ts b/web_src/js/features/common-form.ts index fe201e2a01..67bcd55f52 100644 --- a/web_src/js/features/common-form.ts +++ b/web_src/js/features/common-form.ts @@ -14,6 +14,7 @@ export function initGlobalFormDirtyLeaveConfirm() { export function initGlobalEnterQuickSubmit() { document.addEventListener('keydown', (e) => { + if (e.isComposing) return; if (e.key !== 'Enter') return; const hasCtrlOrMeta = ((e.ctrlKey || e.metaKey) && !e.altKey); if (hasCtrlOrMeta && (e.target as HTMLElement).matches('textarea')) { diff --git a/web_src/js/features/comp/EditorMarkdown.ts b/web_src/js/features/comp/EditorMarkdown.ts index cdc9bdaf9a..d95760e841 100644 --- a/web_src/js/features/comp/EditorMarkdown.ts +++ b/web_src/js/features/comp/EditorMarkdown.ts @@ -223,6 +223,7 @@ function isTextExpanderShown(textarea: HTMLElement): boolean { export function initTextareaMarkdown(textarea: HTMLTextAreaElement) { textarea.addEventListener('keydown', (e) => { + if (e.isComposing) return; if (isTextExpanderShown(textarea)) return; if (e.key === 'Tab' && !e.ctrlKey && !e.metaKey && !e.altKey) { // use Tab/Shift-Tab to indent/unindent the selected lines diff --git a/web_src/js/features/repo-diff.ts b/web_src/js/features/repo-diff.ts index e63de7b3cf..0c8b1357b0 100644 --- a/web_src/js/features/repo-diff.ts +++ b/web_src/js/features/repo-diff.ts @@ -170,7 +170,9 @@ async function loadMoreFiles(btn: Element): Promise { const respFileBoxes = respDoc.querySelector('#diff-file-boxes')!; // the response is a full HTML page, we need to extract the relevant contents: // * append the newly loaded file list items to the existing list - document.querySelector('#diff-incomplete')!.replaceWith(...Array.from(respFileBoxes.children)); + const respFileBoxesChildren = Array.from(respFileBoxes.children); // "children:HTMLCollection" will be empty after replaceWith + document.querySelector('#diff-incomplete')!.replaceWith(...respFileBoxesChildren); + for (const el of respFileBoxesChildren) window.htmx.process(el); onShowMoreFiles(); return true; } catch (error) { @@ -200,7 +202,7 @@ function initRepoDiffShowMore() { const resp = await response.text(); const respDoc = parseDom(resp, 'text/html'); const respFileBody = respDoc.querySelector('#diff-file-boxes .diff-file-body .file-body')!; - const respFileBodyChildren = Array.from(respFileBody.children); // respFileBody.children will be empty after replaceWith + const respFileBodyChildren = Array.from(respFileBody.children); // "children:HTMLCollection" will be empty after replaceWith el.parentElement!.replaceWith(...respFileBodyChildren); for (const el of respFileBodyChildren) window.htmx.process(el); // FIXME: calling onShowMoreFiles is not quite right here. diff --git a/web_src/js/features/repo-issue-sidebar.md b/web_src/js/features/repo-issue-sidebar.md index e1ce0927e1..d732bc6f54 100644 --- a/web_src/js/features/repo-issue-sidebar.md +++ b/web_src/js/features/repo-issue-sidebar.md @@ -23,6 +23,16 @@ When the selected items change, the `combo-value` input will be updated. If there is `data-update-url`, it also calls backend to attach/detach the changed items. Also, the changed items will be synchronized to the `ui list` items. +The menu items must have correct `href`, otherwise the links of synchronized (cloned) items would be wrong. + +Synchronization logic: +* On page load: + * If the dropdown menu contains checked items, there will be no synchronization. + In this case, it's assumed that the dropdown menu is already in sync with the list. + * If the dropdown menu doesn't contain checked items, it will use dropdown's value to mark the selected items as checked. + And the selected (checked) items will be synchronized to the list. +* On dropdown selection change: + * The selected items will be synchronized to the list after the dropdown is hidden The items with the same data-scope only allow one selected at a time. diff --git a/web_src/js/features/repo-issue.ts b/web_src/js/features/repo-issue.ts index 7145936ed1..4b04ed4ccf 100644 --- a/web_src/js/features/repo-issue.ts +++ b/web_src/js/features/repo-issue.ts @@ -82,6 +82,7 @@ function initRepoIssueLabelFilter(elDropdown: HTMLElement) { }); // alt(or option) + enter to exclude selected label elDropdown.addEventListener('keydown', (e: KeyboardEvent) => { + if (e.isComposing) return; if (e.altKey && e.key === 'Enter') { const selectedItem = elDropdown.querySelector('.label-filter-query-item.selected'); if (selectedItem) excludeLabel(e, selectedItem); diff --git a/web_src/js/features/tribute.ts b/web_src/js/features/tribute.ts index 0239204f9e..8434a9b2ac 100644 --- a/web_src/js/features/tribute.ts +++ b/web_src/js/features/tribute.ts @@ -1,6 +1,7 @@ import {emojiKeys, emojiHTML, emojiString} from './emoji.ts'; import {html, htmlRaw} from '../utils/html.ts'; import type {TributeCollection} from 'tributejs'; +import type {MentionValue} from '../types.ts'; export async function attachTribute(element: HTMLElement) { const {default: Tribute} = await import(/* webpackChunkName: "tribute" */'tributejs'); @@ -28,7 +29,7 @@ export async function attachTribute(element: HTMLElement) { }, }; - const mentionCollection: TributeCollection> = { + const mentionCollection: TributeCollection = { values: window.config.mentionValues, requireLeadingSpace: true, menuItemTemplate: (item) => { @@ -44,7 +45,10 @@ export async function attachTribute(element: HTMLElement) { }; const tribute = new Tribute({ - collection: [emojiCollection as TributeCollection, mentionCollection], + collection: [ + emojiCollection as TributeCollection, + mentionCollection as TributeCollection, + ], noMatchTemplate: () => '', }); tribute.attach(element); diff --git a/web_src/js/globals.d.ts b/web_src/js/globals.d.ts index ee90bc3c9c..8de6581dbc 100644 --- a/web_src/js/globals.d.ts +++ b/web_src/js/globals.d.ts @@ -29,13 +29,7 @@ interface Window { pageData: Record, notificationSettings: Record, enableTimeTracking: boolean, - mentionValues: Array<{ - key: string, - value: string, - name: string, - fullname: string, - avatar: string, - }>, + mentionValues: Array, mermaidMaxSourceCharacters: number, i18n: Record, }, diff --git a/web_src/js/markup/codecopy.ts b/web_src/js/markup/codecopy.ts index a6e07a3cf6..819b2b5474 100644 --- a/web_src/js/markup/codecopy.ts +++ b/web_src/js/markup/codecopy.ts @@ -1,22 +1,25 @@ import {svg} from '../svg.ts'; -import {queryElems} from '../utils/dom.ts'; +import {createElementFromAttrs, queryElems} from '../utils/dom.ts'; -export function makeCodeCopyButton(): HTMLButtonElement { - const button = document.createElement('button'); - button.classList.add('code-copy', 'ui', 'button'); - button.innerHTML = svg('octicon-copy'); - return button; +export function makeCodeCopyButton(attrs: Record = {}): HTMLButtonElement { + const btn = createElementFromAttrs('button', { + class: 'ui compact icon button code-copy auto-hide-control', + ...attrs, + }); + btn.innerHTML = svg('octicon-copy'); + return btn; } export function initMarkupCodeCopy(elMarkup: HTMLElement): void { // .markup .code-block code queryElems(elMarkup, '.code-block code', (el) => { if (!el.textContent) return; - const btn = makeCodeCopyButton(); // remove final trailing newline introduced during HTML rendering - btn.setAttribute('data-clipboard-text', el.textContent.replace(/\r?\n$/, '')); + const btn = makeCodeCopyButton({ + 'data-clipboard-text': el.textContent.replace(/\r?\n$/, ''), + }); // we only want to use `.code-block-container` if it exists, no matter `.code-block` exists or not. - const btnContainer = el.closest('.code-block-container') ?? el.closest('.code-block'); - btnContainer!.append(btn); + const btnContainer = el.closest('.code-block-container') ?? el.closest('.code-block')!; + btnContainer.append(btn); }); } diff --git a/web_src/js/markup/mermaid.test.ts b/web_src/js/markup/mermaid.test.ts new file mode 100644 index 0000000000..a7467858f5 --- /dev/null +++ b/web_src/js/markup/mermaid.test.ts @@ -0,0 +1,59 @@ +import {sourceNeedsElk} from './mermaid.ts'; +import {dedent} from '../utils/testhelper.ts'; + +test('MermaidConfigLayoutCheck', () => { + expect(sourceNeedsElk(dedent(` + flowchart TB + elk --> B + `))).toEqual(false); + + expect(sourceNeedsElk(dedent(` + --- + config: + layout : elk + --- + flowchart TB + A --> B + `))).toEqual(true); + + expect(sourceNeedsElk(dedent(` + --- + config: + layout: elk.layered + --- + flowchart TB + A --> B + `))).toEqual(true); + + expect(sourceNeedsElk(` + %%{ init : { "flowchart": { "defaultRenderer": "elk" } } }%% + flowchart TB + A --> B + `)).toEqual(true); + + expect(sourceNeedsElk(dedent(` + --- + config: + layout: 123 + --- + %%{ init : { "class": { "defaultRenderer": "elk.any" } } }%% + flowchart TB + A --> B + `))).toEqual(true); + + expect(sourceNeedsElk(` + %%{init:{ + "layout" : "elk.layered" + }}%% + flowchart TB + A --> B + `)).toEqual(true); + + expect(sourceNeedsElk(` + %%{ initialize: { + 'layout' : 'elk.layered' + }}%% + flowchart TB + A --> B + `)).toEqual(true); +}); diff --git a/web_src/js/markup/mermaid.ts b/web_src/js/markup/mermaid.ts index e1c2935f23..5148ff377c 100644 --- a/web_src/js/markup/mermaid.ts +++ b/web_src/js/markup/mermaid.ts @@ -1,87 +1,251 @@ -import {isDarkTheme} from '../utils.ts'; -import {makeCodeCopyButton} from './codecopy.ts'; +import {isDarkTheme, parseDom} from '../utils.ts'; import {displayError} from './common.ts'; -import {queryElems} from '../utils/dom.ts'; +import {createElementFromAttrs, createElementFromHTML, queryElems} from '../utils/dom.ts'; import {html, htmlRaw} from '../utils/html.ts'; +import {load as loadYaml} from 'js-yaml'; +import type {MermaidConfig} from 'mermaid'; +import {svg} from '../svg.ts'; const {mermaidMaxSourceCharacters} = window.config; -const iframeCss = `:root {color-scheme: normal} -body {margin: 0; padding: 0; overflow: hidden} -#mermaid {display: block; margin: 0 auto}`; +function getIframeCss(): string { + return ` +html, body { height: 100%; } +body { margin: 0; padding: 0; overflow: hidden; } +#mermaid { display: block; margin: 0 auto; } +`; +} + +function isSourceTooLarge(source: string) { + return mermaidMaxSourceCharacters >= 0 && source.length > mermaidMaxSourceCharacters; +} + +function parseYamlInitConfig(source: string): MermaidConfig | null { + // ref: https://github.com/mermaid-js/mermaid/blob/develop/packages/mermaid/src/diagram-api/regexes.ts + const yamlFrontMatterRegex = /^---\s*[\n\r](.*?)[\n\r]---\s*[\n\r]+/s; + const frontmatter = (yamlFrontMatterRegex.exec(source) || [])[1]; + if (!frontmatter) return null; + try { + return (loadYaml(frontmatter) as {config: MermaidConfig})?.config; + } catch { + console.error('invalid or unsupported mermaid init YAML config', frontmatter); + } + return null; +} + +function parseJsonInitConfig(source: string): MermaidConfig | null { + // https://mermaid.js.org/config/directives.html#declaring-directives + // Do as dirty as mermaid does: https://github.com/mermaid-js/mermaid/blob/develop/packages/mermaid/src/utils.ts + // It can even accept invalid JSON string like: + // %%{initialize: { 'logLevel': 'fatal', "theme":'dark', 'startOnLoad': true } }%% + const jsonInitConfigRegex = /%%\{\s*(init|initialize)\s*:\s*(.*?)\}%%/s; + const jsonInitText = (jsonInitConfigRegex.exec(source) || [])[2]; + if (!jsonInitText) return null; + try { + const processed = jsonInitText.trim().replace(/'/g, '"'); + return JSON.parse(processed); + } catch { + console.error('invalid or unsupported mermaid init JSON config', jsonInitText); + } + return null; +} + +function configValueIsElk(layoutOrRenderer: string | undefined) { + if (typeof layoutOrRenderer !== 'string') return false; + return layoutOrRenderer === 'elk' || layoutOrRenderer.startsWith('elk.'); +} + +function configContainsElk(config: MermaidConfig | null) { + if (!config) return false; + // Check the layout from the following properties: + // * config.layout + // * config.{any-diagram-config}.defaultRenderer + // Although only a few diagram types like "flowchart" support "defaultRenderer", + // as long as there is no side effect, here do a general check for all properties of "config", for ease of maintenance + return configValueIsElk(config.layout) || Object.values(config).some((diagCfg) => configValueIsElk(diagCfg?.defaultRenderer)); +} + +export function sourceNeedsElk(source: string) { + if (isSourceTooLarge(source)) return false; + const configYaml = parseYamlInitConfig(source), configJson = parseJsonInitConfig(source); + return configContainsElk(configYaml) || configContainsElk(configJson); +} + +async function loadMermaid(needElkRender: boolean) { + const mermaidPromise = import(/* webpackChunkName: "mermaid" */'mermaid'); + const elkPromise = needElkRender ? import(/* webpackChunkName: "mermaid-layout-elk" */'@mermaid-js/layout-elk') : null; + const results = await Promise.all([mermaidPromise, elkPromise]); + return { + mermaid: results[0].default, + elkLayouts: results[1]?.default, + }; +} + +function initMermaidViewController(viewController: HTMLElement, dragElement: SVGSVGElement) { + let inited = false, isDragging = false; + let currentScale = 1, initLeft = 0, lastLeft = 0, lastTop = 0, lastPageX = 0, lastPageY = 0; + + const resetView = () => { + currentScale = 1; + lastLeft = initLeft; + lastTop = 0; + dragElement.style.left = `${lastLeft}px`; + dragElement.style.top = `${lastTop}px`; + dragElement.style.position = 'absolute'; + dragElement.style.margin = '0'; + }; + + const initAbsolutePosition = () => { + if (inited) return; + // if we need to drag or zoom, use absolute position and get the current "left" from the "margin: auto" layout. + inited = true; + const container = dragElement.parentElement!; + initLeft = container.getBoundingClientRect().width / 2 - dragElement.getBoundingClientRect().width / 2; + resetView(); + }; + + for (const el of viewController.querySelectorAll('[data-control-action]')) { + el.addEventListener('click', () => { + initAbsolutePosition(); + switch (el.getAttribute('data-control-action')) { + case 'zoom-in': + currentScale *= 1.2; + break; + case 'zoom-out': + currentScale /= 1.2; + break; + case 'reset': + resetView(); + break; + } + dragElement.style.transform = `scale(${currentScale})`; + }); + } + + dragElement.addEventListener('mousedown', (e) => { + if (e.button !== 0 || e.altKey || e.ctrlKey || e.metaKey || e.shiftKey) return; // only left mouse button can drag + const target = e.target as Element; + // don't start the drag if the click is on an interactive element (e.g.: link, button) or text element + if (target.closest('div, p, a, span, button, input, text')) return; + + initAbsolutePosition(); + isDragging = true; + lastPageX = e.pageX; + lastPageY = e.pageY; + dragElement.style.cursor = 'grabbing'; + }); + + dragElement.ownerDocument.addEventListener('mousemove', (e) => { + if (!isDragging) return; + lastLeft = e.pageX - lastPageX + lastLeft; + lastTop = e.pageY - lastPageY + lastTop; + dragElement.style.left = `${lastLeft}px`; + dragElement.style.top = `${lastTop}px`; + lastPageX = e.pageX; + lastPageY = e.pageY; + }); + + dragElement.ownerDocument.addEventListener('mouseup', () => { + if (!isDragging) return; + isDragging = false; + dragElement.style.removeProperty('cursor'); + }); +} + +let elkLayoutsRegistered = false; export async function initMarkupCodeMermaid(elMarkup: HTMLElement): Promise { // .markup code.language-mermaid - queryElems(elMarkup, 'code.language-mermaid', async (el) => { - const {default: mermaid} = await import(/* webpackChunkName: "mermaid" */'mermaid'); + const mermaidBlocks: Array<{source: string, parentContainer: HTMLElement}> = []; + const attrMermaidRendered = 'data-markup-mermaid-rendered'; + let needElkRender = false; + for (const elCodeBlock of queryElems(elMarkup, 'code.language-mermaid')) { + const parentContainer = elCodeBlock.closest('pre')!; // it must exist, if no, there must be a bug + if (parentContainer.hasAttribute(attrMermaidRendered)) continue; + parentContainer.setAttribute(attrMermaidRendered, 'true'); - mermaid.initialize({ - startOnLoad: false, - theme: isDarkTheme() ? 'dark' : 'neutral', - securityLevel: 'strict', - suppressErrorRendering: true, - }); + const source = elCodeBlock.textContent ?? ''; + needElkRender = needElkRender || sourceNeedsElk(source); + mermaidBlocks.push({source, parentContainer}); + } + if (!mermaidBlocks.length) return; - const pre = el.closest('pre'); - if (!pre || pre.hasAttribute('data-render-done')) return; + const {mermaid, elkLayouts} = await loadMermaid(needElkRender); + if (elkLayouts && !elkLayoutsRegistered) { + mermaid.registerLayoutLoaders(elkLayouts); + elkLayoutsRegistered = true; + } + mermaid.initialize({ + startOnLoad: false, + theme: isDarkTheme() ? 'dark' : 'neutral', // TODO: maybe it should use "darkMode" to adopt more user-specified theme instead of just "dark" or "neutral" + securityLevel: 'strict', + suppressErrorRendering: true, + }); - const source = el.textContent; - if (mermaidMaxSourceCharacters >= 0 && source.length > mermaidMaxSourceCharacters) { - displayError(pre, new Error(`Mermaid source of ${source.length} characters exceeds the maximum allowed length of ${mermaidMaxSourceCharacters}.`)); - return; + const iframeStyleText = getIframeCss(); + const applyMermaidIframeHeight = (iframe: HTMLIFrameElement, height: number) => { + if (!height) return; + // use a min-height to make sure the buttons won't overlap. + iframe.style.height = `${Math.max(height, 85)}px`; + }; + + // mermaid is a globally shared instance, its document also says "Multiple calls to this function will be enqueued to run serially." + // so here we just simply render the mermaid blocks one by one, no need to do "Promise.all" concurrently + for (const block of mermaidBlocks) { + const {source, parentContainer} = block; + if (isSourceTooLarge(source)) { + displayError(parentContainer, new Error(`Mermaid source of ${source.length} characters exceeds the maximum allowed length of ${mermaidMaxSourceCharacters}.`)); + continue; } try { - await mermaid.parse(source); - } catch (err) { - displayError(pre, err); - return; - } + // render the mermaid diagram to svg text, and parse it to a DOM node + const {svg: svgText, bindFunctions} = await mermaid.render('mermaid', source, parentContainer); + const svgDoc = parseDom(svgText, 'image/svg+xml'); + const svgNode = (svgDoc.documentElement as unknown) as SVGSVGElement; - try { - // can't use bindFunctions here because we can't cross the iframe boundary. This - // means js-based interactions won't work but they aren't intended to work either - const {svg} = await mermaid.render('mermaid', source); + const viewControllerHtml = html` +
+ + + +
+ `; + const viewController = createElementFromHTML(viewControllerHtml); + // create an iframe to sandbox the svg with styles, and set correct height by reading svg's viewBox height const iframe = document.createElement('iframe'); - iframe.classList.add('markup-content-iframe', 'tw-invisible'); - iframe.srcdoc = html`${htmlRaw(svg)}`; + iframe.classList.add('markup-content-iframe', 'is-loading'); + // the styles are not ready, so don't really render anything before the "load" event, to avoid flicker of unstyled content + iframe.srcdoc = html``; - const mermaidBlock = document.createElement('div'); - mermaidBlock.classList.add('mermaid-block', 'is-loading', 'tw-hidden'); - mermaidBlock.append(iframe); - - const btn = makeCodeCopyButton(); - btn.setAttribute('data-clipboard-text', source); - mermaidBlock.append(btn); - - const updateIframeHeight = () => { - const body = iframe.contentWindow?.document?.body; - if (body) { - iframe.style.height = `${body.clientHeight}px`; - } - }; + // although the "viewBox" is optional, mermaid's output should always have a correct viewBox with width and height + const iframeHeightFromViewBox = Math.ceil(svgNode.viewBox?.baseVal?.height ?? 0); + applyMermaidIframeHeight(iframe, iframeHeightFromViewBox); + // the iframe will be fully reloaded if its DOM context is changed (e.g.: moved in the DOM tree). + // to avoid unnecessary reloading, we should insert the iframe to its final position only once. iframe.addEventListener('load', () => { - pre.replaceWith(mermaidBlock); - mermaidBlock.classList.remove('tw-hidden'); - updateIframeHeight(); - setTimeout(() => { // avoid flash of iframe background - mermaidBlock.classList.remove('is-loading'); - iframe.classList.remove('tw-invisible'); - }, 0); + // same origin, so we can operate "iframe head/body" and all elements directly + const style = document.createElement('style'); + style.textContent = iframeStyleText; + iframe.contentDocument!.head.append(style); - // update height when element's visibility state changes, for example when the diagram is inside - // a
+ block and the
block becomes visible upon user interaction, it - // would initially set a incorrect height and the correct height is set during this callback. - (new IntersectionObserver(() => { - updateIframeHeight(); - }, {root: document.documentElement})).observe(iframe); + const iframeBody = iframe.contentDocument!.body; + iframeBody.append(svgNode); + bindFunctions?.(iframeBody); // follow "mermaid.render" doc, attach event handlers to the svg's container + + // according to mermaid, the viewBox height should always exist, here just a fallback for unknown cases. + // and keep in mind: clientHeight can be 0 if the element is hidden (display: none). + if (!iframeHeightFromViewBox) applyMermaidIframeHeight(iframe, iframeBody.clientHeight); + iframe.classList.remove('is-loading'); + initMermaidViewController(viewController, svgNode); }); - document.body.append(mermaidBlock); + const container = createElementFromAttrs('div', {class: 'mermaid-block'}, iframe, viewController); + parentContainer.replaceWith(container); } catch (err) { - displayError(pre, err); + displayError(parentContainer, err); } - }); + } } diff --git a/web_src/js/modules/fomantic/dropdown.ts b/web_src/js/modules/fomantic/dropdown.ts index 79ca60d347..7f7f3611be 100644 --- a/web_src/js/modules/fomantic/dropdown.ts +++ b/web_src/js/modules/fomantic/dropdown.ts @@ -225,6 +225,7 @@ function attachDomEvents(dropdown: HTMLElement, focusable: HTMLElement, menu: HT }; dropdown.addEventListener('keydown', (e: KeyboardEvent) => { + if (e.isComposing) return; // here it must use keydown event before dropdown's keyup handler, otherwise there is no Enter event in our keyup handler if (e.key === 'Enter') { const elItem = menu.querySelector(':scope > .item.selected, .menu > .item.selected'); diff --git a/web_src/js/render/ansi.ts b/web_src/js/render/ansi.ts index 685e916c9a..f5429ef6ad 100644 --- a/web_src/js/render/ansi.ts +++ b/web_src/js/render/ansi.ts @@ -31,7 +31,7 @@ export function renderAnsi(line: string): string { // handle "\rReading...1%\rReading...5%\rReading...100%", // convert it into a multiple-line string: "Reading...1%\nReading...5%\nReading...100%" - const lines = []; + const lines: Array = []; for (const part of line.split('\r')) { if (part === '') continue; const partHtml = ansi_up.ansi_to_html(part); diff --git a/web_src/js/svg.ts b/web_src/js/svg.ts index 3d3e653072..cd19938c99 100644 --- a/web_src/js/svg.ts +++ b/web_src/js/svg.ts @@ -80,6 +80,8 @@ import octiconTrash from '../../public/assets/img/svg/octicon-trash.svg'; import octiconTriangleDown from '../../public/assets/img/svg/octicon-triangle-down.svg'; import octiconX from '../../public/assets/img/svg/octicon-x.svg'; import octiconXCircleFill from '../../public/assets/img/svg/octicon-x-circle-fill.svg'; +import octiconZoomIn from '../../public/assets/img/svg/octicon-zoom-in.svg'; +import octiconZoomOut from '../../public/assets/img/svg/octicon-zoom-out.svg'; const svgs = { 'gitea-double-chevron-left': giteaDoubleChevronLeft, @@ -161,6 +163,8 @@ const svgs = { 'octicon-triangle-down': octiconTriangleDown, 'octicon-x': octiconX, 'octicon-x-circle-fill': octiconXCircleFill, + 'octicon-zoom-in': octiconZoomIn, + 'octicon-zoom-out': octiconZoomOut, }; export type SvgName = keyof typeof svgs; diff --git a/web_src/js/types.ts b/web_src/js/types.ts index 56527729a1..815dfd2f82 100644 --- a/web_src/js/types.ts +++ b/web_src/js/types.ts @@ -2,6 +2,14 @@ export type IntervalId = ReturnType; export type Intent = 'error' | 'warning' | 'info'; +export type MentionValue = { + key: string, + value: string, + name: string, + fullname: string, + avatar: string, +}; + export type RequestData = string | FormData | URLSearchParams | Record; export type RequestOpts = { diff --git a/web_src/js/utils/dom.ts b/web_src/js/utils/dom.ts index dc504b7056..6833a196c3 100644 --- a/web_src/js/utils/dom.ts +++ b/web_src/js/utils/dom.ts @@ -301,7 +301,7 @@ export function createElementFromHTML(htmlString: string) return div.firstChild as T; } -export function createElementFromAttrs(tagName: string, attrs: Record | null, ...children: (Node | string)[]): HTMLElement { +export function createElementFromAttrs(tagName: string, attrs: Record | null, ...children: (Node | string)[]): T { const el = document.createElement(tagName); for (const [key, value] of Object.entries(attrs || {})) { if (value === undefined || value === null) continue; @@ -314,7 +314,7 @@ export function createElementFromAttrs(tagName: string, attrs: Record { diff --git a/web_src/js/utils/glob.test.ts b/web_src/js/utils/glob.test.ts index 0c5d9783c0..fbf83a6cfe 100644 --- a/web_src/js/utils/glob.test.ts +++ b/web_src/js/utils/glob.test.ts @@ -117,7 +117,6 @@ test('GlobCompiler', async () => { for (const c of golangCases) { const compiled = globCompile(c.pattern, c.separators); const msg = `pattern: ${c.pattern}, input: ${c.input}, separators: ${c.separators || '(none)'}, compiled: ${compiled.regexpPattern}`; - // eslint-disable-next-line vitest/valid-expect -- Unlike Jest, Vitest supports a message as the second argument expect(compiled.regexp.test(c.input), msg).toBe(c.matched); } diff --git a/web_src/js/utils/testhelper.ts b/web_src/js/utils/testhelper.ts index 0e0aff9fa3..59eb39778c 100644 --- a/web_src/js/utils/testhelper.ts +++ b/web_src/js/utils/testhelper.ts @@ -4,3 +4,19 @@ export function isInFrontendUnitTest() { return import.meta.env.TEST === 'true'; } + +/** strip common indentation from a string and trim it */ +export function dedent(str: string) { + const match = str.match(/^[ \t]*(?=\S)/gm); + if (!match) return str; + + let minIndent = Number.POSITIVE_INFINITY; + for (const indent of match) { + minIndent = Math.min(minIndent, indent.length); + } + if (minIndent === 0 || minIndent === Number.POSITIVE_INFINITY) { + return str; + } + + return str.replace(new RegExp(`^[ \\t]{${minIndent}}`, 'gm'), '').trim(); +} diff --git a/web_src/js/webcomponents/overflow-menu.ts b/web_src/js/webcomponents/overflow-menu.ts index b1294eae3a..1ddd984c12 100644 --- a/web_src/js/webcomponents/overflow-menu.ts +++ b/web_src/js/webcomponents/overflow-menu.ts @@ -22,6 +22,7 @@ window.customElements.define('overflow-menu', class extends HTMLElement { const div = document.createElement('div'); div.tabIndex = -1; // for initial focus, programmatic focus only div.addEventListener('keydown', (e) => { + if (e.isComposing) return; if (e.key === 'Tab') { const items = this.tippyContent.querySelectorAll('[role="menuitem"]'); if (e.shiftKey) {