Compare commits

..

18 Commits

Author SHA1 Message Date
dependabot[bot]
ff1848072d Chore(deps-dev): Bump jest-preset-angular
Bumps the frontend-jest-dependencies group in /src-ui with 1 update: [jest-preset-angular](https://github.com/thymikee/jest-preset-angular).


Updates `jest-preset-angular` from 15.0.2 to 15.0.3
- [Release notes](https://github.com/thymikee/jest-preset-angular/releases)
- [Changelog](https://github.com/thymikee/jest-preset-angular/blob/main/CHANGELOG.md)
- [Commits](https://github.com/thymikee/jest-preset-angular/compare/v15.0.2...v15.0.3)

---
updated-dependencies:
- dependency-name: jest-preset-angular
  dependency-version: 15.0.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: frontend-jest-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-01 20:07:47 +00:00
shamoon
74b10db028 Fix: improve legibility of processed mail error popover in light mode (#11258) 2025-11-01 12:49:05 -07:00
shamoon
cffb9c34f0 Chore: add headers for wikipedia CI tests (#11253) 2025-11-01 09:37:49 -07:00
GitHub Actions
6f52614817 Auto translate strings 2025-11-01 14:53:03 +00:00
shamoon
a0d3527d20 Fixhancement: truncate large logs, improve auto-scroll (#11239) 2025-11-01 07:49:52 -07:00
shamoon
4e64ca7ca6 Chore: add max-height and overflow to processedmail error popover (#11252) 2025-11-01 07:49:31 -07:00
GitHub Actions
e9511bd3da Auto translate strings 2025-10-31 01:28:27 +00:00
shamoon
8b9ca75a90 Fix: delay iframe DOM removal, handle onafterprint error for print in FF (#11237) 2025-10-30 18:26:42 -07:00
shamoon
9f0a4ac19d Sure sonar, consolidate 2025-10-30 18:00:19 -07:00
shamoon
8f969ecab5 Fix: delay iframe DOM removal for print in FF 2025-10-30 17:24:44 -07:00
shamoon
245e52a4eb Coverage 2025-10-30 17:00:15 -07:00
shamoon
a8c75d95d8 Update document-detail.component.ts 2025-10-30 17:00:15 -07:00
shamoon
d6e2456baf Update document-detail.component.ts 2025-10-30 17:00:15 -07:00
shamoon
3b75d3271e Fix: delay iframe DOM removal for print in FF 2025-10-30 17:00:15 -07:00
GitHub Actions
e88816d141 Auto translate strings 2025-10-30 23:36:37 +00:00
CanbiZ
e5bd4713ac Performance: use virtual scroll container and log level parsing for logs view (#11233)
---------

Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2025-10-30 23:34:53 +00:00
shamoon
b9aced07fb Chore: cache Github version check for 15 minutes (#11235) 2025-10-30 13:53:30 -07:00
shamoon
6b55740f56 Fix: de-deduplicate children in tag list when filtering (#11229) 2025-10-30 07:02:00 -07:00
22 changed files with 503 additions and 253 deletions

View File

@@ -26,7 +26,7 @@ dependencies = [
# WARNING: django does not use semver.
# Only patch versions are guaranteed to not introduce breaking changes.
"django~=5.2.5",
"django-allauth[mfa,socialaccount]~=65.12.1",
"django-allauth[mfa,socialaccount]~=65.4.0",
"django-auditlog~=3.2.1",
"django-cachalot~=2.8.0",
"django-celery-results~=2.6.0",

View File

@@ -672,11 +672,33 @@
<context context-type="linenumber">4</context>
</context-group>
</trans-unit>
<trans-unit id="8461842260159597706" datatype="html">
<source>Show</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/logs/logs.component.html</context>
<context context-type="linenumber">8</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">37</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/saved-views/saved-views.component.html</context>
<context context-type="linenumber">52</context>
</context-group>
</trans-unit>
<trans-unit id="5724363929304709833" datatype="html">
<source>lines</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/logs/logs.component.html</context>
<context context-type="linenumber">17</context>
</context-group>
</trans-unit>
<trans-unit id="8838884664569764142" datatype="html">
<source>Auto refresh</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/logs/logs.component.html</context>
<context context-type="linenumber">8</context>
<context context-type="linenumber">21</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
@@ -687,11 +709,11 @@
<source>Loading...</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/logs/logs.component.html</context>
<context context-type="linenumber">24</context>
<context context-type="linenumber">38</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/logs/logs.component.html</context>
<context context-type="linenumber">36</context>
<context context-type="linenumber">53</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/tasks/tasks.component.html</context>
@@ -7259,25 +7281,25 @@
<source>Print failed.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">1455</context>
<context context-type="linenumber">1460</context>
</context-group>
</trans-unit>
<trans-unit id="6457245677384603573" datatype="html">
<source>Error loading document for printing.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">1463</context>
<context context-type="linenumber">1472</context>
</context-group>
</trans-unit>
<trans-unit id="6085793215710522488" datatype="html">
<source>An error occurred loading tiff: <x id="PH" equiv-text="err.toString()"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">1528</context>
<context context-type="linenumber">1537</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-detail/document-detail.component.ts</context>
<context context-type="linenumber">1532</context>
<context context-type="linenumber">1541</context>
</context-group>
</trans-unit>
<trans-unit id="4958946940233632319" datatype="html">
@@ -7881,17 +7903,6 @@
<context context-type="linenumber">45</context>
</context-group>
</trans-unit>
<trans-unit id="8461842260159597706" datatype="html">
<source>Show</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/document-list/document-list.component.html</context>
<context context-type="linenumber">37</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/manage/saved-views/saved-views.component.html</context>
<context context-type="linenumber">52</context>
</context-group>
</trans-unit>
<trans-unit id="5146398958364876914" datatype="html">
<source>Sort</source>
<context-group purpose="location">

View File

@@ -63,7 +63,7 @@
"jest": "30.2.0",
"jest-environment-jsdom": "^30.2.0",
"jest-junit": "^16.0.0",
"jest-preset-angular": "^15.0.2",
"jest-preset-angular": "^15.0.3",
"jest-websocket-mock": "^2.5.0",
"prettier-plugin-organize-imports": "^4.3.0",
"ts-node": "~10.9.1",

281
src-ui/pnpm-lock.yaml generated
View File

@@ -160,8 +160,8 @@ importers:
specifier: ^16.0.0
version: 16.0.0
jest-preset-angular:
specifier: ^15.0.2
version: 15.0.2(ccefccc315e3e4bd30d78eb49c90d46a)
specifier: ^15.0.3
version: 15.0.3(ccefccc315e3e4bd30d78eb49c90d46a)
jest-websocket-mock:
specifier: ^2.5.0
version: 2.5.0
@@ -1257,8 +1257,8 @@ packages:
'@emnapi/wasi-threads@1.1.0':
resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==}
'@esbuild/aix-ppc64@0.25.10':
resolution: {integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==}
'@esbuild/aix-ppc64@0.25.11':
resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
@@ -1275,8 +1275,8 @@ packages:
cpu: [ppc64]
os: [aix]
'@esbuild/android-arm64@0.25.10':
resolution: {integrity: sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==}
'@esbuild/android-arm64@0.25.11':
resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
@@ -1293,8 +1293,8 @@ packages:
cpu: [arm64]
os: [android]
'@esbuild/android-arm@0.25.10':
resolution: {integrity: sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==}
'@esbuild/android-arm@0.25.11':
resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
@@ -1311,8 +1311,8 @@ packages:
cpu: [arm]
os: [android]
'@esbuild/android-x64@0.25.10':
resolution: {integrity: sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==}
'@esbuild/android-x64@0.25.11':
resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
@@ -1329,8 +1329,8 @@ packages:
cpu: [x64]
os: [android]
'@esbuild/darwin-arm64@0.25.10':
resolution: {integrity: sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==}
'@esbuild/darwin-arm64@0.25.11':
resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
@@ -1347,8 +1347,8 @@ packages:
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-x64@0.25.10':
resolution: {integrity: sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==}
'@esbuild/darwin-x64@0.25.11':
resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
@@ -1365,8 +1365,8 @@ packages:
cpu: [x64]
os: [darwin]
'@esbuild/freebsd-arm64@0.25.10':
resolution: {integrity: sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==}
'@esbuild/freebsd-arm64@0.25.11':
resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
@@ -1383,8 +1383,8 @@ packages:
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-x64@0.25.10':
resolution: {integrity: sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==}
'@esbuild/freebsd-x64@0.25.11':
resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
@@ -1401,8 +1401,8 @@ packages:
cpu: [x64]
os: [freebsd]
'@esbuild/linux-arm64@0.25.10':
resolution: {integrity: sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==}
'@esbuild/linux-arm64@0.25.11':
resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
@@ -1419,8 +1419,8 @@ packages:
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm@0.25.10':
resolution: {integrity: sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==}
'@esbuild/linux-arm@0.25.11':
resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
@@ -1437,8 +1437,8 @@ packages:
cpu: [arm]
os: [linux]
'@esbuild/linux-ia32@0.25.10':
resolution: {integrity: sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==}
'@esbuild/linux-ia32@0.25.11':
resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
@@ -1455,8 +1455,8 @@ packages:
cpu: [ia32]
os: [linux]
'@esbuild/linux-loong64@0.25.10':
resolution: {integrity: sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==}
'@esbuild/linux-loong64@0.25.11':
resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
@@ -1473,8 +1473,8 @@ packages:
cpu: [loong64]
os: [linux]
'@esbuild/linux-mips64el@0.25.10':
resolution: {integrity: sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==}
'@esbuild/linux-mips64el@0.25.11':
resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
@@ -1491,8 +1491,8 @@ packages:
cpu: [mips64el]
os: [linux]
'@esbuild/linux-ppc64@0.25.10':
resolution: {integrity: sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==}
'@esbuild/linux-ppc64@0.25.11':
resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
@@ -1509,8 +1509,8 @@ packages:
cpu: [ppc64]
os: [linux]
'@esbuild/linux-riscv64@0.25.10':
resolution: {integrity: sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==}
'@esbuild/linux-riscv64@0.25.11':
resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
@@ -1527,8 +1527,8 @@ packages:
cpu: [riscv64]
os: [linux]
'@esbuild/linux-s390x@0.25.10':
resolution: {integrity: sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==}
'@esbuild/linux-s390x@0.25.11':
resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
@@ -1545,8 +1545,8 @@ packages:
cpu: [s390x]
os: [linux]
'@esbuild/linux-x64@0.25.10':
resolution: {integrity: sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==}
'@esbuild/linux-x64@0.25.11':
resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
@@ -1563,8 +1563,8 @@ packages:
cpu: [x64]
os: [linux]
'@esbuild/netbsd-arm64@0.25.10':
resolution: {integrity: sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==}
'@esbuild/netbsd-arm64@0.25.11':
resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [netbsd]
@@ -1581,8 +1581,8 @@ packages:
cpu: [arm64]
os: [netbsd]
'@esbuild/netbsd-x64@0.25.10':
resolution: {integrity: sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==}
'@esbuild/netbsd-x64@0.25.11':
resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
@@ -1599,8 +1599,8 @@ packages:
cpu: [x64]
os: [netbsd]
'@esbuild/openbsd-arm64@0.25.10':
resolution: {integrity: sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==}
'@esbuild/openbsd-arm64@0.25.11':
resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
@@ -1617,8 +1617,8 @@ packages:
cpu: [arm64]
os: [openbsd]
'@esbuild/openbsd-x64@0.25.10':
resolution: {integrity: sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==}
'@esbuild/openbsd-x64@0.25.11':
resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
@@ -1635,8 +1635,8 @@ packages:
cpu: [x64]
os: [openbsd]
'@esbuild/openharmony-arm64@0.25.10':
resolution: {integrity: sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==}
'@esbuild/openharmony-arm64@0.25.11':
resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openharmony]
@@ -1647,8 +1647,8 @@ packages:
cpu: [arm64]
os: [openharmony]
'@esbuild/sunos-x64@0.25.10':
resolution: {integrity: sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==}
'@esbuild/sunos-x64@0.25.11':
resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
@@ -1665,8 +1665,8 @@ packages:
cpu: [x64]
os: [sunos]
'@esbuild/win32-arm64@0.25.10':
resolution: {integrity: sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==}
'@esbuild/win32-arm64@0.25.11':
resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
@@ -1683,8 +1683,8 @@ packages:
cpu: [arm64]
os: [win32]
'@esbuild/win32-ia32@0.25.10':
resolution: {integrity: sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==}
'@esbuild/win32-ia32@0.25.11':
resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
@@ -1701,8 +1701,8 @@ packages:
cpu: [ia32]
os: [win32]
'@esbuild/win32-x64@0.25.10':
resolution: {integrity: sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==}
'@esbuild/win32-x64@0.25.11':
resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
@@ -3660,6 +3660,10 @@ packages:
resolution: {integrity: sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==}
engines: {node: '>=8'}
ci-info@4.3.1:
resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==}
engines: {node: '>=8'}
cjs-module-lexer@2.1.0:
resolution: {integrity: sha512-UX0OwmYRYQQetfrLEZeewIFFI+wSTofC+pMBLNuH3RUuu/xzG1oz84UCEDOSoQlN3fZ4+AzmV50ZYvGqkMh9yA==}
@@ -4064,8 +4068,8 @@ packages:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
esbuild-wasm@0.25.10:
resolution: {integrity: sha512-IyyfrTA2iiOh/uhlaJj0aUDgW42lFhr29ZeKouVNOz/8mLyuqWbEuVst+B4RBH18pb3AcOHnaOgyskAbsVOe3A==}
esbuild-wasm@0.25.11:
resolution: {integrity: sha512-60gllbYFIRGzB6KALBB5Va9Wy3VeCi2U0NgmM7r+TFnRgzeEyoCn2D7fhacW2zWbd7MUeTKLDE7RlfYGBQ00bw==}
engines: {node: '>=18'}
hasBin: true
@@ -4074,8 +4078,8 @@ packages:
engines: {node: '>=18'}
hasBin: true
esbuild@0.25.10:
resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==}
esbuild@0.25.11:
resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==}
engines: {node: '>=18'}
hasBin: true
@@ -4886,8 +4890,8 @@ packages:
jsdom:
optional: true
jest-preset-angular@15.0.2:
resolution: {integrity: sha512-XQNKL2BQllWF+eeiLuooMnL23IJ2s7yg7YfX52KhUMmwCTD0HlrU5RLzAgbP7xrsplsGWFlQwCe2kTJss/q0UQ==}
jest-preset-angular@15.0.3:
resolution: {integrity: sha512-W1OpeQ/tq72ZSGXRvUDktL02QBAWIM5SZPK4KI0zoXNvRGiGhNMbxuaFF7anLi27cojmWHm+TewJQI1Dsm9s6A==}
engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0}
peerDependencies:
'@angular/compiler-cli': '>=18.0.0 <21.0.0'
@@ -6072,6 +6076,11 @@ packages:
engines: {node: '>=10'}
hasBin: true
semver@7.7.3:
resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
engines: {node: '>=10'}
hasBin: true
send@0.19.0:
resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==}
engines: {node: '>= 0.8.0'}
@@ -6443,8 +6452,8 @@ packages:
peerDependencies:
typescript: '>=4.8.4'
ts-jest@29.4.4:
resolution: {integrity: sha512-ccVcRABct5ZELCT5U0+DZwkXMCcOCLi2doHRrKy1nK/s7J7bch6TzJMsrY09WxgUUIP/ITfmcDS8D2yl63rnXw==}
ts-jest@29.4.5:
resolution: {integrity: sha512-HO3GyiWn2qvTQA4kTgjDcXiMwYQt68a1Y8+JuLRVpdIzm+UOLSHgl/XqR4c6nzJkq5rOkjc02O2I7P7l/Yof0Q==}
engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0}
hasBin: true
peerDependencies:
@@ -8485,7 +8494,7 @@ snapshots:
tslib: 2.8.1
optional: true
'@esbuild/aix-ppc64@0.25.10':
'@esbuild/aix-ppc64@0.25.11':
optional: true
'@esbuild/aix-ppc64@0.25.5':
@@ -8494,7 +8503,7 @@ snapshots:
'@esbuild/aix-ppc64@0.25.9':
optional: true
'@esbuild/android-arm64@0.25.10':
'@esbuild/android-arm64@0.25.11':
optional: true
'@esbuild/android-arm64@0.25.5':
@@ -8503,7 +8512,7 @@ snapshots:
'@esbuild/android-arm64@0.25.9':
optional: true
'@esbuild/android-arm@0.25.10':
'@esbuild/android-arm@0.25.11':
optional: true
'@esbuild/android-arm@0.25.5':
@@ -8512,7 +8521,7 @@ snapshots:
'@esbuild/android-arm@0.25.9':
optional: true
'@esbuild/android-x64@0.25.10':
'@esbuild/android-x64@0.25.11':
optional: true
'@esbuild/android-x64@0.25.5':
@@ -8521,7 +8530,7 @@ snapshots:
'@esbuild/android-x64@0.25.9':
optional: true
'@esbuild/darwin-arm64@0.25.10':
'@esbuild/darwin-arm64@0.25.11':
optional: true
'@esbuild/darwin-arm64@0.25.5':
@@ -8530,7 +8539,7 @@ snapshots:
'@esbuild/darwin-arm64@0.25.9':
optional: true
'@esbuild/darwin-x64@0.25.10':
'@esbuild/darwin-x64@0.25.11':
optional: true
'@esbuild/darwin-x64@0.25.5':
@@ -8539,7 +8548,7 @@ snapshots:
'@esbuild/darwin-x64@0.25.9':
optional: true
'@esbuild/freebsd-arm64@0.25.10':
'@esbuild/freebsd-arm64@0.25.11':
optional: true
'@esbuild/freebsd-arm64@0.25.5':
@@ -8548,7 +8557,7 @@ snapshots:
'@esbuild/freebsd-arm64@0.25.9':
optional: true
'@esbuild/freebsd-x64@0.25.10':
'@esbuild/freebsd-x64@0.25.11':
optional: true
'@esbuild/freebsd-x64@0.25.5':
@@ -8557,7 +8566,7 @@ snapshots:
'@esbuild/freebsd-x64@0.25.9':
optional: true
'@esbuild/linux-arm64@0.25.10':
'@esbuild/linux-arm64@0.25.11':
optional: true
'@esbuild/linux-arm64@0.25.5':
@@ -8566,7 +8575,7 @@ snapshots:
'@esbuild/linux-arm64@0.25.9':
optional: true
'@esbuild/linux-arm@0.25.10':
'@esbuild/linux-arm@0.25.11':
optional: true
'@esbuild/linux-arm@0.25.5':
@@ -8575,7 +8584,7 @@ snapshots:
'@esbuild/linux-arm@0.25.9':
optional: true
'@esbuild/linux-ia32@0.25.10':
'@esbuild/linux-ia32@0.25.11':
optional: true
'@esbuild/linux-ia32@0.25.5':
@@ -8584,7 +8593,7 @@ snapshots:
'@esbuild/linux-ia32@0.25.9':
optional: true
'@esbuild/linux-loong64@0.25.10':
'@esbuild/linux-loong64@0.25.11':
optional: true
'@esbuild/linux-loong64@0.25.5':
@@ -8593,7 +8602,7 @@ snapshots:
'@esbuild/linux-loong64@0.25.9':
optional: true
'@esbuild/linux-mips64el@0.25.10':
'@esbuild/linux-mips64el@0.25.11':
optional: true
'@esbuild/linux-mips64el@0.25.5':
@@ -8602,7 +8611,7 @@ snapshots:
'@esbuild/linux-mips64el@0.25.9':
optional: true
'@esbuild/linux-ppc64@0.25.10':
'@esbuild/linux-ppc64@0.25.11':
optional: true
'@esbuild/linux-ppc64@0.25.5':
@@ -8611,7 +8620,7 @@ snapshots:
'@esbuild/linux-ppc64@0.25.9':
optional: true
'@esbuild/linux-riscv64@0.25.10':
'@esbuild/linux-riscv64@0.25.11':
optional: true
'@esbuild/linux-riscv64@0.25.5':
@@ -8620,7 +8629,7 @@ snapshots:
'@esbuild/linux-riscv64@0.25.9':
optional: true
'@esbuild/linux-s390x@0.25.10':
'@esbuild/linux-s390x@0.25.11':
optional: true
'@esbuild/linux-s390x@0.25.5':
@@ -8629,7 +8638,7 @@ snapshots:
'@esbuild/linux-s390x@0.25.9':
optional: true
'@esbuild/linux-x64@0.25.10':
'@esbuild/linux-x64@0.25.11':
optional: true
'@esbuild/linux-x64@0.25.5':
@@ -8638,7 +8647,7 @@ snapshots:
'@esbuild/linux-x64@0.25.9':
optional: true
'@esbuild/netbsd-arm64@0.25.10':
'@esbuild/netbsd-arm64@0.25.11':
optional: true
'@esbuild/netbsd-arm64@0.25.5':
@@ -8647,7 +8656,7 @@ snapshots:
'@esbuild/netbsd-arm64@0.25.9':
optional: true
'@esbuild/netbsd-x64@0.25.10':
'@esbuild/netbsd-x64@0.25.11':
optional: true
'@esbuild/netbsd-x64@0.25.5':
@@ -8656,7 +8665,7 @@ snapshots:
'@esbuild/netbsd-x64@0.25.9':
optional: true
'@esbuild/openbsd-arm64@0.25.10':
'@esbuild/openbsd-arm64@0.25.11':
optional: true
'@esbuild/openbsd-arm64@0.25.5':
@@ -8665,7 +8674,7 @@ snapshots:
'@esbuild/openbsd-arm64@0.25.9':
optional: true
'@esbuild/openbsd-x64@0.25.10':
'@esbuild/openbsd-x64@0.25.11':
optional: true
'@esbuild/openbsd-x64@0.25.5':
@@ -8674,13 +8683,13 @@ snapshots:
'@esbuild/openbsd-x64@0.25.9':
optional: true
'@esbuild/openharmony-arm64@0.25.10':
'@esbuild/openharmony-arm64@0.25.11':
optional: true
'@esbuild/openharmony-arm64@0.25.9':
optional: true
'@esbuild/sunos-x64@0.25.10':
'@esbuild/sunos-x64@0.25.11':
optional: true
'@esbuild/sunos-x64@0.25.5':
@@ -8689,7 +8698,7 @@ snapshots:
'@esbuild/sunos-x64@0.25.9':
optional: true
'@esbuild/win32-arm64@0.25.10':
'@esbuild/win32-arm64@0.25.11':
optional: true
'@esbuild/win32-arm64@0.25.5':
@@ -8698,7 +8707,7 @@ snapshots:
'@esbuild/win32-arm64@0.25.9':
optional: true
'@esbuild/win32-ia32@0.25.10':
'@esbuild/win32-ia32@0.25.11':
optional: true
'@esbuild/win32-ia32@0.25.5':
@@ -8707,7 +8716,7 @@ snapshots:
'@esbuild/win32-ia32@0.25.9':
optional: true
'@esbuild/win32-x64@0.25.10':
'@esbuild/win32-x64@0.25.11':
optional: true
'@esbuild/win32-x64@0.25.5':
@@ -10732,6 +10741,8 @@ snapshots:
ci-info@4.3.0: {}
ci-info@4.3.1: {}
cjs-module-lexer@2.1.0: {}
cli-cursor@5.0.0:
@@ -11083,38 +11094,38 @@ snapshots:
dependencies:
es-errors: 1.3.0
esbuild-wasm@0.25.10: {}
esbuild-wasm@0.25.11: {}
esbuild-wasm@0.25.5: {}
esbuild@0.25.10:
esbuild@0.25.11:
optionalDependencies:
'@esbuild/aix-ppc64': 0.25.10
'@esbuild/android-arm': 0.25.10
'@esbuild/android-arm64': 0.25.10
'@esbuild/android-x64': 0.25.10
'@esbuild/darwin-arm64': 0.25.10
'@esbuild/darwin-x64': 0.25.10
'@esbuild/freebsd-arm64': 0.25.10
'@esbuild/freebsd-x64': 0.25.10
'@esbuild/linux-arm': 0.25.10
'@esbuild/linux-arm64': 0.25.10
'@esbuild/linux-ia32': 0.25.10
'@esbuild/linux-loong64': 0.25.10
'@esbuild/linux-mips64el': 0.25.10
'@esbuild/linux-ppc64': 0.25.10
'@esbuild/linux-riscv64': 0.25.10
'@esbuild/linux-s390x': 0.25.10
'@esbuild/linux-x64': 0.25.10
'@esbuild/netbsd-arm64': 0.25.10
'@esbuild/netbsd-x64': 0.25.10
'@esbuild/openbsd-arm64': 0.25.10
'@esbuild/openbsd-x64': 0.25.10
'@esbuild/openharmony-arm64': 0.25.10
'@esbuild/sunos-x64': 0.25.10
'@esbuild/win32-arm64': 0.25.10
'@esbuild/win32-ia32': 0.25.10
'@esbuild/win32-x64': 0.25.10
'@esbuild/aix-ppc64': 0.25.11
'@esbuild/android-arm': 0.25.11
'@esbuild/android-arm64': 0.25.11
'@esbuild/android-x64': 0.25.11
'@esbuild/darwin-arm64': 0.25.11
'@esbuild/darwin-x64': 0.25.11
'@esbuild/freebsd-arm64': 0.25.11
'@esbuild/freebsd-x64': 0.25.11
'@esbuild/linux-arm': 0.25.11
'@esbuild/linux-arm64': 0.25.11
'@esbuild/linux-ia32': 0.25.11
'@esbuild/linux-loong64': 0.25.11
'@esbuild/linux-mips64el': 0.25.11
'@esbuild/linux-ppc64': 0.25.11
'@esbuild/linux-riscv64': 0.25.11
'@esbuild/linux-s390x': 0.25.11
'@esbuild/linux-x64': 0.25.11
'@esbuild/netbsd-arm64': 0.25.11
'@esbuild/netbsd-x64': 0.25.11
'@esbuild/openbsd-arm64': 0.25.11
'@esbuild/openbsd-x64': 0.25.11
'@esbuild/openharmony-arm64': 0.25.11
'@esbuild/sunos-x64': 0.25.11
'@esbuild/win32-arm64': 0.25.11
'@esbuild/win32-ia32': 0.25.11
'@esbuild/win32-x64': 0.25.11
optional: true
esbuild@0.25.5:
@@ -12164,15 +12175,15 @@ snapshots:
'@angular/core': 20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)
'@angular/platform-browser-dynamic': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))
bs-logger: 0.2.6
esbuild-wasm: 0.25.10
esbuild-wasm: 0.25.11
jest: 30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3))
jest-environment-jsdom: 29.7.0(canvas@3.0.0)
jest-util: 29.7.0
pretty-format: 29.7.0
ts-jest: 29.4.4(@babel/core@7.28.4)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.4))(esbuild@0.25.10)(jest-util@29.7.0)(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(typescript@5.8.3)
ts-jest: 29.4.5(@babel/core@7.28.4)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.4))(esbuild@0.25.11)(jest-util@29.7.0)(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(typescript@5.8.3)
typescript: 5.8.3
optionalDependencies:
esbuild: 0.25.10
esbuild: 0.25.11
jsdom: 26.1.0(canvas@3.0.0)
transitivePeerDependencies:
- '@babel/core'
@@ -12184,7 +12195,7 @@ snapshots:
- supports-color
- utf-8-validate
jest-preset-angular@15.0.2(ccefccc315e3e4bd30d78eb49c90d46a):
jest-preset-angular@15.0.3(ccefccc315e3e4bd30d78eb49c90d46a):
dependencies:
'@angular/compiler-cli': 20.3.2(@angular/compiler@20.3.2)(typescript@5.8.3)
'@angular/core': 20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)
@@ -12192,15 +12203,15 @@ snapshots:
'@angular/platform-browser-dynamic': 20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.2)(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.2(@angular/common@20.3.2(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.2(@angular/compiler@20.3.2)(rxjs@7.8.2)(zone.js@0.15.1)))
'@jest/environment-jsdom-abstract': 30.2.0(canvas@3.0.0)(jsdom@26.1.0(canvas@3.0.0))
bs-logger: 0.2.6
esbuild-wasm: 0.25.10
esbuild-wasm: 0.25.11
jest: 30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3))
jest-util: 30.2.0
jsdom: 26.1.0(canvas@3.0.0)
pretty-format: 30.2.0
ts-jest: 29.4.4(@babel/core@7.28.4)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.4))(esbuild@0.25.10)(jest-util@30.2.0)(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(typescript@5.8.3)
ts-jest: 29.4.5(@babel/core@7.28.4)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.4))(esbuild@0.25.11)(jest-util@30.2.0)(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(typescript@5.8.3)
typescript: 5.8.3
optionalDependencies:
esbuild: 0.25.10
esbuild: 0.25.11
transitivePeerDependencies:
- '@babel/core'
- '@jest/transform'
@@ -12303,7 +12314,7 @@ snapshots:
jest-message-util: 30.2.0
jest-util: 30.2.0
pretty-format: 30.2.0
semver: 7.7.2
semver: 7.7.3
synckit: 0.11.11
transitivePeerDependencies:
- supports-color
@@ -12322,7 +12333,7 @@ snapshots:
'@jest/types': 30.0.5
'@types/node': 24.6.1
chalk: 4.1.2
ci-info: 4.3.0
ci-info: 4.3.1
graceful-fs: 4.2.11
picomatch: 4.0.3
@@ -12331,7 +12342,7 @@ snapshots:
'@jest/types': 30.2.0
'@types/node': 24.6.1
chalk: 4.1.2
ci-info: 4.3.0
ci-info: 4.3.1
graceful-fs: 4.2.11
picomatch: 4.0.3
@@ -12649,7 +12660,7 @@ snapshots:
make-dir@4.0.0:
dependencies:
semver: 7.7.2
semver: 7.7.3
make-error@1.3.6: {}
@@ -12904,7 +12915,7 @@ snapshots:
node-abi@3.71.0:
dependencies:
semver: 7.7.2
semver: 7.7.3
optional: true
node-addon-api@6.1.0:
@@ -13625,6 +13636,8 @@ snapshots:
semver@7.7.2: {}
semver@7.7.3: {}
send@0.19.0:
dependencies:
debug: 2.6.9
@@ -14085,7 +14098,7 @@ snapshots:
dependencies:
typescript: 5.8.3
ts-jest@29.4.4(@babel/core@7.28.4)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.4))(esbuild@0.25.10)(jest-util@29.7.0)(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(typescript@5.8.3):
ts-jest@29.4.5(@babel/core@7.28.4)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.4))(esbuild@0.25.11)(jest-util@29.7.0)(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(typescript@5.8.3):
dependencies:
bs-logger: 0.2.6
fast-json-stable-stringify: 2.1.0
@@ -14094,7 +14107,7 @@ snapshots:
json5: 2.2.3
lodash.memoize: 4.1.2
make-error: 1.3.6
semver: 7.7.2
semver: 7.7.3
type-fest: 4.41.0
typescript: 5.8.3
yargs-parser: 21.1.1
@@ -14103,10 +14116,10 @@ snapshots:
'@jest/transform': 30.2.0
'@jest/types': 30.2.0
babel-jest: 30.2.0(@babel/core@7.28.4)
esbuild: 0.25.10
esbuild: 0.25.11
jest-util: 29.7.0
ts-jest@29.4.4(@babel/core@7.28.4)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.4))(esbuild@0.25.10)(jest-util@30.2.0)(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(typescript@5.8.3):
ts-jest@29.4.5(@babel/core@7.28.4)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.4))(esbuild@0.25.11)(jest-util@30.2.0)(jest@30.2.0(@types/node@24.6.1)(ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3)))(typescript@5.8.3):
dependencies:
bs-logger: 0.2.6
fast-json-stable-stringify: 2.1.0
@@ -14115,7 +14128,7 @@ snapshots:
json5: 2.2.3
lodash.memoize: 4.1.2
make-error: 1.3.6
semver: 7.7.2
semver: 7.7.3
type-fest: 4.41.0
typescript: 5.8.3
yargs-parser: 21.1.1
@@ -14124,7 +14137,7 @@ snapshots:
'@jest/transform': 30.2.0
'@jest/types': 30.2.0
babel-jest: 30.2.0(@babel/core@7.28.4)
esbuild: 0.25.10
esbuild: 0.25.11
jest-util: 30.2.0
ts-node@10.9.2(@types/node@24.6.1)(typescript@5.8.3):

View File

@@ -3,9 +3,23 @@
i18n-title
info="Review the log files for the application and for email checking."
i18n-info>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" [(ngModel)]="autoRefreshEnabled">
<label class="form-check-label" for="autoRefreshSwitch" i18n>Auto refresh</label>
<div class="input-group input-group-sm align-items-center">
<div class="input-group input-group-sm me-3">
<span class="input-group-text text-muted" i18n>Show</span>
<input
class="form-control"
type="number"
min="100"
step="100"
[(ngModel)]="limit"
(ngModelChange)="onLimitChange($event)"
style="width: 100px;">
<span class="input-group-text text-muted" i18n>lines</span>
</div>
<div class="form-check form-switch mt-1">
<input class="form-check-input" type="checkbox" role="switch" [(ngModel)]="autoRefreshEnabled">
<label class="form-check-label" for="autoRefreshSwitch" i18n>Auto refresh</label>
</div>
</div>
</pngx-page-header>
@@ -29,14 +43,19 @@
<div [ngbNavOutlet]="nav" class="mt-2"></div>
<div class="bg-dark p-3 text-light font-monospace log-container" #logContainer>
<cdk-virtual-scroll-viewport
itemSize="20"
class="bg-dark p-3 text-light font-monospace log-container"
#logContainer>
@if (loading && logFiles.length) {
<div>
<div class="spinner-border spinner-border-sm me-2" role="status"></div>
<ng-container i18n>Loading...</ng-container>
</div>
}
@for (log of logs; track $index) {
<p class="m-0 p-0 log-entry-{{getLogLevel(log)}}">{{log}}</p>
}
</div>
<p *cdkVirtualFor="let log of logs"
class="m-0 p-0"
[ngClass]="'log-entry-' + log.level">
{{log.message}}
</p>
</cdk-virtual-scroll-viewport>

View File

@@ -18,7 +18,7 @@
.log-container {
overflow-y: scroll;
height: calc(100vh - 200px);
top: 70px;
top: 0;
p {
white-space: pre-wrap;

View File

@@ -1,3 +1,8 @@
import {
CdkVirtualScrollViewport,
ScrollingModule,
} from '@angular/cdk/scrolling'
import { CommonModule } from '@angular/common'
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
import { provideHttpClientTesting } from '@angular/common/http/testing'
import { ComponentFixture, TestBed } from '@angular/core/testing'
@@ -38,6 +43,9 @@ describe('LogsComponent', () => {
NgxBootstrapIconsModule.pick(allIcons),
LogsComponent,
PageHeaderComponent,
CommonModule,
CdkVirtualScrollViewport,
ScrollingModule,
],
providers: [
provideHttpClient(withInterceptorsFromDi()),
@@ -54,13 +62,12 @@ describe('LogsComponent', () => {
fixture = TestBed.createComponent(LogsComponent)
component = fixture.componentInstance
reloadSpy = jest.spyOn(component, 'reloadLogs')
window.HTMLElement.prototype.scroll = function () {} // mock scroll
jest.useFakeTimers()
fixture.detectChanges()
})
it('should display logs with first log initially', () => {
expect(logSpy).toHaveBeenCalledWith('paperless')
expect(logSpy).toHaveBeenCalledWith('paperless', 5000)
fixture.detectChanges()
expect(fixture.debugElement.nativeElement.textContent).toContain(
paperless_logs[0]
@@ -71,7 +78,7 @@ describe('LogsComponent', () => {
fixture.debugElement
.queryAll(By.directive(NgbNavLink))[1]
.nativeElement.dispatchEvent(new MouseEvent('click'))
expect(logSpy).toHaveBeenCalledWith('mail')
expect(logSpy).toHaveBeenCalledWith('mail', 5000)
})
it('should handle error with no logs', () => {
@@ -83,6 +90,10 @@ describe('LogsComponent', () => {
})
it('should auto refresh, allow toggle', () => {
jest
.spyOn(CdkVirtualScrollViewport.prototype, 'scrollToIndex')
.mockImplementation(() => undefined)
jest.advanceTimersByTime(6000)
expect(reloadSpy).toHaveBeenCalledTimes(2)
@@ -90,4 +101,13 @@ describe('LogsComponent', () => {
jest.advanceTimersByTime(6000)
expect(reloadSpy).toHaveBeenCalledTimes(2)
})
it('should debounce limit changes before reloading logs', () => {
const initialCalls = reloadSpy.mock.calls.length
component.onLimitChange(6000)
jest.advanceTimersByTime(299)
expect(reloadSpy).toHaveBeenCalledTimes(initialCalls)
jest.advanceTimersByTime(1)
expect(reloadSpy).toHaveBeenCalledTimes(initialCalls + 1)
})
})

View File

@@ -1,7 +1,11 @@
import {
CdkVirtualScrollViewport,
ScrollingModule,
} from '@angular/cdk/scrolling'
import { CommonModule } from '@angular/common'
import {
ChangeDetectorRef,
Component,
ElementRef,
OnDestroy,
OnInit,
ViewChild,
@@ -9,7 +13,7 @@ import {
} from '@angular/core'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap'
import { filter, takeUntil, timer } from 'rxjs'
import { Subject, debounceTime, filter, takeUntil, timer } from 'rxjs'
import { LogService } from 'src/app/services/rest/log.service'
import { PageHeaderComponent } from '../../common/page-header/page-header.component'
import { LoadingComponentWithPermissions } from '../../loading-component/loading.component'
@@ -21,8 +25,11 @@ import { LoadingComponentWithPermissions } from '../../loading-component/loading
imports: [
PageHeaderComponent,
NgbNavModule,
CommonModule,
FormsModule,
ReactiveFormsModule,
CdkVirtualScrollViewport,
ScrollingModule,
],
})
export class LogsComponent
@@ -32,7 +39,7 @@ export class LogsComponent
private logService = inject(LogService)
private changedetectorRef = inject(ChangeDetectorRef)
public logs: string[] = []
public logs: Array<{ message: string; level: number }> = []
public logFiles: string[] = []
@@ -40,9 +47,17 @@ export class LogsComponent
public autoRefreshEnabled: boolean = true
@ViewChild('logContainer') logContainer: ElementRef
public limit: number = 5000
private readonly limitChange$ = new Subject<number>()
@ViewChild('logContainer') logContainer: CdkVirtualScrollViewport
ngOnInit(): void {
this.limitChange$
.pipe(debounceTime(300), takeUntil(this.unsubscribeNotifier))
.subscribe(() => this.reloadLogs())
this.logService
.list()
.pipe(takeUntil(this.unsubscribeNotifier))
@@ -68,16 +83,33 @@ export class LogsComponent
super.ngOnDestroy()
}
onLimitChange(limit: number): void {
this.limitChange$.next(limit)
}
reloadLogs() {
this.loading = true
this.logService
.get(this.activeLog)
.get(this.activeLog, this.limit)
.pipe(takeUntil(this.unsubscribeNotifier))
.subscribe({
next: (result) => {
this.logs = result
this.loading = false
this.scrollToBottom()
const parsed = this.parseLogsWithLevel(result)
const hasChanges =
parsed.length !== this.logs.length ||
parsed.some((log, idx) => {
const current = this.logs[idx]
return (
!current ||
current.message !== log.message ||
current.level !== log.level
)
})
if (hasChanges) {
this.logs = parsed
this.scrollToBottom()
}
},
error: () => {
this.logs = []
@@ -100,12 +132,19 @@ export class LogsComponent
}
}
private parseLogsWithLevel(
logs: string[]
): Array<{ message: string; level: number }> {
return logs.map((log) => ({
message: log,
level: this.getLogLevel(log),
}))
}
scrollToBottom(): void {
this.changedetectorRef.detectChanges()
this.logContainer?.nativeElement.scroll({
top: this.logContainer.nativeElement.scrollHeight,
left: 0,
behavior: 'auto',
})
if (this.logContainer) {
this.logContainer.scrollToIndex(this.logs.length - 1)
}
}
}

View File

@@ -1489,6 +1489,8 @@ describe('DocumentDetailComponent', () => {
mockContentWindow.onafterprint(new Event('afterprint'))
}
tick(500)
expect(removeChildSpy).toHaveBeenCalledWith(mockIframe)
expect(revokeObjectURLSpy).toHaveBeenCalledWith('blob:mock-url')
@@ -1512,65 +1514,97 @@ describe('DocumentDetailComponent', () => {
)
})
it('should show error toast if printing throws inside iframe', fakeAsync(() => {
initNormally()
const iframePrintErrorCases: Array<{
description: string
thrownError: Error
expectToast: boolean
}> = [
{
description: 'should show error toast if printing throws inside iframe',
thrownError: new Error('focus failed'),
expectToast: true,
},
{
description:
'should suppress toast if cross-origin afterprint error occurs',
thrownError: new DOMException(
'Accessing onafterprint triggered a cross-origin violation',
'SecurityError'
),
expectToast: false,
},
]
const appendChildSpy = jest
.spyOn(document.body, 'appendChild')
.mockImplementation((node: Node) => node)
const removeChildSpy = jest
.spyOn(document.body, 'removeChild')
.mockImplementation((node: Node) => node)
const createObjectURLSpy = jest
.spyOn(URL, 'createObjectURL')
.mockReturnValue('blob:mock-url')
const revokeObjectURLSpy = jest
.spyOn(URL, 'revokeObjectURL')
.mockImplementation(() => {})
iframePrintErrorCases.forEach(({ description, thrownError, expectToast }) => {
it(
description,
fakeAsync(() => {
initNormally()
const toastSpy = jest.spyOn(toastService, 'showError')
const appendChildSpy = jest
.spyOn(document.body, 'appendChild')
.mockImplementation((node: Node) => node)
const removeChildSpy = jest
.spyOn(document.body, 'removeChild')
.mockImplementation((node: Node) => node)
const createObjectURLSpy = jest
.spyOn(URL, 'createObjectURL')
.mockReturnValue('blob:mock-url')
const revokeObjectURLSpy = jest
.spyOn(URL, 'revokeObjectURL')
.mockImplementation(() => {})
const mockContentWindow = {
focus: jest.fn().mockImplementation(() => {
throw new Error('focus failed')
}),
print: jest.fn(),
onafterprint: null,
}
const toastSpy = jest.spyOn(toastService, 'showError')
const mockIframe: any = {
style: {},
src: '',
onload: null,
contentWindow: mockContentWindow,
}
const mockContentWindow = {
focus: jest.fn().mockImplementation(() => {
throw thrownError
}),
print: jest.fn(),
onafterprint: null,
}
const createElementSpy = jest
.spyOn(document, 'createElement')
.mockReturnValue(mockIframe as any)
const mockIframe: any = {
style: {},
src: '',
onload: null,
contentWindow: mockContentWindow,
}
const blob = new Blob(['test'], { type: 'application/pdf' })
component.printDocument()
const createElementSpy = jest
.spyOn(document, 'createElement')
.mockReturnValue(mockIframe as any)
const req = httpTestingController.expectOne(
`${environment.apiBaseUrl}documents/${doc.id}/download/`
const blob = new Blob(['test'], { type: 'application/pdf' })
component.printDocument()
const req = httpTestingController.expectOne(
`${environment.apiBaseUrl}documents/${doc.id}/download/`
)
req.flush(blob)
tick()
if (mockIframe.onload) {
mockIframe.onload(new Event('load'))
}
tick(200)
if (expectToast) {
expect(toastSpy).toHaveBeenCalled()
} else {
expect(toastSpy).not.toHaveBeenCalled()
}
expect(removeChildSpy).toHaveBeenCalledWith(mockIframe)
expect(revokeObjectURLSpy).toHaveBeenCalledWith('blob:mock-url')
createElementSpy.mockRestore()
appendChildSpy.mockRestore()
removeChildSpy.mockRestore()
createObjectURLSpy.mockRestore()
revokeObjectURLSpy.mockRestore()
})
)
req.flush(blob)
tick()
if (mockIframe.onload) {
mockIframe.onload(new Event('load'))
}
expect(toastSpy).toHaveBeenCalled()
expect(removeChildSpy).toHaveBeenCalledWith(mockIframe)
expect(revokeObjectURLSpy).toHaveBeenCalledWith('blob:mock-url')
createElementSpy.mockRestore()
appendChildSpy.mockRestore()
removeChildSpy.mockRestore()
createObjectURLSpy.mockRestore()
revokeObjectURLSpy.mockRestore()
}))
})
})

View File

@@ -21,7 +21,7 @@ import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms'
import { PDFDocumentProxy, PdfViewerModule } from 'ng2-pdf-viewer'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import { DeviceDetectorService } from 'ngx-device-detector'
import { BehaviorSubject, Observable, of, Subject } from 'rxjs'
import { BehaviorSubject, Observable, of, Subject, timer } from 'rxjs'
import {
catchError,
debounceTime,
@@ -1452,9 +1452,18 @@ export class DocumentDetailComponent
URL.revokeObjectURL(blobUrl)
}
} catch (err) {
this.toastService.showError($localize`Print failed.`, err)
document.body.removeChild(iframe)
URL.revokeObjectURL(blobUrl)
// FF throws cross-origin error on onafterprint
const isCrossOriginAfterPrintError =
err instanceof DOMException &&
err.message.includes('onafterprint')
if (!isCrossOriginAfterPrintError) {
this.toastService.showError($localize`Print failed.`, err)
}
timer(100).subscribe(() => {
// delay to avoid FF print failure
document.body.removeChild(iframe)
URL.revokeObjectURL(blobUrl)
})
}
}
},

View File

@@ -68,7 +68,7 @@
</td>
<td>
<ng-template #errorPopover>
<pre class="small text-light">
<pre class="small">
{{ mail.error }}
</pre>
</ng-template>

View File

@@ -1,5 +1,7 @@
::ng-deep .popover {
max-width: 350px;
max-height: 600px;
overflow: hidden;
pre {
white-space: pre-wrap;

View File

@@ -73,9 +73,14 @@ describe('TagListComponent', () => {
)
})
it('should filter out child tags if name filter is empty, otherwise show all', () => {
it('should omit matching children from top level when their parent is present', () => {
const tags = [
{ id: 1, name: 'Tag1', parent: null },
{
id: 1,
name: 'Tag1',
parent: null,
children: [{ id: 2, name: 'Tag2', parent: 1 }],
},
{ id: 2, name: 'Tag2', parent: 1 },
{ id: 3, name: 'Tag3', parent: null },
]
@@ -86,7 +91,13 @@ describe('TagListComponent', () => {
component['_nameFilter'] = 'Tag2' // Simulate non-empty name filter
const filteredWithName = component.filterData(tags as any)
expect(filteredWithName.length).toBe(3)
expect(filteredWithName.length).toBe(2)
expect(filteredWithName.find((t) => t.id === 2)).toBeUndefined()
expect(
filteredWithName
.find((t) => t.id === 1)
?.children?.some((c) => c.id === 2)
).toBe(true)
})
it('should request only parent tags when no name filter is applied', () => {

View File

@@ -69,9 +69,13 @@ export class TagListComponent extends ManagementListComponent<Tag> {
}
filterData(data: Tag[]) {
return this.nameFilter?.length
? [...data]
: data.filter((tag) => !tag.parent)
if (!this.nameFilter?.length) {
return data.filter((tag) => !tag.parent)
}
// When filtering by name, exclude children if their parent is also present
const availableIds = new Set(data.map((tag) => tag.id))
return data.filter((tag) => !tag.parent || !availableIds.has(tag.parent))
}
protected override getSelectableIDs(tags: Tag[]): number[] {

View File

@@ -49,4 +49,14 @@ describe('LogService', () => {
)
expect(req.request.method).toEqual('GET')
})
it('should pass limit param on logs get when provided', () => {
const id: string = 'mail'
const limit: number = 100
subscription = service.get(id, limit).subscribe()
const req = httpTestingController.expectOne(
`${environment.apiBaseUrl}${endpoint}/${id}/?limit=${limit}`
)
expect(req.request.method).toEqual('GET')
})
})

View File

@@ -1,4 +1,4 @@
import { HttpClient } from '@angular/common/http'
import { HttpClient, HttpParams } from '@angular/common/http'
import { Injectable, inject } from '@angular/core'
import { Observable } from 'rxjs'
import { environment } from 'src/environments/environment'
@@ -13,7 +13,13 @@ export class LogService {
return this.http.get<string[]>(`${environment.apiBaseUrl}logs/`)
}
get(id: string): Observable<string[]> {
return this.http.get<string[]>(`${environment.apiBaseUrl}logs/${id}/`)
get(id: string, limit?: number): Observable<string[]> {
let params = new HttpParams()
if (limit !== undefined) {
params = params.set('limit', limit.toString())
}
return this.http.get<string[]>(`${environment.apiBaseUrl}logs/${id}/`, {
params,
})
}
}

View File

@@ -2250,6 +2250,23 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertListEqual(response.data, ["test", "test2"])
def test_get_log_with_limit(self):
log_data = "test1\ntest2\ntest3\n"
with (Path(settings.LOGGING_DIR) / "paperless.log").open("w") as f:
f.write(log_data)
response = self.client.get("/api/logs/paperless/", {"limit": 2})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertListEqual(response.data, ["test2", "test3"])
def test_get_log_with_invalid_limit(self):
log_data = "test1\ntest2\n"
with (Path(settings.LOGGING_DIR) / "paperless.log").open("w") as f:
f.write(log_data)
response = self.client.get("/api/logs/paperless/", {"limit": "abc"})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
response = self.client.get("/api/logs/paperless/", {"limit": -5})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_invalid_regex_other_algorithm(self):
for endpoint in ["correspondents", "tags", "document_types"]:
response = self.client.post(

View File

@@ -1,3 +1,4 @@
from django.core.cache import cache
from pytest_httpx import HTTPXMock
from rest_framework import status
from rest_framework.test import APIClient
@@ -8,6 +9,9 @@ from paperless import version
class TestApiRemoteVersion:
ENDPOINT = "/api/remote_version/"
def setup_method(self):
cache.clear()
def test_remote_version_enabled_no_update_prefix(
self,
rest_api_client: APIClient,

View File

@@ -6,6 +6,7 @@ import re
import tempfile
import zipfile
from collections import defaultdict
from collections import deque
from datetime import datetime
from pathlib import Path
from time import mktime
@@ -50,6 +51,7 @@ from django.utils.timezone import make_aware
from django.utils.translation import get_language
from django.views import View
from django.views.decorators.cache import cache_control
from django.views.decorators.cache import cache_page
from django.views.decorators.http import condition
from django.views.decorators.http import last_modified
from django.views.generic import TemplateView
@@ -69,6 +71,7 @@ from rest_framework import parsers
from rest_framework import serializers
from rest_framework.decorators import action
from rest_framework.exceptions import NotFound
from rest_framework.exceptions import ValidationError
from rest_framework.filters import OrderingFilter
from rest_framework.filters import SearchFilter
from rest_framework.generics import GenericAPIView
@@ -1362,6 +1365,13 @@ class UnifiedSearchViewSet(DocumentViewSet):
type=OpenApiTypes.STR,
location=OpenApiParameter.PATH,
),
OpenApiParameter(
name="limit",
type=OpenApiTypes.INT,
location=OpenApiParameter.QUERY,
description="Return only the last N entries from the log file",
required=False,
),
],
responses={
(200, "application/json"): serializers.ListSerializer(
@@ -1393,8 +1403,22 @@ class LogViewSet(ViewSet):
if not log_file.is_file():
raise Http404
limit_param = request.query_params.get("limit")
if limit_param is not None:
try:
limit = int(limit_param)
except (TypeError, ValueError):
raise ValidationError({"limit": "Must be a positive integer"})
if limit < 1:
raise ValidationError({"limit": "Must be a positive integer"})
else:
limit = None
with log_file.open() as f:
lines = [line.rstrip() for line in f.readlines()]
if limit is None:
lines = [line.rstrip() for line in f.readlines()]
else:
lines = [line.rstrip() for line in deque(f, maxlen=limit)]
return Response(lines)
@@ -2402,6 +2426,7 @@ class UiSettingsView(GenericAPIView):
)
@method_decorator(cache_page(60 * 15), name="dispatch")
@extend_schema_view(
get=extend_schema(
description="Get the current version of the Paperless-NGX server",

View File

@@ -54,8 +54,8 @@ class TestCustomAccountAdapter(TestCase):
# False because request host is not in allowed hosts
self.assertFalse(adapter.is_safe_url(url))
@mock.patch("allauth.core.internal.ratelimit.consume", return_value=True)
def test_pre_authenticate(self, mock_consume):
@mock.patch("allauth.core.ratelimit._consume_rate", return_value=True)
def test_pre_authenticate(self, mock_consume_rate):
adapter = get_adapter()
request = HttpRequest()
request.get_host = mock.Mock(return_value="example.com")

View File

@@ -53,6 +53,15 @@ class TestUrlCanary:
Verify certain URLs are still available so testing is valid still
"""
# Wikimedia rejects requests without a browser-like User-Agent header and returns 403.
_WIKIMEDIA_HEADERS = {
"User-Agent": (
"Mozilla/5.0 (X11; Linux x86_64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/123.0.0.0 Safari/537.36"
),
}
def test_online_image_exception_on_not_available(self):
"""
GIVEN:
@@ -70,6 +79,7 @@ class TestUrlCanary:
with pytest.raises(httpx.HTTPStatusError) as exec_info:
resp = httpx.get(
"https://upload.wikimedia.org/wikipedia/en/f/f7/nonexistent.png",
headers=self._WIKIMEDIA_HEADERS,
)
resp.raise_for_status()
@@ -90,7 +100,10 @@ class TestUrlCanary:
"""
# Now check the URL used in samples/sample.html
resp = httpx.get("https://upload.wikimedia.org/wikipedia/en/f/f7/RickRoll.png")
resp = httpx.get(
"https://upload.wikimedia.org/wikipedia/en/f/f7/RickRoll.png",
headers=self._WIKIMEDIA_HEADERS,
)
resp.raise_for_status()

21
uv.lock generated
View File

@@ -689,13 +689,13 @@ wheels = [
[[package]]
name = "django-allauth"
version = "65.12.1"
version = "65.4.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "asgiref", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
{ name = "django", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/52/94/75d7f8c59e061d1b66a6d917b287817fe02d2671c9e6376a4ddfb3954989/django_allauth-65.12.1.tar.gz", hash = "sha256:662666ff2d5c71766f66b1629ac7345c30796813221184e13e11ed7460940c6a", size = 1967971, upload-time = "2025-10-16T16:39:58.342Z" }
sdist = { url = "https://files.pythonhosted.org/packages/b1/e7/b3232c27da9f43e3db72d16addd90891ee233fa058ddd0588bafcded2ea7/django_allauth-65.4.1.tar.gz", hash = "sha256:60b32aef7dbbcc213319aa4fd8f570e985266ea1162ae6ef7a26a24efca85c8c", size = 1558220, upload-time = "2025-02-07T09:35:18.359Z" }
[package.optional-dependencies]
mfa = [
@@ -703,9 +703,9 @@ mfa = [
{ name = "qrcode", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
]
socialaccount = [
{ name = "oauthlib", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
{ name = "pyjwt", extra = ["crypto"], marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
{ name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
{ name = "requests-oauthlib", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
]
[[package]]
@@ -2262,7 +2262,7 @@ requires-dist = [
{ name = "concurrent-log-handler", specifier = "~=0.9.25" },
{ name = "dateparser", specifier = "~=1.2" },
{ name = "django", specifier = "~=5.2.5" },
{ name = "django-allauth", extras = ["mfa", "socialaccount"], specifier = "~=65.12.1" },
{ name = "django-allauth", extras = ["mfa", "socialaccount"], specifier = "~=65.4.0" },
{ name = "django-auditlog", specifier = "~=3.2.1" },
{ name = "django-cachalot", specifier = "~=2.8.0" },
{ name = "django-celery-results", specifier = "~=2.6.0" },
@@ -3379,6 +3379,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" },
]
[[package]]
name = "requests-oauthlib"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "oauthlib", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
{ name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650, upload-time = "2024-03-22T20:32:29.939Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179, upload-time = "2024-03-22T20:32:28.055Z" },
]
[[package]]
name = "rich"
version = "14.1.0"