diff --git a/CHANGELOG.md b/CHANGELOG.md index daffb1584..d193d3aac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [1.6.0-rc.12] — 2026-08-04 + +### Changed + +- **Routine dependency maintenance across the root tooling, demo, UI, and website workspaces** ([#653](https://github.com/CodesWhat/drydock/pull/653), [#654](https://github.com/CodesWhat/drydock/pull/654), [#655](https://github.com/CodesWhat/drydock/pull/655), [#656](https://github.com/CodesWhat/drydock/pull/656)). The dependency-version guard's Next.js check became a 16.x floor instead of an exact pin so routine patch bumps stop tripping it ([#664](https://github.com/CodesWhat/drydock/pull/664)). +- **Crowdin translation sync** ([#665](https://github.com/CodesWhat/drydock/pull/665)): one French container-component string corrected. + +### Fixed + +- **"Agent Mismatch" no longer appears in the container list/SSE display during the brief window an agent's docker/dockercompose trigger is still (re)registering** ([#605](https://github.com/CodesWhat/drydock/issues/605)). Eligibility is recomputed live on every read, and `AgentClient._doHandshake()` deregisters the agent's components before awaiting the `/api/triggers` fetch and re-register. A read in that window found zero triggers for the agent and `computeUpdateEligibility` raised a hard `agent-mismatch` blocker, disabling the Update button, even though nothing was actually misconfigured — the condition self-corrected once registration finished. `agent-mismatch` now downgrades to a soft blocker (button stays enabled) on display surfaces whenever the container's own agent is mid-registration, per the new `AgentClient.isRegisteringComponents` flag (true only for the deregister→re-register span, not the whole reconnect backoff). Update **admission** (`app/updates/request-update.ts`) is unaffected and stays hard/fail-closed throughout, so an update can never be enqueued through a wrong-agent trigger during that window. +- **WebSocket log streams no longer reject anonymous-auth sessions** ([#636](https://github.com/CodesWhat/drydock/issues/636)). Both WS upgrade paths — the system log stream and the container log stream — gated on `isAuthenticatedSession()` requiring `session.passport.user`, which `passport-anonymous` never sets, so under `DD_ANONYMOUS_AUTH_CONFIRM=true` the log stream WebSocket always rejected the upgrade even though every REST endpoint worked. `isAuthenticatedSession` now also accepts the session when anonymous authentication is the registered mode. +- **Maturity clock: swallowed auth errors surfaced, per-container threshold respected** ([#604](https://github.com/CodesWhat/drydock/issues/604)). `getImagePublishedAt` failures — including GHCR/LSCR 401/403 auth errors — now log at `warn` instead of `debug`, so the maturity gate's silent fallback from the registry `publishedAt` to `updateDetectedAt` is no longer invisible. `getRawUpdateMaturityLevel` (`app/model/container.ts`) and `getContainerMaturityLevel` (`app/api/container/maturity-filter.ts`) now resolve each container's own `updatePolicy.maturityMinAgeDays` before falling back to the global `DD_UI_MATURITY_THRESHOLD_DAYS`, matching the gate's own `isUpdateSuppressed`/`isMaturityGatePending` logic so the hot/mature badge can no longer disagree with the gate in the same API response. +- **Container start/stop/restart/rollback return an explicit 501 instead of an ambiguous 404 for agent containers without lifecycle transport** ([#637](https://github.com/CodesWhat/drydock/issues/637)). `POST /:id/start|stop|restart` and `POST /:id/rollback` returned a bare 404 `No docker trigger found for this container` whenever the lookup missed, indistinguishable from "container not found" — for agent-owned containers this was the only signal the UI got. That lookup miss now returns 501 naming the likely cause (the agent's connection typically hasn't advertised `usesControllerDockerTransport`) when `container.agent` is set; non-agent containers still get the existing 404. This complements the native-transport support that shipped in rc.11 via [#651](https://github.com/CodesWhat/drydock/pull/651), which closed #637's core gap — this is the remaining explicit-error half. + +### Security + +- **`brace-expansion`, `ip-address`, and `fast-uri` overrides advanced to patched releases.** `brace-expansion` moved to 5.0.9 in `app/`, `ui/`, and `e2e/` (CVE-2026-69152, [GHSA-rgw5-rvv9-x895](https://github.com/advisories/GHSA-rgw5-rvv9-x895)); `ip-address` moved to 10.3.1 in `app/` (CVE-2026-54272, CVE-2026-69192, CVE-2026-69198), pulled in transitively via `express-rate-limit` and `mqtt` → `socks`; `fast-uri` advanced from 4.1.1 to 4.1.2 in `app/` and `ui/` (host confusion via backslash authority introducer, CVE-2026-18446, [GHSA-7p8r-x3mc-p8w7](https://github.com/advisories/GHSA-7p8r-x3mc-p8w7), superseding [#658](https://github.com/CodesWhat/drydock/pull/658)). + ## [1.6.0-rc.11] — 2026-08-01 ### Added @@ -2308,7 +2326,8 @@ Remaining upstream-only changes (not ported — not applicable to drydock): | Fix codeberg tests | Covered by drydock's own tests | | Update changelog | Upstream-specific | -[Unreleased]: https://github.com/CodesWhat/drydock/compare/v1.6.0-rc.11...HEAD +[Unreleased]: https://github.com/CodesWhat/drydock/compare/v1.6.0-rc.12...HEAD +[1.6.0-rc.12]: https://github.com/CodesWhat/drydock/compare/v1.6.0-rc.11...v1.6.0-rc.12 [1.6.0-rc.11]: https://github.com/CodesWhat/drydock/compare/v1.6.0-rc.10...v1.6.0-rc.11 [1.6.0-rc.10]: https://github.com/CodesWhat/drydock/compare/v1.6.0-rc.9...v1.6.0-rc.10 [1.6.0-rc.9]: https://github.com/CodesWhat/drydock/compare/v1.6.0-rc.8...v1.6.0-rc.9 diff --git a/README.md b/README.md index b4ba72192..30153d62e 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@

- Version + Version Multi-arch License AGPL-3.0
@@ -178,6 +178,17 @@ See the [Quick Start guide](https://getdrydock.com/docs/quickstart) for Docker C

🆕 Recent Updates

+v1.6.0-rc.12 highlights + +- **Security dependency refresh** — `brace-expansion` 5.0.9 (app/UI/e2e, CVE-2026-69152), `ip-address` 10.3.1 (app runtime, CVE-2026-54272/-69192/-69198), and `fast-uri` 4.1.2 (app/UI, CVE-2026-18446). ([#659](https://github.com/CodesWhat/drydock/pull/659)) +- **Maturity clock** — the hot/mature badge resolves per-container `updatePolicy.maturityMinAgeDays` before the global threshold, matching the gate, and registry publish-date failures log at `warn` instead of disappearing at `debug`. ([#604](https://github.com/CodesWhat/drydock/issues/604)) +- **Agent registration grace** — transient `agent-mismatch`/`no-update-trigger-configured` blockers soften on display surfaces while an agent's components re-register; admission stays fail-closed. ([#605](https://github.com/CodesWhat/drydock/issues/605)) +- **WS log streams + anonymous auth** — log-stream WebSocket upgrades accept sessions when anonymous authentication is the registered mode. ([#636](https://github.com/CodesWhat/drydock/issues/636)) +- **Explicit 501s** — lifecycle actions on agent containers without controller Docker transport return 501 naming the cause instead of an ambiguous 404. ([#637](https://github.com/CodesWhat/drydock/issues/637)) + +
+ +
v1.6.0-rc.11 highlights - **Portwing transport** — Portwing 0.9.0's exact `transport=docker-api`, `execution=controller`, `events=portwing` marker now routes native registry checks, single/batch updates, start/stop/restart, update previews, and backup rollbacks through authenticated Standard HTTP or Edge request/response/stream transport. Portwing remains the lifecycle-event source, and raw inventory cannot erase controller-enriched update results. ([#632](https://github.com/CodesWhat/drydock/issues/632), [#637](https://github.com/CodesWhat/drydock/issues/637), [Portwing #76](https://github.com/CodesWhat/portwing/issues/76)) diff --git a/app/agent/AgentClient.test.ts b/app/agent/AgentClient.test.ts index 31f80086b..9a43e436a 100644 --- a/app/agent/AgentClient.test.ts +++ b/app/agent/AgentClient.test.ts @@ -743,6 +743,50 @@ describe('AgentClient', () => { expect(client.isConnected).toBe(true); }); + test('sets isRegisteringComponents for the deregister -> re-register span and resets after completion (#605)', async () => { + const observedDuringDeregister: boolean[] = []; + const observedDuringWatcherFetch: boolean[] = []; + const observedDuringTriggerFetch: boolean[] = []; + + vi.mocked(registry.deregisterAgentComponents).mockImplementationOnce(async () => { + observedDuringDeregister.push(client.isRegisteringComponents); + }); + + axios.get + .mockResolvedValueOnce({ data: [] }) // containers + .mockImplementationOnce(async () => { + observedDuringWatcherFetch.push(client.isRegisteringComponents); + return { data: [] }; + }) + .mockImplementationOnce(async () => { + observedDuringTriggerFetch.push(client.isRegisteringComponents); + return { data: [] }; + }); + + storeContainer.getContainers.mockReturnValue([]); + + expect(client.isRegisteringComponents).toBe(false); + await client.handshake(); + + expect(observedDuringDeregister).toEqual([true]); + expect(observedDuringWatcherFetch).toEqual([true]); + expect(observedDuringTriggerFetch).toEqual([true]); + expect(client.isRegisteringComponents).toBe(false); + }); + + test('resets isRegisteringComponents to false when a mid-handshake step throws (#605)', async () => { + axios.get.mockResolvedValueOnce({ data: [] }); // containers succeeds + vi.mocked(registry.deregisterAgentComponents).mockRejectedValueOnce( + new Error('deregister failed'), + ); + storeContainer.getContainers.mockReturnValue([]); + + await expect(client.handshake()).rejects.toThrow('deregister failed'); + + expect(client.isRegisteringComponents).toBe(false); + expect(client.isConnected).toBe(false); + }); + test('should emit agent-connected when transitioning to connected state', async () => { axios.get .mockResolvedValueOnce({ data: [] }) @@ -1120,6 +1164,13 @@ describe('AgentClient', () => { expect(spy).toHaveBeenCalled(); }); + test('should reset isRegisteringComponents to false on disconnect (#605)', () => { + client.isConnected = true; + client.isRegisteringComponents = true; + client.scheduleReconnect(1000); + expect(client.isRegisteringComponents).toBe(false); + }); + test('should not schedule duplicate reconnects', () => { const spy = vi.spyOn(client, 'startSse').mockImplementation(() => {}); client.scheduleReconnect(1000); @@ -7575,10 +7626,23 @@ describe('AgentClient', () => { }); describe('handleComponentSync (edge agent public shim)', () => { - test('deregisters agent components and re-registers watchers and triggers', async () => { + test('keeps isRegisteringComponents true through edge component replacement and resets it after success (#605)', async () => { const watchers = [{ type: 'docker', name: 'local', configuration: {} }]; const triggers = [{ type: 'mock', name: 'update', configuration: {} }]; + const observedSteps: string[] = []; + vi.mocked(registry.deregisterAgentComponents).mockImplementationOnce(async () => { + observedSteps.push(`deregister:${client.isRegisteringComponents}`); + }); + vi.mocked(registry.registerComponent) + .mockImplementationOnce(async (component) => { + observedSteps.push(`${component.kind}:${client.isRegisteringComponents}`); + }) + .mockImplementationOnce(async (component) => { + observedSteps.push(`${component.kind}:${client.isRegisteringComponents}`); + }); + + expect(client.isRegisteringComponents).toBe(false); await client.handleComponentSync(watchers, triggers); expect(registry.deregisterAgentComponents).toHaveBeenCalledWith('test-agent'); @@ -7588,6 +7652,38 @@ describe('AgentClient', () => { expect(registry.registerComponent).toHaveBeenCalledWith( expect.objectContaining({ kind: 'trigger', provider: 'mock', name: 'update' }), ); + expect(observedSteps).toEqual(['deregister:true', 'watcher:true', 'trigger:true']); + expect(client.isRegisteringComponents).toBe(false); + }); + + test('resets isRegisteringComponents when edge watcher registration throws (#605)', async () => { + const observedDuringDeregister: boolean[] = []; + let observedDuringWatcherRegistration = false; + + vi.mocked(registry.deregisterAgentComponents) + .mockImplementationOnce(async () => { + observedDuringDeregister.push(client.isRegisteringComponents); + }) + .mockImplementationOnce(async () => { + observedDuringDeregister.push(client.isRegisteringComponents); + }); + vi.mocked(registry.registerComponent).mockImplementationOnce(async () => { + observedDuringWatcherRegistration = client.isRegisteringComponents; + throw new Error('watcher registration failed'); + }); + + await expect( + client.handleComponentSync( + [{ type: 'docker', name: 'local', configuration: {} }], + [{ type: 'mock', name: 'update', configuration: {} }], + ), + ).rejects.toThrow('watcher registration failed'); + + expect(observedDuringDeregister).toEqual([true, true]); + expect(observedDuringWatcherRegistration).toBe(true); + expect(registry.deregisterAgentComponents).toHaveBeenCalledTimes(2); + expect(registry.registerComponent).toHaveBeenCalledTimes(1); + expect(client.isRegisteringComponents).toBe(false); }); test('works with empty watchers and triggers (no-op)', async () => { @@ -7869,6 +7965,26 @@ describe('AgentClient', () => { }); describe('Portwing Docker API transport', () => { + test('reports whether a watcher uses controller Docker transport', async () => { + await client.handleComponentSync( + [ + { + type: 'docker', + name: 'docker', + configuration: { + transport: 'docker-api', + execution: 'controller', + events: 'portwing', + }, + }, + ], + [], + ); + + expect(client.hasControllerDockerTransport('docker')).toBe(true); + expect(client.hasControllerDockerTransport('missing')).toBe(false); + }); + test('component sync synthesizes docker/update only for a controller Docker transport watcher', async () => { const watcher = { type: 'docker', diff --git a/app/agent/AgentClient.ts b/app/agent/AgentClient.ts index e3f0ff995..082397988 100644 --- a/app/agent/AgentClient.ts +++ b/app/agent/AgentClient.ts @@ -276,6 +276,20 @@ export class AgentClient { // Parsed once at construction when authmode is 'ed25519'; undefined in token mode. private readonly ed25519PrivateKey?: KeyObject; public isConnected: boolean; + /** + * True only while this agent's components are being replaced — the span + * inside `_doHandshake()` (and the equivalent edge-path + * `handleComponentSync()`) between deregistering and finishing the + * re-registration (watchers, then triggers). + * Eligibility display surfaces (container list, SSE enrichment) + * read this to soften `agent-mismatch` / `no-update-trigger-configured` to a + * soft blocker during that transient window — see issue #605. Always reset + * in a `finally` so a handshake failure, or a disconnect via + * `scheduleReconnect()`, reverts to the hard-blocker default. Admission + * (`app/updates/request-update.ts`) never reads this field and stays + * fail-closed throughout. + */ + public isRegisteringComponents: boolean; public info: AgentClientRuntimeInfo; private reconnectTimer: NodeJS.Timeout | null; private reconnectAttempts: number; @@ -310,6 +324,7 @@ export class AgentClient { } this.isConnected = false; + this.isRegisteringComponents = false; this.info = {}; this.reconnectTimer = null; this.reconnectAttempts = 0; @@ -331,6 +346,15 @@ export class AgentClient { return this.watcherSnapshotCache.get(watcherSnapshotCacheKey(watcherType, watcherName)); } + /** + * Whether the given watcher on this agent advertises controller Docker + * transport, i.e. lifecycle actions (start/stop/restart/rollback) execute + * locally on the controller instead of being proxied to the agent. + */ + hasControllerDockerTransport(watcherName: string): boolean { + return this.controllerDockerTransportWatchers.has(watcherName); + } + private parseBaseUrl(): URL { // Validate the URL to prevent request forgery (CodeQL js/request-forgery) const parsed = new URL(this.getCandidateUrl()); @@ -936,53 +960,62 @@ export class AgentClient { const containers = response.data; this.log.info(`Handshake successful. Received ${containers.length} containers.`); - // Unregister existing components for this agent - await registry.deregisterAgentComponents(this.name); - - // Fetch and register watchers + // isRegisteringComponents is true for the entire deregister → re-register + // span below, including the container-inventory apply that happens + // between watcher and trigger registration. The `finally` guarantees it + // reverts to false whether registration succeeds or this method throws. + this.isRegisteringComponents = true; try { - const responseWatchers = await axios.get( - `${this.baseUrl}/api/watchers`, - this.buildRequestConfig('GET', '/api/watchers'), - ); - await this.registerAgentWatchersTransactional(responseWatchers.data); - // Only transfer update-enrichment ownership after every controller-side - // watcher/delegate has registered successfully. - this.setControllerDockerTransportWatchers(responseWatchers.data); - this.seedWatcherSnapshotCacheFromHandshake(responseWatchers.data); - } catch (error: unknown) { - this.log.warn(`Failed to fetch/register watchers: ${getErrorMessage(error)}`); - } - - // Apply inventory only after watcher registration. Controller-transport - // descriptors change which fields are authoritative: Portwing owns live - // runtime state, while Drydock's native watcher owns update enrichment. - await this.processAuthoritativeContainers(containers); - // A zero-container handshake is ambiguous: it could mean the agent has - // no running containers, or its in-memory store is fresh-empty after a - // restart while docker still has running containers. Defer the prune - // until the first authoritative watcher snapshot arrives — that path is - // unambiguous because the snapshot is only emitted after a successful - // enumeration with no enrichment errors (#362, #386 / d02080ae). - // Pruning here would wipe last-known state for an agent that's about to - // re-populate it in seconds via its first watch cycle. - if (containers.length > 0) { - this.pruneOldContainers(containers); - } else if (this.hasConnectedOnce) { - this.log.warn( - 'Handshake returned 0 containers; preserving last-known state until the first watch cycle completes', - ); - } + // Unregister existing components for this agent + await registry.deregisterAgentComponents(this.name); - // Fetch and register triggers - try { - const responseTriggers = await axios.get( - `${this.baseUrl}/api/triggers`, - this.buildRequestConfig('GET', '/api/triggers'), - ); - await this.registerAgentComponents('trigger', responseTriggers.data); - } catch (error: unknown) { - this.log.warn(`Failed to fetch/register triggers: ${getErrorMessage(error)}`); + // Fetch and register watchers + try { + const responseWatchers = await axios.get( + `${this.baseUrl}/api/watchers`, + this.buildRequestConfig('GET', '/api/watchers'), + ); + await this.registerAgentWatchersTransactional(responseWatchers.data); + // Only transfer update-enrichment ownership after every controller-side + // watcher/delegate has registered successfully. + this.setControllerDockerTransportWatchers(responseWatchers.data); + this.seedWatcherSnapshotCacheFromHandshake(responseWatchers.data); + } catch (error: unknown) { + this.log.warn(`Failed to fetch/register watchers: ${getErrorMessage(error)}`); + } + + // Apply inventory only after watcher registration. Controller-transport + // descriptors change which fields are authoritative: Portwing owns live + // runtime state, while Drydock's native watcher owns update enrichment. + await this.processAuthoritativeContainers(containers); + // A zero-container handshake is ambiguous: it could mean the agent has + // no running containers, or its in-memory store is fresh-empty after a + // restart while docker still has running containers. Defer the prune + // until the first authoritative watcher snapshot arrives — that path is + // unambiguous because the snapshot is only emitted after a successful + // enumeration with no enrichment errors (#362, #386 / d02080ae). + // Pruning here would wipe last-known state for an agent that's about to + // re-populate it in seconds via its first watch cycle. + if (containers.length > 0) { + this.pruneOldContainers(containers); + } else if (this.hasConnectedOnce) { + this.log.warn( + 'Handshake returned 0 containers; preserving last-known state until the first watch cycle completes', + ); + } + + // Fetch and register triggers + try { + const responseTriggers = await axios.get( + `${this.baseUrl}/api/triggers`, + this.buildRequestConfig('GET', '/api/triggers'), + ); + await this.registerAgentComponents('trigger', responseTriggers.data); + } catch (error: unknown) { + this.log.warn(`Failed to fetch/register triggers: ${getErrorMessage(error)}`); + } + } finally { + this.isRegisteringComponents = false; } this.isConnected = true; @@ -1061,6 +1094,10 @@ export class AgentClient { const reconnectDelay = delay ?? this.getNextReconnectDelayMs(); const wasConnected = this.isConnected; this.isConnected = false; + // A disconnect is never a "still registering" state — it's a hard loss of + // the agent. Reset unconditionally so a disconnect that races a still-running + // _doHandshake() cannot leave eligibility softened after the connection drops. + this.isRegisteringComponents = false; if (wasConnected) { void emitAgentDisconnected({ agentName: this.name, @@ -2148,12 +2185,19 @@ export class AgentClient { watchers: AgentComponentDescriptor[], triggers: AgentComponentDescriptor[], ): Promise { - this.setControllerDockerTransportWatchers([]); - await registry.deregisterAgentComponents(this.name); - await this.registerAgentWatchersTransactional(watchers); - this.setControllerDockerTransportWatchers(watchers); - this.seedWatcherSnapshotCacheFromHandshake(watchers); - await this.registerAgentComponents('trigger', triggers); + // Same deregister → re-register window as _doHandshake(): keep transient + // eligibility blockers soft while components are being replaced. + this.isRegisteringComponents = true; + try { + this.setControllerDockerTransportWatchers([]); + await registry.deregisterAgentComponents(this.name); + await this.registerAgentWatchersTransactional(watchers); + this.setControllerDockerTransportWatchers(watchers); + this.seedWatcherSnapshotCacheFromHandshake(watchers); + await this.registerAgentComponents('trigger', triggers); + } finally { + this.isRegisteringComponents = false; + } } /** diff --git a/app/api/backup.test.ts b/app/api/backup.test.ts index 66f8d42b0..e1f5d952d 100644 --- a/app/api/backup.test.ts +++ b/app/api/backup.test.ts @@ -10,6 +10,7 @@ const { mockGetAllBackups, mockGetBackup, mockGetState, + mockGetAgent, } = vi.hoisted(() => ({ mockRouter: { use: vi.fn(), get: vi.fn(), post: vi.fn() }, mockGetContainer: vi.fn(), @@ -17,6 +18,7 @@ const { mockGetAllBackups: vi.fn(), mockGetBackup: vi.fn(), mockGetState: vi.fn(), + mockGetAgent: vi.fn(), })); vi.mock('express', () => ({ @@ -40,6 +42,10 @@ vi.mock('../registry', () => ({ getState: mockGetState, })); +vi.mock('../agent/manager', () => ({ + getAgent: mockGetAgent, +})); + const { mockBackupLog } = vi.hoisted(() => ({ mockBackupLog: { info: vi.fn(), warn: vi.fn(), debug: vi.fn() }, })); @@ -263,6 +269,183 @@ describe('Backup Router', () => { }); }); + test('should return 404 when a capable agent has no docker trigger registered yet', async () => { + const handler = getHandler('post', '/:id/rollback'); + mockGetContainer.mockReturnValue({ + id: 'c1', + name: 'nginx', + agent: 'edge-1', + watcher: 'edge-1', + }); + mockGetBackupsByName.mockReturnValue([ + { + id: 'b1', + containerId: 'c1', + imageName: 'library/nginx', + imageTag: '1.24', + }, + ]); + mockGetState.mockReturnValue({ trigger: {} }); + mockGetAgent.mockReturnValue({ hasControllerDockerTransport: vi.fn(() => true) }); + + const req = createMockRequest({ params: { id: 'c1' } }); + const res = createMockResponse(); + await handler(req, res); + + expect(res.status).toHaveBeenCalledWith(404); + expect(res.json).toHaveBeenCalledWith({ + error: expect.stringContaining('No docker trigger found'), + }); + }); + + test('should return 404, not 501, when the agent is unknown or disconnected', async () => { + const handler = getHandler('post', '/:id/rollback'); + mockGetContainer.mockReturnValue({ + id: 'c1', + name: 'nginx', + agent: 'edge-1', + watcher: 'edge-1', + }); + mockGetBackupsByName.mockReturnValue([ + { + id: 'b1', + containerId: 'c1', + imageName: 'library/nginx', + imageTag: '1.24', + }, + ]); + mockGetState.mockReturnValue({ trigger: {} }); + mockGetAgent.mockReturnValue(undefined); + + const req = createMockRequest({ params: { id: 'c1' } }); + const res = createMockResponse(); + await handler(req, res); + + expect(res.status).toHaveBeenCalledWith(404); + expect(res.json).toHaveBeenCalledWith({ + error: expect.stringContaining('No docker trigger found'), + }); + }); + + test('should return 501 when the agent-owned container agent lacks controller docker transport', async () => { + const handler = getHandler('post', '/:id/rollback'); + mockGetContainer.mockReturnValue({ + id: 'c1', + name: 'nginx', + agent: 'edge-1', + watcher: 'edge-1', + }); + mockGetBackupsByName.mockReturnValue([ + { + id: 'b1', + containerId: 'c1', + imageName: 'library/nginx', + imageTag: '1.24', + }, + ]); + mockGetState.mockReturnValue({ trigger: {} }); + mockGetAgent.mockReturnValue({ hasControllerDockerTransport: vi.fn(() => false) }); + + const req = createMockRequest({ params: { id: 'c1' } }); + const res = createMockResponse(); + await handler(req, res); + + expect(res.status).toHaveBeenCalledWith(501); + expect(res.json).toHaveBeenCalledWith({ + error: expect.stringContaining("container's agent connection"), + }); + }); + + test('should return 501, not 500, when a legacy incapable AgentTrigger is registered for the container', async () => { + const handler = getHandler('post', '/:id/rollback'); + const legacyAgentTrigger = { + type: 'docker', + agent: 'edge-1', + getWatcher: vi.fn(() => { + throw new Error( + 'AgentTrigger docker.edge-1 cannot provide local Docker capability getWatcher; the agent does not advertise controller Docker transport', + ); + }), + }; + mockGetContainer.mockReturnValue({ + id: 'c1', + name: 'nginx', + agent: 'edge-1', + watcher: 'edge-1', + }); + mockGetBackupsByName.mockReturnValue([ + { + id: 'b1', + containerId: 'c1', + imageName: 'library/nginx', + imageTag: '1.24', + }, + ]); + mockGetState.mockReturnValue({ trigger: { 'docker.edge-1': legacyAgentTrigger } }); + mockGetAgent.mockReturnValue({ hasControllerDockerTransport: vi.fn(() => false) }); + + const req = createMockRequest({ params: { id: 'c1' } }); + const res = createMockResponse(); + await handler(req, res); + + expect(legacyAgentTrigger.getWatcher).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(501); + expect(res.json).toHaveBeenCalledWith({ + error: expect.stringContaining("container's agent connection"), + }); + }); + + test('should roll back an agent-owned container whose agent advertises controller docker transport', async () => { + const handler = getHandler('post', '/:id/rollback'); + const container = { + id: 'c1', + name: 'nginx', + agent: 'edge-1', + watcher: 'edge-1', + image: { registry: { name: 'hub' } }, + }; + const latestBackup = { + id: 'b1', + containerId: 'c1', + imageName: 'library/nginx', + imageTag: '1.24', + }; + + mockGetContainer.mockReturnValue(container); + mockGetBackupsByName.mockReturnValue([latestBackup]); + mockGetAgent.mockReturnValue({ hasControllerDockerTransport: vi.fn(() => true) }); + + const mockCurrentContainer = {}; + const mockContainerSpec = { State: { Running: true } }; + const mockTrigger = { + type: 'docker', + agent: 'edge-1', + getWatcher: vi.fn(() => ({ dockerApi: {} })), + pullImage: vi.fn().mockResolvedValue(undefined), + getCurrentContainer: vi.fn().mockResolvedValue(mockCurrentContainer), + inspectContainer: vi.fn().mockResolvedValue(mockContainerSpec), + stopAndRemoveContainer: vi.fn().mockResolvedValue(undefined), + recreateContainer: vi.fn().mockResolvedValue(undefined), + }; + mockGetState.mockReturnValue({ + trigger: { 'docker.edge-1': mockTrigger }, + registry: { hub: { getAuthPull: vi.fn().mockResolvedValue({}) } }, + }); + + const req = createMockRequest({ params: { id: 'c1' } }); + const res = createMockResponse(); + await handler(req, res); + + expect(mockTrigger.pullImage).toHaveBeenCalled(); + expect(mockTrigger.stopAndRemoveContainer).toHaveBeenCalled(); + expect(mockTrigger.recreateContainer).toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith({ + message: 'Container rolled back successfully', + backup: latestBackup, + }); + }); + test('should rollback successfully', async () => { const handler = getHandler('post', '/:id/rollback'); const container = { diff --git a/app/api/backup.ts b/app/api/backup.ts index 05fbf6dbd..d4de711fa 100644 --- a/app/api/backup.ts +++ b/app/api/backup.ts @@ -10,7 +10,12 @@ import { } from '../triggers/providers/docker/created-container-candidate.js'; import { recordAuditEvent } from './audit-events.js'; import { requireDestructiveActionConfirmation } from './destructive-confirmation.js'; -import { findDockerTriggerForContainer, NO_DOCKER_TRIGGER_FOUND_ERROR } from './docker-trigger.js'; +import { + AGENT_LIFECYCLE_UNSUPPORTED_ERROR, + findDockerTriggerForContainer, + isAgentLifecycleUnsupported, + NO_DOCKER_TRIGGER_FOUND_ERROR, +} from './docker-trigger.js'; import { sendErrorResponse } from './error-response.js'; import { handleContainerActionError } from './helpers.js'; @@ -82,6 +87,11 @@ async function rollbackContainer(req: Request, res: Response) { backup = backups[0]; } + if (isAgentLifecycleUnsupported(container)) { + sendErrorResponse(res, 501, AGENT_LIFECYCLE_UNSUPPORTED_ERROR); + return; + } + const trigger = findDockerTriggerForContainer(registry.getState().trigger, container); if (!trigger) { sendErrorResponse(res, 404, NO_DOCKER_TRIGGER_FOUND_ERROR); diff --git a/app/api/container-actions.test.ts b/app/api/container-actions.test.ts index 2e4ff03fd..a5aa07bce 100644 --- a/app/api/container-actions.test.ts +++ b/app/api/container-actions.test.ts @@ -8,6 +8,7 @@ const { mockUpdateContainer, mockMarkPendingFreshStateAfterManualUpdate, mockGetState, + mockGetAgent, mockInsertAudit, mockGetAuditCounter, mockGetContainerActionsCounter, @@ -19,6 +20,7 @@ const { mockUpdateContainer: vi.fn((c) => c), mockMarkPendingFreshStateAfterManualUpdate: vi.fn(), mockGetState: vi.fn(), + mockGetAgent: vi.fn(), mockInsertAudit: vi.fn(), mockGetAuditCounter: vi.fn(), mockGetContainerActionsCounter: vi.fn(), @@ -42,6 +44,10 @@ vi.mock('../registry', () => ({ getState: mockGetState, })); +vi.mock('../agent/manager', () => ({ + getAgent: mockGetAgent, +})); + vi.mock('../store/audit', () => ({ insertAudit: mockInsertAudit, })); @@ -189,6 +195,126 @@ describe('Container Actions Router', () => { }); }); + test('should start an agent-owned container whose agent advertises controller docker transport', async () => { + const container = { + id: 'c1', + name: 'nginx', + image: { name: 'nginx' }, + agent: 'edge-1', + watcher: 'edge-1', + }; + mockGetContainer.mockReturnValue(container); + const { trigger, dockerContainer } = createDockerTrigger({ agent: 'edge-1' }); + mockGetState.mockReturnValue({ trigger: { 'docker.edge-1': trigger } }); + mockGetAgent.mockReturnValue({ hasControllerDockerTransport: vi.fn(() => true) }); + + const handler = getHandler('post', '/:id/start'); + const req = createMockRequest({ params: { id: 'c1' } }); + const res = createMockResponse(); + await handler(req, res); + + expect(dockerContainer.start).toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith({ + message: 'Container started successfully', + result: expect.any(Object), + }); + }); + + test('should return 404 when a capable agent has no docker trigger registered yet', async () => { + mockGetContainer.mockReturnValue({ + id: 'c1', + name: 'nginx', + agent: 'edge-1', + watcher: 'edge-1', + }); + mockGetState.mockReturnValue({ trigger: {} }); + mockGetAgent.mockReturnValue({ hasControllerDockerTransport: vi.fn(() => true) }); + + const handler = getHandler('post', '/:id/start'); + const req = createMockRequest({ params: { id: 'c1' } }); + const res = createMockResponse(); + await handler(req, res); + + expect(res.status).toHaveBeenCalledWith(404); + expect(res.json).toHaveBeenCalledWith({ + error: expect.stringContaining('No docker trigger found'), + }); + }); + + test('should return 404, not 501, when the agent is unknown or disconnected', async () => { + mockGetContainer.mockReturnValue({ + id: 'c1', + name: 'nginx', + agent: 'edge-1', + watcher: 'edge-1', + }); + mockGetState.mockReturnValue({ trigger: {} }); + mockGetAgent.mockReturnValue(undefined); + + const handler = getHandler('post', '/:id/start'); + const req = createMockRequest({ params: { id: 'c1' } }); + const res = createMockResponse(); + await handler(req, res); + + expect(res.status).toHaveBeenCalledWith(404); + expect(res.json).toHaveBeenCalledWith({ + error: expect.stringContaining('No docker trigger found'), + }); + }); + + test('should return 501 when the agent-owned container agent lacks controller docker transport', async () => { + mockGetContainer.mockReturnValue({ + id: 'c1', + name: 'nginx', + agent: 'edge-1', + watcher: 'edge-1', + }); + mockGetState.mockReturnValue({ trigger: {} }); + mockGetAgent.mockReturnValue({ hasControllerDockerTransport: vi.fn(() => false) }); + + const handler = getHandler('post', '/:id/start'); + const req = createMockRequest({ params: { id: 'c1' } }); + const res = createMockResponse(); + await handler(req, res); + + expect(res.status).toHaveBeenCalledWith(501); + expect(res.json).toHaveBeenCalledWith({ + error: expect.stringContaining("container's agent connection"), + }); + }); + + test('should return 501, not 500, when a legacy incapable AgentTrigger is registered for the container', async () => { + const legacyAgentTrigger = { + type: 'docker', + agent: 'edge-1', + getWatcher: vi.fn(() => { + throw new Error( + 'AgentTrigger docker.edge-1 cannot provide local Docker capability getWatcher; the agent does not advertise controller Docker transport', + ); + }), + }; + mockGetContainer.mockReturnValue({ + id: 'c1', + name: 'nginx', + agent: 'edge-1', + watcher: 'edge-1', + }); + mockGetState.mockReturnValue({ trigger: { 'docker.edge-1': legacyAgentTrigger } }); + mockGetAgent.mockReturnValue({ hasControllerDockerTransport: vi.fn(() => false) }); + + const handler = getHandler('post', '/:id/start'); + const req = createMockRequest({ params: { id: 'c1' } }); + const res = createMockResponse(); + await handler(req, res); + + expect(legacyAgentTrigger.getWatcher).not.toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(501); + expect(res.json).toHaveBeenCalledWith({ + error: expect.stringContaining("container's agent connection"), + }); + }); + test('should return 403 when feature flag is disabled', async () => { mockGetServerConfiguration.mockReturnValue({ feature: { containeractions: false } }); diff --git a/app/api/container-actions.ts b/app/api/container-actions.ts index 9ce0197e4..d0e83d7bb 100644 --- a/app/api/container-actions.ts +++ b/app/api/container-actions.ts @@ -15,7 +15,12 @@ import { UpdateRequestError, } from '../updates/request-update.js'; import { recordAuditEvent } from './audit-events.js'; -import { findDockerTriggerForContainer, NO_DOCKER_TRIGGER_FOUND_ERROR } from './docker-trigger.js'; +import { + AGENT_LIFECYCLE_UNSUPPORTED_ERROR, + findDockerTriggerForContainer, + isAgentLifecycleUnsupported, + NO_DOCKER_TRIGGER_FOUND_ERROR, +} from './docker-trigger.js'; import { sendErrorResponse } from './error-response.js'; import { handleContainerActionError } from './helpers.js'; @@ -118,6 +123,11 @@ async function executeAction( return; } + if (isAgentLifecycleUnsupported(container)) { + sendErrorResponse(res, 501, AGENT_LIFECYCLE_UNSUPPORTED_ERROR); + return; + } + const trigger = findDockerTriggerForContainer(registry.getState().trigger, container); if (!trigger) { sendErrorResponse(res, 404, NO_DOCKER_TRIGGER_FOUND_ERROR); diff --git a/app/api/container/handlers/list.test.ts b/app/api/container/handlers/list.test.ts index e47cd1109..151645a90 100644 --- a/app/api/container/handlers/list.test.ts +++ b/app/api/container/handlers/list.test.ts @@ -1587,6 +1587,79 @@ describe('buildEligibilityContext cross-agent scoping (issue #411)', () => { (result as any).updateEligibility.blockers.find((b: any) => b.reason === 'active-operation'), ).toBeDefined(); }); + + describe('agent-mismatch severity during registration window (#605)', () => { + function mismatchedDockerTrigger() { + return { + type: 'docker', + agent: 'agent-a', + configuration: { threshold: 'all' }, + getId: () => 'docker.update', + isTriggerIncluded: () => true, + isTriggerExcluded: () => false, + }; + } + + test('downgrades to soft when the container agent is still completing registration', () => { + const container = createContainerWithUpdate({ agent: 'agent-b' }); + const getAgent = vi.fn().mockReturnValue({ isRegisteringComponents: true }); + const context: CrudHandlerContext = { + ...createMockContext(), + getTriggers: vi + .fn() + .mockReturnValue({ 'docker.update': mismatchedDockerTrigger() } as never), + getAgent, + }; + + const result = attachUpdateEligibility(context, container); + + const blocker = (result as any).updateEligibility.blockers.find( + (b: any) => b.reason === 'agent-mismatch', + ); + expect(blocker).toBeDefined(); + expect(blocker.severity).toBe('soft'); + expect(getAgent).toHaveBeenCalledWith('agent-b'); + }); + + test('stays hard when the agent is not pending registration', () => { + const container = createContainerWithUpdate({ agent: 'agent-b' }); + const context: CrudHandlerContext = { + ...createMockContext(), + getTriggers: vi + .fn() + .mockReturnValue({ 'docker.update': mismatchedDockerTrigger() } as never), + getAgent: vi.fn().mockReturnValue(undefined), + }; + + const result = attachUpdateEligibility(context, container); + + const blocker = (result as any).updateEligibility.blockers.find( + (b: any) => b.reason === 'agent-mismatch', + ); + expect(blocker).toBeDefined(); + expect(blocker.severity).toBe('hard'); + }); + + test('falls back to an empty agent name when the container has no agent', () => { + const container = createContainerWithUpdate(); + const getAgent = vi.fn().mockReturnValue(undefined); + const context: CrudHandlerContext = { + ...createMockContext(), + getTriggers: vi + .fn() + .mockReturnValue({ 'docker.update': mismatchedDockerTrigger() } as never), + getAgent, + }; + + const result = attachUpdateEligibility(context, container); + + const blocker = (result as any).updateEligibility.blockers.find( + (b: any) => b.reason === 'agent-mismatch', + ); + expect(blocker).toBeDefined(); + expect(getAgent).toHaveBeenCalledWith(''); + }); + }); }); describe('createGetContainersHandler', () => { diff --git a/app/api/container/handlers/list.ts b/app/api/container/handlers/list.ts index b7f5dc3b5..278a9cc46 100644 --- a/app/api/container/handlers/list.ts +++ b/app/api/container/handlers/list.ts @@ -208,6 +208,8 @@ export function attachInProgressUpdateOperation( function buildEligibilityContext(context: CrudHandlerContext): UpdateEligibilityContext { return { triggers: context.getTriggers ? context.getTriggers() : undefined, + isAgentPendingRegistration: (agentName) => + context.getAgent(agentName ?? '')?.isRegisteringComponents === true, getActiveOperation: (container: Container) => { const byId = context.updateOperationStore.getActiveOperationByContainerId(container.id); // Scoped by agent+watcher so cross-agent same-named ops don't affect eligibility (issue #411). diff --git a/app/api/container/log-stream.test.ts b/app/api/container/log-stream.test.ts index 34840175d..aa3dba28f 100644 --- a/app/api/container/log-stream.test.ts +++ b/app/api/container/log-stream.test.ts @@ -4,6 +4,7 @@ import * as configuration from '../../configuration/index.js'; import * as registry from '../../registry/index.js'; import * as storeContainer from '../../store/container.js'; import * as rateLimitKey from '../rate-limit-key.js'; +import { createIdentityAwareUpgradeRateLimitKeyResolver } from '../ws-upgrade-utils.js'; import { attachContainerLogStreamWebSocketServer, createContainerLogStreamGateway, @@ -651,6 +652,159 @@ describe('api/container/log-stream', () => { expect(mockWebSocketServer.handleUpgrade).not.toHaveBeenCalled(); }); + test('accepts passport-less upgrades when anonymous authentication is active', async () => { + const isAnonymousAuthenticationActiveSpy = vi + .spyOn(registry, 'isAnonymousAuthenticationActive') + .mockReturnValue(true); + const ws = new EventEmitter() as EventEmitter & { + send: ReturnType; + close: ReturnType; + }; + ws.send = vi.fn(); + ws.close = vi.fn(() => { + ws.emit('close'); + }); + + const mockWebSocketServer = { + handleUpgrade: vi.fn((_req, _socket, _head, callback: (socket: unknown) => void) => + callback(ws), + ), + }; + + const gateway = createContainerLogStreamGateway({ + getContainer: vi.fn(() => undefined), + getWatchers: vi.fn(() => ({})), + sessionMiddleware: (_req: unknown, _res: unknown, next: (error?: unknown) => void) => + next(), + webSocketServer: mockWebSocketServer, + isRateLimited: vi.fn(() => false), + }); + + const socket = createUpgradeSocket(); + await gateway.handleUpgrade( + createUpgradeRequest('/api/v1/containers/c1/logs/stream') as any, + socket as any, + Buffer.alloc(0), + ); + + expect(socket.write).not.toHaveBeenCalledWith(expect.stringContaining('401 Unauthorized')); + expect(mockWebSocketServer.handleUpgrade).toHaveBeenCalled(); + expect(ws.close).toHaveBeenCalledWith(4004, 'Container not found'); + + isAnonymousAuthenticationActiveSpy.mockRestore(); + }); + + test('rejects unauthenticated upgrades when anonymous authentication is not active', async () => { + const isAnonymousAuthenticationActiveSpy = vi + .spyOn(registry, 'isAnonymousAuthenticationActive') + .mockReturnValue(false); + const mockWebSocketServer = { + handleUpgrade: vi.fn(), + }; + + const gateway = createContainerLogStreamGateway({ + getContainer: vi.fn(), + getWatchers: vi.fn(() => ({})), + sessionMiddleware: (_req: unknown, _res: unknown, next: (error?: unknown) => void) => + next(), + webSocketServer: mockWebSocketServer, + isRateLimited: vi.fn(() => false), + }); + + const socket = createUpgradeSocket(); + await gateway.handleUpgrade( + createUpgradeRequest('/api/v1/containers/c1/logs/stream') as any, + socket as any, + Buffer.alloc(0), + ); + + expect(socket.write).toHaveBeenCalledWith(expect.stringContaining('401 Unauthorized')); + expect(mockWebSocketServer.handleUpgrade).not.toHaveBeenCalled(); + + isAnonymousAuthenticationActiveSpy.mockRestore(); + }); + + test('keys rate limiting by IP for anonymous-authenticated upgrades, not by session', async () => { + const isAnonymousAuthenticationActiveSpy = vi + .spyOn(registry, 'isAnonymousAuthenticationActive') + .mockReturnValue(true); + const capturedRateLimitKeys: string[] = []; + const mockWebSocketServer = { + handleUpgrade: vi.fn((_req, _socket, _head, callback: (socket: unknown) => void) => + callback({ on: vi.fn(), off: vi.fn(), send: vi.fn(), close: vi.fn() }), + ), + }; + + const gateway = createContainerLogStreamGateway({ + getContainer: vi.fn(() => undefined), + getWatchers: vi.fn(() => ({})), + sessionMiddleware: (_req: unknown, _res: unknown, next: (error?: unknown) => void) => + next(), + webSocketServer: mockWebSocketServer, + isRateLimited: (key: string) => { + capturedRateLimitKeys.push(key); + return false; + }, + getRateLimitKey: createIdentityAwareUpgradeRateLimitKeyResolver({ + ratelimit: { identitykeying: true }, + }), + }); + + const socket = createUpgradeSocket(); + await gateway.handleUpgrade( + createUpgradeRequest('/api/v1/containers/c1/logs/stream') as any, + socket as any, + Buffer.alloc(0), + ); + + expect(socket.write).not.toHaveBeenCalledWith(expect.stringContaining('401 Unauthorized')); + expect(capturedRateLimitKeys).toEqual(['ip:127.0.0.1']); + + isAnonymousAuthenticationActiveSpy.mockRestore(); + }); + + test('keeps session-keyed rate limiting for passport-authenticated upgrades', async () => { + const authenticatingMiddleware = ( + req: any, + _res: unknown, + next: (error?: unknown) => void, + ) => { + req.session = { passport: { user: '{"username":"alice"}' } }; + req.sessionID = 'session-1'; + next(); + }; + const capturedRateLimitKeys: string[] = []; + const mockWebSocketServer = { + handleUpgrade: vi.fn((_req, _socket, _head, callback: (socket: unknown) => void) => + callback({ on: vi.fn(), off: vi.fn(), send: vi.fn(), close: vi.fn() }), + ), + }; + + const gateway = createContainerLogStreamGateway({ + getContainer: vi.fn(() => undefined), + getWatchers: vi.fn(() => ({})), + sessionMiddleware: authenticatingMiddleware, + webSocketServer: mockWebSocketServer, + isRateLimited: (key: string) => { + capturedRateLimitKeys.push(key); + return false; + }, + getRateLimitKey: createIdentityAwareUpgradeRateLimitKeyResolver({ + ratelimit: { identitykeying: true }, + }), + }); + + const socket = createUpgradeSocket(); + await gateway.handleUpgrade( + createUpgradeRequest('/api/v1/containers/c1/logs/stream') as any, + socket as any, + Buffer.alloc(0), + ); + + expect(socket.write).not.toHaveBeenCalledWith(expect.stringContaining('401 Unauthorized')); + expect(capturedRateLimitKeys).toEqual(['session:session-1']); + }); + test('closes websocket with 4004 when container is missing', async () => { const ws = new EventEmitter() as EventEmitter & { send: ReturnType; diff --git a/app/api/container/log-stream.ts b/app/api/container/log-stream.ts index 168d08fa7..c51a0754a 100644 --- a/app/api/container/log-stream.ts +++ b/app/api/container/log-stream.ts @@ -599,13 +599,19 @@ export function createContainerLogStreamGateway( } const upgradeRequest = request as UpgradeRequest; - const authenticated = isAuthenticatedSession(upgradeRequest); - const rateLimitKey = getRateLimitKey(upgradeRequest, authenticated); + // Rate-limit keying must stay based on genuine passport authentication only — + // anonymous auth being active shouldn't let anonymous clients earn session-keyed + // (rotatable) rate limits instead of IP-keyed ones. + const passportAuthenticated = isAuthenticatedSession(upgradeRequest); + const gateAuthenticated = isAuthenticatedSession(upgradeRequest, { + anonymousAuthActive: registry.isAnonymousAuthenticationActive(), + }); + const rateLimitKey = getRateLimitKey(upgradeRequest, passportAuthenticated); if (isRateLimited(rateLimitKey)) { writeUpgradeError(socket, 429, 'Too Many Requests'); return; } - if (!authenticated) { + if (!gateAuthenticated) { writeUpgradeError(socket, 401, 'Unauthorized'); return; } diff --git a/app/api/container/maturity-filter.test.ts b/app/api/container/maturity-filter.test.ts index cb2d9bbb0..115e77dda 100644 --- a/app/api/container/maturity-filter.test.ts +++ b/app/api/container/maturity-filter.test.ts @@ -33,4 +33,41 @@ describe('api/container/maturity-filter', () => { expect(applyContainerMaturityFilter(containers, undefined)).toBe(containers); }); + + test('applyContainerMaturityFilter uses per-container updatePolicy.maturityMinAgeDays over the global threshold on the uncached fallback path', () => { + const previousThreshold = process.env.DD_UI_MATURITY_THRESHOLD_DAYS; + process.env.DD_UI_MATURITY_THRESHOLD_DAYS = '7'; + + try { + const tenDaysMs = 10 * 24 * 60 * 60 * 1000; + const containers = [ + { + id: 'c1', + updateAvailable: true, + updateAge: tenDaysMs, + updatePolicy: { maturityMinAgeDays: 30 }, + } as unknown as Container, + { + id: 'c2', + updateAvailable: true, + updateAge: tenDaysMs, + } as unknown as Container, + ]; + + // Global threshold is pinned to 7 days: c2 has no override so it's 'mature'. + // c1 overrides to 30 days, so the same 10-day-old update is still 'hot'. + expect( + applyContainerMaturityFilter(containers, 'hot').map((container) => container.id), + ).toEqual(['c1']); + expect( + applyContainerMaturityFilter(containers, 'mature').map((container) => container.id), + ).toEqual(['c2']); + } finally { + if (previousThreshold === undefined) { + delete process.env.DD_UI_MATURITY_THRESHOLD_DAYS; + } else { + process.env.DD_UI_MATURITY_THRESHOLD_DAYS = previousThreshold; + } + } + }); }); diff --git a/app/api/container/maturity-filter.ts b/app/api/container/maturity-filter.ts index 59bdb3103..4cb9ceffc 100644 --- a/app/api/container/maturity-filter.ts +++ b/app/api/container/maturity-filter.ts @@ -28,13 +28,9 @@ function resolveUiMaturityThresholdDays(): number { ); } -function resolveUiMaturityThresholdMs(): number { - return maturityMinAgeDaysToMilliseconds(resolveUiMaturityThresholdDays()); -} - function getContainerMaturityLevel( container: Container, - uiMaturityThresholdMs: number, + uiMaturityThresholdDays: number, ): ContainerMaturityFilter | undefined { const cachedLevel = container.updateMaturityLevel; if (cachedLevel === 'hot' || cachedLevel === 'mature' || cachedLevel === 'established') { @@ -48,7 +44,11 @@ function getContainerMaturityLevel( if (updateAge >= maturityMinAgeDaysToMilliseconds(ESTABLISHED_UPDATE_AGE_DAYS)) { return 'established'; } - return updateAge >= uiMaturityThresholdMs ? 'mature' : 'hot'; + const maturityThresholdDays = resolveMaturityMinAgeDays( + container.updatePolicy?.maturityMinAgeDays, + uiMaturityThresholdDays, + ); + return updateAge >= maturityMinAgeDaysToMilliseconds(maturityThresholdDays) ? 'mature' : 'hot'; } export function applyContainerMaturityFilter( @@ -59,8 +59,8 @@ export function applyContainerMaturityFilter( return containers; } - const uiMaturityThresholdMs = resolveUiMaturityThresholdMs(); + const uiMaturityThresholdDays = resolveUiMaturityThresholdDays(); return containers.filter( - (container) => getContainerMaturityLevel(container, uiMaturityThresholdMs) === maturityFilter, + (container) => getContainerMaturityLevel(container, uiMaturityThresholdDays) === maturityFilter, ); } diff --git a/app/api/docker-trigger.ts b/app/api/docker-trigger.ts index d509bdc83..930379199 100644 --- a/app/api/docker-trigger.ts +++ b/app/api/docker-trigger.ts @@ -1,9 +1,12 @@ import path from 'node:path'; +import { getAgent } from '../agent/manager.js'; import type { Container } from '../model/container.js'; import type Docker from '../triggers/providers/docker/Docker.js'; import type Trigger from '../triggers/providers/Trigger.js'; export const NO_DOCKER_TRIGGER_FOUND_ERROR = 'No docker trigger found for this container'; +export const AGENT_LIFECYCLE_UNSUPPORTED_ERROR = + "Lifecycle actions (start/stop/restart) are not supported over this container's agent connection, typically because the agent has not advertised the usesControllerDockerTransport capability."; const DEFAULT_TRIGGER_TYPES = ['docker', 'dockercompose']; const COMPOSE_DIRECTORY_FILE_CANDIDATES = new Set([ 'compose.yaml', @@ -255,6 +258,33 @@ export function isTriggerCompatibleWithContainer( return true; } +/** + * Whether lifecycle actions (start/stop/restart/rollback) are unsupported for + * an agent-owned container, decided from the agent's actual advertised + * capability rather than trigger presence/absence: + * + * - No agent on the container: never unsupported (non-agent containers are + * handled by the plain docker-trigger lookup). + * - Agent not currently connected/registered: not decided here; the caller's + * docker-trigger lookup will report the honest transient 404 instead. + * - Agent connected but its watcher hasn't advertised controller Docker + * transport: unsupported, regardless of whether a legacy AgentTrigger is + * still registered for it (that trigger's getWatcher()/rollback methods + * throw rather than working). + */ +export function isAgentLifecycleUnsupported( + container: Pick, +): boolean { + if (!container.agent) { + return false; + } + const agentClient = getAgent(container.agent); + if (!agentClient) { + return false; + } + return !agentClient.hasControllerDockerTransport(container.watcher); +} + /** * Find a docker trigger compatible with a container's agent context. */ diff --git a/app/api/log-stream.test.ts b/app/api/log-stream.test.ts index 11ac5d138..ca900587b 100644 --- a/app/api/log-stream.test.ts +++ b/app/api/log-stream.test.ts @@ -1,12 +1,14 @@ import { EventEmitter } from 'node:events'; import { WebSocketServer } from 'ws'; import * as configuration from '../configuration/index.js'; +import * as registry from '../registry/index.js'; import { attachSystemLogStreamWebSocketServer, createSystemLogStreamGateway, parseSystemLogStreamQuery, } from './log-stream.js'; import * as rateLimitKey from './rate-limit-key.js'; +import { createIdentityAwareUpgradeRateLimitKeyResolver } from './ws-upgrade-utils.js'; function createUpgradeSocket() { return { @@ -403,6 +405,148 @@ describe('api/log-stream', () => { expect(socket.write).toHaveBeenCalledWith(expect.stringContaining('401 Unauthorized')); }); + test('accepts passport-less upgrades when anonymous authentication is active', async () => { + const isAnonymousAuthenticationActiveSpy = vi + .spyOn(registry, 'isAnonymousAuthenticationActive') + .mockReturnValue(true); + const mockHandleUpgrade = vi.fn( + (_req: unknown, _socket: unknown, _head: unknown, callback: (ws: unknown) => void) => { + const ws = { + on: vi.fn((event: string, listener: () => void) => { + if (event === 'close') listener(); + }), + off: vi.fn(), + send: vi.fn(), + close: vi.fn(), + }; + callback(ws); + }, + ); + const gateway = createSystemLogStreamGateway({ + sessionMiddleware: (_req: unknown, _res: unknown, next: (error?: unknown) => void) => + next(), + webSocketServer: { handleUpgrade: mockHandleUpgrade }, + isRateLimited: vi.fn(() => false), + }); + const socket = createUpgradeSocket(); + + await gateway.handleUpgrade( + createUpgradeRequest('/api/v1/log/stream') as any, + socket as any, + Buffer.alloc(0), + ); + + expect(socket.write).not.toHaveBeenCalledWith(expect.stringContaining('401 Unauthorized')); + expect(mockHandleUpgrade).toHaveBeenCalledTimes(1); + + isAnonymousAuthenticationActiveSpy.mockRestore(); + }); + + test('rejects unauthenticated upgrades when anonymous authentication is not active', async () => { + const isAnonymousAuthenticationActiveSpy = vi + .spyOn(registry, 'isAnonymousAuthenticationActive') + .mockReturnValue(false); + const gateway = createSystemLogStreamGateway({ + sessionMiddleware: (_req: unknown, _res: unknown, next: (error?: unknown) => void) => + next(), + webSocketServer: { handleUpgrade: vi.fn() }, + isRateLimited: vi.fn(() => false), + }); + const socket = createUpgradeSocket(); + + await gateway.handleUpgrade( + createUpgradeRequest('/api/v1/log/stream') as any, + socket as any, + Buffer.alloc(0), + ); + + expect(socket.write).toHaveBeenCalledWith(expect.stringContaining('401 Unauthorized')); + + isAnonymousAuthenticationActiveSpy.mockRestore(); + }); + + test('keys rate limiting by IP for anonymous-authenticated upgrades, not by session', async () => { + const isAnonymousAuthenticationActiveSpy = vi + .spyOn(registry, 'isAnonymousAuthenticationActive') + .mockReturnValue(true); + const capturedRateLimitKeys: string[] = []; + const mockHandleUpgrade = vi.fn( + (_req: unknown, _socket: unknown, _head: unknown, callback: (ws: unknown) => void) => { + const ws = { + on: vi.fn((event: string, listener: () => void) => { + if (event === 'close') listener(); + }), + off: vi.fn(), + send: vi.fn(), + close: vi.fn(), + }; + callback(ws); + }, + ); + const gateway = createSystemLogStreamGateway({ + sessionMiddleware: (_req: unknown, _res: unknown, next: (error?: unknown) => void) => + next(), + webSocketServer: { handleUpgrade: mockHandleUpgrade }, + isRateLimited: (key: string) => { + capturedRateLimitKeys.push(key); + return false; + }, + getRateLimitKey: createIdentityAwareUpgradeRateLimitKeyResolver({ + ratelimit: { identitykeying: true }, + }), + }); + const socket = createUpgradeSocket(); + + await gateway.handleUpgrade( + createUpgradeRequest('/api/v1/log/stream') as any, + socket as any, + Buffer.alloc(0), + ); + + expect(socket.write).not.toHaveBeenCalledWith(expect.stringContaining('401 Unauthorized')); + expect(capturedRateLimitKeys).toEqual(['ip:127.0.0.1']); + + isAnonymousAuthenticationActiveSpy.mockRestore(); + }); + + test('keeps session-keyed rate limiting for passport-authenticated upgrades', async () => { + const capturedRateLimitKeys: string[] = []; + const mockHandleUpgrade = vi.fn( + (_req: unknown, _socket: unknown, _head: unknown, callback: (ws: unknown) => void) => { + const ws = { + on: vi.fn((event: string, listener: () => void) => { + if (event === 'close') listener(); + }), + off: vi.fn(), + send: vi.fn(), + close: vi.fn(), + }; + callback(ws); + }, + ); + const gateway = createSystemLogStreamGateway({ + sessionMiddleware: authenticatingSessionMiddleware, + webSocketServer: { handleUpgrade: mockHandleUpgrade }, + isRateLimited: (key: string) => { + capturedRateLimitKeys.push(key); + return false; + }, + getRateLimitKey: createIdentityAwareUpgradeRateLimitKeyResolver({ + ratelimit: { identitykeying: true }, + }), + }); + const socket = createUpgradeSocket(); + + await gateway.handleUpgrade( + createUpgradeRequest('/api/v1/log/stream') as any, + socket as any, + Buffer.alloc(0), + ); + + expect(socket.write).not.toHaveBeenCalledWith(expect.stringContaining('401 Unauthorized')); + expect(capturedRateLimitKeys).toEqual(['session:session-1']); + }); + test('does not write error when socket is already destroyed', async () => { const gateway = createSystemLogStreamGateway({ sessionMiddleware: authenticatingSessionMiddleware, diff --git a/app/api/log-stream.ts b/app/api/log-stream.ts index 3226cd566..6f63c08e1 100644 --- a/app/api/log-stream.ts +++ b/app/api/log-stream.ts @@ -11,6 +11,7 @@ import { onEntry, } from '../log/buffer.js'; import { toDisplayLogEntry } from '../log/display-timestamp.js'; +import * as registry from '../registry/index.js'; import { applySessionMiddleware, createFixedWindowRateLimiter, @@ -255,13 +256,19 @@ export function createSystemLogStreamGateway(dependencies: SystemLogStreamGatewa } const upgradeRequest = request as UpgradeRequest; - const authenticated = isAuthenticatedSession(upgradeRequest); - const rateLimitKey = getRateLimitKey(upgradeRequest, authenticated); + // Rate-limit keying must stay based on genuine passport authentication only — + // anonymous auth being active shouldn't let anonymous clients earn session-keyed + // (rotatable) rate limits instead of IP-keyed ones. + const passportAuthenticated = isAuthenticatedSession(upgradeRequest); + const gateAuthenticated = isAuthenticatedSession(upgradeRequest, { + anonymousAuthActive: registry.isAnonymousAuthenticationActive(), + }); + const rateLimitKey = getRateLimitKey(upgradeRequest, passportAuthenticated); if (isRateLimited(rateLimitKey)) { writeUpgradeError(socket, 429, 'Too Many Requests'); return; } - if (!authenticated) { + if (!gateAuthenticated) { writeUpgradeError(socket, 401, 'Unauthorized'); return; } diff --git a/app/api/openapi/paths/containers.test.ts b/app/api/openapi/paths/containers.test.ts index 4d4c3302f..4b9701d33 100644 --- a/app/api/openapi/paths/containers.test.ts +++ b/app/api/openapi/paths/containers.test.ts @@ -73,6 +73,7 @@ describe('containerPaths', () => { 200: jsonResponse('Container started', { $ref: '#/components/schemas/ContainerActionResponse', }), + 501: errorResponse("Lifecycle actions unsupported by this container's agent connection"), 401: errorResponse('Authentication required'), 403: errorResponse('Container actions feature disabled'), 404: errorResponse('Container or docker trigger not found'), @@ -94,6 +95,7 @@ describe('containerPaths', () => { 200: jsonResponse('Container stopped', { $ref: '#/components/schemas/ContainerActionResponse', }), + 501: errorResponse("Lifecycle actions unsupported by this container's agent connection"), 401: errorResponse('Authentication required'), 403: errorResponse('Container actions feature disabled'), 404: errorResponse('Container or docker trigger not found'), @@ -115,6 +117,7 @@ describe('containerPaths', () => { 200: jsonResponse('Container restarted', { $ref: '#/components/schemas/ContainerActionResponse', }), + 501: errorResponse("Lifecycle actions unsupported by this container's agent connection"), 401: errorResponse('Authentication required'), 403: errorResponse('Container actions feature disabled'), 404: errorResponse('Container or docker trigger not found'), diff --git a/app/api/openapi/paths/containers.ts b/app/api/openapi/paths/containers.ts index cfedcc5b6..70790b2f7 100644 --- a/app/api/openapi/paths/containers.ts +++ b/app/api/openapi/paths/containers.ts @@ -818,6 +818,7 @@ export const containerPaths = { 200: jsonResponse('Rollback successful', { $ref: '#/components/schemas/ContainerRollbackResponse', }), + 501: errorResponse("Lifecycle actions unsupported by this container's agent connection"), 401: errorResponse('Authentication required'), 428: errorResponse('Destructive confirmation header is required'), 404: errorResponse('Container, backup, or trigger not found'), @@ -830,18 +831,27 @@ export const containerPaths = { operationId: 'startContainer', successDescription: 'Container started', failureDescription: 'Container start failed', + additionalErrorResponses: { + 501: errorResponse("Lifecycle actions unsupported by this container's agent connection"), + }, }), '/api/v1/containers/{id}/stop': createRuntimeContainerActionPath({ summary: 'Stop container', operationId: 'stopContainer', successDescription: 'Container stopped', failureDescription: 'Container stop failed', + additionalErrorResponses: { + 501: errorResponse("Lifecycle actions unsupported by this container's agent connection"), + }, }), '/api/v1/containers/{id}/restart': createRuntimeContainerActionPath({ summary: 'Restart container', operationId: 'restartContainer', successDescription: 'Container restarted', failureDescription: 'Container restart failed', + additionalErrorResponses: { + 501: errorResponse("Lifecycle actions unsupported by this container's agent connection"), + }, }), '/api/v1/containers/{id}/update': createRuntimeContainerActionPath({ summary: 'Update container to latest available image', diff --git a/app/api/sse-container-enrichment.test.ts b/app/api/sse-container-enrichment.test.ts index 4a3bf8778..1478e3ee2 100644 --- a/app/api/sse-container-enrichment.test.ts +++ b/app/api/sse-container-enrichment.test.ts @@ -1,16 +1,25 @@ -var { mockGetState, mockGetActiveOperationByContainerId, mockGetActiveOperationByContainerName } = - vi.hoisted(() => { - return { - mockGetState: vi.fn(() => ({ trigger: {}, watcher: {} })), - mockGetActiveOperationByContainerId: vi.fn(() => undefined), - mockGetActiveOperationByContainerName: vi.fn(() => undefined), - }; - }); +var { + mockGetState, + mockGetActiveOperationByContainerId, + mockGetActiveOperationByContainerName, + mockGetAgent, +} = vi.hoisted(() => { + return { + mockGetState: vi.fn(() => ({ trigger: {}, watcher: {} })), + mockGetActiveOperationByContainerId: vi.fn(() => undefined), + mockGetActiveOperationByContainerName: vi.fn(() => undefined), + mockGetAgent: vi.fn(() => undefined), + }; +}); vi.mock('../registry/index.js', () => ({ getState: mockGetState, })); +vi.mock('../agent/manager.js', () => ({ + getAgent: mockGetAgent, +})); + vi.mock('../store/update-operation.js', () => ({ getActiveOperationByContainerId: mockGetActiveOperationByContainerId, getActiveOperationByContainerName: mockGetActiveOperationByContainerName, @@ -23,9 +32,11 @@ describe('enrichContainerLifecyclePayloadWithEligibility', () => { mockGetState.mockClear(); mockGetActiveOperationByContainerId.mockClear(); mockGetActiveOperationByContainerName.mockClear(); + mockGetAgent.mockClear(); mockGetState.mockReturnValue({ trigger: {}, watcher: {} }); mockGetActiveOperationByContainerId.mockReturnValue(undefined); mockGetActiveOperationByContainerName.mockReturnValue(undefined); + mockGetAgent.mockReturnValue(undefined); }); describe('malformed payload guard', () => { @@ -336,4 +347,82 @@ describe('enrichContainerLifecyclePayloadWithEligibility', () => { ).toBe(true); }); }); + + describe('agent-mismatch severity during registration window (#605)', () => { + function mismatchedDockerTrigger() { + return { + type: 'docker', + agent: 'agent-a', + configuration: { threshold: 'all' }, + getId: () => 'docker.update', + isTriggerIncluded: () => true, + isTriggerExcluded: () => false, + }; + } + + const updatePayload = (): any => ({ + id: 'c1', + name: 'mysql', + image: { tag: { value: '9.6.0' } }, + result: { tag: '9.7.0' }, + }); + + test('downgrades to soft when the container agent is still completing registration', () => { + mockGetState.mockReturnValueOnce({ + trigger: { 'docker.update': mismatchedDockerTrigger() }, + watcher: {}, + }); + mockGetAgent.mockReturnValueOnce({ isRegisteringComponents: true }); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result = enrichContainerLifecyclePayloadWithEligibility({ + ...updatePayload(), + agent: 'agent-b', + }) as any; + + const blocker = result.updateEligibility.blockers.find( + (b: { reason: string }) => b.reason === 'agent-mismatch', + ); + expect(blocker).toBeDefined(); + expect(blocker.severity).toBe('soft'); + expect(mockGetAgent).toHaveBeenCalledWith('agent-b'); + }); + + test('stays hard when the agent is not pending registration', () => { + mockGetState.mockReturnValueOnce({ + trigger: { 'docker.update': mismatchedDockerTrigger() }, + watcher: {}, + }); + mockGetAgent.mockReturnValueOnce(undefined); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result = enrichContainerLifecyclePayloadWithEligibility({ + ...updatePayload(), + agent: 'agent-b', + }) as any; + + const blocker = result.updateEligibility.blockers.find( + (b: { reason: string }) => b.reason === 'agent-mismatch', + ); + expect(blocker).toBeDefined(); + expect(blocker.severity).toBe('hard'); + }); + + test('falls back to an empty agent name when the container has no agent', () => { + mockGetState.mockReturnValueOnce({ + trigger: { 'docker.update': mismatchedDockerTrigger() }, + watcher: {}, + }); + mockGetAgent.mockReturnValueOnce(undefined); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const result = enrichContainerLifecyclePayloadWithEligibility(updatePayload()) as any; + + const blocker = result.updateEligibility.blockers.find( + (b: { reason: string }) => b.reason === 'agent-mismatch', + ); + expect(blocker).toBeDefined(); + expect(mockGetAgent).toHaveBeenCalledWith(''); + }); + }); }); diff --git a/app/api/sse-container-enrichment.ts b/app/api/sse-container-enrichment.ts index 0af942311..d8f44fc10 100644 --- a/app/api/sse-container-enrichment.ts +++ b/app/api/sse-container-enrichment.ts @@ -1,3 +1,4 @@ +import { getAgent } from '../agent/manager.js'; import type { ContainerLifecycleEventPayload } from '../event/index.js'; import type { Container } from '../model/container.js'; import { @@ -18,6 +19,8 @@ function buildEligibilityContext(container: Container): UpdateEligibilityContext triggers: registryState.trigger, isSelfUpdateAvailable: isSelfUpdateAvailable(container), maintenanceWindowOpen: getContainerMaintenanceWindowOpen(container, registryState.watcher), + isAgentPendingRegistration: (agentName) => + getAgent(agentName ?? '')?.isRegisteringComponents === true, getActiveOperation: (c: Container) => { const byId = getActiveOperationByContainerId(c.id); // Scoped by agent+watcher so cross-agent same-named ops don't pollute enrichment (issue #411). diff --git a/app/api/ws-upgrade-utils.test.ts b/app/api/ws-upgrade-utils.test.ts index f70199fa4..966b1f314 100644 --- a/app/api/ws-upgrade-utils.test.ts +++ b/app/api/ws-upgrade-utils.test.ts @@ -336,6 +336,16 @@ describe('ws-upgrade-utils', () => { const request = {} as any; expect(isAuthenticatedSession(request)).toBe(false); }); + + test('returns true when anonymousAuthActive is true even without a passport user', () => { + const request = { session: { passport: {} } } as any; + expect(isAuthenticatedSession(request, { anonymousAuthActive: true })).toBe(true); + }); + + test('returns false when anonymousAuthActive is false and passport user is missing', () => { + const request = { session: { passport: {} } } as any; + expect(isAuthenticatedSession(request, { anonymousAuthActive: false })).toBe(false); + }); }); describe('getDefaultRateLimitKey', () => { diff --git a/app/api/ws-upgrade-utils.ts b/app/api/ws-upgrade-utils.ts index d463eb6fa..fc91fe343 100644 --- a/app/api/ws-upgrade-utils.ts +++ b/app/api/ws-upgrade-utils.ts @@ -152,9 +152,17 @@ export async function applySessionMiddleware( }); } -export function isAuthenticatedSession(request: UpgradeRequest): boolean { +export interface IsAuthenticatedSessionOptions { + anonymousAuthActive?: boolean; +} + +export function isAuthenticatedSession( + request: UpgradeRequest, + options: IsAuthenticatedSessionOptions = {}, +): boolean { + const { anonymousAuthActive = false } = options; const passportSession = request.session?.passport; - return passportSession?.user !== undefined; + return passportSession?.user !== undefined || anonymousAuthActive; } export function getDefaultRateLimitKey(request: UpgradeRequest): string { diff --git a/app/model/container.test.ts b/app/model/container.test.ts index 40710d04a..9c7417a66 100644 --- a/app/model/container.test.ts +++ b/app/model/container.test.ts @@ -1878,6 +1878,57 @@ test('model should use DD_UI_MATURITY_THRESHOLD_DAYS for hot/mature cutoff', asy } }); +test('model should use per-container updatePolicy.maturityMinAgeDays over DD_UI_MATURITY_THRESHOLD_DAYS for hot/mature cutoff', async () => { + const previousThreshold = process.env.DD_UI_MATURITY_THRESHOLD_DAYS; + vi.useFakeTimers(); + try { + process.env.DD_UI_MATURITY_THRESHOLD_DAYS = '3'; + const now = new Date('2026-03-15T12:00:00.000Z'); + vi.setSystemTime(now); + const firstSeenAt = new Date(now.getTime() - daysToMs(10)).toISOString(); + + const containerValidated = container.validate({ + id: 'container-123456789', + name: 'test', + watcher: 'test', + firstSeenAt, + updatePolicy: { maturityMinAgeDays: 30 }, + image: { + id: 'image-123456789', + registry: { + name: 'hub', + url: 'https://hub', + }, + name: 'organization/image', + tag: { + value: '1.0.0', + semver: true, + }, + digest: { + watch: false, + repo: undefined, + }, + architecture: 'arch', + os: 'os', + created: '2021-06-12T05:33:38.440Z', + }, + result: { + tag: '1.0.1', + }, + }); + + expect(containerValidated.updateAge).toBe(daysToMs(10)); + expect(containerValidated.updateMaturityLevel).toBe('hot'); + } finally { + vi.useRealTimers(); + if (previousThreshold === undefined) { + delete process.env.DD_UI_MATURITY_THRESHOLD_DAYS; + } else { + process.env.DD_UI_MATURITY_THRESHOLD_DAYS = previousThreshold; + } + } +}); + test('model should keep updateAvailable when remote tag changes past skipped value', async () => { const containerValidated = container.validate({ id: 'container-123456789', diff --git a/app/model/container.ts b/app/model/container.ts index cfb6e3ed4..0c8586371 100644 --- a/app/model/container.ts +++ b/app/model/container.ts @@ -749,7 +749,10 @@ function getRawUpdateMaturityLevel( return 'established'; } - const maturityThresholdDays = resolveUiMaturityThresholdDays(); + const maturityThresholdDays = resolveMaturityMinAgeDays( + container.updatePolicy?.maturityMinAgeDays, + resolveUiMaturityThresholdDays(), + ); const maturityThresholdMs = maturityMinAgeDaysToMilliseconds(maturityThresholdDays); return updateAge >= maturityThresholdMs ? 'mature' : 'hot'; } diff --git a/app/model/update-eligibility.test.ts b/app/model/update-eligibility.test.ts index 0206c11f4..f25196b05 100644 --- a/app/model/update-eligibility.test.ts +++ b/app/model/update-eligibility.test.ts @@ -1006,6 +1006,72 @@ describe('computeUpdateEligibility', () => { const blocker = result.blockers.find((b) => b.reason === 'no-update-trigger-configured'); expect(blocker).toBeDefined(); }); + + describe('severity during the trigger registration window (#605)', () => { + test('downgrades to soft when container is agent-owned and its agent is pending registration', () => { + const container = makeContainerWithTagUpdate({ agent: 'agent-b' }); + const isAgentPendingRegistration = vi.fn().mockReturnValue(true); + const result = computeUpdateEligibility( + container, + makeContext({ + triggers: undefined, + now: FIXED_NOW, + isAgentPendingRegistration, + }), + ); + const blocker = result.blockers.find((b) => b.reason === 'no-update-trigger-configured'); + expect(blocker).toBeDefined(); + expect(blocker?.severity).toBe('soft'); + expect(isAgentPendingRegistration).toHaveBeenCalledWith('agent-b'); + }); + + test('stays hard when container is agent-owned but its agent is not pending registration', () => { + const container = makeContainerWithTagUpdate({ agent: 'agent-b' }); + const isAgentPendingRegistration = vi.fn().mockReturnValue(false); + const result = computeUpdateEligibility( + container, + makeContext({ + triggers: undefined, + now: FIXED_NOW, + isAgentPendingRegistration, + }), + ); + const blocker = result.blockers.find((b) => b.reason === 'no-update-trigger-configured'); + expect(blocker).toBeDefined(); + expect(blocker?.severity).toBe('hard'); + }); + + test('stays hard for non-agent containers even when isAgentPendingRegistration would return true', () => { + const container = makeContainerWithTagUpdate(); // no agent property + const isAgentPendingRegistration = vi.fn().mockReturnValue(true); + const result = computeUpdateEligibility( + container, + makeContext({ + triggers: undefined, + now: FIXED_NOW, + isAgentPendingRegistration, + }), + ); + const blocker = result.blockers.find((b) => b.reason === 'no-update-trigger-configured'); + expect(blocker).toBeDefined(); + expect(blocker?.severity).toBe('hard'); + expect(isAgentPendingRegistration).not.toHaveBeenCalled(); + }); + + test('stays hard for agent-owned containers when no isAgentPendingRegistration callback is supplied', () => { + const container = makeContainerWithTagUpdate({ agent: 'agent-b' }); + const result = computeUpdateEligibility( + container, + makeContext({ + triggers: undefined, + now: FIXED_NOW, + }), + ); + const blocker = result.blockers.find((b) => b.reason === 'no-update-trigger-configured'); + expect(blocker).toBeDefined(); + expect(blocker?.severity).toBe('hard'); + }); + }); }); describe('threshold-not-reached', () => { @@ -1381,6 +1447,58 @@ describe('computeUpdateEligibility', () => { ); expect(result.blockers.find((b) => b.reason === 'agent-mismatch')).toBeUndefined(); }); + + describe('severity during the trigger registration window (#605)', () => { + test('downgrades to soft when isAgentPendingRegistration returns true for the container agent', () => { + const trigger = makeTrigger({ agent: 'agent-a' }); + const container = makeContainerWithTagUpdate({ agent: 'agent-b' }); + const isAgentPendingRegistration = vi.fn().mockReturnValue(true); + const result = computeUpdateEligibility( + container, + makeContext({ + triggers: { 'docker.update': trigger as never }, + now: FIXED_NOW, + isAgentPendingRegistration, + }), + ); + const blocker = result.blockers.find((b) => b.reason === 'agent-mismatch'); + expect(blocker).toBeDefined(); + expect(blocker?.severity).toBe('soft'); + expect(isAgentPendingRegistration).toHaveBeenCalledWith('agent-b'); + }); + + test('stays hard when isAgentPendingRegistration returns false for the container agent', () => { + const trigger = makeTrigger({ agent: 'agent-a' }); + const container = makeContainerWithTagUpdate({ agent: 'agent-b' }); + const isAgentPendingRegistration = vi.fn().mockReturnValue(false); + const result = computeUpdateEligibility( + container, + makeContext({ + triggers: { 'docker.update': trigger as never }, + now: FIXED_NOW, + isAgentPendingRegistration, + }), + ); + const blocker = result.blockers.find((b) => b.reason === 'agent-mismatch'); + expect(blocker).toBeDefined(); + expect(blocker?.severity).toBe('hard'); + }); + + test('stays hard when no isAgentPendingRegistration callback is supplied on the context', () => { + const trigger = makeTrigger({ agent: 'agent-a' }); + const container = makeContainerWithTagUpdate({ agent: 'agent-b' }); + const result = computeUpdateEligibility( + container, + makeContext({ + triggers: { 'docker.update': trigger as never }, + now: FIXED_NOW, + }), + ); + const blocker = result.blockers.find((b) => b.reason === 'agent-mismatch'); + expect(blocker).toBeDefined(); + expect(blocker?.severity).toBe('hard'); + }); + }); }); describe('active-operation', () => { diff --git a/app/model/update-eligibility.ts b/app/model/update-eligibility.ts index 697ba850a..7aeebc6fd 100644 --- a/app/model/update-eligibility.ts +++ b/app/model/update-eligibility.ts @@ -106,8 +106,11 @@ function updateScanMatchesCandidate(container: Container, updateScan: UpdateSecu ); } -function makeBlocker(blocker: Omit): UpdateBlocker { - return { ...blocker, severity: BLOCKER_SEVERITY[blocker.reason] }; +function makeBlocker( + blocker: Omit, + severityOverride?: UpdateBlockerSeverity, +): UpdateBlocker { + return { ...blocker, severity: severityOverride ?? BLOCKER_SEVERITY[blocker.reason] }; } export interface UpdateEligibility { @@ -134,6 +137,20 @@ export interface UpdateEligibilityContext { * eligibility model. Manual UI/API update requests leave it undefined and are never gated by it. */ maintenanceWindowOpen?: boolean; + /** + * Optional. Called with the container's own agent name when the `agent-mismatch` or + * `no-update-trigger-configured` branch is about to fire. Returning `true` (e.g. because + * the agent's client is mid-registration, per `AgentClient.isRegisteringComponents`) + * downgrades that blocker to `soft` instead of the reason's default `hard` severity, so the + * transient window between `AgentClient._doHandshake()` deregistering an agent's components + * and finishing their re-registration doesn't disable manual updates on display surfaces. + * + * This only softens *display* eligibility (container list, SSE enrichment). Admission + * (`app/updates/request-update.ts`) never wires this callback in, so a hard agent-mismatch + * always blocks the actual update request — an update can never be enqueued through a + * wrong-agent trigger during the registration window. See issue #605. + */ + isAgentPendingRegistration?: (agentName: string | undefined) => boolean; } /** @@ -442,32 +459,45 @@ export function computeUpdateEligibility( ); if (!typeOnlyTrigger) { - // 11. no-update-trigger-configured — no docker/dockercompose trigger exists at all + // 11. no-update-trigger-configured — no docker/dockercompose trigger exists at all. + // AgentClient._doHandshake() deregisters an agent's components before re-registering, + // so an agent-owned container can transiently see zero triggers of any kind during that + // window too. Apply the same #605 downgrade as agent-mismatch below. + const isPendingRegistration = container.agent + ? (context.isAgentPendingRegistration?.(container.agent) ?? false) + : false; blockers.push( - makeBlocker({ - reason: 'no-update-trigger-configured', - message: 'No docker or dockercompose action trigger is configured for this container.', - actionable: true, - actionHint: 'Configure `DD_ACTION_DOCKER_*` or `DD_ACTION_DOCKERCOMPOSE_*`.', - }), + makeBlocker( + { + reason: 'no-update-trigger-configured', + message: 'No docker or dockercompose action trigger is configured for this container.', + actionable: true, + actionHint: 'Configure `DD_ACTION_DOCKER_*` or `DD_ACTION_DOCKERCOMPOSE_*`.', + }, + isPendingRegistration ? 'soft' : undefined, + ), ); } else if (!candidateTrigger) { // A docker trigger exists but it's not compatible with this container's agent. // 10. agent-mismatch (detected here because full lookup failed but type-only succeeded) const t = typeOnlyTrigger; const triggerAgent = t.agent; + const isPendingRegistration = context.isAgentPendingRegistration?.(container.agent) ?? false; blockers.push( - makeBlocker({ - reason: 'agent-mismatch', - message: `Update trigger runs on agent '${triggerAgent ?? ''}'; container is on agent '${container.agent ?? ''}'.`, - actionable: true, - actionHint: 'Configure an update trigger for the target agent.', - details: { - triggerAgent, - containerAgent: container.agent, - triggerId: t.getId?.(), + makeBlocker( + { + reason: 'agent-mismatch', + message: `Update trigger runs on agent '${triggerAgent ?? ''}'; container is on agent '${container.agent ?? ''}'.`, + actionable: true, + actionHint: 'Configure an update trigger for the target agent.', + details: { + triggerAgent, + containerAgent: container.agent, + triggerId: t.getId?.(), + }, }, - }), + isPendingRegistration ? 'soft' : undefined, + ), ); } else { const t = candidateTrigger; diff --git a/app/package-lock.json b/app/package-lock.json index a937f6aae..4f2261c23 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -3737,9 +3737,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", - "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { @@ -4798,9 +4798,9 @@ } }, "node_modules/fast-uri": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-4.1.1.tgz", - "integrity": "sha512-YPOs1zD5TG2+EZt+r88LwF6mclA7TPkpwMP7ZN3TO2HiHS8TXvq7QA/17iJsV9dubcLo/f8eEYqMBruyQV21hQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-4.1.2.tgz", + "integrity": "sha512-TyGmBcbDTZXcb2cj5MV89DrF42DKvb3y5DDUNh95iO+IMeAzMkVSxK1PZRrRIpc9yg8U2GhGdbofNa0LS/a4Bw==", "funding": [ { "type": "github", @@ -5396,9 +5396,9 @@ "license": "ISC" }, "node_modules/ip-address": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", - "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.3.1.tgz", + "integrity": "sha512-1e9d3kb97NHJTIJDZW9rKqW2h6+dFa50Dy0fpPSMQp2ADje5gvKsXmdiK6dwY5t76TaTt5+P5N1Y/LoToIxP6g==", "license": "MIT", "engines": { "node": ">= 12" diff --git a/app/package.json b/app/package.json index b51c1c5d0..fb5cee5e8 100644 --- a/app/package.json +++ b/app/package.json @@ -74,8 +74,10 @@ "overrides": { "axios": "1.18.1", "body-parser": "2.3.0", - "fast-uri": "4.1.1", + "brace-expansion": "5.0.9", + "fast-uri": "4.1.2", "fast-xml-parser": "5.10.1", + "ip-address": "10.3.1", "picomatch": "4.0.5", "postcss": "8.5.24", "qs": "6.15.3", diff --git a/app/registry/index.test.ts b/app/registry/index.test.ts index d18064910..868720fea 100644 --- a/app/registry/index.test.ts +++ b/app/registry/index.test.ts @@ -765,6 +765,7 @@ test('registerAuthentications should register anonymous auth when confirmation i mockIsUpgrade.mockReturnValue(false); await registry.testable_registerAuthentications(); expect(Object.keys(registry.getState().authentication)).toEqual(['anonymous.anonymous']); + expect(registry.isAnonymousAuthenticationActive()).toBe(true); } finally { if (previousAnonymousConfirmation === undefined) { delete process.env.DD_ANONYMOUS_AUTH_CONFIRM; @@ -774,6 +775,19 @@ test('registerAuthentications should register anonymous auth when confirmation i } }); +test('isAnonymousAuthenticationActive should return false when no anonymous strategy is registered', async () => { + authentications = { + basic: { + john: { + user: 'john', + hash: TEST_BASIC_HASH, + }, + }, + }; + await registry.testable_registerAuthentications(); + expect(registry.isAnonymousAuthenticationActive()).toBe(false); +}); + test('registerAuthentications should fallback to anonymous when all configured providers fail and confirmation is enabled', async () => { const previousAnonymousConfirmation = process.env.DD_ANONYMOUS_AUTH_CONFIRM; process.env.DD_ANONYMOUS_AUTH_CONFIRM = 'true'; diff --git a/app/registry/index.ts b/app/registry/index.ts index b04d92823..29cf8ed3a 100644 --- a/app/registry/index.ts +++ b/app/registry/index.ts @@ -112,6 +112,17 @@ export function getAuthenticationRegistrationErrors(): AuthenticationRegistratio return [...authenticationRegistrationErrors]; } +/** + * Returns true when the registered authentication state contains a strategy + * whose description type is 'anonymous', i.e. anonymous authentication is + * the configured (and confirmed) auth mode. + */ +export function isAnonymousAuthenticationActive(): boolean { + return Object.values(state.authentication).some( + (authentication) => authentication.getStrategyDescription().type === 'anonymous', + ); +} + function addComponentToState(kind: ComponentKind, component: Component) { const components = state[kind] as Record; components[component.getId()] = component; diff --git a/app/updates/request-update.test.ts b/app/updates/request-update.test.ts index 2eb9a9c26..09ea0b33b 100644 --- a/app/updates/request-update.test.ts +++ b/app/updates/request-update.test.ts @@ -12,18 +12,29 @@ const { mockLogWarn, mockStatSync, mockGetUpdateMode, -} = vi.hoisted(() => ({ - mockGetOperationById: vi.fn(), - mockGetActiveOperationByContainerId: vi.fn(), - mockGetActiveOperationByContainerName: vi.fn(), - mockGetRecentTerminalSucceededOperationByContainerName: vi.fn(() => undefined), - mockHasOtherActiveOperationByContainerName: vi.fn(() => false), - mockInsertOperation: vi.fn(), - mockMarkOperationTerminal: vi.fn(), - mockGetState: vi.fn(() => ({ trigger: {}, watcher: {} })), - mockLogWarn: vi.fn(), - mockStatSync: vi.fn(() => ({ isSocket: () => false })), - mockGetUpdateMode: vi.fn(() => 'auto' as const), + mockGetAgent, + agentFixture, +} = vi.hoisted(() => { + const agentFixture = { name: 'edge-2', isRegisteringComponents: false }; + return { + mockGetOperationById: vi.fn(), + mockGetActiveOperationByContainerId: vi.fn(), + mockGetActiveOperationByContainerName: vi.fn(), + mockGetRecentTerminalSucceededOperationByContainerName: vi.fn(() => undefined), + mockHasOtherActiveOperationByContainerName: vi.fn(() => false), + mockInsertOperation: vi.fn(), + mockMarkOperationTerminal: vi.fn(), + mockGetState: vi.fn(() => ({ trigger: {}, watcher: {} })), + mockLogWarn: vi.fn(), + mockStatSync: vi.fn(() => ({ isSocket: () => false })), + mockGetUpdateMode: vi.fn(() => 'auto' as const), + mockGetAgent: vi.fn(), + agentFixture, + }; +}); + +vi.mock('../agent/manager.js', () => ({ + getAgent: mockGetAgent, })); vi.mock('../store/settings.js', () => ({ @@ -106,6 +117,8 @@ describe('request-update', () => { mockGetState.mockReturnValue({ trigger: {}, watcher: {} }); mockStatSync.mockReturnValue({ isSocket: () => false }); mockGetUpdateMode.mockReturnValue('auto'); + agentFixture.isRegisteringComponents = false; + mockGetAgent.mockReturnValue(undefined); mockInsertOperation.mockImplementation((operation) => ({ id: operation.id || 'op-1', ...operation, @@ -877,6 +890,31 @@ describe('request-update', () => { }); }); + test('stays a hard agent-mismatch rejection even when the agent is still completing registration (#605)', async () => { + agentFixture.isRegisteringComponents = true; + mockGetAgent.mockImplementation((name) => + name === agentFixture.name ? agentFixture : undefined, + ); + const trigger = { + type: 'docker', + trigger: vi.fn(), + agent: 'edge-1', + configuration: { threshold: 'all' }, + getId: () => 'docker.update', + isTriggerIncluded: () => true, + isTriggerExcluded: () => false, + }; + mockGetState.mockReturnValue({ trigger: { 'docker.update': trigger } }); + + await expect( + enqueueContainerUpdate(createContainerWithRawUpdate({ agent: 'edge-2' })), + ).rejects.toMatchObject>({ + statusCode: 404, + message: expect.stringContaining("Update trigger runs on agent 'edge-1'"), + }); + expect(mockGetAgent).not.toHaveBeenCalled(); + }); + test('rejects with 409 when self-update-unavailable blocker fires (drydock self-container, socket absent)', async () => { const trigger = { type: 'docker', diff --git a/app/updates/request-update.ts b/app/updates/request-update.ts index a6c9b4f56..69b1f8910 100644 --- a/app/updates/request-update.ts +++ b/app/updates/request-update.ts @@ -292,6 +292,12 @@ function prepareContainerUpdateRequest( // // The raw-candidate check above is the source of truth for "an update exists" // when a soft gate deliberately makes updateAvailable false. + // + // isAgentPendingRegistration is deliberately NOT wired in here. That softening + // exists only for display surfaces (container list, SSE enrichment) — admission + // stays hard/fail-closed so a hard agent-mismatch can never be bypassed to enqueue + // an update through a wrong-agent trigger during the component re-registration + // window. See issue #605. const eligibility = computeUpdateEligibility(container, { triggers: registry.getState().trigger, getActiveOperation: () => undefined, diff --git a/app/watchers/providers/docker/Docker.containers.labels-version-finding.test.ts b/app/watchers/providers/docker/Docker.containers.labels-version-finding.test.ts index 01a631b76..f1533dc92 100644 --- a/app/watchers/providers/docker/Docker.containers.labels-version-finding.test.ts +++ b/app/watchers/providers/docker/Docker.containers.labels-version-finding.test.ts @@ -257,7 +257,7 @@ describe('Docker Watcher', () => { const result = await docker.findNewVersion(container, mockLogChild); expect(result).toEqual({ tag: '1.0.0' }); - expect(mockLogChild.debug).toHaveBeenCalledWith( + expect(mockLogChild.warn).toHaveBeenCalledWith( expect.stringContaining('publish date lookup failed'), ); }); diff --git a/app/watchers/providers/docker/Docker.containers.test.ts b/app/watchers/providers/docker/Docker.containers.test.ts index 525f65836..1f83839b9 100644 --- a/app/watchers/providers/docker/Docker.containers.test.ts +++ b/app/watchers/providers/docker/Docker.containers.test.ts @@ -1085,7 +1085,7 @@ describe('Docker Watcher', () => { const result = await docker.findNewVersion(container, mockLogChild); expect(result).toEqual({ tag: '1.0.0' }); - expect(mockLogChild.debug).toHaveBeenCalledWith( + expect(mockLogChild.warn).toHaveBeenCalledWith( expect.stringContaining('publish date lookup failed'), ); }); diff --git a/app/watchers/providers/docker/image-comparison.test.ts b/app/watchers/providers/docker/image-comparison.test.ts index 429ccf6bb..90edf9c50 100644 --- a/app/watchers/providers/docker/image-comparison.test.ts +++ b/app/watchers/providers/docker/image-comparison.test.ts @@ -419,7 +419,28 @@ describe('image-comparison', () => { expect(result.publishedAt).toBeUndefined(); expect(result.publishedAtTrusted).toBeUndefined(); expect(result.digest).toBe('sha256:def456'); - expect(log.debug).toHaveBeenCalledWith(expect.stringContaining('registry timeout')); + expect(log.warn).toHaveBeenCalledWith(expect.stringContaining('registry timeout')); + }); + + test('digest-only with comparisonTag continues when getImagePublishedAt throws and logContainer.warn is absent', async () => { + mockGetState.mockReturnValue({ + registry: { + hub: { + getTags: vi.fn().mockResolvedValue(['latest']), + getImageManifestDigest: createManifestLookup(), + normalizeImage: identityNormalizeImage, + getImagePublishedAt: vi.fn().mockRejectedValue(new Error('timeout')), + }, + }, + }); + const log = { error: vi.fn(), debug: vi.fn() } as unknown as Parameters< + typeof findNewVersion + >[1]; + + const result = await findNewVersion(createDigestOnlyContainer() as never, log); + + expect(result.publishedAt).toBeUndefined(); + expect(result.digest).toBe('sha256:def456'); }); test('digest-only with comparisonTag does not set publishedAt when getImagePublishedAt returns non-string', async () => { @@ -808,7 +829,7 @@ describe('image-comparison', () => { expect(result.publishedAtTrusted).toBeUndefined(); }); - test('logs debug and continues when getImagePublishedAt throws', async () => { + test('logs warn and continues when getImagePublishedAt throws', async () => { mockGetState.mockReturnValue({ registry: { hub: { @@ -819,8 +840,35 @@ describe('image-comparison', () => { }, }, }); - const debugFn = vi.fn(); - const log = { error: vi.fn(), warn: vi.fn(), debug: debugFn }; + const warnFn = vi.fn(); + const log = { error: vi.fn(), warn: warnFn, debug: vi.fn() }; + const container = { + image: { + id: 'image-1', + registry: { name: 'hub' }, + name: 'library/nginx', + tag: { value: '1.0.0', semver: false }, + digest: { watch: false }, + }, + }; + const result = await findNewVersion(container as never, log); + expect(result.publishedAt).toBeUndefined(); + expect(warnFn).toHaveBeenCalledWith(expect.stringContaining('API error')); + }); + + test('logs warn on registry auth failure so it is not silently swallowed', async () => { + mockGetState.mockReturnValue({ + registry: { + hub: { + getTags: vi.fn().mockResolvedValue(['1.1.0']), + getImageManifestDigest: createManifestLookup(), + normalizeImage: identityNormalizeImage, + getImagePublishedAt: vi.fn().mockRejectedValue(new Error('401 Unauthorized')), + }, + }, + }); + const warnFn = vi.fn(); + const log = { error: vi.fn(), warn: warnFn, debug: vi.fn() }; const container = { image: { id: 'image-1', @@ -832,10 +880,10 @@ describe('image-comparison', () => { }; const result = await findNewVersion(container as never, log); expect(result.publishedAt).toBeUndefined(); - expect(debugFn).toHaveBeenCalledWith(expect.stringContaining('API error')); + expect(warnFn).toHaveBeenCalledWith(expect.stringContaining('401 Unauthorized')); }); - test('continues silently when getImagePublishedAt throws and logContainer.debug is absent', async () => { + test('continues silently when getImagePublishedAt throws and logContainer.warn is absent', async () => { mockGetState.mockReturnValue({ registry: { hub: { @@ -846,8 +894,8 @@ describe('image-comparison', () => { }, }, }); - // Intentionally omit debug from log to exercise the false branch of typeof logContainer.debug - const log = { error: vi.fn(), warn: vi.fn() } as unknown as Parameters< + // Intentionally omit warn from log to exercise the false branch of typeof logContainer.warn + const log = { error: vi.fn(), debug: vi.fn() } as unknown as Parameters< typeof findNewVersion >[1]; const container = { diff --git a/app/watchers/providers/docker/image-comparison.ts b/app/watchers/providers/docker/image-comparison.ts index 4d5131431..7db9f23c8 100644 --- a/app/watchers/providers/docker/image-comparison.ts +++ b/app/watchers/providers/docker/image-comparison.ts @@ -339,7 +339,9 @@ export async function findNewVersion( } } } catch (error: unknown) { - logContainer.debug(`Remote publish date lookup failed (${getErrorMessage(error)})`); + if (typeof logContainer.warn === 'function') { + logContainer.warn(`Remote publish date lookup failed (${getErrorMessage(error)})`); + } } } else { logContainer.debug('Digest-only image — no registry tag candidate available'); @@ -423,8 +425,8 @@ export async function findNewVersion( } } } catch (error: unknown) { - if (typeof logContainer.debug === 'function') { - logContainer.debug(`Remote publish date lookup failed (${getErrorMessage(error)})`); + if (typeof logContainer.warn === 'function') { + logContainer.warn(`Remote publish date lookup failed (${getErrorMessage(error)})`); } } diff --git a/apps/demo/package-lock.json b/apps/demo/package-lock.json index cd082a001..510e47273 100644 --- a/apps/demo/package-lock.json +++ b/apps/demo/package-lock.json @@ -24,9 +24,9 @@ "@iconify-json/fa6-solid": "1.2.4", "@iconify-json/heroicons": "1.2.3", "@iconify-json/iconoir": "1.2.11", - "@iconify-json/lucide": "1.2.119", + "@iconify-json/lucide": "1.2.121", "@iconify-json/ph": "1.2.2", - "@iconify-json/tabler": "1.2.37", + "@iconify-json/tabler": "1.2.38", "@tailwindcss/vite": "4.3.3", "@types/node": "25.9.5", "@vitejs/plugin-vue": "6.0.8", @@ -650,9 +650,9 @@ } }, "node_modules/@iconify-json/lucide": { - "version": "1.2.119", - "resolved": "https://registry.npmjs.org/@iconify-json/lucide/-/lucide-1.2.119.tgz", - "integrity": "sha512-KbrC7fT5wPshV1KLRM0k91poP0OH4eKEVywEOH0FCczKb+qth8Pcq1Hy9kS2tXeF52s23qzLnG8AJsExPSupeA==", + "version": "1.2.121", + "resolved": "https://registry.npmjs.org/@iconify-json/lucide/-/lucide-1.2.121.tgz", + "integrity": "sha512-zSoQQSmsyFaeTpSwURHJ9PIrsDcGFpDNhGlpiTrs+Ua4uFeH5UsE26L4OEBLrgLWoSK0UO+JhsO8yehSw0403g==", "dev": true, "license": "ISC", "dependencies": { @@ -670,9 +670,9 @@ } }, "node_modules/@iconify-json/tabler": { - "version": "1.2.37", - "resolved": "https://registry.npmjs.org/@iconify-json/tabler/-/tabler-1.2.37.tgz", - "integrity": "sha512-tXc0jGhrxec6eTKxMI9We/r3cEItyU5dG8ngDOEKsL36a6+/xwk4sR7PIZROVajzxmvyUBsn659adDIfcswiUw==", + "version": "1.2.38", + "resolved": "https://registry.npmjs.org/@iconify-json/tabler/-/tabler-1.2.38.tgz", + "integrity": "sha512-9v6ooRU/bKOK/Whv4w2aiK5gBnfCV9Ei+uD1iDDeLu2b+EnF3G3ZKkRKWEOOLG8bJyfKn6XEia6gUQ9oO7cF+Q==", "dev": true, "license": "MIT", "dependencies": { @@ -2846,9 +2846,9 @@ } }, "node_modules/postcss": { - "version": "8.5.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", - "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", + "version": "8.5.25", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", + "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", "funding": [ { "type": "opencollective", diff --git a/apps/demo/package.json b/apps/demo/package.json index 1e2834a31..9cc4040f3 100644 --- a/apps/demo/package.json +++ b/apps/demo/package.json @@ -27,9 +27,9 @@ "@iconify-json/fa6-solid": "1.2.4", "@iconify-json/heroicons": "1.2.3", "@iconify-json/iconoir": "1.2.11", - "@iconify-json/lucide": "1.2.119", + "@iconify-json/lucide": "1.2.121", "@iconify-json/ph": "1.2.2", - "@iconify-json/tabler": "1.2.37", + "@iconify-json/tabler": "1.2.38", "@tailwindcss/vite": "4.3.3", "@types/node": "25.9.5", "@vitejs/plugin-vue": "6.0.8", @@ -38,7 +38,7 @@ "vitest": "4.1.10" }, "overrides": { - "postcss": "8.5.23", + "postcss": "8.5.25", "yaml": "2.9.0", "esbuild": "0.28.1" }, diff --git a/apps/demo/src/mocks/data/agents.ts b/apps/demo/src/mocks/data/agents.ts index 3c70dc8a5..5e30b6425 100644 --- a/apps/demo/src/mocks/data/agents.ts +++ b/apps/demo/src/mocks/data/agents.ts @@ -4,7 +4,7 @@ export const agents = [ host: '192.168.1.50', port: 3001, connected: true, - version: '1.6.0-rc.11', + version: '1.6.0-rc.12', os: 'linux', arch: 'amd64', cpus: 4, diff --git a/apps/demo/src/mocks/data/audit.ts b/apps/demo/src/mocks/data/audit.ts index b4935a92f..dc58412f4 100644 --- a/apps/demo/src/mocks/data/audit.ts +++ b/apps/demo/src/mocks/data/audit.ts @@ -3,7 +3,7 @@ export const auditEntries = [ id: 'aud-001', timestamp: '2026-03-10T08:00:00.000Z', action: 'system:start', - details: 'Drydock v1.6.0-rc.11 started', + details: 'Drydock v1.6.0-rc.12 started', }, { id: 'aud-002', @@ -207,6 +207,6 @@ export const auditEntries = [ timestamp: '2026-03-03T18:00:00.000Z', action: 'container:watch', container: 'drydock', - details: 'Started watching ghcr.io/codeswhat/drydock:1.6.0-rc.11', + details: 'Started watching ghcr.io/codeswhat/drydock:1.6.0-rc.12', }, ]; diff --git a/apps/demo/src/mocks/data/containers.ts b/apps/demo/src/mocks/data/containers.ts index 312a64bf2..500f3ff39 100644 --- a/apps/demo/src/mocks/data/containers.ts +++ b/apps/demo/src/mocks/data/containers.ts @@ -330,7 +330,7 @@ export const containers = [ displayName: 'Drydock', displayIcon: 'sh-drydock', image: 'codeswhat/drydock', - tag: '1.6.0-rc.11', + tag: '1.6.0-rc.12', registryType: 'ghcr', registryUrl: 'https://ghcr.io', scanStatus: 'scanned', diff --git a/apps/demo/src/mocks/data/server.ts b/apps/demo/src/mocks/data/server.ts index 2ef6c05c4..c0154f8ae 100644 --- a/apps/demo/src/mocks/data/server.ts +++ b/apps/demo/src/mocks/data/server.ts @@ -1,5 +1,5 @@ export const serverInfo = { - version: '1.6.0-rc.11', + version: '1.6.0-rc.12', uptime: 864000, hostname: 'drydock-demo', platform: 'linux', diff --git a/apps/demo/src/mocks/handlers/app.ts b/apps/demo/src/mocks/handlers/app.ts index dc86a04c1..1d6f781ff 100644 --- a/apps/demo/src/mocks/handlers/app.ts +++ b/apps/demo/src/mocks/handlers/app.ts @@ -4,7 +4,7 @@ export const appHandlers = [ http.get('/api/v1/app', () => HttpResponse.json({ name: 'Drydock', - version: '1.6.0-rc.11', + version: '1.6.0-rc.12', description: 'Docker container update manager', repository: 'https://github.com/CodesWhat/drydock', documentation: 'https://getdrydock.com/docs', @@ -16,7 +16,7 @@ export const appHandlers = [ return HttpResponse.json( { generatedAt: new Date().toISOString(), - server: { version: '1.6.0-rc.11', mode: 'demo' }, + server: { version: '1.6.0-rc.12', mode: 'demo' }, summary: { containers: 25, watchers: 2, diff --git a/apps/web/package-lock.json b/apps/web/package-lock.json index 61fb4e424..3abde9a11 100644 --- a/apps/web/package-lock.json +++ b/apps/web/package-lock.json @@ -19,18 +19,18 @@ "fumadocs-mdx": "^15.0.12", "fumadocs-ui": "^16.10.5", "lucide-react": "^1.14.0", - "next": "16.2.11", + "next": "16.2.12", "react": "19.2.8", "react-dom": "19.2.8", "remark-custom-heading-id": "^2.0.0", "tailwind-merge": "^3.6.0" }, "devDependencies": { - "@biomejs/biome": "2.5.5", + "@biomejs/biome": "2.5.6", "@tailwindcss/postcss": "4.3.3", "@types/node": "25.9.5", - "@types/react": "19.2.17", - "@types/react-dom": "19.2.3", + "@types/react": "19.2.18", + "@types/react-dom": "19.2.4", "lefthook": "2.1.10", "tailwindcss": "4.3.3", "typescript": "6.0.3" @@ -53,9 +53,9 @@ } }, "node_modules/@biomejs/biome": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.5.tgz", - "integrity": "sha512-r1S8nFsAG1MY+vJFZALzIvwXAJv6ejDQ0mxP21Tgr9YK3ZFtjrvbBwDdNhx1rUqvccEIeNg20cYCNzl6Cr69pQ==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.6.tgz", + "integrity": "sha512-lxVNjv7UF6KfhMJfL9gaUHbWdJdHbsAj6OSmwSYNdhRuG67NxNQ4Xdvh3TUxsSK9sBzJBQhEJj3AopmmNJ5pSA==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { @@ -69,20 +69,20 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.5.5", - "@biomejs/cli-darwin-x64": "2.5.5", - "@biomejs/cli-linux-arm64": "2.5.5", - "@biomejs/cli-linux-arm64-musl": "2.5.5", - "@biomejs/cli-linux-x64": "2.5.5", - "@biomejs/cli-linux-x64-musl": "2.5.5", - "@biomejs/cli-win32-arm64": "2.5.5", - "@biomejs/cli-win32-x64": "2.5.5" + "@biomejs/cli-darwin-arm64": "2.5.6", + "@biomejs/cli-darwin-x64": "2.5.6", + "@biomejs/cli-linux-arm64": "2.5.6", + "@biomejs/cli-linux-arm64-musl": "2.5.6", + "@biomejs/cli-linux-x64": "2.5.6", + "@biomejs/cli-linux-x64-musl": "2.5.6", + "@biomejs/cli-win32-arm64": "2.5.6", + "@biomejs/cli-win32-x64": "2.5.6" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.5.tgz", - "integrity": "sha512-kUrAhXVWUrwmAUnV2iXSK7umxKFysTwvqK+Ty6ptUcLY/7T3SnCAjUowE4uvwaEej6nXZ7hu/dTtbokKdsPeag==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-zMOLZP4oMrjh6m1zcSj1ud2awUPgTuMVbmQhYYWL7J8HwCnbHHBvTm7VBTRuY7epT5bez76IpKYQ11ZAqHFlnw==", "cpu": [ "arm64" ], @@ -97,9 +97,9 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.5.tgz", - "integrity": "sha512-DamiYc5bUYZ2uxlfc+RLEPtz1Abb6PO5eTbOkufLpSGwd/7AMQAdxhFYiXmwwkJL8IsT8S7GvdgwDHqaMFAvKw==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.6.tgz", + "integrity": "sha512-JAC1VqzvO7Th5ZplU0G2uGfkZbxEe9uDDektPAhF0JLusoz1w+T4okp2bkykI0bbaO2vslKiRfj4gU43JaGreA==", "cpu": [ "x64" ], @@ -114,9 +114,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.5.tgz", - "integrity": "sha512-lRKF/pH/1RiYiBKExi3TCZVAtvzEm77aifrvcNiDFrR9WxeAnDUjDnseb6y2XV85mjitLs6SILGm2XG77cHtSQ==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.6.tgz", + "integrity": "sha512-6XsYwCFkp5sMxl85ffhgeGpGgs6A7dRYFnkceZ7WVxvycuTnGdD5xa534Z3xfrBQ0JCMK/mujT6ZNPJoghedwg==", "cpu": [ "arm64" ], @@ -134,9 +134,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.5.tgz", - "integrity": "sha512-U4WMl/sy/E/Q73vf15VspakLRRs2LDFcCeBxJnQfXzssb88zpV6PJPaQ3ezhQ7H6Ht2/8bvuZeHgJWzmoxllZg==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-eUa3jeeYvfMt19LBeh6E5PUZpxnTC4JqNWo+EDjTtQjAr2xLGnWaxACtVU1DQqmHYbvThlJzLX+ZsYgrqh2qVw==", "cpu": [ "arm64" ], @@ -154,9 +154,9 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.5.tgz", - "integrity": "sha512-H/O39nJEw/2Zm/fm7hrmxxoF8kK/aU1uCoPp70ruXVbomaAdLpJJnCmL11Q2JotT8QVHH06So04Oq53lCSwSwQ==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.6.tgz", + "integrity": "sha512-Pop9VXCFUhFTMfFefZ39S+u2rOPyNp5iHlxbZRwXGACHLy2r0jjiRgJHmaEKJzL3SyxlVeGShXhvvElvWowonA==", "cpu": [ "x64" ], @@ -174,9 +174,9 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.5.tgz", - "integrity": "sha512-m7wC7tjX5Lrmo69dc4md8FeKpPU1NTCY1v7xUoQQ2vadWwNnBS0KZOG8471otFPHrTHihQJAjQPgMObpLvDe6A==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-2Vp13QdKysH3HIWLaYLhUUwbK+jbZonJD1K+Lr0d0RO4wH7mkYd43vJixEDm8cUWrowoRz4UUHF1nm9Ae7ym8A==", "cpu": [ "x64" ], @@ -194,9 +194,9 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.5.tgz", - "integrity": "sha512-7BryINPuYypLUAH3o/o5ZdgomJ4zn3EDR0ChZJst7n32S6ZhKbgHXuYydLu+YAnx59ehGFR0z/MG6qnzQi3Yyw==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.6.tgz", + "integrity": "sha512-tDGshcm6BdkZOCGnTDX0Y8/U4IfBSlnUU7T56nNDuPEfed+aHg+u8G36NB43fJVl0Os6+QURXIE1yuD7AaEofA==", "cpu": [ "arm64" ], @@ -211,9 +211,9 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.5.tgz", - "integrity": "sha512-bIBFo+n6MIxdNcVFy5CrurbKiZQiUciK3bt8+O9I4wjFZNTfXLpi+giq47522eXqW5NBc9ulx7dR1SlZKi2J5g==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.6.tgz", + "integrity": "sha512-WN05KwXnTO/2J45RQPvzZMXf7tZUIofHoR35xIPfCo7pQ2RFidxI8sfb5mGsaTxdMmEOzHzOPRCdA5/fCpc7xQ==", "cpu": [ "x64" ], @@ -1357,15 +1357,15 @@ } }, "node_modules/@next/env": { - "version": "16.2.11", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.11.tgz", - "integrity": "sha512-0do5A3BJ2gxWr0ZCMcD6BhW+e595jyxdTl3rXTS6lOtD8ektMiW6CO+EPwt1Eca1DBnm90r/7GdiKWBKxH++DA==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.12.tgz", + "integrity": "sha512-d0Z5Bc13Fa4nR8pFAKx2jay2yhJM16vlfHbTzYnUQAxlNb6B6lmn4hjt69lYNt4kRtyYP6gEM49lPRHNbIyneg==", "license": "MIT" }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.2.11", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.11.tgz", - "integrity": "sha512-wryL4pjKmDwGv2ox6+GZDFxvmtSRLqApBR8kL1j4+vhB7Z5vJC/zAnXpiR9Xkfzl0AS8WLMnsuGV/UKI67/rrw==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.12.tgz", + "integrity": "sha512-0W1R0teHWJrqKX0FH20IzzIWAOuGtBxPGuObrxy1lE8hQvCFj49KE8a3WUg0D7sq6rn6zkM4c7YGUnhudBS6oA==", "cpu": [ "arm64" ], @@ -1379,9 +1379,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.2.11", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.11.tgz", - "integrity": "sha512-aZl2j4f/fLyjQvOhv0Oe9UaMAQHolYpKhctsoYzplSumKJKPUmgjcf6545aBtysLTcu994TREd0+pSgNE4ohmg==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.12.tgz", + "integrity": "sha512-Hy5Ls099+aFUmOLmIgPfLqNi6iCwhL3uQCssz5rWk+5Nkc6TUKCE83DY5BbNylfm3+mfwcSFnLRfrZDJhVxdtw==", "cpu": [ "x64" ], @@ -1395,9 +1395,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.2.11", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.11.tgz", - "integrity": "sha512-5jEriyEnH/LWFy27L2ZG0XaLlyEJIjhsImEsiS9P563PKEVp2BVups/xfOucIrsvVntp11oNcZwjHvaDPYVB5g==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.12.tgz", + "integrity": "sha512-+YqU2h1cQkHsGfvjAsrSmst8UIFBibBGm5x3Xgel8NLMiDQtNOM4sM2GOEMvG5YiOBNeN/Ykk8cQC2S0Xrqljg==", "cpu": [ "arm64" ], @@ -1414,9 +1414,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.2.11", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.11.tgz", - "integrity": "sha512-eIjcpx2fnnFSSkZDbTxy74KnokUXDjfoLClpWelfgHLf621aTqswhwXQ7GkD5K5rplrS6LZ/Bj+mVuvzluBOEg==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.12.tgz", + "integrity": "sha512-0qjhiYBaKAqF63LA1ZWAAnKTzFUguAaZiRa5etMLGGPj/B6uEVjtIZldIzFEp3wHlB0koK6aTzqPtSdplTCjoA==", "cpu": [ "arm64" ], @@ -1433,9 +1433,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.2.11", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.11.tgz", - "integrity": "sha512-8WgzpaWMs46qJT9kiV47cje86L0x/Mu9t8/Gwj+pnbgW3rETVfCnaScPjlYUwNScpOozdcIMHWmAvuZJUonR2w==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.12.tgz", + "integrity": "sha512-7A3q26W+h7gnA15uqBToNuDqBEFZZcqh0mW2mn4AJh/G5pdg2RVE3n4slzLEliASZFG3NmsbEzng/x2Sh09mBg==", "cpu": [ "x64" ], @@ -1452,9 +1452,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.2.11", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.11.tgz", - "integrity": "sha512-I3UgPds7G4ZYnTb/H+5GBGuUT2DhAk6j0mL6A4s63RjFs74wB2hOWP0vaxsK+3NJraExt3eYEPQ/UtT0x/64Nw==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.12.tgz", + "integrity": "sha512-qSjL/uppm+cbh21s72Ss8gkiOhQ4dExWHNGOWy6eZV7STj5WsKehgxT61beSsOj+YYQuTplL376lOCdMQU5T8w==", "cpu": [ "x64" ], @@ -1471,9 +1471,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.2.11", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.11.tgz", - "integrity": "sha512-n89CjtcThnjrwgJMAiI5xbqwLY51zvwC9tSlArmVndAJLYVl9T9UAdlkXTmZvE++idoXe8KdglQlhNRdUp1c6g==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.12.tgz", + "integrity": "sha512-X6hzsOUJac/e7AWSbn9gQ9nzHld1xWP5iyjHpYWvud8pufB679O1xg4JDyKr8Xd69Jvd+kM2Der6uftiZCmjYA==", "cpu": [ "arm64" ], @@ -1487,9 +1487,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.2.11", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.11.tgz", - "integrity": "sha512-md8CLNggS1Dx9pUgApzps5uAf+N8GN9xywzmNx9vHAWo94HtBwCCqkSnhIrdfQe83Dhz8Lfo/20Nb1Zxal092w==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.12.tgz", + "integrity": "sha512-F6fakeHuFTLOPt0bslQJdf+xtT+WIP9DVn/m4y1w1mRnVPyh3D/cNvzlRkxM444xfm+IvvYNSOrKiA2CDJ0Uxw==", "cpu": [ "x64" ], @@ -1502,15 +1502,6 @@ "node": ">= 10" } }, - "node_modules/@orama/orama": { - "version": "3.1.18", - "resolved": "https://registry.npmjs.org/@orama/orama/-/orama-3.1.18.tgz", - "integrity": "sha512-a61ljmRVVyG5MC/698C8/FfFDw5a8LOIvyOLW5fztgUXqUpc1jOfQzOitSCbge657OgXXThmY3Tk8fpiDb4UcA==", - "license": "Apache-2.0", - "engines": { - "node": ">= 20.0.0" - } - }, "node_modules/@radix-ui/number": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.3.tgz", @@ -1524,20 +1515,20 @@ "license": "MIT" }, "node_modules/@radix-ui/react-accordion": { - "version": "1.2.19", - "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.19.tgz", - "integrity": "sha512-ir4jA6TYdFonIC6n6ngyvaNQ6ReOMTqDg7MK/+D7U9KfWJtRoyoGLYwD1umh6LXGxc7QkKH1nwTOy7mewQz42w==", + "version": "1.2.20", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.20.tgz", + "integrity": "sha512-jDhG9FvAEnlhnjrsINbNXcUa4G+L1KqSkJSunkbKEzFRcAb52jvM0PjPxPRvhe1HNc5F5yc0yzzWeeqlH4yBIg==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-collapsible": "1.1.19", - "@radix-ui/react-collection": "1.1.14", - "@radix-ui/react-compose-refs": "1.1.4", - "@radix-ui/react-context": "1.2.1", - "@radix-ui/react-direction": "1.1.3", - "@radix-ui/react-id": "1.1.3", - "@radix-ui/react-primitive": "2.1.9", - "@radix-ui/react-use-controllable-state": "1.2.5" + "@radix-ui/react-collapsible": "1.1.20", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", @@ -1555,12 +1546,12 @@ } }, "node_modules/@radix-ui/react-arrow": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.14.tgz", - "integrity": "sha512-5GcTjiRmUKHDdGz2Fwm+SbVfGDVg1DBCoxUsc1fUL5k84ZYpfwIOjmTLKwCjOw2ZcH3w47rWkkx/vY/VxKuslw==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.15.tgz", + "integrity": "sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.9" + "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", @@ -1578,19 +1569,19 @@ } }, "node_modules/@radix-ui/react-collapsible": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.19.tgz", - "integrity": "sha512-TbvE0YFHJDRQXk6UpczD72Jr0UCGkJtAz3IVHE6W5N10sfJ0ZYAfVbR3+GR0/2BLZWAjHnOAFNSpzLFjdpYJdQ==", + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.20.tgz", + "integrity": "sha512-mcGesGplBnzN2sbvJETzpCNfSMyPnb29q1GRLU+Ib7bJrpIG2ywmRoh2V5VbA2uNvKikKUlVbAPks7JDjz4A8Q==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.4", - "@radix-ui/react-context": "1.2.1", - "@radix-ui/react-id": "1.1.3", - "@radix-ui/react-presence": "1.1.9", - "@radix-ui/react-primitive": "2.1.9", - "@radix-ui/react-use-controllable-state": "1.2.5", - "@radix-ui/react-use-layout-effect": "1.1.3" + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -1608,15 +1599,15 @@ } }, "node_modules/@radix-ui/react-collection": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.14.tgz", - "integrity": "sha512-+n2VZftI+uAtyvmgn9mM33Gdc1gdvc9Fz6pjYm4qtQIXhvDR5oBQDYJCL/z053Q9nTIErpGEbt5H9K83W+mK1Q==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.15.tgz", + "integrity": "sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.4", - "@radix-ui/react-context": "1.2.1", - "@radix-ui/react-primitive": "2.1.9", - "@radix-ui/react-slot": "1.3.2" + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3" }, "peerDependencies": { "@types/react": "*", @@ -1634,9 +1625,9 @@ } }, "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.4.tgz", - "integrity": "sha512-pWJo6lQAfR6uy1n7ii7PaCc9dLPwTXDYbQpORZU5B548Aqvl2pP1SM1vJGKyxIFqZMHRopRO4CQYX2iXAIB5jA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz", + "integrity": "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -1649,9 +1640,9 @@ } }, "node_modules/@radix-ui/react-context": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.1.tgz", - "integrity": "sha512-EraVbFjiIjibpLr6EjvEDmSCYJU2SlKDMiO+qEK/D9GOWnQoAQlpQo2occGYC1UM9MBeEx5Bek3UtW/Qi57vAg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.2.tgz", + "integrity": "sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -1664,24 +1655,24 @@ } }, "node_modules/@radix-ui/react-dialog": { - "version": "1.1.22", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.22.tgz", - "integrity": "sha512-ZvXpCrhEH2OrVa1l8caiuWzrk6Qleq6PeZQ824lXxUxFu6mE4/XzRz1DliOvS18WuBl3A3clg2gHPNiPqz8BjA==", + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.23.tgz", + "integrity": "sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.4", - "@radix-ui/react-context": "1.2.1", - "@radix-ui/react-dismissable-layer": "1.1.18", - "@radix-ui/react-focus-guards": "1.1.5", - "@radix-ui/react-focus-scope": "1.1.15", - "@radix-ui/react-id": "1.1.3", - "@radix-ui/react-portal": "1.1.16", - "@radix-ui/react-presence": "1.1.9", - "@radix-ui/react-primitive": "2.1.9", - "@radix-ui/react-slot": "1.3.2", - "@radix-ui/react-use-controllable-state": "1.2.5", - "@radix-ui/react-use-layout-effect": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, @@ -1701,9 +1692,9 @@ } }, "node_modules/@radix-ui/react-direction": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.3.tgz", - "integrity": "sha512-OdgAA/xb6WOQMKNn0mtbYoruMG6YMaBOnq+evWxSpMmiEMBdeFZawnIWRfPPeVXCRm+0lnN8jpJLjhD7/UIxWw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.4.tgz", + "integrity": "sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -1716,16 +1707,16 @@ } }, "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.18.tgz", - "integrity": "sha512-4tuTRonWG1Etcbc597++jlROMx7r34Otb0fATJhaK16Uq7XRotUS1aGyDX+yOBh668HQzhcpUpTLXFqMRVuFeA==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.19.tgz", + "integrity": "sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.4", - "@radix-ui/react-primitive": "2.1.9", - "@radix-ui/react-use-callback-ref": "1.1.3", - "@radix-ui/react-use-effect-event": "0.0.4" + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-effect-event": "0.0.5" }, "peerDependencies": { "@types/react": "*", @@ -1743,9 +1734,9 @@ } }, "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.5.tgz", - "integrity": "sha512-UQvlB7L/BYh3P8MLvwZnQkH521EDos40Rwnbt5+Qpg4Vbk0z3xJjRUmR6+aka4aT1IQQXFdO5bNPoE7cvFl5xQ==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.6.tgz", + "integrity": "sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -1758,14 +1749,14 @@ } }, "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.15.tgz", - "integrity": "sha512-rKjyFmJRsUhrCg39GJ0DOMfrOxHbw8WaS/KMOUPhLLm80LfDffRQGzF8RdulfDP3QfOJPJj4Sma+EU3E/90ykw==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.16.tgz", + "integrity": "sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.4", - "@radix-ui/react-primitive": "2.1.9", - "@radix-ui/react-use-callback-ref": "1.1.3" + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -1783,12 +1774,12 @@ } }, "node_modules/@radix-ui/react-id": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.3.tgz", - "integrity": "sha512-f/Wxm0ctyMymUJK0fqTSQlm85rbzdAkoNbPXJQ5+6caowVO8Yx+NWGjGz/oGhs/D+WIbbQpOrU0hU2Li2/42xQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.4.tgz", + "integrity": "sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.3" + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -1801,25 +1792,25 @@ } }, "node_modules/@radix-ui/react-navigation-menu": { - "version": "1.2.21", - "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.21.tgz", - "integrity": "sha512-Y5SmS1Qd0SSG724Siw7ie5i+XL8+h3UgWLJcC/HaPKhuOnZz9sWP11gu5I7ZAvJL+rYu7X/FRZujRdf+s1QXTg==", + "version": "1.2.22", + "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.22.tgz", + "integrity": "sha512-ou7iLEJ+yrhQndkkA4U21XIdS/CS45F4iXIkTZcb6/Ne9EMsOuDudVmCwmDnfFZZ+y1FZqXRNSIgBy+YMvZVZg==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-collection": "1.1.14", - "@radix-ui/react-compose-refs": "1.1.4", - "@radix-ui/react-context": "1.2.1", - "@radix-ui/react-direction": "1.1.3", - "@radix-ui/react-dismissable-layer": "1.1.18", - "@radix-ui/react-id": "1.1.3", - "@radix-ui/react-presence": "1.1.9", - "@radix-ui/react-primitive": "2.1.9", - "@radix-ui/react-use-callback-ref": "1.1.3", - "@radix-ui/react-use-controllable-state": "1.2.5", - "@radix-ui/react-use-layout-effect": "1.1.3", - "@radix-ui/react-use-previous": "1.1.3", - "@radix-ui/react-visually-hidden": "1.2.10" + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-previous": "1.1.4", + "@radix-ui/react-visually-hidden": "1.2.11" }, "peerDependencies": { "@types/react": "*", @@ -1837,25 +1828,24 @@ } }, "node_modules/@radix-ui/react-popover": { - "version": "1.1.22", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.22.tgz", - "integrity": "sha512-w5DCbDSP6TiQKmmfFPsZ+gNK/CN1OWx/vkTFFSH70aUPso6RbPNO+Rv08X4SYOblnLoN/x6N6jGsdEWfbeGLig==", + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.23.tgz", + "integrity": "sha512-mw58MrBlyHWFisTOYignD0vf/3gdcgAR+9of1s9G/38CbFiUwH1nCDkc0AUM9IrXFgN5Ue8n45j9WCgyM1sbiQ==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.4", - "@radix-ui/react-context": "1.2.1", - "@radix-ui/react-dismissable-layer": "1.1.18", - "@radix-ui/react-focus-guards": "1.1.5", - "@radix-ui/react-focus-scope": "1.1.15", - "@radix-ui/react-id": "1.1.3", - "@radix-ui/react-popper": "1.3.6", - "@radix-ui/react-portal": "1.1.16", - "@radix-ui/react-presence": "1.1.9", - "@radix-ui/react-primitive": "2.1.9", - "@radix-ui/react-slot": "1.3.2", - "@radix-ui/react-use-controllable-state": "1.2.5", - "@radix-ui/react-use-layout-effect": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-controllable-state": "1.2.6", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.2" }, @@ -1875,20 +1865,20 @@ } }, "node_modules/@radix-ui/react-popper": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.6.tgz", - "integrity": "sha512-hMA05k4dWrsMLWey2njJmGOvBoRirwlilS1EaGF8zufJJUZiJ7dW96p48xVsIj1oJXPdtlcK7yG8W+JcDhFP7A==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.7.tgz", + "integrity": "sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg==", "license": "MIT", "dependencies": { "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.14", - "@radix-ui/react-compose-refs": "1.1.4", - "@radix-ui/react-context": "1.2.1", - "@radix-ui/react-primitive": "2.1.9", - "@radix-ui/react-use-callback-ref": "1.1.3", - "@radix-ui/react-use-layout-effect": "1.1.3", - "@radix-ui/react-use-rect": "1.1.3", - "@radix-ui/react-use-size": "1.1.3", + "@radix-ui/react-arrow": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-rect": "1.1.4", + "@radix-ui/react-use-size": "1.1.4", "@radix-ui/rect": "1.1.3" }, "peerDependencies": { @@ -1907,13 +1897,13 @@ } }, "node_modules/@radix-ui/react-portal": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.16.tgz", - "integrity": "sha512-Lml0Z1vVXni7Sk29TTltPF2+SYTUrTJONrlrfQMTDGNKzEkXHIdHqYDQ/TkHEqsKT9Igy2jDWP7AmFeDeimd2w==", + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.17.tgz", + "integrity": "sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.9", - "@radix-ui/react-use-layout-effect": "1.1.3" + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -1931,12 +1921,12 @@ } }, "node_modules/@radix-ui/react-presence": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.9.tgz", - "integrity": "sha512-LTi1v05bprIb8/GSY/GWusI0jfsYjQ3CD3Nin8o7jVxnpHzVQfzjOQJoJTQkE9bdmOnsS7SFdhkXiBv8PrYnxw==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.10.tgz", + "integrity": "sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.3" + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -1954,12 +1944,12 @@ } }, "node_modules/@radix-ui/react-primitive": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.9.tgz", - "integrity": "sha512-aX5c84AYUD0EIodbQfpujB5XI0CbEaQ5U9du/wntNQK451i5iCJlE2JN6IN1LgQ3SnoBbnW/Jv1Z4rUowZV+hg==", + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.10.tgz", + "integrity": "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==", "license": "MIT", "dependencies": { - "@radix-ui/react-slot": "1.3.2" + "@radix-ui/react-slot": "1.3.3" }, "peerDependencies": { "@types/react": "*", @@ -1977,22 +1967,22 @@ } }, "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.18.tgz", - "integrity": "sha512-TLnxyyaccLuJV5p9PnApzswNNL5Zn9YEvYeDKKbO0dsvqq6Pg1vnExNzp4HnQ5MjhN8jwAOkq7HsDBZ9q66V0A==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.19.tgz", + "integrity": "sha512-V9jI6hDjT7l3jsCQD9bLNvDLM3tH/gdbOTp7Tefp3hbbgCGQoK7tUvrWiRlcoBHIZ809ElXwNQwVo0B98LuTXQ==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-collection": "1.1.14", - "@radix-ui/react-compose-refs": "1.1.4", - "@radix-ui/react-context": "1.2.1", - "@radix-ui/react-direction": "1.1.3", - "@radix-ui/react-id": "1.1.3", - "@radix-ui/react-primitive": "2.1.9", - "@radix-ui/react-use-callback-ref": "1.1.3", - "@radix-ui/react-use-controllable-state": "1.2.5", - "@radix-ui/react-use-is-hydrated": "0.1.2", - "@radix-ui/react-use-layout-effect": "1.1.3" + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-is-hydrated": "0.1.3", + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -2010,20 +2000,20 @@ } }, "node_modules/@radix-ui/react-scroll-area": { - "version": "1.2.17", - "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.17.tgz", - "integrity": "sha512-LTyRyEyxDV8mMx8ef/N+AaigP/XuHggt59um/AgQdjO113VzCXOGzsBb6Jvh7el/Mz4XwZ/+UilkXFDSD/LGiw==", + "version": "1.2.18", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.18.tgz", + "integrity": "sha512-Zn5Cd171wxsO3Dfg8HaW6RifTb9CYTKQJHs/G4+LN1GfmJpaQMZQyQxMprVPHpaz7QY4l9BxK2JwQuzHsXC8nA==", "license": "MIT", "dependencies": { "@radix-ui/number": "1.1.3", "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.4", - "@radix-ui/react-context": "1.2.1", - "@radix-ui/react-direction": "1.1.3", - "@radix-ui/react-presence": "1.1.9", - "@radix-ui/react-primitive": "2.1.9", - "@radix-ui/react-use-callback-ref": "1.1.3", - "@radix-ui/react-use-layout-effect": "1.1.3" + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -2041,13 +2031,12 @@ } }, "node_modules/@radix-ui/react-slot": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.3.2.tgz", - "integrity": "sha512-nLBh0hpzgQA16ioKAWTSQvGjRj7QcI3pTpngtYZ12b4RP9N8eGSj7ziceHyHe04zrd0bAcLyMijO0ws0DLmDWw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.3.3.tgz", + "integrity": "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.4" + "@radix-ui/react-compose-refs": "1.1.5" }, "peerDependencies": { "@types/react": "*", @@ -2060,19 +2049,19 @@ } }, "node_modules/@radix-ui/react-tabs": { - "version": "1.1.20", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.20.tgz", - "integrity": "sha512-/isB6Z/sIgMtnpGTig99pgMU0F8C4AmP7Dk36WN0PZ49H2mNjTpiCqmzIOOOZLez5F26DiZlQDFtPfQNMYb6Mg==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.21.tgz", + "integrity": "sha512-UKxJlZid7FVtsk/WTxj4i4uSEgj2Au+KBbS7SQyTlzMhhn+86Cz3tISZdTa87bfEfcuvZezf2ZsxD4xuEKtkog==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-context": "1.2.1", - "@radix-ui/react-direction": "1.1.3", - "@radix-ui/react-id": "1.1.3", - "@radix-ui/react-presence": "1.1.9", - "@radix-ui/react-primitive": "2.1.9", - "@radix-ui/react-roving-focus": "1.1.18", - "@radix-ui/react-use-controllable-state": "1.2.5" + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", @@ -2090,9 +2079,9 @@ } }, "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.3.tgz", - "integrity": "sha512-AUS7HoBBAncIsGMLNG+CcpLuJ+JIBbZzmyM8Qdb1eIThX0AlhSSC6wn40xfBlPE+ypx/vSSiRWnklUAjy3U3UA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.4.tgz", + "integrity": "sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -2105,14 +2094,14 @@ } }, "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.5.tgz", - "integrity": "sha512-UB1dXpxvHjR48poyKdKdTm7jT0kp3elkUKdKQiOkirlbYumqXinSJtrjDsr9maXNPvL12bKI4CDSmydms/9Aeg==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.6.tgz", + "integrity": "sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-use-effect-event": "0.0.4", - "@radix-ui/react-use-layout-effect": "1.1.3" + "@radix-ui/react-use-effect-event": "0.0.5", + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -2125,12 +2114,12 @@ } }, "node_modules/@radix-ui/react-use-effect-event": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.4.tgz", - "integrity": "sha512-XYcfa6wlXDCwQtePuEiPmXLSAhGL4DWtedSyRgGbG3y10mw+OnrLp6SyeY1gJFMiYF0Dx0nMAX9InylKbLEFQQ==", + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.5.tgz", + "integrity": "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.3" + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -2143,9 +2132,9 @@ } }, "node_modules/@radix-ui/react-use-is-hydrated": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.2.tgz", - "integrity": "sha512-2+gAVu9uaSLbopCTvvuWX9MIgEOlyqXC1Ok+KLCO7cZPSHLENs7dL0KbFQUGFIcFKmDW/bmaQRJtd8E3cnMRUw==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.3.tgz", + "integrity": "sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -2158,9 +2147,9 @@ } }, "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.3.tgz", - "integrity": "sha512-rDiah9wvtqihWtWz02XreeRKIxt2EJF8y5D9rtY9l5A2zxePAtcPiOMpDugNRw5bFHz+1/8viVoc7ZVKiJknCw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.4.tgz", + "integrity": "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -2173,9 +2162,9 @@ } }, "node_modules/@radix-ui/react-use-previous": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.3.tgz", - "integrity": "sha512-kbefNKla5AGhgvMqrg/qS7mYVLXTEHQLqoFIS03nKN2dAfQQ5PK5Tp+2LxS7BV6a6l4HHv4jTg6rtV2Uajwe2w==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.4.tgz", + "integrity": "sha512-XoSLhbRbqxFtgJoi2fNHA3C6pDlY34x508vUpUGoFZfvePfHXHbE1lC4FYFMnJWgiCRroSTw6fOsXQoVS9RwZg==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -2188,9 +2177,9 @@ } }, "node_modules/@radix-ui/react-use-rect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.3.tgz", - "integrity": "sha512-W0GSYZFKEfi6raMiMEfJSngvVbFDIyxtW5JuVg5NoQBY59l0dVQVGLbhog/tOdwq0qtZ+TXuX1ikSNan4/IZXA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.4.tgz", + "integrity": "sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ==", "license": "MIT", "dependencies": { "@radix-ui/rect": "1.1.3" @@ -2206,12 +2195,12 @@ } }, "node_modules/@radix-ui/react-use-size": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.3.tgz", - "integrity": "sha512-jJq6tQQLvO/z4uWbztjwPV+1a/+H4rCaWypasLB/ac8DEdd6+p7dIm7o3F6P2KZPGkPMqD4s5424EdAdoU+GLA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.4.tgz", + "integrity": "sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.3" + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -2224,12 +2213,12 @@ } }, "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.10.tgz", - "integrity": "sha512-j1q1Aw5zpNp/mlNSkXeQNsF4RhRd0g2KYgR/BeN20D+PGncx4NmWQ5sMriucY9hlYG3jVRW5jH9PCQShL+ZiDA==", + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.11.tgz", + "integrity": "sha512-NFS86RYYZb4/exihaESBGOpMJFz8MGLAfu3mOBSGByVnVPC9JPASfYubxd/8KbkQK0sYAv8lVQDEQukDX/qXvQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.9" + "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", @@ -2781,9 +2770,9 @@ } }, "node_modules/@types/react": { - "version": "19.2.17", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", - "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", + "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", "devOptional": true, "license": "MIT", "dependencies": { @@ -2791,9 +2780,9 @@ } }, "node_modules/@types/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==", "devOptional": true, "license": "MIT", "peerDependencies": { @@ -2893,159 +2882,170 @@ } }, "node_modules/@yuku-analyzer/binding-darwin-arm64": { - "version": "0.6.12", - "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-darwin-arm64/-/binding-darwin-arm64-0.6.12.tgz", - "integrity": "sha512-9rpIP7IeybjyvWUf6WnU24h1qo+JdxIHr1o3yb06HoE8tM3S/Jh5RrUw9aw5P9BKSIvSPbLyVlItX7PcD3o5bQ==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-darwin-arm64/-/binding-darwin-arm64-0.8.1.tgz", + "integrity": "sha512-p1LROfS/Fcz+10UFf+uSlVrawlJNdWKHLSH1jOnVdcaa82oaWbehoEdocS6DkYJxcARzG2QpkAGLBrIiMKCRBg==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@yuku-analyzer/binding-darwin-x64": { - "version": "0.6.12", - "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-darwin-x64/-/binding-darwin-x64-0.6.12.tgz", - "integrity": "sha512-ELLhNT4FGnqY8yh0W3cSs9rGMSeUyhib1aYD84RupjlfsrDTrQRoDhWu01Dv6xCfYgASYaj1Abntk91A7njNag==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-darwin-x64/-/binding-darwin-x64-0.8.1.tgz", + "integrity": "sha512-do28aZr3eQ0R61f3RiVOF+vvCw6oq7y/FcOU0KVCZOozH8Tr9ervprgAy7KbfWBezRODu5oa/XMdzP26vnstaA==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@yuku-analyzer/binding-freebsd-x64": { - "version": "0.6.12", - "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-freebsd-x64/-/binding-freebsd-x64-0.6.12.tgz", - "integrity": "sha512-s76XocUMlK9liTyipALFb2K64ku35u/wg238A0NW8U5CUDsuIe/8tu5TzdLjJAGxnd0IV+gBneDt9cJJzLeFRQ==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-freebsd-x64/-/binding-freebsd-x64-0.8.1.tgz", + "integrity": "sha512-9pZxagkj2SOVWs7EF/XArW+1OiY4DHJCxoT/nybYrqNPxrDqk8KmSw3vdkE1GAOp00eaZ256230omoGTm8JdWA==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@yuku-analyzer/binding-linux-arm-gnu": { - "version": "0.6.12", - "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-linux-arm-gnu/-/binding-linux-arm-gnu-0.6.12.tgz", - "integrity": "sha512-hm8Tq0umop3RGu6dOMF61q69tYn1bDp1CeYD5ZjuGFQJclp0moVtjzY4z0bzusicKeZ9+k5LRroR0p5HWC2hDw==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-linux-arm-gnu/-/binding-linux-arm-gnu-0.8.1.tgz", + "integrity": "sha512-uvXjAUndqZQIdqOHdWZMj/rwMVYGr44zLIULkfW/7FhIqMWAoC4PxTp94NsX2X3jj4B6RYF0GGO2ChOW8Ge3+Q==", "cpu": [ "arm" ], "libc": [ "glibc" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@yuku-analyzer/binding-linux-arm-musl": { - "version": "0.6.12", - "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-linux-arm-musl/-/binding-linux-arm-musl-0.6.12.tgz", - "integrity": "sha512-CxtPKLddogHAB3ZHVWaUl+U8jx0pdriTSbQ1K/orlDqU0GDhg8LuIRyUscP7r2/62fGGMzkc119fE71I4Nl1Fg==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-linux-arm-musl/-/binding-linux-arm-musl-0.8.1.tgz", + "integrity": "sha512-f1dPM/0KCwv02dynHF0dmbW4iL7KfXIypWOCO4lh/VSLwPQYjd6RcI+LmKN6oaVhtELzBFguordipnIWbjpsKg==", "cpu": [ "arm" ], "libc": [ "musl" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@yuku-analyzer/binding-linux-arm64-gnu": { - "version": "0.6.12", - "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.6.12.tgz", - "integrity": "sha512-EOyLcpAmF5qAVDKmKvV7xt8oBGeWQ92CqFI4s7h7TRlrF6TfGRrh8PwawGn92gFploNLAYj/1Z9Q1gVvwGgG9g==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.8.1.tgz", + "integrity": "sha512-NmtkwKscALPIZNlvHAdGMYQ0J9Ss7skKGdlom8ikWPF6L/5dH/TibqLhW7w8BwACCNYTAgKQN6B4uYfomL4fZw==", "cpu": [ "arm64" ], "libc": [ "glibc" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@yuku-analyzer/binding-linux-arm64-musl": { - "version": "0.6.12", - "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.6.12.tgz", - "integrity": "sha512-T3eCYy6bMnVRMQEYAbDcpj08/XM93dBTtnn/DDocJN21RARe+KCzWKeL26J3yd3bOW3WVjVLq09BfdpAGB0buQ==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.8.1.tgz", + "integrity": "sha512-RGU5VLGYzHqIH0lbHv5qWGEeaDo5Cy5AG4Y3bIO/79nvS4fR2MamHzgRzTYmlgYLZVeK9RfL52hVv5sSfvp4Xg==", "cpu": [ "arm64" ], "libc": [ "musl" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@yuku-analyzer/binding-linux-x64-gnu": { - "version": "0.6.12", - "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.6.12.tgz", - "integrity": "sha512-1Y+noIuvnDugIVsoIr5NduZqX7KuFTzICSkvG8RW3OKK9URVeTOicKK217i44ABZSSZJ7A0E7vzifapx0c9VDw==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.8.1.tgz", + "integrity": "sha512-274/hhQaj0WV8E88jSWvQFpqllRTtov41uLVwPJvpa4PAfD39VDb61No21v49+jjPwv7xrDOiheVDp9GvIOslg==", "cpu": [ "x64" ], "libc": [ "glibc" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@yuku-analyzer/binding-linux-x64-musl": { - "version": "0.6.12", - "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-linux-x64-musl/-/binding-linux-x64-musl-0.6.12.tgz", - "integrity": "sha512-woN/GuG95Fd6bp+ZQfmiFrZnoA2hdu3vfVSc89A8LElnYpzFaJM81sOZp8f3tVOVUJxbt7KAUiCLwSy34MJKqA==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-linux-x64-musl/-/binding-linux-x64-musl-0.8.1.tgz", + "integrity": "sha512-ZkYH/op9ykMsJMGuHRGcyHlYvLvhQsTbiCClqzyu847mFukJvFP6COG8Lfq3929oBrcPwoulwuYqMUO5Jubpkw==", "cpu": [ "x64" ], "libc": [ "musl" ], + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@yuku-analyzer/binding-win32-arm64": { - "version": "0.6.12", - "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-win32-arm64/-/binding-win32-arm64-0.6.12.tgz", - "integrity": "sha512-8OVFnKbK+lgsL6MqILPLpzlsa00K4KiKsdbHH94hpGcrqaz1jv+k0Y7ujSaoYTWw5Bb7Lr9GJ3L1n1hT2sXoYA==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-win32-arm64/-/binding-win32-arm64-0.8.1.tgz", + "integrity": "sha512-jXlKxJdrX9lsNSIoLgxZ9m0wqqPUWc+KoPxltbZKXhJNCj5hQBQCBgwOkn+QXNDPxFbEi3tlMOpiOvKXQ0S1DQ==", "cpu": [ "arm64" ], + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@yuku-analyzer/binding-win32-x64": { - "version": "0.6.12", - "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-win32-x64/-/binding-win32-x64-0.6.12.tgz", - "integrity": "sha512-3w8w1Xc5njwgbGTcn3JfDxWuQnFvtSll1D8gBlk4U8CI5v7ibKOMIdABucCXH8WtsRREG0ME5Vn0i422eX3zLQ==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@yuku-analyzer/binding-win32-x64/-/binding-win32-x64-0.8.1.tgz", + "integrity": "sha512-IyyYuVmFRYB/w5iTQ3QurgjQPLh3jUNMeWnE4o3Y4FKAUDWiQAEs9J5iNpBeM1f8SrX8Dy4vJwV7E/5enkL84A==", "cpu": [ "x64" ], + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@yuku-toolchain/types": { - "version": "0.6.11", - "resolved": "https://registry.npmjs.org/@yuku-toolchain/types/-/types-0.6.11.tgz", - "integrity": "sha512-i1JYFNJaKNCgyJ/nVoR8GK7wvlXF+ShYzFHBauWcvg8IoiXInK7pVziHcgNz/MWLPNr/Mb/CtmXccrJMkKqSHQ==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@yuku-toolchain/types/-/types-0.8.1.tgz", + "integrity": "sha512-HcGEV3kOn9evBTm2ARYOFNKAI7sY9twQozCVd9EVdlsBlnKi0a2iv8xXxYVHFCBQpX3SvvPXBhk8lcK6NUTdNg==", "license": "MIT" }, "node_modules/acorn": { @@ -3225,9 +3225,9 @@ } }, "node_modules/cnfast": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/cnfast/-/cnfast-0.0.8.tgz", - "integrity": "sha512-EjXKMfGfdwtV4AcNSQ6AwQaVzpC1B7IxeiwA3FlhTXz+YFlMKVi4c1JX9tgD2QOlahQXjB8KUXrBaYG+3v871Q==", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cnfast/-/cnfast-0.1.0.tgz", + "integrity": "sha512-rH0jBKeLkVrK7NsZ5Ba2l7WdMBmm1k0FMpABeXUU1PgTUxbz3261gEuCSsrYuXo4BwAe3yCcIbQ93YyS52lOGQ==", "license": "MIT", "bin": { "cnfast": "bin/cli.js" @@ -3572,12 +3572,12 @@ } }, "node_modules/framer-motion": { - "version": "12.42.2", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.42.2.tgz", - "integrity": "sha512-5XY9luDiu0oHfHBjpDthFMh0ES+122w6p/papSJBweMkO8Sn+PW2QaEgRblQBpWFnuvZS5qvarpt/hO2pjGmnw==", + "version": "12.43.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.43.0.tgz", + "integrity": "sha512-1eaL3RvR/kAlbG7UYcpMptEyzPoENO0c6w7ZnB3/hh2vSAz/6uGAFn6fdoqTBguNstf3MsFhJHsD/0DHiclG+g==", "license": "MIT", "dependencies": { - "motion-dom": "^12.42.2", + "motion-dom": "^12.43.0", "motion-utils": "^12.39.0", "tslib": "^2.4.0" }, @@ -3599,19 +3599,18 @@ } }, "node_modules/fumadocs-core": { - "version": "16.12.1", - "resolved": "https://registry.npmjs.org/fumadocs-core/-/fumadocs-core-16.12.1.tgz", - "integrity": "sha512-6NnDxUqe0hIiShbWjqvLXvPYV0n0gi01UHmDAkDs5KVcfxfgOPz5bAbj45JDY0Ykq39Mr8z1xRt9h/HwIhe8fw==", + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/fumadocs-core/-/fumadocs-core-16.14.0.tgz", + "integrity": "sha512-CQBsVm2XoxytoK5iTxd2Q3L76XBXY9yrWdThH9iJxZPcZ4aHED7YJCYuHPHZDIWg80fy1UAgVc6ogfE+24jTDA==", "license": "MIT", "dependencies": { - "@orama/orama": "^3.1.18", "estree-util-value-to-estree": "^3.5.0", "github-slugger": "^2.0.0", "hast-util-to-estree": "^3.1.3", "hast-util-to-jsx-runtime": "^2.3.6", "mdast-util-mdx": "^3.0.0", "mdast-util-to-markdown": "^2.1.2", - "npm-to-yarn": "3.1.0", + "npm-to-yarn": "3.2.0", "remark": "^15.0.1", "remark-gfm": "^4.0.1", "remark-rehype": "^11.1.2", @@ -3621,7 +3620,8 @@ "unified": "^11.0.5", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3", - "yaml": "^2.9.0" + "yaml": "^2.9.0", + "zbsearch": "^3.3.4" }, "peerDependencies": { "@mdx-js/mdx": "*", @@ -3701,9 +3701,9 @@ } }, "node_modules/fumadocs-mdx": { - "version": "15.2.0", - "resolved": "https://registry.npmjs.org/fumadocs-mdx/-/fumadocs-mdx-15.2.0.tgz", - "integrity": "sha512-+yBP8QYw5wA9LF5eVdMhwbP7KT1OF4B/YfC6PZoD2jz0amZi1B+6QHTI6XoRRSTmhWrI4cL5LU1DspW0itk+NA==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/fumadocs-mdx/-/fumadocs-mdx-15.2.1.tgz", + "integrity": "sha512-lyx35MAFAj9yuLPudNoRGGvauZlT1xRLLw17P0jnvhXikrJNC8mAeg/4WIity5K+3V6ZetBQ2PYIcnli450vMg==", "license": "MIT", "dependencies": { "@mdx-js/mdx": "^3.1.1", @@ -3712,7 +3712,7 @@ "esbuild": "^0.28.1", "estree-util-value-to-estree": "^3.5.0", "github-slugger": "^2.0.0", - "magic-string": "^0.30.21", + "magic-string": "^1.1.0", "mdast-util-mdx": "^3.0.0", "picocolors": "^1.1.1", "picomatch": "^4.0.5", @@ -3723,7 +3723,7 @@ "unist-util-visit": "^5.1.0", "vfile": "^6.0.3", "yaml": "^2.9.0", - "yuku-analyzer": "^0.6.3", + "yuku-analyzer": "^0.8.1", "zod": "^4.4.3" }, "bin": { @@ -3775,28 +3775,37 @@ } } }, + "node_modules/fumadocs-mdx/node_modules/magic-string": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.1.0.tgz", + "integrity": "sha512-kS3VHe0nEPST2saQV4Rbkchcd3UBRkVTQHo1D3h/ZTwFDhai/mfKkmtPAtD129EOI7K3HlHIsFOt0WrI2/oU9g==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/fumadocs-ui": { - "version": "16.12.1", - "resolved": "https://registry.npmjs.org/fumadocs-ui/-/fumadocs-ui-16.12.1.tgz", - "integrity": "sha512-/YYERe99PJYw09RiYmCetdcu9uIjrUff+uoYk1EzgTLNtKlt2FNJJCcWYykG76wWlT28hXRs1bt8eFVq9dIU9w==", + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/fumadocs-ui/-/fumadocs-ui-16.14.0.tgz", + "integrity": "sha512-MijJ96VzC1EPOGJutf+t6ptuGRl2y4h33iwECmM79TvEHvf8Uxtnb1vFMQz0labHvuN/lSR1bV2SENH7t28W4w==", "license": "MIT", "dependencies": { "@fuma-translate/react": "^1.0.2", "@fumadocs/tailwind": "0.1.1", - "@radix-ui/react-accordion": "^1.2.17", - "@radix-ui/react-collapsible": "^1.1.17", - "@radix-ui/react-dialog": "^1.1.20", - "@radix-ui/react-direction": "^1.1.2", - "@radix-ui/react-navigation-menu": "^1.2.19", - "@radix-ui/react-popover": "^1.1.20", - "@radix-ui/react-presence": "^1.1.8", - "@radix-ui/react-scroll-area": "^1.2.15", - "@radix-ui/react-slot": "^1.3.0", - "@radix-ui/react-tabs": "^1.1.18", + "@radix-ui/react-accordion": "^1.2.20", + "@radix-ui/react-collapsible": "^1.1.20", + "@radix-ui/react-dialog": "^1.1.23", + "@radix-ui/react-direction": "^1.1.4", + "@radix-ui/react-navigation-menu": "^1.2.22", + "@radix-ui/react-popover": "^1.1.23", + "@radix-ui/react-presence": "^1.1.10", + "@radix-ui/react-scroll-area": "^1.2.18", + "@radix-ui/react-slot": "^1.3.3", + "@radix-ui/react-tabs": "^1.1.21", "class-variance-authority": "^0.7.1", - "cnfast": "^0.0.8", - "lucide-react": "^1.25.0", - "motion": "^12.42.2", + "cnfast": "^0.1.0", + "lucide-react": "^1.27.0", + "motion": "^12.43.0", "next-themes": "^0.4.6", "react-remove-scroll": "^2.7.2", "rehype-raw": "^7.0.0", @@ -3807,7 +3816,7 @@ "peerDependencies": { "@types/mdx": "*", "@types/react": "*", - "fumadocs-core": "16.12.1", + "fumadocs-core": "16.14.0", "next": "16.x.x", "react": "^19.2.0", "react-dom": "^19.2.0", @@ -4564,9 +4573,9 @@ } }, "node_modules/lucide-react": { - "version": "1.26.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.26.0.tgz", - "integrity": "sha512-raglYVR2+VkMfJL158krjVmE+rV5ST2lzA/KQm1FRSjMHT4MnWaegHxoVEpmc2So3nOEhp9oGejJwAPX8MoAjg==", + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.28.0.tgz", + "integrity": "sha512-fARAFJULsGuDDydjp6+6blekG/sBIM29TerzLjc9bQUKAcEfrSc4ZQKb25KRz4OMKd87cZTb5dgq0w/T6KufVg==", "license": "ISC", "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -4576,6 +4585,7 @@ "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" @@ -5623,12 +5633,12 @@ "license": "MIT" }, "node_modules/motion": { - "version": "12.42.2", - "resolved": "https://registry.npmjs.org/motion/-/motion-12.42.2.tgz", - "integrity": "sha512-Atvv11yUKIid41cVrRBDVX5m8tF8kNpExRSlbpt6APClhDjtwQssgFHhQzejxw7/7YYbjHSPKBVbHo05BuJT5Q==", + "version": "12.43.0", + "resolved": "https://registry.npmjs.org/motion/-/motion-12.43.0.tgz", + "integrity": "sha512-BQgQbSa9Hn3/mtbib0MK53y6JSANa+YKUKlaYnWzAVDH424RYQ5LVpV3pNiWH00BA2z4ojsSdMzqT7g2FQwjuQ==", "license": "MIT", "dependencies": { - "framer-motion": "^12.42.2", + "framer-motion": "^12.43.0", "tslib": "^2.4.0" }, "peerDependencies": { @@ -5649,9 +5659,9 @@ } }, "node_modules/motion-dom": { - "version": "12.42.2", - "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.42.2.tgz", - "integrity": "sha512-5gIMWLp/PycBtJRJWRgjxke5n8dlvkSn2DrYW+tr3XcqAZY1xZh6BJyooJXCM8wdfM7wfMjkBJNLge1CKPUIRA==", + "version": "12.43.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.43.0.tgz", + "integrity": "sha512-azKON4d9S65PEoFUiQTMTgPheEmzf2QngdRc50AKfJp9Q9mmcBVw22c8eMq9k8kxOFHdL7+WZY7N/5F/lwiDag==", "license": "MIT", "dependencies": { "motion-utils": "^12.39.0" @@ -5688,12 +5698,12 @@ } }, "node_modules/next": { - "version": "16.2.11", - "resolved": "https://registry.npmjs.org/next/-/next-16.2.11.tgz", - "integrity": "sha512-B339zaqbyK8cmxhoAvLrcwoabwCP1wz21zSzfqxqXAemTu2BXnH7tQnfcglKv1vnMUIDBc+Hth7XODQriTZiRQ==", + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/next/-/next-16.2.12.tgz", + "integrity": "sha512-iD59eYQWmbFcEbX7v/acG5DRym9iw1DdaPoD0WTA920naWsE25wShzJW4+UvAs8MK9EC2kBfIH6vtto1H1PHGw==", "license": "MIT", "dependencies": { - "@next/env": "16.2.11", + "@next/env": "16.2.12", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", @@ -5707,14 +5717,14 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.2.11", - "@next/swc-darwin-x64": "16.2.11", - "@next/swc-linux-arm64-gnu": "16.2.11", - "@next/swc-linux-arm64-musl": "16.2.11", - "@next/swc-linux-x64-gnu": "16.2.11", - "@next/swc-linux-x64-musl": "16.2.11", - "@next/swc-win32-arm64-msvc": "16.2.11", - "@next/swc-win32-x64-msvc": "16.2.11", + "@next/swc-darwin-arm64": "16.2.12", + "@next/swc-darwin-x64": "16.2.12", + "@next/swc-linux-arm64-gnu": "16.2.12", + "@next/swc-linux-arm64-musl": "16.2.12", + "@next/swc-linux-x64-gnu": "16.2.12", + "@next/swc-linux-x64-musl": "16.2.12", + "@next/swc-win32-arm64-msvc": "16.2.12", + "@next/swc-win32-x64-msvc": "16.2.12", "sharp": "^0.34.5" }, "peerDependencies": { @@ -5751,9 +5761,9 @@ } }, "node_modules/npm-to-yarn": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/npm-to-yarn/-/npm-to-yarn-3.1.0.tgz", - "integrity": "sha512-9gNsO/JB3LeWOZXBX09cKMsCPwVcu1ExIf+GUuTN9G+0zZvLIK0nU9+lE9jue3MSKAxPdrh0rO072mWNvciqeQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/npm-to-yarn/-/npm-to-yarn-3.2.0.tgz", + "integrity": "sha512-K1HmQeZT2HrjpsR6KgqbN2FAXL2NrJJmNUSD9ck7HGTVu1JKXox8n9SB+tjbU8m8JGLF4OscrroPepew/L7/Xw==", "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -5835,9 +5845,9 @@ } }, "node_modules/postcss": { - "version": "8.5.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", - "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", + "version": "8.5.25", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", + "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", "funding": [ { "type": "opencollective", @@ -6711,42 +6721,45 @@ } }, "node_modules/yuku-analyzer": { - "version": "0.6.12", - "resolved": "https://registry.npmjs.org/yuku-analyzer/-/yuku-analyzer-0.6.12.tgz", - "integrity": "sha512-0zu/gwv6nKA3wm2GMjM1iczw9rbt77ijEyR5tXpPQ8AZcXIpXlll66BXOtMHgYudLn91bJx0ybhpARoJWm5/dw==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/yuku-analyzer/-/yuku-analyzer-0.8.1.tgz", + "integrity": "sha512-pe8fmP1Fv0MJh+3+i0EXWzdD2isTzUwJVQc0vmL6mTIcWypo+l0zRiNzwDruO2uZ38kvJ7/CQMhBm5hLuaYWkw==", "license": "MIT", "dependencies": { - "@yuku-toolchain/types": "0.6.11", - "yuku-ast": "0.6.11" + "@yuku-toolchain/types": "^0.8.1", + "yuku-ast": "^0.8.1" }, "optionalDependencies": { - "@yuku-analyzer/binding-darwin-arm64": "0.6.12", - "@yuku-analyzer/binding-darwin-x64": "0.6.12", - "@yuku-analyzer/binding-freebsd-x64": "0.6.12", - "@yuku-analyzer/binding-linux-arm-gnu": "0.6.12", - "@yuku-analyzer/binding-linux-arm-musl": "0.6.12", - "@yuku-analyzer/binding-linux-arm64-gnu": "0.6.12", - "@yuku-analyzer/binding-linux-arm64-musl": "0.6.12", - "@yuku-analyzer/binding-linux-x64-gnu": "0.6.12", - "@yuku-analyzer/binding-linux-x64-musl": "0.6.12", - "@yuku-analyzer/binding-win32-arm64": "0.6.12", - "@yuku-analyzer/binding-win32-x64": "0.6.12" + "@yuku-analyzer/binding-darwin-arm64": "0.8.1", + "@yuku-analyzer/binding-darwin-x64": "0.8.1", + "@yuku-analyzer/binding-freebsd-x64": "0.8.1", + "@yuku-analyzer/binding-linux-arm-gnu": "0.8.1", + "@yuku-analyzer/binding-linux-arm-musl": "0.8.1", + "@yuku-analyzer/binding-linux-arm64-gnu": "0.8.1", + "@yuku-analyzer/binding-linux-arm64-musl": "0.8.1", + "@yuku-analyzer/binding-linux-x64-gnu": "0.8.1", + "@yuku-analyzer/binding-linux-x64-musl": "0.8.1", + "@yuku-analyzer/binding-win32-arm64": "0.8.1", + "@yuku-analyzer/binding-win32-x64": "0.8.1" } }, "node_modules/yuku-ast": { - "version": "0.6.11", - "resolved": "https://registry.npmjs.org/yuku-ast/-/yuku-ast-0.6.11.tgz", - "integrity": "sha512-ZfXkFYVsDewS45+kv3WiA/qNB73CRfxFDEQwfnRMUAR4AD5zRI7PRqxmI2U3Jz/oG41GneTVW6mxDOQal0lgeA==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/yuku-ast/-/yuku-ast-0.8.1.tgz", + "integrity": "sha512-+k1E2f08y0k1+vpdD1KUCzDh2JXvwirMa8YR2Jr7VCS4zAo3eT5A/HbJCqaVGd6idaPY0gwLM9C4seEY4h10Hw==", "license": "MIT", "dependencies": { - "@yuku-toolchain/types": "0.6.8" + "@yuku-toolchain/types": "^0.8.1" } }, - "node_modules/yuku-ast/node_modules/@yuku-toolchain/types": { - "version": "0.6.8", - "resolved": "https://registry.npmjs.org/@yuku-toolchain/types/-/types-0.6.8.tgz", - "integrity": "sha512-AbUd1775RVkOxJkh8hkldIWoU6kRMTCsZFSZq8Ny53q7GkbaVe5UCfleNZ3RWCoz/ZKE8qwfeB7Cj0xqhLWsKA==", - "license": "MIT" + "node_modules/zbsearch": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/zbsearch/-/zbsearch-3.3.4.tgz", + "integrity": "sha512-xGsv9rIwrili/fpLpVwmnCovEcvaAJg1ey+3Ur0+m3x1mnGoVO71iAwn4op420QLGNsQJNmScZjFq5TQ+cRi/g==", + "license": "Apache-2.0", + "engines": { + "node": ">= 20.0.0" + } }, "node_modules/zod": { "version": "4.4.3", diff --git a/apps/web/package.json b/apps/web/package.json index 5171bf4f3..c8fbdc3af 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -33,28 +33,28 @@ "fumadocs-mdx": "^15.0.12", "fumadocs-ui": "^16.10.5", "lucide-react": "^1.14.0", - "next": "16.2.11", + "next": "16.2.12", "react": "19.2.8", "react-dom": "19.2.8", "remark-custom-heading-id": "^2.0.0", "tailwind-merge": "^3.6.0" }, "devDependencies": { - "@biomejs/biome": "2.5.5", + "@biomejs/biome": "2.5.6", "@tailwindcss/postcss": "4.3.3", "@types/node": "25.9.5", - "@types/react": "19.2.17", - "@types/react-dom": "19.2.3", + "@types/react": "19.2.18", + "@types/react-dom": "19.2.4", "lefthook": "2.1.10", "tailwindcss": "4.3.3", "typescript": "6.0.3" }, "overrides": { - "postcss": "8.5.23", + "postcss": "8.5.25", "esbuild": "0.28.1", - "js-yaml": "4.3.0", + "js-yaml": "4.3.1", "sharp": "0.35.3", - "@types/react": "19.2.17", - "@types/react-dom": "19.2.3" + "@types/react": "19.2.18", + "@types/react-dom": "19.2.4" } } diff --git a/apps/web/src/lib/site-config.ts b/apps/web/src/lib/site-config.ts index 224da539f..416395f6b 100644 --- a/apps/web/src/lib/site-config.ts +++ b/apps/web/src/lib/site-config.ts @@ -15,7 +15,7 @@ export const SITE_CONFIG = { /** Brand name shown in the header, footer, and metadata. */ name: "Drydock", /** Current release version shown in the hero badge. */ - version: "1.6.0-rc.11", + version: "1.6.0-rc.12", /** Short product tagline used in page titles and OG metadata. */ tagline: "Container Update Monitoring", /** Default meta / OpenGraph / Twitter description. */ diff --git a/apps/web/src/lib/site-content.ts b/apps/web/src/lib/site-content.ts index 8711146f7..31b54387c 100644 --- a/apps/web/src/lib/site-content.ts +++ b/apps/web/src/lib/site-content.ts @@ -281,7 +281,7 @@ export const roadmap: Milestone[] = [ ], }, { - version: "v1.6.0-rc.11", + version: "v1.6.0-rc.12", title: "Notifications, Policy & Release Intel", emoji: "\u{1F4E8}", status: "next", diff --git a/content/docs/current/api/agent.mdx b/content/docs/current/api/agent.mdx index 0d733b186..7faf7d032 100644 --- a/content/docs/current/api/agent.mdx +++ b/content/docs/current/api/agent.mdx @@ -23,7 +23,7 @@ curl http://drydock:3000/api/v1/agents "host": "192.168.1.50", "port": 3000, "connected": true, - "version": "1.6.0-rc.11", + "version": "1.6.0-rc.12", "os": "linux", "arch": "amd64", "cpus": 4, @@ -153,7 +153,7 @@ Sent immediately upon connection to confirm the handshake. { "type": "dd:ack", "data": { - "version": "1.6.0-rc.11", + "version": "1.6.0-rc.12", "os": "linux", "arch": "amd64", "cpus": 4, diff --git a/content/docs/current/api/app.mdx b/content/docs/current/api/app.mdx index ef618d865..adb8430d1 100644 --- a/content/docs/current/api/app.mdx +++ b/content/docs/current/api/app.mdx @@ -12,7 +12,7 @@ curl http://drydock:3000/api/v1/app { "name":"drydock", - "version":"1.6.0-rc.11" + "version":"1.6.0-rc.12" } ``` diff --git a/content/docs/current/api/portwing.mdx b/content/docs/current/api/portwing.mdx index fb870bd26..36b692e5a 100644 --- a/content/docs/current/api/portwing.mdx +++ b/content/docs/current/api/portwing.mdx @@ -197,7 +197,7 @@ A versioned alias `/api/v1/portwing/ws` is also accepted and is signature-equiva "agentId": "edge-host-01", "agentName": "edge-host-01", "protocol": "portwing/1.0", - "version": "1.6.0-rc.11", + "version": "1.6.0-rc.12", "pubKeyId": "3f8a1c2e9b047d56", "timestamp": 1780329600, "nonce": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", @@ -220,7 +220,7 @@ A name collision with an already-connected agent under the same key (or the in-f "data": { "pollInterval": 300, "config": { - "drydockVersion": "1.6.0-rc.11", + "drydockVersion": "1.6.0-rc.12", "supportedProtocols": "portwing/1.0", "serverCompatLevel": "1.4.0" } diff --git a/content/docs/current/quickstart/index.mdx b/content/docs/current/quickstart/index.mdx index 27b31900c..f0c50bbd4 100644 --- a/content/docs/current/quickstart/index.mdx +++ b/content/docs/current/quickstart/index.mdx @@ -108,7 +108,7 @@ Release tags use the same channel names in every registry: | Tag | Behavior | | --- | --- | -| `1.6.0-rc.11` | Immutable release candidate; best for reproducible testing | +| `1.6.0-rc.12` | Immutable release candidate; best for reproducible testing | | `1.6-rc` | Rolling release-candidate channel; moves to the newest `1.6.0-rc.N` | | `1.6` | Rolling stable minor channel; published only for GA releases | | `1` | Rolling stable major channel; published only for GA releases | diff --git a/content/docs/current/updates/index.mdx b/content/docs/current/updates/index.mdx index f6a1c6d62..e32a8c3f3 100644 --- a/content/docs/current/updates/index.mdx +++ b/content/docs/current/updates/index.mdx @@ -5,6 +5,14 @@ description: "Release update notes and feature highlights, with direct links to ## Unreleased +## v1.6.0-rc.12 Highlights — August 4, 2026 + +- **Security dependency refresh** — `brace-expansion` advanced to 5.0.9 across the app, UI, and e2e workspaces (CVE-2026-69152), `ip-address` to 10.3.1 in the app runtime (CVE-2026-54272, CVE-2026-69192, CVE-2026-69198), and `fast-uri` to 4.1.2 in app and UI (backslash-authority host confusion, CVE-2026-18446) ([#659](https://github.com/CodesWhat/drydock/pull/659)). +- **Maturity badge and gate agree again** — the hot/mature badge now resolves each container's own `updatePolicy.maturityMinAgeDays` before the global `DD_UI_MATURITY_THRESHOLD_DAYS`, matching the gate, and registry publish-date lookup failures (including GHCR/LSCR auth errors) log at `warn` instead of vanishing at `debug` ([#604](https://github.com/CodesWhat/drydock/issues/604)). +- **No more transient "Agent Mismatch"** — the container list and SSE stream soften `agent-mismatch`/`no-update-trigger-configured` to soft blockers while an agent's components are mid-(re)registration; update admission stays fail-closed throughout ([#605](https://github.com/CodesWhat/drydock/issues/605)). +- **WebSocket log streams accept anonymous-auth sessions** — under `DD_ANONYMOUS_AUTH_CONFIRM=true`, both the system and container log-stream upgrades now pass the same auth gate as every REST endpoint ([#636](https://github.com/CodesWhat/drydock/issues/636)). +- **Explicit 501 for missing agent lifecycle transport** — start/stop/restart/rollback on an agent-owned container without controller Docker transport returns a 501 naming the cause instead of an ambiguous 404 ([#637](https://github.com/CodesWhat/drydock/issues/637)). + ## v1.6.0-rc.11 Highlights — August 1, 2026 - **Portwing 0.9 Docker watcher, updates, and lifecycle actions now run controller-side** — Drydock 1.6.0-rc.11+ recognizes the exact `transport=docker-api`, `execution=controller`, `events=portwing` watcher marker, starts its native registry checks and Docker action against a loopback-only authenticated bridge, and carries Docker calls over Standard HTTP or Edge correlated request/response/stream transport. Single/batch updates, start/stop/restart, update preview, and backup rollback use this path even though Portwing advertises no remote trigger. Portwing stays authoritative for lifecycle events, and raw false/unknown inventory no longer erases controller enrichment ([#632](https://github.com/CodesWhat/drydock/issues/632), [#637](https://github.com/CodesWhat/drydock/issues/637), [Portwing #76](https://github.com/CodesWhat/portwing/issues/76)). diff --git a/e2e/package-lock.json b/e2e/package-lock.json index 64ce3b209..990ec8519 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -3658,9 +3658,9 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", - "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" diff --git a/e2e/package.json b/e2e/package.json index f5fe345eb..f590bb54b 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -37,7 +37,7 @@ "lodash": "4.18.1" }, "overrides": { - "brace-expansion": "5.0.8", + "brace-expansion": "5.0.9", "fast-xml-parser": "5.10.1", "joi": "18.2.3", "minimatch": "10.2.5", diff --git a/e2e/tests/security/brace-expansion-lockfile.test.js b/e2e/tests/security/brace-expansion-lockfile.test.js index d76fa6339..dac567b6a 100644 --- a/e2e/tests/security/brace-expansion-lockfile.test.js +++ b/e2e/tests/security/brace-expansion-lockfile.test.js @@ -3,7 +3,7 @@ const { readFileSync } = require('node:fs'); const { join } = require('node:path'); const test = require('node:test'); -const MINIMUM_SAFE_BRACE_EXPANSION_VERSION = '5.0.7'; +const MINIMUM_SAFE_BRACE_EXPANSION_VERSION = '5.0.9'; function compareSemver(a, b) { const aParts = a.split('.').map(Number); diff --git a/package-lock.json b/package-lock.json index 331f70ef7..c21983ba1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,7 @@ "": { "version": "1.6.0", "devDependencies": { - "@biomejs/biome": "2.5.5", + "@biomejs/biome": "2.5.6", "@types/node": "25.9.5", "lefthook": "2.1.10", "vitest": "4.1.10", @@ -18,9 +18,9 @@ } }, "node_modules/@biomejs/biome": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.5.tgz", - "integrity": "sha512-r1S8nFsAG1MY+vJFZALzIvwXAJv6ejDQ0mxP21Tgr9YK3ZFtjrvbBwDdNhx1rUqvccEIeNg20cYCNzl6Cr69pQ==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.6.tgz", + "integrity": "sha512-lxVNjv7UF6KfhMJfL9gaUHbWdJdHbsAj6OSmwSYNdhRuG67NxNQ4Xdvh3TUxsSK9sBzJBQhEJj3AopmmNJ5pSA==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { @@ -34,20 +34,20 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.5.5", - "@biomejs/cli-darwin-x64": "2.5.5", - "@biomejs/cli-linux-arm64": "2.5.5", - "@biomejs/cli-linux-arm64-musl": "2.5.5", - "@biomejs/cli-linux-x64": "2.5.5", - "@biomejs/cli-linux-x64-musl": "2.5.5", - "@biomejs/cli-win32-arm64": "2.5.5", - "@biomejs/cli-win32-x64": "2.5.5" + "@biomejs/cli-darwin-arm64": "2.5.6", + "@biomejs/cli-darwin-x64": "2.5.6", + "@biomejs/cli-linux-arm64": "2.5.6", + "@biomejs/cli-linux-arm64-musl": "2.5.6", + "@biomejs/cli-linux-x64": "2.5.6", + "@biomejs/cli-linux-x64-musl": "2.5.6", + "@biomejs/cli-win32-arm64": "2.5.6", + "@biomejs/cli-win32-x64": "2.5.6" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.5.tgz", - "integrity": "sha512-kUrAhXVWUrwmAUnV2iXSK7umxKFysTwvqK+Ty6ptUcLY/7T3SnCAjUowE4uvwaEej6nXZ7hu/dTtbokKdsPeag==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-zMOLZP4oMrjh6m1zcSj1ud2awUPgTuMVbmQhYYWL7J8HwCnbHHBvTm7VBTRuY7epT5bez76IpKYQ11ZAqHFlnw==", "cpu": [ "arm64" ], @@ -62,9 +62,9 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.5.tgz", - "integrity": "sha512-DamiYc5bUYZ2uxlfc+RLEPtz1Abb6PO5eTbOkufLpSGwd/7AMQAdxhFYiXmwwkJL8IsT8S7GvdgwDHqaMFAvKw==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.6.tgz", + "integrity": "sha512-JAC1VqzvO7Th5ZplU0G2uGfkZbxEe9uDDektPAhF0JLusoz1w+T4okp2bkykI0bbaO2vslKiRfj4gU43JaGreA==", "cpu": [ "x64" ], @@ -79,9 +79,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.5.tgz", - "integrity": "sha512-lRKF/pH/1RiYiBKExi3TCZVAtvzEm77aifrvcNiDFrR9WxeAnDUjDnseb6y2XV85mjitLs6SILGm2XG77cHtSQ==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.6.tgz", + "integrity": "sha512-6XsYwCFkp5sMxl85ffhgeGpGgs6A7dRYFnkceZ7WVxvycuTnGdD5xa534Z3xfrBQ0JCMK/mujT6ZNPJoghedwg==", "cpu": [ "arm64" ], @@ -99,9 +99,9 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.5.tgz", - "integrity": "sha512-U4WMl/sy/E/Q73vf15VspakLRRs2LDFcCeBxJnQfXzssb88zpV6PJPaQ3ezhQ7H6Ht2/8bvuZeHgJWzmoxllZg==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-eUa3jeeYvfMt19LBeh6E5PUZpxnTC4JqNWo+EDjTtQjAr2xLGnWaxACtVU1DQqmHYbvThlJzLX+ZsYgrqh2qVw==", "cpu": [ "arm64" ], @@ -119,9 +119,9 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.5.tgz", - "integrity": "sha512-H/O39nJEw/2Zm/fm7hrmxxoF8kK/aU1uCoPp70ruXVbomaAdLpJJnCmL11Q2JotT8QVHH06So04Oq53lCSwSwQ==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.6.tgz", + "integrity": "sha512-Pop9VXCFUhFTMfFefZ39S+u2rOPyNp5iHlxbZRwXGACHLy2r0jjiRgJHmaEKJzL3SyxlVeGShXhvvElvWowonA==", "cpu": [ "x64" ], @@ -139,9 +139,9 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.5.tgz", - "integrity": "sha512-m7wC7tjX5Lrmo69dc4md8FeKpPU1NTCY1v7xUoQQ2vadWwNnBS0KZOG8471otFPHrTHihQJAjQPgMObpLvDe6A==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-2Vp13QdKysH3HIWLaYLhUUwbK+jbZonJD1K+Lr0d0RO4wH7mkYd43vJixEDm8cUWrowoRz4UUHF1nm9Ae7ym8A==", "cpu": [ "x64" ], @@ -159,9 +159,9 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.5.tgz", - "integrity": "sha512-7BryINPuYypLUAH3o/o5ZdgomJ4zn3EDR0ChZJst7n32S6ZhKbgHXuYydLu+YAnx59ehGFR0z/MG6qnzQi3Yyw==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.6.tgz", + "integrity": "sha512-tDGshcm6BdkZOCGnTDX0Y8/U4IfBSlnUU7T56nNDuPEfed+aHg+u8G36NB43fJVl0Os6+QURXIE1yuD7AaEofA==", "cpu": [ "arm64" ], @@ -176,9 +176,9 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.5.tgz", - "integrity": "sha512-bIBFo+n6MIxdNcVFy5CrurbKiZQiUciK3bt8+O9I4wjFZNTfXLpi+giq47522eXqW5NBc9ulx7dR1SlZKi2J5g==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.6.tgz", + "integrity": "sha512-WN05KwXnTO/2J45RQPvzZMXf7tZUIofHoR35xIPfCo7pQ2RFidxI8sfb5mGsaTxdMmEOzHzOPRCdA5/fCpc7xQ==", "cpu": [ "x64" ], @@ -193,21 +193,21 @@ } }, "node_modules/@emnapi/core": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", - "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "version": "2.0.0-alpha.3", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-2.0.0-alpha.3.tgz", + "integrity": "sha512-AZypUeJ/yByuxyS7BlSNRDOMLMlROYtjYdIAuBmJssVz1UJDSeYxLrdizhXCFYhedC5bqd/ASy8EuNXbVVXp9g==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.2", + "@emnapi/wasi-threads": "2.0.1", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", - "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "version": "2.0.0-alpha.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-2.0.0-alpha.3.tgz", + "integrity": "sha512-hFPAhMUjJD9BSyCANEISPOogeXC9Zo9ZQl7L6vKnaVsMkCtzznaW/naYypeyl0Gv5rYfWYsZbpixTMpjDJzQeA==", "dev": true, "license": "MIT", "optional": true, @@ -216,9 +216,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", - "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-2.0.1.tgz", + "integrity": "sha512-9DsSk+o5NBX0CCJT8s0EROGSGxjR/tKu6aBTaVyq+SjAEQH4XcdcRxPBRzsBLizTTJ49MJjF+jgu3qnO9GLQcQ==", "dev": true, "license": "MIT", "optional": true, @@ -234,28 +234,31 @@ "license": "MIT" }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", - "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.1.tgz", + "integrity": "sha512-KjZdi8Q1wh89gsVmghvbrMgWl6ZWmRmHV6wjB7/g4Zf0dyO+hH3neZUtuDNPO00qq5YE5RITVWvrIZKRaAmzGQ==", "dev": true, "license": "MIT", "optional": true, "dependencies": { "@tybys/wasm-util": "^0.10.3" }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" }, "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.3", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.3" } }, "node_modules/@oxc-project/types": { - "version": "0.139.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", - "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.142.0.tgz", + "integrity": "sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==", "dev": true, "license": "MIT", "funding": { @@ -263,9 +266,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", - "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.1.tgz", + "integrity": "sha512-02hOeOSryYxVrOIphmLAsqnCJWxwlzFk+pEt/N/i6OgT3lShHO7xGCU5cpgchRDHboAEbSjzgGh+O/u1GswQmA==", "cpu": [ "arm64" ], @@ -280,9 +283,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", - "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.1.tgz", + "integrity": "sha512-fMsTOnN0OjFm3CyppWPitKnc8UlliVARUULW6cfU6AIqjdtgmSFWSk9vecHzZduv/yMWIHDlRhM1e8Iff9uAfA==", "cpu": [ "arm64" ], @@ -297,9 +300,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", - "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.1.tgz", + "integrity": "sha512-1wjKdz/XLGKHaTNHjQveQ/B23TKx4ItAqm1JbyVuvNPc4Ze0Fb48s49TAd/2zcplPl8okE/UbTgmlVfwT7eFeQ==", "cpu": [ "x64" ], @@ -314,9 +317,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", - "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.1.tgz", + "integrity": "sha512-Fa0jHR07E7YBN4vOEsbVf2briYNsuOowfLJaXULZM0ldMlaCaj2LJgLMbMe4iacRyZmvR8efFhgR9wKuGclQUg==", "cpu": [ "x64" ], @@ -331,9 +334,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", - "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.1.tgz", + "integrity": "sha512-pzkgu1SSHGgRRyRZ4fbmSgmajbVt+epaLP99NDjFft69v/ypfTi6swBMiVdh2EkQ0OSnHE1lZDM7DRGkyAzUpA==", "cpu": [ "arm" ], @@ -348,9 +351,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", - "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.1.tgz", + "integrity": "sha512-QI5SEDY8cbiYWHx0VO4vIc3UlS6a32vXHjU8Qy/17adEmZIPuByJg13UEvo9c/UCiUkdcVWY83C+b+JrwnNyUg==", "cpu": [ "arm64" ], @@ -368,9 +371,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", - "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.1.tgz", + "integrity": "sha512-Sm41FyCeXqmYcERoYOCbGIL5hNfd8w9LQ7Y61Bev48HkcjaJqV/iiVOaiDxjVTRMS+QKrZmD8cfPt4uMVnvM+A==", "cpu": [ "arm64" ], @@ -388,9 +391,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", - "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.1.tgz", + "integrity": "sha512-2x+WhXTGl9yJYPbltW/BSEPTVz9OIWQyER4N+gJEDWkkn904eRcBzELqh/Hf7K0w/ubGbKNMv0ZC+94QK/IFEg==", "cpu": [ "ppc64" ], @@ -408,9 +411,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", - "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.1.tgz", + "integrity": "sha512-eEjmQpuRQayHPWWnywaWHkFT3ToPbP3RYy42VVd/B9aBGDA+Ol25EIWHxKQST3IiWJjikCWUF7KtbfqwZrzVwQ==", "cpu": [ "s390x" ], @@ -428,9 +431,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", - "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.1.tgz", + "integrity": "sha512-/Orga1fZYkLc/56jBICcHrKchl8Z2UKdDSr3LG9ToWO1lQ6a4Livk9Xz+9WN91zsz5QR3XQz2NNoSDEvP6qadw==", "cpu": [ "x64" ], @@ -448,9 +451,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", - "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.1.tgz", + "integrity": "sha512-xxBJRL+0q0Kce7orznGWLuylHDY65vuARXZRpX+hPdv+DqK2c3NlCsVA98tlWzWNEE7yPqA/1NQ5nnCrj49Y5A==", "cpu": [ "x64" ], @@ -468,9 +471,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", - "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.1.tgz", + "integrity": "sha512-M6AdXIXw3s+/8XpKMzdGDEXGS1S7kwUsy+rcTIUIOx5Ge4nXKCtAFHFV9YKkXvGcC5WMoTjAteLzlsQROVI0Yw==", "cpu": [ "arm64" ], @@ -485,28 +488,25 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", - "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", - "cpu": [ - "wasm32" - ], + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.2.1.tgz", + "integrity": "sha512-/TX0SoRGojHzSAHpfVBbavRVSazg5U3h3Y3VXfcc0cdugq6kxdqw8LPGFiPr+/7gE/60zRcsOY2Vi9b9eT0jww==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.11.1", - "@emnapi/runtime": "1.11.1", - "@napi-rs/wasm-runtime": "^1.1.6" + "@emnapi/core": "2.0.0-alpha.3", + "@emnapi/runtime": "2.0.0-alpha.3", + "@napi-rs/wasm-runtime": "^1.2.0" }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", - "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.1.tgz", + "integrity": "sha512-EvRrivJieyHG+AO9lleZWgq+g0+S7oV2C51yuqlcyU/R9net+sI4Pj0F+lUoP2bEr6TWX3SqFaaS0SzfLxSzkw==", "cpu": [ "arm64" ], @@ -521,9 +521,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", - "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.1.tgz", + "integrity": "sha512-Z4eCmn5QJ/5+azF9knpLWKfVd9aidn0mAe9TpJgvBLId9Ax3t0+JVxBmT25Bv7NBbVW1TZyKjQjQReouMeH5UQ==", "cpu": [ "x64" ], @@ -971,9 +971,9 @@ ] }, "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -987,23 +987,23 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" } }, "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", "cpu": [ "arm64" ], @@ -1022,9 +1022,9 @@ } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", "cpu": [ "arm64" ], @@ -1043,9 +1043,9 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", "cpu": [ "x64" ], @@ -1064,9 +1064,9 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", "cpu": [ "x64" ], @@ -1085,9 +1085,9 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", "cpu": [ "arm" ], @@ -1106,13 +1106,16 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1127,13 +1130,16 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1148,13 +1154,16 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1169,13 +1178,16 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MPL-2.0", "optional": true, "os": [ @@ -1190,9 +1202,9 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", "cpu": [ "arm64" ], @@ -1211,9 +1223,9 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", "cpu": [ "x64" ], @@ -1299,9 +1311,9 @@ } }, "node_modules/postcss": { - "version": "8.5.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", - "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", + "version": "8.5.25", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", + "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", "dev": true, "funding": [ { @@ -1328,13 +1340,13 @@ } }, "node_modules/rolldown": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", - "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.1.tgz", + "integrity": "sha512-4FKJhg8d3OiyQOA6Q1Q0hoFFpW9/OoX+VsHzpECsdsIZoOArrAK90gl59YK/Z+gnDel45bgJZK03ozH/9bCqEw==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.139.0", + "@oxc-project/types": "=0.142.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -1344,21 +1356,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.1.5", - "@rolldown/binding-darwin-arm64": "1.1.5", - "@rolldown/binding-darwin-x64": "1.1.5", - "@rolldown/binding-freebsd-x64": "1.1.5", - "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", - "@rolldown/binding-linux-arm64-gnu": "1.1.5", - "@rolldown/binding-linux-arm64-musl": "1.1.5", - "@rolldown/binding-linux-ppc64-gnu": "1.1.5", - "@rolldown/binding-linux-s390x-gnu": "1.1.5", - "@rolldown/binding-linux-x64-gnu": "1.1.5", - "@rolldown/binding-linux-x64-musl": "1.1.5", - "@rolldown/binding-openharmony-arm64": "1.1.5", - "@rolldown/binding-wasm32-wasi": "1.1.5", - "@rolldown/binding-win32-arm64-msvc": "1.1.5", - "@rolldown/binding-win32-x64-msvc": "1.1.5" + "@rolldown/binding-android-arm64": "1.2.1", + "@rolldown/binding-darwin-arm64": "1.2.1", + "@rolldown/binding-darwin-x64": "1.2.1", + "@rolldown/binding-freebsd-x64": "1.2.1", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.1", + "@rolldown/binding-linux-arm64-gnu": "1.2.1", + "@rolldown/binding-linux-arm64-musl": "1.2.1", + "@rolldown/binding-linux-ppc64-gnu": "1.2.1", + "@rolldown/binding-linux-s390x-gnu": "1.2.1", + "@rolldown/binding-linux-x64-gnu": "1.2.1", + "@rolldown/binding-linux-x64-musl": "1.2.1", + "@rolldown/binding-openharmony-arm64": "1.2.1", + "@rolldown/binding-wasm32-wasi": "1.2.1", + "@rolldown/binding-win32-arm64-msvc": "1.2.1", + "@rolldown/binding-win32-x64-msvc": "1.2.1" } }, "node_modules/siginfo": { @@ -1452,16 +1464,16 @@ "license": "MIT" }, "node_modules/vite": { - "version": "8.1.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz", - "integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.0.tgz", + "integrity": "sha512-pn+CFpM0lwDeKwmOq1ZaBK/9sjorZcgqxki6MbY/jPEVd9vichIlmlD4HmQ5wdP5EgqQCFRaACBxMC7uEGc6lQ==", "dev": true, "license": "MIT", "dependencies": { - "lightningcss": "^1.32.0", + "lightningcss": "^1.33.0", "picomatch": "^4.0.5", - "postcss": "^8.5.17", - "rolldown": "~1.1.5", + "postcss": "^8.5.23", + "rolldown": "~1.2.0", "tinyglobby": "^0.2.17" }, "bin": { @@ -1478,7 +1490,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.3.0", + "@vitejs/devtools": "^0.4.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", diff --git a/package.json b/package.json index 54d584ef6..da3d64fba 100644 --- a/package.json +++ b/package.json @@ -9,14 +9,14 @@ "node": ">=24.0.0" }, "devDependencies": { - "@biomejs/biome": "2.5.5", + "@biomejs/biome": "2.5.6", "@types/node": "25.9.5", "lefthook": "2.1.10", "vitest": "4.1.10", "yaml": "2.9.0" }, "overrides": { - "vite": "8.1.5", - "postcss": "8.5.23" + "vite": "8.2.0", + "postcss": "8.5.25" } } diff --git a/scripts/changelog-links.test.mjs b/scripts/changelog-links.test.mjs index 180c02d87..86e816bd9 100644 --- a/scripts/changelog-links.test.mjs +++ b/scripts/changelog-links.test.mjs @@ -56,7 +56,8 @@ test('every linked changelog heading has exactly one link definition', () => { test('v1.6 RC and v1.5.2 GA have a complete chronological comparison-link chain', () => { const definitions = new Map(getLinkDefinitions(changelog).map(({ label, url }) => [label, url])); const expected = new Map([ - ['Unreleased', `${repositoryUrl}/compare/v1.6.0-rc.11...HEAD`], + ['Unreleased', `${repositoryUrl}/compare/v1.6.0-rc.12...HEAD`], + ['1.6.0-rc.12', `${repositoryUrl}/compare/v1.6.0-rc.11...v1.6.0-rc.12`], ['1.6.0-rc.11', `${repositoryUrl}/compare/v1.6.0-rc.10...v1.6.0-rc.11`], ['1.6.0-rc.10', `${repositoryUrl}/compare/v1.6.0-rc.9...v1.6.0-rc.10`], ['1.6.0-rc.9', `${repositoryUrl}/compare/v1.6.0-rc.8...v1.6.0-rc.9`], diff --git a/scripts/release-docs-identity.test.mjs b/scripts/release-docs-identity.test.mjs index b1c1cc764..5f71f390b 100644 --- a/scripts/release-docs-identity.test.mjs +++ b/scripts/release-docs-identity.test.mjs @@ -2,10 +2,10 @@ import assert from 'node:assert/strict'; import { readdirSync, readFileSync } from 'node:fs'; import test from 'node:test'; -const RC_VERSION = '1.6.0-rc.11'; -const PREV_RC_VERSION = '1.6.0-rc.10'; -const RC_DATE = '2026-08-01'; -const RC_DISPLAY_DATE = 'August 1, 2026'; +const RC_VERSION = '1.6.0-rc.12'; +const PREV_RC_VERSION = '1.6.0-rc.11'; +const RC_DATE = '2026-08-04'; +const RC_DISPLAY_DATE = 'August 4, 2026'; const DOC_ROOTS = ['content/docs/current', 'content/docs/v1.5']; const BROAD_401_CLAIM = /(?:all|every) API (?:call|request)s?(?: (?:is|are) rejected with| returns?) `401`/iu; diff --git a/scripts/release-identity.test.mjs b/scripts/release-identity.test.mjs index 0e77d78c4..7d6dbfdc8 100644 --- a/scripts/release-identity.test.mjs +++ b/scripts/release-identity.test.mjs @@ -3,7 +3,7 @@ import { readFileSync } from 'node:fs'; import test from 'node:test'; const BASE_VERSION = '1.6.0'; -const RC_VERSION = '1.6.0-rc.11'; +const RC_VERSION = '1.6.0-rc.12'; const DEMO_RELEASE_FIXTURES = [ { path: 'apps/demo/src/mocks/data/server.ts', diff --git a/scripts/security-dependency-versions.test.mjs b/scripts/security-dependency-versions.test.mjs index fc85132fd..fc4a05c30 100644 --- a/scripts/security-dependency-versions.test.mjs +++ b/scripts/security-dependency-versions.test.mjs @@ -22,39 +22,66 @@ function resolvedVersion(lockfile, packageName) { return lockfile.packages?.[`node_modules/${packageName}`]?.version; } -// The CVE-2026-16221 fix was backported per-line: 3.1.4 on the 3.x line, -// 4.1.1 on the 4.x line. A plain ">= 3.1.4" semver floor would wrongly pass -// 4.0.0 and 4.1.0, which are newer than 3.1.4 but predate the 4.x backport -// and are still vulnerable. Require the patched floor for whichever major -// line the resolved version is on. +// Only the 4.x line is vetted now: 4.1.2 carries both the CVE-2026-16221 fix +// and the backslash-authority host-confusion fix (GHSA-7p8r-x3mc-p8w7). +// The transitional 3.1.4 branch was dropped once the override moved to 4.x, +// and any other major (including a future 5.x) fails until explicitly vetted. function isFastUriPatched(version) { const major = Number(version.split('.')[0]); - return major === 3 ? compareSemver(version, '3.1.4') >= 0 : compareSemver(version, '4.1.1') >= 0; + return major === 4 && compareSemver(version, '4.1.2') >= 0; } -test('isFastUriPatched rejects unpatched 4.x releases newer than 3.1.4', () => { - assert.ok(isFastUriPatched('3.1.4'), '3.1.4 (3.x floor) should pass'); - assert.ok(!isFastUriPatched('4.1.0'), '4.1.0 predates the 4.x backport and should fail'); - assert.ok(isFastUriPatched('4.1.1'), '4.1.1 (4.x floor) should pass'); +test('isFastUriPatched only accepts vetted 4.x releases at or above 4.1.2', () => { + assert.ok(!isFastUriPatched('3.1.4'), '3.x is no longer a supported line and should fail'); + assert.ok( + !isFastUriPatched('4.1.1'), + '4.1.1 predates the backslash-authority fix and should fail', + ); + assert.ok(isFastUriPatched('4.1.2'), '4.1.2 (current floor) should pass'); + assert.ok(!isFastUriPatched('5.0.0'), 'an unvetted future major should fail'); }); -// fast-uri was pinned to 3.1.4 in app/ and ui/ for CVE-2026-16221 -// (GHSA-v2hh-gcrm-f6hx). 4.1.1 retains the fix, and Renovate's override -// bump to 4.1.1 (#617) is accepted alongside the original pin below. -// Transitional: drop the 3.1.4 branch once #617 lands. +// fast-uri is pinned to 4.1.2 in app/ and ui/: it carries the CVE-2026-16221 +// fix (GHSA-v2hh-gcrm-f6hx) plus the backslash-authority host-confusion fix. test('fast-uri is pinned to a patched release in app and ui', () => { for (const workspace of ['app', 'ui']) { const manifest = readJson(`${workspace}/package.json`); const lockfile = readJson(`${workspace}/package-lock.json`); + assert.ok(isFastUriPatched(manifest.overrides?.['fast-uri']), `${workspace} override`); + assert.ok(isFastUriPatched(resolvedVersion(lockfile, 'fast-uri')), `${workspace} lockfile`); + } +}); + +// brace-expansion 5.0.8 was vulnerable to CVE-2026-69152 (GHSA-rgw5-rvv9-x895), +// patched in 5.0.9. +test('brace-expansion is pinned to a patched release in app, ui, and e2e', () => { + for (const workspace of ['app', 'ui', 'e2e']) { + const manifest = readJson(`${workspace}/package.json`); + const lockfile = readJson(`${workspace}/package-lock.json`); + assert.ok( - ['3.1.4', '4.1.1'].includes(manifest.overrides?.['fast-uri']), + compareSemver(manifest.overrides?.['brace-expansion'], '5.0.9') >= 0, `${workspace} override`, ); - assert.ok(isFastUriPatched(resolvedVersion(lockfile, 'fast-uri')), `${workspace} lockfile`); + assert.ok( + compareSemver(resolvedVersion(lockfile, 'brace-expansion'), '5.0.9') >= 0, + `${workspace} lockfile`, + ); } }); +// ip-address 10.2.0 was vulnerable to CVE-2026-54272, CVE-2026-69192, and +// CVE-2026-69198 (pulled in via express-rate-limit and mqtt -> socks in +// app), patched in 10.3.1. +test('ip-address is pinned to a patched release in app', () => { + const manifest = readJson('app/package.json'); + const lockfile = readJson('app/package-lock.json'); + + assert.ok(compareSemver(manifest.overrides?.['ip-address'], '10.3.1') >= 0, 'app override'); + assert.ok(compareSemver(resolvedVersion(lockfile, 'ip-address'), '10.3.1') >= 0, 'app lockfile'); +}); + test('fast-xml-parser is pinned to the patched release in app', () => { const manifest = readJson('app/package.json'); const lockfile = readJson('app/package-lock.json'); @@ -76,7 +103,12 @@ test('Next.js is pinned past the 16.2.9 security advisory batch', () => { const manifest = readJson('apps/web/package.json'); const lockfile = readJson('apps/web/package-lock.json'); - assert.equal(manifest.dependencies?.next, '16.2.11'); + // Floor, not an exact pin: 16.2.11 closed the advisory batch, and routine + // Renovate bumps past it must not fail the guard (16.x only — a new major + // is a deliberate migration, not a routine bump). + const manifestNext = manifest.dependencies?.next; + assert.ok(manifestNext?.startsWith('16.'), 'apps/web next must stay on the vetted 16.x line'); + assert.ok(compareSemver(manifestNext, '16.2.11') >= 0); assert.ok(compareSemver(resolvedVersion(lockfile, 'next'), '16.2.11') >= 0); }); diff --git a/ui/package-lock.json b/ui/package-lock.json index 08c508fbc..9de7f3259 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -27,7 +27,7 @@ "@iconify-json/fa6-solid": "1.2.4", "@iconify-json/heroicons": "1.2.3", "@iconify-json/iconoir": "1.2.11", - "@iconify-json/lucide": "1.2.120", + "@iconify-json/lucide": "1.2.121", "@iconify-json/ph": "1.2.2", "@iconify-json/tabler": "1.2.38", "@stryker-mutator/core": "9.6.1", @@ -39,9 +39,9 @@ "@vitest/coverage-v8": "4.1.10", "@vue/test-utils": "2.4.11", "jsdom": "29.1.1", - "knip": "6.29.0", + "knip": "6.31.0", "typescript": "6.0.3", - "vite": "8.1.5", + "vite": "8.2.0", "vitest": "4.1.10" }, "engines": { @@ -795,6 +795,7 @@ "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" @@ -806,6 +807,7 @@ "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -928,9 +930,9 @@ } }, "node_modules/@iconify-json/lucide": { - "version": "1.2.120", - "resolved": "https://registry.npmjs.org/@iconify-json/lucide/-/lucide-1.2.120.tgz", - "integrity": "sha512-4ECF24fxag1n5tZlPGrbWuCGqhkwD0rvAzfCXretqQb4KEC8Y6ja3khF8qmvpR1Mk0NQDhBTRK3Fr/YoV8QyJw==", + "version": "1.2.121", + "resolved": "https://registry.npmjs.org/@iconify-json/lucide/-/lucide-1.2.121.tgz", + "integrity": "sha512-zSoQQSmsyFaeTpSwURHJ9PIrsDcGFpDNhGlpiTrs+Ua4uFeH5UsE26L4OEBLrgLWoSK0UO+JhsO8yehSw0403g==", "dev": true, "license": "ISC", "dependencies": { @@ -1432,21 +1434,24 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", - "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.2.tgz", + "integrity": "sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==", "license": "MIT", "optional": true, "dependencies": { "@tybys/wasm-util": "^0.10.3" }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" }, "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.3", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.3" } }, "node_modules/@one-ini/wasm": { @@ -1457,9 +1462,9 @@ "license": "MIT" }, "node_modules/@oxc-parser/binding-android-arm-eabi": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.140.0.tgz", - "integrity": "sha512-ZfjDZ422mo7eo3b3VltqNsV9kmv1qt/sPEAMSl64iOSwhVfd0eIZ9LB79Mbs1xYXJnk7WSROwzBCKDIiVxPTvQ==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.142.0.tgz", + "integrity": "sha512-ZiRGDutGsv1G6bL/ozy/koC0Sv39T1DqyoC4KD1DOy9ZoACm1O5UWhEK2c02Qdk+4lfLVkvFa/mQ0fm/4h1BtQ==", "cpu": [ "arm" ], @@ -1474,9 +1479,9 @@ } }, "node_modules/@oxc-parser/binding-android-arm64": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.140.0.tgz", - "integrity": "sha512-Ia8jSvikUX6Sf+Ht+KOCUF/k1HpR0VlmqIYymubmWDebOEGtsyliHDR6JxsZ4IX3/c/GbrB1uh09aVGQv/LQmQ==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.142.0.tgz", + "integrity": "sha512-WZkvGRLNQTz8lR9zP5nLjUdlroRCopBu3g9zF1p/laE6DzT1UbQo8Rdz5MWhaJUPYg/6gp+jo7HUgsyKaN1FtQ==", "cpu": [ "arm64" ], @@ -1491,9 +1496,9 @@ } }, "node_modules/@oxc-parser/binding-darwin-arm64": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.140.0.tgz", - "integrity": "sha512-G6VK0nK61pH0d0mBjUqSZbVxGqqO5uzeginLDQj+gOO6ObfJjXRwgkD/ol0w1INcnFeAb6YGGO7qc3ueGHaycQ==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.142.0.tgz", + "integrity": "sha512-l4khS8LQOOVYsGRVARo1gSaCT/aBSceUVXgtovWc2+drnxVuDr082WA3OCHVdVzIz5JIrP/y9CWsSKxBDNmYGg==", "cpu": [ "arm64" ], @@ -1508,9 +1513,9 @@ } }, "node_modules/@oxc-parser/binding-darwin-x64": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.140.0.tgz", - "integrity": "sha512-HazBOuZzd2pO1C2uMmp8Gv7mhzMHqKSKDS1OZfcLEvpIcgA+48J92HEtNanVHDIzRD9PRPCV6aS6fkZIWOVl8Q==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.142.0.tgz", + "integrity": "sha512-QBsNF3nqlXmcH2B1YOPqQYmCJoy4HuIjUxGbBO/k5JAJUl68ghU2psRY2zPk+RyBaWqKP/qfL4oaFgEMCdwskA==", "cpu": [ "x64" ], @@ -1525,9 +1530,9 @@ } }, "node_modules/@oxc-parser/binding-freebsd-x64": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.140.0.tgz", - "integrity": "sha512-9hSUU+HmTUyOe4JzMHxNGgLWNY7rrO+6ShicZwImNJacEAACDMIkuEQQkvXSL+WJN50jaNtLYJv8s4OcBdpyUQ==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.142.0.tgz", + "integrity": "sha512-b7Q7m4Cqc6XqNhri3R+QhU+GVy646Pn+bkdhrDdWym/Fdi0ZUa+d73H9dm5H91JtbtAQ/z1d8XKMW3oOV8a4tQ==", "cpu": [ "x64" ], @@ -1542,9 +1547,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.140.0.tgz", - "integrity": "sha512-RAEuQsYtS0KcDFqN0ABTjyyNlokS91JeuDuoW9tEbG0JTbRNXnpQUdbYc/16JoA6Z/2ALbNrE3KmxtqDiuIjCQ==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.142.0.tgz", + "integrity": "sha512-3riVS5IhdH3uCZj1Y9ftDQlR0dvLsIlw/edrRqk8JhgNd5K0XSs+UBtgh50N13CAlW9/TXj6sVGXaKNBocd0Yg==", "cpu": [ "arm" ], @@ -1559,9 +1564,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.140.0.tgz", - "integrity": "sha512-c4CkHvPvqfojouredJ0w3e6+jiBq0SbFyhH61kr/zPb/7XsaYTNKQ54vmlSsopfdQbNDX40ZeK9Abs2Qet6wcw==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.142.0.tgz", + "integrity": "sha512-NmXUOpgpTSkhl795TiXmWppTwmSJ92RC1qvD6e4XOF+slgmo3e6Ah+kEu+6AN8s7NAOEwqGmir58MgSQSWmBSA==", "cpu": [ "arm" ], @@ -1576,9 +1581,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-gnu": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.140.0.tgz", - "integrity": "sha512-yrjmLj8ixPB25yqvPGr28meGjb+keed7m1GqqY/0uqkhZIoT4t9zmfwUgFEtC33C7dtE+UQ7TU0IaVxf97SWJg==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.142.0.tgz", + "integrity": "sha512-gc0EXsKtXgerujmU2Bql3u1L1HsSQ2774R83idq/FoNMPVV/RY/1ErFsvnit7KoiP/sLvzQixeUo4Ut0ic0wmw==", "cpu": [ "arm64" ], @@ -1596,9 +1601,9 @@ } }, "node_modules/@oxc-parser/binding-linux-arm64-musl": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.140.0.tgz", - "integrity": "sha512-ggGMQTN8Agwxp2WiLMpdY671dt0qTDJWiWlJeig3HnUwTnerRl0J2JdGVghWBeDcss2D9S2V2Js6dZHEiVabVA==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.142.0.tgz", + "integrity": "sha512-F2XvmWSE0uWpie+jHKKIFgdVOe9ypGhkEZxKx5DuW215K6cbAC274yYaPkcM7EqY4Df3Weyhpcz3lsURyH2LVg==", "cpu": [ "arm64" ], @@ -1616,9 +1621,9 @@ } }, "node_modules/@oxc-parser/binding-linux-ppc64-gnu": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.140.0.tgz", - "integrity": "sha512-IgTs8xYAFgAUGNmR65tIqjlJ8vKgrfXzC515e9goSdfMyKQV4aJpd2pUUudU4u51G64H0/DSEJEXKOraxm9ZCA==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.142.0.tgz", + "integrity": "sha512-wLMbT21U/QxknQsk+VvNF0b9D2/aGWhcaQQQ+VYlE8FwD5+GoWZIPPXNzyHmkYyhm0KB3itL+TBavjMatqNnYA==", "cpu": [ "ppc64" ], @@ -1636,9 +1641,9 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.140.0.tgz", - "integrity": "sha512-A1x+PMWZmSGaFVOx2YeNTFau8uD+QO14/vLP4GrcuvUPs3+nBkUOjy9Lus86ftHsDojjYMbvBelmKc3F7Rv08g==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.142.0.tgz", + "integrity": "sha512-+G8F/4ckwT7FCJV4H2bt09xEzJbjNCfuL4Sp1AYNaFtFMVtgIGMuJlteT82U+K0UIZ/DzAR/LDlMFnEuajG7Kw==", "cpu": [ "riscv64" ], @@ -1656,9 +1661,9 @@ } }, "node_modules/@oxc-parser/binding-linux-riscv64-musl": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.140.0.tgz", - "integrity": "sha512-zBqpfRo2myWPrPo5xUjeZqlnPXPXsX8BcWtWff66/eGRQdbPjhzPgXa/F+AtxT2afUViPxbuDlwscMKzQ5tg+g==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.142.0.tgz", + "integrity": "sha512-hTsHtTLxMAfCo+rpF5K3qZJKW2NpPN/CHd4mYB3y7XlSdspHkd2gehDIofP64AacA9nWQw2tY3O7wR6UY8IVOA==", "cpu": [ "riscv64" ], @@ -1676,9 +1681,9 @@ } }, "node_modules/@oxc-parser/binding-linux-s390x-gnu": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.140.0.tgz", - "integrity": "sha512-2M1DPm/8w9I//YzFlFC9qXw+r2tJFh5CYwRlYTq2vUJQS7qoQftEDeCZ8EnN7KHtvSiXvYj8mZI5pR7DpXmcEw==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.142.0.tgz", + "integrity": "sha512-6y7qYY3TCUDYjqswImdTGl92y+KA/80twALegQPN27kfY+bG7Ib1+L3jbmrCZQx6wrVnai9IPsEZp07I0hx7JQ==", "cpu": [ "s390x" ], @@ -1696,9 +1701,9 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-gnu": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.140.0.tgz", - "integrity": "sha512-8aRDbZ/U/jO8N7go1MO72jtbpb4uswV8d7vOkMvt/BPgZiyEYvl1VIWK4ESxZZhnJ4tqwVldgX7dNiP/eB1Jdg==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.142.0.tgz", + "integrity": "sha512-i69kAWU+2LgoH5bR+zWiiu+UzAw7Oxkwv7COeJTeY19pn4e70nKQcr9Pm6cL2Z0Z54d+gl9qADlK/0yyuCPiBA==", "cpu": [ "x64" ], @@ -1716,9 +1721,9 @@ } }, "node_modules/@oxc-parser/binding-linux-x64-musl": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.140.0.tgz", - "integrity": "sha512-xRqpeI8U2sQQS1W5BMWRyMTxtagkuLG2dEWruet5lFsWHTvBth11/TpSaJatHdqVVwHN0q3uuoS9zRsGinq8hg==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.142.0.tgz", + "integrity": "sha512-4SQs678MmjYVrmhAgCWD4o0vpaFszXw9xLX5p2Z9MMFcltxiLkA88wQjh80YHjPrXtpyZ2CWI5m+1yNKM0m2Pw==", "cpu": [ "x64" ], @@ -1736,9 +1741,9 @@ } }, "node_modules/@oxc-parser/binding-openharmony-arm64": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.140.0.tgz", - "integrity": "sha512-GbGRe26MqAKciFRvXeHNQJ6VAHYs9R4miP89sEAncysM3n+f4lnyLWgsa9kklJNpfnxdq2yRoNYHFqwBckVimw==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.142.0.tgz", + "integrity": "sha512-YHpx9N7Ln3a++Tc8rv+H7mrK1zyJQOAwCFg8LZ3lTs1T5afGWeZrLPhPT9HLnIwSjCyJqPWVMIrMxbjcmBr2oQ==", "cpu": [ "arm64" ], @@ -1753,9 +1758,9 @@ } }, "node_modules/@oxc-parser/binding-wasm32-wasi": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.140.0.tgz", - "integrity": "sha512-vFiC1hqys+hkX1GnQkIoiTQJNiUm43Z0lO35ETKXTw0YtpW7+cN58YRRXFAQQ+TgpkIi3lrhcxdlnqz+Oi3ptQ==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.142.0.tgz", + "integrity": "sha512-3pLDyY3+oogW73RM5uehNgAiR/Xfb7fvO2Q1Z1gIqZ2+50XDVQmBVlRkHXZTU4gKnQHpwETNsYQVsJ3joVB2iA==", "cpu": [ "wasm32" ], @@ -1795,9 +1800,9 @@ } }, "node_modules/@oxc-parser/binding-win32-arm64-msvc": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.140.0.tgz", - "integrity": "sha512-fGSQldwEYKhM+H8uLt76Op8hh5+FYaR6lvvQ1Txw3Mhn86DyQXLcI0fi1EkFlTK7F+46OCk/j0AJMzZQm6g5Xg==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.142.0.tgz", + "integrity": "sha512-Had/VeVY28Oyb0K+Q4FV8KCzoBycIh93oDK6pCbya9lkzdq+ikMHMgBubsdqqlybjJmQRawCQRrnBRHyQwYvcQ==", "cpu": [ "arm64" ], @@ -1812,9 +1817,9 @@ } }, "node_modules/@oxc-parser/binding-win32-ia32-msvc": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.140.0.tgz", - "integrity": "sha512-sDS2Bai+g3ZWYwfZqmosiSuFDBcVnZ3Ta6pszzsiJoLMqsJEWKcxXXbGa7b7yXr++W2lQNPb3ZRJ8czseqL7RA==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.142.0.tgz", + "integrity": "sha512-GGi3+YphVHavvgs6gum2UXoNCqzHAmPt/nXkn8ZQZstV2Q1qZD1Mn8fz/nWrDkefHQtrG/+1/XrbMxsBTo6Svw==", "cpu": [ "ia32" ], @@ -1829,9 +1834,9 @@ } }, "node_modules/@oxc-parser/binding-win32-x64-msvc": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.140.0.tgz", - "integrity": "sha512-kHbE1zWyb5OQgJA6/5P4WjiuB01sYdQwtZnSSyE58FQEXDAMnyeeq4vj7KgN75i5SlBzOs8A5MrtlD3gOlDKqQ==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.142.0.tgz", + "integrity": "sha512-Ny/Wv4Us1LGC/ljwNTp+Hx3r/pH15EFfeDF0p+n898gt+TtRd6C9SccHcuUhDiNTb8s5tt7jdeAMDRQZ4Vq6hg==", "cpu": [ "x64" ], @@ -1846,10 +1851,10 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.140.0.tgz", - "integrity": "sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ==", - "dev": true, + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.142.0.tgz", + "integrity": "sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==", + "devOptional": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" @@ -2185,9 +2190,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", - "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.1.tgz", + "integrity": "sha512-02hOeOSryYxVrOIphmLAsqnCJWxwlzFk+pEt/N/i6OgT3lShHO7xGCU5cpgchRDHboAEbSjzgGh+O/u1GswQmA==", "cpu": [ "arm64" ], @@ -2201,9 +2206,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", - "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.1.tgz", + "integrity": "sha512-fMsTOnN0OjFm3CyppWPitKnc8UlliVARUULW6cfU6AIqjdtgmSFWSk9vecHzZduv/yMWIHDlRhM1e8Iff9uAfA==", "cpu": [ "arm64" ], @@ -2217,9 +2222,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", - "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.1.tgz", + "integrity": "sha512-1wjKdz/XLGKHaTNHjQveQ/B23TKx4ItAqm1JbyVuvNPc4Ze0Fb48s49TAd/2zcplPl8okE/UbTgmlVfwT7eFeQ==", "cpu": [ "x64" ], @@ -2233,9 +2238,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", - "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.1.tgz", + "integrity": "sha512-Fa0jHR07E7YBN4vOEsbVf2briYNsuOowfLJaXULZM0ldMlaCaj2LJgLMbMe4iacRyZmvR8efFhgR9wKuGclQUg==", "cpu": [ "x64" ], @@ -2249,9 +2254,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", - "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.1.tgz", + "integrity": "sha512-pzkgu1SSHGgRRyRZ4fbmSgmajbVt+epaLP99NDjFft69v/ypfTi6swBMiVdh2EkQ0OSnHE1lZDM7DRGkyAzUpA==", "cpu": [ "arm" ], @@ -2265,9 +2270,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", - "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.1.tgz", + "integrity": "sha512-QI5SEDY8cbiYWHx0VO4vIc3UlS6a32vXHjU8Qy/17adEmZIPuByJg13UEvo9c/UCiUkdcVWY83C+b+JrwnNyUg==", "cpu": [ "arm64" ], @@ -2284,9 +2289,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", - "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.1.tgz", + "integrity": "sha512-Sm41FyCeXqmYcERoYOCbGIL5hNfd8w9LQ7Y61Bev48HkcjaJqV/iiVOaiDxjVTRMS+QKrZmD8cfPt4uMVnvM+A==", "cpu": [ "arm64" ], @@ -2303,9 +2308,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", - "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.1.tgz", + "integrity": "sha512-2x+WhXTGl9yJYPbltW/BSEPTVz9OIWQyER4N+gJEDWkkn904eRcBzELqh/Hf7K0w/ubGbKNMv0ZC+94QK/IFEg==", "cpu": [ "ppc64" ], @@ -2322,9 +2327,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", - "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.1.tgz", + "integrity": "sha512-eEjmQpuRQayHPWWnywaWHkFT3ToPbP3RYy42VVd/B9aBGDA+Ol25EIWHxKQST3IiWJjikCWUF7KtbfqwZrzVwQ==", "cpu": [ "s390x" ], @@ -2341,9 +2346,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", - "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.1.tgz", + "integrity": "sha512-/Orga1fZYkLc/56jBICcHrKchl8Z2UKdDSr3LG9ToWO1lQ6a4Livk9Xz+9WN91zsz5QR3XQz2NNoSDEvP6qadw==", "cpu": [ "x64" ], @@ -2360,9 +2365,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", - "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.1.tgz", + "integrity": "sha512-xxBJRL+0q0Kce7orznGWLuylHDY65vuARXZRpX+hPdv+DqK2c3NlCsVA98tlWzWNEE7yPqA/1NQ5nnCrj49Y5A==", "cpu": [ "x64" ], @@ -2379,9 +2384,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", - "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.1.tgz", + "integrity": "sha512-M6AdXIXw3s+/8XpKMzdGDEXGS1S7kwUsy+rcTIUIOx5Ge4nXKCtAFHFV9YKkXvGcC5WMoTjAteLzlsQROVI0Yw==", "cpu": [ "arm64" ], @@ -2395,27 +2400,55 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", - "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", - "cpu": [ - "wasm32" - ], + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.2.1.tgz", + "integrity": "sha512-/TX0SoRGojHzSAHpfVBbavRVSazg5U3h3Y3VXfcc0cdugq6kxdqw8LPGFiPr+/7gE/60zRcsOY2Vi9b9eT0jww==", "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.11.1", - "@emnapi/runtime": "1.11.1", - "@napi-rs/wasm-runtime": "^1.1.6" + "@emnapi/core": "2.0.0-alpha.3", + "@emnapi/runtime": "2.0.0-alpha.3", + "@napi-rs/wasm-runtime": "^1.2.0" }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/core": { + "version": "2.0.0-alpha.3", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-2.0.0-alpha.3.tgz", + "integrity": "sha512-AZypUeJ/yByuxyS7BlSNRDOMLMlROYtjYdIAuBmJssVz1UJDSeYxLrdizhXCFYhedC5bqd/ASy8EuNXbVVXp9g==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "2.0.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "2.0.0-alpha.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-2.0.0-alpha.3.tgz", + "integrity": "sha512-hFPAhMUjJD9BSyCANEISPOogeXC9Zo9ZQl7L6vKnaVsMkCtzznaW/naYypeyl0Gv5rYfWYsZbpixTMpjDJzQeA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-2.0.1.tgz", + "integrity": "sha512-9DsSk+o5NBX0CCJT8s0EROGSGxjR/tKu6aBTaVyq+SjAEQH4XcdcRxPBRzsBLizTTJ49MJjF+jgu3qnO9GLQcQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", - "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.1.tgz", + "integrity": "sha512-EvRrivJieyHG+AO9lleZWgq+g0+S7oV2C51yuqlcyU/R9net+sI4Pj0F+lUoP2bEr6TWX3SqFaaS0SzfLxSzkw==", "cpu": [ "arm64" ], @@ -2429,9 +2462,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", - "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.1.tgz", + "integrity": "sha512-Z4eCmn5QJ/5+azF9knpLWKfVd9aidn0mAe9TpJgvBLId9Ax3t0+JVxBmT25Bv7NBbVW1TZyKjQjQReouMeH5UQ==", "cpu": [ "x64" ], @@ -3534,9 +3567,9 @@ } }, "node_modules/brace-expansion": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", - "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { @@ -4078,9 +4111,9 @@ } }, "node_modules/fast-uri": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-4.1.1.tgz", - "integrity": "sha512-YPOs1zD5TG2+EZt+r88LwF6mclA7TPkpwMP7ZN3TO2HiHS8TXvq7QA/17iJsV9dubcLo/f8eEYqMBruyQV21hQ==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-4.1.2.tgz", + "integrity": "sha512-TyGmBcbDTZXcb2cj5MV89DrF42DKvb3y5DDUNh95iO+IMeAzMkVSxK1PZRrRIpc9yg8U2GhGdbofNa0LS/a4Bw==", "dev": true, "funding": [ { @@ -4713,9 +4746,9 @@ } }, "node_modules/knip": { - "version": "6.29.0", - "resolved": "https://registry.npmjs.org/knip/-/knip-6.29.0.tgz", - "integrity": "sha512-A3kXqSBky1tWBAqiU9srdtu0Larhzkuyor0aD/gg+ToiqyBncCCs2Q60sLsxmcKhV0OsKss9LV0hMPpwLv711Q==", + "version": "6.31.0", + "resolved": "https://registry.npmjs.org/knip/-/knip-6.31.0.tgz", + "integrity": "sha512-NbeIEmUS2VUMjAkbiSNOKPJeV9wpCsr0660sUyKyMQbk4Iom0++nTLInVp4MJ+LfR4kORnw67bDi5tvO7YLnzA==", "dev": true, "funding": [ { @@ -4733,13 +4766,13 @@ "formatly": "^0.3.0", "get-tsconfig": "4.14.0", "jiti": "^2.7.0", - "oxc-parser": "^0.140.0", + "oxc-parser": "^0.142.0", "oxc-resolver": "11.24.2", "picomatch": "^4.0.5", - "smol-toml": "^1.7.0", + "smol-toml": "^1.7.1", "strip-json-comments": "5.0.3", "tinyglobby": "^0.2.17", - "unbash": "^4.0.3", + "unbash": "^4.0.4", "yaml": "^2.9.0", "zod": "^4.4.3" }, @@ -4755,7 +4788,7 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "devOptional": true, + "dev": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" @@ -4788,6 +4821,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -4808,6 +4842,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -4828,6 +4863,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -4848,6 +4884,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -4868,6 +4905,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -4888,6 +4926,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -4908,6 +4947,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -4928,6 +4968,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -4948,6 +4989,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -4968,6 +5010,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -4988,6 +5031,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -5337,13 +5381,13 @@ "license": "MIT" }, "node_modules/oxc-parser": { - "version": "0.140.0", - "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.140.0.tgz", - "integrity": "sha512-h6QFWd6lBMfjESqgQ27GjzrSDb0qbznp7VDQqp2zvgsrWut4vcchyMIzOVXvGQ2GMZgKw9RWrFNWv9WqGL0p7Q==", + "version": "0.142.0", + "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.142.0.tgz", + "integrity": "sha512-kKR+jPiRJYJDexVoziIg/FVGvr1fT1FZSSJOk6tVoMKKSlsf1Cso+cgGCJkOEDWOP174vRntCPFKg+AS7InWvw==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "^0.140.0" + "@oxc-project/types": "^0.142.0" }, "engines": { "node": "^20.19.0 || >=22.12.0" @@ -5352,26 +5396,26 @@ "url": "https://github.com/sponsors/Boshen" }, "optionalDependencies": { - "@oxc-parser/binding-android-arm-eabi": "0.140.0", - "@oxc-parser/binding-android-arm64": "0.140.0", - "@oxc-parser/binding-darwin-arm64": "0.140.0", - "@oxc-parser/binding-darwin-x64": "0.140.0", - "@oxc-parser/binding-freebsd-x64": "0.140.0", - "@oxc-parser/binding-linux-arm-gnueabihf": "0.140.0", - "@oxc-parser/binding-linux-arm-musleabihf": "0.140.0", - "@oxc-parser/binding-linux-arm64-gnu": "0.140.0", - "@oxc-parser/binding-linux-arm64-musl": "0.140.0", - "@oxc-parser/binding-linux-ppc64-gnu": "0.140.0", - "@oxc-parser/binding-linux-riscv64-gnu": "0.140.0", - "@oxc-parser/binding-linux-riscv64-musl": "0.140.0", - "@oxc-parser/binding-linux-s390x-gnu": "0.140.0", - "@oxc-parser/binding-linux-x64-gnu": "0.140.0", - "@oxc-parser/binding-linux-x64-musl": "0.140.0", - "@oxc-parser/binding-openharmony-arm64": "0.140.0", - "@oxc-parser/binding-wasm32-wasi": "0.140.0", - "@oxc-parser/binding-win32-arm64-msvc": "0.140.0", - "@oxc-parser/binding-win32-ia32-msvc": "0.140.0", - "@oxc-parser/binding-win32-x64-msvc": "0.140.0" + "@oxc-parser/binding-android-arm-eabi": "0.142.0", + "@oxc-parser/binding-android-arm64": "0.142.0", + "@oxc-parser/binding-darwin-arm64": "0.142.0", + "@oxc-parser/binding-darwin-x64": "0.142.0", + "@oxc-parser/binding-freebsd-x64": "0.142.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.142.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.142.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.142.0", + "@oxc-parser/binding-linux-arm64-musl": "0.142.0", + "@oxc-parser/binding-linux-ppc64-gnu": "0.142.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.142.0", + "@oxc-parser/binding-linux-riscv64-musl": "0.142.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.142.0", + "@oxc-parser/binding-linux-x64-gnu": "0.142.0", + "@oxc-parser/binding-linux-x64-musl": "0.142.0", + "@oxc-parser/binding-openharmony-arm64": "0.142.0", + "@oxc-parser/binding-wasm32-wasi": "0.142.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.142.0", + "@oxc-parser/binding-win32-ia32-msvc": "0.142.0", + "@oxc-parser/binding-win32-x64-msvc": "0.142.0" } }, "node_modules/oxc-resolver": { @@ -5574,9 +5618,9 @@ } }, "node_modules/postcss": { - "version": "8.5.24", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.24.tgz", - "integrity": "sha512-8RyVklq0owXUTa4xlpzu4l9AaVKIdQvAcOHZWaMh98HgySsUtxRVf/chRe3dsSLqb6i40BzGRzEUddRaI+9TSw==", + "version": "8.5.25", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", + "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", "funding": [ { "type": "opencollective", @@ -5717,13 +5761,13 @@ "license": "MIT" }, "node_modules/rolldown": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", - "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.1.tgz", + "integrity": "sha512-4FKJhg8d3OiyQOA6Q1Q0hoFFpW9/OoX+VsHzpECsdsIZoOArrAK90gl59YK/Z+gnDel45bgJZK03ozH/9bCqEw==", "devOptional": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.139.0", + "@oxc-project/types": "=0.142.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -5733,31 +5777,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.1.5", - "@rolldown/binding-darwin-arm64": "1.1.5", - "@rolldown/binding-darwin-x64": "1.1.5", - "@rolldown/binding-freebsd-x64": "1.1.5", - "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", - "@rolldown/binding-linux-arm64-gnu": "1.1.5", - "@rolldown/binding-linux-arm64-musl": "1.1.5", - "@rolldown/binding-linux-ppc64-gnu": "1.1.5", - "@rolldown/binding-linux-s390x-gnu": "1.1.5", - "@rolldown/binding-linux-x64-gnu": "1.1.5", - "@rolldown/binding-linux-x64-musl": "1.1.5", - "@rolldown/binding-openharmony-arm64": "1.1.5", - "@rolldown/binding-wasm32-wasi": "1.1.5", - "@rolldown/binding-win32-arm64-msvc": "1.1.5", - "@rolldown/binding-win32-x64-msvc": "1.1.5" - } - }, - "node_modules/rolldown/node_modules/@oxc-project/types": { - "version": "0.139.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", - "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", - "devOptional": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" + "@rolldown/binding-android-arm64": "1.2.1", + "@rolldown/binding-darwin-arm64": "1.2.1", + "@rolldown/binding-darwin-x64": "1.2.1", + "@rolldown/binding-freebsd-x64": "1.2.1", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.1", + "@rolldown/binding-linux-arm64-gnu": "1.2.1", + "@rolldown/binding-linux-arm64-musl": "1.2.1", + "@rolldown/binding-linux-ppc64-gnu": "1.2.1", + "@rolldown/binding-linux-s390x-gnu": "1.2.1", + "@rolldown/binding-linux-x64-gnu": "1.2.1", + "@rolldown/binding-linux-x64-musl": "1.2.1", + "@rolldown/binding-openharmony-arm64": "1.2.1", + "@rolldown/binding-wasm32-wasi": "1.2.1", + "@rolldown/binding-win32-arm64-msvc": "1.2.1", + "@rolldown/binding-win32-x64-msvc": "1.2.1" } }, "node_modules/rxjs": { @@ -6477,16 +6511,16 @@ } }, "node_modules/vite": { - "version": "8.1.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz", - "integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.0.tgz", + "integrity": "sha512-pn+CFpM0lwDeKwmOq1ZaBK/9sjorZcgqxki6MbY/jPEVd9vichIlmlD4HmQ5wdP5EgqQCFRaACBxMC7uEGc6lQ==", "devOptional": true, "license": "MIT", "dependencies": { - "lightningcss": "^1.32.0", + "lightningcss": "^1.33.0", "picomatch": "^4.0.5", - "postcss": "^8.5.17", - "rolldown": "~1.1.5", + "postcss": "^8.5.23", + "rolldown": "~1.2.0", "tinyglobby": "^0.2.17" }, "bin": { @@ -6503,7 +6537,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.3.0", + "@vitejs/devtools": "^0.4.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -6554,6 +6588,268 @@ } } }, + "node_modules/vite/node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "devOptional": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/vite/node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/vitest": { "version": "4.1.10", "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz", diff --git a/ui/package.json b/ui/package.json index 1a7a488ca..2c364527a 100644 --- a/ui/package.json +++ b/ui/package.json @@ -45,7 +45,7 @@ "@iconify-json/fa6-solid": "1.2.4", "@iconify-json/heroicons": "1.2.3", "@iconify-json/iconoir": "1.2.11", - "@iconify-json/lucide": "1.2.120", + "@iconify-json/lucide": "1.2.121", "@iconify-json/ph": "1.2.2", "@iconify-json/tabler": "1.2.38", "@stryker-mutator/core": "9.6.1", @@ -57,17 +57,18 @@ "@vitest/coverage-v8": "4.1.10", "@vue/test-utils": "2.4.11", "jsdom": "29.1.1", - "knip": "6.29.0", + "knip": "6.31.0", "typescript": "6.0.3", - "vite": "8.1.5", + "vite": "8.2.0", "vitest": "4.1.10" }, "overrides": { - "fast-uri": "4.1.1", + "brace-expansion": "5.0.9", + "fast-uri": "4.1.2", "js-cookie": "3.0.8", "minimatch": "10.2.6", "picomatch": "4.0.5", - "postcss": "8.5.24", + "postcss": "8.5.25", "qs": "6.15.3", "yaml": "2.9.0", "@babel/core": "7.29.7", diff --git a/ui/src/locales/fr/containerComponents.json b/ui/src/locales/fr/containerComponents.json index 66f529aeb..0226a29d9 100644 --- a/ui/src/locales/fr/containerComponents.json +++ b/ui/src/locales/fr/containerComponents.json @@ -169,7 +169,7 @@ "statusLive": "En direct", "statusOffline": "Hors ligne", "stdout": "stdout", - "stderr": "stderr" + "stderr": "sdterr" }, "groupedViews": { "statusQueued": "En file",