diff --git a/apps/landing-page/__tests__/setup.test.ts b/apps/landing-page/__tests__/setup.test.ts index 00d1c0c6..1b5e64f8 100644 --- a/apps/landing-page/__tests__/setup.test.ts +++ b/apps/landing-page/__tests__/setup.test.ts @@ -8,7 +8,7 @@ describe('Next.js 16 Project Setup', () => { }); test('Next.js 16.x is installed and locked', () => { - expect(pkg.dependencies.next).toBe('16.1.6'); + expect(pkg.dependencies.next).toBe('16.2.3'); }); test('React 19.x is installed and locked', () => { diff --git a/apps/landing-page/package.json b/apps/landing-page/package.json index 89265a00..a5c557ca 100644 --- a/apps/landing-page/package.json +++ b/apps/landing-page/package.json @@ -32,7 +32,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.563.0", - "next": "16.1.6", + "next": "16.2.3", "next-intl": "^4.8.2", "next-themes": "^0.4.6", "prism-react-renderer": "^2.4.1", @@ -56,7 +56,7 @@ "axe-core": "^4.11.1", "babel-plugin-react-compiler": "1.0.0", "eslint": "^9", - "eslint-config-next": "16.1.6", + "eslint-config-next": "16.2.3", "happy-dom": "^20.8.8", "jest-axe": "^10.0.0", "madge": "^8.0.0", diff --git a/packages/claude-code-plugin/hooks/codingbuddy-hud.py b/packages/claude-code-plugin/hooks/codingbuddy-hud.py index c99ca493..3965259b 100644 --- a/packages/claude-code-plugin/hooks/codingbuddy-hud.py +++ b/packages/claude-code-plugin/hooks/codingbuddy-hud.py @@ -14,6 +14,7 @@ import os import sys from datetime import datetime, timezone +from typing import Optional # --- lib import bootstrap --- # statusLine entry script: sys.path insertion here is intentional so @@ -23,28 +24,40 @@ sys.path.insert(0, _LIB_DIR) # === test_hud.py compatibility re-exports — DO NOT REMOVE without coordinated test update === -# Defensive fallback: statusLine is a hot path invoked by Claude Code on -# every render. If any lib module is temporarily broken (e.g. mid-wave -# refactor), fall back to minimal inline implementations so the status -# bar still renders instead of crashing the Claude Code subprocess. +# Narrow the fallback to ImportError only: real logic bugs in lib modules +# (SyntaxError, NameError, AttributeError) must surface immediately instead +# of being silently swallowed by a catch-all. If a lib module fails to import +# entirely, the outer main() try/except at the bottom of this file still +# emits the minimal safe output via the BUDDY_FACE constant. try: from hud_buddy import BUDDY_FACE # canonical SSoT via tiny_actor_presets -except Exception: # pragma: no cover - defensive - BUDDY_FACE = "\u25d5\u203f\u25d5" # ◕‿◕ +except ImportError: # pragma: no cover - defensive + BUDDY_FACE = "◕‿◕" # minimal constant for safe-output path try: - from hud_rate_limits import format_rate_limits -except Exception: # pragma: no cover - defensive - def format_rate_limits(stdin_data: dict) -> str: # type: ignore[misc] - return "" + from hud_rate_limits import format_rate_limits # noqa: F401 re-exported for test_hud.py +except ImportError: # pragma: no cover - defensive + pass # main() catch-all handles absence try: from hud_version import get_fresh_version as _get_fresh_version # backcompat alias -except Exception: # pragma: no cover - defensive - def _get_fresh_version( # type: ignore[misc] - hud_state: dict, *, plugins_file: str = "" - ) -> str: - return hud_state.get("version", "") +except ImportError: # pragma: no cover - defensive + pass # main() catch-all handles absence + +# Wave 2-B velocity + Wave 2-C cache savings hot-path suffixes for the cost segment. +# Hoisted to module top per perf-1485 H1 so format_status_line avoids a +# sys.modules lookup on every render (~0.47μs saved per call). +try: + from hud_velocity import format_velocity_segment as _format_velocity_segment +except ImportError: # pragma: no cover - defensive + def _format_velocity_segment(stdin_data, hud_state=None): # type: ignore[misc] + return "" + +try: + from hud_cache_savings import format_cache_savings as _format_cache_savings +except ImportError: # pragma: no cover - defensive + def _format_cache_savings(stdin_data): # type: ignore[misc] + return "" # Agent eye glyphs from .ai-rules agent definitions. AGENT_GLYPHS = { @@ -411,17 +424,30 @@ def format_status_line( active_agent: str = "", *, plugins_file: str = "", + plugin_json_file: Optional[str] = None, ) -> str: """Format the statusLine output. Fallback order per field: - version → installed_plugins.json > hud-state.version + version → installed_plugins.json > plugin.json > hud-state.version cost → stdin cost.total_cost_usd > estimate_cost() duration → stdin cost.total_duration_ms > hud-state sessionStartTimestamp agent → stdin agent.name > hud_state.activeAgent > active_agent param model → stdin model.display_name > model.id + + Args: + plugin_json_file: Wave 1-A control for the local ``plugin.json`` + fallback. ``None`` (default) disables tier-2 — matches the + hud_version contract for backwards compatibility. ``""`` + enables the dev-install default path. A non-empty string + overrides the path for tests. ``main()`` passes ``""`` in + production so statusLine always reflects a fresh version. """ - version = _get_fresh_version(hud_state, plugins_file=plugins_file) + version = _get_fresh_version( + hud_state, + plugins_file=plugins_file, + plugin_json_file=plugin_json_file, + ) mode = hud_state.get("currentMode") mode_label = mode if mode else "Ready" @@ -482,7 +508,12 @@ def main(): env_agent = os.environ.get("CODINGBUDDY_ACTIVE_AGENT", "") - output = format_status_line(stdin_data, hud_state, env_agent) + # Pass plugin_json_file="" to enable Wave 1-A dev-install + # plugin.json fallback. Tests opt out by omitting this kwarg + # and relying on the Optional[str]=None default. + output = format_status_line( + stdin_data, hud_state, env_agent, plugin_json_file="" + ) print(output) except Exception: print(f"{BUDDY_FACE} CodingBuddy") diff --git a/packages/claude-code-plugin/hooks/lib/hud_version.py b/packages/claude-code-plugin/hooks/lib/hud_version.py index 9361a0bc..35dac186 100644 --- a/packages/claude-code-plugin/hooks/lib/hud_version.py +++ b/packages/claude-code-plugin/hooks/lib/hud_version.py @@ -1,48 +1,96 @@ -"""Version resolution for CodingBuddy statusLine (#1326). +"""Version resolution for CodingBuddy statusLine (#1326, #1464 Wave 1-A). -Wave 0 extracts the plugin-version fallback logic from -``codingbuddy-hud.py`` so Wave 1-A can extend the resolution chain -without touching the monolith. +Wave 1-A strengthens the version resolution chain with a local +``plugin.json`` fallback so the HUD never shows a stale snapshot after +a plugin update even when ``installed_plugins.json`` is missing or +cannot be parsed. The public entry point is :func:`get_fresh_version`. ``codingbuddy-hud`` calls it internally from ``format_status_line``; callers pass the -current ``hud_state`` dict and an optional ``plugins_file`` override -used by the test-suite to point at a fixture path. +current ``hud_state`` dict and optional path overrides used by tests. -Behavior-preserving contract (mirrors the original monolith helper): +Resolution chain (first non-empty result wins): -1. Attempt to read the freshest version from - ``installed_plugins.json`` via - :func:`hud_helpers.read_installed_version`. -2. On success, return that value. -3. On any failure (missing file, parse error, unexpected exception), - fall back to ``hud_state.get("version", "")``. +1. ``installed_plugins.json`` — authoritative after ``/plugin update`` + (global Claude Code plugin registry). +2. ``../.claude-plugin/plugin.json`` — deterministic via ``__file__`` + relative path, authoritative for dev installs where the plugin is + running from a git checkout. +3. ``hud_state.get("version", "")`` — snapshot written at session + start (may be stale, last resort). """ from __future__ import annotations -from typing import Any, Dict +import json +import os +from typing import Any, Dict, Optional + + +def _default_plugin_json_path() -> str: + """Resolve ``plugin.json`` relative to this module's location. + + ``hud_version.py`` lives at + ``packages/claude-code-plugin/hooks/lib/hud_version.py``. + ``plugin.json`` lives at + ``packages/claude-code-plugin/.claude-plugin/plugin.json``. + So we walk up two levels (``lib/`` -> ``hooks/`` -> package root) + and then descend into ``.claude-plugin/``. + """ + here = os.path.dirname(os.path.abspath(__file__)) + return os.path.normpath( + os.path.join(here, "..", "..", ".claude-plugin", "plugin.json") + ) + + +def _read_local_plugin_json(path: str) -> str: + """Read ``plugin.json`` and return its ``version`` field. + + Returns an empty string on any failure (missing file, parse error, + missing key). Never raises — caller must be able to skip silently. + """ + try: + with open(path, "r", encoding="utf-8") as f: + data = json.load(f) + v = data.get("version") + return v if isinstance(v, str) else "" + except Exception: + return "" def get_fresh_version( hud_state: Dict[str, Any], *, plugins_file: str = "", + plugin_json_file: Optional[str] = None, ) -> str: """Return the freshest known plugin version string. Args: - hud_state: Current HUD state dict (supplies the fallback + hud_state: Current HUD state dict (supplies the final fallback ``version`` field). plugins_file: Optional override for the ``installed_plugins.json`` path, used by tests. + plugin_json_file: Local ``plugin.json`` fallback control: + + * ``None`` (default) — tier-2 fallback is **disabled**. + Only ``installed_plugins.json`` and ``hud_state`` are + consulted. This keeps the signature backwards-compatible + with callers that do not opt in. + * ``""`` — use the default dev-install path resolved from + ``__file__`` (i.e. ``../.claude-plugin/plugin.json``). + ``format_status_line`` passes this in production so + statusLine always reflects a fresh local version. + * non-empty string — treat as an explicit file path + override, used by the test suite for fixture files. Notes: ``hud_helpers`` is imported lazily inside the function body to preserve the hot-path resilience of the original monolith. If ``hud_helpers`` is temporarily broken (e.g. mid-wave refactor), - the statusLine still renders via the ``hud_state`` fallback - instead of crashing at module load. + the statusLine still renders via the later fallbacks instead + of crashing at module load. """ + # 1. Global installed_plugins.json (authoritative after /plugin update) try: from hud_helpers import read_installed_version # lazy for resilience kwargs = {"plugins_file": plugins_file} if plugins_file else {} @@ -51,4 +99,13 @@ def get_fresh_version( return fresh except Exception: pass + + # 2. Local plugin.json (opt-in: None disables this tier entirely) + if plugin_json_file is not None: + path_to_try = plugin_json_file or _default_plugin_json_path() + local = _read_local_plugin_json(path_to_try) + if local: + return local + + # 3. hud-state snapshot (may be stale, last resort) return hud_state.get("version", "") diff --git a/packages/claude-code-plugin/tests/test_hud_version.py b/packages/claude-code-plugin/tests/test_hud_version.py index 46b1b8b8..a8680103 100644 --- a/packages/claude-code-plugin/tests/test_hud_version.py +++ b/packages/claude-code-plugin/tests/test_hud_version.py @@ -44,6 +44,104 @@ def test_reads_installed_plugins_file_when_present(tmp_path): assert result == "7.7.7" +def test_plugin_json_fallback_when_installed_plugins_missing(tmp_path): + """Wave 1-A: plugin.json is the second-tier fallback. + + When installed_plugins.json is missing but a local plugin.json + exists, the returned version must be the plugin.json value. + """ + missing_plugins = tmp_path / "no_plugins.json" + plugin_json = tmp_path / "plugin.json" + plugin_json.write_text('{"version": "8.8.8"}', encoding="utf-8") + result = hud_version.get_fresh_version( + {"version": "stale"}, + plugins_file=str(missing_plugins), + plugin_json_file=str(plugin_json), + ) + assert result == "8.8.8" + + +def test_installed_plugins_wins_over_plugin_json(tmp_path): + """Wave 1-A: installed_plugins.json (tier 1) beats plugin.json (tier 2).""" + plugins = tmp_path / "installed_plugins.json" + plugins.write_text( + '{"plugins": {"codingbuddy@dev": [{"version": "tier-1"}]}}', + encoding="utf-8", + ) + plugin_json = tmp_path / "plugin.json" + plugin_json.write_text('{"version": "tier-2"}', encoding="utf-8") + result = hud_version.get_fresh_version( + {"version": "tier-3"}, + plugins_file=str(plugins), + plugin_json_file=str(plugin_json), + ) + assert result == "tier-1" + + +def test_plugin_json_beats_hud_state(tmp_path): + """Wave 1-A: plugin.json (tier 2) beats hud_state.version (tier 3).""" + missing_plugins = tmp_path / "no_plugins.json" + plugin_json = tmp_path / "plugin.json" + plugin_json.write_text('{"version": "tier-2"}', encoding="utf-8") + result = hud_version.get_fresh_version( + {"version": "tier-3-stale"}, + plugins_file=str(missing_plugins), + plugin_json_file=str(plugin_json), + ) + assert result == "tier-2" + + +def test_all_fallbacks_fail_returns_hud_state_version(tmp_path): + """Wave 1-A: if both files are absent, fall through to hud_state.""" + missing_plugins = tmp_path / "no_plugins.json" + missing_plugin_json = tmp_path / "no_plugin.json" + result = hud_version.get_fresh_version( + {"version": "9.9.9"}, + plugins_file=str(missing_plugins), + plugin_json_file=str(missing_plugin_json), + ) + assert result == "9.9.9" + + +def test_plugin_json_malformed_skipped(tmp_path): + """Wave 1-A: malformed plugin.json must not crash — skip to hud_state.""" + missing_plugins = tmp_path / "no_plugins.json" + bad_plugin_json = tmp_path / "plugin.json" + bad_plugin_json.write_text("this is not json", encoding="utf-8") + result = hud_version.get_fresh_version( + {"version": "fallback"}, + plugins_file=str(missing_plugins), + plugin_json_file=str(bad_plugin_json), + ) + assert result == "fallback" + + +def test_plugin_json_missing_version_key_skipped(tmp_path): + """Wave 1-A: plugin.json without version key skips to hud_state.""" + missing_plugins = tmp_path / "no_plugins.json" + plugin_json = tmp_path / "plugin.json" + plugin_json.write_text('{"name": "codingbuddy"}', encoding="utf-8") + result = hud_version.get_fresh_version( + {"version": "fallback"}, + plugins_file=str(missing_plugins), + plugin_json_file=str(plugin_json), + ) + assert result == "fallback" + + +def test_default_plugin_json_path_resolves_to_real_file(): + """Wave 1-A: __file__-relative default path must point at the real + .claude-plugin/plugin.json in the repo so dev installs work.""" + import pathlib + path = hud_version._default_plugin_json_path() + assert os.path.isfile(path), ( + f"Expected plugin.json at {path}; hud_version default path is wrong." + ) + # Smoke check: the file is parseable and has a version field + version = hud_version._read_local_plugin_json(path) + assert version, "plugin.json exists but version field is empty" + + def test_import_does_not_read_real_plugins_file(monkeypatch, tmp_path): """Lock: module load must not touch ~/.claude/plugins/installed_plugins.json.""" fake_home = tmp_path / "fake_home" diff --git a/yarn.lock b/yarn.lock index 1eac05b0..76937473 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1561,6 +1561,13 @@ __metadata: languageName: node linkType: hard +"@next/env@npm:16.2.3": + version: 16.2.3 + resolution: "@next/env@npm:16.2.3" + checksum: 10c0/56c3fee8ea226efe59ef065e054380f872c00c45c9fe4475eaa45f80773c3c1adc3ead3ccdd77447d3c1aeb4b3004aaaa033dd4a100d3e572fd01b83f992dde8 + languageName: node + linkType: hard + "@next/eslint-plugin-next@npm:16.1.6": version: 16.1.6 resolution: "@next/eslint-plugin-next@npm:16.1.6" @@ -1570,6 +1577,15 @@ __metadata: languageName: node linkType: hard +"@next/eslint-plugin-next@npm:16.2.3": + version: 16.2.3 + resolution: "@next/eslint-plugin-next@npm:16.2.3" + dependencies: + fast-glob: "npm:3.3.1" + checksum: 10c0/be881aa89e0840ab60455b07a2bb9ec0d686c664a0d91e8ca815797a65ca71d7bd79d186b0df5b6892c2bf57bd07fa05421cd93e2812dfeaedfad5ed9fd1023e + languageName: node + linkType: hard + "@next/swc-darwin-arm64@npm:16.1.6": version: 16.1.6 resolution: "@next/swc-darwin-arm64@npm:16.1.6" @@ -1577,6 +1593,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-darwin-arm64@npm:16.2.3": + version: 16.2.3 + resolution: "@next/swc-darwin-arm64@npm:16.2.3" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@next/swc-darwin-x64@npm:16.1.6": version: 16.1.6 resolution: "@next/swc-darwin-x64@npm:16.1.6" @@ -1584,6 +1607,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-darwin-x64@npm:16.2.3": + version: 16.2.3 + resolution: "@next/swc-darwin-x64@npm:16.2.3" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@next/swc-linux-arm64-gnu@npm:16.1.6": version: 16.1.6 resolution: "@next/swc-linux-arm64-gnu@npm:16.1.6" @@ -1591,6 +1621,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-linux-arm64-gnu@npm:16.2.3": + version: 16.2.3 + resolution: "@next/swc-linux-arm64-gnu@npm:16.2.3" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + "@next/swc-linux-arm64-musl@npm:16.1.6": version: 16.1.6 resolution: "@next/swc-linux-arm64-musl@npm:16.1.6" @@ -1598,6 +1635,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-linux-arm64-musl@npm:16.2.3": + version: 16.2.3 + resolution: "@next/swc-linux-arm64-musl@npm:16.2.3" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + "@next/swc-linux-x64-gnu@npm:16.1.6": version: 16.1.6 resolution: "@next/swc-linux-x64-gnu@npm:16.1.6" @@ -1605,6 +1649,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-linux-x64-gnu@npm:16.2.3": + version: 16.2.3 + resolution: "@next/swc-linux-x64-gnu@npm:16.2.3" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + "@next/swc-linux-x64-musl@npm:16.1.6": version: 16.1.6 resolution: "@next/swc-linux-x64-musl@npm:16.1.6" @@ -1612,6 +1663,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-linux-x64-musl@npm:16.2.3": + version: 16.2.3 + resolution: "@next/swc-linux-x64-musl@npm:16.2.3" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + "@next/swc-win32-arm64-msvc@npm:16.1.6": version: 16.1.6 resolution: "@next/swc-win32-arm64-msvc@npm:16.1.6" @@ -1619,6 +1677,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-win32-arm64-msvc@npm:16.2.3": + version: 16.2.3 + resolution: "@next/swc-win32-arm64-msvc@npm:16.2.3" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@next/swc-win32-x64-msvc@npm:16.1.6": version: 16.1.6 resolution: "@next/swc-win32-x64-msvc@npm:16.1.6" @@ -1626,6 +1691,13 @@ __metadata: languageName: node linkType: hard +"@next/swc-win32-x64-msvc@npm:16.2.3": + version: 16.2.3 + resolution: "@next/swc-win32-x64-msvc@npm:16.2.3" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -5276,6 +5348,15 @@ __metadata: languageName: node linkType: hard +"baseline-browser-mapping@npm:^2.9.19": + version: 2.10.17 + resolution: "baseline-browser-mapping@npm:2.10.17" + bin: + baseline-browser-mapping: dist/cli.cjs + checksum: 10c0/e792a92a6b206521681e3ab3a72770023f74a3274450bfe11ba55a075ba26f5820d5d2d02d92e25224b8d01e327b78fbf3e116bdc6ac74b3d9c52f5e3f4a048a + languageName: node + linkType: hard + "better-sqlite3@npm:^11.9.1": version: 11.10.0 resolution: "better-sqlite3@npm:11.10.0" @@ -7028,6 +7109,29 @@ __metadata: languageName: node linkType: hard +"eslint-config-next@npm:16.2.3": + version: 16.2.3 + resolution: "eslint-config-next@npm:16.2.3" + dependencies: + "@next/eslint-plugin-next": "npm:16.2.3" + eslint-import-resolver-node: "npm:^0.3.6" + eslint-import-resolver-typescript: "npm:^3.5.2" + eslint-plugin-import: "npm:^2.32.0" + eslint-plugin-jsx-a11y: "npm:^6.10.0" + eslint-plugin-react: "npm:^7.37.0" + eslint-plugin-react-hooks: "npm:^7.0.0" + globals: "npm:16.4.0" + typescript-eslint: "npm:^8.46.0" + peerDependencies: + eslint: ">=9.0.0" + typescript: ">=3.3.1" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/c6fd3accadb53c636f034baf4363d22847bf824c8ca1ecfa8047a4eee7882d156e75f60f37098357c7ae07e646dfaa23a176336abd3c74aa9a2df61aee984653 + languageName: node + linkType: hard + "eslint-config-prettier@npm:10.1.8": version: 10.1.8 resolution: "eslint-config-prettier@npm:10.1.8" @@ -8942,12 +9046,12 @@ __metadata: class-variance-authority: "npm:^0.7.1" clsx: "npm:^2.1.1" eslint: "npm:^9" - eslint-config-next: "npm:16.1.6" + eslint-config-next: "npm:16.2.3" happy-dom: "npm:^20.8.8" jest-axe: "npm:^10.0.0" lucide-react: "npm:^0.563.0" madge: "npm:^8.0.0" - next: "npm:16.1.6" + next: "npm:16.2.3" next-intl: "npm:^4.8.2" next-themes: "npm:^0.4.6" prettier: "npm:^3.4.2" @@ -9858,6 +9962,66 @@ __metadata: languageName: node linkType: hard +"next@npm:16.2.3": + version: 16.2.3 + resolution: "next@npm:16.2.3" + dependencies: + "@next/env": "npm:16.2.3" + "@next/swc-darwin-arm64": "npm:16.2.3" + "@next/swc-darwin-x64": "npm:16.2.3" + "@next/swc-linux-arm64-gnu": "npm:16.2.3" + "@next/swc-linux-arm64-musl": "npm:16.2.3" + "@next/swc-linux-x64-gnu": "npm:16.2.3" + "@next/swc-linux-x64-musl": "npm:16.2.3" + "@next/swc-win32-arm64-msvc": "npm:16.2.3" + "@next/swc-win32-x64-msvc": "npm:16.2.3" + "@swc/helpers": "npm:0.5.15" + baseline-browser-mapping: "npm:^2.9.19" + caniuse-lite: "npm:^1.0.30001579" + postcss: "npm:8.4.31" + sharp: "npm:^0.34.5" + styled-jsx: "npm:5.1.6" + peerDependencies: + "@opentelemetry/api": ^1.1.0 + "@playwright/test": ^1.51.1 + babel-plugin-react-compiler: "*" + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + dependenciesMeta: + "@next/swc-darwin-arm64": + optional: true + "@next/swc-darwin-x64": + optional: true + "@next/swc-linux-arm64-gnu": + optional: true + "@next/swc-linux-arm64-musl": + optional: true + "@next/swc-linux-x64-gnu": + optional: true + "@next/swc-linux-x64-musl": + optional: true + "@next/swc-win32-arm64-msvc": + optional: true + "@next/swc-win32-x64-msvc": + optional: true + sharp: + optional: true + peerDependenciesMeta: + "@opentelemetry/api": + optional: true + "@playwright/test": + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + bin: + next: dist/bin/next + checksum: 10c0/8a9d27fc773d69f7f471cf1a23bde2ab2950e0411ef3e0d5c1664ed9654e94c3304eae1c4283ec0fa4e70e7b3f4416913350e118e0c18e8b055693dc5d021883 + languageName: node + linkType: hard + "node-abi@npm:^3.3.0": version: 3.89.0 resolution: "node-abi@npm:3.89.0" @@ -11302,7 +11466,7 @@ __metadata: languageName: node linkType: hard -"sharp@npm:^0.34.4": +"sharp@npm:^0.34.4, sharp@npm:^0.34.5": version: 0.34.5 resolution: "sharp@npm:0.34.5" dependencies: