From 04691473d45274d4ca6b752acb706edf63902040 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 24 Oct 2026 22:36:41 +0800 Subject: [PATCH] chore(deps): bump zod from 3.25.76 to 4.6.6 in /frontend (#7392) * chore(deps): bump zod from 3.25.76 to 4.6.6 in /frontend Bumps [zod](https://github.com/colinhacks/zod) from 3.25.76 to 4.6.6. - [Release notes](https://github.com/colinhacks/zod/releases) - [Commits](https://github.com/colinhacks/zod/compare/v3.25.76...v4.6.6) --- updated-dependencies: - dependency-name: zod dependency-version: 4.6.6 dependency-type: direct:production ... Signed-off-by: dependabot[bot] * fix(frontend): complete Zod 4 migration Use explicit string keys for extension setting records so the discovery schema remains assignable to the extension loader contract under Zod 4. Cover accepted primitives, rejected non-primitive settings and inferred public types through the discovery API. Keep Nextra 4.6.1 and its docs theme on their previous Zod 4.3.6 dependency until upstream fixes required children validation. The app remains on Zod 4.6.6. Document and lock the targeted compatibility overrides. * docs: keep frontend guidance within hard budget --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Willem Jiang --- frontend/AGENTS.md | 10 +-- frontend/package.json | 2 +- frontend/pnpm-lock.yaml | 50 ++++++------- frontend/pnpm-workspace.yaml | 4 ++ frontend/src/core/extensions/api.ts | 5 +- .../tests/unit/core/extensions/api.test.ts | 72 +++++++++++++++++++ 6 files changed, 112 insertions(+), 31 deletions(-) create mode 100644 frontend/tests/unit/core/extensions/api.test.ts diff --git a/frontend/AGENTS.md b/frontend/AGENTS.md index 13870c6e2f..870887b8b8 100644 --- a/frontend/AGENTS.md +++ b/frontend/AGENTS.md @@ -12,11 +12,11 @@ Core dependencies: `@langchain/langgraph-sdk` ^1.5.3 for orchestration/streaming `@langchain/core` ^1.1.15, and `@tanstack/react-query` ^5.90.17 for server state. Registry UI: Shadcn, MagicUI, React Bits, Vercel AI SDK (see Code Style). -`pnpm-workspace.yaml` overrides vulnerable `@xmldom/xmldom` 0.9.x releases to -0.9.12 for GHSA-965w-775f-mr7g. Nextra pulls it in through MathJax and -`speech-rule-engine@4.1.2`, which pins 0.9.8. Keep the override until the -upstream dependency chain resolves a patched version without it; regenerate -`pnpm-lock.yaml` and verify the docs build when changing this constraint. +`pnpm-workspace.yaml` pins vulnerable `@xmldom/xmldom` 0.9.x to patched +0.9.12 (GHSA-965w-775f-mr7g) and Nextra 4.6.1's Zod dependencies to 4.3.6 +until upstream fixes missing-`children` validation. App schemas use Zod 4.6.6. +Remove each override after its upstream fix; refresh the lockfile and test +docs builds after changes. ## Commands diff --git a/frontend/package.json b/frontend/package.json index d3ecea5361..69b6bc6131 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -101,7 +101,7 @@ "unist-util-visit": "^5.0.0", "use-stick-to-bottom": "^1.1.1", "uuid": "^14.0.0", - "zod": "^3.24.2" + "zod": "^4.6.6" }, "devDependencies": { "@eslint/eslintrc": "^3.3.1", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 40f3487f86..da41b3a7b4 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -6,6 +6,8 @@ settings: overrides: '@xmldom/xmldom@>=0.9.0 <0.9.12': 0.9.12 + nextra@4.6.1>zod: 4.3.6 + nextra-theme-docs@4.6.1>zod: 4.3.6 importers: @@ -100,7 +102,7 @@ importers: version: 1.0.2(react@19.2.4) '@t3-oss/env-nextjs': specifier: ^0.12.0 - version: 0.12.0(typescript@5.9.3)(zod@3.25.76) + version: 0.12.0(typescript@5.9.3)(zod@4.6.6) '@tanstack/react-query': specifier: ^5.90.17 version: 5.90.20(react@19.2.4) @@ -124,7 +126,7 @@ importers: version: 12.10.0(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) ai: specifier: ^6.0.33 - version: 6.0.78(zod@3.25.76) + version: 6.0.78(zod@4.6.6) best-effort-json-parser: specifier: ^1.2.1 version: 1.2.1 @@ -252,8 +254,8 @@ importers: specifier: ^14.0.0 version: 14.0.0 zod: - specifier: ^3.24.2 - version: 3.25.76 + specifier: ^4.6.6 + version: 4.6.6 devDependencies: '@eslint/eslintrc': specifier: ^3.3.1 @@ -5985,12 +5987,12 @@ packages: yoga-wasm-web@0.3.3: resolution: {integrity: sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA==} - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zod@4.6.6: + resolution: {integrity: sha512-4UmzQUF5fxvfcYHlbjBmejU+w+hbDnWFx4aS8kBx7nPBHcO2SqPbpAG8snbdg+Xse4pRefR98hgjFgIMCI/UhA==} + zustand@4.5.7: resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} engines: {node: '>=12.7.0'} @@ -6029,19 +6031,19 @@ packages: snapshots: - '@ai-sdk/gateway@3.0.39(zod@3.25.76)': + '@ai-sdk/gateway@3.0.39(zod@4.6.6)': dependencies: '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.14(zod@3.25.76) + '@ai-sdk/provider-utils': 4.0.14(zod@4.6.6) '@vercel/oidc': 3.1.0 - zod: 3.25.76 + zod: 4.6.6 - '@ai-sdk/provider-utils@4.0.14(zod@3.25.76)': + '@ai-sdk/provider-utils@4.0.14(zod@4.6.6)': dependencies: '@ai-sdk/provider': 3.0.8 '@standard-schema/spec': 1.1.0 eventsource-parser: 3.0.6 - zod: 3.25.76 + zod: 4.6.6 '@ai-sdk/provider@3.0.8': dependencies: @@ -6702,7 +6704,7 @@ snapshots: mustache: 4.2.0 p-queue: 6.6.2 uuid: 10.0.0 - zod: 3.25.76 + zod: 4.6.6 transitivePeerDependencies: - '@opentelemetry/api' - '@opentelemetry/exporter-trace-otlp-proto' @@ -7907,17 +7909,17 @@ snapshots: dependencies: tslib: 2.8.1 - '@t3-oss/env-core@0.12.0(typescript@5.9.3)(zod@3.25.76)': + '@t3-oss/env-core@0.12.0(typescript@5.9.3)(zod@4.6.6)': optionalDependencies: typescript: 5.9.3 - zod: 3.25.76 + zod: 4.6.6 - '@t3-oss/env-nextjs@0.12.0(typescript@5.9.3)(zod@3.25.76)': + '@t3-oss/env-nextjs@0.12.0(typescript@5.9.3)(zod@4.6.6)': dependencies: - '@t3-oss/env-core': 0.12.0(typescript@5.9.3)(zod@3.25.76) + '@t3-oss/env-core': 0.12.0(typescript@5.9.3)(zod@4.6.6) optionalDependencies: typescript: 5.9.3 - zod: 3.25.76 + zod: 4.6.6 '@tailwindcss/node@4.1.18': dependencies: @@ -8578,13 +8580,13 @@ snapshots: acorn@8.15.0: {} - ai@6.0.78(zod@3.25.76): + ai@6.0.78(zod@4.6.6): dependencies: - '@ai-sdk/gateway': 3.0.39(zod@3.25.76) + '@ai-sdk/gateway': 3.0.39(zod@4.6.6) '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.14(zod@3.25.76) + '@ai-sdk/provider-utils': 4.0.14(zod@4.6.6) '@opentelemetry/api': 1.9.0 - zod: 3.25.76 + zod: 4.6.6 ajv@6.12.6: dependencies: @@ -12578,10 +12580,10 @@ snapshots: yoga-wasm-web@0.3.3: {} - zod@3.25.76: {} - zod@4.3.6: {} + zod@4.6.6: {} + zustand@4.5.7(@types/react@19.2.13)(react@19.2.4): dependencies: use-sync-external-store: 1.6.0(react@19.2.4) diff --git a/frontend/pnpm-workspace.yaml b/frontend/pnpm-workspace.yaml index 70c2ebb4f7..aa5f953f31 100644 --- a/frontend/pnpm-workspace.yaml +++ b/frontend/pnpm-workspace.yaml @@ -2,6 +2,10 @@ packages: [] overrides: # GHSA-965w-775f-mr7g: speech-rule-engine 4.1.2 pins vulnerable 0.9.8. "@xmldom/xmldom@>=0.9.0 <0.9.12": 0.9.12 + # Nextra 4.6.1 strips children before validating required Layout props. + # Keep its previous Zod until https://github.com/shuding/nextra/issues/5036 is fixed. + "nextra@4.6.1>zod": 4.3.6 + "nextra-theme-docs@4.6.1>zod": 4.3.6 ignoredBuiltDependencies: - esbuild - sharp diff --git a/frontend/src/core/extensions/api.ts b/frontend/src/core/extensions/api.ts index 2606c1dc58..b8ce37a1b9 100644 --- a/frontend/src/core/extensions/api.ts +++ b/frontend/src/core/extensions/api.ts @@ -14,7 +14,10 @@ const contributions = z.array( transport: z.string().nullable().optional(), title: z.string(), description: z.string(), - settings: z.record(z.union([z.boolean(), z.number(), z.string()])), + settings: z.record( + z.string(), + z.union([z.boolean(), z.number(), z.string()]), + ), }), ); diff --git a/frontend/tests/unit/core/extensions/api.test.ts b/frontend/tests/unit/core/extensions/api.test.ts new file mode 100644 index 0000000000..b3a4248b7a --- /dev/null +++ b/frontend/tests/unit/core/extensions/api.test.ts @@ -0,0 +1,72 @@ +import { afterEach, expect, rs, test } from "@rstest/core"; + +import { fetchFrontendExtensions } from "@/core/extensions/api"; +import type { FrontendContribution } from "@/core/extensions/contracts"; + +rs.mock("@/core/config", () => ({ getBackendBaseURL: () => "" })); +rs.mock("@/core/static-mode", () => ({ isStaticWebsiteOnly: () => false })); +rs.mock("@/core/api/static-response", () => ({ staticApiResponse: rs.fn() })); + +const entry = { + namespace: "community.bookmarks", + module: "bookmarks.v1", + entry: `/api/plugins/modules/bookmarks.v1/${"a".repeat(64)}.mjs`, + title: "Bookmarks", + description: "", +}; + +afterEach(() => { + rs.restoreAllMocks(); +}); + +for (const [label, settings] of [ + ["empty", {}], + [ + "mixed primitive", + { + enabled: true, + disabled: false, + limit: 3, + zero: 0, + label: "Bookmarks", + empty: "", + }, + ], +] as const) { + test(`discovery preserves ${label} settings for the extension loader`, async () => { + const request = rs + .spyOn(globalThis, "fetch") + .mockResolvedValue( + new Response(JSON.stringify([{ ...entry, settings }])), + ); + + // pnpm typecheck verifies the discovery schema's inferred public contract. + const discovered: FrontendContribution[] = await fetchFrontendExtensions(); + + expect(discovered).toEqual([{ ...entry, settings }]); + expect(request).toHaveBeenCalledWith( + "/api/plugins", + expect.objectContaining({ credentials: "include", cache: "no-store" }), + ); + }); +} + +for (const [label, settings] of [ + ["null value", { enabled: null }], + ["nested object", { options: { enabled: true } }], + ["array value", { options: ["bookmarks"] }], + ["null record", null], + ["array record", []], + ["string record", "enabled"], + ["missing record", undefined], +] as const) { + test(`discovery rejects settings with a ${label}`, async () => { + rs.spyOn(globalThis, "fetch").mockResolvedValue( + new Response(JSON.stringify([{ ...entry, settings }])), + ); + + await expect(fetchFrontendExtensions()).rejects.toMatchObject({ + name: "ZodError", + }); + }); +}