Compare commits
13 Commits
e7dba9a2f7
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f835e54a9 | |||
| 8699ae4f42 | |||
| 0b6f4b39eb | |||
| f7dc22e5e7 | |||
| 3e1057d951 | |||
| d1fcd23087 | |||
| cc7977131a | |||
| 9234d0af15 | |||
| 985c71989f | |||
| e5aba4cf69 | |||
| f9bf31c9a9 | |||
| 32bc65c60b | |||
| 2278473763 |
@@ -111,7 +111,8 @@ jobs:
|
||||
done
|
||||
test "$parser_install_succeeded" = true
|
||||
PIP_CACHE_DIR="$parser_pip_cache" python3 /tmp/pip.pyz install --upgrade --target .python-ci pytest==8.3.5 httpx2==2.12.0
|
||||
PYTHONPATH="$PWD/.python-ci" AI_SERVICE_SKIP_MODEL_LOAD=1 python3 -m pytest -q
|
||||
python3 --version
|
||||
PYTHONFAULTHANDLER=1 PYTHONPATH="$PWD/.python-ci" AI_SERVICE_SKIP_MODEL_LOAD=1 python3 -m pytest -q
|
||||
|
||||
- name: Restore backend
|
||||
# The runner/proxy has occasionally supplied checksum-invalid NuGet cache entries (NU3008).
|
||||
@@ -191,11 +192,13 @@ jobs:
|
||||
&& npm_cache="$(mktemp -d)" \
|
||||
&& npm ci --no-audit --no-fund --cache "$npm_cache" )
|
||||
|
||||
# Moderate React Router advisories remain documented and mitigated; high/critical findings
|
||||
# in either production or test/browser tooling block the build.
|
||||
# High/critical findings remain blocking except the owner's expiring dev-only
|
||||
# GHSA-vfj7-8cjw-p6xm exception; raw audit results are retained and the policy is tested.
|
||||
- name: Audit frontend dependencies
|
||||
working-directory: job-tracker-ui
|
||||
run: npm audit --audit-level=high
|
||||
run: |
|
||||
node --test scripts/audit-dependencies.test.mjs
|
||||
node scripts/audit-dependencies.mjs
|
||||
|
||||
- name: Lint frontend
|
||||
working-directory: job-tracker-ui
|
||||
@@ -310,9 +313,16 @@ jobs:
|
||||
# checksum-valid npm download (`true` became `tr<garbage>e`, or a stray `break` appeared).
|
||||
# Retry only that unmistakable source-corruption signature from a new isolated cache.
|
||||
# Ordinary Axe violations and application assertions remain terminal.
|
||||
if grep -q "AxeBuilder.analyze" "$smoke_log" \
|
||||
&& grep -Eq "page\.evaluate: (ReferenceError|SyntaxError)" "$smoke_log"; then
|
||||
echo "Axe runtime source appears corrupted — reinstalling the locked dependencies from a fresh cache and retrying the complete suite once..."
|
||||
if { grep -q "AxeBuilder.analyze" "$smoke_log" \
|
||||
&& grep -Eq "page\.evaluate: (ReferenceError|SyntaxError)" "$smoke_log"; } \
|
||||
|| { grep -Eq 'node_modules/playwright-core/lib/utilsBundle\.js:[0-9]+' "$smoke_log" \
|
||||
&& grep -Fq 'const { items }$= this;' "$smoke_log" \
|
||||
&& grep -Fq 'SyntaxError: Missing initializer in destructuring declaration' "$smoke_log"; }; then
|
||||
echo "Browser runtime source appears corrupted — reinstalling the locked dependencies from a fresh cache and retrying the complete suite once..."
|
||||
mkdir -p .artifacts/browser-runtime-attempt-1
|
||||
cp "$smoke_log" .artifacts/browser-runtime-attempt-1/smoke.log
|
||||
if [ -d test-results ]; then cp -a test-results .artifacts/browser-runtime-attempt-1/; fi
|
||||
if [ -f .artifacts/playwright-results.json ]; then cp .artifacts/playwright-results.json .artifacts/browser-runtime-attempt-1/; fi
|
||||
rm -rf node_modules
|
||||
npm_cache="$(mktemp -d)"
|
||||
npm ci --no-audit --no-fund --cache "$npm_cache"
|
||||
@@ -327,6 +337,17 @@ jobs:
|
||||
echo "Browser smoke process crashed with SIGSEGV — retrying the unchanged suite once..."
|
||||
npm run test:e2e
|
||||
|
||||
- name: Preserve browser diagnostics
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: browser-diagnostics
|
||||
path: |
|
||||
job-tracker-ui/.artifacts/playwright-results.json
|
||||
job-tracker-ui/.artifacts/browser-runtime-attempt-1/
|
||||
job-tracker-ui/test-results
|
||||
retention-days: 7
|
||||
|
||||
deploy:
|
||||
needs: test
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
|
||||
@@ -242,6 +242,7 @@ services:
|
||||
- "11434"
|
||||
environment:
|
||||
- OLLAMA_HOST=0.0.0.0:11434
|
||||
- GOMAXPROCS=1
|
||||
volumes:
|
||||
- ollama_data:/root/.ollama
|
||||
# On ai_internal so the bundled Ollama stays reachable at http://ollama:11434 now that
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# 17 October 2026 release repair
|
||||
|
||||
The owner authorized promotion of completed `e7dba9a` to main despite existing production NO-GO qualifications, then requested deployment first, `qwen3.5:9b-q4_K_M` on Ollama second, and continued Phase 7 work afterward. Unfinished Settings/import/company changes remain outside this release repair.
|
||||
|
||||
## Deployment findings
|
||||
|
||||
Gitea run 820 initially failed during parser dependency installation: one attempt segfaulted and subsequent downloads failed locked hashes. An unchanged-release retry failed during Git checkout with a corrupt pack. A later attempt passed these stages and the backend checks, then failed the frontend audit with 19 moderate, 9 high and 1 critical findings.
|
||||
|
||||
The NumPy lock hash was already correct. Independent Windows verification and three fresh downloads on the supplied runner host matched the published SHA-256 and passed ZIP CRC checks. The host had rebooted between the earlier failures and these checks; the underlying integrity incident is not diagnosed or declared repaired. Hash enforcement and all ordinary test gates remain enabled.
|
||||
|
||||
Server source edits were preserved privately at `/home/pi/.local/state/jobjakt/pre-release-source-20261017T185504Z`. The only content change was `GOMAXPROCS=1` for bundled Ollama, which this repair retains. The deploy script's executable bit is already restored by the deployment workflow. No database, volume or user data was reset.
|
||||
|
||||
## Bounded dependency changes
|
||||
|
||||
| Dependency | Locked before | Locked after |
|
||||
|---|---|---|
|
||||
| Next.js and matching ESLint/SWC packages | 16.3.4 | 16.3.8 |
|
||||
| Axios | 1.19.0 | 1.20.0 |
|
||||
| brace-expansion | 2.1.4 | 2.1.7 |
|
||||
| sharp and its matching platform payloads | 0.35.4 | 0.35.5 |
|
||||
| source-map-js | 1.2.1 | 1.2.2 |
|
||||
|
||||
React, MUI, routing, application data owners and PDF geometry are unchanged. The production-only audit reports zero vulnerabilities.
|
||||
|
||||
## Explicit temporary development exception
|
||||
|
||||
The owner separately approved **GHSA-vfj7-8cjw-p6xm** in dev-only `braces` through **31 October 2026 UTC**. [The upstream advisory](https://github.com/advisories/GHSA-vfj7-8cjw-p6xm) lists no patched version. Its five linked audit nodes are `braces`, `micromatch`, `fast-glob`, `@next/eslint-plugin-next` and `eslint-config-next`; this is one advisory, not five independent accepted vulnerabilities.
|
||||
|
||||
The audit checker accepts only that high-severity origin, the explicit existing chain, `braces` 3.0.3, and lockfile nodes marked development-only. Production copies, another advisory, critical severity, missing data/nodes, cyclic origins and expiry fail closed. Full audit JSON is saved and printed in CI. All other high/critical findings remain blocking; the existing moderate threshold is retained. The raw full audit still reports the accepted development findings; they are not described as fixed.
|
||||
|
||||
Nine ordinary policy tests cover the exception and its rejection boundaries. The previous strict policy was captured failing the approved exception, and a critical-origin edge case was separately reproduced and repaired. The exception must be removed when upstream supplies a fix or explicitly reviewed before expiry; it does not authorize a future blanket bypass.
|
||||
|
||||
## Verification and remaining work
|
||||
|
||||
Application TypeScript, lint, production build, backend Release compilation, production audit, policy tests and supply-chain/ingress tests pass. Settings persistence, authentication/account isolation and ordinary P2-D01 browser journeys pass (16 cases, zero skipped/flaky).
|
||||
|
||||
Fresh full frontend execution passed 824 tests across 95 suites, with zero skipped or pending cases (18:49-18:58 UTC). The nine separate audit-policy cases also pass. Supply-chain and ingress checks each passed three cases; whitespace and the tracked-secret scan passed. No existing test counts were reduced. Evidence is retained in the local release-20261017 artifact directory. CI and deployed runtime identity must be checked after promotion. Existing ingress, complete backup/restore, rollback, human accessibility/device and production-output qualifications remain open; deployment authorization does not convert them to verified GO. P5-13 stays withheld and no model qualification or Phase 8 work is enabled.
|
||||
@@ -0,0 +1,7 @@
|
||||
# Shared browser authentication fixture — 20 October 2026
|
||||
|
||||
The instrumented Linux suite returned HTTP 429 from fixture logins in Companies, correspondence and preservation journeys. The general browser fixture configured 100 authentication permits per five-minute window. Every test and retry shares the same loopback address and backend process; the server partitions that limiter by remote IP. Fast fixture failures and repeated identity setup can exhaust this shared allowance before the window renews. A rejected fixture login prevents the workflow from running; it is not an application assertion failure.
|
||||
|
||||
The general isolated browser fixture now allows 1,000 login permits per window. This is test-only environment configuration; the application default remains ten. The separate `playwright.rate-limits.config.ts` explicitly retains ten permits and zero retries for real middleware/rate-limit contracts. No production config, auth implementation, login assertion, permission requirement or dedicated throttle expectation is changed. The allowance supplies headroom for the current 331-case full suite, repeated identities and its existing one retry.
|
||||
|
||||
The dedicated real-HTTP throttle suite passed all nine cases at 07:34:38–07:34:50 UTC, zero skips/retries/flaky outcomes, with its unchanged ten-permit configuration. Browser TypeScript and lint pass. The subsequent full Linux run must prove the shared-fixture correction; focused workflow passes alone cannot prove cumulative capacity. The original 429 traces remain evidence. This correction does not establish deployment or Phase 7 acceptance.
|
||||
@@ -0,0 +1,9 @@
|
||||
# Browser CI diagnostic repair — 20 October 2026
|
||||
|
||||
Run 821, attempt 2, tested main commit `2278473763848254c12d65c1062109eb1b7dc4cf`. Its backend build, 1,054 backend tests, 824 frontend tests, dependency policy, lint, TypeScript and production frontend build completed before the browser step. The browser step began at 20:37:52 UTC on 17 October and ended unsuccessfully at 20:51:55 UTC. The downloadable log ends at “Running 331 tests using 1 worker”, without an assertion, timeout or process-exit diagnostic. Deployment was skipped. Subsequent successful scheduled backup jobs do not constitute a successful application deployment.
|
||||
|
||||
The existing GitHub-only Playwright reporter does not emit ordinary per-test progress. The repair adds the built-in list reporter alongside GitHub annotations and a JSON report. An always-run artifact step retains the JSON result and existing failure screenshots/traces for seven days using Gitea-compatible upload-artifact v3. No tests, assertions, timeouts, retries, browser projects, authorization checks or deployment conditions are weakened. This is a diagnostic repair; the underlying termination cause is still unproven and requires the instrumented run.
|
||||
|
||||
Verification: all 331 tests in 53 files are still discovered. A separate temporary probe deliberately failed one ordinary assertion: the process returned 1, printed the failing test name, wrote a trace and persisted one unexpected failure and zero skipped cases in JSON. The probe is an isolated reporter self-check, not an application regression result or committed test. Application/test TypeScript passed. Lint and final hygiene are recorded before publication; the next main CI run remains authoritative for application regression and deployment.
|
||||
|
||||
Unfinished Phase 7 work in the original checkout is excluded. The owner has explicitly authorized this main deployment despite existing release qualifications. The temporary dev-only braces exception retains its original expiry after 31 October 2026; other dependency findings, outstanding production gates and P5-13 withholding remain unchanged. The requested Qwen model is downloaded but is not activated by this change.
|
||||
@@ -0,0 +1,9 @@
|
||||
# Browser fixture portability and confirmation focus contracts
|
||||
|
||||
Run832 at main `d1fcd2308755dfc112a2bd6a736dd763bbc895f9` completed its331-case browser suite:314 passed,16 failed, one flaky Rules draft case. Deployment was skipped. The Playwright runtime-corruption signature did not recur in this completed run. These outcomes do not establish production readiness or resolve the existing intermittent Rules observation.
|
||||
|
||||
Eleven failures came from `seed-operation.py` calling Python3.12's `Path.is_junction` on the Debian runner's Python3.11.2 PosixPath. This was independently reproduced by running the unchanged helper in an isolated, network-disabled `node:20-bookworm` container on the runner host; host Python3.12.3 passed. Junction checks now execute only on Windows, where the existing Python3.12 guard is preserved. Absolute/direct-temp-root, symlink, hardlink, exact owner/schema and insert-only SQL protections remain unchanged. All29 self-checks pass both on local Windows and in that same Python3.11 container after the correction. No existing application data or production container is mounted into the test container.
|
||||
|
||||
Five failures were stale keyboard expectations after the verified shared confirmation scroll-body accessibility correction. The focusable named body now belongs in the native tab cycle. The tests explicitly verify Cancel → reverse Tab to named body → reverse Tab to Confirm → forward Tab to body → forward Tab to Cancel, followed by the original cancellation/focus/draft/action proofs. No focus assertion, axe rule, timeout or application behavior is relaxed. All five affected native cases pass with zero skips/flaky/retries,20October18:03:21–18:04:21UTC.
|
||||
|
||||
The bounded repair changes only the offline fixture and those two browser test consumers. Full release CI and deployed identity still require a new successful run. Production readiness remains NO-GO independently of the owner's authorization to publish main. The Qwen3.5 adapter preparation is separately tested; model activation and P5-13 qualification remain separate.
|
||||
@@ -0,0 +1,9 @@
|
||||
# Browser fixture portability — 20 October 2026
|
||||
|
||||
The instrumented Linux release run on `32bc65c` exposed `spawnSync rtk ENOENT` in the Career Profile import-review and Notifications fixtures. Operations used the same invocation. RTK is a developer command wrapper, not an application or CI dependency. The runner has `python3`; Windows uses `python`.
|
||||
|
||||
The three fixtures now invoke the configured `PLAYWRIGHT_PYTHON_PATH` directly, otherwise the platform's Python executable. The fixture arguments, isolated SQLite paths, owner checks, insert-only authorizer and assertions remain unchanged. No production data or application code is changed.
|
||||
|
||||
All 17 affected native browser cases passed locally without retries, skips or flaky outcomes at 07:19:59–07:22:20 UTC. Browser TypeScript and lint passed. This is focused Windows evidence; the subsequent full Linux release run must establish runner compatibility. The failing Linux traces remain preserved under the original diagnostic run. There is no change to browser inventory or the existing release gates.
|
||||
|
||||
The independent dialog accessibility and fixture-login rate-limit findings remain under investigation. This repair alone does not establish deployment success or Phase 7 acceptance.
|
||||
@@ -0,0 +1,13 @@
|
||||
# Confirmation transition must preserve deliberate focus
|
||||
|
||||
Release833 at main `f7dc22e5e7efe1fd1d64f6ace2c04cf2f95bf558` exposes a distinct confirmation failure after the earlier complete tab-cycle test correction: Cancel initially receives focus and reverse traversal succeeds, but the later opening-transition callback can reset focus while forward keyboard traversal is already under way. This is a user-visible shared-owner race, not permission to relax the tab sequence or increase its timeout.
|
||||
|
||||
A deterministic new component regression focuses the named scroll body before advancing the opening transition. Existing code fails with focus moved back to Cancel:2existing cases pass/1new case fails (`p712-confirm-transition-red-20261020T184351Z`). Removing the redundant `onEntered` forced focus and its button ref leaves the existing Cancel `autoFocus` and MUI focus trap/restoration in place. All3component cases then pass (`p712-confirm-transition-green-20261020T184416Z`). No confirmation resolution, destructive guard, data write or dialog body accessibility changes.
|
||||
|
||||
All5affected native journeys pass after this repair on20October18:44–18:46UTC, with zero skips/flaky/retries. They retain explicit Cancel → reverse Tab body → reverse Tab Confirm → forward Tab body → forward Tab Cancel, the original cancellation/focus/draft assertions, and Discovery create/delete persistence. App/browser TypeScript, lint and production build pass. Full integrated successor validation follows; Linux CI/deployed acceptance is not inferred from Windows.
|
||||
|
||||
The interrupted P7-12 whole-browser attempt also exposed two stale English-only `Technical` template selectors after the approved Bokmål gallery localisation. That separate development test consumer now uses the existing locale key without changing template entitlement or any assertion. Original failing evidence is retained and the fresh successor run must use the repaired committed source.
|
||||
|
||||
Release833 also records Chromium page crashes in the CV-controls and administrator route census. An18:39UTC runner observation reports no cgroup OOM events, no container memory limit,1GiB shared memory and no kernel errors in the queried interval. This observation neither explains the crashes nor proves hardware health. Keep this infrastructure/reliability qualification separate from the deterministically reproduced focus race.
|
||||
|
||||
Production readiness remains NO-GO independently of the owner's release authorization. No model activation or P5-13 qualification is part of this repair.
|
||||
@@ -0,0 +1,9 @@
|
||||
# Confirmation keyboard scrolling — 20 October 2026
|
||||
|
||||
The Linux compose journey found a serious `scrollable-region-focusable` violation in the shared confirmation dialog at a short landscape viewport. Its message body can overflow independently while containing no focusable content. The shared owner is `ConfirmProvider`, also used by attachment deletion, application workspace changes, CV template changes and Career evidence deletion.
|
||||
|
||||
The body now exposes a keyboard-focusable region named by the existing localized dialog title. Cancel retains initial focus; confirmation, dismissal, focus restoration and Promise resolution are unchanged. No new strings, dialog framework or saving semantics are introduced.
|
||||
|
||||
The strengthened existing unit case failed ordinarily before the implementation (one failure, one pass). Afterward all eight cases in the confirmation, confirmation-flow, dialog-flow and modal-state suites passed. The full ten-case native compose suite passed without retries/skips/flaky outcomes at 07:28:17–07:29:43 UTC against a fresh production export. This includes the eight-width bilingual theme/keyboard journey and its unchanged all-enabled axe assertion. Build passed. No test inventory change, accessibility waiver or production authorization change is made.
|
||||
|
||||
Human screen-reader and physical-device acceptance remain pending. This narrow correction does not accept the remaining Phase 7 migration or establish live deployment success.
|
||||
@@ -0,0 +1,9 @@
|
||||
# Notification caption fixture — 20 October 2026
|
||||
|
||||
The full Linux diagnostic found four ordinary shell failures, each repeated by the existing CI retry: the loose `getByText(t.notificationsUnread)` query matched both the visible localized Unread caption and the shell's unread-count live announcement. Both are intentional accessibility/UI content. The fixture intended to assert the exact caption.
|
||||
|
||||
The locator now uses `{ exact: true }`. It still requires the visible translated caption, retains the live status announcement and preserves every notification mark-read/dismiss assertion and axe scan. No product behavior changes. Browser inventory stays 331.
|
||||
|
||||
All 20 existing shell-readiness cases pass without skips/flaky/retries at 07:42:01–07:44:53 UTC, across English/Bokmål and light/dark, with browser TypeScript passing. Whitespace checks pass. Full Linux acceptance remains required for the new main candidate; this focused result is not deployment success.
|
||||
|
||||
Run 828 completed its 331-case browser lane with 274 passing and 57 failing cases. All failed cases are accounted for by the five investigated causes: 35 shared-login 429, 15 Python-wrapper absence, four caption locator collisions, two stale route expectations and one keyboard-scroll axe violation. Original retries and failure artifacts remain retained. Subsequent corrected-run results must determine whether these repairs are sufficient.
|
||||
@@ -0,0 +1,9 @@
|
||||
# Playwright runtime corruption signature — 20 October 2026
|
||||
|
||||
Corrected release run831 did not reach deployment. Its browser lane encountered Chromium target crashes and an installed Playwright parser failure at `node_modules/playwright-core/lib/utilsBundle.js:35841`: `const { items }$= this;`, followed by `SyntaxError: Missing initializer in destructuring declaration`. A worker then exited before executing its cases. Only46 browser attempts appear in the terminal log; this is not a completed331-case regression. An ordinary preview-visibility assertion also failed and is retained, rather than labelled an infrastructure success.
|
||||
|
||||
This resembles earlier runner dependency/download corruption, but the job container was removed before its installed bytes could be compared with the lockfile archive. Neither persistent filesystem corruption nor faulty hardware is proven. The full900-entry diagnostic ZIP was downloaded and CRC-checked: SHA256 `f6d8e9659ca824bb12d283913f6e16f3933974bbc5e11c78e3524d8712e75632`. Original logs and traces remain retained.
|
||||
|
||||
The workflow already allows one complete-suite retry after its narrowly recognized Axe source-corruption signature, using a new npm cache and the unchanged lockfile. Extend that same bounded policy only when all three observed Playwright indicators occur together: the exact dependency path/line marker, the exact malformed statement and the exact parser error. Ordinary application assertion failures, target crashes alone, user-source syntax errors and incomplete signatures remain terminal. The six policy tests execute the actual Bash guard and pass. No assertion, browser inventory, timeout, ordinary retry, dependency integrity or release gate is relaxed.
|
||||
|
||||
Before the existing reinstall/retry, preserve the first smoke log, JSON report if available and complete traces/screenshots under `.artifacts/browser-runtime-attempt-1`; include them in the same always-uploaded artifact. A subsequent successful retry must still complete the entire browser suite; failure remains blocking. This does not repair or qualify the underlying runner, prove the preview assertion resolved or establish deployment success. Production/operator, physical hardware/runtime and manual application qualifications remain open. P5-13 and model activation are unchanged.
|
||||
@@ -0,0 +1,9 @@
|
||||
# Qwen3.5 compatibility preparation
|
||||
|
||||
The owner requested `qwen3.5:9b-q4_K_M` after live deployment. The model is downloaded, but the active sidecar still uses the previous model. This change prepares the existing adapter; it does not activate a model or qualify the withheld P5-13 capability.
|
||||
|
||||
The existing adapter consumes Ollama's final `response`. A synthetic local probe found an empty response with the Qwen3.5 thinking default; a20October probe with `think:false` returned the requested synthetic JSON and no thinking text. The adapter now sets that request field only when the configured base family is exactly `qwen3.5`. Existing temperature, JSON mode, timeouts, response parsing, other model payloads and external-provider consent/fallback remain unchanged. Custom namespace aliases are outside this exact family match.
|
||||
|
||||
Seven new offline cases verify both text/JSON for the requested tag and bare family, and unchanged payloads for qwen2.5, qwen3 and a lookalike family. Four Qwen3.5 cases failed ordinarily before the two-line change; three compatibility cases passed. The full sidecar suite then passed **56/56**, zero failures/errors/skips,8.45seconds,20October13:32UTC. The prior49-case baseline grows by seven; no model calls are part of these tests. Raw RED/GREEN logs and JUnit are retained under `.artifacts/release-20261020/model-compat/`.
|
||||
|
||||
Live activation, deployed adapter identity, GPU residency and final-response smoke remain pending behind the release. P5-13's deployed-identity/366-call/independent-human-review/UI requirements remain unmet and its review feature stays disabled. Production readiness remains NO-GO independently of the owner's release authorization.
|
||||
@@ -0,0 +1,11 @@
|
||||
# Route-census compatibility repair — 20 October 2026
|
||||
|
||||
The diagnostic browser run on `32bc65c` reproduced two ordinary failures in the normal-user and administrator route census. Both expected the legacy Gmail-review redirect to discard unrelated query parameters and its fragment. P7-06 deliberately preserves that context: `App.tsx` redirects `/correspondence/review?route=all#review` to `/correspondence?route=all&view=review#review`, and the approved P7-06 native alias case separately verifies preserved query, fragment and review mode. The application is following its accepted contract; the older census expectation was stale.
|
||||
|
||||
After correcting the exact destination, the same cases exposed a second stale expectation for the former server-generated English error. P7-06 presents the existing translated `gmailReviewNotConnected` message. The repaired census still requires the real HTTP 409, exact `gmail_not_connected` response code and a visible alert containing that approved translation. No permission, error or redirect assertion is removed or made optional.
|
||||
|
||||
The census also includes P7-07's approved `/documents` route, bringing its explicit unique-route assertion from 32 to 33 while retaining every original route. Anonymous entry must remain protected; authenticated entry and refresh must return owned document metadata and the exact Documents heading. This strengthens coverage without adding an application route or feature.
|
||||
|
||||
The initial local whole-suite diagnostic was interrupted after 65 completed cases (63 passed, two failed) once the route failures were reproduced; it is not a full-suite acceptance result. Its logs and both failure traces remain retained. The first corrected census recorded one pass and two failures on the obsolete English alert. The final focused run passed all three ordinary identity cases, zero retries/skips, at 06:57:22–06:59:30 UTC: 198 route entry/refresh combinations plus existing history, ownership and navigation checks. Browser TypeScript passed. The overall browser inventory remains 331 tests in 53 files; the three existing identity cases now cover 33 routes each.
|
||||
|
||||
The source change is confined to the census fixture. Existing product code, CV pagination, persistence, authorization and P5-13 withholding are unchanged. Lint, whitespace and tracked-secret checks pass. The instrumented Linux run remains diagnostic evidence for the previous candidate until the updated main commit receives its own full CI result. Production/operator and manual accessibility qualifications remain open; this repair does not accept the remaining Phase 7 migration.
|
||||
@@ -0,0 +1,11 @@
|
||||
# Bounded follow-redirects security patch
|
||||
|
||||
The21October fresh npm audit identifies production-classified transitive `follow-redirects`1.16.0 with high-severity [array-header credential stripping](https://github.com/advisories/GHSA-q3jh-wrc6-jgq8) and [custom sensitive-header escaping](https://github.com/advisories/GHSA-8r9p-f939-6h3c) advisories. Both advisory records identify1.16.1as patched. This is a dependency finding, not evidence that Jobjakt was exploited or that its browser adapter exercises either vector.
|
||||
|
||||
`npm update follow-redirects --ignore-scripts --no-audit --no-fund` changes exactly one package. The only tracked dependency diff is that lock entry's version, tarball URL and integrity:1.16.0→1.16.1. Existing package ranges, Axios, frameworks, other dependencies, audit policy and the owner's temporary dev-only braces exception remain unchanged. No override or new dependency is introduced.
|
||||
|
||||
Fresh production audit reports0vulnerabilities. The full audit policy passes with only the previously approved dev-only braces-derived high chain accepted through31October; moderate development findings remain recorded. No additional exception is added. Two synthetic loopback contracts verify array-form Authorization/Cookie/Proxy-Authorization stripping and custom credential names containing`^`/`|`, while retaining a public header. Both pass against the installed1.16.1; no real credentials or external target is involved.
|
||||
|
||||
Raw pre/post audit records are `.artifacts/phase7/current/p712-final-security/` and `.artifacts/phase7/current/p712-follow-redirects/`. The bounded helper is `.artifacts/phase7/current/p712-redirect-contract.cjs`. Production build and application/browser TypeScript/lint are rerun; final whole-application/browser proof uses the patched dependency set. The active pre-patch browser candidate is interrupted and retained rather than presented as validating the new lock.
|
||||
|
||||
This patch does not resolve the independent runner's Python/.NET native crashes, qualify deployed identity, activate Qwen3.5or complete P5-13. Production readiness remains NO-GO independently of the owner's release authorization. No Phase8 implementation.
|
||||
@@ -0,0 +1,50 @@
|
||||
# Parser CI native exit investigation, 2026-10-21
|
||||
|
||||
## Verified evidence
|
||||
|
||||
CI run 834, API test job 1721, failed in `Test document parser boundary`.
|
||||
The fetched log is `.artifacts/phase7/p7-09/instrumented-release-ci/20261021T050601Z/failed-job-logs.log`;
|
||||
the adjacent `api-observation.json` records the GET of job 1721's logs.
|
||||
The runner's internal task ID 1689 is separate from the API job ID.
|
||||
|
||||
At 2026-10-20 20:36:38 UTC, pytest reported `56 passed, 1 warning in 5.36s`.
|
||||
The shell subsequently reported a segmentation fault and exit 139. Passing assertions
|
||||
did not constitute successful process completion. Later browser steps did not run;
|
||||
deployment remained blocked.
|
||||
|
||||
Read-only host inspection with
|
||||
`coredumpctl --no-pager info 3804681` confirmed:
|
||||
|
||||
- Signal: 11 (SIGSEGV).
|
||||
- Command: `python3 -m pytest -q`.
|
||||
- Executable: `/usr/bin/python3.11`.
|
||||
- Timestamp: 2026-10-20 22:36:38 CEST (20:36:38 UTC).
|
||||
- Docker scope: `040c0b99c92e0a5a48c00994e10a264884ca8172f5eac94d49e3d7aa1d92fd27`,
|
||||
matching the failed job's container.
|
||||
- Stored core: `/var/lib/systemd/coredump/core.python3.0.7aab2b376ce94d92bf796bbeb62063a4.3804681.1792528598000000.zst`.
|
||||
|
||||
The core was inaccessible to SSH user `pi`; `sudo -n` required a password.
|
||||
No native backtrace was obtained. Kernel journal error entries for
|
||||
20:30-20:40 UTC were empty; that does not establish host health.
|
||||
|
||||
## Interpretation and bounded diagnostic
|
||||
|
||||
The timing is consistent with interpreter shutdown or cleanup, but the root cause
|
||||
is unproven. `AI_SERVICE_SKIP_MODEL_LOAD=1` prevents model weight loading;
|
||||
`app.py` still imports Torch and Transformers. Native extension cleanup is therefore
|
||||
a candidate, not an identified cause. Previous download corruption and browser
|
||||
crashes do not prove the cause of this Python crash.
|
||||
|
||||
The workflow now prints the Python version and enables `PYTHONFAULTHANDLER=1`
|
||||
for the unchanged pytest command. It preserves the existing dependency installs,
|
||||
all tests, and process exit status, without adding retries or suppressing signals.
|
||||
The existing pip log already records installed distribution versions. In this run,
|
||||
the test-runtime install upgraded locked AnyIO 4.14.2 to 4.15.1 and IDNA 3.19 to
|
||||
3.20; this is a reproducibility observation, not a demonstrated cause, and no
|
||||
dependency change is included here.
|
||||
|
||||
Next evidence: inspect the retained core with authorized access, or capture the
|
||||
faulthandler output from a subsequent normal CI run before choosing a fix.
|
||||
The diagnostic change has only received workflow text and whitespace checks;
|
||||
it has not been exercised in CI. No production mutation, model qualification,
|
||||
or successful release is claimed.
|
||||
@@ -43,12 +43,13 @@ function routes(jobId: number, cvId: number): RouteCase[] {
|
||||
{ pattern: '/companies', path: '/companies', heading: t.companies, endpoint: '/companies' },
|
||||
{ pattern: '/offers', path: '/offers', heading: t.offerComparisonTitle, endpoint: '/jobapplications' },
|
||||
{ pattern: '/correspondence', path: '/correspondence', heading: t.correspondenceInboxTitle, endpoint: '/correspondence/page' },
|
||||
{ pattern: '/correspondence/review', path: '/correspondence/review?route=all#review', final: '/correspondence?view=review', heading: t.correspondenceInboxTitle, endpoint: '/gmail/review-candidates', status: 409 },
|
||||
{ pattern: '/correspondence/review', path: '/correspondence/review?route=all#review', final: '/correspondence?route=all&view=review#review', heading: t.correspondenceInboxTitle, endpoint: '/gmail/review-candidates', status: 409 },
|
||||
{ pattern: '/profile', path: '/profile', heading: t.profile, endpoint: '/auth/me' },
|
||||
{ pattern: '/applications/:id', path: `/applications/${jobId}?section=documents&followMode=waiting-update#workspace`, final: `/jobs/${jobId}?section=documents&followMode=waiting-update`, heading: t.jobDetails, endpoint: `/jobapplications/${jobId}/workspace` },
|
||||
{ pattern: '/career', path: '/career?section=profile', heading: t.careerWorkspace, endpoint: '/career/profile' },
|
||||
{ pattern: '/career/builder', path: '/career/builder', heading: t.cvBuilder, endpoint: '/cv/variants' },
|
||||
{ pattern: '/career/builder/:id', path: `/career/builder/${cvId}`, heading: t.cvBuilder, endpoint: `/cv/variants/${cvId}` },
|
||||
{ pattern: '/documents', path: '/documents', heading: t.documentsTitle, endpoint: '/documents' },
|
||||
{ pattern: '/admin/audit', path: '/admin/audit', heading: t.auditLog, endpoint: '/admin/audit' },
|
||||
{ pattern: '/admin/users', path: '/admin/users', heading: t.users, endpoint: '/users' },
|
||||
{ pattern: '/admin/system', path: '/admin/system', heading: t.systemStatus, endpoint: '/admin/system' },
|
||||
@@ -86,7 +87,7 @@ test.beforeEach(async ({ context }) => {
|
||||
});
|
||||
|
||||
for (const identity of ['anonymous', 'normal', 'admin'] as const) {
|
||||
test(`[P5-15-01 E02 E07 A01] all 32 routes retain native entry, refresh and ${identity} permissions`, async ({ page, playwright }, info) => {
|
||||
test(`[P5-15-01 E02 E07 A01] all 33 routes retain native entry, refresh and ${identity} permissions`, async ({ page, playwright }, info) => {
|
||||
test.setTimeout(300_000);
|
||||
const admin = await playwright.request.newContext();
|
||||
const evidence: { pattern: string; mode: string; destination: string; status?: number }[] = [];
|
||||
@@ -105,7 +106,7 @@ for (const identity of ['anonymous', 'normal', 'admin'] as const) {
|
||||
expect(cv.status()).toBe(200); cvId = (await cv.json()).id;
|
||||
}
|
||||
const census = routes(jobId, cvId);
|
||||
expect(new Set(census.map(row => row.pattern)).size).toBe(32);
|
||||
expect(new Set(census.map(row => row.pattern)).size).toBe(33);
|
||||
for (const row of census) {
|
||||
await test.step(row.pattern, async () => {
|
||||
for (const mode of ['entry', 'refresh']) {
|
||||
@@ -123,7 +124,7 @@ for (const identity of ['anonymous', 'normal', 'admin'] as const) {
|
||||
expect.soft(native.status()).toBe(denied ? 403 : row.status ?? 200);
|
||||
if (row.pattern === '/correspondence/review') {
|
||||
expect(await native.json()).toMatchObject({ code: 'gmail_not_connected' });
|
||||
await expect(page.getByRole('alert').filter({ hasText: 'Connect Gmail before using the Gmail review queue.' })).toBeVisible();
|
||||
await expect(page.getByRole('alert').filter({ hasText: t.gmailReviewNotConnected })).toBeVisible();
|
||||
}
|
||||
if (denied) {
|
||||
await expect(page.getByRole('alert').first()).toBeVisible();
|
||||
|
||||
@@ -69,11 +69,11 @@ def seed_operation(payload: object) -> str:
|
||||
except OSError as error:
|
||||
raise SeedDenied("The isolated dataRoot must already exist") from error
|
||||
if (root != requested or root.parent != temporary or not root.is_dir()
|
||||
or requested.is_symlink() or requested.is_junction()
|
||||
or requested.is_symlink() or (sys.platform == "win32" and requested.is_junction())
|
||||
or not re.fullmatch(r"jobtracker-e2e-[A-Za-z0-9][A-Za-z0-9_-]*", root.name)):
|
||||
raise SeedDenied("Only an unlinked jobtracker-e2e-* direct child of system temp is allowed")
|
||||
database = root / "jobtracker.db"
|
||||
if (not database.is_file() or database.is_symlink() or database.is_junction()
|
||||
if (not database.is_file() or database.is_symlink() or (sys.platform == "win32" and database.is_junction())
|
||||
or database.resolve(strict=True) != database or database.stat().st_nlink != 1):
|
||||
raise SeedDenied("The unlinked existing direct jobtracker.db file is required")
|
||||
# EF's SQLite Guid converter writes uppercase canonical text.
|
||||
@@ -110,7 +110,7 @@ def temporary_fixture():
|
||||
yield root
|
||||
finally:
|
||||
assert root.parent == Path(tempfile.gettempdir()).resolve()
|
||||
assert root.name.startswith("jobtracker-e2e-operation-selftest-") and not root.is_symlink() and not root.is_junction()
|
||||
assert root.name.startswith("jobtracker-e2e-operation-selftest-") and not root.is_symlink() and not (sys.platform == "win32" and root.is_junction())
|
||||
|
||||
|
||||
def self_test() -> dict:
|
||||
|
||||
@@ -171,8 +171,13 @@ for (const [mode, language, width] of [["light", "en", 1440], ["dark", "nb", 144
|
||||
await expect(cancel).toBeFocused();
|
||||
await expect(confirm).toHaveAccessibleDescription(t.attachmentsDeleteConfirm.replace("{name}", "Reviewed æøå.txt"));
|
||||
await cancel.press("Shift+Tab");
|
||||
const messageRegion = confirm.getByRole("region", { name: t.attachmentsDeleteTitle, exact: true });
|
||||
await expect(messageRegion).toBeFocused();
|
||||
await messageRegion.press("Shift+Tab");
|
||||
await expect(confirm.getByRole("button", { name: t.attachmentsDelete, exact: true })).toBeFocused();
|
||||
await page.keyboard.press("Tab");
|
||||
await expect(messageRegion).toBeFocused();
|
||||
await page.keyboard.press("Tab");
|
||||
await expect(cancel).toBeFocused();
|
||||
await scan(page, info, "confirm", true);
|
||||
await capture(page, info, `confirm-${suffix}`);
|
||||
|
||||
@@ -113,7 +113,7 @@ function seedPending(info: TestInfo, owner: Owner) {
|
||||
expect(path.dirname(dataRoot)).toBe(realpathSync(os.tmpdir()));
|
||||
expect(path.basename(dataRoot)).toMatch(/^jobtracker-e2e-[A-Za-z0-9][A-Za-z0-9_-]*$/);
|
||||
const helper = path.resolve('e2e/helpers/seed-career-review.py');
|
||||
const result = spawnSync('rtk', ['proxy', process.env.PLAYWRIGHT_PYTHON_PATH || 'python', '-X', 'utf8', helper], {
|
||||
const result = spawnSync(process.env.PLAYWRIGHT_PYTHON_PATH || (process.platform === 'win32' ? 'python' : 'python3'), ['-X', 'utf8', helper], {
|
||||
input: JSON.stringify({ dataRoot, ownerId: owner.id, ownerEmail: owner.email, profile: incoming(),
|
||||
rawText: 'Synthetic pending extraction text — æøå', normalizedText: 'Synthetic pending normalized text; pending contact email; Norwegian B1.' }),
|
||||
encoding: 'utf8', timeout: 15_000, maxBuffer: 1024 * 1024,
|
||||
|
||||
@@ -366,7 +366,11 @@ test('[P7-05 Discovery] keyboard native create and alert updates persist; delete
|
||||
const cancel = dialog.getByRole('button', { name: t.cancel, exact: true });
|
||||
await expect(cancel).toBeFocused();
|
||||
await page.keyboard.press('Shift+Tab');
|
||||
const messageRegion = dialog.getByRole('region', { name: await dialog.getByRole('heading').innerText(), exact: true });
|
||||
await expect(messageRegion).toBeFocused();
|
||||
await messageRegion.press('Shift+Tab');
|
||||
await expect(dialog.getByRole('button', { name: t.adminUsersDelete, exact: true })).toBeFocused();
|
||||
await page.keyboard.press('Tab'); await expect(messageRegion).toBeFocused();
|
||||
await page.keyboard.press('Tab'); await expect(cancel).toBeFocused();
|
||||
await page.keyboard.press('Enter');
|
||||
await expect(dialog).toHaveCount(0); await expect(deleteButton).toBeFocused();
|
||||
|
||||
@@ -42,7 +42,7 @@ function seedQueued(info: TestInfo, owner: Owner) {
|
||||
const metadataRoot = info.config.metadata.isolatedDataRoot;
|
||||
if (typeof metadataRoot !== 'string' || process.env.PLAYWRIGHT_REUSE_SERVERS === 'true') throw new Error('Operation seeding requires explicit isolated non-reused dataRoot');
|
||||
expect(path.isAbsolute(metadataRoot)).toBe(true); const dataRoot = realpathSync(metadataRoot); expect(path.dirname(dataRoot)).toBe(realpathSync(os.tmpdir())); expect(path.basename(dataRoot)).toMatch(/^jobtracker-e2e-[A-Za-z0-9][A-Za-z0-9_-]*$/);
|
||||
const result = spawnSync('rtk', ['proxy', process.env.PLAYWRIGHT_PYTHON_PATH || 'python', '-X', 'utf8', path.resolve('e2e/helpers/seed-operation.py')], { input: JSON.stringify({ dataRoot, ownerId: owner.id, ownerEmail: owner.email }), encoding: 'utf8', timeout: 15_000, maxBuffer: 1024 * 1024 });
|
||||
const result = spawnSync(process.env.PLAYWRIGHT_PYTHON_PATH || (process.platform === 'win32' ? 'python' : 'python3'), ['-X', 'utf8', path.resolve('e2e/helpers/seed-operation.py')], { input: JSON.stringify({ dataRoot, ownerId: owner.id, ownerEmail: owner.email }), encoding: 'utf8', timeout: 15_000, maxBuffer: 1024 * 1024 });
|
||||
expect(result.error, 'guarded helper must start').toBeUndefined(); expect(result.status, result.stderr).toBe(0); const value = JSON.parse(result.stdout) as { operationId: string }; expect(Object.keys(value)).toEqual(['operationId']); expect(value.operationId).toMatch(/^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/); return value.operationId;
|
||||
}
|
||||
async function cancel(api: APIRequestContext, owner: Owner, id: string) { const value = await json<Operation>(await api.post(`${apiUrl}/operations/${id}/cancel`, { headers: owner.headers })); utcFields(value); expect(value).toMatchObject({ id, status: 'cancelled', canCancel: false, canRetry: true }); const notice = (await notices(api)).find(item => item.operationId === id)!; expect(notice).toBeDefined(); expect(notice).toMatchObject({ operationId: id, kind: 'operation_cancelled', linkPath: null, readAtUtc: null }); expect(notice.createdAtUtc).toBe(value.completedAtUtc); return { value, notice }; }
|
||||
|
||||
@@ -42,7 +42,7 @@ function seedQueued(info: TestInfo, owner: Owner) {
|
||||
const metadataRoot = info.config.metadata.isolatedDataRoot;
|
||||
if (typeof metadataRoot !== 'string' || process.env.PLAYWRIGHT_REUSE_SERVERS === 'true') throw new Error('Operation seeding requires explicit isolated non-reused dataRoot');
|
||||
expect(path.isAbsolute(metadataRoot)).toBe(true); const dataRoot = realpathSync(metadataRoot); expect(path.dirname(dataRoot)).toBe(realpathSync(os.tmpdir())); expect(path.basename(dataRoot)).toMatch(/^jobtracker-e2e-[A-Za-z0-9][A-Za-z0-9_-]*$/);
|
||||
const result = spawnSync('rtk', ['proxy', process.env.PLAYWRIGHT_PYTHON_PATH || 'python', '-X', 'utf8', path.resolve('e2e/helpers/seed-operation.py')], { input: JSON.stringify({ dataRoot, ownerId: owner.id, ownerEmail: owner.email }), encoding: 'utf8', timeout: 15_000, maxBuffer: 1024 * 1024 });
|
||||
const result = spawnSync(process.env.PLAYWRIGHT_PYTHON_PATH || (process.platform === 'win32' ? 'python' : 'python3'), ['-X', 'utf8', path.resolve('e2e/helpers/seed-operation.py')], { input: JSON.stringify({ dataRoot, ownerId: owner.id, ownerEmail: owner.email }), encoding: 'utf8', timeout: 15_000, maxBuffer: 1024 * 1024 });
|
||||
expect(result.error, 'guarded helper must start').toBeUndefined(); expect(result.status, result.stderr).toBe(0); const value = JSON.parse(result.stdout) as { operationId: string }; expect(Object.keys(value)).toEqual(['operationId']); expect(value.operationId).toMatch(/^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/); return value.operationId;
|
||||
}
|
||||
async function cancel(api: APIRequestContext, owner: Owner, id: string) { const value = await json<Operation>(await api.post(`${apiUrl}/operations/${id}/cancel`, { headers: owner.headers })); utcFields(value); expect(value).toMatchObject({ id, status: 'cancelled', canCancel: false, canRetry: true }); const notice = (await notices(api)).find(item => item.operationId === id)!; expect(notice).toBeDefined(); expect(notice).toMatchObject({ operationId: id, kind: 'operation_cancelled', linkPath: null, readAtUtc: null }); expect(notice.createdAtUtc).toBe(value.completedAtUtc); return { value, notice }; }
|
||||
|
||||
@@ -268,7 +268,7 @@ for (const mode of ["light", "dark"] as const) for (const language of ["en", "nb
|
||||
? route.fulfill({ status: 204, body: "" }) : route.continue());
|
||||
await page.getByRole("button", { name: t.notifications, exact: true }).click();
|
||||
await expect(page.getByText("CV export failed")).toBeVisible();
|
||||
await expect(page.getByText(t.notificationsUnread)).toBeVisible();
|
||||
await expect(page.getByText(t.notificationsUnread, { exact: true })).toBeVisible();
|
||||
await capture(page, info, `utility-notifications-${suffix}`);
|
||||
await scan(page, info, `utility-notifications-${suffix}`, ".MuiPopover-paper");
|
||||
await page.getByRole("button", { name: t.notificationsMarkRead }).click();
|
||||
|
||||
Generated
+179
-179
@@ -24,9 +24,9 @@
|
||||
"@types/node": "^16.18.126",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"axios": "^1.19.0",
|
||||
"axios": "^1.20.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"next": "^16.3.3",
|
||||
"next": "^16.3.8",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-router-dom": "^7.18.2",
|
||||
@@ -42,7 +42,7 @@
|
||||
"@types/jest": "^30.0.0",
|
||||
"babel-jest": "^30.4.1",
|
||||
"eslint": "^9.39.5",
|
||||
"eslint-config-next": "^16.3.3",
|
||||
"eslint-config-next": "^16.3.8",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^30.4.2",
|
||||
"jest-environment-jsdom": "^30.4.1"
|
||||
@@ -2610,9 +2610,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-arm64": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.4.tgz",
|
||||
"integrity": "sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==",
|
||||
"version": "0.35.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.5.tgz",
|
||||
"integrity": "sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2628,13 +2628,13 @@
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-arm64": "1.3.3"
|
||||
"@img/sharp-libvips-darwin-arm64": "1.3.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-x64": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.4.tgz",
|
||||
"integrity": "sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==",
|
||||
"version": "0.35.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.5.tgz",
|
||||
"integrity": "sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2650,20 +2650,20 @@
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-x64": "1.3.3"
|
||||
"@img/sharp-libvips-darwin-x64": "1.3.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-freebsd-wasm32": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.4.tgz",
|
||||
"integrity": "sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==",
|
||||
"version": "0.35.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.5.tgz",
|
||||
"integrity": "sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"dependencies": {
|
||||
"@img/sharp-wasm32": "0.35.4"
|
||||
"@img/sharp-wasm32": "0.35.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
@@ -2673,9 +2673,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-arm64": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.3.tgz",
|
||||
"integrity": "sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==",
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.4.tgz",
|
||||
"integrity": "sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2689,9 +2689,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-x64": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.3.tgz",
|
||||
"integrity": "sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==",
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.4.tgz",
|
||||
"integrity": "sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2705,9 +2705,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.3.tgz",
|
||||
"integrity": "sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==",
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.4.tgz",
|
||||
"integrity": "sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -2724,9 +2724,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm64": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.3.tgz",
|
||||
"integrity": "sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==",
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.4.tgz",
|
||||
"integrity": "sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2743,9 +2743,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-ppc64": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.3.tgz",
|
||||
"integrity": "sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==",
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.4.tgz",
|
||||
"integrity": "sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -2762,9 +2762,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-riscv64": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.3.tgz",
|
||||
"integrity": "sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==",
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.4.tgz",
|
||||
"integrity": "sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -2781,9 +2781,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-s390x": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.3.tgz",
|
||||
"integrity": "sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==",
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.4.tgz",
|
||||
"integrity": "sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -2800,9 +2800,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-x64": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.3.tgz",
|
||||
"integrity": "sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==",
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.4.tgz",
|
||||
"integrity": "sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2819,9 +2819,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.3.tgz",
|
||||
"integrity": "sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==",
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.4.tgz",
|
||||
"integrity": "sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2838,9 +2838,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.3.tgz",
|
||||
"integrity": "sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==",
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.4.tgz",
|
||||
"integrity": "sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2857,9 +2857,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.4.tgz",
|
||||
"integrity": "sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==",
|
||||
"version": "0.35.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.5.tgz",
|
||||
"integrity": "sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -2878,13 +2878,13 @@
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm": "1.3.3"
|
||||
"@img/sharp-libvips-linux-arm": "1.3.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm64": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.4.tgz",
|
||||
"integrity": "sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==",
|
||||
"version": "0.35.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.5.tgz",
|
||||
"integrity": "sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -2903,13 +2903,13 @@
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm64": "1.3.3"
|
||||
"@img/sharp-libvips-linux-arm64": "1.3.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-ppc64": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.4.tgz",
|
||||
"integrity": "sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==",
|
||||
"version": "0.35.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.5.tgz",
|
||||
"integrity": "sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@@ -2928,13 +2928,13 @@
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-ppc64": "1.3.3"
|
||||
"@img/sharp-libvips-linux-ppc64": "1.3.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-riscv64": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.4.tgz",
|
||||
"integrity": "sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==",
|
||||
"version": "0.35.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.5.tgz",
|
||||
"integrity": "sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -2953,13 +2953,13 @@
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-riscv64": "1.3.3"
|
||||
"@img/sharp-libvips-linux-riscv64": "1.3.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-s390x": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.4.tgz",
|
||||
"integrity": "sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==",
|
||||
"version": "0.35.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.5.tgz",
|
||||
"integrity": "sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@@ -2978,13 +2978,13 @@
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-s390x": "1.3.3"
|
||||
"@img/sharp-libvips-linux-s390x": "1.3.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-x64": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.4.tgz",
|
||||
"integrity": "sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==",
|
||||
"version": "0.35.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.5.tgz",
|
||||
"integrity": "sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -3003,13 +3003,13 @@
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-x64": "1.3.3"
|
||||
"@img/sharp-libvips-linux-x64": "1.3.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-arm64": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.4.tgz",
|
||||
"integrity": "sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==",
|
||||
"version": "0.35.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.5.tgz",
|
||||
"integrity": "sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -3028,13 +3028,13 @@
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.3.3"
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.3.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-x64": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.4.tgz",
|
||||
"integrity": "sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==",
|
||||
"version": "0.35.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.5.tgz",
|
||||
"integrity": "sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -3053,13 +3053,13 @@
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.3.3"
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.3.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-wasm32": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz",
|
||||
"integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==",
|
||||
"version": "0.35.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.5.tgz",
|
||||
"integrity": "sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==",
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
@@ -3073,16 +3073,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-webcontainers-wasm32": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.4.tgz",
|
||||
"integrity": "sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==",
|
||||
"version": "0.35.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.5.tgz",
|
||||
"integrity": "sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@img/sharp-wasm32": "0.35.4"
|
||||
"@img/sharp-wasm32": "0.35.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.9.0"
|
||||
@@ -3092,9 +3092,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-arm64": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.4.tgz",
|
||||
"integrity": "sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==",
|
||||
"version": "0.35.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.5.tgz",
|
||||
"integrity": "sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -3111,9 +3111,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-ia32": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.4.tgz",
|
||||
"integrity": "sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==",
|
||||
"version": "0.35.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.5.tgz",
|
||||
"integrity": "sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -3130,9 +3130,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-x64": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.4.tgz",
|
||||
"integrity": "sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==",
|
||||
"version": "0.35.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.5.tgz",
|
||||
"integrity": "sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -4102,15 +4102,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/env": {
|
||||
"version": "16.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-16.3.4.tgz",
|
||||
"integrity": "sha512-cjWZnUUa6jZq2kFaNe/ZyJdZonOZ/QoN0Zka2nz/FLOrfx14pQuM9c5RaSVkWMqgdt4ksgPAMWPyHSs/CyV48Q==",
|
||||
"version": "16.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-16.3.8.tgz",
|
||||
"integrity": "sha512-Al9zqHVV7TJv0eFuOU4U7Lvv74PTih4Ch63sk2xCIpSTkE3udFnaOcnzP2lQVymiL7yS9Cj2iClUXlR3EQ5sEw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@next/eslint-plugin-next": {
|
||||
"version": "16.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.3.4.tgz",
|
||||
"integrity": "sha512-szW9y2Aumu4z88YXfTzcFsgUAg2k64uzbtcO5L9f1AKS4w/GUKJcbFllRflROVyNPgJtGOnvNxiyp3v6b+prIA==",
|
||||
"version": "16.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.3.8.tgz",
|
||||
"integrity": "sha512-eCR9RcLTZrVS+K3+VOhi0xMdUUGNuriCHHhU1ZoABC/QQ/Gp0OWbLKQxV2/9DkmihYdYRcQWFmZXW8M1AMUXYw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -4151,9 +4151,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-arm64": {
|
||||
"version": "16.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.3.4.tgz",
|
||||
"integrity": "sha512-iBr3I5LZNk5/bgl5//iTgD2tcym14MX0Xo7fD//u9dYAEgGzza1y9oywluPtf74YnOswVdH1908aK9xVz7zQTw==",
|
||||
"version": "16.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.3.8.tgz",
|
||||
"integrity": "sha512-2JPRMh2nmQG5CiL7cXGL9AGwnPWJQ//cTtAUCT+w511QHk79SYz3LGv/pc5X643B/WEO0rvu3Yww0hqwt3kgeA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -4167,9 +4167,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-x64": {
|
||||
"version": "16.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.3.4.tgz",
|
||||
"integrity": "sha512-2dpiSyl2Jw/NrBPaU2MAKGSa+2MR82pJIn4Sm5Rjr+gxAeuh0z158Su3Z2O8zn7UNNq+ej4bToed6RcRN/Lydg==",
|
||||
"version": "16.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.3.8.tgz",
|
||||
"integrity": "sha512-GZtCCOBKJ4leVIT/Th0llWKhD1ca92lzbQiS5R5ON9QkoiFnilFsebDae1JU2a3HWoKMEmEZWGs1AGLavVM72Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -4183,9 +4183,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-gnu": {
|
||||
"version": "16.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.3.4.tgz",
|
||||
"integrity": "sha512-+t+U8HZT+fApePCS5h89CSH3datz29MkzyfCn+6fpsZBG/oiEOhINcb9rtkv6sdpToLGFn2e6146NzaKCXkqrA==",
|
||||
"version": "16.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.3.8.tgz",
|
||||
"integrity": "sha512-O659ygeQYqneJ1fBKMpFxIFqYkYswu8IAS1OCKK/4f3ZgJJm1dRz4fVJZRi/kLLWjnBKnebOePA4WNv+sV1pVA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -4202,9 +4202,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-musl": {
|
||||
"version": "16.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.3.4.tgz",
|
||||
"integrity": "sha512-mx03GNs1ocQA5JQ4FxDMmIsNkdrZh8cuezKCrId28e5/gIPU/l7Kcy2+vmCCzdjnnmXJy+iOAu+7K0QppO6Urg==",
|
||||
"version": "16.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.3.8.tgz",
|
||||
"integrity": "sha512-dSjKSyWpzxoO1d3DIZZcP4XJcNaKeLmxQMFOiYl5vuBRMmweIqnAhty8tAmRsvTss779cK1FtYnDMj40e4TQlg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -4221,9 +4221,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-gnu": {
|
||||
"version": "16.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.3.4.tgz",
|
||||
"integrity": "sha512-YIhGY6fSMfha52bnVxnzc9zaVBzJg+cqQTOD8tXIBSx4fuv0pVMxQTE0PaS59YhnMOiYiG09IMwxJAf/CFm/Dw==",
|
||||
"version": "16.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.3.8.tgz",
|
||||
"integrity": "sha512-lbqOuz3RPRcv+o9msNsJw5x4+Y1ZwPTs6vmL6DCf7i0fZfvng/F59wyeDwqHIvV0mK//RBy/jJkZ+nCKsSMXjQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -4240,9 +4240,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-musl": {
|
||||
"version": "16.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.3.4.tgz",
|
||||
"integrity": "sha512-+eaaX6axpDb0yF1GCpiERe6njplvdC+nks/fKfcHu3XPGRrald8P3/X7yv7QLdjA51knnxwl9pxdIJsg+w1L+Q==",
|
||||
"version": "16.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.3.8.tgz",
|
||||
"integrity": "sha512-+316WswI8ScVgZeUd+1KGaXkHhaYQzCjvH/05TZSpJ8zBizb1a4G7DtO7F12jcBIqMOtsz9ji1t48fmKtzqsGA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -4259,9 +4259,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-arm64-msvc": {
|
||||
"version": "16.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.3.4.tgz",
|
||||
"integrity": "sha512-0jcXW7Xs/uzICrmgV3MhDYDeRy++1CqnpDIerlPIqYO4bhzB4WNbX/aRnQclustsAyTkFKB0z6rbcjmNg5tR8A==",
|
||||
"version": "16.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.3.8.tgz",
|
||||
"integrity": "sha512-ji0gd4kMYUxO+1fJBIbiBVRCjzG/lloiyCccnlebvb1ZJ5qXCPZqYg4Jl1DrrixnWNMKylzgpmMWx0yNDYXlzw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -4275,9 +4275,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-x64-msvc": {
|
||||
"version": "16.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.3.4.tgz",
|
||||
"integrity": "sha512-vvBzwu1pYQCp92maZCFCIw/XgOTMR5tur9GjakwIo2cmwRTMKajRZZDS9+e4KsUZWKu1E007WUeAFXRRjZeuzw==",
|
||||
"version": "16.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.3.8.tgz",
|
||||
"integrity": "sha512-WcTlaKt/TWkh5kUjdJcUmB1XgZ+1c6fz4Y9fDHL73YNSdGaUWjceeWrrlwF0nv19iABYWC4iAq1oX1w4Bn0vfg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -5715,9 +5715,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.19.0.tgz",
|
||||
"integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==",
|
||||
"version": "1.20.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.20.0.tgz",
|
||||
"integrity": "sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.16.0",
|
||||
@@ -5922,9 +5922,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "2.1.4",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz",
|
||||
"integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
|
||||
"version": "2.1.7",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz",
|
||||
"integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -6949,13 +6949,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-next": {
|
||||
"version": "16.3.4",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.3.4.tgz",
|
||||
"integrity": "sha512-35/8RM10huEL9vlr8hUZMERMENHBrnyHN3ZZkF9efSgzGaqK34jIqry44A956//zriUhUAUW0XSkcolhrryqAA==",
|
||||
"version": "16.3.8",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.3.8.tgz",
|
||||
"integrity": "sha512-81vovwMe6NGnoFsl0KUJWzlS+y239i3fdsxs49gSEX3pDQW2cuKy+fxUPXNkQqqx6OCPheYPtBlSufc9aZ0k+w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@next/eslint-plugin-next": "16.3.4",
|
||||
"@next/eslint-plugin-next": "16.3.8",
|
||||
"eslint-import-resolver-node": "^0.3.6",
|
||||
"eslint-import-resolver-typescript": "^3.5.2",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
@@ -7729,9 +7729,9 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.16.0",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
|
||||
"integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
|
||||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.1.tgz",
|
||||
"integrity": "sha512-FNvFGzoMLWmE6Yj9spb/zjd7yiNCHiAW9/Tg9CXrQ8wuu32HtlJOwWO11OJafl5FfY3DxTdQ0vj42zU1kvv5jg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
@@ -10308,12 +10308,12 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/next": {
|
||||
"version": "16.3.4",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-16.3.4.tgz",
|
||||
"integrity": "sha512-/Ztf6CeRH+ejEXUrYtqI4gkS66eFIHuSwqi60RgcpWKodxFZx2/dqVCMKBwILfAHXQ+F1b1vAudgj3mnxqtoIA==",
|
||||
"version": "16.3.8",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-16.3.8.tgz",
|
||||
"integrity": "sha512-U7QEZaTini6wKrb8A8hqLLqYQyCetegKjCpJOyxk642vWoMoU1x5PyZCJFvgYgiptA8xc5j/9xYlZFO7w9Sjmw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@next/env": "16.3.4",
|
||||
"@next/env": "16.3.8",
|
||||
"@swc/helpers": "0.5.23",
|
||||
"baseline-browser-mapping": "^2.9.19",
|
||||
"caniuse-lite": "^1.0.30001579",
|
||||
@@ -10327,14 +10327,14 @@
|
||||
"node": ">=20.9.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@next/swc-darwin-arm64": "16.3.4",
|
||||
"@next/swc-darwin-x64": "16.3.4",
|
||||
"@next/swc-linux-arm64-gnu": "16.3.4",
|
||||
"@next/swc-linux-arm64-musl": "16.3.4",
|
||||
"@next/swc-linux-x64-gnu": "16.3.4",
|
||||
"@next/swc-linux-x64-musl": "16.3.4",
|
||||
"@next/swc-win32-arm64-msvc": "16.3.4",
|
||||
"@next/swc-win32-x64-msvc": "16.3.4",
|
||||
"@next/swc-darwin-arm64": "16.3.8",
|
||||
"@next/swc-darwin-x64": "16.3.8",
|
||||
"@next/swc-linux-arm64-gnu": "16.3.8",
|
||||
"@next/swc-linux-arm64-musl": "16.3.8",
|
||||
"@next/swc-linux-x64-gnu": "16.3.8",
|
||||
"@next/swc-linux-x64-musl": "16.3.8",
|
||||
"@next/swc-win32-arm64-msvc": "16.3.8",
|
||||
"@next/swc-win32-x64-msvc": "16.3.8",
|
||||
"sharp": "^0.35.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -11519,9 +11519,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/sharp": {
|
||||
"version": "0.35.4",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.4.tgz",
|
||||
"integrity": "sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==",
|
||||
"version": "0.35.5",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.5.tgz",
|
||||
"integrity": "sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==",
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
@@ -11536,31 +11536,31 @@
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-darwin-arm64": "0.35.4",
|
||||
"@img/sharp-darwin-x64": "0.35.4",
|
||||
"@img/sharp-freebsd-wasm32": "0.35.4",
|
||||
"@img/sharp-libvips-darwin-arm64": "1.3.3",
|
||||
"@img/sharp-libvips-darwin-x64": "1.3.3",
|
||||
"@img/sharp-libvips-linux-arm": "1.3.3",
|
||||
"@img/sharp-libvips-linux-arm64": "1.3.3",
|
||||
"@img/sharp-libvips-linux-ppc64": "1.3.3",
|
||||
"@img/sharp-libvips-linux-riscv64": "1.3.3",
|
||||
"@img/sharp-libvips-linux-s390x": "1.3.3",
|
||||
"@img/sharp-libvips-linux-x64": "1.3.3",
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.3.3",
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.3.3",
|
||||
"@img/sharp-linux-arm": "0.35.4",
|
||||
"@img/sharp-linux-arm64": "0.35.4",
|
||||
"@img/sharp-linux-ppc64": "0.35.4",
|
||||
"@img/sharp-linux-riscv64": "0.35.4",
|
||||
"@img/sharp-linux-s390x": "0.35.4",
|
||||
"@img/sharp-linux-x64": "0.35.4",
|
||||
"@img/sharp-linuxmusl-arm64": "0.35.4",
|
||||
"@img/sharp-linuxmusl-x64": "0.35.4",
|
||||
"@img/sharp-webcontainers-wasm32": "0.35.4",
|
||||
"@img/sharp-win32-arm64": "0.35.4",
|
||||
"@img/sharp-win32-ia32": "0.35.4",
|
||||
"@img/sharp-win32-x64": "0.35.4"
|
||||
"@img/sharp-darwin-arm64": "0.35.5",
|
||||
"@img/sharp-darwin-x64": "0.35.5",
|
||||
"@img/sharp-freebsd-wasm32": "0.35.5",
|
||||
"@img/sharp-libvips-darwin-arm64": "1.3.4",
|
||||
"@img/sharp-libvips-darwin-x64": "1.3.4",
|
||||
"@img/sharp-libvips-linux-arm": "1.3.4",
|
||||
"@img/sharp-libvips-linux-arm64": "1.3.4",
|
||||
"@img/sharp-libvips-linux-ppc64": "1.3.4",
|
||||
"@img/sharp-libvips-linux-riscv64": "1.3.4",
|
||||
"@img/sharp-libvips-linux-s390x": "1.3.4",
|
||||
"@img/sharp-libvips-linux-x64": "1.3.4",
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.3.4",
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.3.4",
|
||||
"@img/sharp-linux-arm": "0.35.5",
|
||||
"@img/sharp-linux-arm64": "0.35.5",
|
||||
"@img/sharp-linux-ppc64": "0.35.5",
|
||||
"@img/sharp-linux-riscv64": "0.35.5",
|
||||
"@img/sharp-linux-s390x": "0.35.5",
|
||||
"@img/sharp-linux-x64": "0.35.5",
|
||||
"@img/sharp-linuxmusl-arm64": "0.35.5",
|
||||
"@img/sharp-linuxmusl-x64": "0.35.5",
|
||||
"@img/sharp-webcontainers-wasm32": "0.35.5",
|
||||
"@img/sharp-win32-arm64": "0.35.5",
|
||||
"@img/sharp-win32-ia32": "0.35.5",
|
||||
"@img/sharp-win32-x64": "0.35.5"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/node": {
|
||||
@@ -11700,9 +11700,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz",
|
||||
"integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
"@types/node": "^16.18.126",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"axios": "^1.19.0",
|
||||
"axios": "^1.20.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"next": "^16.3.3",
|
||||
"next": "^16.3.8",
|
||||
"react": "^19.2.4",
|
||||
"react-dom": "^19.2.4",
|
||||
"react-router-dom": "^7.18.2",
|
||||
@@ -62,14 +62,14 @@
|
||||
"@types/jest": "^30.0.0",
|
||||
"babel-jest": "^30.4.1",
|
||||
"eslint": "^9.39.5",
|
||||
"eslint-config-next": "^16.3.3",
|
||||
"eslint-config-next": "^16.3.8",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^30.4.2",
|
||||
"jest-environment-jsdom": "^30.4.1"
|
||||
},
|
||||
"overrides": {
|
||||
"brace-expansion": "2.1.4",
|
||||
"brace-expansion": "2.1.7",
|
||||
"postcss": "8.5.25",
|
||||
"sharp": "0.35.4"
|
||||
"sharp": "0.35.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ export default defineConfig({
|
||||
workers: 1,
|
||||
retries: process.env.CI ? 1 : 0,
|
||||
metadata: { isolatedDataRoot: reuseServers ? null : dataRoot },
|
||||
reporter: process.env.CI ? "github" : "list",
|
||||
reporter: process.env.CI
|
||||
? [["list"], ["github"], ["json", { outputFile: ".artifacts/playwright-results.json" }]]
|
||||
: "list",
|
||||
use: {
|
||||
baseURL: appUrl,
|
||||
trace: "retain-on-failure",
|
||||
@@ -40,7 +42,8 @@ export default defineConfig({
|
||||
Auth__AdminEmail: "e2e@example.test",
|
||||
Auth__AdminPassword: "E2ePassword123!",
|
||||
Auth__AllowRegistration: "false",
|
||||
RateLimits__AuthLoginPermitLimit: "100",
|
||||
// The full browser suite shares one loopback IP. Real throttle contracts use the separate 10-permit fixture.
|
||||
RateLimits__AuthLoginPermitLimit: "1000",
|
||||
App__Version: "e2e-verification",
|
||||
App__CommitSha: "e2e1234",
|
||||
Cors__Origins__0: appUrl,
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import fs from 'node:fs';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
|
||||
export function evaluateAudit(report, lock, now = Date.now()) {
|
||||
if (report?.error || !report?.metadata?.vulnerabilities || !report?.vulnerabilities || !lock?.packages) {
|
||||
return { accepted: [], failures: ['Missing or invalid audit/lock data'] };
|
||||
}
|
||||
const accepted = [], failures = [];
|
||||
const allowedChain = new Set(['braces', 'micromatch', 'fast-glob', '@next/eslint-plugin-next', 'eslint-config-next']);
|
||||
const devOnlyApprovedOrigin = (name, visited = new Set()) => {
|
||||
const finding = report.vulnerabilities[name];
|
||||
if (!allowedChain.has(name) || visited.has(name) || finding?.severity !== 'high' ||
|
||||
!Array.isArray(finding.nodes) || !finding.nodes.length ||
|
||||
finding.nodes.some(node => lock.packages[node]?.dev !== true || name === 'braces' && lock.packages[node]?.version !== '3.0.3') ||
|
||||
!Array.isArray(finding.via) || !finding.via.length) return false;
|
||||
const path = new Set(visited).add(name);
|
||||
return finding.via.every(origin => typeof origin === 'string' ? devOnlyApprovedOrigin(origin, path) :
|
||||
origin?.name === 'braces' && origin.dependency === 'braces' && origin.severity === 'high' && origin.url === 'https://github.com/advisories/GHSA-vfj7-8cjw-p6xm');
|
||||
};
|
||||
for (const [name, finding] of Object.entries(report.vulnerabilities)) {
|
||||
if (!['info', 'low', 'moderate', 'high', 'critical'].includes(finding.severity)) {
|
||||
failures.push(`${name}: invalid severity`);
|
||||
} else if (['high', 'critical'].includes(finding.severity)) {
|
||||
// Owner-approved on 17 October: no upstream fix; trusted lint patterns only, never production dependencies.
|
||||
if (Number.isFinite(now) && now < Date.parse('2026-11-01T00:00:00Z') && devOnlyApprovedOrigin(name)) accepted.push(name);
|
||||
else failures.push(`${name}: ${finding.severity}`);
|
||||
}
|
||||
}
|
||||
return { accepted, failures };
|
||||
}
|
||||
|
||||
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
||||
try {
|
||||
const result = spawnSync(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['audit', '--json', '--audit-level=high'], {
|
||||
encoding: 'utf8', maxBuffer: 8 * 1024 * 1024, shell: process.platform === 'win32',
|
||||
});
|
||||
fs.mkdirSync('.artifacts', { recursive: true });
|
||||
fs.writeFileSync('.artifacts/frontend-audit.json', result.stdout || '');
|
||||
if (result.error || result.signal || ![0, 1].includes(result.status)) throw new Error('npm audit did not complete');
|
||||
const report = JSON.parse(result.stdout);
|
||||
const decision = evaluateAudit(report, JSON.parse(fs.readFileSync('package-lock.json', 'utf8')));
|
||||
console.log(JSON.stringify({ report, ...decision,
|
||||
temporaryException: decision.accepted.length ? 'GHSA-vfj7-8cjw-p6xm: dev-only; expires after 2026-10-31 UTC' : null }, null, 2));
|
||||
process.exitCode = decision.failures.length ? 1 : 0;
|
||||
} catch (error) {
|
||||
console.error(error.message);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { evaluateAudit } from './audit-dependencies.mjs';
|
||||
|
||||
const now = Date.parse('2026-10-17T18:00:00Z');
|
||||
function fixture() {
|
||||
return {
|
||||
report: {
|
||||
metadata: { vulnerabilities: { high: 2, critical: 0 } },
|
||||
vulnerabilities: {
|
||||
braces: { severity: 'high', nodes: ['node_modules/braces'], via: [{ name: 'braces', dependency: 'braces', severity: 'high', url: 'https://github.com/advisories/GHSA-vfj7-8cjw-p6xm' }] },
|
||||
micromatch: { severity: 'high', nodes: ['node_modules/micromatch'], via: ['braces'] },
|
||||
},
|
||||
},
|
||||
lock: { packages: { 'node_modules/braces': { dev: true, version: '3.0.3' }, 'node_modules/micromatch': { dev: true, version: '4.0.8' } } },
|
||||
};
|
||||
}
|
||||
test('allows only the approved dev-only advisory and its linked findings before expiry', () => {
|
||||
const { report, lock } = fixture();
|
||||
assert.deepEqual(evaluateAudit(report, lock, now), { accepted: ['braces', 'micromatch'], failures: [] });
|
||||
});
|
||||
test('blocks the same advisory if any affected copy is a production dependency', () => {
|
||||
const { report, lock } = fixture(); delete lock.packages['node_modules/braces'].dev;
|
||||
assert.ok(evaluateAudit(report, lock, now).failures.length);
|
||||
});
|
||||
test('blocks any new advisory even inside the otherwise approved chain', () => {
|
||||
const { report, lock } = fixture(); report.vulnerabilities.braces.via.push({ name: 'braces', url: 'https://github.com/advisories/UNKNOWN' });
|
||||
assert.ok(evaluateAudit(report, lock, now).failures.length);
|
||||
});
|
||||
test('critical findings cannot use the high-only exception', () => {
|
||||
const { report, lock } = fixture(); report.vulnerabilities.braces.severity = 'critical';
|
||||
assert.ok(evaluateAudit(report, lock, now).failures.length);
|
||||
});
|
||||
test('a critical underlying advisory is rejected even if its aggregate severity is inconsistent', () => {
|
||||
const { report, lock } = fixture(); report.vulnerabilities.braces.via[0].severity = 'critical';
|
||||
assert.ok(evaluateAudit(report, lock, now).failures.length);
|
||||
});
|
||||
test('expires after 31 October UTC without an environment-variable bypass', () => {
|
||||
const { report, lock } = fixture();
|
||||
assert.ok(evaluateAudit(report, lock, Date.parse('2026-11-01T00:00:00Z')).failures.length);
|
||||
});
|
||||
test('missing nodes, missing lock entries and cyclic origins fail closed', () => {
|
||||
for (const change of [x => x.report.vulnerabilities.braces.nodes = [], x => delete x.lock.packages['node_modules/braces'], x => x.report.vulnerabilities.braces.via = ['micromatch']]) {
|
||||
const input = fixture(); change(input); assert.ok(evaluateAudit(input.report, input.lock, now).failures.length);
|
||||
}
|
||||
});
|
||||
test('invalid reports and unknown severities fail closed', () => {
|
||||
assert.ok(evaluateAudit({ error: { message: 'Registry unavailable' } }, {}, now).failures.length);
|
||||
const { report, lock } = fixture(); report.vulnerabilities.braces.severity = 'unknown';
|
||||
assert.ok(evaluateAudit(report, lock, now).failures.length);
|
||||
});
|
||||
test('non-blocking moderate findings retain the existing audit threshold', () => {
|
||||
const report = { metadata: { vulnerabilities: { moderate: 1 } }, vulnerabilities: { tool: { severity: 'moderate', via: [] } } };
|
||||
assert.deepEqual(evaluateAudit(report, { packages: {} }, now), { accepted: [], failures: [] });
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import '@testing-library/jest-dom';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { render, screen, fireEvent, act } from '@testing-library/react';
|
||||
import { ConfirmProvider, useConfirm } from './confirm';
|
||||
import { I18nProvider } from './i18n/I18nProvider';
|
||||
|
||||
@@ -31,6 +31,10 @@ test('renders app-owned confirmation dialog', async () => {
|
||||
expect(screen.getByRole('button', { name: /delete/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole('dialog', { name: 'Delete job' })).toHaveAccessibleDescription('Are you sure?');
|
||||
expect(screen.getByRole('button', { name: 'Cancel' })).toHaveFocus();
|
||||
const messageRegion = screen.getByRole('region', { name: 'Delete job' });
|
||||
expect(messageRegion).toHaveAttribute('tabindex', '0');
|
||||
messageRegion.focus();
|
||||
expect(messageRegion).toHaveFocus();
|
||||
});
|
||||
|
||||
test('uses the selected language for shared dialog defaults', async () => {
|
||||
@@ -48,3 +52,18 @@ test('uses the selected language for shared dialog defaults', async () => {
|
||||
expect(screen.getByRole('button', { name: 'Bekreft' })).toBeInTheDocument();
|
||||
window.localStorage.removeItem('uiLanguage');
|
||||
});
|
||||
|
||||
test('finishing the opening transition preserves focus already moved inside the dialog', async () => {
|
||||
jest.useFakeTimers();
|
||||
try {
|
||||
render(<ConfirmProvider><Demo /></ConfirmProvider>);
|
||||
fireEvent.click(screen.getByRole('button', { name: /open confirm/i }));
|
||||
expect(screen.getByRole('button', { name: 'Cancel' })).toHaveFocus();
|
||||
const message = screen.getByRole('region', { name: 'Delete job' });
|
||||
act(() => message.focus());
|
||||
await act(async () => { jest.advanceTimersByTime(1000); });
|
||||
expect(message).toHaveFocus();
|
||||
} finally {
|
||||
jest.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -23,8 +23,8 @@ const ConfirmContext = createContext<ConfirmContextValue | null>(null);
|
||||
export function ConfirmProvider({ children }: { children: React.ReactNode }) {
|
||||
const i18n = useOptionalI18n();
|
||||
const messageId = useId();
|
||||
const titleId = useId();
|
||||
const resolverRef = useRef<((value: boolean) => void) | null>(null);
|
||||
const cancelButtonRef = useRef<HTMLButtonElement>(null);
|
||||
const [state, setState] = useState<ConfirmState>({
|
||||
open: false,
|
||||
message: "",
|
||||
@@ -60,13 +60,12 @@ export function ConfirmProvider({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<ConfirmContext.Provider value={value}>
|
||||
{children}
|
||||
<Dialog open={state.open} onClose={() => closeWith(false)} aria-describedby={messageId} fullWidth maxWidth="xs"
|
||||
<Dialog open={state.open} onClose={() => closeWith(false)} aria-labelledby={titleId} aria-describedby={messageId} fullWidth maxWidth="xs"
|
||||
slotProps={{
|
||||
paper: { sx: { borderRadius: 3, border: 1, borderColor: "divider", backgroundImage: "none" } },
|
||||
transition: { onEntered: () => cancelButtonRef.current?.focus() },
|
||||
}}>
|
||||
<DialogTitle sx={{ pb: 1, fontWeight: 800 }}>{state.title}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogTitle id={titleId} sx={{ pb: 1, fontWeight: 800 }}>{state.title}</DialogTitle>
|
||||
<DialogContent role="region" aria-labelledby={titleId} tabIndex={0}>
|
||||
<Alert severity={state.destructive ? "warning" : "info"} variant="outlined" sx={{ mb: 2 }}>
|
||||
{state.destructive
|
||||
? i18n?.t("confirmDestructiveHint") ?? "This action may be hard to undo."
|
||||
@@ -75,7 +74,7 @@ export function ConfirmProvider({ children }: { children: React.ReactNode }) {
|
||||
<Typography id={messageId} sx={{ color: "text.secondary" }}>{state.message}</Typography>
|
||||
</DialogContent>
|
||||
<DialogActions sx={{ px: 3, pb: 2.5, gap: 1 }}>
|
||||
<Button ref={cancelButtonRef} autoFocus onClick={() => closeWith(false)}>{state.cancelLabel}</Button>
|
||||
<Button autoFocus onClick={() => closeWith(false)}>{state.cancelLabel}</Button>
|
||||
<Button color={state.destructive ? "error" : "primary"} variant="contained" onClick={() => closeWith(true)}>
|
||||
{state.confirmLabel}
|
||||
</Button>
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
"""Exercise the actual workflow's narrow corruption guard, without starting CI."""
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
WORKFLOW = Path(__file__).resolve().parents[1] / '.gitea/workflows/ci-deploy.yml'
|
||||
BASH = str(Path('C:/Program Files/Git/bin/bash.exe')) if os.name == 'nt' else shutil.which('bash')
|
||||
|
||||
|
||||
class BrowserRuntimeRetryTests(unittest.TestCase):
|
||||
def permits_retry(self, log):
|
||||
text = WORKFLOW.read_text(encoding='utf-8')
|
||||
end = text.index(' echo "Browser runtime source appears corrupted')
|
||||
start = text.rfind('\n if ', 0, end) + 1
|
||||
guard = text[start:end]
|
||||
self.assertTrue(guard.strip().startswith('if '))
|
||||
with tempfile.TemporaryDirectory() as folder:
|
||||
fixture = Path(folder) / 'smoke.log'
|
||||
fixture.write_text(log, encoding='utf-8')
|
||||
result = subprocess.run([BASH, '-c', guard + '\nexit 0\nelse\nexit 1\nfi'],
|
||||
env={**os.environ, 'smoke_log': fixture.as_posix()}, capture_output=True, text=True)
|
||||
self.assertIn(result.returncode, [0, 1], result.stderr)
|
||||
return result.returncode == 0
|
||||
|
||||
def test_captured_playwright_source_corruption(self):
|
||||
self.assertTrue(self.permits_retry('/workspace/node_modules/playwright-core/lib/utilsBundle.js:35841\n'
|
||||
'const { items }$= this;\nSyntaxError: Missing initializer in destructuring declaration'))
|
||||
|
||||
def test_existing_axe_source_corruption(self):
|
||||
self.assertTrue(self.permits_retry('AxeBuilder.analyze\npage.evaluate: ReferenceError: invalid runtime source'))
|
||||
|
||||
def test_application_assertion_stays_terminal(self):
|
||||
self.assertFalse(self.permits_retry('expect(region).not.toBeVisible() failed'))
|
||||
|
||||
def test_browser_crash_alone_stays_terminal(self):
|
||||
self.assertFalse(self.permits_retry('Error: page.evaluate: Target crashed'))
|
||||
|
||||
def test_user_javascript_syntax_error_stays_terminal(self):
|
||||
self.assertFalse(self.permits_retry('src/example.js:1\nconst { items }$= this;\nSyntaxError: Missing initializer in destructuring declaration'))
|
||||
|
||||
def test_incomplete_signature_stays_terminal(self):
|
||||
self.assertFalse(self.permits_retry('node_modules/playwright-core/lib/utilsBundle.js:35841\nSyntaxError: Missing initializer in destructuring declaration'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -646,6 +646,8 @@ def _ollama_generate(prompt: str, *, json_mode: bool, temperature: float, timeou
|
||||
"stream": False,
|
||||
"options": {"temperature": temperature},
|
||||
}
|
||||
if OLLAMA_MODEL.split(":", 1)[0] == "qwen3.5":
|
||||
payload["think"] = False
|
||||
if json_mode:
|
||||
payload["format"] = "json"
|
||||
body = _http_post_json(f"{OLLAMA_BASE_URL}/api/generate", payload, {}, timeout)
|
||||
|
||||
@@ -8,6 +8,7 @@ import textwrap
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from docx import Document
|
||||
from fastapi.testclient import TestClient
|
||||
from PIL import Image
|
||||
@@ -253,6 +254,55 @@ def test_provider_defaults_to_ollama_and_is_unchanged(monkeypatch):
|
||||
assert captured["body"]["options"]["temperature"] == 0.1
|
||||
|
||||
|
||||
@pytest.mark.parametrize("ollama_model", ["qwen3.5:9b-q4_K_M", "qwen3.5"])
|
||||
@pytest.mark.parametrize("json_mode", [False, True])
|
||||
def test_qwen35_requests_nonthinking_output_without_consuming_thinking(monkeypatch, ollama_model, json_mode):
|
||||
module = load_app_module(monkeypatch, ollama_model=ollama_model)
|
||||
captured = {}
|
||||
output = '{"score": 7}' if json_mode else "Rewritten CV text"
|
||||
|
||||
def fake_urlopen(req, timeout=None):
|
||||
captured["body"] = json.loads(req.data.decode("utf-8"))
|
||||
captured["timeout"] = timeout
|
||||
captured["url"] = req.full_url
|
||||
return _FakeResponse({
|
||||
"response": output if captured["body"].get("think") is False else "",
|
||||
"thinking": "Private reasoning must never become candidate text.",
|
||||
})
|
||||
|
||||
monkeypatch.setattr(module.urllib_request, "urlopen", fake_urlopen)
|
||||
result = module._ollama_generate_json("synthetic prompt") if json_mode else module._ollama_generate_text("synthetic prompt")
|
||||
|
||||
assert result == ({"score": 7} if json_mode else output)
|
||||
expected = {
|
||||
"model": ollama_model,
|
||||
"prompt": "synthetic prompt",
|
||||
"stream": False,
|
||||
"options": {"temperature": 0.1 if json_mode else 0.2},
|
||||
"think": False,
|
||||
}
|
||||
if json_mode:
|
||||
expected["format"] = "json"
|
||||
assert captured["body"] == expected
|
||||
assert captured["url"] == f"{module.OLLAMA_BASE_URL}/api/generate"
|
||||
assert captured["timeout"] == (120 if json_mode else 180)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("ollama_model", ["qwen2.5:7b", "qwen3:8b", "qwen3.5-plus:9b"])
|
||||
def test_other_ollama_model_payloads_remain_unchanged(monkeypatch, ollama_model):
|
||||
module = load_app_module(monkeypatch, ollama_model=ollama_model)
|
||||
captured = {}
|
||||
_install_fake_urlopen(monkeypatch, module, {"response": " Candidate text ", "thinking": "Reasoning"}, captured)
|
||||
|
||||
assert module._ollama_generate_text("synthetic prompt") == "Candidate text"
|
||||
assert captured["body"] == {
|
||||
"model": ollama_model,
|
||||
"prompt": "synthetic prompt",
|
||||
"stream": False,
|
||||
"options": {"temperature": 0.2},
|
||||
}
|
||||
|
||||
|
||||
def test_local_success_wins_even_when_external_fallback_is_permitted(monkeypatch):
|
||||
monkeypatch.setenv("AI_PROVIDER", "gemini")
|
||||
monkeypatch.setenv("GEMINI_API_KEY", "test-key")
|
||||
|
||||
Reference in New Issue
Block a user