diff --git a/apps/server/package.json b/apps/server/package.json
index 27c98c2e8..33c910f3a 100644
--- a/apps/server/package.json
+++ b/apps/server/package.json
@@ -22,10 +22,10 @@
"@ai-sdk/google": "^3.0.79",
"@ai-sdk/openai": "^3.0.65",
"@ai-sdk/openai-compatible": "^2.0.48",
- "@aws-sdk/client-s3": "^3.1053.0",
+ "@aws-sdk/client-s3": "^3.1054.0",
"@better-auth/api-key": "^1.6.11",
"@better-auth/drizzle-adapter": "^1.6.11",
- "@better-auth/infra": "^0.2.8",
+ "@better-auth/infra": "^0.2.10",
"@better-auth/oauth-provider": "^1.6.11",
"@better-auth/passkey": "^1.6.11",
"@hono/node-server": "^2.0.4",
@@ -52,7 +52,7 @@
"better-auth": "1.6.11",
"cjk-regex": "^3.4.0",
"deepmerge-ts": "^7.1.5",
- "dompurify": "^3.4.5",
+ "dompurify": "^3.4.6",
"dotenv": "^17.4.2",
"drizzle-orm": "1.0.0-rc.3",
"drizzle-zod": "1.0.0-beta.14-a36c63d",
@@ -61,12 +61,12 @@
"hono": "^4.12.23",
"jsonrepair": "^3.14.0",
"node-html-parser": "^7.1.0",
- "nodemailer": "^8.0.8",
+ "nodemailer": "^8.0.9",
"ollama-ai-provider-v2": "^3.5.1",
"pg": "^8.21.0",
"phosphor-icons-react-pdf": "^0.1.3",
"react": "^19.2.6",
- "react-email": "^6.3.3",
+ "react-email": "^6.4.0",
"react-pdf-html": "^2.1.5",
"resumable-stream": "^2.2.12",
"sharp": "^0.34.5",
@@ -80,8 +80,8 @@
"@types/node": "^25.9.1",
"@types/pg": "^8.20.0",
"@types/react": "^19.2.15",
- "@typescript/native-preview": "7.0.0-dev.20260526.1",
- "react-doctor": "^0.2.6",
+ "@typescript/native-preview": "7.0.0-dev.20260527.1",
+ "react-doctor": "^0.2.9",
"tsdown": "^0.22.0",
"tsx": "^4.22.3",
"typescript": "^6.0.3",
diff --git a/apps/web/package.json b/apps/web/package.json
index 0518e98ef..cc87035e1 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -20,7 +20,7 @@
"@ai-sdk/react": "^3.0.193",
"@base-ui/react": "^1.5.0",
"@better-auth/api-key": "^1.6.11",
- "@better-auth/infra": "^0.2.8",
+ "@better-auth/infra": "^0.2.10",
"@better-auth/oauth-provider": "^1.6.11",
"@better-auth/passkey": "^1.6.11",
"@dnd-kit/core": "^6.3.1",
@@ -44,7 +44,7 @@
"@reactive-resume/ui": "workspace:*",
"@reactive-resume/utils": "workspace:*",
"@tailwindcss/vite": "^4.3.0",
- "@tanstack/react-form": "^1.32.0",
+ "@tanstack/react-form": "^1.32.1",
"@tanstack/react-hotkeys": "^0.10.0",
"@tanstack/react-query": "^5.100.14",
"@tanstack/react-router": "^1.170.8",
@@ -96,11 +96,11 @@
"@types/pg": "^8.20.0",
"@types/react": "^19.2.15",
"@types/react-dom": "^19.2.3",
- "@typescript/native-preview": "7.0.0-dev.20260526.1",
+ "@typescript/native-preview": "7.0.0-dev.20260527.1",
"@vitejs/plugin-react": "^6.0.2",
"babel-plugin-macros": "^3.1.0",
"babel-plugin-react-compiler": "^1.0.0",
- "react-doctor": "^0.2.6",
+ "react-doctor": "^0.2.9",
"typescript": "^6.0.3",
"vite": "^8.0.14"
}
diff --git a/apps/web/src/libs/resume/section.tsx b/apps/web/src/libs/resume/section.tsx
index 9de60c0b3..92e3753e5 100644
--- a/apps/web/src/libs/resume/section.tsx
+++ b/apps/web/src/libs/resume/section.tsx
@@ -21,6 +21,7 @@ import {
LayoutIcon,
MessengerLogoIcon,
NotepadIcon,
+ PaintBrushBroadIcon,
PaletteIcon,
PhoneIcon,
ReadCvLogoIcon,
@@ -44,6 +45,7 @@ export type RightSidebarSection =
| "layout"
| "typography"
| "design"
+ | "styles"
| "page"
| "notes"
| "sharing"
@@ -78,6 +80,7 @@ export const rightSidebarSections: RightSidebarSection[] = [
"layout",
"typography",
"design",
+ "styles",
"page",
"notes",
"sharing",
@@ -116,6 +119,7 @@ export const getSectionTitle = (type: SidebarSection | CustomOnlyType): string =
.with("layout", () => t`Layout`)
.with("typography", () => t`Typography`)
.with("design", () => t`Design`)
+ .with("styles", () => t`Custom Styles`)
.with("page", () => t`Page`)
.with("notes", () => t`Notes`)
.with("sharing", () => t`Sharing`)
@@ -159,6 +163,7 @@ export const getSectionIcon = (type: SidebarSection | CustomOnlyType, props?: Ic
.with("layout", () => )
.with("typography", () => )
.with("design", () => )
+ .with("styles", () => )
.with("page", () => )
.with("notes", () => )
.with("sharing", () => )
diff --git a/apps/web/src/routes/builder/$resumeId/-sidebar/right/index.tsx b/apps/web/src/routes/builder/$resumeId/-sidebar/right/index.tsx
index e98722c6c..0957a200f 100644
--- a/apps/web/src/routes/builder/$resumeId/-sidebar/right/index.tsx
+++ b/apps/web/src/routes/builder/$resumeId/-sidebar/right/index.tsx
@@ -17,6 +17,7 @@ import { PageSectionBuilder } from "./sections/page";
import { ResumeAnalysisSectionBuilder } from "./sections/resume-analysis";
import { SharingSectionBuilder } from "./sections/sharing";
import { StatisticsSectionBuilder } from "./sections/statistics";
+import { StylesSectionBuilder } from "./sections/styles";
import { TemplateSectionBuilder } from "./sections/template";
import { TypographySectionBuilder } from "./sections/typography";
@@ -26,6 +27,7 @@ function getSectionComponent(type: RightSidebarSection) {
.with("layout", () => )
.with("typography", () => )
.with("design", () => )
+ .with("styles", () => )
.with("page", () => )
.with("notes", () => )
.with("sharing", () => )
diff --git a/apps/web/src/routes/builder/$resumeId/-sidebar/right/sections/styles.test.tsx b/apps/web/src/routes/builder/$resumeId/-sidebar/right/sections/styles.test.tsx
new file mode 100644
index 000000000..af5a01318
--- /dev/null
+++ b/apps/web/src/routes/builder/$resumeId/-sidebar/right/sections/styles.test.tsx
@@ -0,0 +1,322 @@
+// @vitest-environment happy-dom
+
+import { fireEvent, render, screen, within } from "@testing-library/react";
+import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
+import { i18n } from "@lingui/core";
+import { I18nProvider } from "@lingui/react";
+import { isValidElement } from "react";
+
+const updateResumeData = vi.hoisted(() => vi.fn());
+const styleRules = vi.hoisted(() => [
+ {
+ id: "style-global-heading",
+ label: "All sections: Section heading",
+ enabled: true,
+ target: { scope: "global" },
+ slots: { heading: { color: "rgba(220, 38, 38, 1)" } },
+ },
+]);
+
+vi.mock("../shared/section-base", () => ({
+ SectionBase: ({ children }: { children: React.ReactNode }) =>
{children}
,
+}));
+
+vi.mock("@/features/resume/builder/draft", () => ({
+ useCurrentResume: () => ({
+ data: {
+ metadata: { styleRules },
+ sections: {
+ experience: { title: "Experience" },
+ skills: { title: "Skills" },
+ },
+ customSections: [{ id: "custom-1", title: "Open Source", type: "projects" }],
+ },
+ }),
+ useUpdateResumeData: () => updateResumeData,
+}));
+
+const { StylesSectionBuilder } = await import("./styles");
+const { getSectionIcon, getSectionTitle } = await import("@/libs/resume/section");
+
+beforeAll(() => {
+ i18n.loadAndActivate({ locale: "en", messages: {} });
+});
+
+beforeEach(() => {
+ updateResumeData.mockClear();
+});
+
+const renderStyles = () =>
+ render(
+
+
+ ,
+ );
+
+describe("StylesSectionBuilder", () => {
+ beforeEach(() => {
+ styleRules.splice(0, styleRules.length);
+ styleRules.push({
+ id: "style-global-heading",
+ label: "All sections: Section heading",
+ enabled: true,
+ target: { scope: "global" },
+ slots: { heading: { color: "rgba(220, 38, 38, 1)" } },
+ });
+ });
+
+ it("renders structured style rule controls", () => {
+ renderStyles();
+
+ expect(screen.getByLabelText("Target Scope")).toBeInTheDocument();
+ expect(screen.getByLabelText("Style Slot")).toBeInTheDocument();
+ expect(screen.getByLabelText("Text Color")).toBeInTheDocument();
+ expect(screen.getByLabelText("Text Decoration Color")).toBeInTheDocument();
+ expect(screen.getByRole("heading", { name: "Color" })).toBeInTheDocument();
+ expect(screen.getByRole("heading", { name: "Text" })).toBeInTheDocument();
+ expect(screen.getByRole("heading", { name: "Spacing" })).toBeInTheDocument();
+ expect(screen.getByRole("heading", { name: "Border" })).toBeInTheDocument();
+ expect(screen.getByLabelText("Font Style")).toBeInTheDocument();
+ expect(screen.getByLabelText("Line Height")).toBeInTheDocument();
+ expect(screen.getByLabelText("Letter Spacing")).toBeInTheDocument();
+ expect(screen.getByLabelText("Text Decoration")).toBeInTheDocument();
+ expect(screen.getByLabelText("Decoration Style")).toBeInTheDocument();
+ expect(screen.getByLabelText("Text Align")).toBeInTheDocument();
+ expect(screen.getByLabelText("Text Transform")).toBeInTheDocument();
+ expect(screen.getByLabelText("Opacity")).toBeInTheDocument();
+ expect(screen.getByLabelText("Margin Top")).toBeInTheDocument();
+ expect(screen.getByLabelText("Margin Right")).toBeInTheDocument();
+ expect(screen.getByLabelText("Margin Bottom")).toBeInTheDocument();
+ expect(screen.getByLabelText("Margin Left")).toBeInTheDocument();
+ expect(screen.getByLabelText("Row Gap")).toBeInTheDocument();
+ expect(screen.getByLabelText("Column Gap")).toBeInTheDocument();
+ expect(screen.getByLabelText("Border Style")).toBeInTheDocument();
+ expect(screen.getByRole("option", { name: "Section heading" })).toBeInTheDocument();
+ expect(screen.getByRole("option", { name: "List" })).toBeInTheDocument();
+ expect(screen.getByRole("option", { name: "List item content" })).toBeInTheDocument();
+ expect(screen.queryByRole("option", { name: "Bullet or number" })).not.toBeInTheDocument();
+ });
+
+ it("labels the empty font weight option as default", () => {
+ renderStyles();
+
+ const fontWeightSelect = screen.getByLabelText("Font Weight");
+ expect(within(fontWeightSelect).getByRole("option", { name: "Default" })).toBeInTheDocument();
+ expect(within(fontWeightSelect).queryByRole("option", { name: "Template default" })).not.toBeInTheDocument();
+ });
+
+ it("renames the sidebar entry and uses a distinct icon from design", () => {
+ const designIcon = getSectionIcon("design");
+ const stylesIcon = getSectionIcon("styles");
+
+ expect(getSectionTitle("styles")).toBe("Custom Styles");
+ expect(isValidElement(designIcon)).toBe(true);
+ expect(isValidElement(stylesIcon)).toBe(true);
+ expect(isValidElement(designIcon) && isValidElement(stylesIcon) && designIcon.type !== stylesIcon.type).toBe(true);
+ });
+
+ it("upserts one style rule for the selected target and slot", () => {
+ styleRules.splice(0, styleRules.length);
+ renderStyles();
+
+ fireEvent.change(screen.getByLabelText("Text Color"), { target: { value: "rgba(220, 38, 38, 1)" } });
+
+ expect(updateResumeData).toHaveBeenCalledTimes(1);
+ const recipe = updateResumeData.mock.calls[0]?.[0] as (draft: { metadata: { styleRules: unknown[] } }) => void;
+ const draft = { metadata: { styleRules: [] } };
+ recipe(draft);
+
+ expect(draft.metadata.styleRules).toEqual([
+ {
+ id: "style-global-heading",
+ label: "All sections: Section heading",
+ enabled: true,
+ target: { scope: "global" },
+ slots: { heading: { color: "rgba(220, 38, 38, 1)" } },
+ },
+ ]);
+ });
+
+ it("stores padding as per-side values", () => {
+ styleRules.splice(0, styleRules.length);
+ renderStyles();
+
+ expect(screen.queryByLabelText("Padding")).not.toBeInTheDocument();
+ expect(screen.getByLabelText("Padding Top")).toBeInTheDocument();
+ expect(screen.getByLabelText("Padding Right")).toBeInTheDocument();
+ expect(screen.getByLabelText("Padding Bottom")).toBeInTheDocument();
+ expect(screen.getByLabelText("Padding Left")).toBeInTheDocument();
+
+ fireEvent.change(screen.getByLabelText("Padding Top"), { target: { value: "12" } });
+
+ expect(updateResumeData).toHaveBeenCalledTimes(1);
+ const recipe = updateResumeData.mock.calls[0]?.[0] as (draft: { metadata: { styleRules: unknown[] } }) => void;
+ const draft = { metadata: { styleRules: [] } };
+ recipe(draft);
+
+ expect(draft.metadata.styleRules).toEqual([
+ {
+ id: "style-global-heading",
+ label: "All sections: Section heading",
+ enabled: true,
+ target: { scope: "global" },
+ slots: { heading: { paddingTop: 12 } },
+ },
+ ]);
+ });
+
+ it("stores text decoration intent", () => {
+ styleRules.splice(0, styleRules.length);
+ renderStyles();
+
+ fireEvent.change(screen.getByLabelText("Text Decoration"), { target: { value: "underline" } });
+
+ expect(updateResumeData).toHaveBeenCalledTimes(1);
+ const recipe = updateResumeData.mock.calls[0]?.[0] as (draft: { metadata: { styleRules: unknown[] } }) => void;
+ const draft = { metadata: { styleRules: [] } };
+ recipe(draft);
+
+ expect(draft.metadata.styleRules).toEqual([
+ {
+ id: "style-global-heading",
+ label: "All sections: Section heading",
+ enabled: true,
+ target: { scope: "global" },
+ slots: { heading: { textDecoration: "underline" } },
+ },
+ ]);
+ });
+
+ it("stores margin and gap intent", () => {
+ styleRules.splice(0, styleRules.length);
+ renderStyles();
+
+ expect(screen.getByLabelText("Margin Bottom")).toHaveAttribute("min", "-72");
+ expect(screen.getByLabelText("Row Gap")).toHaveAttribute("min", "-72");
+
+ fireEvent.change(screen.getByLabelText("Margin Bottom"), { target: { value: "-10" } });
+ fireEvent.change(screen.getByLabelText("Row Gap"), { target: { value: "-6" } });
+
+ expect(updateResumeData).toHaveBeenCalledTimes(2);
+
+ const marginRecipe = updateResumeData.mock.calls[0]?.[0] as (draft: {
+ metadata: { styleRules: unknown[] };
+ }) => void;
+ const marginDraft = { metadata: { styleRules: [] } };
+ marginRecipe(marginDraft);
+
+ expect(marginDraft.metadata.styleRules).toEqual([
+ {
+ id: "style-global-heading",
+ label: "All sections: Section heading",
+ enabled: true,
+ target: { scope: "global" },
+ slots: { heading: { marginBottom: -10 } },
+ },
+ ]);
+
+ const gapRecipe = updateResumeData.mock.calls[1]?.[0] as (draft: { metadata: { styleRules: unknown[] } }) => void;
+ const gapDraft = { metadata: { styleRules: [] } };
+ gapRecipe(gapDraft);
+
+ expect(gapDraft.metadata.styleRules).toEqual([
+ {
+ id: "style-global-heading",
+ label: "All sections: Section heading",
+ enabled: true,
+ target: { scope: "global" },
+ slots: { heading: { rowGap: -6 } },
+ },
+ ]);
+ });
+
+ it("stores list slot rules for rich text lists", () => {
+ styleRules.splice(0, styleRules.length);
+ renderStyles();
+
+ fireEvent.change(screen.getByLabelText("Style Slot"), { target: { value: "richList" } });
+ fireEvent.change(screen.getByLabelText("Row Gap"), { target: { value: "8" } });
+
+ expect(updateResumeData).toHaveBeenCalledTimes(1);
+ const recipe = updateResumeData.mock.calls[0]?.[0] as (draft: { metadata: { styleRules: unknown[] } }) => void;
+ const draft = { metadata: { styleRules: [] } };
+ recipe(draft);
+
+ expect(draft.metadata.styleRules).toEqual([
+ {
+ id: "style-global-richList",
+ label: "All sections: List",
+ enabled: true,
+ target: { scope: "global" },
+ slots: { richList: { rowGap: 8 } },
+ },
+ ]);
+ });
+
+ it("can reset the selected style rule", () => {
+ renderStyles();
+
+ fireEvent.click(screen.getByRole("button", { name: "Reset Style" }));
+
+ expect(updateResumeData).toHaveBeenCalledTimes(1);
+ const recipe = updateResumeData.mock.calls[0]?.[0] as (draft: {
+ metadata: { styleRules: { id: string }[] };
+ }) => void;
+ const draft = {
+ metadata: {
+ styleRules: [
+ {
+ id: "style-global-heading",
+ label: "All sections: Section heading",
+ enabled: true,
+ target: { scope: "global" },
+ slots: { heading: { color: "rgba(0, 0, 0, 1)" } },
+ },
+ {
+ id: "style-global-section",
+ label: "All sections: Section container",
+ enabled: true,
+ target: { scope: "global" },
+ slots: { section: { padding: 4 } },
+ },
+ ],
+ },
+ };
+ recipe(draft);
+
+ expect(draft.metadata.styleRules.map((rule) => rule.id)).toEqual(["style-global-section"]);
+ });
+
+ it("lists applied style rules and toggles individual rules", () => {
+ renderStyles();
+
+ expect(screen.getByText("Applied Rules")).toBeInTheDocument();
+ expect(screen.getByText("All sections: Section heading")).toBeInTheDocument();
+
+ fireEvent.click(screen.getByLabelText("Toggle All sections: Section heading"));
+
+ expect(updateResumeData).toHaveBeenCalledTimes(1);
+ const recipe = updateResumeData.mock.calls[0]?.[0] as (draft: {
+ metadata: { styleRules: { id: string; enabled: boolean }[] };
+ }) => void;
+ const draft = { metadata: { styleRules: [{ id: "style-global-heading", enabled: true }] } };
+ recipe(draft);
+
+ expect(draft.metadata.styleRules[0]?.enabled).toBe(false);
+ });
+
+ it("opens a manage rules dialog with editable rule fields", () => {
+ renderStyles();
+
+ fireEvent.click(screen.getByRole("button", { name: "Manage Rules" }));
+
+ expect(screen.getByRole("dialog")).toBeInTheDocument();
+ expect(screen.getByText("Manage Style Rules")).toBeInTheDocument();
+ expect(screen.getByLabelText("Rule Label")).toHaveValue("All sections: Section heading");
+ expect(screen.getByLabelText("Dialog Text Color")).toHaveValue("rgba(220, 38, 38, 1)");
+ expect(screen.getByLabelText("Dialog Padding Top")).toBeInTheDocument();
+ expect(screen.getByLabelText("Dialog Padding Right")).toBeInTheDocument();
+ expect(screen.getByLabelText("Dialog Padding Bottom")).toBeInTheDocument();
+ expect(screen.getByLabelText("Dialog Padding Left")).toBeInTheDocument();
+ });
+});
diff --git a/apps/web/src/routes/builder/$resumeId/-sidebar/right/sections/styles.tsx b/apps/web/src/routes/builder/$resumeId/-sidebar/right/sections/styles.tsx
new file mode 100644
index 000000000..6c5d7af27
--- /dev/null
+++ b/apps/web/src/routes/builder/$resumeId/-sidebar/right/sections/styles.tsx
@@ -0,0 +1,1159 @@
+import type {
+ ResumeData,
+ StyleIntent,
+ StyleRule,
+ StyleRuleTarget,
+ StyleSlot,
+} from "@reactive-resume/schema/resume/data";
+import type { ComponentProps, ReactNode } from "react";
+import { Trans } from "@lingui/react/macro";
+import { SlidersHorizontalIcon, TrashSimpleIcon } from "@phosphor-icons/react";
+import { useMemo, useState } from "react";
+import { sectionTypeSchema } from "@reactive-resume/schema/resume/data";
+import { Badge } from "@reactive-resume/ui/components/badge";
+import { Button } from "@reactive-resume/ui/components/button";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+} from "@reactive-resume/ui/components/dialog";
+import { Input } from "@reactive-resume/ui/components/input";
+import { Label } from "@reactive-resume/ui/components/label";
+import { Separator } from "@reactive-resume/ui/components/separator";
+import { Switch } from "@reactive-resume/ui/components/switch";
+import { cn } from "@reactive-resume/utils/style";
+import { ColorPicker } from "@/components/input/color-picker";
+import { useCurrentResume, useUpdateResumeData } from "@/features/resume/builder/draft";
+import { getSectionTitle } from "@/libs/resume/section";
+import { SectionBase } from "../shared/section-base";
+
+type TargetScope = StyleRuleTarget["scope"];
+
+type StyleSlotOption = {
+ value: StyleSlot;
+ label: string;
+ group: "Section" | "Rich text";
+};
+
+const styleSlotOptions: StyleSlotOption[] = [
+ { value: "section", label: "Section container", group: "Section" },
+ { value: "heading", label: "Section heading", group: "Section" },
+ { value: "item", label: "Item container", group: "Section" },
+ { value: "text", label: "Primary text", group: "Section" },
+ { value: "secondaryText", label: "Secondary text", group: "Section" },
+ { value: "link", label: "Link", group: "Section" },
+ { value: "icon", label: "Icon", group: "Section" },
+ { value: "level", label: "Level indicator", group: "Section" },
+ { value: "richParagraph", label: "Paragraph", group: "Rich text" },
+ { value: "richList", label: "List", group: "Rich text" },
+ { value: "richListItemRow", label: "List item row", group: "Rich text" },
+ { value: "richListItemContent", label: "List item content", group: "Rich text" },
+ { value: "richLink", label: "Inline link", group: "Rich text" },
+ { value: "richBold", label: "Bold text", group: "Rich text" },
+ { value: "richMark", label: "Highlight", group: "Rich text" },
+];
+
+const groupedStyleSlotOptions: [StyleSlotOption["group"], StyleSlotOption[]][] = [
+ ["Section", styleSlotOptions.filter((option) => option.group === "Section")],
+ ["Rich text", styleSlotOptions.filter((option) => option.group === "Rich text")],
+];
+
+const fontWeightOptions = ["100", "200", "300", "400", "500", "600", "700", "800", "900"] as const;
+const fontStyleOptions = [
+ { value: "normal", label: "Normal" },
+ { value: "italic", label: "Italic" },
+] as const satisfies readonly { value: NonNullable; label: string }[];
+const textDecorationOptions = [
+ { value: "none", label: "None" },
+ { value: "underline", label: "Underline" },
+ { value: "line-through", label: "Line through" },
+] as const satisfies readonly { value: NonNullable; label: string }[];
+const textDecorationStyleOptions = [
+ { value: "solid", label: "Solid" },
+ { value: "dashed", label: "Dashed" },
+ { value: "dotted", label: "Dotted" },
+] as const satisfies readonly { value: NonNullable; label: string }[];
+const textAlignOptions = [
+ { value: "left", label: "Left" },
+ { value: "center", label: "Center" },
+ { value: "right", label: "Right" },
+ { value: "justify", label: "Justify" },
+] as const satisfies readonly { value: NonNullable; label: string }[];
+const textTransformOptions = [
+ { value: "none", label: "None" },
+ { value: "uppercase", label: "Uppercase" },
+ { value: "lowercase", label: "Lowercase" },
+ { value: "capitalize", label: "Capitalize" },
+] as const satisfies readonly { value: NonNullable; label: string }[];
+const borderStyleOptions = [
+ { value: "solid", label: "Solid" },
+ { value: "dashed", label: "Dashed" },
+ { value: "dotted", label: "Dotted" },
+] as const satisfies readonly { value: NonNullable; label: string }[];
+
+export function StylesSectionBuilder() {
+ return (
+
+
+
+ );
+}
+
+function StylesSectionForm() {
+ const resume = useCurrentResume();
+ const data = resume.data;
+ const updateResumeData = useUpdateResumeData();
+ const sectionOptions = useMemo(() => getSectionIdOptions(data), [data]);
+ const styleRules = data.metadata.styleRules ?? [];
+
+ const [targetScope, setTargetScope] = useState("global");
+ const [sectionType, setSectionType] = useState("summary");
+ const [sectionId, setSectionId] = useState("summary");
+ const [slot, setSlot] = useState("heading");
+ const [isManageDialogOpen, setManageDialogOpen] = useState(false);
+
+ const target = createTarget({ targetScope, sectionType, sectionId });
+ const ruleId = getStyleRuleId(target, slot);
+ const currentRule = styleRules.find((rule) => rule.id === ruleId);
+ const currentIntent = currentRule?.slots[slot] ?? {};
+ const targetLabel = getTargetLabel(data, target);
+ const slotLabel = getSlotLabel(slot);
+
+ const upsertIntent = (patch: Partial) => {
+ const nextIntent = compactIntent({ ...currentIntent, ...patch });
+
+ updateResumeData((draft) => {
+ draft.metadata.styleRules ??= [];
+ const rules = draft.metadata.styleRules;
+ const existingIndex = rules.findIndex((rule) => rule.id === ruleId);
+ const existingRule = rules[existingIndex];
+
+ if (Object.keys(nextIntent).length === 0) {
+ if (existingIndex >= 0) rules.splice(existingIndex, 1);
+ return;
+ }
+
+ const nextRule: StyleRule = {
+ id: ruleId,
+ label: existingRule?.label || `${targetLabel}: ${slotLabel}`,
+ enabled: existingRule?.enabled ?? true,
+ target,
+ slots: { [slot]: nextIntent },
+ };
+
+ if (existingIndex >= 0) rules[existingIndex] = nextRule;
+ else rules.push(nextRule);
+ });
+ };
+
+ const resetRule = () => {
+ updateResumeData((draft) => {
+ draft.metadata.styleRules = (draft.metadata.styleRules ?? []).filter((rule) => rule.id !== ruleId);
+ });
+ };
+
+ const updateRuleEnabled = (ruleId: string, enabled: boolean) => {
+ updateResumeData((draft) => {
+ const rule = (draft.metadata.styleRules ?? []).find((rule) => rule.id === ruleId);
+ if (rule) rule.enabled = enabled;
+ });
+ };
+
+ const updateRuleLabel = (ruleId: string, label: string) => {
+ updateResumeData((draft) => {
+ const rule = (draft.metadata.styleRules ?? []).find((rule) => rule.id === ruleId);
+ if (rule) rule.label = label;
+ });
+ };
+
+ const updateRuleIntent = (ruleId: string, slot: StyleSlot, patch: Partial) => {
+ updateResumeData((draft) => {
+ const rules = draft.metadata.styleRules ?? [];
+ const ruleIndex = rules.findIndex((rule) => rule.id === ruleId);
+ const rule = rules[ruleIndex];
+ if (!rule) return;
+
+ const nextIntent = compactIntent({ ...(rule.slots[slot] ?? {}), ...patch });
+ if (Object.keys(nextIntent).length === 0) delete rule.slots[slot];
+ else rule.slots[slot] = nextIntent;
+
+ if (getConfiguredSlots(rule).length === 0) rules.splice(ruleIndex, 1);
+ });
+ };
+
+ const deleteRule = (ruleId: string) => {
+ updateResumeData((draft) => {
+ draft.metadata.styleRules = (draft.metadata.styleRules ?? []).filter((rule) => rule.id !== ruleId);
+ });
+ };
+
+ return (
+
+
+
+
+
+
+ {targetScope === "sectionType" && (
+
+
+
+ )}
+
+ {targetScope === "sectionId" && (
+
+
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
setManageDialogOpen(true)}
+ />
+
+
+
+ );
+}
+
+function Field({ label, id, children }: { label: string; id: string; children: ReactNode }) {
+ return (
+
+
+ {children}
+
+ );
+}
+
+function Select({ className, ...props }: ComponentProps<"select">) {
+ return (
+
+ );
+}
+
+function ColorField({
+ label,
+ id,
+ value,
+ placeholder,
+ fallback,
+ onChange,
+}: {
+ label: string;
+ id: string;
+ value: string | undefined;
+ placeholder?: string;
+ fallback: string;
+ onChange: (value: string | undefined) => void;
+}) {
+ return (
+
+
+ onChange(color)} />
+ onChange(event.target.value.trim() || undefined)}
+ />
+
+
+ );
+}
+
+function NumberInput({
+ label,
+ id,
+ value,
+ min,
+ max,
+ step = 1,
+ onChange,
+}: {
+ label: string;
+ id?: string;
+ value: number | undefined;
+ min: number;
+ max: number;
+ step?: number;
+ onChange: (value: number | undefined) => void;
+}) {
+ const inputId = id ?? `style-${label.toLowerCase().replaceAll(" ", "-")}`;
+
+ return (
+
+ {
+ const value = event.target.value;
+ onChange(value === "" ? undefined : Number(value));
+ }}
+ />
+
+ );
+}
+
+function AppliedRulesList({
+ data,
+ rules,
+ onToggleRule,
+ onDeleteRule,
+ onManageRules,
+}: {
+ data: ResumeData;
+ rules: StyleRule[];
+ onToggleRule: (ruleId: string, enabled: boolean) => void;
+ onDeleteRule: (ruleId: string) => void;
+ onManageRules: () => void;
+}) {
+ return (
+
+
+
+
+ Applied Rules
+
+
+ {rules.length} {rules.length === 1 ? rule : rules}
+
+
+
+
+
+
+ {rules.length === 0 ? (
+
+ No style rules yet.
+
+ ) : (
+
+ {rules.map((rule) => (
+
+ ))}
+
+ )}
+
+ );
+}
+
+function AppliedRuleCard({
+ data,
+ rule,
+ onToggleRule,
+ onDeleteRule,
+}: {
+ data: ResumeData;
+ rule: StyleRule;
+ onToggleRule: (ruleId: string, enabled: boolean) => void;
+ onDeleteRule: (ruleId: string) => void;
+}) {
+ const slots = getConfiguredSlots(rule);
+ const primaryIntent = slots[0] ? rule.slots[slots[0]] : undefined;
+ const fallbackLabel = getRuleFallbackLabel(data, rule);
+
+ return (
+
+
+
+
+ {rule.label || fallbackLabel}
+ {!rule.enabled && (
+
+ Off
+
+ )}
+
+
+
+ {getTargetLabel(data, rule.target)}
+ {slots.map((slot) => (
+
+ {getSlotLabel(slot)}
+
+ ))}
+
+
+ {primaryIntent &&
}
+
+
+
+ onToggleRule(rule.id, checked)}
+ />
+
+
+
+
+ );
+}
+
+function ManageStyleRulesDialog({
+ data,
+ rules,
+ open,
+ onOpenChange,
+ onToggleRule,
+ onUpdateRuleLabel,
+ onUpdateRuleIntent,
+ onDeleteRule,
+}: {
+ data: ResumeData;
+ rules: StyleRule[];
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+ onToggleRule: (ruleId: string, enabled: boolean) => void;
+ onUpdateRuleLabel: (ruleId: string, label: string) => void;
+ onUpdateRuleIntent: (ruleId: string, slot: StyleSlot, patch: Partial) => void;
+ onDeleteRule: (ruleId: string) => void;
+}) {
+ return (
+
+ );
+}
+
+function ManagedRuleCard({
+ data,
+ rule,
+ onToggleRule,
+ onUpdateRuleLabel,
+ onUpdateRuleIntent,
+ onDeleteRule,
+}: {
+ data: ResumeData;
+ rule: StyleRule;
+ onToggleRule: (ruleId: string, enabled: boolean) => void;
+ onUpdateRuleLabel: (ruleId: string, label: string) => void;
+ onUpdateRuleIntent: (ruleId: string, slot: StyleSlot, patch: Partial) => void;
+ onDeleteRule: (ruleId: string) => void;
+}) {
+ const slots = getConfiguredSlots(rule);
+ const fallbackLabel = getRuleFallbackLabel(data, rule);
+ const labelId = `style-dialog-${slugify(rule.id)}-label`;
+
+ return (
+
+
+
+
+ onUpdateRuleLabel(rule.id, event.target.value)}
+ />
+
+
+
+ {getTargetLabel(data, rule.target)}
+ {slots.map((slot) => (
+
+ {getSlotLabel(slot)}
+
+ ))}
+
+
+
+
+ onToggleRule(rule.id, checked)}
+ />
+
+
+
+
+ {slots.map((slot) => (
+
onUpdateRuleIntent(rule.id, slot, patch)}
+ />
+ ))}
+
+ );
+}
+
+function RuleIntentEditor({
+ idPrefix,
+ intent,
+ labelPrefix,
+ onChange,
+}: {
+ idPrefix: string;
+ intent: StyleIntent;
+ labelPrefix?: string;
+ onChange: (patch: Partial) => void;
+}) {
+ const labelStart = labelPrefix ? `${labelPrefix} ` : "";
+
+ return (
+
+
+
+ onChange({ color })}
+ />
+ onChange({ backgroundColor })}
+ />
+ onChange({ textDecorationColor })}
+ />
+ onChange({ opacity })}
+ />
+
+
+
+
+
+ onChange({ fontSize })}
+ />
+ onChange({ fontWeight })}
+ />
+ onChange({ fontStyle })}
+ />
+ onChange({ lineHeight })}
+ />
+ onChange({ letterSpacing })}
+ />
+ onChange({ textDecoration })}
+ />
+ onChange({ textDecorationStyle })}
+ />
+ onChange({ textAlign })}
+ />
+ onChange({ textTransform })}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+ onChange({ rowGap })}
+ />
+ onChange({ columnGap })}
+ />
+
+
+
+
+
+
+
+ onChange({ borderStyle })}
+ />
+ onChange({ borderWidth })}
+ />
+ onChange({ borderRadius })}
+ />
+ onChange({ borderColor })}
+ />
+
+
+
+ );
+}
+
+function ControlPanel({ title, children }: { title: string; children: ReactNode }) {
+ return (
+
+ );
+}
+
+function ControlSubsection({ title, children }: { title: string; children: ReactNode }) {
+ return (
+
+ );
+}
+
+function IntentSelectField({
+ label,
+ id,
+ value,
+ options,
+ onChange,
+}: {
+ label: string;
+ id: string;
+ value: TValue | undefined;
+ options: readonly { value: TValue; label: string }[];
+ onChange: (value: TValue | undefined) => void;
+}) {
+ return (
+
+
+
+ );
+}
+
+function FontWeightField({
+ label,
+ id,
+ value,
+ onChange,
+}: {
+ label: string;
+ id: string;
+ value: StyleIntent["fontWeight"] | undefined;
+ onChange: (value: StyleIntent["fontWeight"] | undefined) => void;
+}) {
+ return (
+
+
+
+ );
+}
+
+const paddingSideOptions = [
+ { property: "paddingTop", label: "Top" },
+ { property: "paddingRight", label: "Right" },
+ { property: "paddingBottom", label: "Bottom" },
+ { property: "paddingLeft", label: "Left" },
+] as const;
+
+type PaddingSideProperty = (typeof paddingSideOptions)[number]["property"];
+
+const marginSideOptions = [
+ { property: "marginTop", label: "Top" },
+ { property: "marginRight", label: "Right" },
+ { property: "marginBottom", label: "Bottom" },
+ { property: "marginLeft", label: "Left" },
+] as const;
+
+type MarginSideProperty = (typeof marginSideOptions)[number]["property"];
+
+function PaddingSideInputs({
+ idPrefix,
+ intent,
+ labelPrefix,
+ onChange,
+}: {
+ idPrefix: string;
+ intent: StyleIntent;
+ labelPrefix?: string;
+ onChange: (patch: Partial) => void;
+}) {
+ const labelStart = labelPrefix ? `${labelPrefix} ` : "";
+
+ return (
+
+ {paddingSideOptions.map((side) => (
+ onChange(createPaddingSidePatch(intent, side.property, value))}
+ />
+ ))}
+
+ );
+}
+
+function MarginSideInputs({
+ idPrefix,
+ intent,
+ labelPrefix,
+ onChange,
+}: {
+ idPrefix: string;
+ intent: StyleIntent;
+ labelPrefix?: string;
+ onChange: (patch: Partial) => void;
+}) {
+ const labelStart = labelPrefix ? `${labelPrefix} ` : "";
+
+ return (
+
+ {marginSideOptions.map((side) => (
+ onChange(createMarginSidePatch(side.property, value))}
+ />
+ ))}
+
+ );
+}
+
+function getPaddingSideValue(intent: StyleIntent, property: PaddingSideProperty) {
+ return intent[property] ?? intent.padding;
+}
+
+function createPaddingSidePatch(
+ intent: StyleIntent,
+ property: PaddingSideProperty,
+ value: number | undefined,
+): Partial {
+ if (intent.padding === undefined) return { [property]: value };
+
+ const patch: Partial = { padding: undefined };
+
+ for (const side of paddingSideOptions) {
+ patch[side.property] = intent[side.property] ?? intent.padding;
+ }
+
+ patch[property] = value;
+
+ return patch;
+}
+
+function createMarginSidePatch(property: MarginSideProperty, value: number | undefined): Partial {
+ return { [property]: value };
+}
+
+function RulePropertySummary({ intent }: { intent: StyleIntent }) {
+ const properties = [
+ intent.color && { label: "Text", value: intent.color, color: intent.color },
+ intent.backgroundColor && { label: "Background", value: intent.backgroundColor, color: intent.backgroundColor },
+ intent.textDecorationColor && {
+ label: "Decoration",
+ value: intent.textDecorationColor,
+ color: intent.textDecorationColor,
+ },
+ intent.borderColor && { label: "Border", value: intent.borderColor, color: intent.borderColor },
+ intent.opacity !== undefined && { label: "Opacity", value: `${intent.opacity}` },
+ intent.fontSize && { label: "Size", value: `${intent.fontSize}` },
+ intent.fontWeight && { label: "Weight", value: intent.fontWeight },
+ intent.fontStyle && { label: "Style", value: intent.fontStyle },
+ intent.lineHeight !== undefined && { label: "Line", value: `${intent.lineHeight}` },
+ intent.letterSpacing !== undefined && { label: "Tracking", value: `${intent.letterSpacing}` },
+ intent.textDecoration && { label: "Decoration", value: intent.textDecoration },
+ intent.textDecorationStyle && { label: "Decoration Style", value: intent.textDecorationStyle },
+ intent.textAlign && { label: "Align", value: intent.textAlign },
+ intent.textTransform && { label: "Transform", value: intent.textTransform },
+ getPaddingSummary(intent) && { label: "Padding", value: getPaddingSummary(intent) },
+ getMarginSummary(intent) && { label: "Margin", value: getMarginSummary(intent) },
+ getGapSummary(intent) && { label: "Gap", value: getGapSummary(intent) },
+ intent.borderStyle && { label: "Border Style", value: intent.borderStyle },
+ intent.borderWidth !== undefined && { label: "Border Width", value: `${intent.borderWidth}` },
+ intent.borderRadius !== undefined && { label: "Radius", value: `${intent.borderRadius}` },
+ ].filter(Boolean) as { label: string; value: string; color?: string }[];
+
+ if (properties.length === 0) return null;
+
+ return (
+
+ {properties.map((property) => (
+
+ {property.color && (
+
+ )}
+ {property.label}
+ {property.value}
+
+ ))}
+
+ );
+}
+
+function getPaddingSummary(intent: StyleIntent) {
+ if (intent.padding !== undefined) return `All ${intent.padding}`;
+
+ const sideValues = paddingSideOptions.flatMap((side) => {
+ const value = intent[side.property];
+ if (value === undefined) return [];
+
+ return [`${side.label.at(0)} ${value}`];
+ });
+
+ return sideValues.length > 0 ? sideValues.join(" / ") : undefined;
+}
+
+function getMarginSummary(intent: StyleIntent) {
+ const sideValues = marginSideOptions.flatMap((side) => {
+ const value = intent[side.property];
+ if (value === undefined) return [];
+
+ return [`${side.label.at(0)} ${value}`];
+ });
+
+ return sideValues.length > 0 ? sideValues.join(" / ") : undefined;
+}
+
+function getGapSummary(intent: StyleIntent) {
+ const values = [
+ intent.rowGap !== undefined && `Row ${intent.rowGap}`,
+ intent.columnGap !== undefined && `Column ${intent.columnGap}`,
+ ].filter(Boolean);
+
+ return values.length > 0 ? values.join(" / ") : undefined;
+}
+
+function createTarget({
+ targetScope,
+ sectionType,
+ sectionId,
+}: {
+ targetScope: TargetScope;
+ sectionType: string;
+ sectionId: string;
+}): StyleRuleTarget {
+ if (targetScope === "sectionType") {
+ return {
+ scope: "sectionType",
+ sectionType: sectionType as Extract["sectionType"],
+ };
+ }
+ if (targetScope === "sectionId") return { scope: "sectionId", sectionId };
+
+ return { scope: "global" };
+}
+
+function getStyleRuleId(target: StyleRuleTarget, slot: StyleSlot) {
+ if (target.scope === "global") return `style-global-${slot}`;
+ if (target.scope === "sectionType") return `style-section-type-${target.sectionType}-${slot}`;
+
+ return `style-section-id-${slugify(target.sectionId)}-${slot}`;
+}
+
+function getSlotLabel(slot: StyleSlot) {
+ return styleSlotOptions.find((option) => option.value === slot)?.label ?? slot;
+}
+
+function getTargetLabel(data: ResumeData, target: StyleRuleTarget) {
+ if (target.scope === "global") return "All sections";
+ if (target.scope === "sectionType") return getSectionTitle(target.sectionType);
+
+ return getSectionIdOptions(data).find((option) => option.value === target.sectionId)?.label ?? target.sectionId;
+}
+
+function getRuleFallbackLabel(data: ResumeData, rule: StyleRule) {
+ const slots = getConfiguredSlots(rule);
+ const slot = slots[0];
+ return `${getTargetLabel(data, rule.target)}${slot ? `: ${getSlotLabel(slot)}` : ""}`;
+}
+
+function getConfiguredSlots(rule: StyleRule): StyleSlot[] {
+ const slots: StyleSlot[] = [];
+
+ for (const option of styleSlotOptions) {
+ if (hasIntent(rule.slots[option.value])) slots.push(option.value);
+ }
+
+ return slots;
+}
+
+function getSectionIdOptions(data: ResumeData) {
+ return [
+ { value: "summary", label: data.summary?.title || getSectionTitle("summary") },
+ ...Object.entries(data.sections).map(([section, value]) => ({
+ value: section,
+ label: value.title || getSectionTitle(section as keyof ResumeData["sections"]),
+ })),
+ ...data.customSections.map((section) => ({
+ value: section.id,
+ label: section.title || getSectionTitle(section.type),
+ })),
+ ];
+}
+
+function compactIntent(intent: Partial): StyleIntent {
+ return Object.fromEntries(
+ Object.entries(intent).filter(([, value]) => value !== undefined && value !== ""),
+ ) as StyleIntent;
+}
+
+function hasIntent(intent: StyleIntent | undefined) {
+ return Boolean(intent && Object.keys(intent).length > 0);
+}
+
+function slugify(value: string) {
+ return value
+ .replace(/[^a-zA-Z0-9]+/g, "-")
+ .replace(/(^-|-$)/g, "")
+ .toLowerCase();
+}
diff --git a/docs/adr/0002-structured-style-rules-for-react-pdf.md b/docs/adr/0002-structured-style-rules-for-react-pdf.md
new file mode 100644
index 000000000..d23322736
--- /dev/null
+++ b/docs/adr/0002-structured-style-rules-for-react-pdf.md
@@ -0,0 +1,5 @@
+# Structured Style Rules for React PDF
+
+Reactive Resume no longer renders final PDFs through browser HTML/CSS, so end-user custom CSS would create a misleading contract: React PDF accepts style objects on renderer components, not arbitrary browser selectors. Resume appearance customization is therefore modeled as structured Style Rules in resume metadata, targeting semantic section and rich-text slots that the PDF package translates into safe React PDF styles.
+
+This preserves user-controlled section styling while keeping PDF rendering portable across preview, export, public resume views, templates, and server generation. Raw CSS, raw React PDF style objects, section-item targeting, and direct PDF-preview selection are intentionally out of v1; they can be revisited only if the product needs that power enough to accept the additional validation and layout-breakage risk.
diff --git a/package.json b/package.json
index f3183846e..f2bd40968 100644
--- a/package.json
+++ b/package.json
@@ -52,7 +52,7 @@
"knip": "^6.14.2",
"lefthook": "^2.1.8",
"npm-check-updates": "^22.2.1",
- "turbo": "^2.9.14",
+ "turbo": "^2.9.15",
"typescript": "^6.0.3",
"vitest": "^4.1.7"
}
diff --git a/packages/ai/package.json b/packages/ai/package.json
index 8cc0b52f8..a38942b50 100644
--- a/packages/ai/package.json
+++ b/packages/ai/package.json
@@ -29,7 +29,7 @@
},
"devDependencies": {
"@reactive-resume/config": "workspace:*",
- "@typescript/native-preview": "7.0.0-dev.20260526.1",
+ "@typescript/native-preview": "7.0.0-dev.20260527.1",
"typescript": "^6.0.3"
}
}
diff --git a/packages/api/package.json b/packages/api/package.json
index 4b656d0ad..045c72b93 100644
--- a/packages/api/package.json
+++ b/packages/api/package.json
@@ -23,7 +23,7 @@
"@ai-sdk/google": "^3.0.79",
"@ai-sdk/openai": "^3.0.65",
"@ai-sdk/openai-compatible": "^2.0.48",
- "@aws-sdk/client-s3": "^3.1053.0",
+ "@aws-sdk/client-s3": "^3.1054.0",
"@orpc/client": "^1.14.3",
"@orpc/experimental-ratelimit": "^1.14.3",
"@orpc/server": "^1.14.3",
@@ -41,7 +41,7 @@
"drizzle-orm": "1.0.0-rc.3",
"drizzle-zod": "1.0.0-beta.14-a36c63d",
"es-toolkit": "^1.47.0",
- "ioredis": "^5.10.1",
+ "ioredis": "^5.11.0",
"ollama-ai-provider-v2": "^3.5.1",
"react": "^19.2.6",
"resumable-stream": "^2.2.12",
@@ -52,7 +52,7 @@
"devDependencies": {
"@reactive-resume/config": "workspace:*",
"@types/bcrypt": "^6.0.0",
- "@typescript/native-preview": "7.0.0-dev.20260526.1",
+ "@typescript/native-preview": "7.0.0-dev.20260527.1",
"typescript": "^6.0.3"
}
}
diff --git a/packages/auth/package.json b/packages/auth/package.json
index 4a2434253..cfcd0f657 100644
--- a/packages/auth/package.json
+++ b/packages/auth/package.json
@@ -18,7 +18,7 @@
"dependencies": {
"@better-auth/api-key": "^1.6.11",
"@better-auth/drizzle-adapter": "^1.6.11",
- "@better-auth/infra": "^0.2.8",
+ "@better-auth/infra": "^0.2.10",
"@better-auth/oauth-provider": "^1.6.11",
"@better-auth/passkey": "^1.6.11",
"@reactive-resume/db": "workspace:*",
@@ -36,7 +36,7 @@
"@reactive-resume/config": "workspace:*",
"@types/bcrypt": "^6.0.0",
"@types/react": "^19.2.15",
- "@typescript/native-preview": "7.0.0-dev.20260526.1",
+ "@typescript/native-preview": "7.0.0-dev.20260527.1",
"typescript": "^6.0.3"
}
}
diff --git a/packages/db/package.json b/packages/db/package.json
index 47bb5eaa1..f8fdf61bf 100644
--- a/packages/db/package.json
+++ b/packages/db/package.json
@@ -31,7 +31,7 @@
"devDependencies": {
"@reactive-resume/config": "workspace:*",
"@types/pg": "^8.20.0",
- "@typescript/native-preview": "7.0.0-dev.20260526.1",
+ "@typescript/native-preview": "7.0.0-dev.20260527.1",
"drizzle-kit": "1.0.0-rc.3",
"typescript": "^6.0.3"
}
diff --git a/packages/docx/package.json b/packages/docx/package.json
index 339fb4a91..2c1ffc766 100644
--- a/packages/docx/package.json
+++ b/packages/docx/package.json
@@ -20,7 +20,7 @@
},
"devDependencies": {
"@reactive-resume/config": "workspace:*",
- "@typescript/native-preview": "7.0.0-dev.20260526.1",
+ "@typescript/native-preview": "7.0.0-dev.20260527.1",
"typescript": "^6.0.3"
}
}
diff --git a/packages/email/package.json b/packages/email/package.json
index 86754b0ca..165e482b5 100644
--- a/packages/email/package.json
+++ b/packages/email/package.json
@@ -18,17 +18,17 @@
},
"dependencies": {
"@reactive-resume/env": "workspace:*",
- "nodemailer": "^8.0.8",
+ "nodemailer": "^8.0.9",
"react": "^19.2.6",
- "react-email": "^6.3.3"
+ "react-email": "^6.4.0"
},
"devDependencies": {
- "@react-email/ui": "^6.3.3",
+ "@react-email/ui": "^6.4.0",
"@reactive-resume/config": "workspace:*",
"@types/nodemailer": "^8.0.0",
"@types/react": "^19.2.15",
- "@typescript/native-preview": "7.0.0-dev.20260526.1",
- "react-doctor": "^0.2.6",
+ "@typescript/native-preview": "7.0.0-dev.20260527.1",
+ "react-doctor": "^0.2.9",
"typescript": "^6.0.3"
}
}
diff --git a/packages/env/package.json b/packages/env/package.json
index 8f86b1f38..559d594c7 100644
--- a/packages/env/package.json
+++ b/packages/env/package.json
@@ -22,7 +22,7 @@
"devDependencies": {
"@reactive-resume/config": "workspace:*",
"@types/node": "^25.9.1",
- "@typescript/native-preview": "7.0.0-dev.20260526.1",
+ "@typescript/native-preview": "7.0.0-dev.20260527.1",
"typescript": "^6.0.3"
}
}
diff --git a/packages/fonts/package.json b/packages/fonts/package.json
index 139df7610..9363e1ab8 100644
--- a/packages/fonts/package.json
+++ b/packages/fonts/package.json
@@ -19,7 +19,7 @@
},
"devDependencies": {
"@reactive-resume/config": "workspace:*",
- "@typescript/native-preview": "7.0.0-dev.20260526.1",
+ "@typescript/native-preview": "7.0.0-dev.20260527.1",
"typescript": "^6.0.3"
}
}
diff --git a/packages/import/package.json b/packages/import/package.json
index 15b3de94f..0aa7aa67e 100644
--- a/packages/import/package.json
+++ b/packages/import/package.json
@@ -23,7 +23,7 @@
},
"devDependencies": {
"@reactive-resume/config": "workspace:*",
- "@typescript/native-preview": "7.0.0-dev.20260526.1",
+ "@typescript/native-preview": "7.0.0-dev.20260527.1",
"typescript": "^6.0.3"
}
}
diff --git a/packages/import/src/reactive-resume-v4-json.tsx b/packages/import/src/reactive-resume-v4-json.tsx
index 7be36ba04..afd152a54 100644
--- a/packages/import/src/reactive-resume-v4-json.tsx
+++ b/packages/import/src/reactive-resume-v4-json.tsx
@@ -762,6 +762,7 @@ export class ReactiveResumeV4JSONImporter {
},
},
notes: v4Data.metadata.notes ?? "",
+ styleRules: [],
},
};
diff --git a/packages/mcp/package.json b/packages/mcp/package.json
index 27048946d..9a7df632f 100644
--- a/packages/mcp/package.json
+++ b/packages/mcp/package.json
@@ -29,7 +29,7 @@
},
"devDependencies": {
"@reactive-resume/config": "workspace:*",
- "@typescript/native-preview": "7.0.0-dev.20260526.1",
+ "@typescript/native-preview": "7.0.0-dev.20260527.1",
"typescript": "^6.0.3",
"vitest": "^4.1.7"
}
diff --git a/packages/pdf/package.json b/packages/pdf/package.json
index 554291b55..52c8d64b8 100644
--- a/packages/pdf/package.json
+++ b/packages/pdf/package.json
@@ -38,8 +38,8 @@
"@react-pdf/types": "^2.11.1",
"@reactive-resume/config": "workspace:*",
"@types/react": "^19.2.15",
- "@typescript/native-preview": "7.0.0-dev.20260526.1",
- "react-doctor": "^0.2.6",
+ "@typescript/native-preview": "7.0.0-dev.20260527.1",
+ "react-doctor": "^0.2.9",
"typescript": "^6.0.3"
}
}
diff --git a/packages/pdf/src/templates/shared/context.tsx b/packages/pdf/src/templates/shared/context.tsx
index d88eb8f2d..2d8a337cf 100644
--- a/packages/pdf/src/templates/shared/context.tsx
+++ b/packages/pdf/src/templates/shared/context.tsx
@@ -1,4 +1,6 @@
+import type { StyleSlot } from "@reactive-resume/schema/resume/data";
import type { ReactNode } from "react";
+import type { SectionStyleRuleContext } from "./style-rules";
import type { StyleInput, TemplatePlacement } from "./styles";
import type {
SectionTimelineStyleSlots,
@@ -11,6 +13,8 @@ import type {
TemplateStyleSlots,
} from "./types";
import { createContext, use, useMemo } from "react";
+import { useRender } from "../../context";
+import { resolveStyleRuleSlot } from "./style-rules";
type TemplateContextValue = {
styles: TemplateStyleSlots;
@@ -27,6 +31,7 @@ type TemplateProviderProps = Omit(null);
const TemplatePlacementContext = createContext("main");
+const SectionStyleContext = createContext(null);
const resolveStyleSlot = (slot: TemplateStyleSlot | undefined, context: TemplateStyleContextValue): StyleInput => {
if (!slot) return undefined;
@@ -78,6 +83,16 @@ export const TemplatePlacementProvider = ({
return {children};
};
+export const SectionStyleProvider = ({
+ context,
+ children,
+}: {
+ context: SectionStyleRuleContext;
+ children: ReactNode;
+}) => {
+ return {children};
+};
+
const useTemplateContext = () => {
const context = use(TemplateContext);
@@ -108,6 +123,15 @@ export const useTemplateStyle = (slot: keyof TemplateStyleSlots): StyleInput =>
return resolveStyleSlot(styles[slot] as TemplateStyleSlot | undefined, context);
};
+export const useSectionStyleRule = (slot: StyleSlot): StyleInput => {
+ const data = useRender();
+ const context = use(SectionStyleContext);
+
+ if (!context) return undefined;
+
+ return resolveStyleRuleSlot(data, { ...context, slot });
+};
+
export const useTemplateFeatureStyle = (
feature: keyof TemplateFeatureStyleSlots,
slot: keyof SectionTimelineStyleSlots,
diff --git a/packages/pdf/src/templates/shared/level-display.tsx b/packages/pdf/src/templates/shared/level-display.tsx
index 7b084a161..ce6963c7e 100644
--- a/packages/pdf/src/templates/shared/level-display.tsx
+++ b/packages/pdf/src/templates/shared/level-display.tsx
@@ -2,7 +2,7 @@ import type { Style } from "@react-pdf/types";
import type { IconName } from "phosphor-icons-react-pdf/dynamic";
import { useRender } from "../../context";
import { View } from "../../renderer";
-import { useTemplateIconSlot, useTemplateStyle } from "./context";
+import { useSectionStyleRule, useTemplateIconSlot, useTemplateStyle } from "./context";
import { getTemplateMetrics } from "./metrics";
import { Icon } from "./primitives";
import { composeStyles } from "./styles";
@@ -19,6 +19,7 @@ export const LevelDisplay = ({ level }: { level: number }) => {
const levelItemStyle = useTemplateStyle("levelItem");
const levelItemActiveStyle = useTemplateStyle("levelItemActive");
const levelItemInactiveStyle = useTemplateStyle("levelItemInactive");
+ const levelRuleStyle = useSectionStyleRule("level");
const color = typeof iconProps.color === "string" ? iconProps.color : "#000000";
if (level === 0) return null;
@@ -40,6 +41,7 @@ export const LevelDisplay = ({ level }: { level: number }) => {
style={composeStyles(
{ flexDirection: "row", alignItems: "center", marginTop: 2, columnGap: gap },
levelContainerStyle,
+ levelRuleStyle,
)}
>
{LEVEL_ITEM_KEYS.map((itemKey, index) => {
diff --git a/packages/pdf/src/templates/shared/primitives.tsx b/packages/pdf/src/templates/shared/primitives.tsx
index 1fb50194d..b3ded8590 100644
--- a/packages/pdf/src/templates/shared/primitives.tsx
+++ b/packages/pdf/src/templates/shared/primitives.tsx
@@ -3,7 +3,7 @@ import type { ComponentProps } from "react";
import type { StyleInput } from "./styles";
import { Icon as PhosphorIcon } from "phosphor-icons-react-pdf/dynamic";
import { Link as PdfLink, Text as PdfText, View } from "../../renderer";
-import { useTemplateIconSlot, useTemplateStyle } from "./context";
+import { useSectionStyleRule, useTemplateIconSlot, useTemplateStyle } from "./context";
import { safeTextStyle } from "./safe-text-style";
import { composeLinkStyles, composeStyles } from "./styles";
@@ -17,40 +17,64 @@ export const Div = ({ style, ...props }: ComponentProps) => {
export const Text = ({ style, ...props }: ComponentProps) => {
const textStyle = useTemplateStyle("text");
+ const textRuleStyle = useSectionStyleRule("text");
- return ;
+ return ;
};
export const Heading = ({ style, ...props }: ComponentProps) => {
const headingStyle = useTemplateStyle("heading");
+ const headingRuleStyle = useSectionStyleRule("heading");
- return ;
+ return (
+
+ );
};
export const Link = ({ style, ...props }: ComponentProps) => {
const linkStyle = useTemplateStyle("link");
+ const linkRuleStyle = useSectionStyleRule("link");
- return ;
+ return ;
};
export const Small = ({ style, ...props }: ComponentProps) => {
const textStyle = useTemplateStyle("text");
const smallStyle = useTemplateStyle("small");
+ const secondaryTextRuleStyle = useSectionStyleRule("secondaryText");
- return ;
+ return (
+
+ );
};
export const Bold = ({ style, ...props }: ComponentProps) => {
const textStyle = useTemplateStyle("text");
const boldStyle = useTemplateStyle("bold");
+ const textRuleStyle = useSectionStyleRule("text");
- return ;
+ return (
+
+ );
};
export const Icon = ({ style, ...props }: ComponentProps) => {
const { style: iconStyle, ...iconProps } = useTemplateIconSlot("icon");
+ const iconRuleStyle = useSectionStyleRule("icon");
if (iconProps.display === "none") return null;
- return ;
+ return (
+
+ );
};
diff --git a/packages/pdf/src/templates/shared/rich-text-html.test.ts b/packages/pdf/src/templates/shared/rich-text-html.test.ts
index 69eaf6fb0..cb2df64ef 100644
--- a/packages/pdf/src/templates/shared/rich-text-html.test.ts
+++ b/packages/pdf/src/templates/shared/rich-text-html.test.ts
@@ -26,6 +26,12 @@ describe("normalizeRichTextHtml", () => {
expect(normalizeRichTextHtml(html)).toBe(html);
});
+ it("unwraps single paragraph wrappers inside list items", () => {
+ expect(normalizeRichTextHtml("")).toBe(
+ "",
+ );
+ });
+
it("flushes accumulated inlines before block-level tags", () => {
expect(normalizeRichTextHtml("loose")).toBe("loose
");
});
diff --git a/packages/pdf/src/templates/shared/rich-text-html.ts b/packages/pdf/src/templates/shared/rich-text-html.ts
index d9649941c..8ce822c0d 100644
--- a/packages/pdf/src/templates/shared/rich-text-html.ts
+++ b/packages/pdf/src/templates/shared/rich-text-html.ts
@@ -1,4 +1,4 @@
-import type { Node } from "node-html-parser";
+import type { HTMLElement, Node } from "node-html-parser";
import { NodeType, parse } from "node-html-parser";
export const richTextMarkClassName = "rr-pdf-mark";
@@ -50,6 +50,23 @@ const normalizeMarkElements = (root: ReturnType) => {
}
};
+const isMeaningfulNode = (node: Node): boolean =>
+ node.nodeType !== NodeType.TEXT_NODE || node.toString().trim().length > 0;
+
+const isElement = (node: Node): node is HTMLElement => node.nodeType === NodeType.ELEMENT_NODE;
+
+const unwrapSingleParagraphListItems = (root: ReturnType) => {
+ for (const listItem of root.querySelectorAll("li")) {
+ const meaningfulChildren = listItem.childNodes.filter(isMeaningfulNode);
+ if (meaningfulChildren.length !== 1) continue;
+
+ const child = meaningfulChildren[0];
+ if (!child || !isElement(child) || getTagName(child) !== "p") continue;
+
+ listItem.innerHTML = child.innerHTML;
+ }
+};
+
const isInlineNode = (node: Node): boolean => {
if (node.nodeType === NodeType.TEXT_NODE || node.nodeType === NodeType.COMMENT_NODE) return true;
if (node.nodeType !== NodeType.ELEMENT_NODE) return false;
@@ -98,6 +115,7 @@ export const normalizeRichTextHtml = (html: string): string => {
let inlineNodes: string[] = [];
normalizeMarkElements(root);
+ unwrapSingleParagraphListItems(root);
const flushInlineNodes = () => {
const inlineHtml = inlineNodes.join("").trim();
diff --git a/packages/pdf/src/templates/shared/rich-text-stylesheet.ts b/packages/pdf/src/templates/shared/rich-text-stylesheet.ts
new file mode 100644
index 000000000..6b02da64f
--- /dev/null
+++ b/packages/pdf/src/templates/shared/rich-text-stylesheet.ts
@@ -0,0 +1,52 @@
+import type { Style } from "@react-pdf/types";
+import type { StyleInput } from "./styles";
+import { richTextMarkClassName } from "./rich-text-html";
+import { safeTextStyle } from "./safe-text-style";
+import { mergeLinkStyles, mergeStyles } from "./styles";
+
+type RichTextProseSpacing = {
+ paragraph: Style;
+ listItem: Style;
+};
+
+type CreateRichTextStylesheetOptions = {
+ boldStyle?: StyleInput;
+ linkStyle?: StyleInput;
+ richParagraphStyle?: StyleInput;
+ richParagraphRuleStyle?: StyleInput;
+ richListRuleStyle?: StyleInput;
+ richBoldRuleStyle?: StyleInput;
+ richLinkRuleStyle?: StyleInput;
+ richMarkRuleStyle?: StyleInput;
+ proseSpacing?: RichTextProseSpacing;
+};
+
+const richMarkStyle = {
+ backgroundColor: "#ffff00",
+} satisfies Style;
+
+const emptyProseSpacing: RichTextProseSpacing = {
+ paragraph: {},
+ listItem: {},
+};
+
+export const createRichTextStylesheet = ({
+ boldStyle,
+ linkStyle,
+ richParagraphStyle,
+ richParagraphRuleStyle,
+ richListRuleStyle,
+ richBoldRuleStyle,
+ richLinkRuleStyle,
+ richMarkRuleStyle,
+ proseSpacing = emptyProseSpacing,
+}: CreateRichTextStylesheetOptions = {}) => ({
+ b: mergeStyles(boldStyle, richBoldRuleStyle, safeTextStyle),
+ strong: mergeStyles(boldStyle, richBoldRuleStyle, safeTextStyle),
+ ul: mergeStyles(richListRuleStyle),
+ ol: mergeStyles(richListRuleStyle),
+ li: mergeStyles(proseSpacing.listItem),
+ [`.${richTextMarkClassName}`]: mergeStyles(richMarkStyle, richMarkRuleStyle, safeTextStyle),
+ p: mergeStyles(richParagraphStyle, richParagraphRuleStyle, safeTextStyle, proseSpacing.paragraph),
+ a: mergeLinkStyles(linkStyle, richLinkRuleStyle, safeTextStyle),
+});
diff --git a/packages/pdf/src/templates/shared/rich-text.test.ts b/packages/pdf/src/templates/shared/rich-text.test.ts
index 849861487..692fc9e62 100644
--- a/packages/pdf/src/templates/shared/rich-text.test.ts
+++ b/packages/pdf/src/templates/shared/rich-text.test.ts
@@ -5,6 +5,7 @@ import { parse } from "node-html-parser";
import { createElement } from "react";
import { normalizeRichTextHtml } from "./rich-text-html";
import { renderRichTextParagraph } from "./rich-text-renderers";
+import { createRichTextStylesheet } from "./rich-text-stylesheet";
type PdfElement = ReactElement<{ children?: unknown; style?: unknown }>;
@@ -22,13 +23,13 @@ describe("normalizeRichTextHtml", () => {
it("preserves existing block rich text", () => {
expect(normalizeRichTextHtml("Existing paragraph.
")).toBe(
- "Existing paragraph.
",
+ "Existing paragraph.
",
);
});
it("wraps inline runs around top-level blocks", () => {
expect(normalizeRichTextHtml("Intro textOutro")).toBe(
- "Intro text
Outro
",
+ "Intro text
Outro
",
);
});
});
@@ -50,3 +51,19 @@ describe("renderRichTextParagraph", () => {
expect(props.children).toEqual(["Plain ", expect.any(Object), " text"]);
});
});
+
+describe("createRichTextStylesheet", () => {
+ it("applies list style rules to unordered and ordered list containers", () => {
+ const stylesheet = createRichTextStylesheet({
+ richListRuleStyle: { rowGap: 8 },
+ proseSpacing: {
+ paragraph: { marginTop: 12, marginBottom: 12 },
+ listItem: { marginTop: 2, marginBottom: 2 },
+ },
+ });
+
+ expect(stylesheet.ul).toEqual({ rowGap: 8 });
+ expect(stylesheet.ol).toEqual({ rowGap: 8 });
+ expect(stylesheet.li).toEqual({ marginTop: 2, marginBottom: 2 });
+ });
+});
diff --git a/packages/pdf/src/templates/shared/rich-text.tsx b/packages/pdf/src/templates/shared/rich-text.tsx
index 6a504668b..2947ca3fa 100644
--- a/packages/pdf/src/templates/shared/rich-text.tsx
+++ b/packages/pdf/src/templates/shared/rich-text.tsx
@@ -4,8 +4,8 @@ import { cloneElement, isValidElement } from "react";
import { Html } from "react-pdf-html";
import { useRender } from "../../context";
import { Text as PdfText, View } from "../../renderer";
-import { useTemplateStyle } from "./context";
-import { convertPseudoBulletParagraphs, normalizeRichTextHtml, richTextMarkClassName } from "./rich-text-html";
+import { useSectionStyleRule, useTemplateStyle } from "./context";
+import { convertPseudoBulletParagraphs, normalizeRichTextHtml } from "./rich-text-html";
import { renderRichTextParagraph, toRichTextStyleArray } from "./rich-text-renderers";
import {
createRichTextProseSpacing,
@@ -14,17 +14,14 @@ import {
resolveRichTextBodyLineHeight,
stripRichTextVerticalMargins,
} from "./rich-text-spacing";
+import { createRichTextStylesheet } from "./rich-text-stylesheet";
import { safeTextStyle } from "./safe-text-style";
-import { composeStyles, mergeLinkStyles, mergeStyles } from "./styles";
+import { composeStyles } from "./styles";
const richListItemContentStackStyle = {
flexDirection: "column",
} satisfies Style;
-const richMarkStyle = {
- backgroundColor: "#ffff00",
-} satisfies Style;
-
// react-pdf textkit reads BiDi base direction from each run's own `direction` attribute
// (default "ltr"), and react-pdf-html buckets inline content into styleless inner
// frames — so the rtl style has to be injected onto every descendant, not just a wrapper.
@@ -65,7 +62,19 @@ export const RichText = ({ children }: { children: string }) => {
const richListItemRowStyle = useTemplateStyle("richListItemRow");
const richListItemMarkerStyle = useTemplateStyle("richListItemMarker");
const richListItemContentStyle = useTemplateStyle("richListItemContent");
- const bodyLineHeight = resolveRichTextBodyLineHeight(richParagraphStyle, richListItemContentStyle);
+ const richParagraphRuleStyle = useSectionStyleRule("richParagraph");
+ const richListRuleStyle = useSectionStyleRule("richList");
+ const richListItemRowRuleStyle = useSectionStyleRule("richListItemRow");
+ const richListItemContentRuleStyle = useSectionStyleRule("richListItemContent");
+ const richLinkRuleStyle = useSectionStyleRule("richLink");
+ const richBoldRuleStyle = useSectionStyleRule("richBold");
+ const richMarkRuleStyle = useSectionStyleRule("richMark");
+ const bodyLineHeight = resolveRichTextBodyLineHeight(
+ richParagraphStyle,
+ richParagraphRuleStyle,
+ richListItemContentStyle,
+ richListItemContentRuleStyle,
+ );
const proseSpacing = createRichTextProseSpacing(bodyLineHeight);
const normalized = normalizeRichTextHtml(children);
@@ -85,7 +94,9 @@ export const RichText = ({ children }: { children: string }) => {
{children},
+ b: ({ children }) => (
+ {children}
+ ),
p: (props) => {
const paragraphProps = {
...props,
@@ -111,7 +122,13 @@ export const RichText = ({ children }: { children: string }) => {
const contentNode = rtl ? (
{applyRtlDirectionRecursively(children)}
@@ -120,6 +137,7 @@ export const RichText = ({ children }: { children: string }) => {
key="content"
style={composeStyles(
richListItemContentStyle,
+ richListItemContentRuleStyle,
contentItemStyles,
richListItemContentStackStyle,
safeTextStyle,
@@ -132,20 +150,30 @@ export const RichText = ({ children }: { children: string }) => {
// Yoga ignores `flexDirection`/`direction` on rows inside react-pdf-html's
// (works fine for split-row/contact-list). Swap DOM order to position the marker.
return (
-
+
{rtl ? [contentNode, markerNode] : [markerNode, contentNode]}
);
},
}}
- stylesheet={{
- b: mergeStyles(boldStyle, safeTextStyle),
- strong: mergeStyles(boldStyle, safeTextStyle),
- li: mergeStyles(proseSpacing.listItem),
- [`.${richTextMarkClassName}`]: mergeStyles(richMarkStyle, safeTextStyle),
- p: mergeStyles(richParagraphStyle, safeTextStyle, proseSpacing.paragraph),
- a: mergeLinkStyles(linkStyle, safeTextStyle),
- }}
+ stylesheet={createRichTextStylesheet({
+ boldStyle,
+ linkStyle,
+ richParagraphStyle,
+ richParagraphRuleStyle,
+ richListRuleStyle,
+ richBoldRuleStyle,
+ richLinkRuleStyle,
+ richMarkRuleStyle,
+ proseSpacing,
+ })}
>
{html}
diff --git a/packages/pdf/src/templates/shared/sections.tsx b/packages/pdf/src/templates/shared/sections.tsx
index 245f0ade3..cd2a767dc 100644
--- a/packages/pdf/src/templates/shared/sections.tsx
+++ b/packages/pdf/src/templates/shared/sections.tsx
@@ -27,7 +27,9 @@ import { getResumeSectionTitle } from "../../section-title";
import { getSectionItemRows, getSectionItemsLayout, shouldUseSectionTimeline } from "./columns";
import { getWebsiteDisplayText } from "./contact";
import {
+ SectionStyleProvider,
TemplatePlacementProvider,
+ useSectionStyleRule,
useTemplateFeature,
useTemplateFeatureStyle,
useTemplatePlacement,
@@ -41,6 +43,7 @@ import { RichText } from "./rich-text";
import { createRtlStyleHelpers } from "./rtl";
import { getInlineItemWebsiteUrl, shouldRenderSeparateItemWebsite } from "./section-links";
import { hasSplitRowText, promoteSplitRowRight } from "./split-row";
+import { getSectionStyleRuleContext } from "./style-rules";
import { composeStyles } from "./styles";
type SectionItemsContextValue = {
@@ -88,12 +91,16 @@ const SectionShell = ({
}) => {
const data = useRender();
const sectionStyle = useTemplateStyle("section");
+ const sectionRuleStyle = useSectionStyleRule("section");
const sectionHeadingStyle = useTemplateStyle("sectionHeading");
+ const sectionHeadingRuleStyle = useSectionStyleRule("heading");
const sectionTitle = getResumeSectionTitle(data, sectionId, title);
return (
-
- {showHeading && {sectionTitle}}
+
+ {showHeading && (
+ {sectionTitle}
+ )}
{children}
);
@@ -160,13 +167,14 @@ const SectionItems = ({ children, columns = 1 }: { children: ReactNode; columns?
const SectionItem = ({ children, style }: { children: ReactNode; style?: StyleInput }) => {
const { itemStyle: sectionItemStyle, useTimeline } = useSectionItemsContext();
const itemStyle = useTemplateStyle("item");
+ const itemRuleStyle = useSectionStyleRule("item");
const timelineItemStyle = useTemplateFeatureStyle("sectionTimeline", "item");
const timelineMarkerStyle = useTemplateFeatureStyle("sectionTimeline", "marker");
const timelineDotStyle = useTemplateFeatureStyle("sectionTimeline", "dot");
const timelineContentStyle = useTemplateFeatureStyle("sectionTimeline", "content");
if (!useTimeline) {
- return {children}
;
+ return {children}
;
}
return (
@@ -174,7 +182,7 @@ const SectionItem = ({ children, style }: { children: ReactNode; style?: StyleIn
- {children}
+ {children}
);
};
@@ -924,23 +932,25 @@ export const Section = ({
return (
- {match(section)
- .with("summary", () => )
- .with("profiles", () => )
- .with("experience", () => )
- .with("education", () => )
- .with("projects", () => )
- .with("skills", () => )
- .with("languages", () => )
- .with("interests", () => )
- .with("awards", () => )
- .with("certifications", () => )
- .with("publications", () => )
- .with("volunteer", () => )
- .with("references", () => )
- .otherwise(() => (
-
- ))}
+
+ {match(section)
+ .with("summary", () => )
+ .with("profiles", () => )
+ .with("experience", () => )
+ .with("education", () => )
+ .with("projects", () => )
+ .with("skills", () => )
+ .with("languages", () => )
+ .with("interests", () => )
+ .with("awards", () => )
+ .with("certifications", () => )
+ .with("publications", () => )
+ .with("volunteer", () => )
+ .with("references", () => )
+ .otherwise(() => (
+
+ ))}
+
);
};
diff --git a/packages/pdf/src/templates/shared/style-rules.test.ts b/packages/pdf/src/templates/shared/style-rules.test.ts
new file mode 100644
index 000000000..386aef12b
--- /dev/null
+++ b/packages/pdf/src/templates/shared/style-rules.test.ts
@@ -0,0 +1,172 @@
+import type { ResumeData } from "@reactive-resume/schema/resume/data";
+import { describe, expect, it } from "vitest";
+import { defaultResumeData } from "@reactive-resume/schema/resume/default";
+import { getSectionStyleRuleContext, resolveStyleRuleSlot } from "./style-rules";
+
+const createResumeData = (styleRules: ResumeData["metadata"]["styleRules"]): ResumeData => ({
+ ...defaultResumeData,
+ metadata: {
+ ...defaultResumeData.metadata,
+ styleRules,
+ },
+});
+
+describe("resolveStyleRuleSlot", () => {
+ it("applies global, section-type, and section-id rules in specificity order", () => {
+ const data = createResumeData([
+ {
+ id: "global-heading",
+ label: "Global Heading",
+ enabled: true,
+ target: { scope: "global" },
+ slots: { heading: { color: "rgba(0, 0, 0, 1)", fontSize: 12 } },
+ },
+ {
+ id: "experience-heading",
+ label: "Experience Heading",
+ enabled: true,
+ target: { scope: "sectionType", sectionType: "experience" },
+ slots: { heading: { color: "rgba(220, 38, 38, 1)" } },
+ },
+ {
+ id: "specific-heading",
+ label: "Specific Heading",
+ enabled: true,
+ target: { scope: "sectionId", sectionId: "experience" },
+ slots: { heading: { fontSize: 15 } },
+ },
+ ]);
+
+ expect(
+ resolveStyleRuleSlot(data, {
+ sectionId: "experience",
+ sectionType: "experience",
+ slot: "heading",
+ }),
+ ).toEqual({ color: "#dc2626", fontSize: 15 });
+ });
+
+ it("ignores disabled rules and non-matching deleted section ids", () => {
+ const data = createResumeData([
+ {
+ id: "disabled",
+ label: "Disabled",
+ enabled: false,
+ target: { scope: "global" },
+ slots: { section: { backgroundColor: "rgba(0, 0, 0, 1)" } },
+ },
+ {
+ id: "missing",
+ label: "Missing",
+ enabled: true,
+ target: { scope: "sectionId", sectionId: "missing-section" },
+ slots: { section: { backgroundColor: "rgba(220, 38, 38, 1)" } },
+ },
+ ]);
+
+ expect(
+ resolveStyleRuleSlot(data, {
+ sectionId: "experience",
+ sectionType: "experience",
+ slot: "section",
+ }),
+ ).toEqual({});
+ });
+
+ it("resolves custom section types for section-type rules", () => {
+ const data = createResumeData([
+ {
+ id: "custom-summary-rich-text",
+ label: "Custom Summary Rich Text",
+ enabled: true,
+ target: { scope: "sectionType", sectionType: "summary" },
+ slots: { richParagraph: { color: "rgba(21, 93, 252, 1)" } },
+ },
+ ]);
+ const customSection = {
+ ...defaultResumeData.summary,
+ id: "custom-summary",
+ type: "summary" as const,
+ items: [],
+ };
+ const customData = { ...data, customSections: [customSection] };
+
+ expect(getSectionStyleRuleContext(customData, "custom-summary")).toEqual({
+ sectionId: "custom-summary",
+ sectionType: "summary",
+ });
+ expect(
+ resolveStyleRuleSlot(customData, {
+ sectionId: "custom-summary",
+ sectionType: "summary",
+ slot: "richParagraph",
+ }),
+ ).toEqual({ color: "#155dfc" });
+ });
+
+ it("clamps unsafe spacing values before returning React PDF styles", () => {
+ const data = createResumeData([
+ {
+ id: "spacing",
+ label: "Spacing",
+ enabled: true,
+ target: { scope: "global" },
+ slots: { item: { padding: 1000, marginTop: -1000, rowGap: -12, borderWidth: -5 } },
+ },
+ ]);
+
+ expect(
+ resolveStyleRuleSlot(data, {
+ sectionId: "skills",
+ sectionType: "skills",
+ slot: "item",
+ }),
+ ).toEqual({ padding: 72, marginTop: -72, rowGap: -12, borderWidth: 0 });
+ });
+
+ it("resolves visible text, opacity, and border style properties", () => {
+ const data = createResumeData([
+ {
+ id: "rich-link-style",
+ label: "Rich Link Style",
+ enabled: true,
+ target: { scope: "global" },
+ slots: {
+ richLink: {
+ color: "rgba(21, 93, 252, 1)",
+ textDecoration: "underline",
+ textDecorationColor: "rgba(220, 38, 38, 1)",
+ textDecorationStyle: "dashed",
+ fontStyle: "italic",
+ lineHeight: 9,
+ letterSpacing: -100,
+ textAlign: "right",
+ textTransform: "uppercase",
+ opacity: 2,
+ borderStyle: "dotted",
+ },
+ },
+ },
+ ]);
+
+ expect(
+ resolveStyleRuleSlot(data, {
+ sectionId: "experience",
+ sectionType: "experience",
+ slot: "richLink",
+ }),
+ ).toEqual({
+ color: "#155dfc",
+ textDecoration: "underline",
+ textDecorationColor: "#dc2626",
+ textDecorationStyle: "dashed",
+ fontStyle: "italic",
+ lineHeight: 4,
+ letterSpacing: -16,
+ textAlign: "right",
+ textTransform: "uppercase",
+ opacity: 1,
+ borderStyle: "dotted",
+ });
+ });
+});
diff --git a/packages/pdf/src/templates/shared/style-rules.ts b/packages/pdf/src/templates/shared/style-rules.ts
new file mode 100644
index 000000000..1d946b65f
--- /dev/null
+++ b/packages/pdf/src/templates/shared/style-rules.ts
@@ -0,0 +1,134 @@
+import type { Style } from "@react-pdf/types";
+import type {
+ CustomSectionType,
+ ResumeData,
+ SectionType,
+ StyleIntent,
+ StyleSlot,
+} from "@reactive-resume/schema/resume/data";
+import { rgbaStringToHex } from "@reactive-resume/utils/color";
+
+export type SectionStyleRuleContext = {
+ sectionId: string;
+ sectionType?: CustomSectionType | undefined;
+};
+
+export type ResolveStyleRuleSlotOptions = SectionStyleRuleContext & {
+ slot: StyleSlot;
+};
+
+const spacingProperties = [
+ "padding",
+ "paddingTop",
+ "paddingRight",
+ "paddingBottom",
+ "paddingLeft",
+ "marginTop",
+ "marginRight",
+ "marginBottom",
+ "marginLeft",
+ "rowGap",
+ "columnGap",
+ "borderWidth",
+ "borderRadius",
+] as const;
+
+const colorProperties = ["color", "backgroundColor", "borderColor", "textDecorationColor"] as const;
+
+const spacingPropertyRange = (property: (typeof spacingProperties)[number]) => {
+ if (property === "borderWidth" || property === "borderRadius")
+ return { min: 0, max: property === "borderWidth" ? 24 : 72 };
+
+ return { min: -72, max: 72 };
+};
+
+const builtInSectionTypes = new Set([
+ "profiles",
+ "experience",
+ "education",
+ "projects",
+ "skills",
+ "languages",
+ "interests",
+ "awards",
+ "certifications",
+ "publications",
+ "volunteer",
+ "references",
+]);
+
+const clamp = (value: number, min: number, max: number) => Math.min(max, Math.max(min, value));
+
+const toPdfColor = (value: string) => {
+ try {
+ return rgbaStringToHex(value);
+ } catch {
+ return value;
+ }
+};
+
+const toStyle = (intent: StyleIntent | undefined): Style => {
+ if (!intent) return {};
+
+ const style: Style = {};
+
+ for (const property of colorProperties) {
+ const value = intent[property];
+ if (value) style[property] = toPdfColor(value);
+ }
+
+ if (intent.opacity !== undefined) style.opacity = clamp(intent.opacity, 0, 1);
+ if (intent.fontSize !== undefined) style.fontSize = clamp(intent.fontSize, 6, 48);
+ if (intent.fontWeight !== undefined) style.fontWeight = intent.fontWeight;
+ if (intent.fontStyle !== undefined) style.fontStyle = intent.fontStyle;
+ if (intent.lineHeight !== undefined) style.lineHeight = clamp(intent.lineHeight, 0.5, 4);
+ if (intent.letterSpacing !== undefined) style.letterSpacing = clamp(intent.letterSpacing, -16, 16);
+ if (intent.textDecoration !== undefined) style.textDecoration = intent.textDecoration;
+ if (intent.textDecorationStyle !== undefined) style.textDecorationStyle = intent.textDecorationStyle;
+ if (intent.textAlign !== undefined) style.textAlign = intent.textAlign;
+ if (intent.textTransform !== undefined) style.textTransform = intent.textTransform;
+ if (intent.borderStyle !== undefined) style.borderStyle = intent.borderStyle;
+
+ for (const property of spacingProperties) {
+ const value = intent[property];
+ const range = spacingPropertyRange(property);
+ if (value !== undefined) style[property] = clamp(value, range.min, range.max);
+ }
+
+ return style;
+};
+
+export const getSectionStyleRuleContext = (data: ResumeData, sectionId: string): SectionStyleRuleContext => {
+ if (sectionId === "summary") return { sectionId, sectionType: "summary" };
+ if (builtInSectionTypes.has(sectionId as SectionType)) {
+ return { sectionId, sectionType: sectionId as SectionType };
+ }
+
+ const customSection = data.customSections.find((section) => section.id === sectionId);
+
+ return { sectionId, sectionType: customSection?.type };
+};
+
+export const resolveStyleRuleSlot = (data: ResumeData, options: ResolveStyleRuleSlotOptions): Style => {
+ const matchingRules = (data.metadata.styleRules ?? []).filter((rule) => {
+ if (!rule.enabled) return false;
+ if (!rule.slots[options.slot]) return false;
+
+ if (rule.target.scope === "global") return true;
+ if (rule.target.scope === "sectionType") return rule.target.sectionType === options.sectionType;
+ if (rule.target.scope === "sectionId") return rule.target.sectionId === options.sectionId;
+
+ return false;
+ });
+
+ const specificity = { global: 0, sectionType: 1, sectionId: 2 } satisfies Record<
+ "global" | "sectionType" | "sectionId",
+ number
+ >;
+
+ const bySpecificity = [...matchingRules].sort((a, b) => {
+ return specificity[a.target.scope] - specificity[b.target.scope];
+ });
+
+ return Object.assign({}, ...bySpecificity.map((rule) => toStyle(rule.slots[options.slot])));
+};
diff --git a/packages/resume/package.json b/packages/resume/package.json
index 8f69c69c8..d7df3d5c1 100644
--- a/packages/resume/package.json
+++ b/packages/resume/package.json
@@ -21,7 +21,7 @@
},
"devDependencies": {
"@reactive-resume/config": "workspace:*",
- "@typescript/native-preview": "7.0.0-dev.20260526.1",
+ "@typescript/native-preview": "7.0.0-dev.20260527.1",
"typescript": "^6.0.3"
}
}
diff --git a/packages/schema/package.json b/packages/schema/package.json
index 29884600c..ee6705666 100644
--- a/packages/schema/package.json
+++ b/packages/schema/package.json
@@ -22,7 +22,7 @@
},
"devDependencies": {
"@reactive-resume/config": "workspace:*",
- "@typescript/native-preview": "7.0.0-dev.20260526.1",
+ "@typescript/native-preview": "7.0.0-dev.20260527.1",
"typescript": "^6.0.3"
}
}
diff --git a/packages/schema/src/resume/data.test.ts b/packages/schema/src/resume/data.test.ts
index 04eb59579..ba05ff083 100644
--- a/packages/schema/src/resume/data.test.ts
+++ b/packages/schema/src/resume/data.test.ts
@@ -8,6 +8,8 @@ import {
pictureSchema,
resumeDataSchema,
skillItemSchema,
+ styleRuleSchema,
+ styleRulesSchema,
websiteSchema,
} from "./data";
import { defaultResumeData } from "./default";
@@ -270,3 +272,108 @@ describe("pageSchema", () => {
}
});
});
+
+describe("styleRulesSchema", () => {
+ it("defaults to an empty rule list on default resume metadata", () => {
+ expect(defaultResumeData.metadata.styleRules).toEqual([]);
+ expect(styleRulesSchema.safeParse(defaultResumeData.metadata.styleRules).success).toBe(true);
+ });
+
+ it("accepts global, section-type, and section-id targets", () => {
+ const rules = [
+ {
+ id: "global-headings",
+ label: "Global headings",
+ enabled: true,
+ target: { scope: "global" },
+ slots: {
+ heading: {
+ color: "rgba(220, 38, 38, 1)",
+ fontWeight: "700",
+ fontStyle: "italic",
+ lineHeight: 1.35,
+ letterSpacing: -0.5,
+ textDecoration: "underline",
+ textDecorationColor: "rgba(220, 38, 38, 1)",
+ textDecorationStyle: "dotted",
+ textAlign: "center",
+ textTransform: "uppercase",
+ opacity: 0.85,
+ },
+ },
+ },
+ {
+ id: "experience-items",
+ label: "Experience items",
+ enabled: true,
+ target: { scope: "sectionType", sectionType: "experience" },
+ slots: {
+ item: {
+ backgroundColor: "rgba(245, 245, 245, 1)",
+ padding: -6,
+ marginBottom: -4,
+ rowGap: -2,
+ borderStyle: "dashed",
+ },
+ },
+ },
+ {
+ id: "custom-section",
+ label: "Custom section",
+ enabled: true,
+ target: { scope: "sectionId", sectionId: "94ddf90f-46ef-4b0a-9a99-2ed118af52dd" },
+ slots: { richList: { rowGap: 6 } },
+ },
+ ];
+
+ expect(styleRulesSchema.safeParse(rules).success).toBe(true);
+ });
+
+ it("rejects section-item targets for v1", () => {
+ expect(
+ styleRuleSchema.safeParse({
+ id: "item-style",
+ label: "Item Style",
+ enabled: true,
+ target: { scope: "sectionItem", sectionId: "experience", itemId: "item-1" },
+ slots: { item: { color: "rgba(0, 0, 0, 1)" } },
+ }).success,
+ ).toBe(false);
+ });
+
+ it("rejects unsupported raw layout properties", () => {
+ expect(
+ styleRuleSchema.safeParse({
+ id: "unsafe",
+ label: "Unsafe",
+ enabled: true,
+ target: { scope: "global" },
+ slots: { section: { position: "absolute" } },
+ }).success,
+ ).toBe(false);
+ });
+
+ it("rejects the removed rich-text marker slot", () => {
+ expect(
+ styleRuleSchema.safeParse({
+ id: "marker",
+ label: "Marker",
+ enabled: true,
+ target: { scope: "global" },
+ slots: { richListItemMarker: { color: "rgba(21, 93, 252, 1)" } },
+ }).success,
+ ).toBe(false);
+ });
+
+ it("rejects unsafe visible style values outside the v1 ranges", () => {
+ expect(
+ styleRuleSchema.safeParse({
+ id: "unsafe-visible",
+ label: "Unsafe Visible",
+ enabled: true,
+ target: { scope: "global" },
+ slots: { heading: { opacity: 2, lineHeight: 10, letterSpacing: -17 } },
+ }).success,
+ ).toBe(false);
+ });
+});
diff --git a/packages/schema/src/resume/data.ts b/packages/schema/src/resume/data.ts
index 57f18d265..d0eba2e48 100644
--- a/packages/schema/src/resume/data.ts
+++ b/packages/schema/src/resume/data.ts
@@ -491,6 +491,102 @@ export const typographySchema = z.object({
heading: typographyItemSchema.describe("The typography for the headings of the resume."),
});
+export const styleSlotSchema = z.enum([
+ "section",
+ "heading",
+ "item",
+ "text",
+ "secondaryText",
+ "link",
+ "icon",
+ "level",
+ "richParagraph",
+ "richList",
+ "richListItemRow",
+ "richListItemContent",
+ "richLink",
+ "richBold",
+ "richMark",
+]);
+
+export type StyleSlot = z.infer;
+
+export const styleIntentSchema = z
+ .strictObject({
+ color: z.string().optional(),
+ backgroundColor: z.string().optional(),
+ borderColor: z.string().optional(),
+ textDecorationColor: z.string().optional(),
+ opacity: z.number().min(0).max(1).optional(),
+ fontSize: z.number().min(6).max(48).optional(),
+ fontWeight: fontWeightSchema.optional(),
+ fontStyle: z.enum(["normal", "italic"]).optional(),
+ lineHeight: z.number().min(0.5).max(4).optional(),
+ letterSpacing: z.number().min(-16).max(16).optional(),
+ textDecoration: z.enum(["none", "underline", "line-through"]).optional(),
+ textDecorationStyle: z.enum(["solid", "dashed", "dotted"]).optional(),
+ textAlign: z.enum(["left", "center", "right", "justify"]).optional(),
+ textTransform: z.enum(["none", "uppercase", "lowercase", "capitalize"]).optional(),
+ padding: z.number().min(-72).max(72).optional(),
+ paddingTop: z.number().min(-72).max(72).optional(),
+ paddingRight: z.number().min(-72).max(72).optional(),
+ paddingBottom: z.number().min(-72).max(72).optional(),
+ paddingLeft: z.number().min(-72).max(72).optional(),
+ marginTop: z.number().min(-72).max(72).optional(),
+ marginRight: z.number().min(-72).max(72).optional(),
+ marginBottom: z.number().min(-72).max(72).optional(),
+ marginLeft: z.number().min(-72).max(72).optional(),
+ rowGap: z.number().min(-72).max(72).optional(),
+ columnGap: z.number().min(-72).max(72).optional(),
+ borderStyle: z.enum(["solid", "dashed", "dotted"]).optional(),
+ borderWidth: z.number().min(0).optional(),
+ borderRadius: z.number().min(0).optional(),
+ })
+ .describe("Constrained visual style intent that can be safely translated to React PDF styles.");
+
+export type StyleIntent = z.infer;
+
+export const styleRuleSlotsSchema = z
+ .strictObject({
+ section: styleIntentSchema.optional(),
+ heading: styleIntentSchema.optional(),
+ item: styleIntentSchema.optional(),
+ text: styleIntentSchema.optional(),
+ secondaryText: styleIntentSchema.optional(),
+ link: styleIntentSchema.optional(),
+ icon: styleIntentSchema.optional(),
+ level: styleIntentSchema.optional(),
+ richParagraph: styleIntentSchema.optional(),
+ richList: styleIntentSchema.optional(),
+ richListItemRow: styleIntentSchema.optional(),
+ richListItemContent: styleIntentSchema.optional(),
+ richLink: styleIntentSchema.optional(),
+ richBold: styleIntentSchema.optional(),
+ richMark: styleIntentSchema.optional(),
+ })
+ .refine((slots) => Object.values(slots).some(Boolean), {
+ message: "At least one style slot must be configured.",
+ });
+
+export const styleRuleTargetSchema = z.discriminatedUnion("scope", [
+ z.strictObject({ scope: z.literal("global") }),
+ z.strictObject({ scope: z.literal("sectionType"), sectionType: sectionTypeSchema }),
+ z.strictObject({ scope: z.literal("sectionId"), sectionId: z.string().min(1) }),
+]);
+
+export const styleRuleSchema = z.strictObject({
+ id: z.string().min(1).describe("Unique identifier for this style rule."),
+ label: z.string().catch("").describe("Human-readable label for this style rule."),
+ enabled: z.boolean().catch(true).describe("Whether this style rule should affect PDF rendering."),
+ target: styleRuleTargetSchema.describe("The resume content this style rule applies to."),
+ slots: styleRuleSlotsSchema.describe("The semantic style slots configured by this rule."),
+});
+
+export const styleRulesSchema = z.array(styleRuleSchema).catch([]);
+
+export type StyleRule = z.infer;
+export type StyleRuleTarget = z.infer;
+
export const metadataSchema = z.object({
template: templateSchema
.catch("onyx")
@@ -512,6 +608,9 @@ export const metadataSchema = z.object({
.describe(
"Personal notes for the resume. Can be used to add any additional information or instructions for the resume. These notes are not displayed on the resume, they are only visible to the author of the resume when editing the resume. This should be a HTML-formatted string.",
),
+ styleRules: styleRulesSchema.describe(
+ "Structured style rules that target semantic resume sections and slots for React PDF rendering.",
+ ),
});
export const resumeDataSchema = z.looseObject({
diff --git a/packages/schema/src/resume/default.ts b/packages/schema/src/resume/default.ts
index e0e00c14e..84de0d7b4 100644
--- a/packages/schema/src/resume/default.ts
+++ b/packages/schema/src/resume/default.ts
@@ -150,5 +150,6 @@ export const defaultResumeData: ResumeData = {
},
},
notes: "",
+ styleRules: [],
},
};
diff --git a/packages/schema/src/resume/sample.ts b/packages/schema/src/resume/sample.ts
index 1280d9f28..4f252cbf5 100644
--- a/packages/schema/src/resume/sample.ts
+++ b/packages/schema/src/resume/sample.ts
@@ -580,5 +580,6 @@ export const sampleResumeData: ResumeData = {
},
},
notes: "",
+ styleRules: [],
},
};
diff --git a/packages/ui/package.json b/packages/ui/package.json
index 2998874c3..78b60598c 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -31,7 +31,7 @@
"react": "^19.2.6",
"react-dom": "^19.2.6",
"react-resizable-panels": "^4.11.2",
- "shadcn": "^4.8.0",
+ "shadcn": "^4.8.1",
"sonner": "^2.0.7",
"tw-animate-css": "^1.4.0"
},
@@ -42,9 +42,9 @@
"@types/js-cookie": "^3.0.6",
"@types/react": "^19.2.15",
"@types/react-dom": "^19.2.3",
- "@typescript/native-preview": "7.0.0-dev.20260526.1",
+ "@typescript/native-preview": "7.0.0-dev.20260527.1",
"postcss": "^8.5.15",
- "react-doctor": "^0.2.6",
+ "react-doctor": "^0.2.9",
"tailwindcss": "^4.3.0",
"typescript": "^6.0.3"
}
diff --git a/packages/utils/package.json b/packages/utils/package.json
index 906aa59de..ceaac73ae 100644
--- a/packages/utils/package.json
+++ b/packages/utils/package.json
@@ -30,7 +30,7 @@
"@sindresorhus/slugify": "^3.0.0",
"@uiw/color-convert": "^2.10.3",
"clsx": "^2.1.1",
- "dompurify": "^3.4.5",
+ "dompurify": "^3.4.6",
"tailwind-merge": "^3.6.0",
"unique-names-generator": "^4.7.1",
"uuid": "^14.0.0",
@@ -39,7 +39,7 @@
"devDependencies": {
"@reactive-resume/config": "workspace:*",
"@types/node": "^25.9.1",
- "@typescript/native-preview": "7.0.0-dev.20260526.1",
+ "@typescript/native-preview": "7.0.0-dev.20260527.1",
"typescript": "^6.0.3"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a59ad6d51..05ac9e218 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -57,8 +57,8 @@ importers:
specifier: ^22.2.1
version: 22.2.1
turbo:
- specifier: ^2.9.14
- version: 2.9.14
+ specifier: ^2.9.15
+ version: 2.9.15
typescript:
specifier: ^6.0.3
version: 6.0.3
@@ -81,8 +81,8 @@ importers:
specifier: ^2.0.48
version: 2.0.48(zod@4.4.3)
'@aws-sdk/client-s3':
- specifier: ^3.1053.0
- version: 3.1053.0
+ specifier: ^3.1054.0
+ version: 3.1054.0
'@better-auth/api-key':
specifier: ^1.6.11
version: 1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.1)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)(better-auth@1.6.11(@opentelemetry/api@1.9.1)(drizzle-orm@1.0.0-rc.3(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(effect@4.0.0-beta.70)(pg@8.21.0)(zod@4.4.3))(next@16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(pg@8.21.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7))
@@ -90,8 +90,8 @@ importers:
specifier: ^1.6.11
version: 1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.1)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)(drizzle-orm@1.0.0-rc.3(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(effect@4.0.0-beta.70)(pg@8.21.0)(zod@4.4.3))
'@better-auth/infra':
- specifier: ^0.2.8
- version: 0.2.8(97af957868093b766f74c847443b5514)
+ specifier: ^0.2.10
+ version: 0.2.10(97af957868093b766f74c847443b5514)
'@better-auth/oauth-provider':
specifier: ^1.6.11
version: 1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.1)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-auth@1.6.11(@opentelemetry/api@1.9.1)(drizzle-orm@1.0.0-rc.3(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(effect@4.0.0-beta.70)(pg@8.21.0)(zod@4.4.3))(next@16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(pg@8.21.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7))(better-call@1.3.5(zod@4.4.3))
@@ -171,8 +171,8 @@ importers:
specifier: ^7.1.5
version: 7.1.5
dompurify:
- specifier: ^3.4.5
- version: 3.4.5
+ specifier: ^3.4.6
+ version: 3.4.6
dotenv:
specifier: ^17.4.2
version: 17.4.2
@@ -198,8 +198,8 @@ importers:
specifier: ^7.1.0
version: 7.1.0
nodemailer:
- specifier: ^8.0.8
- version: 8.0.8
+ specifier: ^8.0.9
+ version: 8.0.9
ollama-ai-provider-v2:
specifier: ^3.5.1
version: 3.5.1(ai@6.0.191(zod@4.4.3))(zod@4.4.3)
@@ -213,8 +213,8 @@ importers:
specifier: ^19.2.6
version: 19.2.6
react-email:
- specifier: ^6.3.3
- version: 6.3.3(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ specifier: ^6.4.0
+ version: 6.4.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
react-pdf-html:
specifier: ^2.1.5
version: 2.1.5(@react-pdf/renderer@4.5.1(react@19.2.6))(react@19.2.6)
@@ -250,14 +250,14 @@ importers:
specifier: ^19.2.15
version: 19.2.15
'@typescript/native-preview':
- specifier: 7.0.0-dev.20260526.1
- version: 7.0.0-dev.20260526.1
+ specifier: 7.0.0-dev.20260527.1
+ version: 7.0.0-dev.20260527.1
react-doctor:
- specifier: ^0.2.6
- version: 0.2.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
+ specifier: ^0.2.9
+ version: 0.2.9(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(eslint@10.4.0(jiti@2.7.0))
tsdown:
specifier: ^0.22.0
- version: 0.22.0(@typescript/native-preview@7.0.0-dev.20260526.1)(oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(tsx@4.22.3)(typescript@6.0.3)
+ version: 0.22.0(@typescript/native-preview@7.0.0-dev.20260527.1)(oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(tsx@4.22.3)(typescript@6.0.3)
tsx:
specifier: ^4.22.3
version: 4.22.3
@@ -280,8 +280,8 @@ importers:
specifier: ^1.6.11
version: 1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.1)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)(better-auth@1.6.11(@opentelemetry/api@1.9.1)(drizzle-orm@1.0.0-rc.3(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(effect@4.0.0-beta.70)(pg@8.21.0)(zod@4.4.3))(next@16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(pg@8.21.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7))
'@better-auth/infra':
- specifier: ^0.2.8
- version: 0.2.8(97af957868093b766f74c847443b5514)
+ specifier: ^0.2.10
+ version: 0.2.10(97af957868093b766f74c847443b5514)
'@better-auth/oauth-provider':
specifier: ^1.6.11
version: 1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.1)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-auth@1.6.11(@opentelemetry/api@1.9.1)(drizzle-orm@1.0.0-rc.3(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(effect@4.0.0-beta.70)(pg@8.21.0)(zod@4.4.3))(next@16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(pg@8.21.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7))(better-call@1.3.5(zod@4.4.3))
@@ -352,8 +352,8 @@ importers:
specifier: ^4.3.0
version: 4.3.0(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3)(yaml@2.9.0))
'@tanstack/react-form':
- specifier: ^1.32.0
- version: 1.32.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ specifier: ^1.32.1
+ version: 1.32.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@tanstack/react-hotkeys':
specifier: ^0.10.0
version: 0.10.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
@@ -503,8 +503,8 @@ importers:
specifier: ^19.2.3
version: 19.2.3(@types/react@19.2.15)
'@typescript/native-preview':
- specifier: 7.0.0-dev.20260526.1
- version: 7.0.0-dev.20260526.1
+ specifier: 7.0.0-dev.20260527.1
+ version: 7.0.0-dev.20260527.1
'@vitejs/plugin-react':
specifier: ^6.0.2
version: 6.0.2(@rolldown/plugin-babel@0.2.3(@babel/core@7.29.7)(@babel/runtime@7.29.7)(rolldown@1.0.2)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3)(yaml@2.9.0)))(babel-plugin-react-compiler@1.0.0)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.3)(yaml@2.9.0))
@@ -515,8 +515,8 @@ importers:
specifier: ^1.0.0
version: 1.0.0
react-doctor:
- specifier: ^0.2.6
- version: 0.2.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
+ specifier: ^0.2.9
+ version: 0.2.9(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(eslint@10.4.0(jiti@2.7.0))
typescript:
specifier: ^6.0.3
version: 6.0.3
@@ -549,8 +549,8 @@ importers:
specifier: workspace:*
version: link:../config
'@typescript/native-preview':
- specifier: 7.0.0-dev.20260526.1
- version: 7.0.0-dev.20260526.1
+ specifier: 7.0.0-dev.20260527.1
+ version: 7.0.0-dev.20260527.1
typescript:
specifier: ^6.0.3
version: 6.0.3
@@ -570,8 +570,8 @@ importers:
specifier: ^2.0.48
version: 2.0.48(zod@4.4.3)
'@aws-sdk/client-s3':
- specifier: ^3.1053.0
- version: 3.1053.0
+ specifier: ^3.1054.0
+ version: 3.1054.0
'@orpc/client':
specifier: ^1.14.3
version: 1.14.3(@opentelemetry/api@1.9.1)
@@ -624,8 +624,8 @@ importers:
specifier: ^1.47.0
version: 1.47.0
ioredis:
- specifier: ^5.10.1
- version: 5.10.1
+ specifier: ^5.11.0
+ version: 5.11.0
ollama-ai-provider-v2:
specifier: ^3.5.1
version: 3.5.1(ai@6.0.191(zod@4.4.3))(zod@4.4.3)
@@ -652,8 +652,8 @@ importers:
specifier: ^6.0.0
version: 6.0.0
'@typescript/native-preview':
- specifier: 7.0.0-dev.20260526.1
- version: 7.0.0-dev.20260526.1
+ specifier: 7.0.0-dev.20260527.1
+ version: 7.0.0-dev.20260527.1
typescript:
specifier: ^6.0.3
version: 6.0.3
@@ -667,8 +667,8 @@ importers:
specifier: ^1.6.11
version: 1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.1)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)(drizzle-orm@1.0.0-rc.3(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(effect@4.0.0-beta.70)(pg@8.21.0)(zod@4.4.3))
'@better-auth/infra':
- specifier: ^0.2.8
- version: 0.2.8(97af957868093b766f74c847443b5514)
+ specifier: ^0.2.10
+ version: 0.2.10(97af957868093b766f74c847443b5514)
'@better-auth/oauth-provider':
specifier: ^1.6.11
version: 1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.1)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-auth@1.6.11(@opentelemetry/api@1.9.1)(drizzle-orm@1.0.0-rc.3(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(effect@4.0.0-beta.70)(pg@8.21.0)(zod@4.4.3))(next@16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(pg@8.21.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7))(better-call@1.3.5(zod@4.4.3))
@@ -716,8 +716,8 @@ importers:
specifier: ^19.2.15
version: 19.2.15
'@typescript/native-preview':
- specifier: 7.0.0-dev.20260526.1
- version: 7.0.0-dev.20260526.1
+ specifier: 7.0.0-dev.20260527.1
+ version: 7.0.0-dev.20260527.1
typescript:
specifier: ^6.0.3
version: 6.0.3
@@ -749,8 +749,8 @@ importers:
specifier: ^8.20.0
version: 8.20.0
'@typescript/native-preview':
- specifier: 7.0.0-dev.20260526.1
- version: 7.0.0-dev.20260526.1
+ specifier: 7.0.0-dev.20260527.1
+ version: 7.0.0-dev.20260527.1
drizzle-kit:
specifier: 1.0.0-rc.3
version: 1.0.0-rc.3
@@ -774,8 +774,8 @@ importers:
specifier: workspace:*
version: link:../config
'@typescript/native-preview':
- specifier: 7.0.0-dev.20260526.1
- version: 7.0.0-dev.20260526.1
+ specifier: 7.0.0-dev.20260527.1
+ version: 7.0.0-dev.20260527.1
typescript:
specifier: ^6.0.3
version: 6.0.3
@@ -786,18 +786,18 @@ importers:
specifier: workspace:*
version: link:../env
nodemailer:
- specifier: ^8.0.8
- version: 8.0.8
+ specifier: ^8.0.9
+ version: 8.0.9
react:
specifier: ^19.2.6
version: 19.2.6
react-email:
- specifier: ^6.3.3
- version: 6.3.3(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ specifier: ^6.4.0
+ version: 6.4.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
devDependencies:
'@react-email/ui':
- specifier: ^6.3.3
- version: 6.3.3(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ specifier: ^6.4.0
+ version: 6.4.0(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
'@reactive-resume/config':
specifier: workspace:*
version: link:../config
@@ -808,11 +808,11 @@ importers:
specifier: ^19.2.15
version: 19.2.15
'@typescript/native-preview':
- specifier: 7.0.0-dev.20260526.1
- version: 7.0.0-dev.20260526.1
+ specifier: 7.0.0-dev.20260527.1
+ version: 7.0.0-dev.20260527.1
react-doctor:
- specifier: ^0.2.6
- version: 0.2.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
+ specifier: ^0.2.9
+ version: 0.2.9(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(eslint@10.4.0(jiti@2.7.0))
typescript:
specifier: ^6.0.3
version: 6.0.3
@@ -839,8 +839,8 @@ importers:
specifier: ^25.9.1
version: 25.9.1
'@typescript/native-preview':
- specifier: 7.0.0-dev.20260526.1
- version: 7.0.0-dev.20260526.1
+ specifier: 7.0.0-dev.20260527.1
+ version: 7.0.0-dev.20260527.1
typescript:
specifier: ^6.0.3
version: 6.0.3
@@ -855,8 +855,8 @@ importers:
specifier: workspace:*
version: link:../config
'@typescript/native-preview':
- specifier: 7.0.0-dev.20260526.1
- version: 7.0.0-dev.20260526.1
+ specifier: 7.0.0-dev.20260527.1
+ version: 7.0.0-dev.20260527.1
typescript:
specifier: ^6.0.3
version: 6.0.3
@@ -880,8 +880,8 @@ importers:
specifier: workspace:*
version: link:../config
'@typescript/native-preview':
- specifier: 7.0.0-dev.20260526.1
- version: 7.0.0-dev.20260526.1
+ specifier: 7.0.0-dev.20260527.1
+ version: 7.0.0-dev.20260527.1
typescript:
specifier: ^6.0.3
version: 6.0.3
@@ -914,8 +914,8 @@ importers:
specifier: workspace:*
version: link:../config
'@typescript/native-preview':
- specifier: 7.0.0-dev.20260526.1
- version: 7.0.0-dev.20260526.1
+ specifier: 7.0.0-dev.20260527.1
+ version: 7.0.0-dev.20260527.1
typescript:
specifier: ^6.0.3
version: 6.0.3
@@ -966,11 +966,11 @@ importers:
specifier: ^19.2.15
version: 19.2.15
'@typescript/native-preview':
- specifier: 7.0.0-dev.20260526.1
- version: 7.0.0-dev.20260526.1
+ specifier: 7.0.0-dev.20260527.1
+ version: 7.0.0-dev.20260527.1
react-doctor:
- specifier: ^0.2.6
- version: 0.2.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
+ specifier: ^0.2.9
+ version: 0.2.9(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(eslint@10.4.0(jiti@2.7.0))
typescript:
specifier: ^6.0.3
version: 6.0.3
@@ -991,8 +991,8 @@ importers:
specifier: workspace:*
version: link:../config
'@typescript/native-preview':
- specifier: 7.0.0-dev.20260526.1
- version: 7.0.0-dev.20260526.1
+ specifier: 7.0.0-dev.20260527.1
+ version: 7.0.0-dev.20260527.1
typescript:
specifier: ^6.0.3
version: 6.0.3
@@ -1007,8 +1007,8 @@ importers:
specifier: workspace:*
version: link:../config
'@typescript/native-preview':
- specifier: 7.0.0-dev.20260526.1
- version: 7.0.0-dev.20260526.1
+ specifier: 7.0.0-dev.20260527.1
+ version: 7.0.0-dev.20260527.1
typescript:
specifier: ^6.0.3
version: 6.0.3
@@ -1052,8 +1052,8 @@ importers:
specifier: ^4.11.2
version: 4.11.2(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
shadcn:
- specifier: ^4.8.0
- version: 4.8.0(@types/node@25.9.1)(babel-plugin-macros@3.1.0)(typescript@6.0.3)
+ specifier: ^4.8.1
+ version: 4.8.1(@types/node@25.9.1)(babel-plugin-macros@3.1.0)(typescript@6.0.3)
sonner:
specifier: ^2.0.7
version: 2.0.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
@@ -1080,14 +1080,14 @@ importers:
specifier: ^19.2.3
version: 19.2.3(@types/react@19.2.15)
'@typescript/native-preview':
- specifier: 7.0.0-dev.20260526.1
- version: 7.0.0-dev.20260526.1
+ specifier: 7.0.0-dev.20260527.1
+ version: 7.0.0-dev.20260527.1
postcss:
specifier: ^8.5.15
version: 8.5.15
react-doctor:
- specifier: ^0.2.6
- version: 0.2.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
+ specifier: ^0.2.9
+ version: 0.2.9(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(eslint@10.4.0(jiti@2.7.0))
tailwindcss:
specifier: ^4.3.0
version: 4.3.0
@@ -1107,8 +1107,8 @@ importers:
specifier: ^2.1.1
version: 2.1.1
dompurify:
- specifier: ^3.4.5
- version: 3.4.5
+ specifier: ^3.4.6
+ version: 3.4.6
tailwind-merge:
specifier: ^3.6.0
version: 3.6.0
@@ -1129,8 +1129,8 @@ importers:
specifier: ^25.9.1
version: 25.9.1
'@typescript/native-preview':
- specifier: 7.0.0-dev.20260526.1
- version: 7.0.0-dev.20260526.1
+ specifier: 7.0.0-dev.20260527.1
+ version: 7.0.0-dev.20260527.1
typescript:
specifier: ^6.0.3
version: 6.0.3
@@ -1147,8 +1147,8 @@ importers:
specifier: ^8.20.0
version: 8.20.0
'@typescript/native-preview':
- specifier: 7.0.0-dev.20260526.1
- version: 7.0.0-dev.20260526.1
+ specifier: 7.0.0-dev.20260527.1
+ version: 7.0.0-dev.20260527.1
drizzle-orm:
specifier: 1.0.0-rc.3
version: 1.0.0-rc.3(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(effect@4.0.0-beta.70)(pg@8.21.0)(zod@4.4.3)
@@ -1241,84 +1241,84 @@ packages:
'@aws-crypto/util@5.2.0':
resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==}
- '@aws-sdk/client-s3@3.1053.0':
- resolution: {integrity: sha512-/oGxoB6p1Nqs935Blt+v1o+anSCEf2n3RjIrcLz84i4cn2Gr+Z7JpDdUkG5+74r5ctqEPG7k/phTGbJ9fNKnHg==}
+ '@aws-sdk/client-s3@3.1054.0':
+ resolution: {integrity: sha512-2ue7uVqaHYX4rytkcrLySYU/m/ZlRbL8KojWefbR24B0/TcFkqN2IovpBFrnmla/dtZAn9eVSlhHeEddOghZ5w==}
engines: {node: '>=20.0.0'}
- '@aws-sdk/core@3.974.13':
- resolution: {integrity: sha512-+Y5/4tHki0uYgyx8eun146DegRVQBpdKGK5RbV0FTKJPpaKTchvqVxrrRFK6Wk0JksO4iAZKw3eqxGEIwtO98w==}
+ '@aws-sdk/core@3.974.14':
+ resolution: {integrity: sha512-ppamm04uoj3hhNO5IlQSs5D6rWX1fWkzcn6a4pZrojk8Y6ObY9wzLDdT/Eq3gv6O9hOebi9tYTNB8b8fQj9XJw==}
engines: {node: '>=20.0.0'}
'@aws-sdk/crc64-nvme@3.972.9':
resolution: {integrity: sha512-P+QGozmXn2mZZI7sDgk+aUm+RTI61MPSFB+Ir2vjEjEbEsE4e7hYtzrDvAUxZy9ko81h53e11+F/GYlvwDkaOQ==}
engines: {node: '>=20.0.0'}
- '@aws-sdk/credential-provider-env@3.972.39':
- resolution: {integrity: sha512-29wX9zpAvEt1vcj0psha+y6ygBHy2V/S72mp6e7q0KARLWXq+pwE/lR6qGkwknQvruh52lXvlqZIga8Hdxkucw==}
+ '@aws-sdk/credential-provider-env@3.972.40':
+ resolution: {integrity: sha512-jjT0p0Y7KZtcvExYiPCLJnqM9lkXDV1KBEg/13OE2DXv/9batzlyJHVKUEnRNJccY0O2Sul17E1su38CgdBhGQ==}
engines: {node: '>=20.0.0'}
- '@aws-sdk/credential-provider-http@3.972.41':
- resolution: {integrity: sha512-IA3CQTjtJkb6u1H4mE4936c8OPBMa9Jggtwe8U2Mqw/vvb/tZ5Ebd0mcZcX0uKWQhOyYo/+qNIwkV5Xh+FeJJA==}
+ '@aws-sdk/credential-provider-http@3.972.42':
+ resolution: {integrity: sha512-+3fsKtWybe5BjKEUA3/07oh7Ayfd82IED2+gyyaVfS/4PU78E3TaOQxSGOJ1t7Imefoidw/ne9QA7apX8wEnJg==}
engines: {node: '>=20.0.0'}
- '@aws-sdk/credential-provider-ini@3.972.43':
- resolution: {integrity: sha512-4mzII+3mZEVXXE1xzrLQrCJL7/r62A63bA6SVzZoNL5rqCJghpf+xgGltVrIBBs0n+mOZBKrQl2tRREtvZ5l6A==}
+ '@aws-sdk/credential-provider-ini@3.972.44':
+ resolution: {integrity: sha512-gZFw5wBefCIPg9vpT+gV5FdhfNKhYTVDZa1IsZCcn3SRoYUOJ/E05vwIogkJoonqBL0ttBGi5vhthX7xceekRg==}
engines: {node: '>=20.0.0'}
- '@aws-sdk/credential-provider-login@3.972.43':
- resolution: {integrity: sha512-HG7kQCwXtbv3oBV61Ins0oNX8KKyvrMqqRkb6ZiAfQHbMuHaiNaEb2KnpKLPkNpqImSBK82UkVE/kaY6IfWikA==}
+ '@aws-sdk/credential-provider-login@3.972.44':
+ resolution: {integrity: sha512-QqEGHfQeZgUDqh7zpqHufrZ8T644ELEWvB+4gUdewLyRw4IRF+6CJqeQuRWqucZdQzoQeMh7fNAD9BWxFAdNig==}
engines: {node: '>=20.0.0'}
- '@aws-sdk/credential-provider-node@3.972.44':
- resolution: {integrity: sha512-sDaBIT0yrNNIPfvlsiTCmANm07zKju+ipWODjEXgZlsjMeIJR3LVp7RDyAOzUoAsTbDfYKDWp+i5WrFiQP6rmQ==}
+ '@aws-sdk/credential-provider-node@3.972.45':
+ resolution: {integrity: sha512-3YCv52ExXIRz3LAVNysevd+s7akSpg9dl39v9LJ7dOQH+s5rHi3jMZYQyxwMmglxQGMuzYRfQ0o1VSP2UOlIRw==}
engines: {node: '>=20.0.0'}
- '@aws-sdk/credential-provider-process@3.972.39':
- resolution: {integrity: sha512-2k/amBifLd75eXNwgvPw/2lKYSQ3NhvHQgkVKVjfUq13/eJ3JRtHmznuFenn74OK3sSfp4SMy1YB2w+UVXoKqA==}
+ '@aws-sdk/credential-provider-process@3.972.40':
+ resolution: {integrity: sha512-cXaozlgJCOwmE6D7x4npcPdyk7kiFZdrGjN3D6tXXtItJJMNGPafDfAJn4YQmciMooG/X+b0Y6RTqdVVMx26jg==}
engines: {node: '>=20.0.0'}
- '@aws-sdk/credential-provider-sso@3.972.43':
- resolution: {integrity: sha512-LPc3+Y4vhH1T4x6CMqwCM6hk5+SRf/Lwmgm8INm95wxTtIRHcMwQUVkDzWu4Iw/RSncxYM2BC01OrYbxOPZvyg==}
+ '@aws-sdk/credential-provider-sso@3.972.44':
+ resolution: {integrity: sha512-YePoj5kQuPmE0MHnyftXCfsO8ZSBd2kDr50XEIUrdejSbGFlayYvUuCohdb8drhGhPm6b65o7H1eC26EZhwUvA==}
engines: {node: '>=20.0.0'}
- '@aws-sdk/credential-provider-web-identity@3.972.43':
- resolution: {integrity: sha512-wQtL34lUD/09VXjwAUo2T+I3aEXRDxMB3DKmTJL/Zj0Gi6sLDTrVhae1XVt01yzkquOWajI/sZW72JGDZ1ciTw==}
+ '@aws-sdk/credential-provider-web-identity@3.972.44':
+ resolution: {integrity: sha512-Ys/JJe++8Z2Y5meR1taMBaVcrGBA0/XsVTQR+qOKZbdNyg+8Jlv5rYZSwh8SqEHY00goSOZy7PHzZ2rLNQxDLg==}
engines: {node: '>=20.0.0'}
- '@aws-sdk/middleware-bucket-endpoint@3.972.15':
- resolution: {integrity: sha512-O2HDANa+MrvbxpaRVQDiH3T13uAa9AkMjKyZmDygwauAmmvqZ5B0iRmKW+fuVGW6NPXuyXurFgIx69lSvmAWGA==}
+ '@aws-sdk/middleware-bucket-endpoint@3.972.16':
+ resolution: {integrity: sha512-FhasMTBDBmMN7EEa1hUeHwo5p5Mv3Dm8w0VEbdXX/6ola/uyhRuJt8zGkH09mLTmab20USTzEpPqyqEoe1MqNg==}
engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-expect-continue@3.972.13':
resolution: {integrity: sha512-sHiqIFg8o2ipT7t40B89Vj0ubSUtY6OSt/+Ee/OXhHch5K4+81zP2+QX8Lkc/nJ2QSmCySxOke7TEbmX69fe2g==}
engines: {node: '>=20.0.0'}
- '@aws-sdk/middleware-flexible-checksums@3.974.21':
- resolution: {integrity: sha512-alAu9heyiBK/OmRNXVxq8mmPTgeW2AQ6EYjRsI38kPZa1MZvt2Jh+BlGq7/GG9OVXOaEgD7DlGj/Lzfy5OmuEg==}
+ '@aws-sdk/middleware-flexible-checksums@3.974.22':
+ resolution: {integrity: sha512-ot1kZ1JGHUxcXPOARhej/n/+Odfx9VPt60pNrUq8Lf/U2blIF3+uj5v56gw76VD70dZvrfeLNo9jKz6pQJfOlA==}
engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-location-constraint@3.972.11':
resolution: {integrity: sha512-hkfspNUP4criAH6ton6BGKgnm5dZx+7bUOy1YqlTfejDeUPAM23D81q/IX+hdlS3KUsfwGz5ADTqZWKBEUpf4A==}
engines: {node: '>=20.0.0'}
- '@aws-sdk/middleware-sdk-s3@3.972.42':
- resolution: {integrity: sha512-/xNqNGXv9LaxZd25L9VV4pnSOw9OdDNO4rAHamM+h3KQBSITljIH9vk3dveGga1I2j36lQd0rdG3gjNEXvtNew==}
+ '@aws-sdk/middleware-sdk-s3@3.972.43':
+ resolution: {integrity: sha512-CBmixMY36JdAdt9ALgm7yVlvOXGUCHt9Z2kn5p9XVO5StO6HCH+cayV7YYV1CDLsXvVyebaXgBmif9wHoxCeNA==}
engines: {node: '>=20.0.0'}
'@aws-sdk/middleware-ssec@3.972.11':
resolution: {integrity: sha512-7PQvGNhtveKlvVqNahqWx5yrwxP7ecwAoB1dYBf8eKwfo2tzzCbNnW+q2nO3N066ktQaB4iBQbDRWtizm+amoQ==}
engines: {node: '>=20.0.0'}
- '@aws-sdk/nested-clients@3.997.11':
- resolution: {integrity: sha512-nWXXJ1r/r8N2Gw1pWolRgED38/A9A8DHR2ETWIv220zh4PZHcybbR4hUVWWktmNXTRHzDJwRluapHn0rZxuoqA==}
+ '@aws-sdk/nested-clients@3.997.12':
+ resolution: {integrity: sha512-Js2VYaCM269feB0cs0cGmlIhdOgT9aMqzdBx68lCy6kVCYfzr0T36ovUFDvfUmatkuBeyBJhCwaLBh7P8meH5Q==}
engines: {node: '>=20.0.0'}
- '@aws-sdk/signature-v4-multi-region@3.996.28':
- resolution: {integrity: sha512-qs9z5LqXO/CZC2Lg9SGKpoLU8Rhi+m2pFKZqfO9pytX1clc0katqtsDNupJxFy0xT9wsZSPzM2v1y+/H/zfp5Q==}
+ '@aws-sdk/signature-v4-multi-region@3.996.29':
+ resolution: {integrity: sha512-Few9FoQqOt/0KSvZYP+qdW0dfOhfQ9N+gl2UUDvCPW6mkPKHli9LMbKxWj+wZ5zKPaOoqxuR3Hhy3OTpndkfSw==}
engines: {node: '>=20.0.0'}
- '@aws-sdk/token-providers@3.1052.0':
- resolution: {integrity: sha512-QqZNB3so7UIDxZtroc85TQaLVxdZRFm0eWM1CSR2N+b06as9TOrilvrlTZuj3guYlxMs6yLOgGxnklJ5qMYtTw==}
+ '@aws-sdk/token-providers@3.1054.0':
+ resolution: {integrity: sha512-hG9YKApmZOw+drJ9Nuoaf/OvC8e5W1+3eoLeN5p2uVCZRWsv27teIS0b4kiH6Sfv3WMmamqYJxmE2WMwyp/L/A==}
engines: {node: '>=20.0.0'}
'@aws-sdk/types@3.973.9':
@@ -1329,8 +1329,8 @@ packages:
resolution: {integrity: sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==}
engines: {node: '>=20.0.0'}
- '@aws-sdk/xml-builder@3.972.25':
- resolution: {integrity: sha512-GH+Kjz4nPKWKHnsiQpnhP1MJdTGIcK4rAka6tzakgjjUkVgNsmPeEbbRAf09SzS1hjGu6duGHCBsxYke0BhHjQ==}
+ '@aws-sdk/xml-builder@3.972.26':
+ resolution: {integrity: sha512-cDbrqvDS73whl6YAPSPq0U6whzG6UWI9PuWh0wrUuGoZexhWEqhdunbukV7iBoaWnFV1AODutM5hOD6rtn439g==}
engines: {node: '>=20.0.0'}
'@aws/lambda-invoke-store@0.2.4':
@@ -1574,8 +1574,8 @@ packages:
drizzle-orm:
optional: true
- '@better-auth/infra@0.2.8':
- resolution: {integrity: sha512-FSessQK8BiKHGMABHN2ezvIcZaldYXxC4aNuVmdOClb9460h3xSXsQj2/pkGqtBbcA/YZFvrh3uJdhy2BgxJrA==}
+ '@better-auth/infra@0.2.10':
+ resolution: {integrity: sha512-u7ZZ7q4otp8TfRssm0DFfs1AsHPCj/nlVcus3xPhPvFVN+iUC0DMUp7gR4MsNZ2Lg6OH+3aRBSamyYrywz54zQ==}
peerDependencies:
'@better-auth/core': '>=1.4.0'
'@better-auth/sso': '>=1.4.0'
@@ -2182,6 +2182,36 @@ packages:
cpu: [x64]
os: [win32]
+ '@eslint-community/eslint-utils@4.9.1':
+ resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+ '@eslint-community/regexpp@4.12.2':
+ resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ '@eslint/config-array@0.23.5':
+ resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@eslint/config-helpers@0.6.0':
+ resolution: {integrity: sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@eslint/core@1.2.1':
+ resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@eslint/object-schema@3.0.5':
+ resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ '@eslint/plugin-kit@0.7.1':
+ resolution: {integrity: sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
'@floating-ui/core@1.7.5':
resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==}
@@ -2215,6 +2245,26 @@ packages:
peerDependencies:
hono: ^4
+ '@humanfs/core@0.19.2':
+ resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanfs/node@0.16.8':
+ resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanfs/types@0.15.0':
+ resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanwhocodes/module-importer@1.0.1':
+ resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+ engines: {node: '>=12.22'}
+
+ '@humanwhocodes/retry@0.4.3':
+ resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
+ engines: {node: '>=18.18'}
+
'@iarna/toml@2.2.5':
resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==}
@@ -2406,8 +2456,8 @@ packages:
'@types/node':
optional: true
- '@ioredis/commands@1.5.1':
- resolution: {integrity: sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==}
+ '@ioredis/commands@1.10.0':
+ resolution: {integrity: sha512-UmeW7z4LfctwoQ5wkhVzgq8tXkreED2xZGpX+Bg+zA+WJFZCT6c062AfCK/Dfk81xZnnwdhJCUMkitihRaoC2Q==}
'@jest/schemas@29.6.3':
resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
@@ -3568,8 +3618,8 @@ packages:
react: ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^18.0 || ^19.0 || ^19.0.0-rc
- '@react-email/ui@6.3.3':
- resolution: {integrity: sha512-ur1zabkODwYZ1KxQ4ho//xDnCXycLXFar6Nxa0XhjKOo3HFY5csSYt1kjADyY+uHy948JobtARjeDUefuw04Ww==}
+ '@react-email/ui@6.4.0':
+ resolution: {integrity: sha512-M9AD/Jvx0+SFjRS6kP69UgvPw2dvfKU4nqdEf7E4EeLY6Bh7i3gMVXTNVWKgRMid9tGmiqnynPib1NnZ+uvzNg==}
'@react-pdf/fns@3.1.3':
resolution: {integrity: sha512-0I7pApDr1/RLAKbizuLy/IHTEa93LSPy/bEwYniboC3Xqnp6Od8xFJKbKEzGw2wh/5zKFFwl00g4t9RwgIMc3w==}
@@ -3810,8 +3860,8 @@ packages:
'@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
- '@swc/helpers@0.5.22':
- resolution: {integrity: sha512-/e2Ly3Docn9kYByap6TV4oquJ3wQuz3c+kC74riqtkwU9CwTMeuj6t2rW+bRr4pyOx/CYQM4wr0RgaKQwGEz0A==}
+ '@swc/helpers@0.5.23':
+ resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==}
'@t3-oss/env-core@0.13.11':
resolution: {integrity: sha512-sM7GYY+KL7H/Hl0BE0inWfk3nRHZOLhmVn7sHGxaZt9FAR6KqREXAE+6TqKfiavfXmpRxO/OZ2QgKRd+oiBYRQ==}
@@ -3937,8 +3987,8 @@ packages:
engines: {node: '>=18'}
hasBin: true
- '@tanstack/form-core@1.32.0':
- resolution: {integrity: sha512-Tn5VRDSjyqjmaet2tJMuEWDRFyrCaon03vxXPlSSaiSs6C/N7lCIwGCXJbZXEUq1kTj8jYN9qyXHbsz4LQHcow==}
+ '@tanstack/form-core@1.32.1':
+ resolution: {integrity: sha512-5yTCJ1/0bBjdVDsZsqPpLMVZLLN/G39b+ONnwv4vjz2jDes4YAd63cVwti5RtWuGuS1yLc5tVrGl1rWyVYsNGw==}
'@tanstack/history@1.162.0':
resolution: {integrity: sha512-79pf/RkhteYZTRgcR4F9kbk84P2N8rugQJswxfIqovlbRiT3yI7eBE+5QorIrZaOKktsgzRlXh1l/du/xpl4iA==}
@@ -3955,8 +4005,8 @@ packages:
'@tanstack/query-core@5.100.14':
resolution: {integrity: sha512-5X41dGpxgeaHISCRW2oYwcSycZeULZzAunaudXT9ov1KOTj9xwt0CH6hbwqP1/z74ZWF7rYFnDpyYH07XFcZew==}
- '@tanstack/react-form@1.32.0':
- resolution: {integrity: sha512-6WP5SQTA6/H9crCpvpq3ZppYWqtrdE5NjOy6ebABi6uAQPqhfTzrdjS9t40mCZCFtGI5585OhJV6zBP/KN2zcw==}
+ '@tanstack/react-form@1.32.1':
+ resolution: {integrity: sha512-GQ6IdIFnAJvhVaBZJIyVzi14c8b02W4SopJFzFZCjYFIAb5CfTZCbvHZ4Cnd5byd0OzTwLLW/R95noRKD+2+ZA==}
peerDependencies:
'@tanstack/react-start': '*'
react: ^17.0.0 || ^18.0.0 || ^19.0.0
@@ -4245,33 +4295,33 @@ packages:
'@ts-morph/common@0.27.0':
resolution: {integrity: sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==}
- '@turbo/darwin-64@2.9.14':
- resolution: {integrity: sha512-t7QiPflaEyBE4oayeZtSmu4mEfjgIrcNlNNl1z1dmIVPqEdtA7+CfTf8d7KXsOGPh6aNgWjKxyvQg9uGfDQF+A==}
+ '@turbo/darwin-64@2.9.15':
+ resolution: {integrity: sha512-nnDo9R1Df+s2x6jxlERtbg7xRpuicf8p4J2krcnjeaMBt3q9V41pGXa4t9YM2Y4ozozsVJ+CH405CJUrWIQK4Q==}
cpu: [x64]
os: [darwin]
- '@turbo/darwin-arm64@2.9.14':
- resolution: {integrity: sha512-d23147mC9BsCPA9mJ0h/ubcpbRgcJBXbcG3+Vq7YLhjz3IXuvQsJ1UXH8f4MD76ZjJ4m/E4aRdJV+MW88CDfbw==}
+ '@turbo/darwin-arm64@2.9.15':
+ resolution: {integrity: sha512-fDSx56oqoFuS+yUQw7hqjQTkjrSLdMcplhuLC8HcSkWC6YrpwEmUUYsPYHPxy4ALvLxnmPQuk6XoSD8tdkjP+g==}
cpu: [arm64]
os: [darwin]
- '@turbo/linux-64@2.9.14':
- resolution: {integrity: sha512-P3ZKB5tuUDdDQWuAsACGUR1qv9W7BNWxdxqVJ0kZNuNNPRaVYTPPikLcp79+GiEcW3npsR+KyP38lnQiBc5aSA==}
+ '@turbo/linux-64@2.9.15':
+ resolution: {integrity: sha512-/bmxn+x/xE+oh0VzEXt/zf2zsORAYZPrL3db5/VrXzYt0Z4wxcvffwJBGlSfla2smfS1BLGBiyWldJlWDXJVXA==}
cpu: [x64]
os: [linux]
- '@turbo/linux-arm64@2.9.14':
- resolution: {integrity: sha512-ZRTlzcUMrrPv9ZuDzRF9n60Ym13bKeG9jDB8WjxyLhWNzV+AJQN+zdpIk3NJYf2zQsGUm1mNar2P0elRzLw25g==}
+ '@turbo/linux-arm64@2.9.15':
+ resolution: {integrity: sha512-cbOaDe1ijz5As+mimOOHgmRMolZZZO7miNBHHp5xdiYMm2Q/Dwu1JVLx/Kw4s7xjocG/oEoHrpHrxpEAIEfNiw==}
cpu: [arm64]
os: [linux]
- '@turbo/windows-64@2.9.14':
- resolution: {integrity: sha512-exanwN6sIduZwykYeiTQj8kCmOhazP5WOz3bvXMcYtjhL6Z3iRWLewKrXCBq0bqwSP3iBMb/AerRCnHI4lx46A==}
+ '@turbo/windows-64@2.9.15':
+ resolution: {integrity: sha512-/Fzm7afui7uK7dFBwrTXKuDhBBTiHk5I+hMVAPMR7cqQyDo2norCNUsN9PdNuYcmzYbhSOxzz498wQYvSAz29w==}
cpu: [x64]
os: [win32]
- '@turbo/windows-arm64@2.9.14':
- resolution: {integrity: sha512-fVdCsnmYoKICsycbWuuGp6Jvi51/3G/UluFWuAUCvR8PIW5IJkAk5BM9UF8PSm0Q2IphWHFZjYEgjHsh3B9y/g==}
+ '@turbo/windows-arm64@2.9.15':
+ resolution: {integrity: sha512-fOHEsLcqVdFXLw2ApWv4gxwfHzkUnpo9rHGml+9+dyHj148m/Bc+556kEvb5+4u6prI1LMd8zEZE2HcO6Jn2VQ==}
cpu: [arm64]
os: [win32]
@@ -4335,6 +4385,9 @@ packages:
'@types/jsesc@2.5.1':
resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==}
+ '@types/json-schema@7.0.15':
+ resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
'@types/mdast@4.0.4':
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
@@ -4398,50 +4451,50 @@ packages:
resolution: {integrity: sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260526.1':
- resolution: {integrity: sha512-7kxHwV+zjANOeDn236ob/2nZy72slfid0ukGd0ApVRmDfG3iob4YEO8TuDet1RxNClis+k9l6zaPBJkujczzkA==}
+ '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260527.1':
+ resolution: {integrity: sha512-bDi6FJ644n3uKdp/ZI7j50ChVyGOsrJrkwihQb6x3yByFQkTINLu3e6ZkY+HveQ2Zw2vy9SGN8E7b3A5iSOO0A==}
engines: {node: '>=16.20.0'}
cpu: [arm64]
os: [darwin]
- '@typescript/native-preview-darwin-x64@7.0.0-dev.20260526.1':
- resolution: {integrity: sha512-V2RY5nKkNyBQMK39lCdG//VvCVwKhibQBhuelIm8TR4P2EduhJHB42ftr6lEVgZjW03qlrAEE/FWwTV6Wku2vA==}
+ '@typescript/native-preview-darwin-x64@7.0.0-dev.20260527.1':
+ resolution: {integrity: sha512-r6GXrTdalXZu1/b5goMpAe+efZvOfwdE45gl8Tti3fckP9icK3xdiN+VnNi0RL2/c2L86RyN8nGxihaCHGCKbw==}
engines: {node: '>=16.20.0'}
cpu: [x64]
os: [darwin]
- '@typescript/native-preview-linux-arm64@7.0.0-dev.20260526.1':
- resolution: {integrity: sha512-IVDSXjEyKgfa1SvwWux77cdb6W4ReycvLM9hYiJv42vzDJRt3g+H4LmaI6Ng+0Lq5syylzpVhJe4nh3logUsag==}
+ '@typescript/native-preview-linux-arm64@7.0.0-dev.20260527.1':
+ resolution: {integrity: sha512-QJAFyPJgJqJVLbVPHl5xL7FCn3HNPLdpEm8l7KBgiYpltLhU1p/LJ3iN0XpFRAhq9ojWbZebo8t/h8MX35QjTQ==}
engines: {node: '>=16.20.0'}
cpu: [arm64]
os: [linux]
- '@typescript/native-preview-linux-arm@7.0.0-dev.20260526.1':
- resolution: {integrity: sha512-gEctrS100iZEwZgrJYuVN4pPW922CcozDneNwJkQld3zmFfSeGRLzj9oXCzP1JbMLq2np+DYrIkJ5ErPF1mwlA==}
+ '@typescript/native-preview-linux-arm@7.0.0-dev.20260527.1':
+ resolution: {integrity: sha512-BlfQBatMkZHi3o+atxoUW0czGJNjo9cpO1BoQeB3gxZ7D/cDZHYHmKFSSRx8UxMktwP5k5lPxi0wgA3Ic2mQyQ==}
engines: {node: '>=16.20.0'}
cpu: [arm]
os: [linux]
- '@typescript/native-preview-linux-x64@7.0.0-dev.20260526.1':
- resolution: {integrity: sha512-3t4r52PTZgRh+dO0me8o4GCPj3PEixtIEC1vqmQxR/JjDunLn4YOFwfDLyNWEflOxauPRKV5pWkSpd6HiBsZfw==}
+ '@typescript/native-preview-linux-x64@7.0.0-dev.20260527.1':
+ resolution: {integrity: sha512-UFB7ZdK2/vIIi62nfn3JhyGV7qR/qXjKPQaPVXwzCvaPieTZcsNsALjKU0W5WHThyi+5p3U7O3dGE7n6P4q4Yw==}
engines: {node: '>=16.20.0'}
cpu: [x64]
os: [linux]
- '@typescript/native-preview-win32-arm64@7.0.0-dev.20260526.1':
- resolution: {integrity: sha512-0uR0M92lNwpgOyGBk+eZAAFuCsa3lc2l3IkvGWMo3fkD+eIBb8NwaqIhKpQ9VGvr6xfjJ3uf210j0Vxhl4HcLQ==}
+ '@typescript/native-preview-win32-arm64@7.0.0-dev.20260527.1':
+ resolution: {integrity: sha512-rp/q9+9H77JQvepC/UpDP8CdeTGSGyhp9BVbmFwqUV2NhMHPldfys3ihY7OQdoVBgWIKQyxEHB+FTr8Z7kre1Q==}
engines: {node: '>=16.20.0'}
cpu: [arm64]
os: [win32]
- '@typescript/native-preview-win32-x64@7.0.0-dev.20260526.1':
- resolution: {integrity: sha512-UvwEPV9tDgV8GAWkp5Kmq2LRKUlhwtrl0PUpMDOKInaXAndMtUCOQ+aFqd015jPmfNPKs84BRQoQYXyeaHVJDw==}
+ '@typescript/native-preview-win32-x64@7.0.0-dev.20260527.1':
+ resolution: {integrity: sha512-864Pq4qoDcacUJhs2/kQplyfwNO0APUmx1k8qUaJt2P9ZGF0Pu++afJi7OagImHMiEQcmigjmZPuOodOk5YmqQ==}
engines: {node: '>=16.20.0'}
cpu: [x64]
os: [win32]
- '@typescript/native-preview@7.0.0-dev.20260526.1':
- resolution: {integrity: sha512-ys/rYFNEVb4LZ7zUFxWZ6iv5gnIW29WRv3L9+hJKfAF3d/ekI76B4Ej4dU7odNCFiX85sCotljzq8N0QyruuoA==}
+ '@typescript/native-preview@7.0.0-dev.20260527.1':
+ resolution: {integrity: sha512-j81qKiwCPgMEjtk8uDLP+TDW60l6mugoJ7SNzfHWv1PJ6bUjIAHuag4P1jSLm1IpKuMuB3TTi4f61n7TJi8Jog==}
engines: {node: '>=16.20.0'}
hasBin: true
@@ -4562,6 +4615,16 @@ packages:
resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
engines: {node: '>= 0.6'}
+ acorn-jsx@5.3.2:
+ resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+ acorn@8.16.0:
+ resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
agent-base@7.1.4:
resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
engines: {node: '>= 14'}
@@ -4584,6 +4647,9 @@ packages:
ajv:
optional: true
+ ajv@6.15.0:
+ resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==}
+
ajv@8.20.0:
resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==}
@@ -4914,8 +4980,8 @@ packages:
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
engines: {node: '>=6'}
- cluster-key-slot@1.1.2:
- resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==}
+ cluster-key-slot@1.1.1:
+ resolution: {integrity: sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==}
engines: {node: '>=0.10.0'}
cmdk@1.1.1:
@@ -5101,6 +5167,9 @@ packages:
babel-plugin-macros:
optional: true
+ deep-is@0.1.4:
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
deepmerge-ts@7.1.5:
resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==}
engines: {node: '>=16.0.0'}
@@ -5136,8 +5205,8 @@ packages:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'}
- deslop-js@0.0.12:
- resolution: {integrity: sha512-DsoVhhrgdqjkHxD9zFFJS7r+wDkbbpbdYVfwTNo6s89xTDbZQ9xW0EOd17a45Cfo1z0KxJQkjtJi/fHZ/LpBPA==}
+ deslop-js@0.0.13:
+ resolution: {integrity: sha512-gIXD+wY2/NHkZHpNrb8MWS6NJs3ee0XunAenBCvwHJlWnedDbIrg70hdNR7bDzYZLe9LGJZMLEI1w2CC72Gk5A==}
detect-libc@2.1.2:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
@@ -5176,8 +5245,8 @@ packages:
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
engines: {node: '>= 4'}
- dompurify@3.4.5:
- resolution: {integrity: sha512-OrwIBKsdNSVEeubdJ1HBv/wNENRM9ytAVCv7YXt//A3vPdVMNuACRqK9mXCGCBW2ln7BT/A4X0jXHo2Gu89miA==}
+ dompurify@3.4.6:
+ resolution: {integrity: sha512-+7gzEI8trIIQkVCvQ3ucGtNfH3nOmDgVTzc62rAAOlMxLth78pwpPoZCPc7CyRzAQF89MqcfPdEWkDwnjgqktg==}
domutils@3.2.2:
resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
@@ -5343,8 +5412,8 @@ packages:
effect@4.0.0-beta.70:
resolution: {integrity: sha512-8AwGTRiNriirHGEYHrOS0E9fzdhIqCdZjiHP1YXmNo2UyPGS43ILsymsSHT7V0DJS+8dvlKq2RxnrDBUhDNZHg==}
- electron-to-chromium@1.5.361:
- resolution: {integrity: sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==}
+ electron-to-chromium@1.5.362:
+ resolution: {integrity: sha512-PUY2DrLvkjkUuWqq+KPL2iWshrJsZOcIojzRQ7eXFacc9dWga7MGMJAa15VbiejSZB1PAXaRLAiKgruHP8LB1w==}
emoji-regex-xs@1.0.0:
resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==}
@@ -5433,23 +5502,55 @@ packages:
escape-html@1.0.3:
resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
+ escape-string-regexp@4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+
escape-string-regexp@5.0.0:
resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
engines: {node: '>=12'}
+ eslint-plugin-react-hooks@7.1.1:
+ resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0
+
eslint-scope@9.1.2:
resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+ eslint-visitor-keys@3.4.3:
+ resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
eslint-visitor-keys@5.0.1:
resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+ eslint@10.4.0:
+ resolution: {integrity: sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+ hasBin: true
+ peerDependencies:
+ jiti: '*'
+ peerDependenciesMeta:
+ jiti:
+ optional: true
+
+ espree@11.2.0:
+ resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
esprima@4.0.1:
resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
engines: {node: '>=4'}
hasBin: true
+ esquery@1.7.0:
+ resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==}
+ engines: {node: '>=0.10'}
+
esrecurse@4.3.0:
resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
engines: {node: '>=4.0'}
@@ -5464,6 +5565,10 @@ packages:
estree-walker@3.0.3:
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
etag@1.8.1:
resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
engines: {node: '>= 0.6'}
@@ -5523,6 +5628,12 @@ packages:
fast-json-patch@3.1.1:
resolution: {integrity: sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==}
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+ fast-levenshtein@2.0.6:
+ resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
fast-string-truncated-width@3.0.3:
resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==}
@@ -5572,6 +5683,10 @@ packages:
resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==}
engines: {node: '>=18'}
+ file-entry-cache@8.0.0:
+ resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
+ engines: {node: '>=16.0.0'}
+
fill-range@7.1.1:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
@@ -5583,6 +5698,17 @@ packages:
find-my-way-ts@0.1.6:
resolution: {integrity: sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA==}
+ find-up@5.0.0:
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+ engines: {node: '>=10'}
+
+ flat-cache@4.0.1:
+ resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
+ engines: {node: '>=16'}
+
+ flatted@3.4.2:
+ resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==}
+
fontkit@2.0.4:
resolution: {integrity: sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==}
@@ -5688,6 +5814,10 @@ packages:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
+ glob-parent@6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+
glob@13.0.6:
resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==}
engines: {node: 18 || 20 || >=22}
@@ -5743,6 +5873,12 @@ packages:
headers-polyfill@5.0.1:
resolution: {integrity: sha512-1TJ6Fih/b8h5TIcv+1+Hw0PDQWJTKDKzFZzcKOiW1wJza3XoAQlkCuXLbymPYB8+ZQyw8mHvdw560e8zVFIWyA==}
+ hermes-estree@0.25.1:
+ resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==}
+
+ hermes-parser@0.25.1:
+ resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==}
+
hono@4.12.23:
resolution: {integrity: sha512-eIaZ9qDgu7XV0pxOCrg7/WhnQ6Ivm22UcxhXx/A3dcbqbbYgBEkc6e/J/s7j2tS96zoB0S9VBdLwQNCWwUo4LA==}
engines: {node: '>=16.9.0'}
@@ -5810,6 +5946,10 @@ packages:
resolution: {integrity: sha512-NkJQA7oZ4YHQhd2+H3BoRFKF3d/XNsiKpHZCQEMH9pDX27hQQLsTyOocyRgaIVtf8gHX3Nt3LPkR4e5EdtPAGQ==}
engines: {node: ^22.18.0 || >=24.0.0}
+ imurmurhash@0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+
indent-string@4.0.0:
resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
engines: {node: '>=8'}
@@ -5828,8 +5968,8 @@ packages:
inline-style-parser@0.2.7:
resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==}
- ioredis@5.10.1:
- resolution: {integrity: sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA==}
+ ioredis@5.11.0:
+ resolution: {integrity: sha512-EZBErytyVovD8f6pDfG3Kb37N6Y3lmDA9NNj+4+IP13CzzHGeX+OyeRM2Um13khRzoBSzzL+5lVnCX8V2RLeMg==}
engines: {node: '>=12.22.0'}
ip-address@10.2.0:
@@ -6018,9 +6158,15 @@ packages:
engines: {node: '>=6'}
hasBin: true
+ json-buffer@3.0.1:
+ resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
json-parse-even-better-errors@2.3.1:
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
+ json-schema-traverse@0.4.1:
+ resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
json-schema-traverse@1.0.0:
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
@@ -6030,6 +6176,9 @@ packages:
json-schema@0.4.0:
resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
+ json-stable-stringify-without-jsonify@1.0.1:
+ resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
json5@2.2.3:
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
engines: {node: '>=6'}
@@ -6048,6 +6197,9 @@ packages:
jszip@3.10.1:
resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
+ keyv@4.5.4:
+ resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
kleur@3.0.3:
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
engines: {node: '>=6'}
@@ -6129,6 +6281,10 @@ packages:
resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
engines: {node: '>=6'}
+ levn@0.4.1:
+ resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+ engines: {node: '>= 0.8.0'}
+
libphonenumber-js@1.13.3:
resolution: {integrity: sha512-xMkdAMqcyG7iN2WZZmGIfWbYxW4orRkny+0/AXIbwL0xll2zkDX0Vzo/BXFa6+7mh2UvJl9MbcTtHk0YXkFtBA==}
@@ -6222,15 +6378,13 @@ packages:
linkifyjs@4.3.3:
resolution: {integrity: sha512-P8aEP5U/D1/IlTY2OeYsErdwh9bGuLE30NcXtKEjgdHcahveQoQwM2yZNsioQHsWFz0P7KKudisbrzCgR0sDHg==}
+ locate-path@6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+
lodash.debounce@4.0.8:
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
- lodash.defaults@4.2.0:
- resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==}
-
- lodash.isarguments@3.1.0:
- resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==}
-
log-symbols@6.0.0:
resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==}
engines: {node: '>=18'}
@@ -6501,6 +6655,9 @@ packages:
resolution: {integrity: sha512-XPUa/jz+P1oJvN9VBxw4L9MtdFfaH3DAryqPssqhb2kXjmb9npz0dly6rCsgFWOPr4Yg9mTfM3MDZgZZ+7A3lA==}
engines: {node: ^20.0.0 || >=22.0.0}
+ natural-compare@1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
negotiator@0.6.3:
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
engines: {node: '>= 0.6'}
@@ -6574,8 +6731,8 @@ packages:
node-rsa@1.1.1:
resolution: {integrity: sha512-Jd4cvbJMryN21r5HgxQOpMEqv+ooke/korixNNK3mGqfGJmy0M77WDDzo/05969+OkMy3XW1UuZsSmW9KQm7Fw==}
- nodemailer@8.0.8:
- resolution: {integrity: sha512-p+XsnzXGdtIHXUu2ugxdfG+eX2nehsGhMjW9h0CWj1BhE30hrFz0kh0yIM0/VjUgVsRrDj+80ZO+I1nSkGE4tA==}
+ nodemailer@8.0.9:
+ resolution: {integrity: sha512-5ofa7BUN8+C+Hckh5V2GjeeOGRQBx0CJQA6KxrvuZfC8iU4/q7sLn8XrtEEhJkjV6HdyIiQs7Bba6bTao8JhkA==}
engines: {node: '>=6.0.0'}
normalize-path@3.0.0:
@@ -6650,6 +6807,10 @@ packages:
openapi-types@12.1.3:
resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
+ optionator@0.9.4:
+ resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+ engines: {node: '>= 0.8.0'}
+
ora@8.2.0:
resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==}
engines: {node: '>=18'}
@@ -6675,8 +6836,8 @@ packages:
oxc-resolver@11.19.1:
resolution: {integrity: sha512-qE/CIg/spwrTBFt5aKmwe3ifeDdLfA2NESN30E42X/lII5ClF8V7Wt6WIJhcGZjp0/Q+nQ+9vgxGk//xZNX2hg==}
- oxlint-plugin-react-doctor@0.2.6:
- resolution: {integrity: sha512-af0bMoHfVI3DawVJKmsi/tLTpigSK/AXs8HNpkV98Bj8T5jPqN+N9LJVuCIUt7qk4O7rpAXXh/9oMHyVEs9kNA==}
+ oxlint-plugin-react-doctor@0.2.9:
+ resolution: {integrity: sha512-wnmC4OBbog9IQuZY2evNNe1P5IacUDLnYqOOpOSq8Adb+kjFmNxPPG8SMH+/OWkKld3NVC5KMM2pSw+oxnEIUw==}
engines: {node: ^20.19.0 || >=22.12.0}
oxlint@1.67.0:
@@ -6692,6 +6853,14 @@ packages:
vite-plus:
optional: true
+ p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+
+ p-locate@5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+
pako@0.2.9:
resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==}
@@ -6726,6 +6895,10 @@ packages:
path-browserify@1.0.1:
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
+ path-exists@4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+
path-expression-matcher@1.5.0:
resolution: {integrity: sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==}
engines: {node: '>=14.0.0'}
@@ -6865,6 +7038,10 @@ packages:
resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==}
engines: {node: '>=20'}
+ prelude-ls@1.2.1:
+ resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+ engines: {node: '>= 0.8.0'}
+
prettier@3.8.3:
resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==}
engines: {node: '>=14'}
@@ -6944,6 +7121,10 @@ packages:
engines: {node: '>=16.0.0'}
hasBin: true
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
pure-rand@8.4.0:
resolution: {integrity: sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A==}
@@ -6984,23 +7165,18 @@ packages:
resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==}
engines: {node: '>= 0.10'}
- react-doctor@0.2.6:
- resolution: {integrity: sha512-Dwl6WY5wLCuy/i59rSK2veoJrTEw1hLzR7TXg5rjlvFR0JHsPW3kq2zFwHj/amtYESEmKCShIvAubzzy2b4nyw==}
+ react-doctor@0.2.9:
+ resolution: {integrity: sha512-lfTA5OOQmy95nreKad+UueFAsVLxtjRTwZQpo50VAfsFytpDjiAb+caB2DV+Tdga8pk9th18DUZjTuSDWFFvZQ==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
- peerDependencies:
- eslint-plugin-react-hooks: ^6 || ^7
- peerDependenciesMeta:
- eslint-plugin-react-hooks:
- optional: true
react-dom@19.2.6:
resolution: {integrity: sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==}
peerDependencies:
react: ^19.2.6
- react-email@6.3.3:
- resolution: {integrity: sha512-mkHgVfuoDLM1To/u0ZcdJy/3q1Z3SdOe1A5/GtsUqApP68RQOztBe+AOk3BIJC1POexnqwDZZwV+Pw+srVlH7Q==}
+ react-email@6.4.0:
+ resolution: {integrity: sha512-WDS6zl60zPllvNj14xj9nOB2uJwhyNWB+PI+YpXukpAhIsc6zo3QcAVreZ7chZLMZRM2X/dzF1d6rRrZ7TGKmw==}
engines: {node: '>=20.0.0'}
hasBin: true
peerDependencies:
@@ -7268,8 +7444,8 @@ packages:
setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
- shadcn@4.8.0:
- resolution: {integrity: sha512-LAm3I/1FdoU/zu5GVG8Hbna4X9zlzEG5TeeCPXqsopkjvGk8QUF9OFhqeRN8oM6Oh/ynUI/yQHZxQAO3Ymcqsg==}
+ shadcn@4.8.1:
+ resolution: {integrity: sha512-B2J63uYwqfLJPNzZk2AAlonXG1aJhwv8xmXlUag8OZdV61CQB6570nkXLjU1u9XN1qXm0LpJhSFQ0ZhlOruA4Q==}
hasBin: true
sharp@0.34.5:
@@ -7616,13 +7792,17 @@ packages:
resolution: {integrity: sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==}
engines: {node: '>= 6.0.0'}
- turbo@2.9.14:
- resolution: {integrity: sha512-BQqXRr4UoWI3UPFrtznCLykYHxwxWh53iCB57x092jPMjIlW1wnm3N895g5irpiXmnxUhREBB0n6+y8BHhs4nw==}
+ turbo@2.9.15:
+ resolution: {integrity: sha512-VpKvD9Z0Hu/xrGUAYX1wnhfpqv835wIwGqeKfulvBPTOcDap0E3nFwyzCAVV85fB1sBcBDEfTP+7FSW7GzwWSQ==}
hasBin: true
tw-animate-css@1.4.0:
resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==}
+ type-check@0.4.0:
+ resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+ engines: {node: '>= 0.8.0'}
+
type-fest@5.6.0:
resolution: {integrity: sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==}
engines: {node: '>=20'}
@@ -7707,6 +7887,9 @@ packages:
peerDependencies:
browserslist: '>= 4.21.0'
+ uri-js@4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
use-callback-ref@1.3.3:
resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==}
engines: {node: '>=10'}
@@ -7891,6 +8074,10 @@ packages:
wildcard-match@5.1.4:
resolution: {integrity: sha512-wldeCaczs8XXq7hj+5d/F38JE2r7EXgb6WQDM84RVwxy81T/sxB5e9+uZLK9Q9oNz1mlvjut+QtvgaOQFPVq/g==}
+ word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+ engines: {node: '>=0.10.0'}
+
wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
@@ -7992,6 +8179,10 @@ packages:
resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==}
engines: {node: ^20.19.0 || ^22.12.0 || >=23}
+ yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
yocto-spinner@1.2.0:
resolution: {integrity: sha512-Yw0hUB6UA3o4YUgKy3oSe9a4cxoaZ9sBfYDw+JSxo6Id0KoJGoxzPA24qqUXYKBWABs/zDSGTz9kww7t3F0XGw==}
engines: {node: '>=18.19'}
@@ -8008,6 +8199,12 @@ packages:
peerDependencies:
zod: ^3.25.28 || ^4
+ zod-validation-error@4.0.2:
+ resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==}
+ engines: {node: '>=18.0.0'}
+ peerDependencies:
+ zod: ^3.25.0 || ^4.0.0
+
zod@3.25.76:
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
@@ -8146,20 +8343,20 @@ snapshots:
'@smithy/util-utf8': 2.3.0
tslib: 2.8.1
- '@aws-sdk/client-s3@3.1053.0':
+ '@aws-sdk/client-s3@3.1054.0':
dependencies:
'@aws-crypto/sha1-browser': 5.2.0
'@aws-crypto/sha256-browser': 5.2.0
'@aws-crypto/sha256-js': 5.2.0
- '@aws-sdk/core': 3.974.13
- '@aws-sdk/credential-provider-node': 3.972.44
- '@aws-sdk/middleware-bucket-endpoint': 3.972.15
+ '@aws-sdk/core': 3.974.14
+ '@aws-sdk/credential-provider-node': 3.972.45
+ '@aws-sdk/middleware-bucket-endpoint': 3.972.16
'@aws-sdk/middleware-expect-continue': 3.972.13
- '@aws-sdk/middleware-flexible-checksums': 3.974.21
+ '@aws-sdk/middleware-flexible-checksums': 3.974.22
'@aws-sdk/middleware-location-constraint': 3.972.11
- '@aws-sdk/middleware-sdk-s3': 3.972.42
+ '@aws-sdk/middleware-sdk-s3': 3.972.43
'@aws-sdk/middleware-ssec': 3.972.11
- '@aws-sdk/signature-v4-multi-region': 3.996.28
+ '@aws-sdk/signature-v4-multi-region': 3.996.29
'@aws-sdk/types': 3.973.9
'@smithy/core': 3.24.4
'@smithy/fetch-http-handler': 5.4.4
@@ -8167,10 +8364,10 @@ snapshots:
'@smithy/types': 4.14.2
tslib: 2.8.1
- '@aws-sdk/core@3.974.13':
+ '@aws-sdk/core@3.974.14':
dependencies:
'@aws-sdk/types': 3.973.9
- '@aws-sdk/xml-builder': 3.972.25
+ '@aws-sdk/xml-builder': 3.972.26
'@aws/lambda-invoke-store': 0.2.4
'@smithy/core': 3.24.4
'@smithy/signature-v4': 5.4.4
@@ -8183,17 +8380,17 @@ snapshots:
'@smithy/types': 4.14.2
tslib: 2.8.1
- '@aws-sdk/credential-provider-env@3.972.39':
+ '@aws-sdk/credential-provider-env@3.972.40':
dependencies:
- '@aws-sdk/core': 3.974.13
+ '@aws-sdk/core': 3.974.14
'@aws-sdk/types': 3.973.9
'@smithy/core': 3.24.4
'@smithy/types': 4.14.2
tslib: 2.8.1
- '@aws-sdk/credential-provider-http@3.972.41':
+ '@aws-sdk/credential-provider-http@3.972.42':
dependencies:
- '@aws-sdk/core': 3.974.13
+ '@aws-sdk/core': 3.974.14
'@aws-sdk/types': 3.973.9
'@smithy/core': 3.24.4
'@smithy/fetch-http-handler': 5.4.4
@@ -8201,75 +8398,75 @@ snapshots:
'@smithy/types': 4.14.2
tslib: 2.8.1
- '@aws-sdk/credential-provider-ini@3.972.43':
+ '@aws-sdk/credential-provider-ini@3.972.44':
dependencies:
- '@aws-sdk/core': 3.974.13
- '@aws-sdk/credential-provider-env': 3.972.39
- '@aws-sdk/credential-provider-http': 3.972.41
- '@aws-sdk/credential-provider-login': 3.972.43
- '@aws-sdk/credential-provider-process': 3.972.39
- '@aws-sdk/credential-provider-sso': 3.972.43
- '@aws-sdk/credential-provider-web-identity': 3.972.43
- '@aws-sdk/nested-clients': 3.997.11
+ '@aws-sdk/core': 3.974.14
+ '@aws-sdk/credential-provider-env': 3.972.40
+ '@aws-sdk/credential-provider-http': 3.972.42
+ '@aws-sdk/credential-provider-login': 3.972.44
+ '@aws-sdk/credential-provider-process': 3.972.40
+ '@aws-sdk/credential-provider-sso': 3.972.44
+ '@aws-sdk/credential-provider-web-identity': 3.972.44
+ '@aws-sdk/nested-clients': 3.997.12
'@aws-sdk/types': 3.973.9
'@smithy/core': 3.24.4
'@smithy/credential-provider-imds': 4.3.4
'@smithy/types': 4.14.2
tslib: 2.8.1
- '@aws-sdk/credential-provider-login@3.972.43':
+ '@aws-sdk/credential-provider-login@3.972.44':
dependencies:
- '@aws-sdk/core': 3.974.13
- '@aws-sdk/nested-clients': 3.997.11
+ '@aws-sdk/core': 3.974.14
+ '@aws-sdk/nested-clients': 3.997.12
'@aws-sdk/types': 3.973.9
'@smithy/core': 3.24.4
'@smithy/types': 4.14.2
tslib: 2.8.1
- '@aws-sdk/credential-provider-node@3.972.44':
+ '@aws-sdk/credential-provider-node@3.972.45':
dependencies:
- '@aws-sdk/credential-provider-env': 3.972.39
- '@aws-sdk/credential-provider-http': 3.972.41
- '@aws-sdk/credential-provider-ini': 3.972.43
- '@aws-sdk/credential-provider-process': 3.972.39
- '@aws-sdk/credential-provider-sso': 3.972.43
- '@aws-sdk/credential-provider-web-identity': 3.972.43
+ '@aws-sdk/credential-provider-env': 3.972.40
+ '@aws-sdk/credential-provider-http': 3.972.42
+ '@aws-sdk/credential-provider-ini': 3.972.44
+ '@aws-sdk/credential-provider-process': 3.972.40
+ '@aws-sdk/credential-provider-sso': 3.972.44
+ '@aws-sdk/credential-provider-web-identity': 3.972.44
'@aws-sdk/types': 3.973.9
'@smithy/core': 3.24.4
'@smithy/credential-provider-imds': 4.3.4
'@smithy/types': 4.14.2
tslib: 2.8.1
- '@aws-sdk/credential-provider-process@3.972.39':
+ '@aws-sdk/credential-provider-process@3.972.40':
dependencies:
- '@aws-sdk/core': 3.974.13
+ '@aws-sdk/core': 3.974.14
'@aws-sdk/types': 3.973.9
'@smithy/core': 3.24.4
'@smithy/types': 4.14.2
tslib: 2.8.1
- '@aws-sdk/credential-provider-sso@3.972.43':
+ '@aws-sdk/credential-provider-sso@3.972.44':
dependencies:
- '@aws-sdk/core': 3.974.13
- '@aws-sdk/nested-clients': 3.997.11
- '@aws-sdk/token-providers': 3.1052.0
+ '@aws-sdk/core': 3.974.14
+ '@aws-sdk/nested-clients': 3.997.12
+ '@aws-sdk/token-providers': 3.1054.0
'@aws-sdk/types': 3.973.9
'@smithy/core': 3.24.4
'@smithy/types': 4.14.2
tslib: 2.8.1
- '@aws-sdk/credential-provider-web-identity@3.972.43':
+ '@aws-sdk/credential-provider-web-identity@3.972.44':
dependencies:
- '@aws-sdk/core': 3.974.13
- '@aws-sdk/nested-clients': 3.997.11
+ '@aws-sdk/core': 3.974.14
+ '@aws-sdk/nested-clients': 3.997.12
'@aws-sdk/types': 3.973.9
'@smithy/core': 3.24.4
'@smithy/types': 4.14.2
tslib: 2.8.1
- '@aws-sdk/middleware-bucket-endpoint@3.972.15':
+ '@aws-sdk/middleware-bucket-endpoint@3.972.16':
dependencies:
- '@aws-sdk/core': 3.974.13
+ '@aws-sdk/core': 3.974.14
'@aws-sdk/types': 3.973.9
'@smithy/core': 3.24.4
'@smithy/types': 4.14.2
@@ -8282,12 +8479,12 @@ snapshots:
'@smithy/types': 4.14.2
tslib: 2.8.1
- '@aws-sdk/middleware-flexible-checksums@3.974.21':
+ '@aws-sdk/middleware-flexible-checksums@3.974.22':
dependencies:
'@aws-crypto/crc32': 5.2.0
'@aws-crypto/crc32c': 5.2.0
'@aws-crypto/util': 5.2.0
- '@aws-sdk/core': 3.974.13
+ '@aws-sdk/core': 3.974.14
'@aws-sdk/crc64-nvme': 3.972.9
'@aws-sdk/types': 3.973.9
'@smithy/core': 3.24.4
@@ -8300,13 +8497,12 @@ snapshots:
'@smithy/types': 4.14.2
tslib: 2.8.1
- '@aws-sdk/middleware-sdk-s3@3.972.42':
+ '@aws-sdk/middleware-sdk-s3@3.972.43':
dependencies:
- '@aws-sdk/core': 3.974.13
- '@aws-sdk/signature-v4-multi-region': 3.996.28
+ '@aws-sdk/core': 3.974.14
+ '@aws-sdk/signature-v4-multi-region': 3.996.29
'@aws-sdk/types': 3.973.9
'@smithy/core': 3.24.4
- '@smithy/signature-v4': 5.4.4
'@smithy/types': 4.14.2
tslib: 2.8.1
@@ -8316,12 +8512,12 @@ snapshots:
'@smithy/types': 4.14.2
tslib: 2.8.1
- '@aws-sdk/nested-clients@3.997.11':
+ '@aws-sdk/nested-clients@3.997.12':
dependencies:
'@aws-crypto/sha256-browser': 5.2.0
'@aws-crypto/sha256-js': 5.2.0
- '@aws-sdk/core': 3.974.13
- '@aws-sdk/signature-v4-multi-region': 3.996.28
+ '@aws-sdk/core': 3.974.14
+ '@aws-sdk/signature-v4-multi-region': 3.996.29
'@aws-sdk/types': 3.973.9
'@smithy/core': 3.24.4
'@smithy/fetch-http-handler': 5.4.4
@@ -8329,18 +8525,17 @@ snapshots:
'@smithy/types': 4.14.2
tslib: 2.8.1
- '@aws-sdk/signature-v4-multi-region@3.996.28':
+ '@aws-sdk/signature-v4-multi-region@3.996.29':
dependencies:
'@aws-sdk/types': 3.973.9
- '@smithy/core': 3.24.4
'@smithy/signature-v4': 5.4.4
'@smithy/types': 4.14.2
tslib: 2.8.1
- '@aws-sdk/token-providers@3.1052.0':
+ '@aws-sdk/token-providers@3.1054.0':
dependencies:
- '@aws-sdk/core': 3.974.13
- '@aws-sdk/nested-clients': 3.997.11
+ '@aws-sdk/core': 3.974.14
+ '@aws-sdk/nested-clients': 3.997.12
'@aws-sdk/types': 3.973.9
'@smithy/core': 3.24.4
'@smithy/types': 4.14.2
@@ -8355,9 +8550,8 @@ snapshots:
dependencies:
tslib: 2.8.1
- '@aws-sdk/xml-builder@3.972.25':
+ '@aws-sdk/xml-builder@3.972.26':
dependencies:
- '@nodable/entities': 2.1.0
'@smithy/types': 4.14.2
fast-xml-parser: 5.7.3
tslib: 2.8.1
@@ -8649,7 +8843,7 @@ snapshots:
optionalDependencies:
drizzle-orm: 1.0.0-rc.3(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(effect@4.0.0-beta.70)(pg@8.21.0)(zod@4.4.3)
- '@better-auth/infra@0.2.8(97af957868093b766f74c847443b5514)':
+ '@better-auth/infra@0.2.10(97af957868093b766f74c847443b5514)':
dependencies:
'@better-auth/core': 1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.1)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0)
'@better-auth/sso': 1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(@opentelemetry/api@1.9.1)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-auth@1.6.11(@opentelemetry/api@1.9.1)(drizzle-orm@1.0.0-rc.3(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(effect@4.0.0-beta.70)(pg@8.21.0)(zod@4.4.3))(next@16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(pg@8.21.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.7))(better-call@1.3.5(zod@4.4.3))
@@ -9109,6 +9303,36 @@ snapshots:
'@esbuild/win32-x64@0.28.0':
optional: true
+ '@eslint-community/eslint-utils@4.9.1(eslint@10.4.0(jiti@2.7.0))':
+ dependencies:
+ eslint: 10.4.0(jiti@2.7.0)
+ eslint-visitor-keys: 3.4.3
+
+ '@eslint-community/regexpp@4.12.2': {}
+
+ '@eslint/config-array@0.23.5':
+ dependencies:
+ '@eslint/object-schema': 3.0.5
+ debug: 4.4.3
+ minimatch: 10.2.5
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/config-helpers@0.6.0':
+ dependencies:
+ '@eslint/core': 1.2.1
+
+ '@eslint/core@1.2.1':
+ dependencies:
+ '@types/json-schema': 7.0.15
+
+ '@eslint/object-schema@3.0.5': {}
+
+ '@eslint/plugin-kit@0.7.1':
+ dependencies:
+ '@eslint/core': 1.2.1
+ levn: 0.4.1
+
'@floating-ui/core@1.7.5':
dependencies:
'@floating-ui/utils': 0.2.11
@@ -9138,6 +9362,22 @@ snapshots:
dependencies:
hono: 4.12.23
+ '@humanfs/core@0.19.2':
+ dependencies:
+ '@humanfs/types': 0.15.0
+
+ '@humanfs/node@0.16.8':
+ dependencies:
+ '@humanfs/core': 0.19.2
+ '@humanfs/types': 0.15.0
+ '@humanwhocodes/retry': 0.4.3
+
+ '@humanfs/types@0.15.0': {}
+
+ '@humanwhocodes/module-importer@1.0.1': {}
+
+ '@humanwhocodes/retry@0.4.3': {}
+
'@iarna/toml@2.2.5': {}
'@img/colour@1.1.0': {}
@@ -9263,7 +9503,7 @@ snapshots:
optionalDependencies:
'@types/node': 25.9.1
- '@ioredis/commands@1.5.1': {}
+ '@ioredis/commands@1.10.0': {}
'@jest/schemas@29.6.3':
dependencies:
@@ -10291,7 +10531,7 @@ snapshots:
react: 19.2.6
react-dom: 19.2.6(react@19.2.6)
- '@react-email/ui@6.3.3(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
+ '@react-email/ui@6.4.0(@opentelemetry/api@1.9.1)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
dependencies:
esbuild: 0.28.0
next: 16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
@@ -10562,7 +10802,7 @@ snapshots:
dependencies:
tslib: 2.8.1
- '@swc/helpers@0.5.22':
+ '@swc/helpers@0.5.23':
dependencies:
tslib: 2.8.1
@@ -10654,7 +10894,7 @@ snapshots:
'@tanstack/devtools-event-client@0.4.3': {}
- '@tanstack/form-core@1.32.0':
+ '@tanstack/form-core@1.32.1':
dependencies:
'@tanstack/devtools-event-client': 0.4.3
'@tanstack/pacer-lite': 0.1.1
@@ -10670,9 +10910,9 @@ snapshots:
'@tanstack/query-core@5.100.14': {}
- '@tanstack/react-form@1.32.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
+ '@tanstack/react-form@1.32.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6)':
dependencies:
- '@tanstack/form-core': 1.32.0
+ '@tanstack/form-core': 1.32.1
'@tanstack/react-store': 0.9.3(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
react: 19.2.6
transitivePeerDependencies:
@@ -11006,22 +11246,22 @@ snapshots:
minimatch: 10.2.5
path-browserify: 1.0.1
- '@turbo/darwin-64@2.9.14':
+ '@turbo/darwin-64@2.9.15':
optional: true
- '@turbo/darwin-arm64@2.9.14':
+ '@turbo/darwin-arm64@2.9.15':
optional: true
- '@turbo/linux-64@2.9.14':
+ '@turbo/linux-64@2.9.15':
optional: true
- '@turbo/linux-arm64@2.9.14':
+ '@turbo/linux-arm64@2.9.15':
optional: true
- '@turbo/windows-64@2.9.14':
+ '@turbo/windows-64@2.9.15':
optional: true
- '@turbo/windows-arm64@2.9.14':
+ '@turbo/windows-arm64@2.9.15':
optional: true
'@tybys/wasm-util@0.10.2':
@@ -11097,6 +11337,8 @@ snapshots:
'@types/jsesc@2.5.1': {}
+ '@types/json-schema@7.0.15': {}
+
'@types/mdast@4.0.4':
dependencies:
'@types/unist': 3.0.3
@@ -11158,36 +11400,36 @@ snapshots:
'@typescript-eslint/types@8.60.0': {}
- '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260526.1':
+ '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260527.1':
optional: true
- '@typescript/native-preview-darwin-x64@7.0.0-dev.20260526.1':
+ '@typescript/native-preview-darwin-x64@7.0.0-dev.20260527.1':
optional: true
- '@typescript/native-preview-linux-arm64@7.0.0-dev.20260526.1':
+ '@typescript/native-preview-linux-arm64@7.0.0-dev.20260527.1':
optional: true
- '@typescript/native-preview-linux-arm@7.0.0-dev.20260526.1':
+ '@typescript/native-preview-linux-arm@7.0.0-dev.20260527.1':
optional: true
- '@typescript/native-preview-linux-x64@7.0.0-dev.20260526.1':
+ '@typescript/native-preview-linux-x64@7.0.0-dev.20260527.1':
optional: true
- '@typescript/native-preview-win32-arm64@7.0.0-dev.20260526.1':
+ '@typescript/native-preview-win32-arm64@7.0.0-dev.20260527.1':
optional: true
- '@typescript/native-preview-win32-x64@7.0.0-dev.20260526.1':
+ '@typescript/native-preview-win32-x64@7.0.0-dev.20260527.1':
optional: true
- '@typescript/native-preview@7.0.0-dev.20260526.1':
+ '@typescript/native-preview@7.0.0-dev.20260527.1':
optionalDependencies:
- '@typescript/native-preview-darwin-arm64': 7.0.0-dev.20260526.1
- '@typescript/native-preview-darwin-x64': 7.0.0-dev.20260526.1
- '@typescript/native-preview-linux-arm': 7.0.0-dev.20260526.1
- '@typescript/native-preview-linux-arm64': 7.0.0-dev.20260526.1
- '@typescript/native-preview-linux-x64': 7.0.0-dev.20260526.1
- '@typescript/native-preview-win32-arm64': 7.0.0-dev.20260526.1
- '@typescript/native-preview-win32-x64': 7.0.0-dev.20260526.1
+ '@typescript/native-preview-darwin-arm64': 7.0.0-dev.20260527.1
+ '@typescript/native-preview-darwin-x64': 7.0.0-dev.20260527.1
+ '@typescript/native-preview-linux-arm': 7.0.0-dev.20260527.1
+ '@typescript/native-preview-linux-arm64': 7.0.0-dev.20260527.1
+ '@typescript/native-preview-linux-x64': 7.0.0-dev.20260527.1
+ '@typescript/native-preview-win32-arm64': 7.0.0-dev.20260527.1
+ '@typescript/native-preview-win32-x64': 7.0.0-dev.20260527.1
'@uiw/color-convert@2.10.3(@babel/runtime@7.29.7)':
dependencies:
@@ -11317,6 +11559,12 @@ snapshots:
mime-types: 3.0.2
negotiator: 1.0.0
+ acorn-jsx@5.3.2(acorn@8.16.0):
+ dependencies:
+ acorn: 8.16.0
+
+ acorn@8.16.0: {}
+
agent-base@7.1.4: {}
agent-install@0.0.5:
@@ -11340,6 +11588,13 @@ snapshots:
optionalDependencies:
ajv: 8.20.0
+ ajv@6.15.0:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-json-stable-stringify: 2.1.0
+ json-schema-traverse: 0.4.1
+ uri-js: 4.4.1
+
ajv@8.20.0:
dependencies:
fast-deep-equal: 3.1.3
@@ -11529,7 +11784,7 @@ snapshots:
dependencies:
baseline-browser-mapping: 2.10.32
caniuse-lite: 1.0.30001793
- electron-to-chromium: 1.5.361
+ electron-to-chromium: 1.5.362
node-releases: 2.0.46
update-browserslist-db: 1.2.3(browserslist@4.28.2)
@@ -11629,7 +11884,7 @@ snapshots:
clsx@2.1.1: {}
- cluster-key-slot@1.1.2: {}
+ cluster-key-slot@1.1.1: {}
cmdk@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.15))(@types/react@19.2.15)(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
dependencies:
@@ -11796,6 +12051,8 @@ snapshots:
optionalDependencies:
babel-plugin-macros: 3.1.0
+ deep-is@0.1.4: {}
+
deepmerge-ts@7.1.5: {}
deepmerge@4.3.1: {}
@@ -11817,7 +12074,7 @@ snapshots:
dequal@2.0.3: {}
- deslop-js@0.0.12(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0):
+ deslop-js@0.0.13(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0):
dependencies:
'@oxc-project/types': 0.132.0
fast-glob: 3.3.3
@@ -11866,7 +12123,7 @@ snapshots:
dependencies:
domelementtype: 2.3.0
- dompurify@3.4.5:
+ dompurify@3.4.6:
optionalDependencies:
'@types/trusted-types': 2.0.7
@@ -11939,7 +12196,7 @@ snapshots:
uuid: 14.0.0
yaml: 2.9.0
- electron-to-chromium@1.5.361: {}
+ electron-to-chromium@1.5.362: {}
emoji-regex-xs@1.0.0: {}
@@ -12066,8 +12323,21 @@ snapshots:
escape-html@1.0.3: {}
+ escape-string-regexp@4.0.0: {}
+
escape-string-regexp@5.0.0: {}
+ eslint-plugin-react-hooks@7.1.1(eslint@10.4.0(jiti@2.7.0)):
+ dependencies:
+ '@babel/core': 7.29.7
+ '@babel/parser': 7.29.7
+ eslint: 10.4.0(jiti@2.7.0)
+ hermes-parser: 0.25.1
+ zod: 4.4.3
+ zod-validation-error: 4.0.2(zod@4.4.3)
+ transitivePeerDependencies:
+ - supports-color
+
eslint-scope@9.1.2:
dependencies:
'@types/esrecurse': 4.3.1
@@ -12075,10 +12345,59 @@ snapshots:
esrecurse: 4.3.0
estraverse: 5.3.0
+ eslint-visitor-keys@3.4.3: {}
+
eslint-visitor-keys@5.0.1: {}
+ eslint@10.4.0(jiti@2.7.0):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.0(jiti@2.7.0))
+ '@eslint-community/regexpp': 4.12.2
+ '@eslint/config-array': 0.23.5
+ '@eslint/config-helpers': 0.6.0
+ '@eslint/core': 1.2.1
+ '@eslint/plugin-kit': 0.7.1
+ '@humanfs/node': 0.16.8
+ '@humanwhocodes/module-importer': 1.0.1
+ '@humanwhocodes/retry': 0.4.3
+ '@types/estree': 1.0.9
+ ajv: 6.15.0
+ cross-spawn: 7.0.6
+ debug: 4.4.3
+ escape-string-regexp: 4.0.0
+ eslint-scope: 9.1.2
+ eslint-visitor-keys: 5.0.1
+ espree: 11.2.0
+ esquery: 1.7.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 8.0.0
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ ignore: 5.3.2
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ json-stable-stringify-without-jsonify: 1.0.1
+ minimatch: 10.2.5
+ natural-compare: 1.4.0
+ optionator: 0.9.4
+ optionalDependencies:
+ jiti: 2.7.0
+ transitivePeerDependencies:
+ - supports-color
+
+ espree@11.2.0:
+ dependencies:
+ acorn: 8.16.0
+ acorn-jsx: 5.3.2(acorn@8.16.0)
+ eslint-visitor-keys: 5.0.1
+
esprima@4.0.1: {}
+ esquery@1.7.0:
+ dependencies:
+ estraverse: 5.3.0
+
esrecurse@4.3.0:
dependencies:
estraverse: 5.3.0
@@ -12091,6 +12410,8 @@ snapshots:
dependencies:
'@types/estree': 1.0.9
+ esutils@2.0.3: {}
+
etag@1.8.1: {}
events@3.3.0: {}
@@ -12188,6 +12509,10 @@ snapshots:
fast-json-patch@3.1.1: {}
+ fast-json-stable-stringify@2.1.0: {}
+
+ fast-levenshtein@2.0.6: {}
+
fast-string-truncated-width@3.0.3: {}
fast-string-width@3.0.2:
@@ -12243,6 +12568,10 @@ snapshots:
dependencies:
is-unicode-supported: 2.1.0
+ file-entry-cache@8.0.0:
+ dependencies:
+ flat-cache: 4.0.1
+
fill-range@7.1.1:
dependencies:
to-regex-range: 5.0.1
@@ -12260,9 +12589,21 @@ snapshots:
find-my-way-ts@0.1.6: {}
+ find-up@5.0.0:
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+
+ flat-cache@4.0.1:
+ dependencies:
+ flatted: 3.4.2
+ keyv: 4.5.4
+
+ flatted@3.4.2: {}
+
fontkit@2.0.4:
dependencies:
- '@swc/helpers': 0.5.22
+ '@swc/helpers': 0.5.23
brotli: 1.3.3
clone: 2.1.2
dfa: 1.2.0
@@ -12363,6 +12704,10 @@ snapshots:
dependencies:
is-glob: 4.0.3
+ glob-parent@6.0.2:
+ dependencies:
+ is-glob: 4.0.3
+
glob@13.0.6:
dependencies:
minimatch: 10.2.5
@@ -12437,6 +12782,12 @@ snapshots:
'@types/set-cookie-parser': 2.4.10
set-cookie-parser: 3.1.0
+ hermes-estree@0.25.1: {}
+
+ hermes-parser@0.25.1:
+ dependencies:
+ hermes-estree: 0.25.1
+
hono@4.12.23: {}
hookable@6.1.1: {}
@@ -12504,6 +12855,8 @@ snapshots:
import-without-cache@0.4.0: {}
+ imurmurhash@0.1.4: {}
+
indent-string@4.0.0: {}
inherits@2.0.4: {}
@@ -12514,14 +12867,12 @@ snapshots:
inline-style-parser@0.2.7: {}
- ioredis@5.10.1:
+ ioredis@5.11.0:
dependencies:
- '@ioredis/commands': 1.5.1
- cluster-key-slot: 1.1.2
+ '@ioredis/commands': 1.10.0
+ cluster-key-slot: 1.1.1
debug: 4.4.3
denque: 2.1.0
- lodash.defaults: 4.2.0
- lodash.isarguments: 3.1.0
redis-errors: 1.2.0
redis-parser: 3.0.0
standard-as-callback: 2.1.0
@@ -12657,14 +13008,20 @@ snapshots:
jsesc@3.1.0: {}
+ json-buffer@3.0.1: {}
+
json-parse-even-better-errors@2.3.1: {}
+ json-schema-traverse@0.4.1: {}
+
json-schema-traverse@1.0.0: {}
json-schema-typed@8.0.2: {}
json-schema@0.4.0: {}
+ json-stable-stringify-without-jsonify@1.0.1: {}
+
json5@2.2.3: {}
jsonc-parser@3.3.1: {}
@@ -12684,6 +13041,10 @@ snapshots:
readable-stream: 2.3.8
setimmediate: 1.0.5
+ keyv@4.5.4:
+ dependencies:
+ json-buffer: 3.0.1
+
kleur@3.0.3: {}
kleur@4.1.5: {}
@@ -12759,6 +13120,11 @@ snapshots:
leven@3.1.0: {}
+ levn@0.4.1:
+ dependencies:
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+
libphonenumber-js@1.13.3: {}
lie@3.3.0:
@@ -12825,12 +13191,12 @@ snapshots:
linkifyjs@4.3.3: {}
+ locate-path@6.0.0:
+ dependencies:
+ p-locate: 5.0.0
+
lodash.debounce@4.0.8: {}
- lodash.defaults@4.2.0: {}
-
- lodash.isarguments@3.1.0: {}
-
log-symbols@6.0.0:
dependencies:
chalk: 5.6.2
@@ -13213,6 +13579,8 @@ snapshots:
nanostores@1.3.0: {}
+ natural-compare@1.4.0: {}
+
negotiator@0.6.3: {}
negotiator@1.0.0: {}
@@ -13283,7 +13651,7 @@ snapshots:
dependencies:
asn1: 0.2.6
- nodemailer@8.0.8: {}
+ nodemailer@8.0.9: {}
normalize-path@3.0.0: {}
@@ -13354,6 +13722,15 @@ snapshots:
openapi-types@12.1.3: {}
+ optionator@0.9.4:
+ dependencies:
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ word-wrap: 1.2.5
+
ora@8.2.0:
dependencies:
chalk: 5.6.2
@@ -13457,7 +13834,7 @@ snapshots:
- '@emnapi/core'
- '@emnapi/runtime'
- oxlint-plugin-react-doctor@0.2.6:
+ oxlint-plugin-react-doctor@0.2.9:
dependencies:
'@typescript-eslint/types': 8.60.0
eslint-scope: 9.1.2
@@ -13485,6 +13862,14 @@ snapshots:
'@oxlint/binding-win32-ia32-msvc': 1.67.0
'@oxlint/binding-win32-x64-msvc': 1.67.0
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-locate@5.0.0:
+ dependencies:
+ p-limit: 3.1.0
+
pako@0.2.9: {}
pako@1.0.11: {}
@@ -13523,6 +13908,8 @@ snapshots:
path-browserify@1.0.1: {}
+ path-exists@4.0.0: {}
+
path-expression-matcher@1.5.0: {}
path-key@3.1.1: {}
@@ -13636,6 +14023,8 @@ snapshots:
powershell-utils@0.1.0: {}
+ prelude-ls@1.2.1: {}
+
prettier@3.8.3: {}
pretty-format@27.5.1:
@@ -13749,6 +14138,8 @@ snapshots:
dependencies:
commander: 10.0.1
+ punycode@2.3.1: {}
+
pure-rand@8.4.0: {}
pvtsutils@1.3.6:
@@ -13784,22 +14175,25 @@ snapshots:
iconv-lite: 0.7.2
unpipe: 1.0.0
- react-doctor@0.2.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0):
+ react-doctor@0.2.9(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(eslint@10.4.0(jiti@2.7.0)):
dependencies:
'@effect/platform-node-shared': 4.0.0-beta.70(effect@4.0.0-beta.70)
agent-install: 0.0.5
conf: 15.1.0
- deslop-js: 0.0.12(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
+ deslop-js: 0.0.13(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
effect: 4.0.0-beta.70
+ eslint-plugin-react-hooks: 7.1.1(eslint@10.4.0(jiti@2.7.0))
oxlint: 1.67.0
- oxlint-plugin-react-doctor: 0.2.6
+ oxlint-plugin-react-doctor: 0.2.9
prompts: 2.4.2
typescript: 6.0.3
transitivePeerDependencies:
- '@emnapi/core'
- '@emnapi/runtime'
- bufferutil
+ - eslint
- oxlint-tsgolint
+ - supports-color
- utf-8-validate
- vite-plus
@@ -13808,7 +14202,7 @@ snapshots:
react: 19.2.6
scheduler: 0.27.0
- react-email@6.3.3(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
+ react-email@6.4.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6):
dependencies:
'@babel/parser': 7.27.0
'@babel/traverse': 7.27.0
@@ -14000,7 +14394,7 @@ snapshots:
reusify@1.1.0: {}
- rolldown-plugin-dts@0.25.1(@typescript/native-preview@7.0.0-dev.20260526.1)(oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rolldown@1.0.2)(typescript@6.0.3):
+ rolldown-plugin-dts@0.25.1(@typescript/native-preview@7.0.0-dev.20260527.1)(oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rolldown@1.0.2)(typescript@6.0.3):
dependencies:
'@babel/generator': 8.0.0-rc.5
'@babel/helper-validator-identifier': 8.0.0-rc.5
@@ -14012,7 +14406,7 @@ snapshots:
obug: 2.1.1
rolldown: 1.0.2
optionalDependencies:
- '@typescript/native-preview': 7.0.0-dev.20260526.1
+ '@typescript/native-preview': 7.0.0-dev.20260527.1
typescript: 6.0.3
transitivePeerDependencies:
- oxc-resolver
@@ -14129,7 +14523,7 @@ snapshots:
setprototypeof@1.2.0: {}
- shadcn@4.8.0(@types/node@25.9.1)(babel-plugin-macros@3.1.0)(typescript@6.0.3):
+ shadcn@4.8.1(@types/node@25.9.1)(babel-plugin-macros@3.1.0)(typescript@6.0.3):
dependencies:
'@babel/core': 7.29.7
'@babel/parser': 7.29.7
@@ -14470,7 +14864,7 @@ snapshots:
minimist: 1.2.8
strip-bom: 3.0.0
- tsdown@0.22.0(@typescript/native-preview@7.0.0-dev.20260526.1)(oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(tsx@4.22.3)(typescript@6.0.3):
+ tsdown@0.22.0(@typescript/native-preview@7.0.0-dev.20260527.1)(oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(tsx@4.22.3)(typescript@6.0.3):
dependencies:
ansis: 4.3.0
cac: 7.0.0
@@ -14481,7 +14875,7 @@ snapshots:
obug: 2.1.1
picomatch: 4.0.4
rolldown: 1.0.2
- rolldown-plugin-dts: 0.25.1(@typescript/native-preview@7.0.0-dev.20260526.1)(oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rolldown@1.0.2)(typescript@6.0.3)
+ rolldown-plugin-dts: 0.25.1(@typescript/native-preview@7.0.0-dev.20260527.1)(oxc-resolver@11.19.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rolldown@1.0.2)(typescript@6.0.3)
semver: 7.8.1
tinyexec: 1.2.2
tinyglobby: 0.2.16
@@ -14510,17 +14904,21 @@ snapshots:
dependencies:
tslib: 1.14.1
- turbo@2.9.14:
+ turbo@2.9.15:
optionalDependencies:
- '@turbo/darwin-64': 2.9.14
- '@turbo/darwin-arm64': 2.9.14
- '@turbo/linux-64': 2.9.14
- '@turbo/linux-arm64': 2.9.14
- '@turbo/windows-64': 2.9.14
- '@turbo/windows-arm64': 2.9.14
+ '@turbo/darwin-64': 2.9.15
+ '@turbo/darwin-arm64': 2.9.15
+ '@turbo/linux-64': 2.9.15
+ '@turbo/linux-arm64': 2.9.15
+ '@turbo/windows-64': 2.9.15
+ '@turbo/windows-arm64': 2.9.15
tw-animate-css@1.4.0: {}
+ type-check@0.4.0:
+ dependencies:
+ prelude-ls: 1.2.1
+
type-fest@5.6.0:
dependencies:
tagged-tag: 1.0.0
@@ -14613,6 +15011,10 @@ snapshots:
escalade: 3.2.0
picocolors: 1.1.1
+ uri-js@4.4.1:
+ dependencies:
+ punycode: 2.3.1
+
use-callback-ref@1.3.3(@types/react@19.2.15)(react@19.2.6):
dependencies:
react: 19.2.6
@@ -14735,6 +15137,8 @@ snapshots:
wildcard-match@5.1.4: {}
+ word-wrap@1.2.5: {}
+
wrap-ansi@7.0.0:
dependencies:
ansi-styles: 4.3.0
@@ -14811,6 +15215,8 @@ snapshots:
y18n: 5.0.8
yargs-parser: 22.0.0
+ yocto-queue@0.1.0: {}
+
yocto-spinner@1.2.0:
dependencies:
yoctocolors: 2.1.2
@@ -14827,6 +15233,10 @@ snapshots:
dependencies:
zod: 4.4.3
+ zod-validation-error@4.0.2(zod@4.4.3):
+ dependencies:
+ zod: 4.4.3
+
zod@3.25.76: {}
zod@4.4.3: {}
diff --git a/tooling/package.json b/tooling/package.json
index 3f803ab47..0048128ab 100644
--- a/tooling/package.json
+++ b/tooling/package.json
@@ -12,7 +12,7 @@
"@reactive-resume/config": "workspace:*",
"@reactive-resume/env": "workspace:*",
"@types/pg": "^8.20.0",
- "@typescript/native-preview": "7.0.0-dev.20260526.1",
+ "@typescript/native-preview": "7.0.0-dev.20260527.1",
"drizzle-orm": "1.0.0-rc.3",
"pg": "^8.21.0",
"tsx": "^4.22.3"