Skip to content

deps: upgrade to zod 4 - #2689

Merged
MohamedBassem merged 2 commits into
karakeep-app:mainfrom
MohamedBassem:zod-v4
Apr 14, 2026
Merged

MohamedBassem merged 2 commits into
karakeep-app:mainfrom
MohamedBassem:zod-v4

Conversation

@MohamedBassem

Copy link
Copy Markdown
Member

No description provided.

@coderabbitai

coderabbitai Bot commented Apr 10, 2026 •

Copy link
Copy Markdown
Contributor

Walkthrough

Monorepo-wide Zod migration and OpenAPI adjustments: dependency bumps to Zod v4, Zod API/import changes, schema composition/defaulting migrations (.merge→.extend, .default→.prefault), targeted code fixes for Zod v4 incompatibilities, and regenerated OpenAPI spec updates.

Changes

Cohort / File(s) Summary
Dependency bumps
apps/browser-extension/package.json, apps/mcp/package.json, apps/mobile/package.json, apps/web/package.json, apps/workers/package.json, packages/api/package.json, packages/open-api/package.json, packages/shared/package.json, packages/trpc/package.json, packages/benchmarks/package.json, packages/e2e_tests/package.json, tools/compare-models/package.json
Bumped zod to ^4.0.0 across many packages; also bumped related deps (@modelcontextprotocol/sdk, @asteasolutions/zod-to-openapi, @hono/zod-validator, liteque, openai) and removed zod-to-json-schema from shared.
OpenAPI bootstrap & spec
packages/open-api/zod-extend.ts, packages/open-api/index.ts, packages/open-api/karakeep-openapi-spec.json
Added zod-extend side-effect module to call extendZodWithOpenApi(z) at import; updated OpenAPI spec (PaginatedBookmarks reintroduced, date-time formats, enums allowing null, file/binary schemas, parameter descriptions).
Zod import style & extension removal
packages/open-api/lib/*.ts (admin, assets, backups, bookmarks, errors, feeds, highlights, lists, tags, types, users)
Replaced named z imports with import * as z and removed local extendZodWithOpenApi(z) calls; adjusted usages accordingly.
Schema composition changes
packages/shared/types/*.ts, packages/api/routes/*.ts, packages/open-api/lib/bookmarks.ts, packages/open-api/lib/lists.ts, packages/open-api/lib/tags.ts, packages/trpc/routers/*.ts
Replaced .merge() / .and() with .extend(...shape) / safeExtend / intersection or equivalent patterns to compose object schemas under Zod v4.
Defaulting/prefault changes
packages/shared/config.ts, packages/plugins/queue-restate/src/env.ts, packages/trpc/routers/subscriptions.ts, tools/compare-models/src/config.ts, packages/api/utils/types.ts
Replaced several .default() usages on parsed/coerced/env schemas with .prefault() per Zod v4 semantics.
Zod API migrations & minor logic tweaks
apps/mcp/src/bookmarks.ts, apps/mcp/src/tags.ts, apps/workers/workers/crawlerWorker.ts, apps/workers/workers/utils/parseHtmlSubprocessIpc.ts, apps/workers/metascraper-plugins/metascraper-reddit.ts, packages/shared/inference.ts, packages/trpc/index.ts, packages/shared/types/rules.ts, apps/workers/...
Adjusted code to Zod v4 API surfaces: conditional object selection patterns, error.issues usage, z.looseObject() replacing .passthrough(), z.toJSONSchema() replacing external call, z.flattenError() usage, narrowed generic Zod type constraints, and small control-flow return change.
UI form validation tweaks
apps/web/components/dashboard/bookmarks/BulkManageListsModal.tsx, apps/web/components/dashboard/bookmarks/ManageListsModal.tsx, apps/web/components/signin/ResetPasswordForm.tsx
Changed Zod field error callbacks (required_error → conditional error), and replaced .merge() with .extend() in password schema composition without altering form wiring.
Misc small changes
apps/mcp/src/bookmarks.ts, apps/mcp/src/tags.ts, packages/plugins/queue-restate/src/types.ts, others
Explicit typing in map callbacks, switch to explicit conditional payload construction, tightened record key types, and updated generic constraints from z.ZodTypeAny → z.ZodType.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to verify that the description is related to the changeset. Please provide a pull request description explaining the motivation and impact of upgrading Zod to version 4, including any breaking changes or migration steps taken.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'deps: upgrade to zod 4' directly and clearly describes the primary change: upgrading the Zod dependency from version 3 to version 4 across the entire codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented Apr 10, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR upgrades Zod from v3 to v4 across the entire monorepo. The migration correctly applies all Zod 4 breaking-change patterns: .prefault() replaces .default() on transform chains (where the default value must be the input type, not the output type), .safeExtend() is used wherever the base schema carries refinements (e.g. zSignUpSchema), z.looseObject() replaces .passthrough(), z.toJSONSchema() replaces the zod-to-json-schema library, ZodError.issues replaces .errors, and extendZodWithOpenApi is consolidated into a single side-effect import to avoid double-registration.

Confidence Score: 5/5

  • This PR is safe to merge; the migration is thorough and all Zod 4 API changes are applied correctly.
  • All breaking-change patterns from Zod 3 → 4 are handled correctly. prefault/default semantics, safeExtend on refined schemas, z.intersection for discriminated unions, and the centralized extendZodWithOpenApi call are all sound. No P0/P1 issues found; the updated OpenAPI spec JSON confirms the spec generation was also validated.
  • No files require special attention.

Important Files Changed

Filename Overview
packages/shared/config.ts Correctly replaces .default("x") with .prefault("x") on transform chains, preserving pre-parse semantics; plain-number defaults like .default(0) are untouched.
packages/shared/types/bookmarks.ts Migrates .merge(z.object(...)) → .extend(z.object(...).shape), replaces .and(discriminatedUnion) with z.intersection(), and adds .meta() for OpenAPI datetime hints; all changes are semantically correct.
packages/open-api/zod-extend.ts New file that centralizes the single extendZodWithOpenApi(z) call; individual lib files no longer call it, avoiding duplicate registration issues in Zod 4.
packages/open-api/index.ts Side-effect imports ./zod-extend before all lib registries, guaranteeing extendZodWithOpenApi is called before any .openapi() method is invoked at module init time.
packages/shared/inference.ts Removes zod-to-json-schema dependency and uses Zod 4's native z.toJSONSchema() for Ollama structured output; functionally equivalent.
packages/trpc/routers/users.ts Uses zSignUpSchema.safeExtend() correctly — safeExtend is the required method for extending schemas that contain refinements in Zod 4 (plain .extend() would throw).
packages/shared/types/admin.ts Replaces .and() with safeExtend() to combine zSignUpSchema and zRoleSchema; correctly handles the refinement on zSignUpSchema.
packages/api/utils/pagination.ts Splits the transform+pipe into two transforms to work around Zod 4 coerce.date() behavior in pipelines; functionally correct, slightly verbose.
apps/workers/workers/utils/parseHtmlSubprocessIpc.ts Replaces .passthrough() with z.looseObject(), which is the Zod 4 canonical API for pass-through object schemas.
packages/plugins/queue-restate/src/types.ts Updates z.ZodTypeAny to z.ZodType generic constraint, which is the correct Zod 4 replacement for ZodTypeAny.

Reviews (2): Last reviewed commit: "fix: preserve zod 4 schema compatibility" | Re-trigger Greptile

Comment thread packages/shared/inference.ts Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c84ea35112

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/shared/types/admin.ts Outdated
});

export const zAdminCreateUserSchema = zSignUpSchema.and(zRoleSchema);
export const zAdminCreateUserSchema = zSignUpSchema.extend(zRoleSchema.shape);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Badge Use safeExtend for refined signup schema in admin type

zSignUpSchema is defined with .refine(...) in packages/shared/types/users.ts, and in Zod 4 calling .extend() on a schema with refinements throws at schema construction time. Because zAdminCreateUserSchema is created at module load, importing @karakeep/shared/types/admin will throw before any request handling, which can break server startup paths that load admin/router code. Switch this to .safeExtend(...) (or an equivalent merge pattern) to preserve refinements without throwing.

Useful? React with 👍 / 👎.

Comment thread packages/trpc/routers/users.ts Outdated
}),
)
.input(zSignUpSchema.and(z.object({ redirectUrl: z.string().optional() })))
.input(zSignUpSchema.extend({ redirectUrl: z.string().optional() }))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Badge Avoid extend on refined signup schema in users router input

This applies .extend() to zSignUpSchema, which already has a password-confirmation refinement; in Zod 4, extending refined object schemas with .extend() throws. Since this runs while constructing the router input schema, loading the users router can fail during initialization rather than at request validation time. Use .safeExtend(...) (or intersection with a plain object schema) so the router can initialize correctly.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/open-api/karakeep-openapi-spec.json (1)

680-695: ⚠️ Potential issue | 🟡 Minor

Feed.lastFetchedAt is still missing format: "date-time"

This field is still emitted as a plain nullable string even though its own description/example treat it as a timestamp. Every endpoint returning #/components/schemas/Feed will keep advertising lastFetchedAt as unstructured text until the source schema carries the missing format metadata. Fix this in the generator/source schema rather than patching the generated JSON.

🛠️ Proposed fix
           "lastFetchedAt": {
             "type": "string",
             "nullable": true,
+            "format": "date-time",
             "description": "ISO 8601 timestamp of the last fetch attempt, or null if never fetched.",
             "example": "2025-01-15T12:00:00.000Z"
           }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/open-api/karakeep-openapi-spec.json` around lines 680 - 695, The
OpenAPI schema for Feed is emitting lastFetchedAt as a plain nullable string;
update the source schema/generator so the Feed property lastFetchedAt includes
"format": "date-time" (i.e., change the Feed schema definition that declares
lastFetchedAt to add format: "date-time"), then re-run the OpenAPI generation
step so the produced karakeep-openapi-spec.json contains the date-time format
for lastFetchedAt instead of leaving it as an unstructured string; do not
manually edit the generated JSON output.
🧹 Nitpick comments (2)
packages/open-api/index.ts (1)

8-8: Consolidate the extendZodWithOpenApi initialization path.

The new import on line 8 applies the Zod extension via ./zod-extend, but all 11 feature modules in packages/open-api/lib/ (users, tags, types, lists, highlights, feeds, errors, backups, assets, admin, bookmarks) still call extendZodWithOpenApi(z) independently. This results in the extension being patched redundantly across multiple entry points. Since the library is designed to be initialized once, remove the per-module calls and rely solely on the centralized ./zod-extend import.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/open-api/index.ts` at line 8, Multiple feature modules in
packages/open-api/lib/* (e.g., users, tags, types, lists, highlights, feeds,
errors, backups, assets, admin, bookmarks) are each calling
extendZodWithOpenApi(z) redundantly while the centralized import "./zod-extend"
in packages/open-api/index.ts already applies that extension once; remove the
per-module calls to extendZodWithOpenApi(z) from each of those feature files and
rely solely on the single centralized initialization in index.ts so the Zod
extension is applied exactly once.
packages/open-api/karakeep-openapi-spec.json (1)

3880-3882: Keep binary download endpoints consistent

These changes correctly type uploads and backup downloads as binary, but GET /assets/{assetId} still has a 200 response with no content schema. The published contract will stay asymmetric until that endpoint is documented with a binary response too.

Also applies to: 4677-4681

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/open-api/karakeep-openapi-spec.json` around lines 3880 - 3882, The
GET /assets/{assetId} response is missing a binary response schema; update the
OpenAPI path item for the operationId or path "GET /assets/{assetId}" to add a
200 response content entry such as "application/octet-stream" (or the
appropriate media type) with schema { "type": "string", "format": "binary" } so
the endpoint is documented as returning binary data; apply the same fix to the
other occurrence noted (around lines 4677-4681) to keep upload/download
endpoints symmetric.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/open-api/zod-extend.ts`:
- Around line 1-4: Multiple modules are calling extendZodWithOpenApi(z)
redundantly; keep a single global initialization in zod-extend.ts (which is
imported by packages/open-api/index.ts) and remove the duplicate
extendZodWithOpenApi(z) calls from the listed lib files (feeds.ts, types.ts,
lists.ts, highlights.ts, users.ts, errors.ts, tags.ts, bookmarks.ts, backups.ts,
admin.ts, assets.ts); ensure the central zod-extend.ts retains the
extendZodWithOpenApi(z) call and that index.ts imports it so the global
extension is applied once before any zod schema files load.

In `@packages/shared/inference.ts`:
- Around line 284-288: The comment about "zod-to-json-schema" and a local type
suppression is stale and misleading for the structured property; update or
remove it so it reflects the current implementation where structured is set via
z.toJSONSchema(optsWithDefaults.schema) without any local suppression. Locate
the structured assignment that references optsWithDefaults.schema and
z.toJSONSchema and replace the old note with a short accurate comment (or remove
it) describing that we call Zod's toJSONSchema at runtime and no longer rely on
zod-to-json-schema/type suppression.

---

Outside diff comments:
In `@packages/open-api/karakeep-openapi-spec.json`:
- Around line 680-695: The OpenAPI schema for Feed is emitting lastFetchedAt as
a plain nullable string; update the source schema/generator so the Feed property
lastFetchedAt includes "format": "date-time" (i.e., change the Feed schema
definition that declares lastFetchedAt to add format: "date-time"), then re-run
the OpenAPI generation step so the produced karakeep-openapi-spec.json contains
the date-time format for lastFetchedAt instead of leaving it as an unstructured
string; do not manually edit the generated JSON output.

---

Nitpick comments:
In `@packages/open-api/index.ts`:
- Line 8: Multiple feature modules in packages/open-api/lib/* (e.g., users,
tags, types, lists, highlights, feeds, errors, backups, assets, admin,
bookmarks) are each calling extendZodWithOpenApi(z) redundantly while the
centralized import "./zod-extend" in packages/open-api/index.ts already applies
that extension once; remove the per-module calls to extendZodWithOpenApi(z) from
each of those feature files and rely solely on the single centralized
initialization in index.ts so the Zod extension is applied exactly once.

In `@packages/open-api/karakeep-openapi-spec.json`:
- Around line 3880-3882: The GET /assets/{assetId} response is missing a binary
response schema; update the OpenAPI path item for the operationId or path "GET
/assets/{assetId}" to add a 200 response content entry such as
"application/octet-stream" (or the appropriate media type) with schema { "type":
"string", "format": "binary" } so the endpoint is documented as returning binary
data; apply the same fix to the other occurrence noted (around lines 4677-4681)
to keep upload/download endpoints symmetric.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c7fcf4ea-84e5-4992-9618-0c5083a7f669

📥 Commits

Reviewing files that changed from the base of the PR and between 700359e and c84ea35.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (53)
  • apps/browser-extension/package.json
  • apps/mcp/package.json
  • apps/mcp/src/bookmarks.ts
  • apps/mcp/src/tags.ts
  • apps/mobile/package.json
  • apps/web/components/dashboard/bookmarks/BulkManageListsModal.tsx
  • apps/web/components/dashboard/bookmarks/ManageListsModal.tsx
  • apps/web/components/signin/ResetPasswordForm.tsx
  • apps/web/package.json
  • apps/workers/metascraper-plugins/metascraper-reddit.ts
  • apps/workers/package.json
  • apps/workers/workers/crawlerWorker.ts
  • apps/workers/workers/utils/parseHtmlSubprocessIpc.ts
  • packages/api/package.json
  • packages/api/routes/bookmarks.ts
  • packages/api/routes/lists.ts
  • packages/api/routes/tags.ts
  • packages/api/utils/pagination.ts
  • packages/api/utils/types.ts
  • packages/benchmarks/package.json
  • packages/e2e_tests/package.json
  • packages/open-api/index.ts
  • packages/open-api/karakeep-openapi-spec.json
  • packages/open-api/lib/admin.ts
  • packages/open-api/lib/assets.ts
  • packages/open-api/lib/backups.ts
  • packages/open-api/lib/bookmarks.ts
  • packages/open-api/lib/errors.ts
  • packages/open-api/lib/feeds.ts
  • packages/open-api/lib/highlights.ts
  • packages/open-api/lib/lists.ts
  • packages/open-api/lib/tags.ts
  • packages/open-api/lib/types.ts
  • packages/open-api/lib/users.ts
  • packages/open-api/package.json
  • packages/open-api/zod-extend.ts
  • packages/plugins/package.json
  • packages/plugins/queue-restate/src/env.ts
  • packages/plugins/queue-restate/src/types.ts
  • packages/shared/config.ts
  • packages/shared/inference.ts
  • packages/shared/package.json
  • packages/shared/types/admin.ts
  • packages/shared/types/bookmarks.ts
  • packages/shared/types/highlights.ts
  • packages/shared/types/rules.ts
  • packages/trpc/index.ts
  • packages/trpc/package.json
  • packages/trpc/routers/publicBookmarks.ts
  • packages/trpc/routers/subscriptions.ts
  • packages/trpc/routers/users.ts
  • tools/compare-models/package.json
  • tools/compare-models/src/config.ts

Comment on lines +1 to +4
import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
import * as z from "zod";

extendZodWithOpenApi(z);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n --type ts 'extendZodWithOpenApi\s*\(' packages/open-api

Repository: karakeep-app/karakeep

Length of output: 782


Consolidate extendZodWithOpenApi(z) initialization to avoid redundant global extension calls.

packages/open-api/zod-extend.ts now initializes this at module load via side effect import in packages/open-api/index.ts, but 11 files in packages/open-api/lib/ still re-initialize it locally: feeds.ts, types.ts, lists.ts, highlights.ts, users.ts, errors.ts, tags.ts, bookmarks.ts, backups.ts, admin.ts, and assets.ts. Either remove the calls from all lib files (if the centralized initialization in zod-extend.ts is guaranteed to load first), or consolidate to a single location and remove zod-extend.ts.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/open-api/zod-extend.ts` around lines 1 - 4, Multiple modules are
calling extendZodWithOpenApi(z) redundantly; keep a single global initialization
in zod-extend.ts (which is imported by packages/open-api/index.ts) and remove
the duplicate extendZodWithOpenApi(z) calls from the listed lib files (feeds.ts,
types.ts, lists.ts, highlights.ts, users.ts, errors.ts, tags.ts, bookmarks.ts,
backups.ts, admin.ts, assets.ts); ensure the central zod-extend.ts retains the
extendZodWithOpenApi(z) call and that index.ts imports it so the global
extension is applied once before any zod schema files load.

Comment thread packages/shared/inference.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
packages/shared/inference.ts (1)

284-287: ⚠️ Potential issue | 🟡 Minor

Update the stale migration comment near structured schema conversion.

Line 284 and Line 285 still reference zod-to-json-schema and a type suppression that no longer exists in this block, while Line 287 already uses z.toJSONSchema(...).

📝 Proposed fix
-          // zod-to-json-schema still exposes Zod 3-era types even though it supports Zod 4.
-          // The runtime call is valid; this suppression is for the stale declaration shape only.
+          // Use Zod 4's native JSON Schema emitter for Ollama structured output.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/shared/inference.ts` around lines 284 - 287, The comment above the
structured schema conversion is outdated; remove or update the stale migration
note referencing "zod-to-json-schema" and the nonexistent type suppression so it
accurately reflects current code: keep the runtime conversion using
z.toJSONSchema(optsWithDefaults.schema) in the structured assignment and replace
the old migration text with a short explanation that Zod schemas are converted
at runtime via z.toJSONSchema in the structured field (referencing structured
and optsWithDefaults.schema and z.toJSONSchema).
🧹 Nitpick comments (1)
packages/shared/config.ts (1)

5-10: Narrow stringBool’s default type.

defaultValue: string lets invalid prefaults type-check and only fail at startup. Typing this as "true" | "false" keeps the helper aligned with its own refine.

♻️ Proposed fix
-const stringBool = (defaultValue: string) =>
+const stringBool = (defaultValue: "true" | "false") =>

As per coding guidelines, "Use TypeScript for type safety in the Karakeep project".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/shared/config.ts` around lines 5 - 10, The helper stringBool
currently accepts defaultValue: string which permits invalid defaults to
type-check; change its parameter type to defaultValue: "true" | "false" to
enforce valid defaults at compile time, update the stringBool function signature
accordingly, and fix any callers of stringBool to pass only the literal "true"
or "false" so the z.prefault/default value aligns with the subsequent
refine/transform logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/open-api/karakeep-openapi-spec.json`:
- Around line 917-921: The OpenAPI property "createdAt" is marked "nullable:
true" but the server Zod schema in packages/shared/types/bookmarks.ts uses
z.coerce.date().optional() (lines around 163 and 230), so clients generated from
the spec may send null which the server rejects; fix by either removing
"nullable: true" from the OpenAPI spec for "createdAt" (also at the other
occurrence) so spec matches the server, or update the Zod schema to accept nulls
(e.g., change z.coerce.date().optional() to a null-accepting form such as
z.coerce.date().nullish()/z.coerce.date().nullable().optional() or equivalent)
so the server accepts null payloads.

In `@packages/shared/types/admin.ts`:
- Line 9: The zAdminCreateUserSchema is created by extending zSignUpSchema which
has a refinement, and using zSignUpSchema.extend causes Zod 4 to throw; change
the construction of zAdminCreateUserSchema to use zSignUpSchema.safeExtend(...)
instead of .extend(...) so the schema with the .refine on zSignUpSchema is
extended safely (update the expression that currently references
zAdminCreateUserSchema to call safeExtend with zRoleSchema.shape).

---

Duplicate comments:
In `@packages/shared/inference.ts`:
- Around line 284-287: The comment above the structured schema conversion is
outdated; remove or update the stale migration note referencing
"zod-to-json-schema" and the nonexistent type suppression so it accurately
reflects current code: keep the runtime conversion using
z.toJSONSchema(optsWithDefaults.schema) in the structured assignment and replace
the old migration text with a short explanation that Zod schemas are converted
at runtime via z.toJSONSchema in the structured field (referencing structured
and optsWithDefaults.schema and z.toJSONSchema).

---

Nitpick comments:
In `@packages/shared/config.ts`:
- Around line 5-10: The helper stringBool currently accepts defaultValue: string
which permits invalid defaults to type-check; change its parameter type to
defaultValue: "true" | "false" to enforce valid defaults at compile time, update
the stringBool function signature accordingly, and fix any callers of stringBool
to pass only the literal "true" or "false" so the z.prefault/default value
aligns with the subsequent refine/transform logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 477db34b-2b57-4541-9234-88c3374a9923

📥 Commits

Reviewing files that changed from the base of the PR and between 700359e and c84ea35.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (53)
  • apps/browser-extension/package.json
  • apps/mcp/package.json
  • apps/mcp/src/bookmarks.ts
  • apps/mcp/src/tags.ts
  • apps/mobile/package.json
  • apps/web/components/dashboard/bookmarks/BulkManageListsModal.tsx
  • apps/web/components/dashboard/bookmarks/ManageListsModal.tsx
  • apps/web/components/signin/ResetPasswordForm.tsx
  • apps/web/package.json
  • apps/workers/metascraper-plugins/metascraper-reddit.ts
  • apps/workers/package.json
  • apps/workers/workers/crawlerWorker.ts
  • apps/workers/workers/utils/parseHtmlSubprocessIpc.ts
  • packages/api/package.json
  • packages/api/routes/bookmarks.ts
  • packages/api/routes/lists.ts
  • packages/api/routes/tags.ts
  • packages/api/utils/pagination.ts
  • packages/api/utils/types.ts
  • packages/benchmarks/package.json
  • packages/e2e_tests/package.json
  • packages/open-api/index.ts
  • packages/open-api/karakeep-openapi-spec.json
  • packages/open-api/lib/admin.ts
  • packages/open-api/lib/assets.ts
  • packages/open-api/lib/backups.ts
  • packages/open-api/lib/bookmarks.ts
  • packages/open-api/lib/errors.ts
  • packages/open-api/lib/feeds.ts
  • packages/open-api/lib/highlights.ts
  • packages/open-api/lib/lists.ts
  • packages/open-api/lib/tags.ts
  • packages/open-api/lib/types.ts
  • packages/open-api/lib/users.ts
  • packages/open-api/package.json
  • packages/open-api/zod-extend.ts
  • packages/plugins/package.json
  • packages/plugins/queue-restate/src/env.ts
  • packages/plugins/queue-restate/src/types.ts
  • packages/shared/config.ts
  • packages/shared/inference.ts
  • packages/shared/package.json
  • packages/shared/types/admin.ts
  • packages/shared/types/bookmarks.ts
  • packages/shared/types/highlights.ts
  • packages/shared/types/rules.ts
  • packages/trpc/index.ts
  • packages/trpc/package.json
  • packages/trpc/routers/publicBookmarks.ts
  • packages/trpc/routers/subscriptions.ts
  • packages/trpc/routers/users.ts
  • tools/compare-models/package.json
  • tools/compare-models/src/config.ts

Comment thread packages/open-api/karakeep-openapi-spec.json
Comment thread packages/shared/types/admin.ts Outdated
Use safeExtend where refined signup schemas are extended so the affected modules can initialize safely under Zod 4. Keep the generated OpenAPI output aligned with runtime validation and centralize the zod-to-openapi initialization.
@MohamedBassem

Copy link
Copy Markdown
Member Author

@greptile review

@MohamedBassem

Copy link
Copy Markdown
Member Author

@codex review

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
packages/open-api/karakeep-openapi-spec.json (1)

104-108: Consider documenting a hard page-size cap on paginated arrays.

If the backend enforces an upper bound for limit, adding maxItems to response arrays will keep generated clients and validators aligned with runtime behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/open-api/karakeep-openapi-spec.json` around lines 104 - 108, The
"bookmarks" response array (items -> $ref: "#/components/schemas/Bookmark")
lacks a maxItems constraint; add a "maxItems": <backend-enforced-cap> to this
schema so generated clients/validators match the runtime upper bound, and apply
the same change to any other paginated array schemas if the backend enforces
limits—consult the backend/service owner for the exact cap to use before adding
it.
packages/open-api/lib/bookmarks.ts (1)

1-2: The module's .openapi() calls are already protected by correct bootstrap order in the package entrypoint.

Line 30 executes .openapi() at module scope. The concern about missing the Zod patch is mitigated: packages/open-api/index.ts imports "./zod-extend" on line 8, then imports this module on line 13, ensuring the patch runs first. All direct imports of this module come through that entrypoint (none from external packages or unguarded paths).

That said, adding an explicit import "../zod-extend"; at the top of this file would make the dependency visible and prevent issues if the module is ever refactored to be imported elsewhere. It's not required, but would be a reasonable defensive measure.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/open-api/lib/bookmarks.ts` around lines 1 - 2, This module calls
.openapi() at module scope and should explicitly import the Zod patch to make
the dependency visible; add an import for "../zod-extend" at the top of this
file so the Zod extensions are applied before the module-scope .openapi()
invocation (the symbols to look for are the module-scope .openapi() calls and
the OpenAPIRegistry usage to locate where to insert the import).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/open-api/karakeep-openapi-spec.json`:
- Around line 104-108: The "bookmarks" response array (items -> $ref:
"#/components/schemas/Bookmark") lacks a maxItems constraint; add a "maxItems":
<backend-enforced-cap> to this schema so generated clients/validators match the
runtime upper bound, and apply the same change to any other paginated array
schemas if the backend enforces limits—consult the backend/service owner for the
exact cap to use before adding it.

In `@packages/open-api/lib/bookmarks.ts`:
- Around line 1-2: This module calls .openapi() at module scope and should
explicitly import the Zod patch to make the dependency visible; add an import
for "../zod-extend" at the top of this file so the Zod extensions are applied
before the module-scope .openapi() invocation (the symbols to look for are the
module-scope .openapi() calls and the OpenAPIRegistry usage to locate where to
insert the import).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b9adaaf8-3f73-4707-839b-3216c46e844d

📥 Commits

Reviewing files that changed from the base of the PR and between c84ea35 and 035b436.

📒 Files selected for processing (16)
  • packages/open-api/karakeep-openapi-spec.json
  • packages/open-api/lib/admin.ts
  • packages/open-api/lib/assets.ts
  • packages/open-api/lib/backups.ts
  • packages/open-api/lib/bookmarks.ts
  • packages/open-api/lib/errors.ts
  • packages/open-api/lib/feeds.ts
  • packages/open-api/lib/highlights.ts
  • packages/open-api/lib/lists.ts
  • packages/open-api/lib/tags.ts
  • packages/open-api/lib/types.ts
  • packages/open-api/lib/users.ts
  • packages/shared/inference.ts
  • packages/shared/types/admin.ts
  • packages/shared/types/bookmarks.ts
  • packages/trpc/routers/users.ts
🚧 Files skipped from review as they are similar to previous changes (12)
  • packages/trpc/routers/users.ts
  • packages/open-api/lib/errors.ts
  • packages/open-api/lib/lists.ts
  • packages/open-api/lib/backups.ts
  • packages/open-api/lib/feeds.ts
  • packages/open-api/lib/assets.ts
  • packages/shared/types/admin.ts
  • packages/open-api/lib/users.ts
  • packages/open-api/lib/tags.ts
  • packages/open-api/lib/types.ts
  • packages/shared/inference.ts
  • packages/shared/types/bookmarks.ts

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@MohamedBassem
MohamedBassem merged commit fa24e42 into karakeep-app:main Apr 14, 2026
7 checks passed
johnford2002 added a commit to johnford2002/karakeep that referenced this pull request May 19, 2026
* fix: add support for instapaper archived status and folders

* fix(mobile): truncate bookmark title with tailwind shrink (karakeep-app#2699)

* fix(mobile): remove react-native-keyboard-controller patch (karakeep-app#2697)

The upstream fix for the status bar inset issue was merged and released
in v1.21.5, so our local patch is no longer needed.

Closes karakeep-app#2581

* docs(mobile): add SENTRY_DISABLE_AUTO_UPLOAD=true to build apk without sentry dsn (karakeep-app#2700)

* docs(mobile): add SENTRY_DISABLE_AUTO_UPLOAD=true to build apk without sentry dsn

* include pr suggestion

* put SENTRY_DISABLE_AUTO_UPLOAD=true in package.json scripts instead

* deps: upgrade to zod 4 (karakeep-app#2689)

* deps: upgrade to zod 4

* fix: preserve zod 4 schema compatibility

Use safeExtend where refined signup schemas are extended so the affected modules can initialize safely under Zod 4. Keep the generated OpenAPI output aligned with runtime validation and centralize the zod-to-openapi initialization.

* chore: freshen up docker-compose.yml for local dev purposes (karakeep-app#2149)

* feat(mobile): add keep screen on toggle for bookmark viewing (karakeep-app#2691)

* feat(mobile): add keep screen on toggle for bookmark viewing

Adds a Reading-section toggle that activates expo-keep-awake while a
bookmark is open, covering all in-app views (reader, browser, screenshot,
archive, pdf, text, asset). Defaults off; releases the wake lock as soon
as the user navigates away from the bookmark detail screen.

Closes karakeep-app#2650

* fix(mobile): disable keep-screen-on toggle while settings are hydrating

Prevents writing stale default settings to SecureStore if the user
toggles before hydration completes.

* fix: add clawhub metadata to karakeep skill

* fix: fix clawhub skill yaml

* docs: add skills to docs

* docs: Update worker list in the environment variable reference (karakeep-app#2710)

* deps: upgrade oxlint and oxfmt

* fix: add missing source to createBookmarks from import

* feat: Add Readwise Reader import (karakeep-app#2690)

Co-authored-by: Luis Zuniga <wichogzf@gmail.com>

* fix: deduplicate crawl jobs using idempotency key (karakeep-app#2678)

* feat: add OneTab import support (karakeep-app#2495)

* feat(import): add OneTab import support

Adds a OneTab import card to the ImportExport settings page.
Implements a parser for OneTab's "URL | Title" line format with
deduplication, and registers "onetab" as an import source.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: add tests for the inference worker

* fix: add ability to manually finalize/delete staged import sessions (karakeep-app#2713)

* fix(docker): add `ffmpeg` as required by `yt-dlp` (karakeep-app#2721)

* fix: invalidate existing password reset tokens before issuing a new one (karakeep-app#2722)

- Wrap reset token replacement in a transaction
- Delete any prior tokens for the user before inserting the new token

* fix: add ratelimiting for the changePassword endpoint (karakeep-app#2723)

* fix: sanitize user names before storing them (karakeep-app#2724)

* Sanitize user names before storing them

- Strip HTML from signup and OAuth display names
- Enforce non-empty plain-text names in validation
- Add tests for sanitized and HTML-only name inputs

* more changes

* Harden user name validation

* more changes

* fix: add data: to style-src and font-src CSP directives in serveAsset (karakeep-app#2657)

Monolith-generated full-page archives inline stylesheets and fonts as
data: URIs. The previous CSP blocked these, causing broken rendering.
Adding data: to style-src and a new font-src directive unblocks them,
matching the precedent set by img-src which already includes data:.

Fixes: karakeep-app#2621

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: fix pnpm symlink conflict (karakeep-app#2725)

* fix: include userId in ratelimit key if the user is authed (karakeep-app#2726)

* feat(mcp): add update-bookmark tool (karakeep-app#2717)

Wraps PATCH /bookmarks/{bookmarkId} so MCP clients can modify an
existing bookmark instead of only creating/reading one. Exposes the
commonly useful patchable fields (title, note, summary, archived,
favourited, url, description, author, publisher, createdAt); less
common/destructive ones (text, assetContent, datePublished,
dateModified) are intentionally omitted to reduce accidental clobbering
of crawler-populated data.

The PATCH response schema does not include tags/content/assets, so the
tool follows up with a GET to return a full compactBookmark view of the
updated record.

* fix: use relative path for logout redirect to avoid port mismatch (karakeep-app#2655)

signOut({ redirect: false }) returns an absolute URL constructed from
NEXTAUTH_URL (e.g. http://localhost:3000/). When Docker remaps an
external port to the internal one, this sends users to the wrong port.
Using router.push('/') lets Next.js resolve against the user's origin.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(web): auto-add bookmark to list on selection (karakeep-app#2629)

* feat(web): auto-add bookmark to list on selection

Remove the 'Add' button from Manage Lists modal. When a user selects
a list from the dropdown, the bookmark is now automatically added to
that list, simplifying the user experience.

Users can still remove a bookmark from a list by clicking the X button
next to the list name.

Fixes karakeep-app#2604

* fix: add loading state and prevent duplicate submission in ManageListsModal

- Added isPending state from useAddBookmarkToList hook
- Added duplicate submission prevention in onChange handler
- Added disabled prop to BookmarkListSelector component

* style: fix indentation in useAddBookmarkToList callback

- Fix incorrect indentation of onSuccess and onError callbacks
- Add trailing newline at end of file

* fix: address review comments for PR karakeep-app#2629

- Add trailing newline at end of file
- Remove redundant in-flight guard check (disabled prop handles it)
- Add guard for getPathById to prevent crash on stale cache
- Disable interactions while list membership data is loading

* fix: use translation for DialogTitle in ManageListsModal

* fix: add isLoading guard to onChange callback in BookmarkListSelector

* fix(web): improve ManageListsModal accessibility and rendering

- Add aria-label to remove button for accessibility (CodeRabbit comment 3006737430)
- Remove allLists gating to allow list.name fallback to render (CodeRabbit comment 3006737429)
- Use optional chaining for path lookup

* feat(extension): add manual save toggle for browser extension (karakeep-app#2693)

* feat: add manual save toggle for browser extension

* fix: add check for appending ellipses

* chore: add PR template inspired from immich

* chore: fix PR template path

* feat: Add native single-file support to browser extension (karakeep-app#2197)

* feat: Add native SingleFile support to browser extension

This commit integrates single-file-core into the browser
extension, allowing users to capture complete web page archives when
saving bookmarks.

* fix: truncate urls in crawler logs

* feat: change the default new list emoji to folder

* feat: add support for configuring OpenAI reasoning effort (karakeep-app#2718)

* Add support for configuring OpenAI reasoning effort

* Update docs' formatting

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* minor fixes

* minor fixes

---------

Co-authored-by: Luis Zuniga <wichogzf@gmail.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix: skip url getContentType fetch if precrawledArchiveAssetId exists (karakeep-app#2580)

* deps: upgrade astro to 6.1.6. fixes karakeep-app#2728

* fix: fix broken cache invalidation requiring refresh after adding a bookmark to a tag or a list

* feat: add granular API key scopes (karakeep-app#2731)

* fix: fix promise race leaks in the worker (karakeep-app#2737)

* fix: change the archive button in the manage lists dialog to be a secondary button

* chore: add portless pnpm scripts

* feat(mobile): customizable reader toolbar (karakeep-app#2527)

* feat(mobile): add auto-hiding reader toolbar with customizable actions and overflow menu

Redesign the bookmark reader bottom bar with configurable toolbar actions,
an overflow ellipsis menu using native MenuView, and auto-hide on scroll.
Add toolbar settings page for reordering visible/overflow actions. Support
iOS 26 glass pill styling and transparent headers with edge-to-edge content.

* Update apps/mobile/components/bookmarks/BottomActions.tsx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix comment

* fix(mobile): address PR review findings for reader toolbar

- Fix silent fall-through when sharing non-image/non-pdf assets
- Pass contentInsetTop to browser/archive WebViews for iOS 26
- Auto-inject missing action IDs into overflow on settings load
- Extract NAV_BAR_HEIGHT constant to replace magic number 44
- Remove unused ownerOnly/linkOnly from ToolbarActionMeta
- Extract triggerHaptic() helper to handle async catch
- Deduplicate image/PDF share logic with finally cleanup
- Add console.warn for unknown menu action events
- Simplify headerPlatformOptions to plain isIOS26 ternary
- Fix inaccurate comment about top/bottom padding

* remove margin around text view

* fix bottom inset

* fix reading progress bar regression, add small gap

* Add redesigned reading progress pill, blur effect for iOS 18 and lower

* small review fixes

* fix icon colors in dark mode

* Add missing @/lib/ios module for isIOS26 platform detection

* downgrade expo-blur to match expo

* Update to stable expo-glass-effect version

* fix(mobile): close reader view after archiving bookmark

* chore(mobile): remove auto-hiding header/footer and pill redesign from toolbar PR

Split these concerns out so this branch lands only the customizable toolbar.
Auto-hiding native header, auto-hiding bottom toolbar, and the redesigned
continue-reading pill will return together in a follow-up reader-chrome PR,
where they belong architecturally as a single feature.

* fix footer padding on iOS 26

* unify handling of archive and fav in bottom actions

* abstract share logic

* dynamic labels depending on bookmark state

* fix overflow submenu in android

* review comments

* feat: add metrics for sub status

* chore: add infra for the event logger

* feat: add exhaustive event logging

* fix: fix nodejs bundling for the eventLogger

* fix: upgrade zod resolver to fix form validations (karakeep-app#2744)

* fix: bump up zod resolver

* fix: follow greptile review

* fix: reject user deletion with active subscription

* chore: add event logging for bookmark summarization

* fix: fix subscription tranisiton event logging

* feat(mobile): revamp search and navigation chrome (karakeep-app#2685)

* FAB styling looks good, still working on search

* Finish pr

* use latest expo-glass-effect version

* feat(mobile): auto-focus search bar when iOS search tab gains focus

* fix(mobile): address PR review feedback

- Fix search history: save new terms on blur via useEffect in
  BookmarkSearchResults and index.ios.tsx onBlur handler
- Fix keepPreviousData: use isPending instead of isFetching for spinner
  guards so stale results stay visible during refetches
- Add enabled guard on search queries to avoid firing on empty input
- Increase debounce from 10ms to 300ms on iOS search tab
- Add accessibility labels/roles to close buttons in search and
  InlineSearch
- Increase ProfileAvatarButton tap target to 44px minimum, add a11y
  props, convert static styles to Tailwind
- Fix tags empty state to show "No tags match your search" when
  filtering
- Fix stale comment in Android search stub

* feat(mobile): prevent iOS tab bar from shrinking and resize FAB

Disable tab bar minimize behavior on iOS so it stays at full height
when scrolling. Enlarge the FAB to better match the native search
button size and adjust its positioning.

* fix broken ProfileAvatarButton on iOS due to bad CodeRabbit suggestion

* fix(mobile): lock FAB glass effect to app color scheme

Pin GlassView and BlurView to the app's current color scheme instead
of adapting to background content, preventing the FAB from switching
to light appearance over white content in dark mode.

* fix(mobile): add tintColor to FAB glass effect for consistent appearance

Use GlassView tintColor to prevent the FAB from adapting to background
content, keeping it dark in dark mode and light in light mode regardless
of what's behind it.

* fix(mobile): use PlatformColor for FAB icon vibrancy on iOS

Replace hardcoded foreground color with PlatformColor("label") so the
FAB icon participates in UIKit vibrancy and stays legible against the
adaptive liquid glass effect. Revert FAB GlassView to its clean default
with no colorScheme or tintColor overrides.

* fix(mobile): trim search term in onBlur before persisting

The onBlur handler checked trim().length but passed the untrimmed value
to addTerm(), allowing whitespace-padded terms into search history.

* revert bad change in lists

* revert tag spacing change

Co-authored-by: Copilot <copilot@github.com>

* remove unused search component

* fix broken view switching in ios search results

* drop the search screen stack from root layout

* remove global search

* search on type

* remove duplicate expo-blur from package.json

* restore header for ios

---------

Co-authored-by: Copilot <copilot@github.com>

* fix: debounce bulk query invalidations (karakeep-app#2745)

* Debounce bulk query invalidations

* Avoid scheduling predicate invalidations

* Document predicate invalidation bypass

* feat: Allow multiple lists for Rule Engine karakeep-app#2490 (karakeep-app#2620)

* feat: Allow multiple lists for Rule Engine karakeep-app#2490

feat: Allow multiple lists for Rule Engine karakeep-app#2490

feat: Allow multiple lists for Rule Engine karakeep-app#2490

* re-add db migration

* codex review comments

* chore: use .gitignore as source of truth for oxfmt and oxlint (karakeep-app#2740)

* chore: use .gitignore as source of truth for oxfmt and oxlint

Wire `--ignore-path=../../.gitignore` into every per-package format/lint
script and prune the duplicated `ignorePatterns` arrays from each
oxfmt/oxlint config. For oxfmt in packages with a local `.gitignore`
(mobile, browser-extension, cli, mcp, sdk, benchmarks, e2e_tests), pass
the local file as a second `--ignore-path` too. oxlint only accepts one
`--ignore-path`, so mobile retains `ios/`, `android/` etc. in its
`ignorePatterns`.

Observed issue: files in the `.astro/` directory in apps/landing were
getting flagged by the format check in the precommit hook, because
oxfmt running from the package cwd does not auto-discover the root
`.gitignore` (it only reads a `.gitignore` in cwd). The explicit
`--ignore-path` makes the gitignore policy consistent regardless of the
invocation directory.

* tests: add a test for browser rendering

* fix(ui): resolve text clipping in bookmark cards (karakeep-app#2748)

* fix: prevent text overflow

* Update apps/web/components/dashboard/bookmarks/NotePreview.tsx

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* refactor: correct formatting

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* feat: redesign list and tag page headers (karakeep-app#2749)

* feat: redesign list and tag page headers

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(landing): add safari links to landing

* deps: upgrade protobufjs

* fix: fix allow the adblocker to run after request interception

* feat: Add agentic skills links to the landing page (karakeep-app#2761)

* fix: fix SSRF handling with redirects in crawler worker (karakeep-app#2759)

* feat(mobile): open links and images in in-app browser (karakeep-app#2696)

* feat(mobile): open links and images in in-app browser instead of navigating away

Links and media clicked within bookmark reader, browser, and archive
views now open in an in-app browser overlay (expo-web-browser) or
fullscreen image viewer instead of navigating the current view away
from the bookmark content.

* fix(mobile): address PR review comments for link handling

Use origin-based comparison instead of one-shot hasLoaded flag in
BookmarkLinkBrowserPreview to correctly handle HTTP redirects on iOS.
Remove dead href.startsWith("#") branch in BookmarkHtmlHighlighterDom.
Combine duplicate imports from react-native-webview/lib/WebViewTypes.

* fix: format BookmarkLinkPreview import

* fix: extract bookmarkUrl to preserve type narrowing in useCallback

* fix(mobile): allow text selection on links for highlighting

Strip href from links and store in data-href so the browser treats them
as regular selectable text instead of activating native link gestures
(iOS preview sheet, Android drag-and-drop). A MutationObserver re-strips
hrefs after DOM changes from highlight effects.

* feat: Add CLI auth config support (karakeep-app#2751)

* fix: typo in docker docs (karakeep-app#2762)

* fix: fix timezone handling in date displays across the app (karakeep-app#2682)

* fix: use safe favicon resolution in metascraper (karakeep-app#2763)

* fix: use correct env variable syntax for perplexity (karakeep-app#2765)

* fix: use correct env variable syntax

* fix: env syntax for perplexity base url

* fix: harden video worker redirect validation against SSRF (karakeep-app#2760)

* refactor: refactor bulk actions to store bookmark ids (karakeep-app#2767)

* fix(mobile): fix scrolling in manage tags page in ios

* docs: add a comment to security docs about SSRF mitigations

* feat: add keyboard shortcuts (karakeep-app#2750)

* i18n: Sync weblate translations

Co-authored-by: CircleCrop <guhengting@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: OpenAI <noreply-mt-openai@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/ar/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/cs/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/da/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/de/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/el/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/en_US/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/es/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/fa/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/fi/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/fr/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/ga/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/gl/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/hr/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/hu/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/it/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/ja/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/ko/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/nl/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/pl/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/pt/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/ru/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/sk/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/sl/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/sv/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/tr/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/uk/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/vi/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/karakeep/karakeep/zh_Hant/
Translation: Karakeep/Karakeep

* i18n: fix automated i18n strings

* feat(web): add more details to the all lists page

* fix(crawler): proper handling for proxy auth with CDP interceptions

* release(docs): release the 0.32 docs

* release: release cli, sdk, mobile and extension

* release(cli): release CLI 0.32.0

* docs: minor docs update to mention the skills and the new singlefile integration

* feat(browser-extension): request host permission at runtime for client side crawling (karakeep-app#2784)

Move `<all_urls>` from `host_permissions` to `optional_host_permissions` so it
isn't requested at install time. The permission is now requested via
`chrome.permissions.request()` when the user enables client-side crawling in the
extension settings, and removed when they disable it. The settings toggle and
the capture path both gate on whether the permission is actually granted.

https://claude.ai/code/session_01NnQnr1B9KSRriTaQkZM6AP

Co-authored-by: Claude <noreply@anthropic.com>

* release(extension): Release version 1.2.11

* docs: update screenshots in the docs

* fix(mobile): fix insets on new iOS settings formSheet modal (karakeep-app#2793)

The settings screen is presented as an iOS formSheet with an opaque,
non-large header (since 649efb8). iOS sizes the formSheet's inner
content view ~headerHeight pixels taller than the visible sheet area,
so the bottom of the settings list was clipped behind the sheet's
bottom edge.

contentInsetAdjustmentBehavior="automatic" correctly handles the top
(adding the right top inset to clear the opaque header), but does not
add a corresponding bottom inset for the formSheet's invisible bottom
region. Adding useHeaderHeight() to paddingBottom pushes the trailing
content up to a visible position.

* fix(mobile): stack Upload Image Quality slider on two lines (karakeep-app#2794)

The MEDIA → Upload Image Quality row in mobile settings crammed the
label, percentage, and slider onto one horizontal line, leaving the
slider only a few dozen pixels wide on phone widths. Switch the row
to a vertical layout: label + percentage on top, full-width slider
beneath.

* feat(mobile): tablet/iPad UI polish for toolbar and FAB (karakeep-app#2788)

* Set max width for reader toolbar on tablets

* fix(mobile): remove FAB bottom inset on iPad

iPad's tab bar geometry doesn't need the extra 57–60pt lift the iPhone FAB uses.

* fix(mobile): give reader toolbar icons proper spacing on iPad

Replace the inner row's width/maxWidth + space-between approach with an
intrinsic gap. The earlier cap caused the row to collapse to its natural
icon width inside the hug-fit GlassView, leaving icons cramped against
each other. Using `gap: 28` gives each icon real breathing room and lets
the pill size to its contents naturally.

* fix(mobile): fix formsheet padding in manage_lists, manage_tags and info sheets

* fix(mobile): fix color of list separator in recent searches list

* fix(mobile): improve perf of manage_tags page

* release(mobile): release mobile v1.9.4

* fix: serve unoptimized nextjs images

This is a temporary fix to be able to patch the recent NextJS security
update until a proper fix is available.

* deps: upgrade nextjs to 15.5.18

* docs: update SECURITY.md

* feat(db): add PostgreSQL migration for upstream schema sync

Adds PG equivalent of the SQLite migrations 0083 (apiKey scopes) and
0084 (rule engine multi-list refactor) brought in by the upstream merge.

The auto-generated migration was missing two things needed for parity
with the SQLite version:

1. A SQL DEFAULT for apiKey.scopes to backfill existing rows (matches
   SQLite's `DEFAULT '["fullaccess"]'`).
2. Data migration for the event JSON in ruleEngineRules: delete rules
   with empty listId in addedToList/removedFromList events, then
   transform single listId to a listIds array.

Both data-migration steps mirror what upstream's SQLite migration does
using SQLite's json_extract/json_set, translated to PG's jsonb operators.

Verified with `drizzle-kit check` (schema/migration consistent) and the
schema-sync test (column lists match across dialects).

* fix(docker): pin postgres overlay to v18 and fix volume mount

Bumps the PostgreSQL overlay from postgres:16-alpine to postgres:18-alpine
to match the production deployment, and fixes the volume mount that PG18
rejects.

PG18+ stores data in a version-specific subdirectory under
/var/lib/postgresql (e.g. /var/lib/postgresql/18/docker). The image
refuses to start if the volume is mounted at the legacy
/var/lib/postgresql/data path, even on a fresh install. Mounting one
level up lets PG18 manage its own subdirectory layout.

Also documents the migration-smoke-test command pattern in the overlay
header: pair the overlay with a base compose file and `up postgres -d`
to bring up just postgres, then run `pnpm --filter=@karakeep/db migrate`
from the host. Verified end-to-end against postgres:18-alpine (PG 18.4).

Upgrading an existing PG16 fork deployment to this overlay is a breaking
change — the volume layout is incompatible and requires pg_upgrade or
a dump/restore.

---------

Co-authored-by: Mohamed Bassem <me@mbassem.com>
Co-authored-by: xuatz <xzlow10@gmail.com>
Co-authored-by: Evan Simkowitz <esimkowitz@users.noreply.github.com>
Co-authored-by: Max <43565398+maxim-mityutko@users.noreply.github.com>
Co-authored-by: Luis G. Zúñiga <github@nuevenueves.com>
Co-authored-by: Luis Zuniga <wichogzf@gmail.com>
Co-authored-by: Nikhil Chowdhury <54472147+NikhilChowdhury27@users.noreply.github.com>
Co-authored-by: apo-mak <apo-mak@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Maxime Pinot <contact@maximepinot.com>
Co-authored-by: Om Mishra <152969928+howwohmm@users.noreply.github.com>
Co-authored-by: Vladimir Balko <147043796+vbalko-claimate@users.noreply.github.com>
Co-authored-by: 冰水泡枸杞 <87858694+xingzihai@users.noreply.github.com>
Co-authored-by: Morgan Kesler <keslerm@aegiszero.is>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: GSAlex <GSAlex@users.noreply.github.com>
Co-authored-by: Beluga <mikhailbialouwork@gmail.com>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Ahmed Abdelkarim <ahmad.abdelkarim89@gmail.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Mark :) <Mxrk@users.noreply.github.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: CircleCrop <guhengting@gmail.com>
Co-authored-by: OpenAI <noreply-mt-openai@weblate.org>
djmango referenced this pull request in djmango/karakeep Sep 19, 2026
* deps: upgrade to zod 4

* fix: preserve zod 4 schema compatibility

Use safeExtend where refined signup schemas are extended so the affected modules can initialize safely under Zod 4. Keep the generated OpenAPI output aligned with runtime validation and centralize the zod-to-openapi initialization.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant