From 192880e416e94742661c51de6b27b07448b3d3ae Mon Sep 17 00:00:00 2001 From: Amruth Pillai Date: Wed, 18 Mar 2026 22:03:24 +0100 Subject: [PATCH] use vite+ --- .devcontainer/devcontainer.json | 56 +- .github/.well-known/funding.json | 132 +- .ncurc.cjs | 16 +- .oxfmtrc.json | 25 - .oxlintrc.json | 54 - .vite-hooks/pre-commit | 1 + .vscode/extensions.json | 13 +- .vscode/settings.json | 76 +- AGENTS.md | 78 + components.json | 48 +- docs/changelog/index.mdx | 130 +- docs/community/spotlight.mdx | 6 +- docs/contributing/architecture.mdx | 52 +- docs/contributing/development.mdx | 22 +- docs/contributing/translations.mdx | 48 +- docs/docs.json | 296 +- docs/getting-started/index.mdx | 66 +- docs/getting-started/quickstart.mdx | 48 +- .../guides/accessing-the-previous-version.mdx | 8 +- docs/guides/adding-a-cover-letter.mdx | 36 +- docs/guides/checking-service-status.mdx | 24 +- docs/guides/choosing-a-template.mdx | 36 +- docs/guides/creating-an-account.mdx | 12 +- docs/guides/creating-your-first-resume.mdx | 2 +- docs/guides/deleting-your-account.mdx | 4 +- docs/guides/exporting-your-resume.mdx | 2106 +-- docs/guides/fitting-content-on-a-page.mdx | 44 +- docs/guides/json-resume-schema.mdx | 3880 ++-- docs/guides/linking-social-accounts.mdx | 8 +- docs/guides/moving-items-between-sections.mdx | 16 +- docs/guides/selecting-page-format.mdx | 64 +- docs/guides/setting-up-passkeys.mdx | 10 +- .../setting-up-two-factor-authentication.mdx | 8 +- docs/guides/sharing-your-resume-publicly.mdx | 68 +- docs/guides/updating-your-profile.mdx | 4 +- docs/guides/using-ai.mdx | 12 +- docs/guides/using-custom-css.mdx | 90 +- docs/guides/using-private-notes.mdx | 20 +- docs/guides/using-the-api.mdx | 4 +- docs/guides/using-the-mcp-server.mdx | 40 +- docs/guides/using-the-patch-api.mdx | 16 +- docs/legal/privacy-policy.mdx | 6 +- docs/legal/terms-of-service.mdx | 6 +- docs/self-hosting/docker.mdx | 136 +- docs/self-hosting/examples.mdx | 22 +- docs/self-hosting/migration.mdx | 112 +- docs/self-hosting/sso.mdx | 48 +- docs/spec.json | 6190 +++---- drizzle.config.ts | 12 +- knip.json | 14 +- lingui.config.ts | 144 +- locales/af-ZA.po | 1 - locales/am-ET.po | 1 - locales/ar-SA.po | 1 - locales/az-AZ.po | 1 - locales/bg-BG.po | 1 - locales/bn-BD.po | 1 - locales/ca-ES.po | 1 - locales/cs-CZ.po | 1 - locales/da-DK.po | 1 - locales/de-DE.po | 1 - locales/el-GR.po | 1 - locales/en-GB.po | 1 - locales/es-ES.po | 1 - locales/fa-IR.po | 1 - locales/fi-FI.po | 1 - locales/fr-FR.po | 1 - locales/he-IL.po | 1 - locales/hi-IN.po | 1 - locales/hu-HU.po | 1 - locales/id-ID.po | 1 - locales/it-IT.po | 1 - locales/ja-JP.po | 1 - locales/km-KH.po | 1 - locales/kn-IN.po | 1 - locales/ko-KR.po | 1 - locales/lt-LT.po | 1 - locales/lv-LV.po | 1 - locales/ml-IN.po | 1 - locales/mr-IN.po | 1 - locales/ms-MY.po | 1 - locales/ne-NP.po | 1 - locales/nl-NL.po | 1 - locales/no-NO.po | 1 - locales/or-IN.po | 1 - locales/pl-PL.po | 1 - locales/pt-BR.po | 1 - locales/pt-PT.po | 1 - locales/ro-RO.po | 1 - locales/ru-RU.po | 1 - locales/sk-SK.po | 1 - locales/sl-SI.po | 1 - locales/sq-AL.po | 1 - locales/sr-SP.po | 1 - locales/sv-SE.po | 1 - locales/ta-IN.po | 1 - locales/te-IN.po | 1 - locales/th-TH.po | 1 - locales/tr-TR.po | 1 - locales/uk-UA.po | 1 - locales/uz-UZ.po | 1 - locales/vi-VN.po | 1 - locales/zh-CN.po | 1 - locales/zh-TW.po | 1 - .../snapshot.json | 3296 ++-- .../snapshot.json | 3786 ++-- .../snapshot.json | 3702 ++-- .../20260305091016_closed_pixie/snapshot.json | 3728 ++-- .../snapshot.json | 3754 ++-- .../snapshot.json | 3838 ++-- package.json | 348 +- plugins/1.migrate.ts | 34 +- pnpm-lock.yaml | 1453 +- public/funding.json | 132 +- scripts/database/migrate.ts | 24 +- scripts/database/reset.ts | 38 +- scripts/fonts/generate.ts | 246 +- scripts/fonts/types.ts | 72 +- scripts/migration/resume.ts | 722 +- scripts/migration/test-database.ts | 18 +- scripts/migration/user.ts | 634 +- skills/resume-builder/references/schema.md | 282 +- src/components/ai/chat.tsx | 626 +- src/components/animation/comet-card.tsx | 120 +- src/components/animation/count-up.tsx | 202 +- src/components/animation/spotlight.tsx | 196 +- src/components/animation/text-mask.tsx | 188 +- src/components/command-palette/index.tsx | 186 +- src/components/command-palette/pages/base.tsx | 20 +- .../command-palette/pages/navigation.tsx | 188 +- .../pages/preferences/index.tsx | 34 +- .../pages/preferences/language.tsx | 32 +- .../pages/preferences/theme.tsx | 36 +- .../command-palette/pages/resumes.tsx | 116 +- src/components/command-palette/store.ts | 74 +- src/components/input/chip-input.tsx | 504 +- src/components/input/color-picker.tsx | 52 +- src/components/input/github-stars-button.tsx | 40 +- src/components/input/icon-picker.tsx | 144 +- src/components/input/rich-input.module.css | 290 +- src/components/input/rich-input.tsx | 1254 +- src/components/input/url-input.tsx | 112 +- .../input/visually-hidden-input.tsx | 214 +- .../layout/breakpoint-indicator.tsx | 58 +- src/components/layout/error-screen.tsx | 32 +- src/components/layout/loading-screen.tsx | 16 +- src/components/layout/not-found-screen.tsx | 36 +- src/components/level/combobox.tsx | 32 +- src/components/level/display.tsx | 106 +- src/components/locale/combobox.tsx | 40 +- .../resume/hooks/use-css-variables.tsx | 60 +- src/components/resume/hooks/use-webfonts.tsx | 78 +- src/components/resume/preview.module.css | 198 +- src/components/resume/preview.tsx | 244 +- .../resume/shared/get-section-component.tsx | 326 +- .../resume/shared/items/awards-item.tsx | 64 +- .../shared/items/certifications-item.tsx | 74 +- .../resume/shared/items/cover-letter-item.tsx | 24 +- .../resume/shared/items/education-item.tsx | 78 +- .../resume/shared/items/experience-item.tsx | 138 +- .../resume/shared/items/interests-item.tsx | 32 +- .../resume/shared/items/languages-item.tsx | 28 +- .../resume/shared/items/profiles-item.tsx | 46 +- .../resume/shared/items/projects-item.tsx | 60 +- .../resume/shared/items/publications-item.tsx | 74 +- .../resume/shared/items/references-item.tsx | 80 +- .../resume/shared/items/skills-item.tsx | 36 +- .../resume/shared/items/summary-item.tsx | 14 +- .../resume/shared/items/volunteer-item.tsx | 68 +- src/components/resume/shared/linked-title.tsx | 24 +- src/components/resume/shared/page-icon.tsx | 16 +- src/components/resume/shared/page-level.tsx | 6 +- src/components/resume/shared/page-link.tsx | 18 +- src/components/resume/shared/page-picture.tsx | 48 +- src/components/resume/shared/page-section.tsx | 44 +- src/components/resume/shared/page-summary.tsx | 22 +- src/components/resume/store/resume.ts | 70 +- src/components/resume/templates/azurill.tsx | 220 +- src/components/resume/templates/bronzor.tsx | 142 +- src/components/resume/templates/chikorita.tsx | 184 +- src/components/resume/templates/ditgar.tsx | 168 +- src/components/resume/templates/ditto.tsx | 160 +- src/components/resume/templates/gengar.tsx | 200 +- src/components/resume/templates/glalie.tsx | 176 +- src/components/resume/templates/kakuna.tsx | 134 +- src/components/resume/templates/lapras.tsx | 174 +- src/components/resume/templates/leafish.tsx | 162 +- src/components/resume/templates/onyx.tsx | 130 +- src/components/resume/templates/pikachu.tsx | 182 +- src/components/resume/templates/rhyhorn.tsx | 134 +- src/components/resume/templates/types.ts | 4 +- src/components/theme/combobox.tsx | 30 +- src/components/theme/provider.tsx | 50 +- src/components/theme/toggle-button.tsx | 84 +- src/components/typography/combobox.tsx | 120 +- src/components/typography/font-display.tsx | 52 +- src/components/typography/types.ts | 20 +- src/components/typography/webfontlist.json | 15000 ++++++++-------- src/components/ui/accordion.tsx | 70 +- src/components/ui/alert-dialog.tsx | 216 +- src/components/ui/alert.tsx | 70 +- src/components/ui/avatar.tsx | 130 +- src/components/ui/badge.tsx | 54 +- src/components/ui/brand-icon.tsx | 34 +- src/components/ui/button-group.tsx | 108 +- src/components/ui/button.tsx | 62 +- src/components/ui/combobox.tsx | 712 +- src/components/ui/command.tsx | 224 +- src/components/ui/context-menu.tsx | 338 +- src/components/ui/copyright.tsx | 62 +- src/components/ui/dialog.tsx | 156 +- src/components/ui/dropdown-menu.tsx | 374 +- src/components/ui/form.tsx | 198 +- src/components/ui/hover-card.tsx | 58 +- src/components/ui/input-group.tsx | 202 +- src/components/ui/input-otp.tsx | 114 +- src/components/ui/input.tsx | 22 +- src/components/ui/kbd.tsx | 22 +- src/components/ui/label.tsx | 20 +- src/components/ui/popover.tsx | 76 +- src/components/ui/resizable.tsx | 56 +- src/components/ui/scroll-area.tsx | 52 +- src/components/ui/separator.tsx | 22 +- src/components/ui/sheet.tsx | 120 +- src/components/ui/sidebar.tsx | 1046 +- src/components/ui/skeleton.tsx | 2 +- src/components/ui/slider.tsx | 70 +- src/components/ui/sonner.tsx | 60 +- src/components/ui/spinner.tsx | 2 +- src/components/ui/switch.tsx | 40 +- src/components/ui/tabs.tsx | 94 +- src/components/ui/textarea.tsx | 20 +- src/components/ui/toggle.tsx | 46 +- src/components/ui/tooltip.tsx | 68 +- src/components/user/dropdown-menu.tsx | 170 +- src/dialogs/api-key/create.tsx | 312 +- src/dialogs/auth/change-password.tsx | 224 +- src/dialogs/auth/disable-two-factor.tsx | 152 +- src/dialogs/auth/enable-two-factor.tsx | 550 +- src/dialogs/manager.tsx | 94 +- src/dialogs/resume/import.tsx | 480 +- src/dialogs/resume/index.tsx | 570 +- src/dialogs/resume/sections/award.tsx | 384 +- src/dialogs/resume/sections/certification.tsx | 384 +- src/dialogs/resume/sections/cover-letter.tsx | 246 +- src/dialogs/resume/sections/custom.tsx | 304 +- src/dialogs/resume/sections/education.tsx | 474 +- src/dialogs/resume/sections/experience.tsx | 658 +- src/dialogs/resume/sections/interest.tsx | 298 +- src/dialogs/resume/sections/language.tsx | 308 +- src/dialogs/resume/sections/profile.tsx | 380 +- src/dialogs/resume/sections/project.tsx | 354 +- src/dialogs/resume/sections/publication.tsx | 384 +- src/dialogs/resume/sections/reference.tsx | 384 +- src/dialogs/resume/sections/skill.tsx | 380 +- src/dialogs/resume/sections/summary-item.tsx | 212 +- src/dialogs/resume/sections/volunteer.tsx | 384 +- src/dialogs/resume/template/data.ts | 192 +- src/dialogs/resume/template/gallery.tsx | 180 +- src/dialogs/store.ts | 352 +- src/hooks/use-confirm.tsx | 134 +- src/hooks/use-controlled-state.tsx | 34 +- src/hooks/use-form-blocker.tsx | 88 +- src/hooks/use-mobile.tsx | 22 +- src/hooks/use-prompt.tsx | 204 +- src/integrations/ai/store.ts | 130 +- src/integrations/ai/tools/patch-resume.ts | 14 +- src/integrations/auth/client.ts | 34 +- src/integrations/auth/config.ts | 400 +- src/integrations/auth/functions.ts | 18 +- src/integrations/auth/types.ts | 4 +- src/integrations/drizzle/client.ts | 24 +- src/integrations/drizzle/schema.ts | 590 +- src/integrations/email/service.ts | 88 +- src/integrations/import/json-resume.tsx | 1000 +- .../import/reactive-resume-json.tsx | 116 +- .../import/reactive-resume-v4-json.tsx | 1396 +- src/integrations/orpc/client.ts | 82 +- src/integrations/orpc/context.ts | 92 +- src/integrations/orpc/dto/resume.ts | 160 +- .../orpc/helpers/resume-access.ts | 36 +- src/integrations/orpc/router/ai.ts | 310 +- src/integrations/orpc/router/auth.ts | 60 +- src/integrations/orpc/router/flags.ts | 36 +- src/integrations/orpc/router/index.ts | 14 +- src/integrations/orpc/router/printer.ts | 94 +- src/integrations/orpc/router/resume.ts | 692 +- src/integrations/orpc/router/statistics.ts | 96 +- src/integrations/orpc/router/storage.ts | 158 +- src/integrations/orpc/services/ai.ts | 628 +- src/integrations/orpc/services/auth.ts | 56 +- src/integrations/orpc/services/flags.ts | 12 +- src/integrations/orpc/services/printer.ts | 558 +- src/integrations/orpc/services/resume.ts | 718 +- src/integrations/orpc/services/statistics.ts | 148 +- src/integrations/orpc/services/storage.ts | 518 +- src/integrations/query/client.ts | 60 +- src/routeTree.gen.ts | 1124 +- src/router.tsx | 52 +- src/routes/$username/$slug.tsx | 126 +- src/routes/__root.tsx | 172 +- src/routes/_home/-sections/donate.tsx | 422 +- src/routes/_home/-sections/faq.tsx | 198 +- src/routes/_home/-sections/features.tsx | 352 +- src/routes/_home/-sections/footer.tsx | 208 +- src/routes/_home/-sections/header.tsx | 130 +- src/routes/_home/-sections/hero.tsx | 256 +- src/routes/_home/-sections/prefooter.tsx | 62 +- .../_home/-sections/product-hunt-banner.tsx | 46 +- src/routes/_home/-sections/statistics.tsx | 152 +- src/routes/_home/-sections/templates.tsx | 172 +- src/routes/_home/-sections/testimonials.tsx | 180 +- src/routes/_home/index.tsx | 36 +- src/routes/_home/route.tsx | 26 +- src/routes/api/auth.$.ts | 22 +- src/routes/api/health.ts | 186 +- src/routes/api/openapi.$.ts | 120 +- src/routes/api/rpc.$.ts | 40 +- src/routes/auth/-components/social-auth.tsx | 146 +- src/routes/auth/forgot-password.tsx | 184 +- src/routes/auth/index.tsx | 8 +- src/routes/auth/login.tsx | 300 +- src/routes/auth/register.tsx | 404 +- src/routes/auth/reset-password.tsx | 158 +- src/routes/auth/resume-password.tsx | 190 +- src/routes/auth/route.tsx | 14 +- src/routes/auth/verify-2fa-backup.tsx | 190 +- src/routes/auth/verify-2fa.tsx | 204 +- .../builder/$resumeId/-components/dock.tsx | 244 +- .../builder/$resumeId/-components/edge.tsx | 24 +- .../builder/$resumeId/-components/header.tsx | 246 +- .../builder/$resumeId/-sidebar/left/index.tsx | 152 +- .../-sidebar/left/sections/awards.tsx | 42 +- .../-sidebar/left/sections/basics.tsx | 204 +- .../-sidebar/left/sections/certifications.tsx | 60 +- .../-sidebar/left/sections/custom-fields.tsx | 272 +- .../-sidebar/left/sections/custom.tsx | 458 +- .../-sidebar/left/sections/education.tsx | 42 +- .../-sidebar/left/sections/experience.tsx | 58 +- .../-sidebar/left/sections/interests.tsx | 42 +- .../-sidebar/left/sections/languages.tsx | 42 +- .../-sidebar/left/sections/picture.tsx | 792 +- .../-sidebar/left/sections/profiles.tsx | 42 +- .../-sidebar/left/sections/projects.tsx | 50 +- .../-sidebar/left/sections/publications.tsx | 42 +- .../-sidebar/left/sections/references.tsx | 42 +- .../-sidebar/left/sections/skills.tsx | 42 +- .../-sidebar/left/sections/summary.tsx | 24 +- .../-sidebar/left/sections/volunteer.tsx | 54 +- .../-sidebar/left/shared/section-base.tsx | 96 +- .../-sidebar/left/shared/section-item.tsx | 516 +- .../-sidebar/left/shared/section-menu.tsx | 266 +- .../$resumeId/-sidebar/right/index.tsx | 134 +- .../-sidebar/right/sections/css-editor.tsx | 376 +- .../$resumeId/-sidebar/right/sections/css.tsx | 142 +- .../-sidebar/right/sections/design.tsx | 484 +- .../-sidebar/right/sections/export.tsx | 130 +- .../-sidebar/right/sections/information.tsx | 184 +- .../-sidebar/right/sections/layout/index.tsx | 144 +- .../-sidebar/right/sections/layout/pages.tsx | 614 +- .../-sidebar/right/sections/notes.tsx | 58 +- .../-sidebar/right/sections/page.tsx | 454 +- .../-sidebar/right/sections/sharing.tsx | 246 +- .../-sidebar/right/sections/statistics.tsx | 106 +- .../-sidebar/right/sections/template.tsx | 82 +- .../-sidebar/right/sections/typography.tsx | 544 +- .../-sidebar/right/shared/section-base.tsx | 70 +- .../builder/$resumeId/-store/section.ts | 72 +- .../builder/$resumeId/-store/sidebar.ts | 108 +- src/routes/builder/$resumeId/index.tsx | 50 +- src/routes/builder/$resumeId/route.tsx | 182 +- src/routes/dashboard/-components/functions.ts | 12 +- src/routes/dashboard/-components/header.tsx | 20 +- src/routes/dashboard/-components/sidebar.tsx | 308 +- src/routes/dashboard/index.tsx | 6 +- .../resumes/-components/cards/base-card.tsx | 58 +- .../resumes/-components/cards/create-card.tsx | 24 +- .../resumes/-components/cards/import-card.tsx | 24 +- .../resumes/-components/cards/resume-card.tsx | 96 +- .../resumes/-components/grid-view.tsx | 60 +- .../resumes/-components/list-view.tsx | 182 +- .../-components/menus/context-menu.tsx | 180 +- .../-components/menus/dropdown-menu.tsx | 176 +- src/routes/dashboard/resumes/index.tsx | 160 +- src/routes/dashboard/route.tsx | 46 +- src/routes/dashboard/settings/ai.tsx | 412 +- src/routes/dashboard/settings/api-keys.tsx | 206 +- .../authentication/-components/hooks.tsx | 106 +- .../authentication/-components/password.tsx | 86 +- .../-components/social-provider.tsx | 90 +- .../authentication/-components/two-factor.tsx | 84 +- .../settings/authentication/index.tsx | 46 +- src/routes/dashboard/settings/danger-zone.tsx | 112 +- src/routes/dashboard/settings/preferences.tsx | 76 +- src/routes/dashboard/settings/profile.tsx | 378 +- src/routes/mcp/-helpers/prompts.ts | 442 +- src/routes/mcp/-helpers/resources.ts | 160 +- src/routes/mcp/-helpers/tools.ts | 748 +- src/routes/mcp/index.ts | 106 +- src/routes/printer/$resumeId.tsx | 58 +- src/routes/schema[.]json.ts | 36 +- src/routes/uploads/$userId.$.tsx | 166 +- src/schema/icons.ts | 3062 ++-- src/schema/page.ts | 48 +- src/schema/resume/data.ts | 998 +- src/schema/resume/sample.ts | 1110 +- src/schema/schema.json | 3844 ++-- src/schema/templates.ts | 40 +- src/server.ts | 6 +- src/styles/globals.css | 278 +- src/utils/color.ts | 68 +- src/utils/env.ts | 114 +- src/utils/file.ts | 44 +- src/utils/locale.ts | 280 +- src/utils/logger.ts | 100 +- src/utils/password.ts | 2 +- src/utils/printer-token.ts | 52 +- src/utils/resume/move-item.ts | 288 +- src/utils/resume/patch.ts | 110 +- src/utils/resume/section.tsx | 276 +- src/utils/sanitize.ts | 158 +- src/utils/string.ts | 42 +- src/utils/style.ts | 2 +- src/utils/theme.ts | 34 +- src/vite-env.d.ts | 78 +- tsconfig.json | 54 +- vite.config.ts | 383 +- 427 files changed, 58915 insertions(+), 58759 deletions(-) delete mode 100644 .oxfmtrc.json delete mode 100644 .oxlintrc.json create mode 100755 .vite-hooks/pre-commit diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 497563031..28dd1e851 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,33 +1,33 @@ { - "name": "Reactive Resume", - "service": "reactive_resume", - "dockerComposeFile": "docker-compose.yml", - "workspaceFolder": "/workspace", + "name": "Reactive Resume", + "service": "reactive_resume", + "dockerComposeFile": "docker-compose.yml", + "workspaceFolder": "/workspace", - "forwardPorts": [3000, 4000, 5432, 8333], - "portsAttributes": { - "3000": { "label": "Reactive Resume", "onAutoForward": "openBrowser" }, - "4000": { "label": "Browserless (Printer)" }, - "5432": { "label": "PostgreSQL" }, - "8333": { "label": "SeaweedFS (S3)" } - }, + "forwardPorts": [3000, 4000, 5432, 8333], + "portsAttributes": { + "3000": { "label": "Reactive Resume", "onAutoForward": "openBrowser" }, + "4000": { "label": "Browserless (Printer)" }, + "5432": { "label": "PostgreSQL" }, + "8333": { "label": "SeaweedFS (S3)" } + }, - "customizations": { - "vscode": { - "extensions": [ - "oxc.oxc-vscode", - "bradlc.vscode-tailwindcss", - "lokalise.i18n-ally", - "typescriptteam.native-preview" - ], - "settings": { - "editor.defaultFormatter": "oxc.oxc-vscode", - "editor.codeActionsOnSave": { - "source.fixAll.oxc": "always" - } - } - } - }, + "customizations": { + "vscode": { + "extensions": [ + "oxc.oxc-vscode", + "bradlc.vscode-tailwindcss", + "lokalise.i18n-ally", + "typescriptteam.native-preview" + ], + "settings": { + "editor.defaultFormatter": "oxc.oxc-vscode", + "editor.codeActionsOnSave": { + "source.fixAll.oxc": "always" + } + } + } + }, - "postCreateCommand": "corepack enable && pnpm install" + "postCreateCommand": "corepack enable && pnpm install" } diff --git a/.github/.well-known/funding.json b/.github/.well-known/funding.json index b58de0dc5..bc2b25393 100644 --- a/.github/.well-known/funding.json +++ b/.github/.well-known/funding.json @@ -1,68 +1,68 @@ { - "$schema": "https://fundingjson.org/schema/v1.1.0.json", - "version": "v1.1.0", - "entity": { - "type": "individual", - "role": "maintainer", - "name": "Amruth Pillai", - "email": "im.amruth@gmail.com", - "description": "Software Engineer", - "webpageUrl": { - "url": "https://rxresu.me/funding.json" - } - }, - "projects": [ - { - "guid": "reactive-resume", - "name": "Reactive Resume", - "description": "A free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.", - "webpageUrl": { - "url": "https://rxresu.me" - }, - "repositoryUrl": { - "url": "https://github.com/amruthpillai/reactive-resume", - "wellKnown": "https://github.com/amruthpillai/reactive-resume/blob/main/.github/.well-known/funding-manifest-urls" - }, - "licenses": ["spdx:MIT"], - "tags": ["data", "design", "productivity", "resume-builder"] - } - ], - "funding": { - "plans": [ - { - "guid": "sponsor", - "status": "active", - "name": "Sponsor", - "description": "Support the project on a recurring basis by becoming a sponsor.", - "amount": 10, - "currency": "EUR", - "frequency": "monthly", - "channels": ["github", "open-collective"] - }, - { - "guid": "donation", - "status": "active", - "name": "Donation", - "description": "Show your support for the project by making a one-time donation.", - "amount": 0, - "currency": "EUR", - "frequency": "one-time", - "channels": ["github", "open-collective"] - } - ], - "channels": [ - { - "guid": "github", - "type": "payment-provider", - "description": "GitHub Sponsors", - "address": "https://github.com/sponsors/AmruthPillai" - }, - { - "guid": "open-collective", - "type": "payment-provider", - "description": "Open Collective", - "address": "https://opencollective.com/reactive-resume" - } - ] - } + "$schema": "https://fundingjson.org/schema/v1.1.0.json", + "version": "v1.1.0", + "entity": { + "type": "individual", + "role": "maintainer", + "name": "Amruth Pillai", + "email": "im.amruth@gmail.com", + "description": "Software Engineer", + "webpageUrl": { + "url": "https://rxresu.me/funding.json" + } + }, + "projects": [ + { + "guid": "reactive-resume", + "name": "Reactive Resume", + "description": "A free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.", + "webpageUrl": { + "url": "https://rxresu.me" + }, + "repositoryUrl": { + "url": "https://github.com/amruthpillai/reactive-resume", + "wellKnown": "https://github.com/amruthpillai/reactive-resume/blob/main/.github/.well-known/funding-manifest-urls" + }, + "licenses": ["spdx:MIT"], + "tags": ["data", "design", "productivity", "resume-builder"] + } + ], + "funding": { + "plans": [ + { + "guid": "sponsor", + "status": "active", + "name": "Sponsor", + "description": "Support the project on a recurring basis by becoming a sponsor.", + "amount": 10, + "currency": "EUR", + "frequency": "monthly", + "channels": ["github", "open-collective"] + }, + { + "guid": "donation", + "status": "active", + "name": "Donation", + "description": "Show your support for the project by making a one-time donation.", + "amount": 0, + "currency": "EUR", + "frequency": "one-time", + "channels": ["github", "open-collective"] + } + ], + "channels": [ + { + "guid": "github", + "type": "payment-provider", + "description": "GitHub Sponsors", + "address": "https://github.com/sponsors/AmruthPillai" + }, + { + "guid": "open-collective", + "type": "payment-provider", + "description": "Open Collective", + "address": "https://opencollective.com/reactive-resume" + } + ] + } } diff --git a/.ncurc.cjs b/.ncurc.cjs index 9d396b1d7..2998a31a4 100644 --- a/.ncurc.cjs +++ b/.ncurc.cjs @@ -6,12 +6,12 @@ const betaPackages = ["drizzle-orm", "drizzle-kit", "drizzle-zod"]; /** @type {import('npm-check-updates').RunOptions} */ module.exports = { - upgrade: true, - install: "always", - packageManager: "pnpm", - target: (packageName) => { - if (nextPackages.includes(packageName)) return "@next"; - if (betaPackages.includes(packageName)) return "@beta"; - return "latest"; - }, + upgrade: true, + install: "always", + packageManager: "pnpm", + target: (packageName) => { + if (nextPackages.includes(packageName)) return "@next"; + if (betaPackages.includes(packageName)) return "@beta"; + return "latest"; + }, }; diff --git a/.oxfmtrc.json b/.oxfmtrc.json deleted file mode 100644 index 49987fb55..000000000 --- a/.oxfmtrc.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "$schema": "./node_modules/oxfmt/configuration_schema.json", - "useTabs": true, - "printWidth": 120, - "trailingComma": "all", - "ignorePatterns": [], - "sortPackageJson": { - "sortScripts": true - }, - "sortTailwindcss": { - "stylesheet": "./src/styles/globals.css", - "functions": ["clsx", "cva", "cn"] - }, - "sortImports": { - "groups": [ - "type-import", - ["value-builtin", "value-external"], - "type-internal", - "value-internal", - ["type-parent", "type-sibling", "type-index"], - ["value-parent", "value-sibling", "value-index"], - "unknown" - ] - } -} diff --git a/.oxlintrc.json b/.oxlintrc.json deleted file mode 100644 index 416d1b358..000000000 --- a/.oxlintrc.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "$schema": "./node_modules/oxlint/configuration_schema.json", - "categories": {}, - "env": { - "builtin": true - }, - "globals": {}, - "ignorePatterns": [], - "options": { - "typeAware": true - }, - "plugins": [ - "eslint", - "typescript", - "unicorn", - "oxc", - "react", - "react-perf", - "import", - "jsdoc", - "node", - "promise", - "vitest" - ], - "rules": { - "react/exhaustive-deps": "off" - }, - "settings": { - "jsdoc": { - "augmentsExtendsReplacesDocs": false, - "exemptDestructuredRootsFromChecks": false, - "ignoreInternal": false, - "ignorePrivate": false, - "ignoreReplacesDocs": true, - "implementsReplacesDocs": false, - "overrideReplacesDocs": true, - "tagNamePreference": {} - }, - "jsx-a11y": { - "attributes": {}, - "components": {}, - "polymorphicPropName": "" - }, - "react": { - "version": "19.2.4", - "formComponents": [], - "linkComponents": ["Link"], - "componentWrapperFunctions": [] - }, - "vitest": { - "typecheck": true - } - } -} diff --git a/.vite-hooks/pre-commit b/.vite-hooks/pre-commit new file mode 100755 index 000000000..85fb65b4f --- /dev/null +++ b/.vite-hooks/pre-commit @@ -0,0 +1 @@ +vp staged diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 1d7e2ea1f..74ed08e01 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,8 +1,9 @@ { - "recommendations": [ - "oxc.oxc-vscode", - "bradlc.vscode-tailwindcss", - "lokalise.i18n-ally", - "typescriptteam.native-preview" - ] + "recommendations": [ + "oxc.oxc-vscode", + "bradlc.vscode-tailwindcss", + "lokalise.i18n-ally", + "typescriptteam.native-preview", + "VoidZero.vite-plus-extension-pack" + ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 0de7c1f70..c532c8db5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,39 +1,41 @@ { - "[typescript]": { - "editor.defaultFormatter": "oxc.oxc-vscode" - }, - "editor.codeActionsOnSave": { - "source.fixAll.oxc": "always" - }, - "editor.defaultFormatter": "oxc.oxc-vscode", - "editor.formatOnSave": true, - "files.associations": { - "*.css": "tailwindcss" - }, - "files.readonlyInclude": { - "**/routeTree.gen.ts": true, - "pnpm-lock.yaml": true - }, - "files.watcherExclude": { - "**/routeTree.gen.ts": true, - "locales/**.po": true, - "pnpm-lock.yaml": true - }, - "i18n-ally.enabledParsers": ["po"], - "i18n-ally.localesPaths": ["locales"], - "i18n-ally.sourceLanguage": "en-US", - "oxc.fmt.configPath": ".oxfmtrc.json", - "search.exclude": { - "**/routeTree.gen.ts": true, - "locales/**.po": true, - "pnpm-lock.yaml": true - }, - "tailwindCSS.classFunctions": ["cn", "cva"], - "tailwindCSS.experimental.classRegex": [ - ["class:\\s*?[\"'`]([^\"'`]*).*?,"], - ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"], - ["cn\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"] - ], - "tailwindCSS.experimental.configFile": "src/styles/globals.css", - "typescript.experimental.useTsgo": true + "[typescript]": { + "editor.defaultFormatter": "oxc.oxc-vscode" + }, + "editor.codeActionsOnSave": { + "source.fixAll.oxc": "always" + }, + "editor.defaultFormatter": "oxc.oxc-vscode", + "editor.formatOnSave": true, + "editor.formatOnSaveMode": "file", + "files.associations": { + "*.css": "tailwindcss" + }, + "files.readonlyInclude": { + "**/routeTree.gen.ts": true, + "pnpm-lock.yaml": true + }, + "files.watcherExclude": { + "**/routeTree.gen.ts": true, + "locales/**.po": true, + "pnpm-lock.yaml": true + }, + "i18n-ally.enabledParsers": ["po"], + "i18n-ally.localesPaths": ["locales"], + "i18n-ally.sourceLanguage": "en-US", + "oxc.enable.oxfmt": true, + "oxc.enable.oxlint": true, + "search.exclude": { + "**/routeTree.gen.ts": true, + "locales/**.po": true, + "pnpm-lock.yaml": true + }, + "tailwindCSS.classFunctions": ["cn", "cva"], + "tailwindCSS.experimental.classRegex": [ + ["class:\\s*?[\"'`]([^\"'`]*).*?,"], + ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"], + ["cn\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"] + ], + "tailwindCSS.experimental.configFile": "src/styles/globals.css", + "typescript.experimental.useTsgo": true } diff --git a/AGENTS.md b/AGENTS.md index 768f16d97..6403965c5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -46,3 +46,81 @@ See `scripts` in `package.json`. Key ones: - `pnpm.onlyBuiltDependencies` in `package.json` controls which packages are allowed to run install scripts — no interactive `pnpm approve-builds` needed. - Email verification is optional in dev — after signup, click "Continue" to skip. - Vite 8 is beta (`^8.0.0-beta.15`); Nitro uses a nightly build. Occasional upstream issues may occur. + + + +# Using Vite+, the Unified Toolchain for the Web + +This project is using Vite+, a unified toolchain built on top of Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, and Vite Task. Vite+ wraps runtime management, package management, and frontend tooling in a single global CLI called `vp`. Vite+ is distinct from Vite, but it invokes Vite through `vp dev` and `vp build`. + +## Vite+ Workflow + +`vp` is a global binary that handles the full development lifecycle. Run `vp help` to print a list of commands and `vp --help` for information about a specific command. + +### Start + +- create - Create a new project from a template +- migrate - Migrate an existing project to Vite+ +- config - Configure hooks and agent integration +- staged - Run linters on staged files +- install (`i`) - Install dependencies +- env - Manage Node.js versions + +### Develop + +- dev - Run the development server +- check - Run format, lint, and TypeScript type checks +- lint - Lint code +- fmt - Format code +- test - Run tests + +### Execute + +- run - Run monorepo tasks +- exec - Execute a command from local `node_modules/.bin` +- dlx - Execute a package binary without installing it as a dependency +- cache - Manage the task cache + +### Build + +- build - Build for production +- pack - Build libraries +- preview - Preview production build + +### Manage Dependencies + +Vite+ automatically detects and wraps the underlying package manager such as pnpm, npm, or Yarn through the `packageManager` field in `package.json` or package manager-specific lockfiles. + +- add - Add packages to dependencies +- remove (`rm`, `un`, `uninstall`) - Remove packages from dependencies +- update (`up`) - Update packages to latest versions +- dedupe - Deduplicate dependencies +- outdated - Check for outdated packages +- list (`ls`) - List installed packages +- why (`explain`) - Show why a package is installed +- info (`view`, `show`) - View package information from the registry +- link (`ln`) / unlink - Manage local package links +- pm - Forward a command to the package manager + +### Maintain + +- upgrade - Update `vp` itself to the latest version + +These commands map to their corresponding tools. For example, `vp dev --port 3000` runs Vite's dev server and works the same as Vite. `vp test` runs JavaScript tests through the bundled Vitest. The version of all tools can be checked using `vp --version`. This is useful when researching documentation, features, and bugs. + +## Common Pitfalls + +- **Using the package manager directly:** Do not use pnpm, npm, or Yarn directly. Vite+ can handle all package manager operations. +- **Always use Vite commands to run tools:** Don't attempt to run `vp vitest` or `vp oxlint`. They do not exist. Use `vp test` and `vp lint` instead. +- **Running scripts:** Vite+ commands take precedence over `package.json` scripts. If there is a `test` script defined in `scripts` that conflicts with the built-in `vp test` command, run it using `vp run test`. +- **Do not install Vitest, Oxlint, Oxfmt, or tsdown directly:** Vite+ wraps these tools. They must not be installed directly. You cannot upgrade these tools by installing their latest versions. Always use Vite+ commands. +- **Use Vite+ wrappers for one-off binaries:** Use `vp dlx` instead of package-manager-specific `dlx`/`npx` commands. +- **Import JavaScript modules from `vite-plus`:** Instead of importing from `vite` or `vitest`, all modules should be imported from the project's `vite-plus` dependency. For example, `import { defineConfig } from 'vite-plus';` or `import { expect, test, vi } from 'vite-plus/test';`. You must not install `vitest` to import test utilities. +- **Type-Aware Linting:** There is no need to install `oxlint-tsgolint`, `vp lint --type-aware` works out of the box. + +## Review Checklist for Agents + +- [ ] Run `vp install` after pulling remote changes and before getting started. +- [ ] Run `vp check` and `vp test` to validate changes. + + diff --git a/components.json b/components.json index eafbc171d..6d7b7c120 100644 --- a/components.json +++ b/components.json @@ -1,26 +1,26 @@ { - "$schema": "https://ui.shadcn.com/schema.json", - "style": "base-vega", - "rsc": false, - "tsx": true, - "tailwind": { - "config": "", - "css": "src/styles.css", - "baseColor": "zinc", - "cssVariables": true, - "prefix": "" - }, - "iconLibrary": "phosphor", - "menuColor": "default", - "menuAccent": "bold", - "aliases": { - "components": "@/components", - "utils": "@/utils/style", - "ui": "@/components/ui", - "lib": "@/utils", - "hooks": "@/hooks" - }, - "registries": { - "@animate-ui": "https://animate-ui.com/r/{name}.json" - } + "$schema": "https://ui.shadcn.com/schema.json", + "style": "base-vega", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/styles.css", + "baseColor": "zinc", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "phosphor", + "menuColor": "default", + "menuAccent": "bold", + "aliases": { + "components": "@/components", + "utils": "@/utils/style", + "ui": "@/components/ui", + "lib": "@/utils", + "hooks": "@/hooks" + }, + "registries": { + "@animate-ui": "https://animate-ui.com/r/{name}.json" + } } diff --git a/docs/changelog/index.mdx b/docs/changelog/index.mdx index 687147f6f..1998c60d7 100644 --- a/docs/changelog/index.mdx +++ b/docs/changelog/index.mdx @@ -53,95 +53,95 @@ rss: true - - Add Computer Modern web fonts to the font selector, allowing the user to choose from a variety of "Computer Modern" - (LaTeX) fonts. - This lets you create a resume that looks just like a LaTeX document, with the same fonts and styles. - - Update dependencies to the latest versions. + - Add Computer Modern web fonts to the font selector, allowing the user to choose from a variety of "Computer Modern" + (LaTeX) fonts. - This lets you create a resume that looks just like a LaTeX document, with the same fonts and styles. + - Update dependencies to the latest versions. - - Remove Passkey support from the authentication system, as it was causing issues with the authentication provider. - - Update dependencies to the latest versions. + - Remove Passkey support from the authentication system, as it was causing issues with the authentication provider. - + Update dependencies to the latest versions. - - Introduce a new **MCP (Model Context Protocol) server** that lets you manage and edit resumes from any - MCP-compatible AI tool — Claude Desktop, Cursor, Codex, and more. Supports listing, reading, creating, deleting, - locking/unlocking, and patching resumes via natural language. [(guide)](/guides/using-the-mcp-server) - Add an **AI - Chat** panel to the resume builder, allowing you to modify your resume through conversational AI directly within the - editor. The chat uses tool-calling to apply JSON Patch operations to your resume in real-time, with visual feedback - for each change. - Add a system prompt and `patch_resume` tool for the AI chat, enabling structured, minimal-diff - resume edits following RFC 6902 JSON Patch operations. - Chat history is now persisted per resume in localStorage, so - conversations are preserved across sessions. - Fix rendering issues in the Lapras and Onyx resume templates. - - Improvements to the Combobox and ScrollArea UI components. - Fix an issue with skills item rendering in the shared - resume components. - Update authentication configuration and auth route handling. - Update the JSON Schema to reflect - the latest resume data model. - Update dependencies to the latest versions. + - Introduce a new **MCP (Model Context Protocol) server** that lets you manage and edit resumes from any + MCP-compatible AI tool — Claude Desktop, Cursor, Codex, and more. Supports listing, reading, creating, deleting, + locking/unlocking, and patching resumes via natural language. [(guide)](/guides/using-the-mcp-server) - Add an **AI + Chat** panel to the resume builder, allowing you to modify your resume through conversational AI directly within the + editor. The chat uses tool-calling to apply JSON Patch operations to your resume in real-time, with visual feedback + for each change. - Add a system prompt and `patch_resume` tool for the AI chat, enabling structured, minimal-diff + resume edits following RFC 6902 JSON Patch operations. - Chat history is now persisted per resume in localStorage, so + conversations are preserved across sessions. - Fix rendering issues in the Lapras and Onyx resume templates. - + Improvements to the Combobox and ScrollArea UI components. - Fix an issue with skills item rendering in the shared + resume components. - Update authentication configuration and auth route handling. - Update the JSON Schema to reflect + the latest resume data model. - Update dependencies to the latest versions. - - Implement Atomic Resume Patching API for fine-grained resume updates, allowing partial, atomic updates to resumes - via a new PATCH endpoint. [#2692](https://github.com/amruthpillai/reactive-resume/pull/2692) - The API endpoint `PUT - /resume/{id}` now returns the updated resume object instead of void. Also, resume routing/services now use explicit - DTOs for input and output schemas. [#2688](https://github.com/amruthpillai/reactive-resume/pull/2688) - Add error - logging for API server errors (server-side only) to improve debugging and reliability. - Refactor and clean up - imports/exports for clarity and maintainability. - Added `.devcontainer` configuration for improved contributor - development environment. - Update dependencies to the latest versions. - Add build status badge and documentation link - to README. - Sync latest translations from Crowdin (notably: French, other languages). - Other minor fixes and - improvements. + - Implement Atomic Resume Patching API for fine-grained resume updates, allowing partial, atomic updates to resumes + via a new PATCH endpoint. [#2692](https://github.com/amruthpillai/reactive-resume/pull/2692) - The API endpoint `PUT + /resume/{id}` now returns the updated resume object instead of void. Also, resume routing/services now use explicit + DTOs for input and output schemas. [#2688](https://github.com/amruthpillai/reactive-resume/pull/2688) - Add error + logging for API server errors (server-side only) to improve debugging and reliability. - Refactor and clean up + imports/exports for clarity and maintainability. - Added `.devcontainer` configuration for improved contributor + development environment. - Update dependencies to the latest versions. - Add build status badge and documentation link + to README. - Sync latest translations from Crowdin (notably: French, other languages). - Other minor fixes and + improvements. - - Implement Cover Letter functionality in the resume builder, allowing the user to create cover letters as custom - sections. [(link)](/guides/adding-a-cover-letter) - Implement full-screen mode for the rich text editor in the resume - builder, allowing the user to write in a more focused environment. - Implement a new custom section type: `summary`, - which allows the user to add another "summary" like section to the resume. - Implement a `useFormBlocker` hook to - prevent the user from closing a dialog while the form has unsaved changes. - Fix an issue where keywords spacing was - not consistent in the interests section. [#2631](https://github.com/amruthpillai/reactive-resume/pull/2631) - Fix an - issue where the AI connection test was not working correctly, also return appropriate error messages for AI provider - issues. + - Implement Cover Letter functionality in the resume builder, allowing the user to create cover letters as custom + sections. [(link)](/guides/adding-a-cover-letter) - Implement full-screen mode for the rich text editor in the resume + builder, allowing the user to write in a more focused environment. - Implement a new custom section type: `summary`, + which allows the user to add another "summary" like section to the resume. - Implement a `useFormBlocker` hook to + prevent the user from closing a dialog while the form has unsaved changes. - Fix an issue where keywords spacing was + not consistent in the interests section. [#2631](https://github.com/amruthpillai/reactive-resume/pull/2631) - Fix an + issue where the AI connection test was not working correctly, also return appropriate error messages for AI provider + issues. - - Bring back Undo/Redo functionality in the resume builder for improved editing experience. - Arrange the sidebar - builder dynamically based on the section type in each template. - [#2564](https://github.com/amruthpillai/reactive-resume/pull/2603) - Remove extra spacing when proficiency is empty. - [#2607](https://github.com/amruthpillai/reactive-resume/pull/2626) - Fix rendering in Pikachu template: conditionally - render header and page picture using `isFirstPage`, and respect the `fullWidth` property for page layout. - Fixes to - templates to improve layout and rendering consistency. - Fix GitHub OAuth login for users migrated from previous - versions. - Improve communication with the printer service and reduce resource usage for better PDF generation - reliability. - Update and sync translations from Crowdin (Afrikaans, Persian, Portuguese/Brazilian, and other - languages). - Update translation sources and configuration. - Fix "empty" Git merge remnants in codebase. - Update - package dependencies and fix self-hosting guide links in README. - Remove dead code, update screenshots, and add PWA - (Progressive Web App) support. - Update links to PDF example files in documentation. - Other bug fixes and minor - improvements (#2542, #2573, #2598). + - Bring back Undo/Redo functionality in the resume builder for improved editing experience. - Arrange the sidebar + builder dynamically based on the section type in each template. + [#2564](https://github.com/amruthpillai/reactive-resume/pull/2603) - Remove extra spacing when proficiency is empty. + [#2607](https://github.com/amruthpillai/reactive-resume/pull/2626) - Fix rendering in Pikachu template: conditionally + render header and page picture using `isFirstPage`, and respect the `fullWidth` property for page layout. - Fixes to + templates to improve layout and rendering consistency. - Fix GitHub OAuth login for users migrated from previous + versions. - Improve communication with the printer service and reduce resource usage for better PDF generation + reliability. - Update and sync translations from Crowdin (Afrikaans, Persian, Portuguese/Brazilian, and other + languages). - Update translation sources and configuration. - Fix "empty" Git merge remnants in codebase. - Update + package dependencies and fix self-hosting guide links in README. - Remove dead code, update screenshots, and add PWA + (Progressive Web App) support. - Update links to PDF example files in documentation. - Other bug fixes and minor + improvements (#2542, #2573, #2598). - - Implement the ability to print Free-Form PDFs which do not have a fixed page height, allowing the user to fit the - content on a page as they see fit. [(link)](/guides/selecting-page-format) - Allow the user to override the default - endpoint for all AI providers, not just Ollama. Also display the default endpoint for each provider in the AI settings - page. - Updated the chip input component, to allow the user to add, edit, remove or reorder keywords for skills and - other sections. - Improved RTL support across the app, thanks to @obreo for the contribution. - [(link)](https://github.com/amruthpillai/reactive-resume/pull/2583) - Updates to the translation configuration to - remove line numbers from the translation files, as this was causing unnecessary diffs. - Updated the video on the - homepage to be lighter and faster to load, while still maintaining the same quality. - Increased the screenshot TTL - for resumes, to avoid regenerating screenshots unnecessarily unless the resume has been updated recently (in the last - hour). - Update dependencies and translations to the latest versions. + - Implement the ability to print Free-Form PDFs which do not have a fixed page height, allowing the user to fit the + content on a page as they see fit. [(link)](/guides/selecting-page-format) - Allow the user to override the default + endpoint for all AI providers, not just Ollama. Also display the default endpoint for each provider in the AI settings + page. - Updated the chip input component, to allow the user to add, edit, remove or reorder keywords for skills and + other sections. - Improved RTL support across the app, thanks to @obreo for the contribution. + [(link)](https://github.com/amruthpillai/reactive-resume/pull/2583) - Updates to the translation configuration to + remove line numbers from the translation files, as this was causing unnecessary diffs. - Updated the video on the + homepage to be lighter and faster to load, while still maintaining the same quality. - Increased the screenshot TTL + for resumes, to avoid regenerating screenshots unnecessarily unless the resume has been updated recently (in the last + hour). - Update dependencies and translations to the latest versions. - - Added an agent skill `skills/resume-builder` for agentic AI assistants to build resumes for Reactive Resume through - conversational AI. - Added a new guide on how to fit content on a page, to avoid issues when exporting to PDF. - [(link)](/guides/fitting-content-on-a-page) - Display an alert when the content is too tall for a page, to help the - user fit the content on a page. - Fix an issue with the Ditgar template, where the page was not respecting the - `fullWidth` setting. - Updated the JSON Schema to conform to a proper format. - Updated the Discord Server invite link - to a new one. - Updated dependencies to the latest versions. + - Added an agent skill `skills/resume-builder` for agentic AI assistants to build resumes for Reactive Resume through + conversational AI. - Added a new guide on how to fit content on a page, to avoid issues when exporting to PDF. + [(link)](/guides/fitting-content-on-a-page) - Display an alert when the content is too tall for a page, to help the + user fit the content on a page. - Fix an issue with the Ditgar template, where the page was not respecting the + `fullWidth` setting. - Updated the JSON Schema to conform to a proper format. - Updated the Discord Server invite link + to a new one. - Updated dependencies to the latest versions. - - Updated translations from Crowdin. - Added a Community Spotlight section to the documentation. - Remove `-r - require-metadata` from the Dockerfile as it was not needed. - Fixed inconsistencies in the docker compose examples in - the documentation. - Fixed an issue with usernames not allowing hyphens in them. - Fixed issues with the printer - service, when using the `getResumeScreenshot` or `printResumeAsPDF` endpoints. + - Updated translations from Crowdin. - Added a Community Spotlight section to the documentation. - Remove `-r + require-metadata` from the Dockerfile as it was not needed. - Fixed inconsistencies in the docker compose examples in + the documentation. - Fixed an issue with usernames not allowing hyphens in them. - Fixed issues with the printer + service, when using the `getResumeScreenshot` or `printResumeAsPDF` endpoints. diff --git a/docs/community/spotlight.mdx b/docs/community/spotlight.mdx index e6b5c97e0..89faba5d0 100644 --- a/docs/community/spotlight.mdx +++ b/docs/community/spotlight.mdx @@ -4,9 +4,9 @@ description: "A showcase of articles, videos, and social media posts from the Re --- - Have you created something that makes use of Reactive Resume, or spread the word about it in your own way? I'd love to - feature it here! Send me an email at [hello@amruthpillai.com](mailto:hello@amruthpillai.com) and I'll make sure to add - it to this page. + Have you created something that makes use of Reactive Resume, or spread the word about it in your own way? I'd love to + feature it here! Send me an email at [hello@amruthpillai.com](mailto:hello@amruthpillai.com) and I'll make sure to add + it to this page. --- diff --git a/docs/contributing/architecture.mdx b/docs/contributing/architecture.mdx index 8ff759617..44ba60ea4 100644 --- a/docs/contributing/architecture.mdx +++ b/docs/contributing/architecture.mdx @@ -12,14 +12,14 @@ This guide provides a comprehensive overview of Reactive Resume's architecture a Reactive Resume is built with a modern, type-safe stack: - - - **React 19** with TanStack Start - **TypeScript** for type safety - **Tailwind CSS** for styling - **Radix UI** - for accessible components - - - - **ORPC** for type-safe RPC - **Drizzle ORM** with PostgreSQL - **Better Auth** for authentication - **Sharp** for - image processing - + + - **React 19** with TanStack Start - **TypeScript** for type safety - **Tailwind CSS** for styling - **Radix UI** + for accessible components + + + - **ORPC** for type-safe RPC - **Drizzle ORM** with PostgreSQL - **Better Auth** for authentication - **Sharp** for + image processing + --- @@ -183,10 +183,10 @@ The database schema is defined using Drizzle ORM in `src/integrations/drizzle/sc import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core"; export const resume = pgTable("resume", { - id: uuid("id").primaryKey().defaultRandom(), - title: text("title").notNull(), - slug: text("slug").notNull(), - // ... more fields + id: uuid("id").primaryKey().defaultRandom(), + title: text("title").notNull(), + slug: text("slug").notNull(), + // ... more fields }); ``` @@ -206,12 +206,12 @@ export const resume = pgTable("resume", { import { cn } from "@/utils/style"; interface MyComponentProps { - title: string; - className?: string; + title: string; + className?: string; } export const MyComponent = ({ title, className }: MyComponentProps) => { - return
{title}
; + return
{title}
; }; ``` @@ -226,11 +226,11 @@ export const MyComponent = ({ title, className }: MyComponentProps) => { import { createFileRoute } from "@tanstack/react-router"; export const Route = createFileRoute("/my-page")({ - component: MyPage, + component: MyPage, }); function MyPage() { - return
My Page Content
; + return
My Page Content
; } ``` @@ -246,9 +246,9 @@ import { z } from "zod"; import { publicProcedure, router } from "../server"; export const myRouter = router({ - hello: publicProcedure.input(z.object({ name: z.string() })).handler(async ({ input }) => { - return { message: `Hello, ${input.name}!` }; - }), + hello: publicProcedure.input(z.object({ name: z.string() })).handler(async ({ input }) => { + return { message: `Hello, ${input.name}!` }; + }), }); ``` @@ -313,10 +313,10 @@ const title = t`Welcome`; ## Need Help? - - Ask questions and discuss ideas with the community. - - - Report bugs or request features. - + + Ask questions and discuss ideas with the community. + + + Report bugs or request features. + diff --git a/docs/contributing/development.mdx b/docs/contributing/development.mdx index 49d0c62ee..e2c4d59c7 100644 --- a/docs/contributing/development.mdx +++ b/docs/contributing/development.mdx @@ -4,8 +4,8 @@ description: "Set up a local development environment for Reactive Resume" --- - **Prerequisites**: - [Node.js](https://nodejs.org/) v20 or higher - [pnpm](https://pnpm.io/) v10.28.0 or higher - (package manager) - [Docker](https://docs.docker.com/get-docker/) and Docker Compose - [Git](https://git-scm.com/) + **Prerequisites**: - [Node.js](https://nodejs.org/) v20 or higher - [pnpm](https://pnpm.io/) v10.28.0 or higher + (package manager) - [Docker](https://docs.docker.com/get-docker/) and Docker Compose - [Git](https://git-scm.com/) This guide walks you through setting up Reactive Resume for local development. Whether you're contributing to the project or customizing it for your needs, these steps will get you up and running. @@ -265,9 +265,9 @@ pnpm run typecheck ``` - Configure your IDE to use Oxlint for automatic linting and Oxfmt for automatic formatting on save. For VS Code, - install the [Oxlint extension](https://marketplace.visualstudio.com/items?itemName=oxc.oxc-vscode) and configure the - settings in `.vscode/settings.json`. + Configure your IDE to use Oxlint for automatic linting and Oxfmt for automatic formatting on save. For VS Code, + install the [Oxlint extension](https://marketplace.visualstudio.com/items?itemName=oxc.oxc-vscode) and configure the + settings in `.vscode/settings.json`. --- @@ -303,10 +303,10 @@ pnpm run typecheck ## Next Steps - - Deep dive into the project architecture and codebase structure. - - - View the source code and contribute to the project. - + + Deep dive into the project architecture and codebase structure. + + + View the source code and contribute to the project. + diff --git a/docs/contributing/translations.mdx b/docs/contributing/translations.mdx index 25189f96e..8bb3b5a7e 100644 --- a/docs/contributing/translations.mdx +++ b/docs/contributing/translations.mdx @@ -12,8 +12,8 @@ Reactive Resume is used by people all around the world, and translations help ma Reactive Resume uses [Crowdin](https://crowdin.com/) as its localization management platform. Crowdin provides a user-friendly interface where translators can contribute translations without needing to write code or work with files directly. - The Reactive Resume Crowdin project is available at - [https://crowdin.com/project/reactive-resume](https://crowdin.com/project/reactive-resume). + The Reactive Resume Crowdin project is available at + [https://crowdin.com/project/reactive-resume](https://crowdin.com/project/reactive-resume). Once translations are submitted and approved on Crowdin, they are automatically synced to the codebase and will be available in the next release of the app. @@ -94,8 +94,8 @@ Some technical terms (like "PDF", "URL", "JSON") are often kept in English acros If your language is not listed in the Crowdin project, you can request it to be added. - Before requesting a new language, please check if it's already available in the [Crowdin - project](https://crowdin.com/project/reactive-resume). + Before requesting a new language, please check if it's already available in the [Crowdin + project](https://crowdin.com/project/reactive-resume). To request a new language: @@ -115,8 +115,8 @@ Once approved, the language will be added to Crowdin and you can begin translati Translations submitted on Crowdin are synced to the codebase periodically. Once merged, they will be included in the next release of Reactive Resume. - There may be a delay between submitting translations and seeing them live in the app. This is normal and depends on - the release cycle. + There may be a delay between submitting translations and seeing them live in the app. This is normal and depends on + the release cycle. --- @@ -124,18 +124,18 @@ Translations submitted on Crowdin are synced to the codebase periodically. Once ## Tips for Effective Translation - - Crowdin often shows context, screenshots, or comments to help you understand where the text appears in the app. - - - Review translations by other contributors and vote for accurate ones to help maintain quality. - - - Use Crowdin's comment feature to ask about unclear strings or discuss translations with other contributors. - - - Check the project glossary (if available) to ensure terminology is used consistently across the app. - + + Crowdin often shows context, screenshots, or comments to help you understand where the text appears in the app. + + + Review translations by other contributors and vote for accurate ones to help maintain quality. + + + Use Crowdin's comment feature to ask about unclear strings or discuss translations with other contributors. + + + Check the project glossary (if available) to ensure terminology is used consistently across the app. + --- @@ -143,12 +143,12 @@ Translations submitted on Crowdin are synced to the codebase periodically. Once ## Need Help? - - Official Crowdin documentation for translators. - - - Report issues or request new languages. - + + Official Crowdin documentation for translators. + + + Report issues or request new languages. + --- diff --git a/docs/docs.json b/docs/docs.json index 707140a47..764636c83 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -1,150 +1,150 @@ { - "$schema": "https://mintlify.com/docs.json", - "theme": "mint", - "name": "Reactive Resume", - "favicon": "/favicon.svg", - "description": "A one-of-a-kind resume builder that keeps your privacy in mind. Completely secure, customizable, portable, open-source and free forever. Try it out today!", - "seo": { - "indexing": "all", - "metatags": { - "canonical": "https://docs.rxresu.me", - "og:image": "https://rxresu.me/opengraph/banner.jpg" - } - }, - "fonts": { - "heading": { - "family": "IBM Plex Sans" - }, - "body": { - "family": "IBM Plex Sans" - } - }, - "colors": { - "primary": "#166534", - "light": "#07C983", - "dark": "#15803D" - }, - "navigation": { - "tabs": [ - { - "icon": "book", - "tab": "Documentation", - "groups": [ - { - "group": "Getting Started", - "pages": [ - "getting-started/index", - "getting-started/quickstart", - "guides/checking-service-status", - "guides/accessing-the-previous-version" - ] - }, - { - "group": "Account", - "pages": [ - "guides/creating-an-account", - "guides/updating-your-profile", - "guides/linking-social-accounts", - "guides/deleting-your-account" - ] - }, - { - "group": "Resume Builder", - "pages": [ - "guides/creating-your-first-resume", - "guides/choosing-a-template", - "guides/selecting-page-format", - "guides/moving-items-between-sections", - "guides/fitting-content-on-a-page", - "guides/adding-a-cover-letter", - "guides/exporting-your-resume", - "guides/sharing-your-resume-publicly", - "guides/using-private-notes", - "guides/using-custom-css" - ] - }, - { - "group": "Security", - "pages": ["guides/setting-up-two-factor-authentication", "guides/setting-up-passkeys"] - }, - { - "group": "Integrations", - "pages": [ - "guides/using-the-api", - "guides/using-the-patch-api", - "guides/using-the-mcp-server", - "guides/using-ai", - "guides/json-resume-schema" - ] - }, - { - "group": "Self-Hosting", - "pages": ["self-hosting/docker", "self-hosting/examples", "self-hosting/sso", "self-hosting/migration"] - }, - { - "group": "Contributing", - "pages": ["contributing/architecture", "contributing/development", "contributing/translations"] - }, - { - "group": "Community", - "pages": ["community/spotlight"] - }, - { - "group": "Legal", - "pages": ["legal/license", "legal/privacy-policy", "legal/terms-of-service"] - } - ] - }, - { - "icon": "clock", - "tab": "Changelog", - "pages": ["changelog/index"] - }, - { - "icon": "code", - "tab": "API Reference", - "openapi": "/spec.json" - } - ], - "global": { - "anchors": [ - { - "icon": "link", - "anchor": "Reactive Resume", - "href": "https://rxresu.me" - }, - { - "icon": "github", - "anchor": "Source Code", - "href": "https://github.com/amruthpillai/reactive-resume" - } - ] - } - }, - "logo": { - "light": "/logo/light.svg", - "dark": "/logo/dark.svg" - }, - "navbar": { - "links": [ - { - "label": "Support", - "href": "https://github.com/amruthpillai/reactive-resume/issues" - } - ], - "primary": { - "type": "button", - "label": "Get Started", - "href": "https://rxresu.me" - } - }, - "contextual": { - "options": ["copy", "view"] - }, - "footer": { - "socials": { - "website": "https://rxresu.me", - "github": "https://github.com/amruthpillai/reactive-resume" - } - } + "$schema": "https://mintlify.com/docs.json", + "theme": "mint", + "name": "Reactive Resume", + "favicon": "/favicon.svg", + "description": "A one-of-a-kind resume builder that keeps your privacy in mind. Completely secure, customizable, portable, open-source and free forever. Try it out today!", + "seo": { + "indexing": "all", + "metatags": { + "canonical": "https://docs.rxresu.me", + "og:image": "https://rxresu.me/opengraph/banner.jpg" + } + }, + "fonts": { + "heading": { + "family": "IBM Plex Sans" + }, + "body": { + "family": "IBM Plex Sans" + } + }, + "colors": { + "primary": "#166534", + "light": "#07C983", + "dark": "#15803D" + }, + "navigation": { + "tabs": [ + { + "icon": "book", + "tab": "Documentation", + "groups": [ + { + "group": "Getting Started", + "pages": [ + "getting-started/index", + "getting-started/quickstart", + "guides/checking-service-status", + "guides/accessing-the-previous-version" + ] + }, + { + "group": "Account", + "pages": [ + "guides/creating-an-account", + "guides/updating-your-profile", + "guides/linking-social-accounts", + "guides/deleting-your-account" + ] + }, + { + "group": "Resume Builder", + "pages": [ + "guides/creating-your-first-resume", + "guides/choosing-a-template", + "guides/selecting-page-format", + "guides/moving-items-between-sections", + "guides/fitting-content-on-a-page", + "guides/adding-a-cover-letter", + "guides/exporting-your-resume", + "guides/sharing-your-resume-publicly", + "guides/using-private-notes", + "guides/using-custom-css" + ] + }, + { + "group": "Security", + "pages": ["guides/setting-up-two-factor-authentication", "guides/setting-up-passkeys"] + }, + { + "group": "Integrations", + "pages": [ + "guides/using-the-api", + "guides/using-the-patch-api", + "guides/using-the-mcp-server", + "guides/using-ai", + "guides/json-resume-schema" + ] + }, + { + "group": "Self-Hosting", + "pages": ["self-hosting/docker", "self-hosting/examples", "self-hosting/sso", "self-hosting/migration"] + }, + { + "group": "Contributing", + "pages": ["contributing/architecture", "contributing/development", "contributing/translations"] + }, + { + "group": "Community", + "pages": ["community/spotlight"] + }, + { + "group": "Legal", + "pages": ["legal/license", "legal/privacy-policy", "legal/terms-of-service"] + } + ] + }, + { + "icon": "clock", + "tab": "Changelog", + "pages": ["changelog/index"] + }, + { + "icon": "code", + "tab": "API Reference", + "openapi": "/spec.json" + } + ], + "global": { + "anchors": [ + { + "icon": "link", + "anchor": "Reactive Resume", + "href": "https://rxresu.me" + }, + { + "icon": "github", + "anchor": "Source Code", + "href": "https://github.com/amruthpillai/reactive-resume" + } + ] + } + }, + "logo": { + "light": "/logo/light.svg", + "dark": "/logo/dark.svg" + }, + "navbar": { + "links": [ + { + "label": "Support", + "href": "https://github.com/amruthpillai/reactive-resume/issues" + } + ], + "primary": { + "type": "button", + "label": "Get Started", + "href": "https://rxresu.me" + } + }, + "contextual": { + "options": ["copy", "view"] + }, + "footer": { + "socials": { + "website": "https://rxresu.me", + "github": "https://github.com/amruthpillai/reactive-resume" + } + } } diff --git a/docs/getting-started/index.mdx b/docs/getting-started/index.mdx index e6df85efa..417b21a8f 100644 --- a/docs/getting-started/index.mdx +++ b/docs/getting-started/index.mdx @@ -4,7 +4,7 @@ description: "Welcome to the documentation for Reactive Resume, a free and open- --- - Reactive Resume Banner + Reactive Resume Banner ## What is Reactive Resume? @@ -12,24 +12,24 @@ description: "Welcome to the documentation for Reactive Resume, a free and open- **Reactive Resume** is a free and open-source resume builder designed to simplify the process of creating, updating, and sharing your professional resume. Built with privacy as a core principle, it gives you complete control over your data while providing a powerful, modern experience. - - Your data stays yours. No tracking, no ads, and complete transparency with our open-source codebase. - - - Choose from professionally designed templates that make your resume stand out from the crowd. - - - See changes instantly as you type. What you see is exactly what you'll get when you export. - - - Download your resume as PDF, share it via a unique link, or print it directly from your browser. - + + Your data stays yours. No tracking, no ads, and complete transparency with our open-source codebase. + + + Choose from professionally designed templates that make your resume stand out from the crowd. + + + See changes instantly as you type. What you see is exactly what you'll get when you export. + + + Download your resume as PDF, share it via a unique link, or print it directly from your browser. + ## Key Features - An infographic of the major features of Reactive Resume + An infographic of the major features of Reactive Resume @@ -68,12 +68,12 @@ description: "Welcome to the documentation for Reactive Resume, a free and open- Whether you want to use our hosted version or run your own instance, Reactive Resume has got you covered. - - Get up and running in minutes with our hosted version or deploy your own instance. - - - Set up a local development environment to contribute or customize Reactive Resume. - + + Get up and running in minutes with our hosted version or deploy your own instance. + + + Set up a local development environment to contribute or customize Reactive Resume. + ## Tech Stack @@ -95,20 +95,20 @@ Reactive Resume is built with modern web technologies: ## Community & Support - - Star the repo, report issues, and contribute to the project. - - - Join our Reddit community to get help and connect with other users. - - - Join our Discord server to get help and connect with other users. - - - Support the development and long-term sustainability of Reactive Resume. - + + Star the repo, report issues, and contribute to the project. + + + Join our Reddit community to get help and connect with other users. + + + Join our Discord server to get help and connect with other users. + + + Support the development and long-term sustainability of Reactive Resume. + - **Need help?** Feel free to reach out via [email](mailto:hello@amruthpillai.com) or open an issue on GitHub. + **Need help?** Feel free to reach out via [email](mailto:hello@amruthpillai.com) or open an issue on GitHub. diff --git a/docs/getting-started/quickstart.mdx b/docs/getting-started/quickstart.mdx index 52fb0b10b..823d250be 100644 --- a/docs/getting-started/quickstart.mdx +++ b/docs/getting-started/quickstart.mdx @@ -8,12 +8,12 @@ description: "Get started with Reactive Resume in minutes — use our hosted ver Reactive Resume offers flexibility in how you want to use it. Choose the option that best fits your needs: - - The fastest way to get started. **Recommended for most users.** - - - Deploy your own instance with complete control. **Requires some technical knowledge.** - + + The fastest way to get started. **Recommended for most users.** + + + Deploy your own instance with complete control. **Requires some technical knowledge.** + --- @@ -59,8 +59,8 @@ Before you begin, ensure you have the following installed: - [Docker Compose](https://docs.docker.com/compose/install/) (v2.0 or higher) - There is no difference in features between the cloud-hosted version and the self-hosted option. Both - provide the same privacy, customization, and functionality. Choose whichever deployment type suits your needs! + There is no difference in features between the cloud-hosted version and the self-hosted option. Both + provide the same privacy, customization, and functionality. Choose whichever deployment type suits your needs! ### Quick Deployment @@ -166,17 +166,17 @@ Here's a complete list of environment variables you can configure: > **Note:** Some variables are only required for using related features (OAuth, SMTP, S3, etc.) and can be left unset if unused. - **Hybrid Setup Note**: The `PRINTER_APP_URL` variable is required when running Reactive Resume outside of Docker while - the printer service is running inside Docker. In this scenario, the printer needs to reach your local app to render - resumes for PDF generation. Since Docker containers cannot access `localhost` on your host machine directly, you must - set `PRINTER_APP_URL` to `http://host.docker.internal:3000`. This special hostname allows Docker containers to - communicate with services running on your host machine. + **Hybrid Setup Note**: The `PRINTER_APP_URL` variable is required when running Reactive Resume outside of Docker while + the printer service is running inside Docker. In this scenario, the printer needs to reach your local app to render + resumes for PDF generation. Since Docker containers cannot access `localhost` on your host machine directly, you must + set `PRINTER_APP_URL` to `http://host.docker.internal:3000`. This special hostname allows Docker containers to + communicate with services running on your host machine. - **Alternative Printer Options**: If you don't want to use browserless, you can use any headless Chrome/Chromium - instance with its remote debugging port open. For example, run `chromium --remote-debugging-port=9222` and point - `PRINTER_ENDPOINT` to that instance. + **Alternative Printer Options**: If you don't want to use browserless, you can use any headless Chrome/Chromium + instance with its remote debugging port open. For example, run `chromium --remote-debugging-port=9222` and point + `PRINTER_ENDPOINT` to that instance. --- @@ -184,15 +184,15 @@ Here's a complete list of environment variables you can configure: ## Next Steps - - Set up a development environment to contribute or customize Reactive Resume. - - - Learn about the project structure and architecture. - + + Set up a development environment to contribute or customize Reactive Resume. + + + Learn about the project structure and architecture. + - **Having trouble?** Check our [GitHub Issues](https://github.com/amruthpillai/reactive-resume/issues) or reach out via - [email](mailto:hello@amruthpillai.com). + **Having trouble?** Check our [GitHub Issues](https://github.com/amruthpillai/reactive-resume/issues) or reach out via + [email](mailto:hello@amruthpillai.com). diff --git a/docs/guides/accessing-the-previous-version.mdx b/docs/guides/accessing-the-previous-version.mdx index c304ff95c..537319ed1 100644 --- a/docs/guides/accessing-the-previous-version.mdx +++ b/docs/guides/accessing-the-previous-version.mdx @@ -22,8 +22,8 @@ There are several reasons why v4 remains available: The previous version will continue to run for as long as possible—until the maintainer runs out of breath or funds to keep the server active. There are no immediate plans to shut it down. - While v4 will remain accessible for the foreseeable future, I recommend gradually transitioning to the latest version - to benefit from new features, improvements, and ongoing support. + While v4 will remain accessible for the foreseeable future, I recommend gradually transitioning to the latest version + to benefit from new features, improvements, and ongoing support. ## Accessing your v4 resumes @@ -57,8 +57,8 @@ If you'd like to move your resumes to the latest version of Reactive Resume, you 4. Use the import feature to upload your JSON file (select the "Reactive Resume v4 (JSON)" option) - Your existing resumes should already be available in the new version. If you don't see them, you can manually import - them using the steps above. + Your existing resumes should already be available in the new version. If you don't see them, you can manually import + them using the steps above. ## Questions or issues? diff --git a/docs/guides/adding-a-cover-letter.mdx b/docs/guides/adding-a-cover-letter.mdx index 290abdcfd..3ca2651ae 100644 --- a/docs/guides/adding-a-cover-letter.mdx +++ b/docs/guides/adding-a-cover-letter.mdx @@ -20,16 +20,16 @@ A cover letter is a personalized document that accompanies your resume when appl - In the left sidebar, scroll down to find the **Custom Sections** area at the bottom. + In the left sidebar, scroll down to find the **Custom Sections** area at the bottom. - Click Add a new custom section to open the section creation dialog. + Click Add a new custom section to open the section creation dialog. - In the dialog: - Enter a **Title** for your section (e.g., "Cover Letter" or "Cover Letter - [Company Name]") - Select - **Cover Letter** from the **Type** dropdown - Click **Create** to add the section + In the dialog: - Enter a **Title** for your section (e.g., "Cover Letter" or "Cover Letter - [Company Name]") - Select + **Cover Letter** from the **Type** dropdown - Click **Create** to add the section @@ -77,8 +77,8 @@ The **Content** field is where you write the body of your cover letter. Include: - **Sign-off**: "Sincerely," followed by your name - The recipient and content are both rendered directly on your resume without a section header, giving your cover letter - a clean, professional appearance. + The recipient and content are both rendered directly on your resume without a section header, giving your cover letter + a clean, professional appearance. ## Using fullscreen mode for writing @@ -91,13 +91,13 @@ For a distraction-free writing experience, use the fullscreen mode in the rich t - In either the Recipient or Content field, click the **expand icon** (arrows pointing outward) in the bottom-right - corner of the editor. + In either the Recipient or Content field, click the **expand icon** (arrows pointing outward) in the bottom-right + corner of the editor. - The editor will expand to nearly fill your screen, giving you a focused writing environment with all formatting tools - available. + The editor will expand to nearly fill your screen, giving you a focused writing environment with all formatting tools + available. @@ -139,8 +139,8 @@ Cover letters are typically on their own page, separate from the resume content. - When exporting as PDF, pages are rendered in order. If you want the cover letter first, place it on page 1 and move - your resume content to subsequent pages. + When exporting as PDF, pages are rendered in order. If you want the cover letter first, place it on page 1 and move + your resume content to subsequent pages. ## Tips for effective cover letters @@ -148,18 +148,18 @@ Cover letters are typically on their own page, separate from the resume content. **Keep it concise**: Aim for 250-400 words. Recruiters spend about one minute reading cover letters. - **Tailor each letter**: Customize your cover letter for each application. Reference specific job requirements and - company values. + **Tailor each letter**: Customize your cover letter for each application. Reference specific job requirements and + company values. - **Use the same styling**: Your cover letter will inherit the fonts and colors from your resume template, ensuring a - cohesive look. + **Use the same styling**: Your cover letter will inherit the fonts and colors from your resume template, ensuring a + cohesive look. - **Proofread carefully**: Spelling and grammar errors can disqualify your application. Review your letter before - exporting. + **Proofread carefully**: Spelling and grammar errors can disqualify your application. Review your letter before + exporting. ## Managing multiple cover letters diff --git a/docs/guides/checking-service-status.mdx b/docs/guides/checking-service-status.mdx index 3eda3eb45..ef239856a 100644 --- a/docs/guides/checking-service-status.mdx +++ b/docs/guides/checking-service-status.mdx @@ -8,7 +8,7 @@ description: "Learn how to check the status of Reactive Resume's servers and wha You can monitor the health and availability of Reactive Resume's servers at any time by visiting our status page: - View real-time server metrics including uptime, CPU usage, memory usage, and more. + View real-time server metrics including uptime, CPU usage, memory usage, and more. The status page provides information such as: @@ -48,8 +48,8 @@ If you notice the servers are experiencing high load or downtime, here are some ## A Note on Server Capacity - Reactive Resume is a **free service** that runs on limited server resources. As an open-source project maintained by a - single developer, it's not feasible to invest in powerful dedicated servers without community support. + Reactive Resume is a **free service** that runs on limited server resources. As an open-source project maintained by a + single developer, it's not feasible to invest in powerful dedicated servers without community support. The reality is: @@ -67,17 +67,17 @@ If you find Reactive Resume valuable and want to help keep the servers running s Your donations directly help cover server costs, improve infrastructure, and keep Reactive Resume free for everyone. - Support Reactive Resume's development and server costs through Open Collective. Every contribution helps keep the - service running. + Support Reactive Resume's development and server costs through Open Collective. Every contribution helps keep the + service running. - - Make a single contribution of any amount to help with immediate server costs. - - - Become a backer with a monthly contribution to provide sustainable support. - + + Make a single contribution of any amount to help with immediate server costs. + + + Become a backer with a monthly contribution to provide sustainable support. + --- @@ -87,7 +87,7 @@ Your donations directly help cover server costs, improve infrastructure, and kee If you need guaranteed uptime or want to avoid shared server limitations, you can always self-host Reactive Resume on your own infrastructure. - Learn how to deploy Reactive Resume on your own servers using Docker. + Learn how to deploy Reactive Resume on your own servers using Docker. Self-hosting gives you: diff --git a/docs/guides/choosing-a-template.mdx b/docs/guides/choosing-a-template.mdx index b1cdf842c..cfd8ed201 100644 --- a/docs/guides/choosing-a-template.mdx +++ b/docs/guides/choosing-a-template.mdx @@ -20,7 +20,7 @@ Changing your resume template is simple and can be done at any time without losi - In the resume builder, look for the right sidebar. This is where you'll find all the design and layout options. + In the resume builder, look for the right sidebar. This is where you'll find all the design and layout options. @@ -66,13 +66,13 @@ Changing your resume template is simple and can be done at any time without losi - If you have a lot of experience to showcase, choose a template with efficient space usage. For shorter resumes, - templates with more white space can make your content feel more substantial. + If you have a lot of experience to showcase, choose a template with efficient space usage. For shorter resumes, + templates with more white space can make your content feel more substantial. - Your resume is part of your personal brand. Choose a template that reflects your personality while remaining - professional and appropriate for your target roles. + Your resume is part of your personal brand. Choose a template that reflects your personality while remaining + professional and appropriate for your target roles. @@ -87,8 +87,8 @@ Changing your resume template is simple and can be done at any time without losi Reactive Resume includes a variety of professionally designed templates. Each template can be further customized with your choice of colors, fonts, and layout options. - All templates support the same features and sections. The difference is purely in how they present your information - visually. + All templates support the same features and sections. The difference is purely in how they present your information + visually.
@@ -97,47 +97,47 @@ Reactive Resume includes a variety of professionally designed templates. Each te - Bronzor template preview + Bronzor template preview - Chikorita template preview + Chikorita template preview - Ditto template preview + Ditto template preview - Ditgar template preview + Ditgar template preview - Gengar template preview + Gengar template preview - Glalie template preview + Glalie template preview - Kakuna template preview + Kakuna template preview - Lapras template preview + Lapras template preview - Leafish template preview + Leafish template preview - Onyx template preview + Onyx template preview - Pikachu template preview + Pikachu template preview diff --git a/docs/guides/creating-an-account.mdx b/docs/guides/creating-an-account.mdx index 9b74ebee1..736228bae 100644 --- a/docs/guides/creating-an-account.mdx +++ b/docs/guides/creating-an-account.mdx @@ -59,12 +59,12 @@ description: "Learn how to create an account on Reactive Resume and get started ## Account Security Tips - - Create a password that's at least 8 characters long and includes a mix of letters, numbers, and special characters. - - - Setup two-factor authentication or passkeys on your account to add an extra layer of security. - + + Create a password that's at least 8 characters long and includes a mix of letters, numbers, and special characters. + + + Setup two-factor authentication or passkeys on your account to add an extra layer of security. + --- diff --git a/docs/guides/creating-your-first-resume.mdx b/docs/guides/creating-your-first-resume.mdx index aa3f6e284..c10119b4c 100644 --- a/docs/guides/creating-your-first-resume.mdx +++ b/docs/guides/creating-your-first-resume.mdx @@ -23,7 +23,7 @@ description: "Learn how to create your first resume in Reactive Resume, set a na - In the Resumes Dashboard, click on the Create a new resume card to open the creation form. + In the Resumes Dashboard, click on the Create a new resume card to open the creation form. diff --git a/docs/guides/deleting-your-account.mdx b/docs/guides/deleting-your-account.mdx index c94b3328c..31afb5dee 100644 --- a/docs/guides/deleting-your-account.mdx +++ b/docs/guides/deleting-your-account.mdx @@ -9,8 +9,8 @@ description: "Learn how to permanently delete your Reactive Resume account and a - Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section, - click on Danger Zone. + Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section, + click on Danger Zone. diff --git a/docs/guides/exporting-your-resume.mdx b/docs/guides/exporting-your-resume.mdx index 6b37b5d3d..11d2382a7 100644 --- a/docs/guides/exporting-your-resume.mdx +++ b/docs/guides/exporting-your-resume.mdx @@ -19,8 +19,8 @@ You'll see two export options: Click the **PDF** button to generate and download a PDF version of your resume. - The PDF is rendered server-side using your current resume data and template settings. What you see in the preview is - what you'll get in the PDF, split into multiple pages if necessary. + The PDF is rendered server-side using your current resume data and template settings. What you see in the preview is + what you'll get in the PDF, split into multiple pages if necessary. ### When to use PDF exports @@ -52,8 +52,8 @@ The JSON file contains your **complete resume data**, including: - You can import a JSON export back into Reactive Resume to create a new resume based on that data. This is useful for - creating variations or restoring from a backup. + You can import a JSON export back into Reactive Resume to create a new resume based on that data. This is useful for + creating variations or restoring from a backup. @@ -104,8 +104,8 @@ One of the most powerful uses for JSON exports is working with AI assistants lik - Always review AI-generated changes before using them. AI assistants can make mistakes or suggest content that doesn't - accurately reflect your experience. + Always review AI-generated changes before using them. AI assistants can make mistakes or suggest content that doesn't + accurately reflect your experience. ### Example prompts for AI assistants @@ -127,1056 +127,1056 @@ The exported JSON follows the Reactive Resume schema. Here's a high-level overvi ```json { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "picture": { - "type": "object", - "properties": { - "hidden": { "type": "boolean", "description": "Whether to hide the picture from the resume." }, - "url": { - "type": "string", - "description": "The URL to the picture to display on the resume. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide." - }, - "size": { - "type": "number", - "minimum": 32, - "maximum": 512, - "description": "The size of the picture to display on the resume, defined in points (pt)." - }, - "rotation": { - "type": "number", - "minimum": 0, - "maximum": 360, - "description": "The rotation of the picture to display on the resume, defined in degrees (°)." - }, - "aspectRatio": { - "type": "number", - "minimum": 0.5, - "maximum": 2.5, - "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)." - }, - "borderRadius": { - "type": "number", - "minimum": 0, - "maximum": 100, - "description": "The border radius of the picture to display on the resume, defined in points (pt)." - }, - "borderColor": { - "type": "string", - "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)." - }, - "borderWidth": { - "type": "number", - "minimum": 0, - "description": "The width of the border of the picture to display on the resume, defined in points (pt)." - }, - "shadowColor": { - "type": "string", - "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)." - }, - "shadowWidth": { - "type": "number", - "minimum": 0, - "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)." - } - }, - "required": [ - "hidden", - "url", - "size", - "rotation", - "aspectRatio", - "borderRadius", - "borderColor", - "borderWidth", - "shadowColor", - "shadowWidth" - ], - "additionalProperties": false, - "description": "Configuration for photograph displayed on the resume" - }, - "basics": { - "type": "object", - "properties": { - "name": { "type": "string", "description": "The full name of the author of the resume." }, - "headline": { "type": "string", "description": "The headline of the author of the resume." }, - "email": { - "anyOf": [ - { - "type": "string", - "format": "email", - "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$" - }, - { "type": "string", "const": "" } - ], - "description": "The email address of the author of the resume. Leave blank to hide." - }, - "phone": { - "type": "string", - "description": "The phone number of the author of the resume. Leave blank to hide." - }, - "location": { "type": "string", "description": "The location of the author of the resume." }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The website of the author of the resume." - }, - "customFields": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the custom field. Usually generated as a UUID." - }, - "icon": { - "type": "string", - "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." - }, - "text": { "type": "string", "description": "The text to display for the custom field." } - }, - "required": ["id", "icon", "text"], - "additionalProperties": false - }, - "description": "The custom fields to display on the resume." - } - }, - "required": ["name", "headline", "email", "phone", "location", "website", "customFields"], - "additionalProperties": false, - "description": "Basic information about the author, such as name, email, phone, location, and website" - }, - "summary": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the summary of the resume." }, - "columns": { "type": "number", "description": "The number of columns the summary should span across." }, - "hidden": { "type": "boolean", "description": "Whether to hide the summary from the resume." }, - "content": { - "type": "string", - "description": "The content of the summary of the resume. This should be a HTML-formatted string. Leave blank to hide." - } - }, - "required": ["title", "columns", "hidden", "content"], - "additionalProperties": false, - "description": "Summary section of the resume, useful for a short bio or introduction" - }, - "sections": { - "type": "object", - "properties": { - "profiles": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { "type": "number", "description": "The number of columns the section should span across." }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "icon": { - "type": "string", - "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." - }, - "network": { - "type": "string", - "minLength": 1, - "description": "The name of the network or platform." - }, - "username": { - "type": "string", - "description": "The username of the author on the network or platform." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The link to the profile of the author on the network or platform, if any." - } - }, - "required": ["id", "hidden", "icon", "network", "username", "website"], - "additionalProperties": false - }, - "description": "The items to display in the profiles section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the profiles of the author." - }, - "experience": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { "type": "number", "description": "The number of columns the section should span across." }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "company": { - "type": "string", - "minLength": 1, - "description": "The name of the company or organization." - }, - "position": { "type": "string", "description": "The position held at the company or organization." }, - "location": { "type": "string", "description": "The location of the company or organization." }, - "period": { - "type": "string", - "description": "The period of time the author was employed at the company or organization." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The website of the company or organization, if any." - }, - "description": { - "type": "string", - "description": "The description of the experience. This should be a HTML-formatted string. Leave blank to hide." - } - }, - "required": ["id", "hidden", "company", "position", "location", "period", "website", "description"], - "additionalProperties": false - }, - "description": "The items to display in the experience section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the experience of the author." - }, - "education": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { "type": "number", "description": "The number of columns the section should span across." }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "school": { - "type": "string", - "minLength": 1, - "description": "The name of the school or institution." - }, - "degree": { "type": "string", "description": "The degree or qualification obtained." }, - "area": { "type": "string", "description": "The area of study or specialization." }, - "grade": { "type": "string", "description": "The grade or score achieved." }, - "location": { "type": "string", "description": "The location of the school or institution." }, - "period": { "type": "string", "description": "The period of time the education was obtained over." }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The website of the school or institution, if any." - }, - "description": { - "type": "string", - "description": "The description of the education. This should be a HTML-formatted string. Leave blank to hide." - } - }, - "required": [ - "id", - "hidden", - "school", - "degree", - "area", - "grade", - "location", - "period", - "website", - "description" - ], - "additionalProperties": false - }, - "description": "The items to display in the education section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the education of the author." - }, - "projects": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { "type": "number", "description": "The number of columns the section should span across." }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "name": { "type": "string", "minLength": 1, "description": "The name of the project." }, - "period": { "type": "string", "description": "The period of time the project was worked on." }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The link to the project, if any." - }, - "description": { - "type": "string", - "description": "The description of the project. This should be a HTML-formatted string. Leave blank to hide." - } - }, - "required": ["id", "hidden", "name", "period", "website", "description"], - "additionalProperties": false - }, - "description": "The items to display in the projects section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the projects of the author." - }, - "skills": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { "type": "number", "description": "The number of columns the section should span across." }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "icon": { - "type": "string", - "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." - }, - "name": { "type": "string", "minLength": 1, "description": "The name of the skill." }, - "proficiency": { - "type": "string", - "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc." - }, - "level": { - "default": 0, - "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.", - "type": "number", - "minimum": 0, - "maximum": 5 - }, - "keywords": { - "default": [], - "description": "The keywords associated with the skill, if any. These are displayed as tags below the name.", - "type": "array", - "items": { "type": "string" } - } - }, - "required": ["id", "hidden", "icon", "name", "proficiency", "level", "keywords"], - "additionalProperties": false - }, - "description": "The items to display in the skills section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the skills of the author." - }, - "languages": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { "type": "number", "description": "The number of columns the section should span across." }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "language": { - "type": "string", - "minLength": 1, - "description": "The name of the language the author knows." - }, - "fluency": { - "type": "string", - "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." - }, - "level": { - "default": 0, - "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.", - "type": "number", - "minimum": 0, - "maximum": 5 - } - }, - "required": ["id", "hidden", "language", "fluency", "level"], - "additionalProperties": false - }, - "description": "The items to display in the languages section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the languages of the author." - }, - "interests": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { "type": "number", "description": "The number of columns the section should span across." }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "icon": { - "type": "string", - "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." - }, - "name": { "type": "string", "minLength": 1, "description": "The name of the interest/hobby." }, - "keywords": { - "default": [], - "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name.", - "type": "array", - "items": { "type": "string" } - } - }, - "required": ["id", "hidden", "icon", "name", "keywords"], - "additionalProperties": false - }, - "description": "The items to display in the interests section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the interests of the author." - }, - "awards": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { "type": "number", "description": "The number of columns the section should span across." }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "title": { "type": "string", "minLength": 1, "description": "The title of the award." }, - "awarder": { "type": "string", "description": "The awarder of the award." }, - "date": { "type": "string", "description": "The date when the award was received." }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The website of the award, if any." - }, - "description": { - "type": "string", - "description": "The description of the award. This should be a HTML-formatted string. Leave blank to hide." - } - }, - "required": ["id", "hidden", "title", "awarder", "date", "website", "description"], - "additionalProperties": false - }, - "description": "The items to display in the awards section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the awards of the author." - }, - "certifications": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { "type": "number", "description": "The number of columns the section should span across." }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "title": { "type": "string", "minLength": 1, "description": "The title of the certification." }, - "issuer": { "type": "string", "description": "The issuer of the certification." }, - "date": { "type": "string", "description": "The date when the certification was received." }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The website of the certification, if any." - }, - "description": { - "type": "string", - "description": "The description of the certification. This should be a HTML-formatted string. Leave blank to hide." - } - }, - "required": ["id", "hidden", "title", "issuer", "date", "website", "description"], - "additionalProperties": false - }, - "description": "The items to display in the certifications section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the certifications of the author." - }, - "publications": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { "type": "number", "description": "The number of columns the section should span across." }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "title": { "type": "string", "minLength": 1, "description": "The title of the publication." }, - "publisher": { "type": "string", "description": "The publisher of the publication." }, - "date": { "type": "string", "description": "The date when the publication was published." }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The link to the publication, if any." - }, - "description": { - "type": "string", - "description": "The description of the publication. This should be a HTML-formatted string. Leave blank to hide." - } - }, - "required": ["id", "hidden", "title", "publisher", "date", "website", "description"], - "additionalProperties": false - }, - "description": "The items to display in the publications section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the publications of the author." - }, - "volunteer": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { "type": "number", "description": "The number of columns the section should span across." }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "organization": { - "type": "string", - "minLength": 1, - "description": "The name of the organization or company." - }, - "location": { "type": "string", "description": "The location of the organization or company." }, - "period": { - "type": "string", - "description": "The period of time the author was volunteered at the organization or company." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The link to the organization or company, if any." - }, - "description": { - "type": "string", - "description": "The description of the volunteer experience. This should be a HTML-formatted string. Leave blank to hide." - } - }, - "required": ["id", "hidden", "organization", "location", "period", "website", "description"], - "additionalProperties": false - }, - "description": "The items to display in the volunteer section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the volunteer experience of the author." - }, - "references": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { "type": "number", "description": "The number of columns the section should span across." }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "name": { - "type": "string", - "minLength": 1, - "description": "The name of the reference, or a note such as 'Available upon request'." - }, - "description": { - "type": "string", - "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string. Leave blank to hide." - } - }, - "required": ["id", "hidden", "name", "description"], - "additionalProperties": false - }, - "description": "The items to display in the references section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the references of the author." - } - }, - "required": [ - "profiles", - "experience", - "education", - "projects", - "skills", - "languages", - "interests", - "awards", - "certifications", - "publications", - "volunteer", - "references" - ], - "additionalProperties": false, - "description": "Various sections of the resume, such as experience, education, projects, etc." - }, - "customSections": { - "type": "array", - "items": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { "type": "number", "description": "The number of columns the section should span across." }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "id": { - "type": "string", - "description": "The unique identifier for the custom section. Usually generated as a UUID." - }, - "content": { - "type": "string", - "description": "The content of the custom section. This should be a HTML-formatted string. Leave blank to hide." - } - }, - "required": ["title", "columns", "hidden", "id", "content"], - "additionalProperties": false - }, - "description": "Custom sections of the resume, such as a custom section for notes, etc." - }, - "metadata": { - "type": "object", - "properties": { - "template": { - "default": "onyx", - "description": "The template to use for the resume. Determines the overall design and appearance of the resume.", - "type": "string", - "enum": [ - "azurill", - "bronzor", - "chikorita", - "ditto", - "gengar", - "glalie", - "kakuna", - "lapras", - "leafish", - "onyx", - "pikachu", - "rhyhorn" - ] - }, - "layout": { - "type": "object", - "properties": { - "sidebarWidth": { - "default": 35, - "description": "The width of the sidebar column, defined as a percentage of the page width.", - "type": "number", - "minimum": 10, - "maximum": 50 - }, - "pages": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fullWidth": { - "type": "boolean", - "description": "Whether the layout of the page should be full width. If true, the main column will span the entire width of the page. This means that there should be no items in the sidebar column." - }, - "main": { - "type": "array", - "items": { "type": "string" }, - "description": "The items to display in the main column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." - }, - "sidebar": { - "type": "array", - "items": { "type": "string" }, - "description": "The items to display in the sidebar column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." - } - }, - "required": ["fullWidth", "main", "sidebar"], - "additionalProperties": false - }, - "description": "The pages to display in the layout." - } - }, - "required": ["sidebarWidth", "pages"], - "additionalProperties": false, - "description": "The layout of the resume. Determines the structure and arrangement of the sections on the resume." - }, - "css": { - "type": "object", - "properties": { - "enabled": { "type": "boolean", "description": "Whether to enable custom CSS for the resume." }, - "value": { - "type": "string", - "description": "The custom CSS to apply to the resume. This should be a valid CSS string." - } - }, - "required": ["enabled", "value"], - "additionalProperties": false, - "description": "Custom CSS to apply to the resume. Can be used to override the default styles of the template." - }, - "page": { - "type": "object", - "properties": { - "gapX": { - "type": "number", - "minimum": 0, - "description": "The horizontal gap between the sections of the page, defined in points (pt)." - }, - "gapY": { - "type": "number", - "minimum": 0, - "description": "The vertical gap between the sections of the page, defined in points (pt)." - }, - "marginX": { - "type": "number", - "minimum": 0, - "description": "The horizontal margin of the page, defined in points (pt)." - }, - "marginY": { - "type": "number", - "minimum": 0, - "description": "The vertical margin of the page, defined in points (pt)." - }, - "format": { - "type": "string", - "enum": ["a4", "letter"], - "description": "The format of the page. Can be 'a4' or 'letter'." - }, - "locale": { - "default": "en-US", - "anyOf": [ - { "type": "string", "const": "en-US" }, - { "type": "string", "const": "de-DE" }, - { "type": "string", "const": "zu-ZA" } - ], - "description": "The language used in the resume, used for displaying pre-translated section headings, if not overridden." - } - }, - "required": ["gapX", "gapY", "marginX", "marginY", "format", "locale"], - "additionalProperties": false, - "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." - }, - "design": { - "type": "object", - "properties": { - "level": { - "type": "object", - "properties": { - "icon": { - "type": "string", - "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." - }, - "type": { - "type": "string", - "enum": ["hidden", "circle", "square", "rectangle", "rectangle-full", "progress-bar", "icon"], - "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field." - } - }, - "required": ["icon", "type"], - "additionalProperties": false - }, - "colors": { - "type": "object", - "properties": { - "primary": { - "type": "string", - "description": "The primary color of the design, defined as rgba(r, g, b, a)." - }, - "text": { - "type": "string", - "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)." - }, - "background": { - "type": "string", - "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)." - } - }, - "required": ["primary", "text", "background"], - "additionalProperties": false - } - }, - "required": ["level", "colors"], - "additionalProperties": false, - "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume." - }, - "typography": { - "type": "object", - "properties": { - "body": { - "type": "object", - "properties": { - "fontFamily": { - "type": "string", - "description": "The family of the font to use. Must be a font that is available on Google Fonts." - }, - "fontWeights": { - "default": ["400"], - "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font.", - "type": "array", - "items": { "type": "string", "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] } - }, - "fontSize": { - "default": 11, - "description": "The size of the font to use, defined in points (pt).", - "type": "number", - "minimum": 6, - "maximum": 24 - }, - "lineHeight": { - "default": 1.5, - "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x).", - "type": "number", - "minimum": 0.5, - "maximum": 4 - } - }, - "required": ["fontFamily", "fontWeights", "fontSize", "lineHeight"], - "additionalProperties": false, - "description": "The typography for the body of the resume." - }, - "heading": { - "type": "object", - "properties": { - "fontFamily": { - "type": "string", - "description": "The family of the font to use. Must be a font that is available on Google Fonts." - }, - "fontWeights": { - "default": ["400"], - "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font.", - "type": "array", - "items": { "type": "string", "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] } - }, - "fontSize": { - "default": 11, - "description": "The size of the font to use, defined in points (pt).", - "type": "number", - "minimum": 6, - "maximum": 24 - }, - "lineHeight": { - "default": 1.5, - "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x).", - "type": "number", - "minimum": 0.5, - "maximum": 4 - } - }, - "required": ["fontFamily", "fontWeights", "fontSize", "lineHeight"], - "additionalProperties": false, - "description": "The typography for the headings of the resume." - } - }, - "required": ["body", "heading"], - "additionalProperties": false, - "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume." - }, - "notes": { - "type": "string", - "description": "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." - } - }, - "required": ["template", "layout", "css", "page", "design", "typography", "notes"], - "additionalProperties": false, - "description": "Metadata for the resume, such as template, layout, typography, etc. This section describes the overall design and appearance of the resume." - }, - "version": { "type": "string", "const": "5.0.0", "description": "The version of the Reactive Resume JSON Schema" }, - "$schema": { - "type": "string", - "const": "http://localhost:3000/schema.json", - "description": "The URL of the Reactive Resume JSON Schema, used for validation and documentation purposes." - } - }, - "required": ["picture", "basics", "summary", "sections", "customSections", "metadata", "version", "$schema"], - "additionalProperties": false + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "picture": { + "type": "object", + "properties": { + "hidden": { "type": "boolean", "description": "Whether to hide the picture from the resume." }, + "url": { + "type": "string", + "description": "The URL to the picture to display on the resume. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide." + }, + "size": { + "type": "number", + "minimum": 32, + "maximum": 512, + "description": "The size of the picture to display on the resume, defined in points (pt)." + }, + "rotation": { + "type": "number", + "minimum": 0, + "maximum": 360, + "description": "The rotation of the picture to display on the resume, defined in degrees (°)." + }, + "aspectRatio": { + "type": "number", + "minimum": 0.5, + "maximum": 2.5, + "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)." + }, + "borderRadius": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The border radius of the picture to display on the resume, defined in points (pt)." + }, + "borderColor": { + "type": "string", + "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "borderWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the border of the picture to display on the resume, defined in points (pt)." + }, + "shadowColor": { + "type": "string", + "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "shadowWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)." + } + }, + "required": [ + "hidden", + "url", + "size", + "rotation", + "aspectRatio", + "borderRadius", + "borderColor", + "borderWidth", + "shadowColor", + "shadowWidth" + ], + "additionalProperties": false, + "description": "Configuration for photograph displayed on the resume" + }, + "basics": { + "type": "object", + "properties": { + "name": { "type": "string", "description": "The full name of the author of the resume." }, + "headline": { "type": "string", "description": "The headline of the author of the resume." }, + "email": { + "anyOf": [ + { + "type": "string", + "format": "email", + "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$" + }, + { "type": "string", "const": "" } + ], + "description": "The email address of the author of the resume. Leave blank to hide." + }, + "phone": { + "type": "string", + "description": "The phone number of the author of the resume. Leave blank to hide." + }, + "location": { "type": "string", "description": "The location of the author of the resume." }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The website of the author of the resume." + }, + "customFields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the custom field. Usually generated as a UUID." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "text": { "type": "string", "description": "The text to display for the custom field." } + }, + "required": ["id", "icon", "text"], + "additionalProperties": false + }, + "description": "The custom fields to display on the resume." + } + }, + "required": ["name", "headline", "email", "phone", "location", "website", "customFields"], + "additionalProperties": false, + "description": "Basic information about the author, such as name, email, phone, location, and website" + }, + "summary": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the summary of the resume." }, + "columns": { "type": "number", "description": "The number of columns the summary should span across." }, + "hidden": { "type": "boolean", "description": "Whether to hide the summary from the resume." }, + "content": { + "type": "string", + "description": "The content of the summary of the resume. This should be a HTML-formatted string. Leave blank to hide." + } + }, + "required": ["title", "columns", "hidden", "content"], + "additionalProperties": false, + "description": "Summary section of the resume, useful for a short bio or introduction" + }, + "sections": { + "type": "object", + "properties": { + "profiles": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { "type": "number", "description": "The number of columns the section should span across." }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "network": { + "type": "string", + "minLength": 1, + "description": "The name of the network or platform." + }, + "username": { + "type": "string", + "description": "The username of the author on the network or platform." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The link to the profile of the author on the network or platform, if any." + } + }, + "required": ["id", "hidden", "icon", "network", "username", "website"], + "additionalProperties": false + }, + "description": "The items to display in the profiles section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the profiles of the author." + }, + "experience": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { "type": "number", "description": "The number of columns the section should span across." }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "company": { + "type": "string", + "minLength": 1, + "description": "The name of the company or organization." + }, + "position": { "type": "string", "description": "The position held at the company or organization." }, + "location": { "type": "string", "description": "The location of the company or organization." }, + "period": { + "type": "string", + "description": "The period of time the author was employed at the company or organization." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The website of the company or organization, if any." + }, + "description": { + "type": "string", + "description": "The description of the experience. This should be a HTML-formatted string. Leave blank to hide." + } + }, + "required": ["id", "hidden", "company", "position", "location", "period", "website", "description"], + "additionalProperties": false + }, + "description": "The items to display in the experience section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the experience of the author." + }, + "education": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { "type": "number", "description": "The number of columns the section should span across." }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "school": { + "type": "string", + "minLength": 1, + "description": "The name of the school or institution." + }, + "degree": { "type": "string", "description": "The degree or qualification obtained." }, + "area": { "type": "string", "description": "The area of study or specialization." }, + "grade": { "type": "string", "description": "The grade or score achieved." }, + "location": { "type": "string", "description": "The location of the school or institution." }, + "period": { "type": "string", "description": "The period of time the education was obtained over." }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The website of the school or institution, if any." + }, + "description": { + "type": "string", + "description": "The description of the education. This should be a HTML-formatted string. Leave blank to hide." + } + }, + "required": [ + "id", + "hidden", + "school", + "degree", + "area", + "grade", + "location", + "period", + "website", + "description" + ], + "additionalProperties": false + }, + "description": "The items to display in the education section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the education of the author." + }, + "projects": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { "type": "number", "description": "The number of columns the section should span across." }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "name": { "type": "string", "minLength": 1, "description": "The name of the project." }, + "period": { "type": "string", "description": "The period of time the project was worked on." }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The link to the project, if any." + }, + "description": { + "type": "string", + "description": "The description of the project. This should be a HTML-formatted string. Leave blank to hide." + } + }, + "required": ["id", "hidden", "name", "period", "website", "description"], + "additionalProperties": false + }, + "description": "The items to display in the projects section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the projects of the author." + }, + "skills": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { "type": "number", "description": "The number of columns the section should span across." }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "name": { "type": "string", "minLength": 1, "description": "The name of the skill." }, + "proficiency": { + "type": "string", + "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc." + }, + "level": { + "default": 0, + "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.", + "type": "number", + "minimum": 0, + "maximum": 5 + }, + "keywords": { + "default": [], + "description": "The keywords associated with the skill, if any. These are displayed as tags below the name.", + "type": "array", + "items": { "type": "string" } + } + }, + "required": ["id", "hidden", "icon", "name", "proficiency", "level", "keywords"], + "additionalProperties": false + }, + "description": "The items to display in the skills section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the skills of the author." + }, + "languages": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { "type": "number", "description": "The number of columns the section should span across." }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "language": { + "type": "string", + "minLength": 1, + "description": "The name of the language the author knows." + }, + "fluency": { + "type": "string", + "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." + }, + "level": { + "default": 0, + "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.", + "type": "number", + "minimum": 0, + "maximum": 5 + } + }, + "required": ["id", "hidden", "language", "fluency", "level"], + "additionalProperties": false + }, + "description": "The items to display in the languages section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the languages of the author." + }, + "interests": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { "type": "number", "description": "The number of columns the section should span across." }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "name": { "type": "string", "minLength": 1, "description": "The name of the interest/hobby." }, + "keywords": { + "default": [], + "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name.", + "type": "array", + "items": { "type": "string" } + } + }, + "required": ["id", "hidden", "icon", "name", "keywords"], + "additionalProperties": false + }, + "description": "The items to display in the interests section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the interests of the author." + }, + "awards": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { "type": "number", "description": "The number of columns the section should span across." }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "title": { "type": "string", "minLength": 1, "description": "The title of the award." }, + "awarder": { "type": "string", "description": "The awarder of the award." }, + "date": { "type": "string", "description": "The date when the award was received." }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The website of the award, if any." + }, + "description": { + "type": "string", + "description": "The description of the award. This should be a HTML-formatted string. Leave blank to hide." + } + }, + "required": ["id", "hidden", "title", "awarder", "date", "website", "description"], + "additionalProperties": false + }, + "description": "The items to display in the awards section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the awards of the author." + }, + "certifications": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { "type": "number", "description": "The number of columns the section should span across." }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "title": { "type": "string", "minLength": 1, "description": "The title of the certification." }, + "issuer": { "type": "string", "description": "The issuer of the certification." }, + "date": { "type": "string", "description": "The date when the certification was received." }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The website of the certification, if any." + }, + "description": { + "type": "string", + "description": "The description of the certification. This should be a HTML-formatted string. Leave blank to hide." + } + }, + "required": ["id", "hidden", "title", "issuer", "date", "website", "description"], + "additionalProperties": false + }, + "description": "The items to display in the certifications section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the certifications of the author." + }, + "publications": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { "type": "number", "description": "The number of columns the section should span across." }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "title": { "type": "string", "minLength": 1, "description": "The title of the publication." }, + "publisher": { "type": "string", "description": "The publisher of the publication." }, + "date": { "type": "string", "description": "The date when the publication was published." }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The link to the publication, if any." + }, + "description": { + "type": "string", + "description": "The description of the publication. This should be a HTML-formatted string. Leave blank to hide." + } + }, + "required": ["id", "hidden", "title", "publisher", "date", "website", "description"], + "additionalProperties": false + }, + "description": "The items to display in the publications section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the publications of the author." + }, + "volunteer": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { "type": "number", "description": "The number of columns the section should span across." }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "organization": { + "type": "string", + "minLength": 1, + "description": "The name of the organization or company." + }, + "location": { "type": "string", "description": "The location of the organization or company." }, + "period": { + "type": "string", + "description": "The period of time the author was volunteered at the organization or company." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://). Leave blank to hide." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The link to the organization or company, if any." + }, + "description": { + "type": "string", + "description": "The description of the volunteer experience. This should be a HTML-formatted string. Leave blank to hide." + } + }, + "required": ["id", "hidden", "organization", "location", "period", "website", "description"], + "additionalProperties": false + }, + "description": "The items to display in the volunteer section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the volunteer experience of the author." + }, + "references": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { "type": "number", "description": "The number of columns the section should span across." }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the reference, or a note such as 'Available upon request'." + }, + "description": { + "type": "string", + "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string. Leave blank to hide." + } + }, + "required": ["id", "hidden", "name", "description"], + "additionalProperties": false + }, + "description": "The items to display in the references section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the references of the author." + } + }, + "required": [ + "profiles", + "experience", + "education", + "projects", + "skills", + "languages", + "interests", + "awards", + "certifications", + "publications", + "volunteer", + "references" + ], + "additionalProperties": false, + "description": "Various sections of the resume, such as experience, education, projects, etc." + }, + "customSections": { + "type": "array", + "items": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { "type": "number", "description": "The number of columns the section should span across." }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "content": { + "type": "string", + "description": "The content of the custom section. This should be a HTML-formatted string. Leave blank to hide." + } + }, + "required": ["title", "columns", "hidden", "id", "content"], + "additionalProperties": false + }, + "description": "Custom sections of the resume, such as a custom section for notes, etc." + }, + "metadata": { + "type": "object", + "properties": { + "template": { + "default": "onyx", + "description": "The template to use for the resume. Determines the overall design and appearance of the resume.", + "type": "string", + "enum": [ + "azurill", + "bronzor", + "chikorita", + "ditto", + "gengar", + "glalie", + "kakuna", + "lapras", + "leafish", + "onyx", + "pikachu", + "rhyhorn" + ] + }, + "layout": { + "type": "object", + "properties": { + "sidebarWidth": { + "default": 35, + "description": "The width of the sidebar column, defined as a percentage of the page width.", + "type": "number", + "minimum": 10, + "maximum": 50 + }, + "pages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fullWidth": { + "type": "boolean", + "description": "Whether the layout of the page should be full width. If true, the main column will span the entire width of the page. This means that there should be no items in the sidebar column." + }, + "main": { + "type": "array", + "items": { "type": "string" }, + "description": "The items to display in the main column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." + }, + "sidebar": { + "type": "array", + "items": { "type": "string" }, + "description": "The items to display in the sidebar column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." + } + }, + "required": ["fullWidth", "main", "sidebar"], + "additionalProperties": false + }, + "description": "The pages to display in the layout." + } + }, + "required": ["sidebarWidth", "pages"], + "additionalProperties": false, + "description": "The layout of the resume. Determines the structure and arrangement of the sections on the resume." + }, + "css": { + "type": "object", + "properties": { + "enabled": { "type": "boolean", "description": "Whether to enable custom CSS for the resume." }, + "value": { + "type": "string", + "description": "The custom CSS to apply to the resume. This should be a valid CSS string." + } + }, + "required": ["enabled", "value"], + "additionalProperties": false, + "description": "Custom CSS to apply to the resume. Can be used to override the default styles of the template." + }, + "page": { + "type": "object", + "properties": { + "gapX": { + "type": "number", + "minimum": 0, + "description": "The horizontal gap between the sections of the page, defined in points (pt)." + }, + "gapY": { + "type": "number", + "minimum": 0, + "description": "The vertical gap between the sections of the page, defined in points (pt)." + }, + "marginX": { + "type": "number", + "minimum": 0, + "description": "The horizontal margin of the page, defined in points (pt)." + }, + "marginY": { + "type": "number", + "minimum": 0, + "description": "The vertical margin of the page, defined in points (pt)." + }, + "format": { + "type": "string", + "enum": ["a4", "letter"], + "description": "The format of the page. Can be 'a4' or 'letter'." + }, + "locale": { + "default": "en-US", + "anyOf": [ + { "type": "string", "const": "en-US" }, + { "type": "string", "const": "de-DE" }, + { "type": "string", "const": "zu-ZA" } + ], + "description": "The language used in the resume, used for displaying pre-translated section headings, if not overridden." + } + }, + "required": ["gapX", "gapY", "marginX", "marginY", "format", "locale"], + "additionalProperties": false, + "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." + }, + "design": { + "type": "object", + "properties": { + "level": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "type": { + "type": "string", + "enum": ["hidden", "circle", "square", "rectangle", "rectangle-full", "progress-bar", "icon"], + "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field." + } + }, + "required": ["icon", "type"], + "additionalProperties": false + }, + "colors": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "description": "The primary color of the design, defined as rgba(r, g, b, a)." + }, + "text": { + "type": "string", + "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)." + }, + "background": { + "type": "string", + "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)." + } + }, + "required": ["primary", "text", "background"], + "additionalProperties": false + } + }, + "required": ["level", "colors"], + "additionalProperties": false, + "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume." + }, + "typography": { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a font that is available on Google Fonts." + }, + "fontWeights": { + "default": ["400"], + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font.", + "type": "array", + "items": { "type": "string", "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] } + }, + "fontSize": { + "default": 11, + "description": "The size of the font to use, defined in points (pt).", + "type": "number", + "minimum": 6, + "maximum": 24 + }, + "lineHeight": { + "default": 1.5, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x).", + "type": "number", + "minimum": 0.5, + "maximum": 4 + } + }, + "required": ["fontFamily", "fontWeights", "fontSize", "lineHeight"], + "additionalProperties": false, + "description": "The typography for the body of the resume." + }, + "heading": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a font that is available on Google Fonts." + }, + "fontWeights": { + "default": ["400"], + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font.", + "type": "array", + "items": { "type": "string", "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] } + }, + "fontSize": { + "default": 11, + "description": "The size of the font to use, defined in points (pt).", + "type": "number", + "minimum": 6, + "maximum": 24 + }, + "lineHeight": { + "default": 1.5, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x).", + "type": "number", + "minimum": 0.5, + "maximum": 4 + } + }, + "required": ["fontFamily", "fontWeights", "fontSize", "lineHeight"], + "additionalProperties": false, + "description": "The typography for the headings of the resume." + } + }, + "required": ["body", "heading"], + "additionalProperties": false, + "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume." + }, + "notes": { + "type": "string", + "description": "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." + } + }, + "required": ["template", "layout", "css", "page", "design", "typography", "notes"], + "additionalProperties": false, + "description": "Metadata for the resume, such as template, layout, typography, etc. This section describes the overall design and appearance of the resume." + }, + "version": { "type": "string", "const": "5.0.0", "description": "The version of the Reactive Resume JSON Schema" }, + "$schema": { + "type": "string", + "const": "http://localhost:3000/schema.json", + "description": "The URL of the Reactive Resume JSON Schema, used for validation and documentation purposes." + } + }, + "required": ["picture", "basics", "summary", "sections", "customSections", "metadata", "version", "$schema"], + "additionalProperties": false } ``` - You can access the complete JSON schema at [`https://rxresu.me/schema.json`](https://rxresu.me/schema.json). - Referencing this is helpful for validating your resume JSON or providing a detailed schema reference when - collaborating with AI assistants. + You can access the complete JSON schema at [`https://rxresu.me/schema.json`](https://rxresu.me/schema.json). + Referencing this is helpful for validating your resume JSON or providing a detailed schema reference when + collaborating with AI assistants. ## Re-importing your JSON @@ -1191,7 +1191,7 @@ To import a JSON file back into Reactive Resume: Find the import card and click it to open the import dialog. - In the format options, select **Reactive Resume (JSON)** if your file was exported from the current version. + In the format options, select **Reactive Resume (JSON)** if your file was exported from the current version. Click the upload area and select your JSON file. diff --git a/docs/guides/fitting-content-on-a-page.mdx b/docs/guides/fitting-content-on-a-page.mdx index 18f22642e..456e55238 100644 --- a/docs/guides/fitting-content-on-a-page.mdx +++ b/docs/guides/fitting-content-on-a-page.mdx @@ -6,16 +6,16 @@ description: "Learn how to reorganize and format your resume content to fit with When your resume content overflows the page, Reactive Resume displays a warning just below the page. This guide explains how to reorganize and adjust your content so everything fits within your chosen page format. - Screenshot of the overflow warning message in the resume builder + Screenshot of the overflow warning message in the resume builder - While Reactive Resume supports multi-page resumes, each page has a fixed height based on your chosen format (A4 or - Letter). If a single page's content exceeds this height, parts of your resume may be rendered improperly when printed - or exported. + While Reactive Resume supports multi-page resumes, each page has a fixed height based on your chosen format (A4 or + Letter). If a single page's content exceeds this height, parts of your resume may be rendered improperly when printed + or exported. ## Quick fixes @@ -36,8 +36,8 @@ With Free-Form: To switch formats, go to the **Page** section in the right sidebar and change the **Format** to Free-Form. For more details, see [Selecting the right page format](/guides/selecting-page-format). - Since most resumes are viewed digitally today, Free-Form is often the best choice unless you specifically need to - print physical copies. + Since most resumes are viewed digitally today, Free-Form is often the best choice unless you specifically need to + print physical copies. ### 2. Shorten text blocks @@ -58,10 +58,10 @@ Some sections work better in multiple columns, especially lists of short items. In the left sidebar, find the section you want to adjust, click on the section heading (not an item), and change the **Columns** setting. - Screenshot of the columns setting for a section + Screenshot of the columns setting for a section Good candidates for multi-column layouts: @@ -75,8 +75,8 @@ Good candidates for multi-column layouts: | Certifications (if brief) | > 2 columns | - Multi-column layouts work best for sections with short, uniform items. Sections with long descriptions (like - Experience or Projects) usually work better in a single column. + Multi-column layouts work best for sections with short, uniform items. Sections with long descriptions (like + Experience or Projects) usually work better in a single column. ### 4. Move items to another page @@ -92,8 +92,8 @@ Use the **Move to** feature to relocate items: For detailed instructions, see [Moving items between sections](/guides/moving-items-between-sections). - Keep your most recent and relevant experience on page one. Move older positions or less critical sections (like older - projects or volunteer work) to subsequent pages. + Keep your most recent and relevant experience on page one. Move older positions or less critical sections (like older + projects or volunteer work) to subsequent pages. ### 5. Adjust layout and design settings @@ -112,9 +112,9 @@ Open the right sidebar and explore these options: | **Picture size** | Picture (left sidebar) | A smaller photo leaves more room for text | - **Reducing font size is the best option** when you need to fit more content while keeping A4 or Letter format. - Reducing body font from 11pt to 10.5pt (or even 10pt) can free up significant space while remaining readable. The - editor supports 0.1pt increments, so you can fine-tune precisely. + **Reducing font size is the best option** when you need to fit more content while keeping A4 or Letter format. + Reducing body font from 11pt to 10.5pt (or even 10pt) can free up significant space while remaining readable. The + editor supports 0.1pt increments, so you can fine-tune precisely. ### 6. Hide less important sections @@ -141,8 +141,8 @@ Try this order: 5. Fine-tune font size and spacing last - Use the live preview to see changes as you make them. Small adjustments add up—reducing font size by 0.5pt combined - with slightly smaller margins can recover enough space for several lines of content. + Use the live preview to see changes as you make them. Small adjustments add up—reducing font size by 0.5pt combined + with slightly smaller margins can recover enough space for several lines of content. ## Troubleshooting diff --git a/docs/guides/json-resume-schema.mdx b/docs/guides/json-resume-schema.mdx index a09ff37df..04eebde00 100644 --- a/docs/guides/json-resume-schema.mdx +++ b/docs/guides/json-resume-schema.mdx @@ -37,8 +37,8 @@ The schema includes a `version` field to track compatibility: ```json { - "$schema": "https://rxresu.me/schema.json", - "version": "5.0.0" + "$schema": "https://rxresu.me/schema.json", + "version": "5.0.0" } ``` @@ -59,7 +59,7 @@ const validate = ajv.compile(schema); const isValid = validate(resumeData); if (!isValid) { - console.error(validate.errors); + console.error(validate.errors); } ``` @@ -69,11 +69,11 @@ Add the `$schema` property to your resume JSON files to get autocompletion and v ```json { - "$schema": "https://rxresu.me/schema.json", - "version": "5.0.0", - "basics": { - "name": "John Doe" - } + "$schema": "https://rxresu.me/schema.json", + "version": "5.0.0", + "basics": { + "name": "John Doe" + } } ``` @@ -105,16 +105,16 @@ Each section within `sections` follows a consistent structure: ```json { - "title": "Experience", - "columns": 1, - "hidden": false, - "items": [ - { - "id": "uuid", - "hidden": false - // ... section-specific fields - } - ] + "title": "Experience", + "columns": 1, + "hidden": false, + "items": [ + { + "id": "uuid", + "hidden": false + // ... section-specific fields + } + ] } ``` @@ -137,1927 +137,1927 @@ Below is the complete JSON Schema for Reactive Resume. You can also fetch the la ```json /schema.json lines expandable { - "$schema": "https://json-schema.org/draft/2020-12/schema", - "type": "object", - "properties": { - "picture": { - "type": "object", - "properties": { - "hidden": { - "type": "boolean", - "description": "Whether to hide the picture from the resume." - }, - "url": { - "type": "string", - "description": "The URL to the picture to display on the resume. Must be a valid URL with a protocol (http:// or https://)." - }, - "size": { - "type": "number", - "minimum": 32, - "maximum": 512, - "description": "The size of the picture to display on the resume, defined in points (pt)." - }, - "rotation": { - "type": "number", - "minimum": 0, - "maximum": 360, - "description": "The rotation of the picture to display on the resume, defined in degrees (°)." - }, - "aspectRatio": { - "type": "number", - "minimum": 0.5, - "maximum": 2.5, - "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)." - }, - "borderRadius": { - "type": "number", - "minimum": 0, - "maximum": 100, - "description": "The border radius of the picture to display on the resume, defined in points (pt)." - }, - "borderColor": { - "type": "string", - "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)." - }, - "borderWidth": { - "type": "number", - "minimum": 0, - "description": "The width of the border of the picture to display on the resume, defined in points (pt)." - }, - "shadowColor": { - "type": "string", - "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)." - }, - "shadowWidth": { - "type": "number", - "minimum": 0, - "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)." - } - }, - "required": [ - "hidden", - "url", - "size", - "rotation", - "aspectRatio", - "borderRadius", - "borderColor", - "borderWidth", - "shadowColor", - "shadowWidth" - ], - "additionalProperties": false, - "description": "Configuration for photograph displayed on the resume" - }, - "basics": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "The full name of the author of the resume." - }, - "headline": { - "type": "string", - "description": "The headline of the author of the resume." - }, - "email": { - "anyOf": [ - { - "type": "string", - "format": "email", - "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$" - }, - { - "type": "string", - "const": "" - } - ], - "description": "The email address of the author of the resume." - }, - "phone": { - "type": "string", - "description": "The phone number of the author of the resume." - }, - "location": { - "type": "string", - "description": "The location of the author of the resume." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The website of the author of the resume." - }, - "customFields": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the custom field. Usually generated as a UUID." - }, - "icon": { - "type": "string", - "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." - }, - "text": { - "type": "string", - "description": "The text to display for the custom field." - }, - "link": { - "default": "", - "anyOf": [ - { - "type": "string", - "format": "uri" - }, - { - "type": "string", - "const": "" - } - ], - "description": "If the custom field should be a link, the URL to link to." - } - }, - "required": ["id", "icon", "text", "link"], - "additionalProperties": false - }, - "description": "The custom fields to display on the resume." - } - }, - "required": ["name", "headline", "email", "phone", "location", "website", "customFields"], - "additionalProperties": false, - "description": "Basic information about the author, such as name, email, phone, location, and website" - }, - "summary": { - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "The title of the summary of the resume." - }, - "columns": { - "type": "number", - "description": "The number of columns the summary should span across." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the summary from the resume." - }, - "content": { - "type": "string", - "description": "The content of the summary of the resume. This should be a HTML-formatted string." - } - }, - "required": ["title", "columns", "hidden", "content"], - "additionalProperties": false, - "description": "Summary section of the resume, useful for a short bio or introduction" - }, - "sections": { - "type": "object", - "properties": { - "profiles": { - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "The title of the section." - }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the section from the resume." - }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "icon": { - "type": "string", - "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." - }, - "network": { - "type": "string", - "minLength": 1, - "description": "The name of the network or platform." - }, - "username": { - "type": "string", - "description": "The username of the author on the network or platform." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The link to the profile of the author on the network or platform, if any." - } - }, - "required": ["id", "hidden", "icon", "network", "username", "website"], - "additionalProperties": false - }, - "description": "The items to display in the profiles section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the profiles of the author." - }, - "experience": { - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "The title of the section." - }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the section from the resume." - }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "company": { - "type": "string", - "minLength": 1, - "description": "The name of the company or organization." - }, - "position": { - "type": "string", - "description": "The position held at the company or organization." - }, - "location": { - "type": "string", - "description": "The location of the company or organization." - }, - "period": { - "type": "string", - "description": "The period of time the author was employed at the company or organization." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The website of the company or organization, if any." - }, - "description": { - "type": "string", - "description": "The description of the experience. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "company", "position", "location", "period", "website", "description"], - "additionalProperties": false - }, - "description": "The items to display in the experience section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the experience of the author." - }, - "education": { - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "The title of the section." - }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the section from the resume." - }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "school": { - "type": "string", - "minLength": 1, - "description": "The name of the school or institution." - }, - "degree": { - "type": "string", - "description": "The degree or qualification obtained." - }, - "area": { - "type": "string", - "description": "The area of study or specialization." - }, - "grade": { - "type": "string", - "description": "The grade or score achieved." - }, - "location": { - "type": "string", - "description": "The location of the school or institution." - }, - "period": { - "type": "string", - "description": "The period of time the education was obtained over." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The website of the school or institution, if any." - }, - "description": { - "type": "string", - "description": "The description of the education. This should be a HTML-formatted string." - } - }, - "required": [ - "id", - "hidden", - "school", - "degree", - "area", - "grade", - "location", - "period", - "website", - "description" - ], - "additionalProperties": false - }, - "description": "The items to display in the education section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the education of the author." - }, - "projects": { - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "The title of the section." - }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the section from the resume." - }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "name": { - "type": "string", - "minLength": 1, - "description": "The name of the project." - }, - "period": { - "type": "string", - "description": "The period of time the project was worked on." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The link to the project, if any." - }, - "description": { - "type": "string", - "description": "The description of the project. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "name", "period", "website", "description"], - "additionalProperties": false - }, - "description": "The items to display in the projects section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the projects of the author." - }, - "skills": { - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "The title of the section." - }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the section from the resume." - }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "icon": { - "type": "string", - "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." - }, - "name": { - "type": "string", - "minLength": 1, - "description": "The name of the skill." - }, - "proficiency": { - "type": "string", - "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc." - }, - "level": { - "default": 0, - "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.", - "type": "number", - "minimum": 0, - "maximum": 5 - }, - "keywords": { - "default": [], - "description": "The keywords associated with the skill, if any. These are displayed as tags below the name.", - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": ["id", "hidden", "icon", "name", "proficiency", "level", "keywords"], - "additionalProperties": false - }, - "description": "The items to display in the skills section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the skills of the author." - }, - "languages": { - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "The title of the section." - }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the section from the resume." - }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "language": { - "type": "string", - "minLength": 1, - "description": "The name of the language the author knows." - }, - "fluency": { - "type": "string", - "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." - }, - "level": { - "default": 0, - "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.", - "type": "number", - "minimum": 0, - "maximum": 5 - } - }, - "required": ["id", "hidden", "language", "fluency", "level"], - "additionalProperties": false - }, - "description": "The items to display in the languages section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the languages of the author." - }, - "interests": { - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "The title of the section." - }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the section from the resume." - }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "icon": { - "type": "string", - "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." - }, - "name": { - "type": "string", - "minLength": 1, - "description": "The name of the interest/hobby." - }, - "keywords": { - "default": [], - "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name.", - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": ["id", "hidden", "icon", "name", "keywords"], - "additionalProperties": false - }, - "description": "The items to display in the interests section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the interests of the author." - }, - "awards": { - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "The title of the section." - }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the section from the resume." - }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "title": { - "type": "string", - "minLength": 1, - "description": "The title of the award." - }, - "awarder": { - "type": "string", - "description": "The awarder of the award." - }, - "date": { - "type": "string", - "description": "The date when the award was received." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The website of the award, if any." - }, - "description": { - "type": "string", - "description": "The description of the award. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "title", "awarder", "date", "website", "description"], - "additionalProperties": false - }, - "description": "The items to display in the awards section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the awards of the author." - }, - "certifications": { - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "The title of the section." - }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the section from the resume." - }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "title": { - "type": "string", - "minLength": 1, - "description": "The title of the certification." - }, - "issuer": { - "type": "string", - "description": "The issuer of the certification." - }, - "date": { - "type": "string", - "description": "The date when the certification was received." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The website of the certification, if any." - }, - "description": { - "type": "string", - "description": "The description of the certification. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "title", "issuer", "date", "website", "description"], - "additionalProperties": false - }, - "description": "The items to display in the certifications section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the certifications of the author." - }, - "publications": { - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "The title of the section." - }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the section from the resume." - }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "title": { - "type": "string", - "minLength": 1, - "description": "The title of the publication." - }, - "publisher": { - "type": "string", - "description": "The publisher of the publication." - }, - "date": { - "type": "string", - "description": "The date when the publication was published." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The link to the publication, if any." - }, - "description": { - "type": "string", - "description": "The description of the publication. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "title", "publisher", "date", "website", "description"], - "additionalProperties": false - }, - "description": "The items to display in the publications section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the publications of the author." - }, - "volunteer": { - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "The title of the section." - }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the section from the resume." - }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "organization": { - "type": "string", - "minLength": 1, - "description": "The name of the organization or company." - }, - "location": { - "type": "string", - "description": "The location of the organization or company." - }, - "period": { - "type": "string", - "description": "The period of time the author was volunteered at the organization or company." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The link to the organization or company, if any." - }, - "description": { - "type": "string", - "description": "The description of the volunteer experience. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "organization", "location", "period", "website", "description"], - "additionalProperties": false - }, - "description": "The items to display in the volunteer section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the volunteer experience of the author." - }, - "references": { - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "The title of the section." - }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the section from the resume." - }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "name": { - "type": "string", - "minLength": 1, - "description": "The name of the reference, or a note such as 'Available upon request'." - }, - "position": { - "type": "string", - "description": "The position or job title of the reference." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The website or LinkedIn profile of the reference, if any." - }, - "phone": { - "type": "string", - "description": "The phone number of the reference." - }, - "description": { - "type": "string", - "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "name", "position", "website", "phone", "description"], - "additionalProperties": false - }, - "description": "The items to display in the references section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "additionalProperties": false, - "description": "The section to display the references of the author." - } - }, - "required": [ - "profiles", - "experience", - "education", - "projects", - "skills", - "languages", - "interests", - "awards", - "certifications", - "publications", - "volunteer", - "references" - ], - "additionalProperties": false, - "description": "Various sections of the resume, such as experience, education, projects, etc." - }, - "customSections": { - "type": "array", - "items": { - "type": "object", - "properties": { - "title": { - "type": "string", - "description": "The title of the section." - }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the section from the resume." - }, - "id": { - "type": "string", - "description": "The unique identifier for the custom section. Usually generated as a UUID." - }, - "type": { - "type": "string", - "enum": [ - "profiles", - "experience", - "education", - "projects", - "skills", - "languages", - "interests", - "awards", - "certifications", - "publications", - "volunteer", - "references" - ], - "description": "The type of items this custom section contains. Determines which item schema and form fields to use." - }, - "items": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "icon": { - "type": "string", - "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." - }, - "network": { - "type": "string", - "minLength": 1, - "description": "The name of the network or platform." - }, - "username": { - "type": "string", - "description": "The username of the author on the network or platform." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The link to the profile of the author on the network or platform, if any." - } - }, - "required": ["id", "hidden", "icon", "network", "username", "website"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "company": { - "type": "string", - "minLength": 1, - "description": "The name of the company or organization." - }, - "position": { - "type": "string", - "description": "The position held at the company or organization." - }, - "location": { - "type": "string", - "description": "The location of the company or organization." - }, - "period": { - "type": "string", - "description": "The period of time the author was employed at the company or organization." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The website of the company or organization, if any." - }, - "description": { - "type": "string", - "description": "The description of the experience. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "company", "position", "location", "period", "website", "description"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "school": { - "type": "string", - "minLength": 1, - "description": "The name of the school or institution." - }, - "degree": { - "type": "string", - "description": "The degree or qualification obtained." - }, - "area": { - "type": "string", - "description": "The area of study or specialization." - }, - "grade": { - "type": "string", - "description": "The grade or score achieved." - }, - "location": { - "type": "string", - "description": "The location of the school or institution." - }, - "period": { - "type": "string", - "description": "The period of time the education was obtained over." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The website of the school or institution, if any." - }, - "description": { - "type": "string", - "description": "The description of the education. This should be a HTML-formatted string." - } - }, - "required": [ - "id", - "hidden", - "school", - "degree", - "area", - "grade", - "location", - "period", - "website", - "description" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "name": { - "type": "string", - "minLength": 1, - "description": "The name of the project." - }, - "period": { - "type": "string", - "description": "The period of time the project was worked on." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The link to the project, if any." - }, - "description": { - "type": "string", - "description": "The description of the project. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "name", "period", "website", "description"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "icon": { - "type": "string", - "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." - }, - "name": { - "type": "string", - "minLength": 1, - "description": "The name of the skill." - }, - "proficiency": { - "type": "string", - "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc." - }, - "level": { - "default": 0, - "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.", - "type": "number", - "minimum": 0, - "maximum": 5 - }, - "keywords": { - "default": [], - "description": "The keywords associated with the skill, if any. These are displayed as tags below the name.", - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": ["id", "hidden", "icon", "name", "proficiency", "level", "keywords"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "language": { - "type": "string", - "minLength": 1, - "description": "The name of the language the author knows." - }, - "fluency": { - "type": "string", - "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." - }, - "level": { - "default": 0, - "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.", - "type": "number", - "minimum": 0, - "maximum": 5 - } - }, - "required": ["id", "hidden", "language", "fluency", "level"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "icon": { - "type": "string", - "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." - }, - "name": { - "type": "string", - "minLength": 1, - "description": "The name of the interest/hobby." - }, - "keywords": { - "default": [], - "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name.", - "type": "array", - "items": { - "type": "string" - } - } - }, - "required": ["id", "hidden", "icon", "name", "keywords"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "title": { - "type": "string", - "minLength": 1, - "description": "The title of the award." - }, - "awarder": { - "type": "string", - "description": "The awarder of the award." - }, - "date": { - "type": "string", - "description": "The date when the award was received." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The website of the award, if any." - }, - "description": { - "type": "string", - "description": "The description of the award. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "title", "awarder", "date", "website", "description"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "title": { - "type": "string", - "minLength": 1, - "description": "The title of the certification." - }, - "issuer": { - "type": "string", - "description": "The issuer of the certification." - }, - "date": { - "type": "string", - "description": "The date when the certification was received." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The website of the certification, if any." - }, - "description": { - "type": "string", - "description": "The description of the certification. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "title", "issuer", "date", "website", "description"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "title": { - "type": "string", - "minLength": 1, - "description": "The title of the publication." - }, - "publisher": { - "type": "string", - "description": "The publisher of the publication." - }, - "date": { - "type": "string", - "description": "The date when the publication was published." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The link to the publication, if any." - }, - "description": { - "type": "string", - "description": "The description of the publication. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "title", "publisher", "date", "website", "description"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "organization": { - "type": "string", - "minLength": 1, - "description": "The name of the organization or company." - }, - "location": { - "type": "string", - "description": "The location of the organization or company." - }, - "period": { - "type": "string", - "description": "The period of time the author was volunteered at the organization or company." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The link to the organization or company, if any." - }, - "description": { - "type": "string", - "description": "The description of the volunteer experience. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "organization", "location", "period", "website", "description"], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { - "type": "boolean", - "description": "Whether to hide the item from the resume." - }, - "name": { - "type": "string", - "minLength": 1, - "description": "The name of the reference, or a note such as 'Available upon request'." - }, - "position": { - "type": "string", - "description": "The position or job title of the reference." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "additionalProperties": false, - "description": "The website or LinkedIn profile of the reference, if any." - }, - "phone": { - "type": "string", - "description": "The phone number of the reference." - }, - "description": { - "type": "string", - "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "name", "position", "website", "phone", "description"], - "additionalProperties": false - } - ] - }, - "description": "The items to display in the custom section. Items follow the schema of the section type." - } - }, - "required": ["title", "columns", "hidden", "id", "type", "items"], - "additionalProperties": false - }, - "description": "Custom sections of the resume, such as a custom section for notes, etc." - }, - "metadata": { - "type": "object", - "properties": { - "template": { - "default": "onyx", - "description": "The template to use for the resume. Determines the overall design and appearance of the resume.", - "type": "string", - "enum": [ - "azurill", - "bronzor", - "chikorita", - "ditto", - "ditgar", - "gengar", - "glalie", - "kakuna", - "lapras", - "leafish", - "onyx", - "pikachu", - "rhyhorn" - ] - }, - "layout": { - "type": "object", - "properties": { - "sidebarWidth": { - "default": 35, - "description": "The width of the sidebar column, defined as a percentage of the page width.", - "type": "number", - "minimum": 10, - "maximum": 50 - }, - "pages": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fullWidth": { - "type": "boolean", - "description": "Whether the layout of the page should be full width. If true, the main column will span the entire width of the page. This means that there should be no items in the sidebar column." - }, - "main": { - "type": "array", - "items": { - "type": "string" - }, - "description": "The items to display in the main column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." - }, - "sidebar": { - "type": "array", - "items": { - "type": "string" - }, - "description": "The items to display in the sidebar column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." - } - }, - "required": ["fullWidth", "main", "sidebar"], - "additionalProperties": false - }, - "description": "The pages to display in the layout." - } - }, - "required": ["sidebarWidth", "pages"], - "additionalProperties": false, - "description": "The layout of the resume. Determines the structure and arrangement of the sections on the resume." - }, - "css": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean", - "description": "Whether to enable custom CSS for the resume." - }, - "value": { - "type": "string", - "description": "The custom CSS to apply to the resume. This should be a valid CSS string." - } - }, - "required": ["enabled", "value"], - "additionalProperties": false, - "description": "Custom CSS to apply to the resume. Can be used to override the default styles of the template." - }, - "page": { - "type": "object", - "properties": { - "gapX": { - "type": "number", - "minimum": 0, - "description": "The horizontal gap between the sections of the page, defined in points (pt)." - }, - "gapY": { - "type": "number", - "minimum": 0, - "description": "The vertical gap between the sections of the page, defined in points (pt)." - }, - "marginX": { - "type": "number", - "minimum": 0, - "description": "The horizontal margin of the page, defined in points (pt)." - }, - "marginY": { - "type": "number", - "minimum": 0, - "description": "The vertical margin of the page, defined in points (pt)." - }, - "format": { - "type": "string", - "enum": ["a4", "letter", "free-form"], - "description": "The format of the page. Can be 'a4', 'letter', or 'free-form'." - }, - "locale": { - "default": "en-US", - "type": "string", - "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden." - }, - "hideIcons": { - "default": false, - "type": "boolean", - "description": "Whether to hide the icons of the sections." - } - }, - "required": ["gapX", "gapY", "marginX", "marginY", "format", "locale", "hideIcons"], - "additionalProperties": false, - "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." - }, - "design": { - "type": "object", - "properties": { - "level": { - "type": "object", - "properties": { - "icon": { - "type": "string", - "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." - }, - "type": { - "type": "string", - "enum": ["hidden", "circle", "square", "rectangle", "rectangle-full", "progress-bar", "icon"], - "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field." - } - }, - "required": ["icon", "type"], - "additionalProperties": false - }, - "colors": { - "type": "object", - "properties": { - "primary": { - "type": "string", - "description": "The primary color of the design, defined as rgba(r, g, b, a)." - }, - "text": { - "type": "string", - "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)." - }, - "background": { - "type": "string", - "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)." - } - }, - "required": ["primary", "text", "background"], - "additionalProperties": false - } - }, - "required": ["level", "colors"], - "additionalProperties": false, - "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume." - }, - "typography": { - "type": "object", - "properties": { - "body": { - "type": "object", - "properties": { - "fontFamily": { - "type": "string", - "description": "The family of the font to use. Must be a font that is available on Google Fonts." - }, - "fontWeights": { - "default": ["400"], - "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font.", - "type": "array", - "items": { - "type": "string", - "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] - } - }, - "fontSize": { - "default": 11, - "description": "The size of the font to use, defined in points (pt).", - "type": "number", - "minimum": 6, - "maximum": 24 - }, - "lineHeight": { - "default": 1.5, - "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x).", - "type": "number", - "minimum": 0.5, - "maximum": 4 - } - }, - "required": ["fontFamily", "fontWeights", "fontSize", "lineHeight"], - "additionalProperties": false, - "description": "The typography for the body of the resume." - }, - "heading": { - "type": "object", - "properties": { - "fontFamily": { - "type": "string", - "description": "The family of the font to use. Must be a font that is available on Google Fonts." - }, - "fontWeights": { - "default": ["400"], - "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font.", - "type": "array", - "items": { - "type": "string", - "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] - } - }, - "fontSize": { - "default": 11, - "description": "The size of the font to use, defined in points (pt).", - "type": "number", - "minimum": 6, - "maximum": 24 - }, - "lineHeight": { - "default": 1.5, - "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x).", - "type": "number", - "minimum": 0.5, - "maximum": 4 - } - }, - "required": ["fontFamily", "fontWeights", "fontSize", "lineHeight"], - "additionalProperties": false, - "description": "The typography for the headings of the resume." - } - }, - "required": ["body", "heading"], - "additionalProperties": false, - "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume." - }, - "notes": { - "type": "string", - "description": "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." - } - }, - "required": ["template", "layout", "css", "page", "design", "typography", "notes"], - "additionalProperties": false, - "description": "Metadata for the resume, such as template, layout, typography, etc. This section describes the overall design and appearance of the resume." - } - }, - "required": ["picture", "basics", "summary", "sections", "customSections", "metadata"], - "additionalProperties": false + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "picture": { + "type": "object", + "properties": { + "hidden": { + "type": "boolean", + "description": "Whether to hide the picture from the resume." + }, + "url": { + "type": "string", + "description": "The URL to the picture to display on the resume. Must be a valid URL with a protocol (http:// or https://)." + }, + "size": { + "type": "number", + "minimum": 32, + "maximum": 512, + "description": "The size of the picture to display on the resume, defined in points (pt)." + }, + "rotation": { + "type": "number", + "minimum": 0, + "maximum": 360, + "description": "The rotation of the picture to display on the resume, defined in degrees (°)." + }, + "aspectRatio": { + "type": "number", + "minimum": 0.5, + "maximum": 2.5, + "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)." + }, + "borderRadius": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The border radius of the picture to display on the resume, defined in points (pt)." + }, + "borderColor": { + "type": "string", + "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "borderWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the border of the picture to display on the resume, defined in points (pt)." + }, + "shadowColor": { + "type": "string", + "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "shadowWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)." + } + }, + "required": [ + "hidden", + "url", + "size", + "rotation", + "aspectRatio", + "borderRadius", + "borderColor", + "borderWidth", + "shadowColor", + "shadowWidth" + ], + "additionalProperties": false, + "description": "Configuration for photograph displayed on the resume" + }, + "basics": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The full name of the author of the resume." + }, + "headline": { + "type": "string", + "description": "The headline of the author of the resume." + }, + "email": { + "anyOf": [ + { + "type": "string", + "format": "email", + "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$" + }, + { + "type": "string", + "const": "" + } + ], + "description": "The email address of the author of the resume." + }, + "phone": { + "type": "string", + "description": "The phone number of the author of the resume." + }, + "location": { + "type": "string", + "description": "The location of the author of the resume." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The website of the author of the resume." + }, + "customFields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the custom field. Usually generated as a UUID." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "text": { + "type": "string", + "description": "The text to display for the custom field." + }, + "link": { + "default": "", + "anyOf": [ + { + "type": "string", + "format": "uri" + }, + { + "type": "string", + "const": "" + } + ], + "description": "If the custom field should be a link, the URL to link to." + } + }, + "required": ["id", "icon", "text", "link"], + "additionalProperties": false + }, + "description": "The custom fields to display on the resume." + } + }, + "required": ["name", "headline", "email", "phone", "location", "website", "customFields"], + "additionalProperties": false, + "description": "Basic information about the author, such as name, email, phone, location, and website" + }, + "summary": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the summary of the resume." + }, + "columns": { + "type": "number", + "description": "The number of columns the summary should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the summary from the resume." + }, + "content": { + "type": "string", + "description": "The content of the summary of the resume. This should be a HTML-formatted string." + } + }, + "required": ["title", "columns", "hidden", "content"], + "additionalProperties": false, + "description": "Summary section of the resume, useful for a short bio or introduction" + }, + "sections": { + "type": "object", + "properties": { + "profiles": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "network": { + "type": "string", + "minLength": 1, + "description": "The name of the network or platform." + }, + "username": { + "type": "string", + "description": "The username of the author on the network or platform." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The link to the profile of the author on the network or platform, if any." + } + }, + "required": ["id", "hidden", "icon", "network", "username", "website"], + "additionalProperties": false + }, + "description": "The items to display in the profiles section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the profiles of the author." + }, + "experience": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "company": { + "type": "string", + "minLength": 1, + "description": "The name of the company or organization." + }, + "position": { + "type": "string", + "description": "The position held at the company or organization." + }, + "location": { + "type": "string", + "description": "The location of the company or organization." + }, + "period": { + "type": "string", + "description": "The period of time the author was employed at the company or organization." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The website of the company or organization, if any." + }, + "description": { + "type": "string", + "description": "The description of the experience. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "company", "position", "location", "period", "website", "description"], + "additionalProperties": false + }, + "description": "The items to display in the experience section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the experience of the author." + }, + "education": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "school": { + "type": "string", + "minLength": 1, + "description": "The name of the school or institution." + }, + "degree": { + "type": "string", + "description": "The degree or qualification obtained." + }, + "area": { + "type": "string", + "description": "The area of study or specialization." + }, + "grade": { + "type": "string", + "description": "The grade or score achieved." + }, + "location": { + "type": "string", + "description": "The location of the school or institution." + }, + "period": { + "type": "string", + "description": "The period of time the education was obtained over." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The website of the school or institution, if any." + }, + "description": { + "type": "string", + "description": "The description of the education. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "school", + "degree", + "area", + "grade", + "location", + "period", + "website", + "description" + ], + "additionalProperties": false + }, + "description": "The items to display in the education section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the education of the author." + }, + "projects": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the project." + }, + "period": { + "type": "string", + "description": "The period of time the project was worked on." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The link to the project, if any." + }, + "description": { + "type": "string", + "description": "The description of the project. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "name", "period", "website", "description"], + "additionalProperties": false + }, + "description": "The items to display in the projects section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the projects of the author." + }, + "skills": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the skill." + }, + "proficiency": { + "type": "string", + "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc." + }, + "level": { + "default": 0, + "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.", + "type": "number", + "minimum": 0, + "maximum": 5 + }, + "keywords": { + "default": [], + "description": "The keywords associated with the skill, if any. These are displayed as tags below the name.", + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["id", "hidden", "icon", "name", "proficiency", "level", "keywords"], + "additionalProperties": false + }, + "description": "The items to display in the skills section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the skills of the author." + }, + "languages": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "language": { + "type": "string", + "minLength": 1, + "description": "The name of the language the author knows." + }, + "fluency": { + "type": "string", + "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." + }, + "level": { + "default": 0, + "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.", + "type": "number", + "minimum": 0, + "maximum": 5 + } + }, + "required": ["id", "hidden", "language", "fluency", "level"], + "additionalProperties": false + }, + "description": "The items to display in the languages section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the languages of the author." + }, + "interests": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the interest/hobby." + }, + "keywords": { + "default": [], + "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name.", + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["id", "hidden", "icon", "name", "keywords"], + "additionalProperties": false + }, + "description": "The items to display in the interests section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the interests of the author." + }, + "awards": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "minLength": 1, + "description": "The title of the award." + }, + "awarder": { + "type": "string", + "description": "The awarder of the award." + }, + "date": { + "type": "string", + "description": "The date when the award was received." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The website of the award, if any." + }, + "description": { + "type": "string", + "description": "The description of the award. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "title", "awarder", "date", "website", "description"], + "additionalProperties": false + }, + "description": "The items to display in the awards section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the awards of the author." + }, + "certifications": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "minLength": 1, + "description": "The title of the certification." + }, + "issuer": { + "type": "string", + "description": "The issuer of the certification." + }, + "date": { + "type": "string", + "description": "The date when the certification was received." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The website of the certification, if any." + }, + "description": { + "type": "string", + "description": "The description of the certification. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "title", "issuer", "date", "website", "description"], + "additionalProperties": false + }, + "description": "The items to display in the certifications section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the certifications of the author." + }, + "publications": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "minLength": 1, + "description": "The title of the publication." + }, + "publisher": { + "type": "string", + "description": "The publisher of the publication." + }, + "date": { + "type": "string", + "description": "The date when the publication was published." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The link to the publication, if any." + }, + "description": { + "type": "string", + "description": "The description of the publication. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "title", "publisher", "date", "website", "description"], + "additionalProperties": false + }, + "description": "The items to display in the publications section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the publications of the author." + }, + "volunteer": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "organization": { + "type": "string", + "minLength": 1, + "description": "The name of the organization or company." + }, + "location": { + "type": "string", + "description": "The location of the organization or company." + }, + "period": { + "type": "string", + "description": "The period of time the author was volunteered at the organization or company." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The link to the organization or company, if any." + }, + "description": { + "type": "string", + "description": "The description of the volunteer experience. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "organization", "location", "period", "website", "description"], + "additionalProperties": false + }, + "description": "The items to display in the volunteer section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the volunteer experience of the author." + }, + "references": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the reference, or a note such as 'Available upon request'." + }, + "position": { + "type": "string", + "description": "The position or job title of the reference." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The website or LinkedIn profile of the reference, if any." + }, + "phone": { + "type": "string", + "description": "The phone number of the reference." + }, + "description": { + "type": "string", + "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "name", "position", "website", "phone", "description"], + "additionalProperties": false + }, + "description": "The items to display in the references section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "additionalProperties": false, + "description": "The section to display the references of the author." + } + }, + "required": [ + "profiles", + "experience", + "education", + "projects", + "skills", + "languages", + "interests", + "awards", + "certifications", + "publications", + "volunteer", + "references" + ], + "additionalProperties": false, + "description": "Various sections of the resume, such as experience, education, projects, etc." + }, + "customSections": { + "type": "array", + "items": { + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "The title of the section." + }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "type": "string", + "enum": [ + "profiles", + "experience", + "education", + "projects", + "skills", + "languages", + "interests", + "awards", + "certifications", + "publications", + "volunteer", + "references" + ], + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "network": { + "type": "string", + "minLength": 1, + "description": "The name of the network or platform." + }, + "username": { + "type": "string", + "description": "The username of the author on the network or platform." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The link to the profile of the author on the network or platform, if any." + } + }, + "required": ["id", "hidden", "icon", "network", "username", "website"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "company": { + "type": "string", + "minLength": 1, + "description": "The name of the company or organization." + }, + "position": { + "type": "string", + "description": "The position held at the company or organization." + }, + "location": { + "type": "string", + "description": "The location of the company or organization." + }, + "period": { + "type": "string", + "description": "The period of time the author was employed at the company or organization." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The website of the company or organization, if any." + }, + "description": { + "type": "string", + "description": "The description of the experience. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "company", "position", "location", "period", "website", "description"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "school": { + "type": "string", + "minLength": 1, + "description": "The name of the school or institution." + }, + "degree": { + "type": "string", + "description": "The degree or qualification obtained." + }, + "area": { + "type": "string", + "description": "The area of study or specialization." + }, + "grade": { + "type": "string", + "description": "The grade or score achieved." + }, + "location": { + "type": "string", + "description": "The location of the school or institution." + }, + "period": { + "type": "string", + "description": "The period of time the education was obtained over." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The website of the school or institution, if any." + }, + "description": { + "type": "string", + "description": "The description of the education. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "school", + "degree", + "area", + "grade", + "location", + "period", + "website", + "description" + ], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the project." + }, + "period": { + "type": "string", + "description": "The period of time the project was worked on." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The link to the project, if any." + }, + "description": { + "type": "string", + "description": "The description of the project. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "name", "period", "website", "description"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the skill." + }, + "proficiency": { + "type": "string", + "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc." + }, + "level": { + "default": 0, + "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.", + "type": "number", + "minimum": 0, + "maximum": 5 + }, + "keywords": { + "default": [], + "description": "The keywords associated with the skill, if any. These are displayed as tags below the name.", + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["id", "hidden", "icon", "name", "proficiency", "level", "keywords"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "language": { + "type": "string", + "minLength": 1, + "description": "The name of the language the author knows." + }, + "fluency": { + "type": "string", + "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." + }, + "level": { + "default": 0, + "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden.", + "type": "number", + "minimum": 0, + "maximum": 5 + } + }, + "required": ["id", "hidden", "language", "fluency", "level"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the interest/hobby." + }, + "keywords": { + "default": [], + "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name.", + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": ["id", "hidden", "icon", "name", "keywords"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "minLength": 1, + "description": "The title of the award." + }, + "awarder": { + "type": "string", + "description": "The awarder of the award." + }, + "date": { + "type": "string", + "description": "The date when the award was received." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The website of the award, if any." + }, + "description": { + "type": "string", + "description": "The description of the award. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "title", "awarder", "date", "website", "description"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "minLength": 1, + "description": "The title of the certification." + }, + "issuer": { + "type": "string", + "description": "The issuer of the certification." + }, + "date": { + "type": "string", + "description": "The date when the certification was received." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The website of the certification, if any." + }, + "description": { + "type": "string", + "description": "The description of the certification. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "title", "issuer", "date", "website", "description"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "minLength": 1, + "description": "The title of the publication." + }, + "publisher": { + "type": "string", + "description": "The publisher of the publication." + }, + "date": { + "type": "string", + "description": "The date when the publication was published." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The link to the publication, if any." + }, + "description": { + "type": "string", + "description": "The description of the publication. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "title", "publisher", "date", "website", "description"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "organization": { + "type": "string", + "minLength": 1, + "description": "The name of the organization or company." + }, + "location": { + "type": "string", + "description": "The location of the organization or company." + }, + "period": { + "type": "string", + "description": "The period of time the author was volunteered at the organization or company." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The link to the organization or company, if any." + }, + "description": { + "type": "string", + "description": "The description of the volunteer experience. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "organization", "location", "period", "website", "description"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the reference, or a note such as 'Available upon request'." + }, + "position": { + "type": "string", + "description": "The position or job title of the reference." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "additionalProperties": false, + "description": "The website or LinkedIn profile of the reference, if any." + }, + "phone": { + "type": "string", + "description": "The phone number of the reference." + }, + "description": { + "type": "string", + "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "name", "position", "website", "phone", "description"], + "additionalProperties": false + } + ] + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": ["title", "columns", "hidden", "id", "type", "items"], + "additionalProperties": false + }, + "description": "Custom sections of the resume, such as a custom section for notes, etc." + }, + "metadata": { + "type": "object", + "properties": { + "template": { + "default": "onyx", + "description": "The template to use for the resume. Determines the overall design and appearance of the resume.", + "type": "string", + "enum": [ + "azurill", + "bronzor", + "chikorita", + "ditto", + "ditgar", + "gengar", + "glalie", + "kakuna", + "lapras", + "leafish", + "onyx", + "pikachu", + "rhyhorn" + ] + }, + "layout": { + "type": "object", + "properties": { + "sidebarWidth": { + "default": 35, + "description": "The width of the sidebar column, defined as a percentage of the page width.", + "type": "number", + "minimum": 10, + "maximum": 50 + }, + "pages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fullWidth": { + "type": "boolean", + "description": "Whether the layout of the page should be full width. If true, the main column will span the entire width of the page. This means that there should be no items in the sidebar column." + }, + "main": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The items to display in the main column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." + }, + "sidebar": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The items to display in the sidebar column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." + } + }, + "required": ["fullWidth", "main", "sidebar"], + "additionalProperties": false + }, + "description": "The pages to display in the layout." + } + }, + "required": ["sidebarWidth", "pages"], + "additionalProperties": false, + "description": "The layout of the resume. Determines the structure and arrangement of the sections on the resume." + }, + "css": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Whether to enable custom CSS for the resume." + }, + "value": { + "type": "string", + "description": "The custom CSS to apply to the resume. This should be a valid CSS string." + } + }, + "required": ["enabled", "value"], + "additionalProperties": false, + "description": "Custom CSS to apply to the resume. Can be used to override the default styles of the template." + }, + "page": { + "type": "object", + "properties": { + "gapX": { + "type": "number", + "minimum": 0, + "description": "The horizontal gap between the sections of the page, defined in points (pt)." + }, + "gapY": { + "type": "number", + "minimum": 0, + "description": "The vertical gap between the sections of the page, defined in points (pt)." + }, + "marginX": { + "type": "number", + "minimum": 0, + "description": "The horizontal margin of the page, defined in points (pt)." + }, + "marginY": { + "type": "number", + "minimum": 0, + "description": "The vertical margin of the page, defined in points (pt)." + }, + "format": { + "type": "string", + "enum": ["a4", "letter", "free-form"], + "description": "The format of the page. Can be 'a4', 'letter', or 'free-form'." + }, + "locale": { + "default": "en-US", + "type": "string", + "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden." + }, + "hideIcons": { + "default": false, + "type": "boolean", + "description": "Whether to hide the icons of the sections." + } + }, + "required": ["gapX", "gapY", "marginX", "marginY", "format", "locale", "hideIcons"], + "additionalProperties": false, + "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." + }, + "design": { + "type": "object", + "properties": { + "level": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "type": { + "type": "string", + "enum": ["hidden", "circle", "square", "rectangle", "rectangle-full", "progress-bar", "icon"], + "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field." + } + }, + "required": ["icon", "type"], + "additionalProperties": false + }, + "colors": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "description": "The primary color of the design, defined as rgba(r, g, b, a)." + }, + "text": { + "type": "string", + "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)." + }, + "background": { + "type": "string", + "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)." + } + }, + "required": ["primary", "text", "background"], + "additionalProperties": false + } + }, + "required": ["level", "colors"], + "additionalProperties": false, + "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume." + }, + "typography": { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a font that is available on Google Fonts." + }, + "fontWeights": { + "default": ["400"], + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font.", + "type": "array", + "items": { + "type": "string", + "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] + } + }, + "fontSize": { + "default": 11, + "description": "The size of the font to use, defined in points (pt).", + "type": "number", + "minimum": 6, + "maximum": 24 + }, + "lineHeight": { + "default": 1.5, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x).", + "type": "number", + "minimum": 0.5, + "maximum": 4 + } + }, + "required": ["fontFamily", "fontWeights", "fontSize", "lineHeight"], + "additionalProperties": false, + "description": "The typography for the body of the resume." + }, + "heading": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a font that is available on Google Fonts." + }, + "fontWeights": { + "default": ["400"], + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font.", + "type": "array", + "items": { + "type": "string", + "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] + } + }, + "fontSize": { + "default": 11, + "description": "The size of the font to use, defined in points (pt).", + "type": "number", + "minimum": 6, + "maximum": 24 + }, + "lineHeight": { + "default": 1.5, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x).", + "type": "number", + "minimum": 0.5, + "maximum": 4 + } + }, + "required": ["fontFamily", "fontWeights", "fontSize", "lineHeight"], + "additionalProperties": false, + "description": "The typography for the headings of the resume." + } + }, + "required": ["body", "heading"], + "additionalProperties": false, + "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume." + }, + "notes": { + "type": "string", + "description": "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." + } + }, + "required": ["template", "layout", "css", "page", "design", "typography", "notes"], + "additionalProperties": false, + "description": "Metadata for the resume, such as template, layout, typography, etc. This section describes the overall design and appearance of the resume." + } + }, + "required": ["picture", "basics", "summary", "sections", "customSections", "metadata"], + "additionalProperties": false } ``` diff --git a/docs/guides/linking-social-accounts.mdx b/docs/guides/linking-social-accounts.mdx index 77f5480f7..b869131bd 100644 --- a/docs/guides/linking-social-accounts.mdx +++ b/docs/guides/linking-social-accounts.mdx @@ -9,13 +9,13 @@ description: "Learn how to connect or disconnect social sign-in providers like G - Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section, - click on the Authentication link. + Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section, + click on the Authentication link. - On the Authentication page, you may see sections for one or more providers (for example, Google or{" "} - GitHub), depending on what is enabled on your instance. + On the Authentication page, you may see sections for one or more providers (for example, Google or{" "} + GitHub), depending on what is enabled on your instance. diff --git a/docs/guides/moving-items-between-sections.mdx b/docs/guides/moving-items-between-sections.mdx index 444acb7e8..84cd4c37c 100644 --- a/docs/guides/moving-items-between-sections.mdx +++ b/docs/guides/moving-items-between-sections.mdx @@ -42,8 +42,8 @@ If you have a long work history or extensive project list, you may want to split - In the dropdown menu, hover over or click the Move to option. This will open a submenu showing all - available destinations. + In the dropdown menu, hover over or click the Move to option. This will open a submenu showing all + available destinations. @@ -115,18 +115,18 @@ A common use case is when your work history is too long to fit on a single page. ## Tips for organizing multi-page resumes - **Keep it logical**: Maintain chronological order within each page. Move complete job entries rather than splitting a - single role across pages. + **Keep it logical**: Maintain chronological order within each page. Move complete job entries rather than splitting a + single role across pages. - **Use custom section names**: After moving items to a new custom section, you can rename it (e.g., "Earlier - Experience" or "Additional Projects") to provide context for recruiters. + **Use custom section names**: After moving items to a new custom section, you can rename it (e.g., "Earlier + Experience" or "Additional Projects") to provide context for recruiters. - Moving items reorganizes your content but doesn't affect the data itself. You can always move items back or to - different sections as needed. + Moving items reorganizes your content but doesn't affect the data itself. You can always move items back or to + different sections as needed. ## Troubleshooting diff --git a/docs/guides/selecting-page-format.mdx b/docs/guides/selecting-page-format.mdx index 2dc92efc5..acddba2e0 100644 --- a/docs/guides/selecting-page-format.mdx +++ b/docs/guides/selecting-page-format.mdx @@ -39,8 +39,8 @@ Free-Form is a modern format designed for digital-first resumes. Instead of conf | Height | Unlimited | - With Free-Form, there are no page breaks. Your entire resume renders as one seamless document, regardless of how much - content you have. + With Free-Form, there are no page breaks. Your entire resume renders as one seamless document, regardless of how much + content you have. ## Why Free-Form exists @@ -57,8 +57,8 @@ When your resume is processed digitally: Since physical page constraints no longer matter for most use cases, Free-Form lets you focus on content rather than worrying about fitting everything into arbitrary page limits. - If you don't plan on printing your resume, Free-Form is often the simplest choice. You never have to worry about - content overflowing or awkward page breaks. + If you don't plan on printing your resume, Free-Form is often the simplest choice. You never have to worry about + content overflowing or awkward page breaks. ## How to change your page format @@ -69,11 +69,11 @@ Since physical page constraints no longer matter for most use cases, Free-Form l - In the resume builder, look for the right sidebar on the right side of the screen. + In the resume builder, look for the right sidebar on the right side of the screen. - In the right sidebar, find and click on the **Page** section to expand it. + In the right sidebar, find and click on the **Page** section to expand it. @@ -105,8 +105,8 @@ Use this quick reference to decide which format fits your needs: | Traditional industries (law, finance) | A4 or Letter | - If you switch from Free-Form to A4 or Letter, your content will be split across multiple pages. Review the result - carefully to ensure page breaks don't occur in awkward places. + If you switch from Free-Form to A4 or Letter, your content will be split across multiple pages. Review the result + carefully to ensure page breaks don't occur in awkward places. ## Example PDFs @@ -114,24 +114,24 @@ Use this quick reference to decide which format fits your needs: Download these sample resumes to see how different formats affect the final PDF output: - - A sample resume in A4 format showing traditional page breaks and constraints. - - - A sample resume in Free-Form format showing a continuous single-page layout. - + + A sample resume in A4 format showing traditional page breaks and constraints. + + + A sample resume in Free-Form format showing a continuous single-page layout. + ## Frequently asked questions @@ -142,18 +142,18 @@ Download these sample resumes to see how different formats affect the final PDF - Absolutely. You can change the format at any time from the Page section in the right sidebar. Your content is - preserved—only the layout changes. + Absolutely. You can change the format at any time from the Page section in the right sidebar. Your content is + preserved—only the layout changes. - If the employer specifically requests a one-page resume and expects a traditional format, use A4 or Letter and ensure - your content fits within a single page. See [Fitting content on a page](/guides/fitting-content-on-a-page) for tips. + If the employer specifically requests a one-page resume and expects a traditional format, use A4 or Letter and ensure + your content fits within a single page. See [Fitting content on a page](/guides/fitting-content-on-a-page) for tips. - Slightly. A longer single page may produce a marginally larger PDF than a paginated version of the same content, but - the difference is negligible for typical resume lengths. + Slightly. A longer single page may produce a marginally larger PDF than a paginated version of the same content, but + the difference is negligible for typical resume lengths. diff --git a/docs/guides/setting-up-passkeys.mdx b/docs/guides/setting-up-passkeys.mdx index 9288305be..11d761d5f 100644 --- a/docs/guides/setting-up-passkeys.mdx +++ b/docs/guides/setting-up-passkeys.mdx @@ -18,17 +18,17 @@ For more details and technical updates, see the - Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section, - click on the Authentication link. + Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section, + click on the Authentication link. - In the Passkeys section, click on the Register New Device button. + In the Passkeys section, click on the Register New Device button. - You'll be prompted to enter a name for the passkey. Use something descriptive so you can recognize it later (for - example, “MacBook Touch ID” or “iPhone Face ID”). + You'll be prompted to enter a name for the passkey. Use something descriptive so you can recognize it later (for + example, “MacBook Touch ID” or “iPhone Face ID”). diff --git a/docs/guides/setting-up-two-factor-authentication.mdx b/docs/guides/setting-up-two-factor-authentication.mdx index 640a30ec1..c3a560109 100644 --- a/docs/guides/setting-up-two-factor-authentication.mdx +++ b/docs/guides/setting-up-two-factor-authentication.mdx @@ -23,13 +23,13 @@ description: "Learn how to enable two-factor authentication (2FA) to add an extr - Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section, - click on the Authentication link. + Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section, + click on the Authentication link. - On the Authentication page, find the Two-Factor Authentication section and click on the{" "} - Enable 2FA button. + On the Authentication page, find the Two-Factor Authentication section and click on the{" "} + Enable 2FA button. diff --git a/docs/guides/sharing-your-resume-publicly.mdx b/docs/guides/sharing-your-resume-publicly.mdx index 3a3c6c3d8..ad790a205 100644 --- a/docs/guides/sharing-your-resume-publicly.mdx +++ b/docs/guides/sharing-your-resume-publicly.mdx @@ -8,18 +8,18 @@ Reactive Resume lets you share your resume via a **public URL** that anyone can ## Key benefits of public sharing - - Viewers always see the latest version of your resume. No need to send new files when you make updates. - - - See how many times your resume has been viewed and downloaded. - - - Optionally require a password so only people you trust can access your resume. - - - A simple URL that works anywhere—email signatures, LinkedIn, portfolios, job applications. - + + Viewers always see the latest version of your resume. No need to send new files when you make updates. + + + See how many times your resume has been viewed and downloaded. + + + Optionally require a password so only people you trust can access your resume. + + + A simple URL that works anywhere—email signatures, LinkedIn, portfolios, job applications. + ## How to enable public sharing @@ -32,7 +32,7 @@ Reactive Resume lets you share your resume via a **public URL** that anyone can In the **right sidebar**, select **Sharing**. - Turn on the **Allow Public Access** switch. Once enabled, your resume will be accessible via its public URL. + Turn on the **Allow Public Access** switch. Once enabled, your resume will be accessible via its public URL. @@ -48,8 +48,8 @@ Reactive Resume lets you share your resume via a **public URL** that anyone can - The `{slug}` is the unique, URL-safe name you assign to your resume when you create it. If you want to change your - slug, go to the dashboard, right-click your resume card, and choose "Update" to edit its details. + The `{slug}` is the unique, URL-safe name you assign to your resume when you create it. If you want to change your + slug, go to the dashboard, right-click your resume card, and choose "Update" to edit its details. ## How the public URL works @@ -62,8 +62,8 @@ When someone visits your public resume URL: 4. **Views are tracked** - Each visit is counted in your resume statistics (see below) - Changes you make in the builder are reflected immediately on the public URL. There's no separate "publish" step—your - public resume is always in sync. + Changes you make in the builder are reflected immediately on the public URL. There's no separate "publish" step—your + public resume is always in sync. ## Tracking views and downloads @@ -84,8 +84,8 @@ You'll see: | **Last downloaded** | The date when your resume was last downloaded | - Statistics are only tracked when your resume is **public**. If you turn off public access, tracking pauses but your - existing stats are preserved. + Statistics are only tracked when your resume is **public**. If you turn off public access, tracking pauses but your + existing stats are preserved. ### What counts as a view? @@ -97,8 +97,8 @@ A view is counted each time someone loads your public resume page. This includes - Search engine visits (if your resume is indexed) - Only **you** can see your resume's statistics. Visitors to your public URL cannot see how many views or downloads your - resume has. + Only **you** can see your resume's statistics. Visitors to your public URL cannot see how many views or downloads your + resume has. ## Password protecting your resume @@ -121,7 +121,7 @@ When password protection is enabled: Below the public URL, click the **Set Password** button. - Type a password (6-64 characters) and confirm. This password will be required to view your resume. + Type a password (6-64 characters) and confirm. This password will be required to view your resume. @@ -130,7 +130,7 @@ When password protection is enabled: - Choose a password you're comfortable sharing. Anyone with the password can view and download your resume. + Choose a password you're comfortable sharing. Anyone with the password can view and download your resume. ### How to remove password protection @@ -151,19 +151,19 @@ Your resume will become accessible to anyone with the public URL. - Include your resume link in your email signature. Anyone you correspond with can quickly access your resume. + Include your resume link in your email signature. Anyone you correspond with can quickly access your resume. - Embed or link to your resume from your personal website. The link always shows your latest resume. + Embed or link to your resume from your personal website. The link always shows your latest resume. - Some applications ask for a link to your resume. Your public URL is a professional alternative to file uploads. + Some applications ask for a link to your resume. Your public URL is a professional alternative to file uploads. - Share your resume URL via QR code or NFC. Update your resume before the event and everyone gets the latest version. + Share your resume URL via QR code or NFC. Update your resume before the event and everyone gets the latest version. @@ -186,8 +186,8 @@ When public access is disabled: - Password protection settings are preserved - If you only want to temporarily hide your resume, consider using password protection instead of disabling public - access entirely. This way, your URL remains active for people who have the password. + If you only want to temporarily hide your resume, consider using password protection instead of disabling public + access entirely. This way, your URL remains active for people who have the password. ## Frequently asked questions @@ -198,17 +198,17 @@ When public access is disabled: - Public resumes may be indexed by search engines. If you want to prevent indexing, use password protection or keep your - resume private. + Public resumes may be indexed by search engines. If you want to prevent indexing, use password protection or keep your + resume private. - Views are only counted when someone visits your public resume URL while not logged in. Visits made while you're logged - in to your account are not included in the view statistics. + Views are only counted when someone visits your public resume URL while not logged in. Visits made while you're logged + in to your account are not included in the view statistics. - No, Reactive Resume only tracks view and download counts, not the identity of visitors. This protects visitor privacy. + No, Reactive Resume only tracks view and download counts, not the identity of visitors. This protects visitor privacy. diff --git a/docs/guides/updating-your-profile.mdx b/docs/guides/updating-your-profile.mdx index 8b9f67c2f..e3ba9e4ef 100644 --- a/docs/guides/updating-your-profile.mdx +++ b/docs/guides/updating-your-profile.mdx @@ -14,8 +14,8 @@ description: "Learn how to update your profile information including your name, - Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section, - click on the Profile link. + Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section, + click on the Profile link. diff --git a/docs/guides/using-ai.mdx b/docs/guides/using-ai.mdx index b3135e22b..fbe8aba86 100644 --- a/docs/guides/using-ai.mdx +++ b/docs/guides/using-ai.mdx @@ -9,8 +9,8 @@ description: "Learn how to configure an AI provider and API key for AI-assisted - Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section, - click on Artificial Intelligence. + Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section, + click on Artificial Intelligence. @@ -35,8 +35,8 @@ description: "Learn how to configure an AI provider and API key for AI-assisted - In the Model field, enter the model you want to use (for example, gpt-5.2,{" "} - claude-4.5-opus, or gemini-3-flash). + In the Model field, enter the model you want to use (for example, gpt-5.2,{" "} + claude-4.5-opus, or gemini-3-flash). @@ -68,8 +68,8 @@ CORS is a browser feature that prevents web pages from making requests to a diff Because of this restriction, the app has to **forward your AI requests (along with your credentials) through our server**, which then contacts the AI provider and returns the response to your browser. **Your credentials are not stored on our server; they are only used to make the request you initiated, and are not logged or retained.** - If you're curious or would like to audit how your AI requests are forwarded, you can review the code yourself. See the - relevant implementation in src/integrations/orpc/router/ai.ts within the codebase. + If you're curious or would like to audit how your AI requests are forwarded, you can review the code yourself. See the + relevant implementation in src/integrations/orpc/router/ai.ts within the codebase. --- diff --git a/docs/guides/using-custom-css.mdx b/docs/guides/using-custom-css.mdx index 0830437b1..b8412d1f2 100644 --- a/docs/guides/using-custom-css.mdx +++ b/docs/guides/using-custom-css.mdx @@ -12,10 +12,10 @@ The **Custom CSS** panel lets you write your own CSS rules to change how your re - **Scoped styling (mostly)**: to avoid affecting the rest of the app, your CSS is _usually_ scoped to the resume preview. - Screenshot of the Custom CSS section in the right sidebar + Screenshot of the Custom CSS section in the right sidebar ## Where to find it @@ -48,14 +48,14 @@ Example: ```css /* Normal rules: rely on auto-scoping */ .page a { - text-decoration: none; + text-decoration: none; } /* At-rules: manually scope selectors */ @media print { - .resume-preview-container .page a { - text-decoration: none; - } + .resume-preview-container .page a { + text-decoration: none; + } } ``` @@ -65,7 +65,7 @@ Keep each rule's selector on **one line**, for example: ```css .page-section-experience .section-item-title { - font-weight: 700; + font-weight: 700; } ``` @@ -99,7 +99,7 @@ To apply styles only on one template, prefix with the template wrapper: ```css .template-azurill .page-header { - gap: 12pt; + gap: 12pt; } ``` @@ -109,7 +109,7 @@ Each page has a class like `.page-0`, `.page-1`, etc. ```css .page-0 .page-header { - margin-bottom: 6pt; + margin-bottom: 6pt; } ``` @@ -119,8 +119,8 @@ Custom sections include a dynamic class: `.page-section-`. ```css .page-section-custom { - border-top: 1pt solid color-mix(in srgb, var(--page-text-color) 15%, transparent); - padding-top: 6pt; + border-top: 1pt solid color-mix(in srgb, var(--page-text-color) 15%, transparent); + padding-top: 6pt; } ``` @@ -142,9 +142,9 @@ Example (tighten spacing without changing your layout settings): ```css .page { - --page-margin-x: 10pt; - --page-margin-y: 10pt; - --page-gap-y: 6pt; + --page-margin-x: 10pt; + --page-margin-y: 10pt; + --page-gap-y: 6pt; } ``` @@ -154,10 +154,10 @@ Example (tighten spacing without changing your layout settings): ```css .page-section > h6 { - margin-bottom: 2pt; - font-weight: 800; - letter-spacing: 0.02em; - text-transform: uppercase; + margin-bottom: 2pt; + font-weight: 800; + letter-spacing: 0.02em; + text-transform: uppercase; } ``` @@ -165,12 +165,12 @@ Example (tighten spacing without changing your layout settings): ```css .page-section { - padding-bottom: 8pt; - border-bottom: 1pt solid color-mix(in srgb, var(--page-text-color) 12%, transparent); + padding-bottom: 8pt; + border-bottom: 1pt solid color-mix(in srgb, var(--page-text-color) 12%, transparent); } .page-section:last-child { - border-bottom: none; + border-bottom: none; } ``` @@ -178,8 +178,8 @@ Example (tighten spacing without changing your layout settings): ```css .section-item-metadata { - font-size: calc(var(--page-body-font-size) * 0.9pt); - opacity: 0.75; + font-size: calc(var(--page-body-font-size) * 0.9pt); + opacity: 0.75; } ``` @@ -187,16 +187,16 @@ Example (tighten spacing without changing your layout settings): ```css .section-item-description { - opacity: 0.95; + opacity: 0.95; } .section-item-description ul { - margin-left: 12pt; - padding-left: 0; + margin-left: 12pt; + padding-left: 0; } .section-item-description li { - margin: 2pt 0; + margin: 2pt 0; } ``` @@ -204,11 +204,11 @@ Example (tighten spacing without changing your layout settings): ```css .section-item-website a { - display: inline-block; - padding: 3pt 9pt; - border-radius: 999pt; - border: 2pt solid color-mix(in srgb, var(--page-primary-color) 45%, transparent); - text-decoration: none; + display: inline-block; + padding: 3pt 9pt; + border-radius: 999pt; + border: 2pt solid color-mix(in srgb, var(--page-primary-color) 45%, transparent); + text-decoration: none; } ``` @@ -216,7 +216,7 @@ Example (tighten spacing without changing your layout settings): ```css .page-picture { - display: none; + display: none; } ``` @@ -224,12 +224,12 @@ Example (tighten spacing without changing your layout settings): ```css .page-header { - gap: 6pt; - padding-bottom: 6pt; + gap: 6pt; + padding-bottom: 6pt; } .basics-headline { - opacity: 0.8; + opacity: 0.8; } ``` @@ -237,16 +237,16 @@ Example (tighten spacing without changing your layout settings): ```css .skills-item-name { - font-weight: 700; + font-weight: 700; } .skills-item-proficiency { - margin-top: 1pt; + margin-top: 1pt; } .skills-item-keywords { - display: block; - opacity: 0.85; + display: block; + opacity: 0.85; } ``` @@ -254,9 +254,9 @@ Example (tighten spacing without changing your layout settings): ```css .page-section-profiles { - background: color-mix(in srgb, var(--page-primary-color) 10%, transparent); - border-radius: 8pt; - padding: 8pt 12pt; + background: color-mix(in srgb, var(--page-primary-color) 10%, transparent); + border-radius: 8pt; + padding: 8pt 12pt; } ``` diff --git a/docs/guides/using-private-notes.mdx b/docs/guides/using-private-notes.mdx index a3defe64a..024848677 100644 --- a/docs/guides/using-private-notes.mdx +++ b/docs/guides/using-private-notes.mdx @@ -10,7 +10,7 @@ The **Private Notes** section is a dedicated space for storing personal informat Think of it as a built-in notebook attached to each resume where you can jot down anything relevant to your job search. - Private Notes are stored securely with your resume data and are only visible to you when editing your resume. + Private Notes are stored securely with your resume data and are only visible to you when editing your resume. ## Where to find it @@ -18,10 +18,10 @@ Think of it as a built-in notebook attached to each resume where you can jot dow In the resume builder, open the **right sidebar** and select **Notes** from the available sections. - Screenshot of the Notes section in the right sidebar + Screenshot of the Notes section in the right sidebar ## Use cases @@ -42,8 +42,8 @@ Keep a record of where you've sent this particular resume: Paste links to the original job postings so you can quickly reference them when preparing for interviews or following up. - Job postings often get taken down after a position is filled. Consider copying key requirements or responsibilities - into your notes as a backup. + Job postings often get taken down after a position is filled. Consider copying key requirements or responsibilities + into your notes as a backup. ### Interview preparation @@ -79,7 +79,7 @@ Track your follow-up schedule: - Use the rich text editor to add your notes. You can format text with bold, italics, bullet points, and more. + Use the rich text editor to add your notes. You can format text with bold, italics, bullet points, and more. @@ -90,8 +90,8 @@ Track your follow-up schedule: ## Privacy guarantee - Private Notes are for your eyes only. They will **never** appear on: - Your exported PDF or other file formats - Your - publicly shared resume link - Any printed version of your resume + Private Notes are for your eyes only. They will **never** appear on: - Your exported PDF or other file formats - Your + publicly shared resume link - Any printed version of your resume This makes it safe to store sensitive information like salary expectations, candid thoughts about opportunities, or personal reminders that you wouldn't want an employer to see. diff --git a/docs/guides/using-the-api.mdx b/docs/guides/using-the-api.mdx index 81717264e..24849c106 100644 --- a/docs/guides/using-the-api.mdx +++ b/docs/guides/using-the-api.mdx @@ -9,8 +9,8 @@ description: "Learn how to create API keys and authenticate requests to the Reac - Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section, - click on the API Keys link. + Once you're in the dashboard, look for the sidebar navigation on the left. Under the Settings section, + click on the API Keys link. diff --git a/docs/guides/using-the-mcp-server.mdx b/docs/guides/using-the-mcp-server.mdx index 1f158d67f..c6edc259e 100644 --- a/docs/guides/using-the-mcp-server.mdx +++ b/docs/guides/using-the-mcp-server.mdx @@ -30,14 +30,14 @@ If your client supports the `url` field (e.g. **Cursor**), use this — no extra ```json { - "mcpServers": { - "reactive-resume": { - "url": "https://rxresu.me/mcp", - "headers": { - "x-api-key": "your-api-key" - } - } - } + "mcpServers": { + "reactive-resume": { + "url": "https://rxresu.me/mcp", + "headers": { + "x-api-key": "your-api-key" + } + } + } } ``` @@ -47,12 +47,12 @@ If your client only supports `command` / `args` (e.g. **Claude Desktop**), use [ ```json { - "mcpServers": { - "reactive-resume": { - "command": "npx", - "args": ["mcp-remote", "https://rxresu.me/mcp", "--header", "x-api-key:your-api-key"] - } - } + "mcpServers": { + "reactive-resume": { + "command": "npx", + "args": ["mcp-remote", "https://rxresu.me/mcp", "--header", "x-api-key:your-api-key"] + } + } } ``` @@ -72,10 +72,10 @@ If you're running a self-hosted Reactive Resume instance, replace `https://rxres ```json { - "url": "https://resume.example.com/mcp", - "headers": { - "x-api-key": "your-api-key" - } + "url": "https://resume.example.com/mcp", + "headers": { + "x-api-key": "your-api-key" + } } ``` @@ -160,8 +160,8 @@ Once your MCP client is connected, you can use natural language to interact with - "Tailor my resume for this job description: ..." (uses `tailor_resume`) - The AI will use `get_resume` to inspect your current resume before making changes with `patch_resume`. This ensures - the correct JSON paths are used. + The AI will use `get_resume` to inspect your current resume before making changes with `patch_resume`. This ensures + the correct JSON paths are used. ## Troubleshooting diff --git a/docs/guides/using-the-patch-api.mdx b/docs/guides/using-the-patch-api.mdx index 071f82e6a..443a273f6 100644 --- a/docs/guides/using-the-patch-api.mdx +++ b/docs/guides/using-the-patch-api.mdx @@ -17,8 +17,8 @@ This is based on the [JSON Patch (RFC 6902)](https://datatracker.ietf.org/doc/ht | Replace the entire resume data at once | **PUT** | - The PATCH endpoint only modifies the resume `data` (the JSONB column). To update top-level resume properties like - `name`, `slug`, `tags`, or `isPublic`, use the existing `PUT /resume/{id}` endpoint. + The PATCH endpoint only modifies the resume `data` (the JSONB column). To update top-level resume properties like + `name`, `slug`, `tags`, or `isPublic`, use the existing `PUT /resume/{id}` endpoint. ## Authentication @@ -26,7 +26,7 @@ This is based on the [JSON Patch (RFC 6902)](https://datatracker.ietf.org/doc/ht All requests require your API key in the `x-api-key` header. See [Using the API](/guides/using-the-api) for how to create one. - If you're self-hosting, replace `https://rxresu.me` with your instance URL. The API is served under `/api/openapi`. + If you're self-hosting, replace `https://rxresu.me` with your instance URL. The API is served under `/api/openapi`. ## Endpoint @@ -41,7 +41,7 @@ The resume ID is taken from the URL path, so the request body only requires the ```json { - "operations": [{ "op": "replace", "path": "/basics/name", "value": "Jane Doe" }] + "operations": [{ "op": "replace", "path": "/basics/name", "value": "Jane Doe" }] } ``` @@ -101,8 +101,8 @@ curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \ ``` - The path `/sections/experience/items/-` uses the special `-` index, which means "append to the end of the array". To - insert at a specific position, use a numeric index like `/sections/experience/items/0` for the beginning. + The path `/sections/experience/items/-` uses the special `-` index, which means "append to the end of the array". To + insert at a specific position, use a numeric index like `/sections/experience/items/0` for the beginning. ### Remove an Item from a Section @@ -179,8 +179,8 @@ curl -X PATCH "https://rxresu.me/api/openapi/resume/YOUR_RESUME_ID" \ | `403` | `RESUME_LOCKED` | The resume is locked and cannot be modified. Unlock it first. | - All operations in a single request are applied atomically. If any operation fails (including a `test`), none of the - operations are applied. + All operations in a single request are applied atomically. If any operation fails (including a `test`), none of the + operations are applied. ## Tips diff --git a/docs/legal/privacy-policy.mdx b/docs/legal/privacy-policy.mdx index bf2fe97c6..ab199cbf9 100644 --- a/docs/legal/privacy-policy.mdx +++ b/docs/legal/privacy-policy.mdx @@ -17,9 +17,9 @@ Reactive Resume is open-source and can be operated in different ways (for exampl If you are self-hosting, **you** are the Service Operator and responsible for compliance (including configuring email delivery, storage, and logging appropriately). - **Note for self-hosted deployments:** If you are using the official hosted service at rxresu.me, the - project's published support contact is hello@amruthpillai.com. Replace the placeholders above with the - correct operator details for your deployment if you are self-hosting. + **Note for self-hosted deployments:** If you are using the official hosted service at rxresu.me, the + project's published support contact is hello@amruthpillai.com. Replace the placeholders above with the + correct operator details for your deployment if you are self-hosting. --- diff --git a/docs/legal/terms-of-service.mdx b/docs/legal/terms-of-service.mdx index c5415b219..f3c3e14ab 100644 --- a/docs/legal/terms-of-service.mdx +++ b/docs/legal/terms-of-service.mdx @@ -15,9 +15,9 @@ These Terms of Service (the “Terms”) govern your access to and use of **Reac If you do not agree, do not use the Service. - Replace the placeholders above with the correct operator details for your deployment. If you are using the official - hosted service at rxresu.me, the project's published support contact is{" "} - hello@amruthpillai.com. + Replace the placeholders above with the correct operator details for your deployment. If you are using the official + hosted service at rxresu.me, the project's published support contact is{" "} + hello@amruthpillai.com. --- diff --git a/docs/self-hosting/docker.mdx b/docs/self-hosting/docker.mdx index 46146b298..a90942d61 100644 --- a/docs/self-hosting/docker.mdx +++ b/docs/self-hosting/docker.mdx @@ -8,14 +8,14 @@ description: "A comprehensive guide to self-host Reactive Resume with Docker (Po Reactive Resume can be self-hosted using Docker in a matter of minutes, and this guide will walk you through the process. Here are some of the services you'll need to get started: - Stores accounts, resumes, and application data. - Generates PDFs and screenshots using a headless Chromium browser. - - SMTP for verification emails, password reset, etc. If not configured, emails are logged to the server console. - - - Use S3-compatible storage, or local persistent storage via /app/data. - + Stores accounts, resumes, and application data. + Generates PDFs and screenshots using a headless Chromium browser. + + SMTP for verification emails, password reset, etc. If not configured, emails are logged to the server console. + + + Use S3-compatible storage, or local persistent storage via /app/data. + You can pull the latest app image from: @@ -26,9 +26,9 @@ You can pull the latest app image from: ## Minimum requirements - Docker Engine + Docker Compose plugin (or Docker Desktop). - 2 vCPU / 2 GB RAM minimum (4 GB recommended if Postgres + Printer run on the same host). - Enough for Postgres + uploads (start with 10-20 GB and scale as needed). + Docker Engine + Docker Compose plugin (or Docker Desktop). + 2 vCPU / 2 GB RAM minimum (4 GB recommended if Postgres + Printer run on the same host). + Enough for Postgres + uploads (start with 10-20 GB and scale as needed). ## Quickstart using Docker Compose @@ -249,45 +249,45 @@ docker compose logs -f reactive-resume ## How startup works (database migrations) - On every start, the server automatically runs database migrations before serving traffic. If migrations fail - (usually due to a DB connection issue), the container will exit with an error. + On every start, the server automatically runs database migrations before serving traffic. If migrations fail + (usually due to a DB connection issue), the container will exit with an error. ## Environment variables - -
    -
  • - APP_URL -
  • -
  • - DATABASE_URL -
  • -
  • - PRINTER_ENDPOINT -
  • -
  • - AUTH_SECRET -
  • -
-
- -
    -
  • - SMTP (SMTP_*) -
  • -
  • - Social auth (GOOGLE_*, GITHUB_*) -
  • -
  • - S3 storage (S3_*) -
  • -
  • - Feature flags (FLAG_*) -
  • -
-
+ +
    +
  • + APP_URL +
  • +
  • + DATABASE_URL +
  • +
  • + PRINTER_ENDPOINT +
  • +
  • + AUTH_SECRET +
  • +
+
+ +
    +
  • + SMTP (SMTP_*) +
  • +
  • + Social auth (GOOGLE_*, GITHUB_*) +
  • +
  • + S3 storage (S3_*) +
  • +
  • + Feature flags (FLAG_*) +
  • +
+
@@ -317,8 +317,8 @@ docker compose logs -f reactive-resume - - **`DATABASE_URL`**: Postgres connection string in the format `postgresql://USER:PASSWORD@HOST:PORT/DATABASE`. - In - Docker Compose, set `HOST` to the Postgres service name (e.g. `postgres`), not `localhost`. + - **`DATABASE_URL`**: Postgres connection string in the format `postgresql://USER:PASSWORD@HOST:PORT/DATABASE`. - In + Docker Compose, set `HOST` to the Postgres service name (e.g. `postgres`), not `localhost`. @@ -361,15 +361,15 @@ openssl rand -hex 32 - - **Default (local)**: If all `S3_*` values are empty, uploads are stored under `/app/data`. Mount it to persistent - storage (for example `./data:/app/data`) or uploads can be lost on container recreation. - **S3/S3-compatible**: - Configure these to store uploads in an S3-compatible service (SeaweedFS, MinIO, AWS S3, etc.): - - **`S3_ACCESS_KEY_ID`** - **`S3_SECRET_ACCESS_KEY`** - **`S3_REGION`** - **`S3_ENDPOINT`** (for S3-compatible - providers; may be blank for AWS depending on your setup) - **`S3_BUCKET`** - **`S3_FORCE_PATH_STYLE`**: Controls how - the bucket is addressed in URLs. Defaults to `"false"`. - Set to `"true"` for **path-style** URLs - (`https://s3-server.com/bucket`). Common with **MinIO**, **SeaweedFS**, and other self-hosted S3-compatible services. - - Set to `"false"` for **virtual-hosted-style** URLs (`https://bucket.s3-server.com`). Common with **AWS S3**, - **Cloudflare R2**, and most cloud providers. + - **Default (local)**: If all `S3_*` values are empty, uploads are stored under `/app/data`. Mount it to persistent + storage (for example `./data:/app/data`) or uploads can be lost on container recreation. - **S3/S3-compatible**: + Configure these to store uploads in an S3-compatible service (SeaweedFS, MinIO, AWS S3, etc.): - + **`S3_ACCESS_KEY_ID`** - **`S3_SECRET_ACCESS_KEY`** - **`S3_REGION`** - **`S3_ENDPOINT`** (for S3-compatible + providers; may be blank for AWS depending on your setup) - **`S3_BUCKET`** - **`S3_FORCE_PATH_STYLE`**: Controls how + the bucket is addressed in URLs. Defaults to `"false"`. - Set to `"true"` for **path-style** URLs + (`https://s3-server.com/bucket`). Common with **MinIO**, **SeaweedFS**, and other self-hosted S3-compatible services. + - Set to `"false"` for **virtual-hosted-style** URLs (`https://bucket.s3-server.com`). Common with **AWS S3**, + **Cloudflare R2**, and most cloud providers. @@ -445,8 +445,8 @@ Most reverse proxies (such as **Traefik**, **Caddy**, or **nginx** with upstream This is particularly useful in high-availability setups where you have multiple instances of Reactive Resume. If one instance becomes unhealthy (for example, it loses its database connection), the reverse proxy will stop routing traffic to it until it recovers. - If you're using **Traefik**, it automatically respects Docker health checks when using the Docker provider. Unhealthy - containers are excluded from routing without any additional configuration. + If you're using **Traefik**, it automatically respects Docker health checks when using the Docker provider. Unhealthy + containers are excluded from routing without any additional configuration. ### Manually checking health @@ -476,25 +476,25 @@ A healthy response returns HTTP 200. Any other response (or a connection failure - - **Common cause**: `APP_URL` doesn't match the URL you're actually using (especially behind a reverse proxy). - - **Fix**: set `APP_URL` to the public URL (preferably HTTPS) and restart the container. + - **Common cause**: `APP_URL` doesn't match the URL you're actually using (especially behind a reverse proxy). - + **Fix**: set `APP_URL` to the public URL (preferably HTTPS) and restart the container. - - **Common cause**: Reactive Resume can't reach the printer or the printer can't reach your app. - **Checks**: - - `PRINTER_ENDPOINT` should usually be `http://printer:3000` in Compose. - If you use - `PRINTER_APP_URL="http://host.docker.internal:3000"`, ensure `extra_hosts: host-gateway` is present for the printer - service. + - **Common cause**: Reactive Resume can't reach the printer or the printer can't reach your app. - **Checks**: - + `PRINTER_ENDPOINT` should usually be `http://printer:3000` in Compose. - If you use + `PRINTER_APP_URL="http://host.docker.internal:3000"`, ensure `extra_hosts: host-gateway` is present for the printer + service. - - **Cause**: you didn't mount persistent storage for `/app/data` (when not using S3). - **Fix**: add a volume mount - like `./data:/app/data` and redeploy. + - **Cause**: you didn't mount persistent storage for `/app/data` (when not using S3). - **Fix**: add a volume mount + like `./data:/app/data` and redeploy. - - **Expected behavior**: if SMTP vars are empty, the app logs emails to the console instead. - **Fix**: configure SMTP - and verify your provider's TLS/port settings. + - **Expected behavior**: if SMTP vars are empty, the app logs emails to the console instead. - **Fix**: configure SMTP + and verify your provider's TLS/port settings. diff --git a/docs/self-hosting/examples.mdx b/docs/self-hosting/examples.mdx index 4ce43d3c3..cd7fc05eb 100644 --- a/docs/self-hosting/examples.mdx +++ b/docs/self-hosting/examples.mdx @@ -10,9 +10,9 @@ Every self-hosted setup is unique. You might be running on a single VPS, a Kuber These examples go beyond the basic setup in the [Self-Hosting with Docker](/self-hosting/docker) guide, showing production-ready configurations with reverse proxies, SSL termination, and other common patterns. - **Help others by sharing your setup!** If you have a working configuration that isn't covered here, I'd love to - include it. Simply [open a pull request](https://github.com/amruthpillai/reactive-resume) with your example added to - this page. Your contribution helps the community and makes self-hosting easier for everyone. + **Help others by sharing your setup!** If you have a working configuration that isn't covered here, I'd love to + include it. Simply [open a pull request](https://github.com/amruthpillai/reactive-resume) with your example added to + this page. Your contribution helps the community and makes self-hosting easier for everyone. --- @@ -22,8 +22,8 @@ These examples go beyond the basic setup in the [Self-Hosting with Docker](/self This example uses [Traefik](https://traefik.io/) as a reverse proxy with automatic SSL certificate management via Let's Encrypt. Only the Reactive Resume app is exposed through Traefik—Postgres and the printer remain on an internal network. - Traefik automatically discovers services via Docker labels and handles SSL certificates, making it ideal for setups - where you want minimal configuration. + Traefik automatically discovers services via Docker labels and handles SSL certificates, making it ideal for setups + where you want minimal configuration. ```yaml compose-traefik.yml lines expandable @@ -294,8 +294,8 @@ http { ``` - For automatic SSL certificates with nginx, consider using [certbot](https://certbot.eff.org/) with the `--nginx` - plugin, or a companion container like [nginx-proxy-acme](https://github.com/nginx-proxy/acme-companion). + For automatic SSL certificates with nginx, consider using [certbot](https://certbot.eff.org/) with the `--nginx` + plugin, or a companion container like [nginx-proxy-acme](https://github.com/nginx-proxy/acme-companion). --- @@ -305,8 +305,8 @@ http { This example demonstrates a production-grade Docker Swarm deployment with multiple replicas, health checks, rolling updates, and Traefik integration. It includes SeaweedFS for S3-compatible storage and a PostgreSQL database with custom configuration. - Docker Swarm is great for multi-node deployments where you need high availability and easy scaling. The app service is - configured with 2 replicas and rolling update strategy. + Docker Swarm is great for multi-node deployments where you need high availability and easy scaling. The app service is + configured with 2 replicas and rolling update strategy. ```yaml compose-swarm.yml lines expandable @@ -477,8 +477,8 @@ docker stack rm reactive_resume ``` - This example assumes you have an external Traefik network already set up. Adjust the `traefik_network` reference and - labels based on your Traefik configuration. + This example assumes you have an external Traefik network already set up. Adjust the `traefik_network` reference and + labels based on your Traefik configuration. --- diff --git a/docs/self-hosting/migration.mdx b/docs/self-hosting/migration.mdx index 92f4ffc7e..b11345deb 100644 --- a/docs/self-hosting/migration.mdx +++ b/docs/self-hosting/migration.mdx @@ -8,8 +8,8 @@ description: "A step-by-step guide to migrate your Reactive Resume instance from This guide walks you through migrating your Reactive Resume installation from **v4 to v5**. The migration process involves setting up a new v5 instance alongside your existing v4 instance, then transferring your users and resumes to the new system. - **Keep your v4 instance running** until you have successfully migrated all data to v5 and verified everything works - correctly. This ensures you have a fallback in case anything goes wrong during the migration. + **Keep your v4 instance running** until you have successfully migrated all data to v5 and verified everything works + correctly. This ensures you have a fallback in case anything goes wrong during the migration. ## Prerequisites @@ -17,15 +17,15 @@ This guide walks you through migrating your Reactive Resume installation from ** Before starting the migration, ensure you have: - Your existing Reactive Resume v4 instance should be running and accessible. - - A fresh Reactive Resume v5 instance set up and running. Follow the [Self-Hosting with Docker](/self-hosting/docker) - guide if you haven't done this yet. - - - Access to both your v4 PostgreSQL database (source) and v5 PostgreSQL database (target). - - A recent backup of your v4 database. Always backup before any migration. + Your existing Reactive Resume v4 instance should be running and accessible. + + A fresh Reactive Resume v5 instance set up and running. Follow the [Self-Hosting with Docker](/self-hosting/docker) + guide if you haven't done this yet. + + + Access to both your v4 PostgreSQL database (source) and v5 PostgreSQL database (target). + + A recent backup of your v4 database. Always backup before any migration. ## Choosing a Migration Method @@ -33,14 +33,14 @@ Before starting the migration, ensure you have: The best migration approach depends on the size of your instance: - - **Best for**: Small instances with a handful of resumes. Uses the built-in Import Dialog to manually convert resumes - one at a time. - - - **Best for**: Large instances with many users and resumes. Uses migration scripts to batch-process all users and - resumes automatically. - + + **Best for**: Small instances with a handful of resumes. Uses the built-in Import Dialog to manually convert resumes + one at a time. + + + **Best for**: Large instances with many users and resumes. Uses migration scripts to batch-process all users and + resumes automatically. + ## Manual Migration (Small Instances) @@ -53,9 +53,9 @@ If you have only a few resumes to migrate, the simplest approach is to use the *
- In your new v5 instance: 1. Log in or create a new account 2. Click **Create Resume** or use the **Import** option 3. - Select the **Reactive Resume v4** format 4. Upload your exported JSON file The import process automatically converts - the v4 format to v5. + In your new v5 instance: 1. Log in or create a new account 2. Click **Create Resume** or use the **Import** option 3. + Select the **Reactive Resume v4** format 4. Upload your exported JSON file The import process automatically converts + the v4 format to v5. @@ -64,8 +64,8 @@ If you have only a few resumes to migrate, the simplest approach is to use the * - The Import Dialog handles the schema conversion automatically, so you don't need to worry about format differences - between v4 and v5. + The Import Dialog handles the schema conversion automatically, so you don't need to worry about format differences + between v4 and v5. ## Automated Migration (Large Instances) @@ -77,17 +77,17 @@ For instances with many users and resumes, use the migration scripts to automate To run the migration scripts, you need the following installed on your host machine: - - **tsx** - TypeScript execution environment. Install globally with: ```bash npm install -g tsx ``` - - - **dotenvx** (or any tool to load `.env` files). Install globally with: ```bash npm install -g @dotenvx/dotenvx ``` - Alternatively, you can use `dotenv`, `direnv`, or export the variables manually. - - - Clone the Reactive Resume repository to access the migration scripts: ```bash git clone - https://github.com/amruthpillai/reactive-resume.git cd reactive-resume pnpm install ``` - + + **tsx** - TypeScript execution environment. Install globally with: ```bash npm install -g tsx ``` + + + **dotenvx** (or any tool to load `.env` files). Install globally with: ```bash npm install -g @dotenvx/dotenvx ``` + Alternatively, you can use `dotenv`, `direnv`, or export the variables manually. + + + Clone the Reactive Resume repository to access the migration scripts: ```bash git clone + https://github.com/amruthpillai/reactive-resume.git cd reactive-resume pnpm install ``` + ### Environment Setup @@ -103,8 +103,8 @@ PRODUCTION_DATABASE_URL="postgresql://user:password@localhost:5432/reactive_resu ``` - Double-check your connection strings! `DATABASE_URL` should point to your **new v5 database** and - `PRODUCTION_DATABASE_URL` should point to your **old v4 database**. Mixing these up could cause data loss. + Double-check your connection strings! `DATABASE_URL` should point to your **new v5 database** and + `PRODUCTION_DATABASE_URL` should point to your **old v4 database**. Mixing these up could cause data loss. ### Step 1: Migrate Users @@ -124,8 +124,8 @@ dotenvx run -- tsx scripts/migration/user.ts - Creates a mapping file (`scripts/migration/user-id-map.json`) that links old user IDs to new ones - The script saves progress automatically. If interrupted (Ctrl+C), you can run it again and it will resume from where - it left off. + The script saves progress automatically. If interrupted (Ctrl+C), you can run it again and it will resume from where + it left off. **Expected output:** @@ -196,8 +196,8 @@ Both migration scripts support graceful shutdown and resume: - **Resume migration**: Run the script again to continue from where you left off - If you need to restart the migration from scratch, delete the progress files and the user ID mapping file before - running the scripts again. + If you need to restart the migration from scratch, delete the progress files and the user ID mapping file before + running the scripts again. ## Post-Migration Steps @@ -210,12 +210,12 @@ After completing the migration: - - Create a test resume and export it as PDF - Verify social logins work (if configured) - Check that two-factor - authentication works for migrated users + - Create a test resume and export it as PDF - Verify social logins work (if configured) - Check that two-factor + authentication works for migrated users - Once verified, update your DNS records or reverse proxy to point to the new v5 instance. + Once verified, update your DNS records or reverse proxy to point to the new v5 instance. @@ -231,18 +231,18 @@ After completing the migration: - User profile pictures (avatars) are stored as references in the database. If you were using S3 storage, ensure your v5 - instance has access to the same bucket, or users may need to re-upload their avatars. + User profile pictures (avatars) are stored as references in the database. If you were using S3 storage, ensure your v5 + instance has access to the same bucket, or users may need to re-upload their avatars. - Similar to profile pictures, any images embedded in resumes need to be accessible from your v5 instance. Consider - migrating your storage bucket or updating references as needed. + Similar to profile pictures, any images embedded in resumes need to be accessible from your v5 instance. Consider + migrating your storage bucket or updating references as needed. - If you're using custom OAuth providers, ensure the same providers are configured in v5 with matching client IDs. Users - authenticate with the same provider ID, so mismatched configurations will cause login failures. + If you're using custom OAuth providers, ensure the same providers are configured in v5 with matching client IDs. Users + authenticate with the same provider ID, so mismatched configurations will cause login failures. @@ -263,18 +263,18 @@ After completing the migration: - Users are skipped if: - Their email already exists in the v5 database - Their username already exists in the v5 - database - They were already migrated in a previous run Check the console output for skip reasons. + Users are skipped if: - Their email already exists in the v5 database - Their username already exists in the v5 + database - They were already migrated in a previous run Check the console output for skip reasons. - Resumes are skipped if: - The associated user wasn't migrated (user ID not in mapping file) - A resume with the same - slug already exists for that user - They were already migrated in a previous run + Resumes are skipped if: - The associated user wasn't migrated (user ID not in mapping file) - A resume with the same + slug already exists for that user - They were already migrated in a previous run - If a resume can't be parsed from v4 format, it will be created with default empty data. Check the console output for - warnings about specific resumes, and consider manually importing those using the Import Dialog. + If a resume can't be parsed from v4 format, it will be created with default empty data. Check the console output for + warnings about specific resumes, and consider manually importing those using the Import Dialog. diff --git a/docs/self-hosting/sso.mdx b/docs/self-hosting/sso.mdx index b1e77f5da..a314ab0aa 100644 --- a/docs/self-hosting/sso.mdx +++ b/docs/self-hosting/sso.mdx @@ -12,8 +12,8 @@ Reactive Resume supports custom OAuth providers, allowing you to integrate with - Integrate with existing LDAP/Active Directory infrastructure - Custom OAuth is designed for **self-hosted instances**. If you're using the hosted version at - [rxresu.me](https://rxresu.me), you can use the built-in Google and GitHub sign-in options. + Custom OAuth is designed for **self-hosted instances**. If you're using the hosted version at + [rxresu.me](https://rxresu.me), you can use the built-in Google and GitHub sign-in options. ## Environment Variables @@ -82,8 +82,8 @@ https://resume.example.com/api/auth/oauth2/callback/custom ``` - Make sure the callback URL exactly matches what you configure in your OAuth provider. A mismatch will cause - authentication to fail. + Make sure the callback URL exactly matches what you configure in your OAuth provider. A mismatch will cause + authentication to fail. ## Profile Mapping @@ -98,7 +98,7 @@ Reactive Resume automatically maps user profile data from the OAuth provider. Th | **Avatar** | `image` → `picture` → `avatar_url` | - The OAuth provider **must** return an email address. If no email is provided, authentication will fail with an error. + The OAuth provider **must** return an email address. If no email is provided, authentication will fail with an error. ## Provider-Specific Setup @@ -273,9 +273,9 @@ OAUTH_DISCOVERY_URL="https://auth.company.com/application/o/reactive-resume/.wel - The callback URL configured in your OAuth provider must exactly match: ``` - {APP_URL}/api/auth/oauth2/callback/custom ``` Common issues: - Trailing slash mismatch - HTTP vs HTTPS mismatch - Port - number differences - Path case sensitivity + The callback URL configured in your OAuth provider must exactly match: ``` + {APP_URL}/api/auth/oauth2/callback/custom ``` Common issues: - Trailing slash mismatch - HTTP vs HTTPS mismatch - Port + number differences - Path case sensitivity @@ -288,9 +288,9 @@ OAUTH_DISCOVERY_URL="https://auth.company.com/application/o/reactive-resume/.wel - If running behind a reverse proxy: - Ensure `APP_URL` matches your public URL - Verify the proxy passes the correct - headers (`X-Forwarded-Proto`, `X-Forwarded-Host`) - Check that your OAuth provider allows the redirect URI from your - domain + If running behind a reverse proxy: - Ensure `APP_URL` matches your public URL - Verify the proxy passes the correct + headers (`X-Forwarded-Proto`, `X-Forwarded-Host`) - Check that your OAuth provider allows the redirect URI from your + domain @@ -307,17 +307,17 @@ OAUTH_DISCOVERY_URL="https://auth.company.com/application/o/reactive-resume/.wel ## Security Considerations - - Always use HTTPS for both your Reactive Resume instance and OAuth provider in production. OAuth tokens should never - be transmitted over unencrypted connections. - - - Never commit `OAUTH_CLIENT_SECRET` to version control. Use environment variables or a secrets manager. - - - Configure your OAuth provider to only allow the exact redirect URI. Avoid wildcards in redirect URI configurations. - - - Only request the scopes you need. The default (`openid profile email`) is sufficient for Reactive Resume. - + + Always use HTTPS for both your Reactive Resume instance and OAuth provider in production. OAuth tokens should never + be transmitted over unencrypted connections. + + + Never commit `OAUTH_CLIENT_SECRET` to version control. Use environment variables or a secrets manager. + + + Configure your OAuth provider to only allow the exact redirect URI. Avoid wildcards in redirect URI configurations. + + + Only request the scopes you need. The default (`openid profile email`) is sufficient for Reactive Resume. + diff --git a/docs/spec.json b/docs/spec.json index 1838aafc5..751210ab8 100644 --- a/docs/spec.json +++ b/docs/spec.json @@ -1,3097 +1,3097 @@ { - "info": { - "title": "Reactive Resume", - "version": "5.0.9", - "description": "Reactive Resume API", - "license": { "name": "MIT", "url": "https://github.com/amruthpillai/reactive-resume/blob/main/LICENSE" }, - "contact": { "name": "Amruth Pillai", "email": "hello@amruthpillai.com", "url": "https://amruthpillai.com" } - }, - "servers": [{ "url": "http://localhost:3000/api/openapi" }], - "externalDocs": { "url": "https://docs.rxresu.me", "description": "Reactive Resume Documentation" }, - "components": { - "securitySchemes": { - "apiKey": { - "type": "apiKey", - "name": "x-api-key", - "in": "header", - "description": "The API key to authenticate requests." - } - }, - "schemas": { - "ResumeData": { - "type": "object", - "properties": { - "picture": { - "type": "object", - "properties": { - "hidden": { "type": "boolean", "description": "Whether to hide the picture from the resume." }, - "url": { - "type": "string", - "description": "The URL to the picture to display on the resume. Must be a valid URL with a protocol (http:// or https://)." - }, - "size": { - "type": "number", - "minimum": 32, - "maximum": 512, - "description": "The size of the picture to display on the resume, defined in points (pt)." - }, - "rotation": { - "type": "number", - "minimum": 0, - "maximum": 360, - "description": "The rotation of the picture to display on the resume, defined in degrees (°)." - }, - "aspectRatio": { - "type": "number", - "minimum": 0.5, - "maximum": 2.5, - "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)." - }, - "borderRadius": { - "type": "number", - "minimum": 0, - "maximum": 100, - "description": "The border radius of the picture to display on the resume, defined in points (pt)." - }, - "borderColor": { - "type": "string", - "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)." - }, - "borderWidth": { - "type": "number", - "minimum": 0, - "description": "The width of the border of the picture to display on the resume, defined in points (pt)." - }, - "shadowColor": { - "type": "string", - "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)." - }, - "shadowWidth": { - "type": "number", - "minimum": 0, - "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)." - } - }, - "required": [ - "hidden", - "url", - "size", - "rotation", - "aspectRatio", - "borderRadius", - "borderColor", - "borderWidth", - "shadowColor", - "shadowWidth" - ], - "description": "Configuration for photograph displayed on the resume" - }, - "basics": { - "type": "object", - "properties": { - "name": { "type": "string", "description": "The full name of the author of the resume." }, - "headline": { "type": "string", "description": "The headline of the author of the resume." }, - "email": { "type": "string", "description": "The email address of the author of the resume." }, - "phone": { "type": "string", "description": "The phone number of the author of the resume." }, - "location": { "type": "string", "description": "The location of the author of the resume." }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "description": "The website of the author of the resume." - }, - "customFields": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the custom field. Usually generated as a UUID." - }, - "icon": { - "type": "string", - "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." - }, - "text": { "type": "string", "description": "The text to display for the custom field." }, - "link": { - "type": "string", - "description": "If the custom field should be a link, the URL to link to." - } - }, - "required": ["id", "icon", "text", "link"] - }, - "description": "The custom fields to display on the resume." - } - }, - "required": ["name", "headline", "email", "phone", "location", "website", "customFields"], - "description": "Basic information about the author, such as name, email, phone, location, and website" - }, - "summary": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the summary of the resume." }, - "columns": { "type": "number", "description": "The number of columns the summary should span across." }, - "hidden": { "type": "boolean", "description": "Whether to hide the summary from the resume." }, - "content": { - "type": "string", - "description": "The content of the summary of the resume. This should be a HTML-formatted string." - } - }, - "required": ["title", "columns", "hidden", "content"], - "description": "Summary section of the resume, useful for a short bio or introduction" - }, - "sections": { - "type": "object", - "properties": { - "profiles": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "icon": { - "type": "string", - "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." - }, - "network": { - "type": "string", - "minLength": 1, - "description": "The name of the network or platform." - }, - "username": { - "type": "string", - "description": "The username of the author on the network or platform." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "description": "The link to the profile of the author on the network or platform, if any." - } - }, - "required": ["id", "hidden", "icon", "network", "username", "website"] - }, - "description": "The items to display in the profiles section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "description": "The section to display the profiles of the author." - }, - "experience": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "company": { - "type": "string", - "minLength": 1, - "description": "The name of the company or organization." - }, - "position": { - "type": "string", - "description": "The position held at the company or organization." - }, - "location": { "type": "string", "description": "The location of the company or organization." }, - "period": { - "type": "string", - "description": "The period of time the author was employed at the company or organization." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "description": "The website of the company or organization, if any." - }, - "description": { - "type": "string", - "description": "The description of the experience. This should be a HTML-formatted string." - } - }, - "required": [ - "id", - "hidden", - "company", - "position", - "location", - "period", - "website", - "description" - ] - }, - "description": "The items to display in the experience section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "description": "The section to display the experience of the author." - }, - "education": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "school": { - "type": "string", - "minLength": 1, - "description": "The name of the school or institution." - }, - "degree": { "type": "string", "description": "The degree or qualification obtained." }, - "area": { "type": "string", "description": "The area of study or specialization." }, - "grade": { "type": "string", "description": "The grade or score achieved." }, - "location": { "type": "string", "description": "The location of the school or institution." }, - "period": { - "type": "string", - "description": "The period of time the education was obtained over." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "description": "The website of the school or institution, if any." - }, - "description": { - "type": "string", - "description": "The description of the education. This should be a HTML-formatted string." - } - }, - "required": [ - "id", - "hidden", - "school", - "degree", - "area", - "grade", - "location", - "period", - "website", - "description" - ] - }, - "description": "The items to display in the education section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "description": "The section to display the education of the author." - }, - "projects": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "name": { "type": "string", "minLength": 1, "description": "The name of the project." }, - "period": { "type": "string", "description": "The period of time the project was worked on." }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "description": "The link to the project, if any." - }, - "description": { - "type": "string", - "description": "The description of the project. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "name", "period", "website", "description"] - }, - "description": "The items to display in the projects section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "description": "The section to display the projects of the author." - }, - "skills": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "icon": { - "type": "string", - "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." - }, - "name": { "type": "string", "minLength": 1, "description": "The name of the skill." }, - "proficiency": { - "type": "string", - "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc." - }, - "level": { - "type": "number", - "minimum": 0, - "maximum": 5, - "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." - }, - "keywords": { - "type": "array", - "items": { "type": "string" }, - "description": "The keywords associated with the skill, if any. These are displayed as tags below the name." - } - }, - "required": ["id", "hidden", "icon", "name", "proficiency", "level", "keywords"] - }, - "description": "The items to display in the skills section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "description": "The section to display the skills of the author." - }, - "languages": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "language": { - "type": "string", - "minLength": 1, - "description": "The name of the language the author knows." - }, - "fluency": { - "type": "string", - "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." - }, - "level": { - "type": "number", - "minimum": 0, - "maximum": 5, - "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." - } - }, - "required": ["id", "hidden", "language", "fluency", "level"] - }, - "description": "The items to display in the languages section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "description": "The section to display the languages of the author." - }, - "interests": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "icon": { - "type": "string", - "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." - }, - "name": { "type": "string", "minLength": 1, "description": "The name of the interest/hobby." }, - "keywords": { - "type": "array", - "items": { "type": "string" }, - "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name." - } - }, - "required": ["id", "hidden", "icon", "name", "keywords"] - }, - "description": "The items to display in the interests section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "description": "The section to display the interests of the author." - }, - "awards": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "title": { "type": "string", "minLength": 1, "description": "The title of the award." }, - "awarder": { "type": "string", "description": "The awarder of the award." }, - "date": { "type": "string", "description": "The date when the award was received." }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "description": "The website of the award, if any." - }, - "description": { - "type": "string", - "description": "The description of the award. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "title", "awarder", "date", "website", "description"] - }, - "description": "The items to display in the awards section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "description": "The section to display the awards of the author." - }, - "certifications": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "title": { "type": "string", "minLength": 1, "description": "The title of the certification." }, - "issuer": { "type": "string", "description": "The issuer of the certification." }, - "date": { "type": "string", "description": "The date when the certification was received." }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "description": "The website of the certification, if any." - }, - "description": { - "type": "string", - "description": "The description of the certification. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "title", "issuer", "date", "website", "description"] - }, - "description": "The items to display in the certifications section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "description": "The section to display the certifications of the author." - }, - "publications": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "title": { "type": "string", "minLength": 1, "description": "The title of the publication." }, - "publisher": { "type": "string", "description": "The publisher of the publication." }, - "date": { "type": "string", "description": "The date when the publication was published." }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "description": "The link to the publication, if any." - }, - "description": { - "type": "string", - "description": "The description of the publication. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "title", "publisher", "date", "website", "description"] - }, - "description": "The items to display in the publications section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "description": "The section to display the publications of the author." - }, - "volunteer": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "organization": { - "type": "string", - "minLength": 1, - "description": "The name of the organization or company." - }, - "location": { "type": "string", "description": "The location of the organization or company." }, - "period": { - "type": "string", - "description": "The period of time the author was volunteered at the organization or company." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "description": "The link to the organization or company, if any." - }, - "description": { - "type": "string", - "description": "The description of the volunteer experience. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "organization", "location", "period", "website", "description"] - }, - "description": "The items to display in the volunteer section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "description": "The section to display the volunteer experience of the author." - }, - "references": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { - "type": "number", - "description": "The number of columns the section should span across." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "items": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "name": { - "type": "string", - "minLength": 1, - "description": "The name of the reference, or a note such as 'Available upon request'." - }, - "position": { "type": "string", "description": "The position or job title of the reference." }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "description": "The website or LinkedIn profile of the reference, if any." - }, - "phone": { "type": "string", "description": "The phone number of the reference." }, - "description": { - "type": "string", - "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "name", "position", "website", "phone", "description"] - }, - "description": "The items to display in the references section." - } - }, - "required": ["title", "columns", "hidden", "items"], - "description": "The section to display the references of the author." - } - }, - "required": [ - "profiles", - "experience", - "education", - "projects", - "skills", - "languages", - "interests", - "awards", - "certifications", - "publications", - "volunteer", - "references" - ], - "description": "Various sections of the resume, such as experience, education, projects, etc." - }, - "customSections": { - "type": "array", - "items": { - "type": "object", - "properties": { - "title": { "type": "string", "description": "The title of the section." }, - "columns": { "type": "number", "description": "The number of columns the section should span across." }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, - "id": { - "type": "string", - "description": "The unique identifier for the custom section. Usually generated as a UUID." - }, - "type": { - "enum": [ - "summary", - "profiles", - "experience", - "education", - "projects", - "skills", - "languages", - "interests", - "awards", - "certifications", - "publications", - "volunteer", - "references", - "cover-letter" - ], - "description": "The type of items this custom section contains. Determines which item schema and form fields to use." - }, - "items": { - "type": "array", - "items": { - "anyOf": [ - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "recipient": { - "type": "string", - "description": "The recipient's address block as HTML (name, title, company, address, email)." - }, - "content": { - "type": "string", - "description": "The cover letter body as HTML (salutation, paragraphs, closing, signature)." - } - }, - "required": ["id", "hidden", "recipient", "content"] - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "content": { - "type": "string", - "description": "The rich text content of the summary item. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "content"] - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "icon": { - "type": "string", - "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." - }, - "network": { - "type": "string", - "minLength": 1, - "description": "The name of the network or platform." - }, - "username": { - "type": "string", - "description": "The username of the author on the network or platform." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "description": "The link to the profile of the author on the network or platform, if any." - } - }, - "required": ["id", "hidden", "icon", "network", "username", "website"] - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "company": { - "type": "string", - "minLength": 1, - "description": "The name of the company or organization." - }, - "position": { - "type": "string", - "description": "The position held at the company or organization." - }, - "location": { - "type": "string", - "description": "The location of the company or organization." - }, - "period": { - "type": "string", - "description": "The period of time the author was employed at the company or organization." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "description": "The website of the company or organization, if any." - }, - "description": { - "type": "string", - "description": "The description of the experience. This should be a HTML-formatted string." - } - }, - "required": [ - "id", - "hidden", - "company", - "position", - "location", - "period", - "website", - "description" - ] - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "school": { - "type": "string", - "minLength": 1, - "description": "The name of the school or institution." - }, - "degree": { "type": "string", "description": "The degree or qualification obtained." }, - "area": { "type": "string", "description": "The area of study or specialization." }, - "grade": { "type": "string", "description": "The grade or score achieved." }, - "location": { "type": "string", "description": "The location of the school or institution." }, - "period": { - "type": "string", - "description": "The period of time the education was obtained over." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "description": "The website of the school or institution, if any." - }, - "description": { - "type": "string", - "description": "The description of the education. This should be a HTML-formatted string." - } - }, - "required": [ - "id", - "hidden", - "school", - "degree", - "area", - "grade", - "location", - "period", - "website", - "description" - ] - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "name": { "type": "string", "minLength": 1, "description": "The name of the project." }, - "period": { - "type": "string", - "description": "The period of time the project was worked on." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "description": "The link to the project, if any." - }, - "description": { - "type": "string", - "description": "The description of the project. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "name", "period", "website", "description"] - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "icon": { - "type": "string", - "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." - }, - "name": { "type": "string", "minLength": 1, "description": "The name of the skill." }, - "proficiency": { - "type": "string", - "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc." - }, - "level": { - "type": "number", - "minimum": 0, - "maximum": 5, - "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." - }, - "keywords": { - "type": "array", - "items": { "type": "string" }, - "description": "The keywords associated with the skill, if any. These are displayed as tags below the name." - } - }, - "required": ["id", "hidden", "icon", "name", "proficiency", "level", "keywords"] - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "language": { - "type": "string", - "minLength": 1, - "description": "The name of the language the author knows." - }, - "fluency": { - "type": "string", - "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." - }, - "level": { - "type": "number", - "minimum": 0, - "maximum": 5, - "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." - } - }, - "required": ["id", "hidden", "language", "fluency", "level"] - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "icon": { - "type": "string", - "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." - }, - "name": { - "type": "string", - "minLength": 1, - "description": "The name of the interest/hobby." - }, - "keywords": { - "type": "array", - "items": { "type": "string" }, - "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name." - } - }, - "required": ["id", "hidden", "icon", "name", "keywords"] - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "title": { "type": "string", "minLength": 1, "description": "The title of the award." }, - "awarder": { "type": "string", "description": "The awarder of the award." }, - "date": { "type": "string", "description": "The date when the award was received." }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "description": "The website of the award, if any." - }, - "description": { - "type": "string", - "description": "The description of the award. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "title", "awarder", "date", "website", "description"] - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "title": { - "type": "string", - "minLength": 1, - "description": "The title of the certification." - }, - "issuer": { "type": "string", "description": "The issuer of the certification." }, - "date": { "type": "string", "description": "The date when the certification was received." }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "description": "The website of the certification, if any." - }, - "description": { - "type": "string", - "description": "The description of the certification. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "title", "issuer", "date", "website", "description"] - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "title": { "type": "string", "minLength": 1, "description": "The title of the publication." }, - "publisher": { "type": "string", "description": "The publisher of the publication." }, - "date": { "type": "string", "description": "The date when the publication was published." }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "description": "The link to the publication, if any." - }, - "description": { - "type": "string", - "description": "The description of the publication. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "title", "publisher", "date", "website", "description"] - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "organization": { - "type": "string", - "minLength": 1, - "description": "The name of the organization or company." - }, - "location": { - "type": "string", - "description": "The location of the organization or company." - }, - "period": { - "type": "string", - "description": "The period of time the author was volunteered at the organization or company." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "description": "The link to the organization or company, if any." - }, - "description": { - "type": "string", - "description": "The description of the volunteer experience. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "organization", "location", "period", "website", "description"] - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "The unique identifier for the item. Usually generated as a UUID." - }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "options": { - "type": "object", - "properties": { - "showLinkInTitle": { - "type": "boolean", - "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." - } - }, - "required": ["showLinkInTitle"], - "description": "Display options for this item." - }, - "name": { - "type": "string", - "minLength": 1, - "description": "The name of the reference, or a note such as 'Available upon request'." - }, - "position": { - "type": "string", - "description": "The position or job title of the reference." - }, - "website": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." - }, - "label": { - "type": "string", - "description": "The label to display for the URL. Leave blank to display the URL as-is." - } - }, - "required": ["url", "label"], - "description": "The website or LinkedIn profile of the reference, if any." - }, - "phone": { "type": "string", "description": "The phone number of the reference." }, - "description": { - "type": "string", - "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." - } - }, - "required": ["id", "hidden", "name", "position", "website", "phone", "description"] - } - ] - }, - "description": "The items to display in the custom section. Items follow the schema of the section type." - } - }, - "required": ["title", "columns", "hidden", "id", "type", "items"] - }, - "description": "Custom sections of the resume, such as a custom section for notes, etc." - }, - "metadata": { - "type": "object", - "properties": { - "template": { - "enum": [ - "azurill", - "bronzor", - "chikorita", - "ditgar", - "ditto", - "gengar", - "glalie", - "kakuna", - "lapras", - "leafish", - "onyx", - "pikachu", - "rhyhorn" - ], - "description": "The template to use for the resume. Determines the overall design and appearance of the resume." - }, - "layout": { - "type": "object", - "properties": { - "sidebarWidth": { - "type": "number", - "minimum": 10, - "maximum": 50, - "description": "The width of the sidebar column, defined as a percentage of the page width." - }, - "pages": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fullWidth": { - "type": "boolean", - "description": "Whether the layout of the page should be full width. If true, the main column will span the entire width of the page. This means that there should be no items in the sidebar column." - }, - "main": { - "type": "array", - "items": { "type": "string" }, - "description": "The items to display in the main column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." - }, - "sidebar": { - "type": "array", - "items": { "type": "string" }, - "description": "The items to display in the sidebar column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." - } - }, - "required": ["fullWidth", "main", "sidebar"] - }, - "description": "The pages to display in the layout." - } - }, - "required": ["sidebarWidth", "pages"], - "description": "The layout of the resume. Determines the structure and arrangement of the sections on the resume." - }, - "css": { - "type": "object", - "properties": { - "enabled": { "type": "boolean", "description": "Whether to enable custom CSS for the resume." }, - "value": { - "type": "string", - "description": "The custom CSS to apply to the resume. This should be a valid CSS string." - } - }, - "required": ["enabled", "value"], - "description": "Custom CSS to apply to the resume. Can be used to override the default styles of the template." - }, - "page": { - "type": "object", - "properties": { - "gapX": { - "type": "number", - "minimum": 0, - "description": "The horizontal gap between the sections of the page, defined in points (pt)." - }, - "gapY": { - "type": "number", - "minimum": 0, - "description": "The vertical gap between the sections of the page, defined in points (pt)." - }, - "marginX": { - "type": "number", - "minimum": 0, - "description": "The horizontal margin of the page, defined in points (pt)." - }, - "marginY": { - "type": "number", - "minimum": 0, - "description": "The vertical margin of the page, defined in points (pt)." - }, - "format": { - "enum": ["a4", "letter", "free-form"], - "description": "The format of the page. Can be 'a4', 'letter' or 'free-form'." - }, - "locale": { - "type": "string", - "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden." - }, - "hideIcons": { "type": "boolean", "description": "Whether to hide the icons of the sections." } - }, - "required": ["gapX", "gapY", "marginX", "marginY", "format", "locale", "hideIcons"], - "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." - }, - "design": { - "type": "object", - "properties": { - "level": { - "type": "object", - "properties": { - "icon": { - "type": "string", - "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." - }, - "type": { - "enum": ["hidden", "circle", "square", "rectangle", "rectangle-full", "progress-bar", "icon"], - "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field." - } - }, - "required": ["icon", "type"] - }, - "colors": { - "type": "object", - "properties": { - "primary": { - "type": "string", - "description": "The primary color of the design, defined as rgba(r, g, b, a)." - }, - "text": { - "type": "string", - "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)." - }, - "background": { - "type": "string", - "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)." - } - }, - "required": ["primary", "text", "background"] - } - }, - "required": ["level", "colors"], - "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume." - }, - "typography": { - "type": "object", - "properties": { - "body": { - "type": "object", - "properties": { - "fontFamily": { - "type": "string", - "description": "The family of the font to use. Must be a font that is available on Google Fonts." - }, - "fontWeights": { - "type": "array", - "items": { "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, - "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." - }, - "fontSize": { - "type": "number", - "minimum": 6, - "maximum": 24, - "description": "The size of the font to use, defined in points (pt)." - }, - "lineHeight": { - "type": "number", - "minimum": 0.5, - "maximum": 4, - "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." - } - }, - "required": ["fontFamily", "fontWeights", "fontSize", "lineHeight"], - "description": "The typography for the body of the resume." - }, - "heading": { - "type": "object", - "properties": { - "fontFamily": { - "type": "string", - "description": "The family of the font to use. Must be a font that is available on Google Fonts." - }, - "fontWeights": { - "type": "array", - "items": { "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, - "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." - }, - "fontSize": { - "type": "number", - "minimum": 6, - "maximum": 24, - "description": "The size of the font to use, defined in points (pt)." - }, - "lineHeight": { - "type": "number", - "minimum": 0.5, - "maximum": 4, - "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." - } - }, - "required": ["fontFamily", "fontWeights", "fontSize", "lineHeight"], - "description": "The typography for the headings of the resume." - } - }, - "required": ["body", "heading"], - "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume." - }, - "notes": { - "type": "string", - "description": "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." - } - }, - "required": ["template", "layout", "css", "page", "design", "typography", "notes"], - "description": "Metadata for the resume, such as template, layout, typography, etc. This section describes the overall design and appearance of the resume." - } - }, - "required": ["picture", "basics", "summary", "sections", "customSections", "metadata"] - } - } - }, - "security": [{ "apiKey": [] }], - "openapi": "3.1.1", - "paths": { - "/ai/test-connection": { - "post": { - "operationId": "testAiConnection", - "summary": "Test AI provider connection", - "description": "Validates the connection to an AI provider by sending a simple test prompt. Requires the provider type, model name, API key, and an optional base URL. Supported providers: OpenAI, Anthropic, Google Gemini, Ollama, and Vercel AI Gateway. Requires authentication.", - "tags": ["AI"], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "provider": { "enum": ["ollama", "openai", "gemini", "anthropic", "vercel-ai-gateway"] }, - "model": { "type": "string" }, - "apiKey": { "type": "string" }, - "baseURL": { "type": "string" } - }, - "required": ["provider", "model", "apiKey", "baseURL"] - } - } - } - }, - "responses": { - "200": { - "description": "The AI provider connection was successful.", - "content": { "application/json": { "schema": { "anyOf": [{}, { "not": {} }] } } } - }, - "502": { - "description": "502", - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "type": "object", - "properties": { - "defined": { "const": true }, - "code": { "const": "BAD_GATEWAY" }, - "status": { "const": 502 }, - "message": { - "type": "string", - "default": "The AI provider returned an error or is unreachable." - }, - "data": {} - }, - "required": ["defined", "code", "status", "message"] - }, - { - "type": "object", - "properties": { - "defined": { "const": false }, - "code": { "type": "string" }, - "status": { "type": "number" }, - "message": { "type": "string" }, - "data": {} - }, - "required": ["defined", "code", "status", "message"] - } - ] - } - } - } - } - } - } - }, - "/ai/parse-pdf": { - "post": { - "operationId": "parseResumePdf", - "summary": "Parse a PDF file into resume data", - "description": "Extracts structured resume data from a PDF file using the specified AI provider. The file should be sent as a base64-encoded string along with AI provider credentials. Returns a complete ResumeData object. Requires authentication.", - "tags": ["AI"], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "provider": { "enum": ["ollama", "openai", "gemini", "anthropic", "vercel-ai-gateway"] }, - "model": { "type": "string" }, - "apiKey": { "type": "string" }, - "baseURL": { "type": "string" }, - "file": { - "type": "object", - "properties": { "name": { "type": "string" }, "data": { "type": "string" } }, - "required": ["name", "data"] - } - }, - "required": ["provider", "model", "apiKey", "baseURL", "file"] - } - } - } - }, - "responses": { - "200": { - "description": "The PDF was successfully parsed into structured resume data.", - "content": { "application/json": { "schema": { "anyOf": [{}, { "not": {} }] } } } - }, - "502": { - "description": "502", - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "type": "object", - "properties": { - "defined": { "const": true }, - "code": { "const": "BAD_GATEWAY" }, - "status": { "const": 502 }, - "message": { - "type": "string", - "default": "The AI provider returned an error or is unreachable." - }, - "data": {} - }, - "required": ["defined", "code", "status", "message"] - }, - { - "type": "object", - "properties": { - "defined": { "const": false }, - "code": { "type": "string" }, - "status": { "type": "number" }, - "message": { "type": "string" }, - "data": {} - }, - "required": ["defined", "code", "status", "message"] - } - ] - } - } - } - } - } - } - }, - "/ai/parse-docx": { - "post": { - "operationId": "parseResumeDocx", - "summary": "Parse a DOCX file into resume data", - "description": "Extracts structured resume data from a DOCX or DOC file using the specified AI provider. The file should be sent as a base64-encoded string along with AI provider credentials and the document's media type. Returns a complete ResumeData object. Requires authentication.", - "tags": ["AI"], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "provider": { "enum": ["ollama", "openai", "gemini", "anthropic", "vercel-ai-gateway"] }, - "model": { "type": "string" }, - "apiKey": { "type": "string" }, - "baseURL": { "type": "string" }, - "file": { - "type": "object", - "properties": { "name": { "type": "string" }, "data": { "type": "string" } }, - "required": ["name", "data"] - }, - "mediaType": { - "enum": [ - "application/msword", - "application/vnd.openxmlformats-officedocument.wordprocessingml.document" - ] - } - }, - "required": ["provider", "model", "apiKey", "baseURL", "file", "mediaType"] - } - } - } - }, - "responses": { - "200": { - "description": "The DOCX was successfully parsed into structured resume data.", - "content": { "application/json": { "schema": { "anyOf": [{}, { "not": {} }] } } } - }, - "502": { - "description": "502", - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "type": "object", - "properties": { - "defined": { "const": true }, - "code": { "const": "BAD_GATEWAY" }, - "status": { "const": 502 }, - "message": { - "type": "string", - "default": "The AI provider returned an error or is unreachable." - }, - "data": {} - }, - "required": ["defined", "code", "status", "message"] - }, - { - "type": "object", - "properties": { - "defined": { "const": false }, - "code": { "type": "string" }, - "status": { "type": "number" }, - "message": { "type": "string" }, - "data": {} - }, - "required": ["defined", "code", "status", "message"] - } - ] - } - } - } - } - } - } - }, - "/ai/chat": { - "post": { - "operationId": "aiChat", - "summary": "Chat with AI to modify resume", - "description": "Streams a chat response from the configured AI provider. The LLM can call the patch_resume tool to generate JSON Patch operations that modify the resume. Requires authentication and AI provider credentials.", - "tags": ["AI"], - "responses": { - "200": { - "description": "OK", - "content": { "application/json": { "schema": { "anyOf": [{}, { "not": {} }] } } } - } - } - } - }, - "/auth/providers": { - "get": { - "operationId": "listAuthProviders", - "summary": "List authentication providers", - "description": "Returns a list of all authentication providers enabled on this Reactive Resume instance, along with their display names. Possible providers include password-based credentials, Google, GitHub, and custom OAuth. No authentication required.", - "tags": ["Authentication"], - "responses": { - "200": { - "description": "A map of enabled authentication provider identifiers to their display names.", - "content": { "application/json": { "schema": { "anyOf": [{}, { "not": {} }] } } } - } - } - } - }, - "/auth/account": { - "delete": { - "operationId": "deleteAccount", - "summary": "Delete user account", - "description": "Permanently deletes the authenticated user's account, including all resumes, uploaded files (profile pictures, screenshots, PDFs), and associated data. This action is irreversible. Requires authentication.", - "tags": ["Authentication"], - "responses": { - "200": { - "description": "The user account and all associated data have been successfully deleted.", - "content": { "application/json": { "schema": { "anyOf": [{}, { "not": {} }] } } } - } - } - } - }, - "/flags": { - "get": { - "operationId": "getFeatureFlags", - "summary": "Get feature flags", - "description": "Returns the current feature flags for this Reactive Resume instance. Feature flags control instance-wide settings such as whether new user signups or email-based authentication are disabled. No authentication required.", - "tags": ["Feature Flags"], - "responses": { - "200": { - "description": "The current feature flags for this instance.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "disableSignups": { - "type": "boolean", - "description": "Whether new user signups are disabled on this instance." - }, - "disableEmailAuth": { - "type": "boolean", - "description": "Whether email-based authentication is disabled on this instance." - } - }, - "required": ["disableSignups", "disableEmailAuth"] - } - } - } - } - } - } - }, - "/resumes/tags": { - "get": { - "operationId": "listResumeTags", - "summary": "List all resume tags", - "description": "Returns a sorted list of all unique tags across the authenticated user's resumes. Useful for populating tag filters in the dashboard. Requires authentication.", - "tags": ["Resumes"], - "responses": { - "200": { - "description": "A sorted array of unique tag strings.", - "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string" } } } } - } - } - } - }, - "/resumes/{id}/statistics": { - "get": { - "operationId": "getResumeStatistics", - "summary": "Get resume statistics", - "description": "Returns view and download statistics for the specified resume, including total counts and the timestamps of the last view and download. Requires authentication.", - "tags": ["Resume Statistics"], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { "type": "string", "description": "The unique identifier of the resume." } - } - ], - "responses": { - "200": { - "description": "The resume's view and download statistics.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "isPublic": { "type": "boolean", "description": "Whether the resume is currently public." }, - "views": { "type": "number", "description": "Total number of times the resume has been viewed." }, - "downloads": { - "type": "number", - "description": "Total number of times the resume has been downloaded." - }, - "lastViewedAt": { - "anyOf": [ - { "type": "string", "format": "date-time", "x-native-type": "date" }, - { "type": "null" } - ], - "description": "Timestamp of the last view, or null if never viewed." - }, - "lastDownloadedAt": { - "anyOf": [ - { "type": "string", "format": "date-time", "x-native-type": "date" }, - { "type": "null" } - ], - "description": "Timestamp of the last download, or null if never downloaded." - } - }, - "required": ["isPublic", "views", "downloads", "lastViewedAt", "lastDownloadedAt"] - } - } - } - } - } - } - }, - "/resumes": { - "get": { - "operationId": "listResumes", - "summary": "List all resumes", - "description": "Returns a list of all resumes belonging to the authenticated user. Results can be filtered by tags and sorted by last updated date, creation date, or name. Resume data is not included in the response for performance; use the get endpoint to fetch full resume data. Requires authentication.", - "tags": ["Resumes"], - "parameters": [ - { - "name": "tags", - "in": "query", - "schema": { "type": "array", "items": { "type": "string" }, "default": [] }, - "style": "deepObject", - "explode": true, - "allowEmptyValue": true, - "allowReserved": true - }, - { - "name": "sort", - "in": "query", - "schema": { "enum": ["lastUpdatedAt", "createdAt", "name"], "default": "lastUpdatedAt" }, - "style": "deepObject", - "explode": true, - "allowEmptyValue": true, - "allowReserved": true - } - ], - "responses": { - "200": { - "description": "A list of resumes with their metadata (without full resume data).", - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { "type": "string", "description": "The ID of the resume." }, - "name": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "description": "The name of the resume." - }, - "slug": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "description": "The slug of the resume." - }, - "tags": { - "type": "array", - "items": { "type": "string" }, - "description": "The tags of the resume." - }, - "isPublic": { "type": "boolean", "description": "Whether the resume is public." }, - "isLocked": { "type": "boolean", "description": "Whether the resume is locked." }, - "createdAt": { - "type": "string", - "format": "date-time", - "x-native-type": "date", - "description": "The date and time the resume was created." - }, - "updatedAt": { - "type": "string", - "format": "date-time", - "x-native-type": "date", - "description": "The date and time the resume was last updated." - } - }, - "required": ["id", "name", "slug", "tags", "isPublic", "isLocked", "createdAt", "updatedAt"] - } - } - } - } - } - } - }, - "post": { - "operationId": "createResume", - "summary": "Create a new resume", - "description": "Creates a new resume with the given name, slug, and tags. Optionally initializes the resume with sample data by setting withSampleData to true. The slug must be unique across the user's resumes. Returns the ID of the newly created resume. Requires authentication.", - "tags": ["Resumes"], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "description": "The name of the resume." - }, - "slug": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "description": "The slug of the resume." - }, - "tags": { "type": "array", "items": { "type": "string" }, "description": "The tags of the resume." }, - "withSampleData": { "type": "boolean", "default": false } - }, - "required": ["name", "slug", "tags"] - } - } - } - }, - "responses": { - "200": { - "description": "The ID of the newly created resume.", - "content": { - "application/json": { "schema": { "type": "string", "description": "The ID of the created resume." } } - } - }, - "400": { - "description": "400", - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "type": "object", - "properties": { - "defined": { "const": true }, - "code": { "const": "RESUME_SLUG_ALREADY_EXISTS" }, - "status": { "const": 400 }, - "message": { "type": "string", "default": "A resume with this slug already exists." }, - "data": {} - }, - "required": ["defined", "code", "status", "message"] - }, - { - "type": "object", - "properties": { - "defined": { "const": false }, - "code": { "type": "string" }, - "status": { "type": "number" }, - "message": { "type": "string" }, - "data": {} - }, - "required": ["defined", "code", "status", "message"] - } - ] - } - } - } - } - } - } - }, - "/resumes/{id}": { - "get": { - "operationId": "getResume", - "summary": "Get resume by ID", - "description": "Returns a single resume with its full data, identified by its unique ID. Only resumes belonging to the authenticated user can be retrieved. Requires authentication.", - "tags": ["Resumes"], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { "type": "string", "description": "The ID of the resume." } - } - ], - "responses": { - "200": { - "description": "The resume with its full data.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "id": { "type": "string", "description": "The ID of the resume." }, - "name": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "description": "The name of the resume." - }, - "slug": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "description": "The slug of the resume." - }, - "tags": { - "type": "array", - "items": { "type": "string" }, - "description": "The tags of the resume." - }, - "isPublic": { "type": "boolean", "description": "Whether the resume is public." }, - "isLocked": { "type": "boolean", "description": "Whether the resume is locked." }, - "data": { "$ref": "#/components/schemas/ResumeData" }, - "hasPassword": { "type": "boolean" } - }, - "required": ["id", "name", "slug", "tags", "isPublic", "isLocked", "data", "hasPassword"] - } - } - } - } - } - }, - "put": { - "operationId": "updateResume", - "summary": "Update a resume", - "description": "Updates one or more fields of a resume identified by its ID. All fields are optional; only provided fields will be updated. Locked resumes cannot be updated. Requires authentication.", - "tags": ["Resumes"], - "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }], - "requestBody": { - "required": false, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "description": "The name of the resume." - }, - "slug": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "description": "The slug of the resume." - }, - "tags": { "type": "array", "items": { "type": "string" }, "description": "The tags of the resume." }, - "data": { "$ref": "#/components/schemas/ResumeData" }, - "isPublic": { "type": "boolean", "description": "Whether the resume is public." } - }, - "required": [] - } - } - } - }, - "responses": { - "200": { - "description": "The updated resume with its full data.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "id": { "type": "string", "description": "The ID of the resume." }, - "name": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "description": "The name of the resume." - }, - "slug": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "description": "The slug of the resume." - }, - "tags": { - "type": "array", - "items": { "type": "string" }, - "description": "The tags of the resume." - }, - "isPublic": { "type": "boolean", "description": "Whether the resume is public." }, - "isLocked": { "type": "boolean", "description": "Whether the resume is locked." }, - "data": { "$ref": "#/components/schemas/ResumeData" } - }, - "required": ["id", "name", "slug", "tags", "isPublic", "isLocked", "data"] - } - } - } - }, - "400": { - "description": "400", - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "type": "object", - "properties": { - "defined": { "const": true }, - "code": { "const": "RESUME_SLUG_ALREADY_EXISTS" }, - "status": { "const": 400 }, - "message": { "type": "string", "default": "A resume with this slug already exists." }, - "data": {} - }, - "required": ["defined", "code", "status", "message"] - }, - { - "type": "object", - "properties": { - "defined": { "const": false }, - "code": { "type": "string" }, - "status": { "type": "number" }, - "message": { "type": "string" }, - "data": {} - }, - "required": ["defined", "code", "status", "message"] - } - ] - } - } - } - } - } - }, - "patch": { - "operationId": "patchResume", - "summary": "Patch resume data", - "description": "Applies JSON Patch (RFC 6902) operations to partially update a resume's data. This allows small, targeted changes (e.g. updating a single field) without sending the entire resume object. Locked resumes cannot be patched. Requires authentication.", - "tags": ["Resumes"], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { "type": "string", "description": "The ID of the resume to patch." } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "operations": { - "type": "array", - "minItems": 1, - "items": { - "anyOf": [ - { - "type": "object", - "properties": { "op": { "const": "add" }, "path": { "type": "string" }, "value": {} }, - "required": ["op", "path"] - }, - { - "type": "object", - "properties": { "op": { "const": "remove" }, "path": { "type": "string" } }, - "required": ["op", "path"] - }, - { - "type": "object", - "properties": { "op": { "const": "replace" }, "path": { "type": "string" }, "value": {} }, - "required": ["op", "path"] - }, - { - "type": "object", - "properties": { - "op": { "const": "move" }, - "path": { "type": "string" }, - "from": { "type": "string" } - }, - "required": ["op", "path", "from"] - }, - { - "type": "object", - "properties": { - "op": { "const": "copy" }, - "path": { "type": "string" }, - "from": { "type": "string" } - }, - "required": ["op", "path", "from"] - }, - { - "type": "object", - "properties": { "op": { "const": "test" }, "path": { "type": "string" }, "value": {} }, - "required": ["op", "path"] - } - ] - }, - "description": "An array of JSON Patch (RFC 6902) operations to apply to the resume data." - } - }, - "required": ["operations"] - } - } - } - }, - "responses": { - "200": { - "description": "The patched resume with its full data.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "id": { "type": "string", "description": "The ID of the resume." }, - "name": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "description": "The name of the resume." - }, - "slug": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "description": "The slug of the resume." - }, - "tags": { - "type": "array", - "items": { "type": "string" }, - "description": "The tags of the resume." - }, - "isPublic": { "type": "boolean", "description": "Whether the resume is public." }, - "isLocked": { "type": "boolean", "description": "Whether the resume is locked." }, - "data": { "$ref": "#/components/schemas/ResumeData" }, - "hasPassword": { "type": "boolean" } - }, - "required": ["id", "name", "slug", "tags", "isPublic", "isLocked", "data", "hasPassword"] - } - } - } - }, - "400": { - "description": "400", - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "type": "object", - "properties": { - "defined": { "const": true }, - "code": { "const": "INVALID_PATCH_OPERATIONS" }, - "status": { "const": 400 }, - "message": { - "type": "string", - "default": "The patch operations are invalid or produced an invalid resume." - }, - "data": {} - }, - "required": ["defined", "code", "status", "message"] - }, - { - "type": "object", - "properties": { - "defined": { "const": false }, - "code": { "type": "string" }, - "status": { "type": "number" }, - "message": { "type": "string" }, - "data": {} - }, - "required": ["defined", "code", "status", "message"] - } - ] - } - } - } - } - } - }, - "delete": { - "operationId": "deleteResume", - "summary": "Delete a resume", - "description": "Permanently deletes a resume and its associated files (screenshots, PDFs) from storage. Locked resumes cannot be deleted; unlock the resume first. Requires authentication.", - "tags": ["Resumes"], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { "type": "string", "description": "The ID of the resume." } - } - ], - "requestBody": { - "required": false, - "content": { "application/json": { "schema": { "type": "object", "properties": {}, "required": [] } } } - }, - "responses": { - "200": { - "description": "The resume and its associated files were deleted successfully.", - "content": { "application/json": { "schema": { "anyOf": [{ "not": {} }, { "not": {} }] } } } - } - } - } - }, - "/resumes/{username}/{slug}": { - "get": { - "operationId": "getResumeBySlug", - "summary": "Get public resume by username and slug", - "description": "Returns a publicly shared resume identified by the owner's username and the resume's slug. If the resume is password-protected and the viewer has not yet verified the password, a 401 error with code NEED_PASSWORD is returned. No authentication required for public resumes; if authenticated as the owner, private resumes are also accessible.", - "tags": ["Resume Sharing"], - "parameters": [ - { "name": "username", "in": "path", "required": true, "schema": { "type": "string" } }, - { "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } } - ], - "responses": { - "200": { - "description": "The public resume with its full data.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "id": { "type": "string", "description": "The ID of the resume." }, - "name": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "description": "The name of the resume." - }, - "slug": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "description": "The slug of the resume." - }, - "tags": { - "type": "array", - "items": { "type": "string" }, - "description": "The tags of the resume." - }, - "isPublic": { "type": "boolean", "description": "Whether the resume is public." }, - "isLocked": { "type": "boolean", "description": "Whether the resume is locked." }, - "data": { "$ref": "#/components/schemas/ResumeData" } - }, - "required": ["id", "name", "slug", "tags", "isPublic", "isLocked", "data"] - } - } - } - } - } - } - }, - "/resumes/import": { - "post": { - "operationId": "importResume", - "summary": "Import a resume", - "description": "Creates a new resume from an existing ResumeData object (e.g. from a previously exported JSON file). A random name and slug are generated automatically. Returns the ID of the imported resume. Requires authentication.", - "tags": ["Resumes"], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { "data": { "$ref": "#/components/schemas/ResumeData" } }, - "required": ["data"] - } - } - } - }, - "responses": { - "200": { - "description": "The ID of the imported resume.", - "content": { - "application/json": { "schema": { "type": "string", "description": "The ID of the imported resume." } } - } - }, - "400": { - "description": "400", - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "type": "object", - "properties": { - "defined": { "const": true }, - "code": { "const": "RESUME_SLUG_ALREADY_EXISTS" }, - "status": { "const": 400 }, - "message": { "type": "string", "default": "A resume with this slug already exists." }, - "data": {} - }, - "required": ["defined", "code", "status", "message"] - }, - { - "type": "object", - "properties": { - "defined": { "const": false }, - "code": { "type": "string" }, - "status": { "type": "number" }, - "message": { "type": "string" }, - "data": {} - }, - "required": ["defined", "code", "status", "message"] - } - ] - } - } - } - } - } - } - }, - "/resumes/{id}/lock": { - "post": { - "operationId": "setResumeLocked", - "summary": "Set resume lock status", - "description": "Toggles the locked status of a resume. When locked, a resume cannot be updated, patched, or deleted. Useful for protecting finalized resumes from accidental edits. Requires authentication.", - "tags": ["Resumes"], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { "type": "string", "description": "The ID of the resume." } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { "isLocked": { "type": "boolean", "description": "Whether the resume is locked." } }, - "required": ["isLocked"] - } - } - } - }, - "responses": { - "200": { - "description": "The resume lock status was updated successfully.", - "content": { "application/json": { "schema": { "anyOf": [{ "not": {} }, { "not": {} }] } } } - } - } - } - }, - "/resumes/{id}/password": { - "put": { - "operationId": "setResumePassword", - "summary": "Set resume password", - "description": "Sets or updates a password on a resume. When a password is set, viewers of the public resume must enter the password before the resume data is revealed. The password must be between 6 and 64 characters. Requires authentication.", - "tags": ["Resume Sharing"], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { "type": "string", "description": "The ID of the resume." } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { "password": { "type": "string", "minLength": 6, "maxLength": 64 } }, - "required": ["password"] - } - } - } - }, - "responses": { - "200": { - "description": "The resume password was set successfully.", - "content": { "application/json": { "schema": { "anyOf": [{ "not": {} }, { "not": {} }] } } } - } - } - }, - "delete": { - "operationId": "removeResumePassword", - "summary": "Remove resume password", - "description": "Removes password protection from a resume. After removal, the resume (if public) can be viewed without entering a password. Requires authentication.", - "tags": ["Resume Sharing"], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { "type": "string", "description": "The ID of the resume." } - } - ], - "requestBody": { - "required": false, - "content": { "application/json": { "schema": { "type": "object", "properties": {}, "required": [] } } } - }, - "responses": { - "200": { - "description": "The resume password was removed successfully.", - "content": { "application/json": { "schema": { "anyOf": [{ "not": {} }, { "not": {} }] } } } - } - } - } - }, - "/resumes/{username}/{slug}/password/verify": { - "post": { - "operationId": "verifyResumePassword", - "summary": "Verify resume password", - "description": "Verifies a password for a password-protected public resume. On success, the viewer is granted access to view the resume data for the duration of their session. No authentication required.", - "tags": ["Resume Sharing"], - "parameters": [ - { - "name": "username", - "in": "path", - "required": true, - "schema": { "type": "string", "minLength": 1, "description": "The username of the resume owner." } - }, - { - "name": "slug", - "in": "path", - "required": true, - "schema": { "type": "string", "minLength": 1, "description": "The slug of the resume." } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "password": { "type": "string", "minLength": 1, "description": "The password to verify." } - }, - "required": ["password"] - } - } - } - }, - "responses": { - "200": { - "description": "The password was verified successfully and access has been granted.", - "content": { "application/json": { "schema": { "type": "boolean" } } } - } - } - } - }, - "/resumes/{id}/duplicate": { - "post": { - "operationId": "duplicateResume", - "summary": "Duplicate a resume", - "description": "Creates a copy of an existing resume with the same data. Optionally override the name, slug, and tags for the duplicate. If not provided, the original resume's name, slug, and tags are used. Returns the ID of the duplicated resume. Requires authentication.", - "tags": ["Resumes"], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { "type": "string", "description": "The ID of the resume." } - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "description": "The name of the resume." - }, - "slug": { - "type": "string", - "minLength": 1, - "maxLength": 64, - "description": "The slug of the resume." - }, - "tags": { "type": "array", "items": { "type": "string" }, "description": "The tags of the resume." } - }, - "required": ["name", "slug", "tags"] - } - } - } - }, - "responses": { - "200": { - "description": "The ID of the duplicated resume.", - "content": { - "application/json": { "schema": { "type": "string", "description": "The ID of the duplicated resume." } } - } - } - } - } - }, - "/resumes/{id}/pdf": { - "get": { - "operationId": "exportResumePdf", - "summary": "Export resume as PDF", - "description": "Generates a PDF from the specified resume and uploads it to storage. Returns a URL to download the generated PDF file. If the request is made by an unauthenticated user (e.g. via a public share link), the resume's download count is incremented. Authentication is optional.", - "tags": ["Resume Export"], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { "type": "string", "description": "The unique identifier of the resume to export." } - } - ], - "responses": { - "200": { - "description": "The PDF was generated successfully. Returns a URL to download the file.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "url": { "type": "string", "description": "The URL to download the generated PDF file." } - }, - "required": ["url"] - } - } - } - } - } - } - }, - "/resumes/{id}/screenshot": { - "get": { - "operationId": "getResumeScreenshot", - "summary": "Get resume screenshot", - "description": "Returns a URL to a screenshot image of the first page of the specified resume. Screenshots are cached for up to 6 hours and regenerated automatically when the resume is updated. Returns null if the screenshot cannot be generated. Requires authentication.", - "tags": ["Resume Export"], - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { "type": "string", "description": "The unique identifier of the resume." } - } - ], - "responses": { - "200": { - "description": "The screenshot URL, or null if the screenshot could not be generated.", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "url": { - "anyOf": [{ "type": "string" }, { "type": "null" }], - "description": "The URL to the screenshot image, or null." - } - }, - "required": ["url"] - } - } - } - } - } - } - }, - "/statistics/users": { - "get": { - "operationId": "getUserCount", - "summary": "Get total number of users", - "description": "Returns the total number of registered users on this Reactive Resume instance. The count is cached for up to 6 hours for performance. No authentication required.", - "tags": ["Platform Statistics"], - "responses": { - "200": { - "description": "The total number of registered users.", - "content": { - "application/json": { - "schema": { "type": "number", "description": "The total number of registered users." } - } - } - } - } - } - }, - "/statistics/resumes": { - "get": { - "operationId": "getResumeCount", - "summary": "Get total number of resumes", - "description": "Returns the total number of resumes created on this Reactive Resume instance. The count is cached for up to 6 hours for performance. No authentication required.", - "tags": ["Platform Statistics"], - "responses": { - "200": { - "description": "The total number of resumes created.", - "content": { - "application/json": { - "schema": { "type": "number", "description": "The total number of resumes created." } - } - } - } - } - } - }, - "/statistics/github/stars": { - "get": { - "operationId": "getGitHubStarCount", - "summary": "Get GitHub star count", - "description": "Returns the number of GitHub stars for the Reactive Resume repository. The count is cached for up to 6 hours and falls back to a last-known value if the GitHub API is unavailable. No authentication required.", - "tags": ["Platform Statistics"], - "responses": { - "200": { - "description": "The number of GitHub stars for the Reactive Resume repository.", - "content": { - "application/json": { "schema": { "type": "number", "description": "The number of GitHub stars." } } - } - } - } - } - } - } + "info": { + "title": "Reactive Resume", + "version": "5.0.9", + "description": "Reactive Resume API", + "license": { "name": "MIT", "url": "https://github.com/amruthpillai/reactive-resume/blob/main/LICENSE" }, + "contact": { "name": "Amruth Pillai", "email": "hello@amruthpillai.com", "url": "https://amruthpillai.com" } + }, + "servers": [{ "url": "http://localhost:3000/api/openapi" }], + "externalDocs": { "url": "https://docs.rxresu.me", "description": "Reactive Resume Documentation" }, + "components": { + "securitySchemes": { + "apiKey": { + "type": "apiKey", + "name": "x-api-key", + "in": "header", + "description": "The API key to authenticate requests." + } + }, + "schemas": { + "ResumeData": { + "type": "object", + "properties": { + "picture": { + "type": "object", + "properties": { + "hidden": { "type": "boolean", "description": "Whether to hide the picture from the resume." }, + "url": { + "type": "string", + "description": "The URL to the picture to display on the resume. Must be a valid URL with a protocol (http:// or https://)." + }, + "size": { + "type": "number", + "minimum": 32, + "maximum": 512, + "description": "The size of the picture to display on the resume, defined in points (pt)." + }, + "rotation": { + "type": "number", + "minimum": 0, + "maximum": 360, + "description": "The rotation of the picture to display on the resume, defined in degrees (°)." + }, + "aspectRatio": { + "type": "number", + "minimum": 0.5, + "maximum": 2.5, + "description": "The aspect ratio of the picture to display on the resume, defined as width / height (e.g. 1.5 for 1.5:1 or 0.5 for 1:2)." + }, + "borderRadius": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "The border radius of the picture to display on the resume, defined in points (pt)." + }, + "borderColor": { + "type": "string", + "description": "The color of the border of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "borderWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the border of the picture to display on the resume, defined in points (pt)." + }, + "shadowColor": { + "type": "string", + "description": "The color of the shadow of the picture to display on the resume, defined as rgba(r, g, b, a)." + }, + "shadowWidth": { + "type": "number", + "minimum": 0, + "description": "The width of the shadow of the picture to display on the resume, defined in points (pt)." + } + }, + "required": [ + "hidden", + "url", + "size", + "rotation", + "aspectRatio", + "borderRadius", + "borderColor", + "borderWidth", + "shadowColor", + "shadowWidth" + ], + "description": "Configuration for photograph displayed on the resume" + }, + "basics": { + "type": "object", + "properties": { + "name": { "type": "string", "description": "The full name of the author of the resume." }, + "headline": { "type": "string", "description": "The headline of the author of the resume." }, + "email": { "type": "string", "description": "The email address of the author of the resume." }, + "phone": { "type": "string", "description": "The phone number of the author of the resume." }, + "location": { "type": "string", "description": "The location of the author of the resume." }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "description": "The website of the author of the resume." + }, + "customFields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the custom field. Usually generated as a UUID." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "text": { "type": "string", "description": "The text to display for the custom field." }, + "link": { + "type": "string", + "description": "If the custom field should be a link, the URL to link to." + } + }, + "required": ["id", "icon", "text", "link"] + }, + "description": "The custom fields to display on the resume." + } + }, + "required": ["name", "headline", "email", "phone", "location", "website", "customFields"], + "description": "Basic information about the author, such as name, email, phone, location, and website" + }, + "summary": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the summary of the resume." }, + "columns": { "type": "number", "description": "The number of columns the summary should span across." }, + "hidden": { "type": "boolean", "description": "Whether to hide the summary from the resume." }, + "content": { + "type": "string", + "description": "The content of the summary of the resume. This should be a HTML-formatted string." + } + }, + "required": ["title", "columns", "hidden", "content"], + "description": "Summary section of the resume, useful for a short bio or introduction" + }, + "sections": { + "type": "object", + "properties": { + "profiles": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "network": { + "type": "string", + "minLength": 1, + "description": "The name of the network or platform." + }, + "username": { + "type": "string", + "description": "The username of the author on the network or platform." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "description": "The link to the profile of the author on the network or platform, if any." + } + }, + "required": ["id", "hidden", "icon", "network", "username", "website"] + }, + "description": "The items to display in the profiles section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "description": "The section to display the profiles of the author." + }, + "experience": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "company": { + "type": "string", + "minLength": 1, + "description": "The name of the company or organization." + }, + "position": { + "type": "string", + "description": "The position held at the company or organization." + }, + "location": { "type": "string", "description": "The location of the company or organization." }, + "period": { + "type": "string", + "description": "The period of time the author was employed at the company or organization." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "description": "The website of the company or organization, if any." + }, + "description": { + "type": "string", + "description": "The description of the experience. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "company", + "position", + "location", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the experience section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "description": "The section to display the experience of the author." + }, + "education": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "school": { + "type": "string", + "minLength": 1, + "description": "The name of the school or institution." + }, + "degree": { "type": "string", "description": "The degree or qualification obtained." }, + "area": { "type": "string", "description": "The area of study or specialization." }, + "grade": { "type": "string", "description": "The grade or score achieved." }, + "location": { "type": "string", "description": "The location of the school or institution." }, + "period": { + "type": "string", + "description": "The period of time the education was obtained over." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "description": "The website of the school or institution, if any." + }, + "description": { + "type": "string", + "description": "The description of the education. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "school", + "degree", + "area", + "grade", + "location", + "period", + "website", + "description" + ] + }, + "description": "The items to display in the education section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "description": "The section to display the education of the author." + }, + "projects": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "name": { "type": "string", "minLength": 1, "description": "The name of the project." }, + "period": { "type": "string", "description": "The period of time the project was worked on." }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "description": "The link to the project, if any." + }, + "description": { + "type": "string", + "description": "The description of the project. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "name", "period", "website", "description"] + }, + "description": "The items to display in the projects section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "description": "The section to display the projects of the author." + }, + "skills": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "name": { "type": "string", "minLength": 1, "description": "The name of the skill." }, + "proficiency": { + "type": "string", + "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + }, + "keywords": { + "type": "array", + "items": { "type": "string" }, + "description": "The keywords associated with the skill, if any. These are displayed as tags below the name." + } + }, + "required": ["id", "hidden", "icon", "name", "proficiency", "level", "keywords"] + }, + "description": "The items to display in the skills section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "description": "The section to display the skills of the author." + }, + "languages": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "language": { + "type": "string", + "minLength": 1, + "description": "The name of the language the author knows." + }, + "fluency": { + "type": "string", + "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + } + }, + "required": ["id", "hidden", "language", "fluency", "level"] + }, + "description": "The items to display in the languages section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "description": "The section to display the languages of the author." + }, + "interests": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "name": { "type": "string", "minLength": 1, "description": "The name of the interest/hobby." }, + "keywords": { + "type": "array", + "items": { "type": "string" }, + "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name." + } + }, + "required": ["id", "hidden", "icon", "name", "keywords"] + }, + "description": "The items to display in the interests section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "description": "The section to display the interests of the author." + }, + "awards": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "title": { "type": "string", "minLength": 1, "description": "The title of the award." }, + "awarder": { "type": "string", "description": "The awarder of the award." }, + "date": { "type": "string", "description": "The date when the award was received." }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "description": "The website of the award, if any." + }, + "description": { + "type": "string", + "description": "The description of the award. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "title", "awarder", "date", "website", "description"] + }, + "description": "The items to display in the awards section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "description": "The section to display the awards of the author." + }, + "certifications": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "title": { "type": "string", "minLength": 1, "description": "The title of the certification." }, + "issuer": { "type": "string", "description": "The issuer of the certification." }, + "date": { "type": "string", "description": "The date when the certification was received." }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "description": "The website of the certification, if any." + }, + "description": { + "type": "string", + "description": "The description of the certification. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "title", "issuer", "date", "website", "description"] + }, + "description": "The items to display in the certifications section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "description": "The section to display the certifications of the author." + }, + "publications": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "title": { "type": "string", "minLength": 1, "description": "The title of the publication." }, + "publisher": { "type": "string", "description": "The publisher of the publication." }, + "date": { "type": "string", "description": "The date when the publication was published." }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "description": "The link to the publication, if any." + }, + "description": { + "type": "string", + "description": "The description of the publication. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "title", "publisher", "date", "website", "description"] + }, + "description": "The items to display in the publications section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "description": "The section to display the publications of the author." + }, + "volunteer": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "organization": { + "type": "string", + "minLength": 1, + "description": "The name of the organization or company." + }, + "location": { "type": "string", "description": "The location of the organization or company." }, + "period": { + "type": "string", + "description": "The period of time the author was volunteered at the organization or company." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "description": "The link to the organization or company, if any." + }, + "description": { + "type": "string", + "description": "The description of the volunteer experience. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "organization", "location", "period", "website", "description"] + }, + "description": "The items to display in the volunteer section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "description": "The section to display the volunteer experience of the author." + }, + "references": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { + "type": "number", + "description": "The number of columns the section should span across." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the reference, or a note such as 'Available upon request'." + }, + "position": { "type": "string", "description": "The position or job title of the reference." }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "description": "The website or LinkedIn profile of the reference, if any." + }, + "phone": { "type": "string", "description": "The phone number of the reference." }, + "description": { + "type": "string", + "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "name", "position", "website", "phone", "description"] + }, + "description": "The items to display in the references section." + } + }, + "required": ["title", "columns", "hidden", "items"], + "description": "The section to display the references of the author." + } + }, + "required": [ + "profiles", + "experience", + "education", + "projects", + "skills", + "languages", + "interests", + "awards", + "certifications", + "publications", + "volunteer", + "references" + ], + "description": "Various sections of the resume, such as experience, education, projects, etc." + }, + "customSections": { + "type": "array", + "items": { + "type": "object", + "properties": { + "title": { "type": "string", "description": "The title of the section." }, + "columns": { "type": "number", "description": "The number of columns the section should span across." }, + "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "id": { + "type": "string", + "description": "The unique identifier for the custom section. Usually generated as a UUID." + }, + "type": { + "enum": [ + "summary", + "profiles", + "experience", + "education", + "projects", + "skills", + "languages", + "interests", + "awards", + "certifications", + "publications", + "volunteer", + "references", + "cover-letter" + ], + "description": "The type of items this custom section contains. Determines which item schema and form fields to use." + }, + "items": { + "type": "array", + "items": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "recipient": { + "type": "string", + "description": "The recipient's address block as HTML (name, title, company, address, email)." + }, + "content": { + "type": "string", + "description": "The cover letter body as HTML (salutation, paragraphs, closing, signature)." + } + }, + "required": ["id", "hidden", "recipient", "content"] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "content": { + "type": "string", + "description": "The rich text content of the summary item. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "content"] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "network": { + "type": "string", + "minLength": 1, + "description": "The name of the network or platform." + }, + "username": { + "type": "string", + "description": "The username of the author on the network or platform." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "description": "The link to the profile of the author on the network or platform, if any." + } + }, + "required": ["id", "hidden", "icon", "network", "username", "website"] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "company": { + "type": "string", + "minLength": 1, + "description": "The name of the company or organization." + }, + "position": { + "type": "string", + "description": "The position held at the company or organization." + }, + "location": { + "type": "string", + "description": "The location of the company or organization." + }, + "period": { + "type": "string", + "description": "The period of time the author was employed at the company or organization." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "description": "The website of the company or organization, if any." + }, + "description": { + "type": "string", + "description": "The description of the experience. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "company", + "position", + "location", + "period", + "website", + "description" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "school": { + "type": "string", + "minLength": 1, + "description": "The name of the school or institution." + }, + "degree": { "type": "string", "description": "The degree or qualification obtained." }, + "area": { "type": "string", "description": "The area of study or specialization." }, + "grade": { "type": "string", "description": "The grade or score achieved." }, + "location": { "type": "string", "description": "The location of the school or institution." }, + "period": { + "type": "string", + "description": "The period of time the education was obtained over." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "description": "The website of the school or institution, if any." + }, + "description": { + "type": "string", + "description": "The description of the education. This should be a HTML-formatted string." + } + }, + "required": [ + "id", + "hidden", + "school", + "degree", + "area", + "grade", + "location", + "period", + "website", + "description" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "name": { "type": "string", "minLength": 1, "description": "The name of the project." }, + "period": { + "type": "string", + "description": "The period of time the project was worked on." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "description": "The link to the project, if any." + }, + "description": { + "type": "string", + "description": "The description of the project. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "name", "period", "website", "description"] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "name": { "type": "string", "minLength": 1, "description": "The name of the skill." }, + "proficiency": { + "type": "string", + "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the skill, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + }, + "keywords": { + "type": "array", + "items": { "type": "string" }, + "description": "The keywords associated with the skill, if any. These are displayed as tags below the name." + } + }, + "required": ["id", "hidden", "icon", "name", "proficiency", "level", "keywords"] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "language": { + "type": "string", + "minLength": 1, + "description": "The name of the language the author knows." + }, + "fluency": { + "type": "string", + "description": "The fluency level of the language. Can be any text, such as 'Native', 'Fluent', 'Conversational', etc. or can also be a CEFR level (A1, A2, B1, B2, C1, C2)." + }, + "level": { + "type": "number", + "minimum": 0, + "maximum": 5, + "description": "The proficiency level of the language, defined as a number between 0 and 5. If set to 0, the icons displaying the level will be hidden." + } + }, + "required": ["id", "hidden", "language", "fluency", "level"] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the interest/hobby." + }, + "keywords": { + "type": "array", + "items": { "type": "string" }, + "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name." + } + }, + "required": ["id", "hidden", "icon", "name", "keywords"] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "title": { "type": "string", "minLength": 1, "description": "The title of the award." }, + "awarder": { "type": "string", "description": "The awarder of the award." }, + "date": { "type": "string", "description": "The date when the award was received." }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "description": "The website of the award, if any." + }, + "description": { + "type": "string", + "description": "The description of the award. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "title", "awarder", "date", "website", "description"] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "title": { + "type": "string", + "minLength": 1, + "description": "The title of the certification." + }, + "issuer": { "type": "string", "description": "The issuer of the certification." }, + "date": { "type": "string", "description": "The date when the certification was received." }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "description": "The website of the certification, if any." + }, + "description": { + "type": "string", + "description": "The description of the certification. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "title", "issuer", "date", "website", "description"] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "title": { "type": "string", "minLength": 1, "description": "The title of the publication." }, + "publisher": { "type": "string", "description": "The publisher of the publication." }, + "date": { "type": "string", "description": "The date when the publication was published." }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "description": "The link to the publication, if any." + }, + "description": { + "type": "string", + "description": "The description of the publication. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "title", "publisher", "date", "website", "description"] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "organization": { + "type": "string", + "minLength": 1, + "description": "The name of the organization or company." + }, + "location": { + "type": "string", + "description": "The location of the organization or company." + }, + "period": { + "type": "string", + "description": "The period of time the author was volunteered at the organization or company." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "description": "The link to the organization or company, if any." + }, + "description": { + "type": "string", + "description": "The description of the volunteer experience. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "organization", "location", "period", "website", "description"] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The unique identifier for the item. Usually generated as a UUID." + }, + "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "options": { + "type": "object", + "properties": { + "showLinkInTitle": { + "type": "boolean", + "description": "If true, the website URL is rendered as a hyperlink on the title instead of a separate link at the bottom." + } + }, + "required": ["showLinkInTitle"], + "description": "Display options for this item." + }, + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the reference, or a note such as 'Available upon request'." + }, + "position": { + "type": "string", + "description": "The position or job title of the reference." + }, + "website": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "The URL to show as a link. Must be a valid URL with a protocol (http:// or https://)." + }, + "label": { + "type": "string", + "description": "The label to display for the URL. Leave blank to display the URL as-is." + } + }, + "required": ["url", "label"], + "description": "The website or LinkedIn profile of the reference, if any." + }, + "phone": { "type": "string", "description": "The phone number of the reference." }, + "description": { + "type": "string", + "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." + } + }, + "required": ["id", "hidden", "name", "position", "website", "phone", "description"] + } + ] + }, + "description": "The items to display in the custom section. Items follow the schema of the section type." + } + }, + "required": ["title", "columns", "hidden", "id", "type", "items"] + }, + "description": "Custom sections of the resume, such as a custom section for notes, etc." + }, + "metadata": { + "type": "object", + "properties": { + "template": { + "enum": [ + "azurill", + "bronzor", + "chikorita", + "ditgar", + "ditto", + "gengar", + "glalie", + "kakuna", + "lapras", + "leafish", + "onyx", + "pikachu", + "rhyhorn" + ], + "description": "The template to use for the resume. Determines the overall design and appearance of the resume." + }, + "layout": { + "type": "object", + "properties": { + "sidebarWidth": { + "type": "number", + "minimum": 10, + "maximum": 50, + "description": "The width of the sidebar column, defined as a percentage of the page width." + }, + "pages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fullWidth": { + "type": "boolean", + "description": "Whether the layout of the page should be full width. If true, the main column will span the entire width of the page. This means that there should be no items in the sidebar column." + }, + "main": { + "type": "array", + "items": { "type": "string" }, + "description": "The items to display in the main column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." + }, + "sidebar": { + "type": "array", + "items": { "type": "string" }, + "description": "The items to display in the sidebar column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." + } + }, + "required": ["fullWidth", "main", "sidebar"] + }, + "description": "The pages to display in the layout." + } + }, + "required": ["sidebarWidth", "pages"], + "description": "The layout of the resume. Determines the structure and arrangement of the sections on the resume." + }, + "css": { + "type": "object", + "properties": { + "enabled": { "type": "boolean", "description": "Whether to enable custom CSS for the resume." }, + "value": { + "type": "string", + "description": "The custom CSS to apply to the resume. This should be a valid CSS string." + } + }, + "required": ["enabled", "value"], + "description": "Custom CSS to apply to the resume. Can be used to override the default styles of the template." + }, + "page": { + "type": "object", + "properties": { + "gapX": { + "type": "number", + "minimum": 0, + "description": "The horizontal gap between the sections of the page, defined in points (pt)." + }, + "gapY": { + "type": "number", + "minimum": 0, + "description": "The vertical gap between the sections of the page, defined in points (pt)." + }, + "marginX": { + "type": "number", + "minimum": 0, + "description": "The horizontal margin of the page, defined in points (pt)." + }, + "marginY": { + "type": "number", + "minimum": 0, + "description": "The vertical margin of the page, defined in points (pt)." + }, + "format": { + "enum": ["a4", "letter", "free-form"], + "description": "The format of the page. Can be 'a4', 'letter' or 'free-form'." + }, + "locale": { + "type": "string", + "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden." + }, + "hideIcons": { "type": "boolean", "description": "Whether to hide the icons of the sections." } + }, + "required": ["gapX", "gapY", "marginX", "marginY", "format", "locale", "hideIcons"], + "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." + }, + "design": { + "type": "object", + "properties": { + "level": { + "type": "object", + "properties": { + "icon": { + "type": "string", + "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." + }, + "type": { + "enum": ["hidden", "circle", "square", "rectangle", "rectangle-full", "progress-bar", "icon"], + "description": "The type of the level design. 'hidden' will hide the level design, 'circle' will display a circle, 'square' will display a square, 'rectangle' will display a rectangle, 'rectangle-full' will display a full rectangle, 'progress-bar' will display a progress bar, and 'icon' will display an icon. If 'icon' is selected, the icon to display should be specified in the 'icon' field." + } + }, + "required": ["icon", "type"] + }, + "colors": { + "type": "object", + "properties": { + "primary": { + "type": "string", + "description": "The primary color of the design, defined as rgba(r, g, b, a)." + }, + "text": { + "type": "string", + "description": "The text color of the design, defined as rgba(r, g, b, a). Usually set to black: rgba(0, 0, 0, 1)." + }, + "background": { + "type": "string", + "description": "The background color of the design, defined as rgba(r, g, b, a). Usually set to white: rgba(255, 255, 255, 1)." + } + }, + "required": ["primary", "text", "background"] + } + }, + "required": ["level", "colors"], + "description": "The design settings of the resume. Determines the colors, level designs, and typography of the resume." + }, + "typography": { + "type": "object", + "properties": { + "body": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a font that is available on Google Fonts." + }, + "fontWeights": { + "type": "array", + "items": { "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": ["fontFamily", "fontWeights", "fontSize", "lineHeight"], + "description": "The typography for the body of the resume." + }, + "heading": { + "type": "object", + "properties": { + "fontFamily": { + "type": "string", + "description": "The family of the font to use. Must be a font that is available on Google Fonts." + }, + "fontWeights": { + "type": "array", + "items": { "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] }, + "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font." + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 24, + "description": "The size of the font to use, defined in points (pt)." + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4, + "description": "The line height of the font to use, defined as a multiplier of the font size (e.g. 1.5 for 1.5x)." + } + }, + "required": ["fontFamily", "fontWeights", "fontSize", "lineHeight"], + "description": "The typography for the headings of the resume." + } + }, + "required": ["body", "heading"], + "description": "The typography settings of the resume. Determines the fonts and sizes of the body and headings of the resume." + }, + "notes": { + "type": "string", + "description": "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." + } + }, + "required": ["template", "layout", "css", "page", "design", "typography", "notes"], + "description": "Metadata for the resume, such as template, layout, typography, etc. This section describes the overall design and appearance of the resume." + } + }, + "required": ["picture", "basics", "summary", "sections", "customSections", "metadata"] + } + } + }, + "security": [{ "apiKey": [] }], + "openapi": "3.1.1", + "paths": { + "/ai/test-connection": { + "post": { + "operationId": "testAiConnection", + "summary": "Test AI provider connection", + "description": "Validates the connection to an AI provider by sending a simple test prompt. Requires the provider type, model name, API key, and an optional base URL. Supported providers: OpenAI, Anthropic, Google Gemini, Ollama, and Vercel AI Gateway. Requires authentication.", + "tags": ["AI"], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "provider": { "enum": ["ollama", "openai", "gemini", "anthropic", "vercel-ai-gateway"] }, + "model": { "type": "string" }, + "apiKey": { "type": "string" }, + "baseURL": { "type": "string" } + }, + "required": ["provider", "model", "apiKey", "baseURL"] + } + } + } + }, + "responses": { + "200": { + "description": "The AI provider connection was successful.", + "content": { "application/json": { "schema": { "anyOf": [{}, { "not": {} }] } } } + }, + "502": { + "description": "502", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { "const": true }, + "code": { "const": "BAD_GATEWAY" }, + "status": { "const": 502 }, + "message": { + "type": "string", + "default": "The AI provider returned an error or is unreachable." + }, + "data": {} + }, + "required": ["defined", "code", "status", "message"] + }, + { + "type": "object", + "properties": { + "defined": { "const": false }, + "code": { "type": "string" }, + "status": { "type": "number" }, + "message": { "type": "string" }, + "data": {} + }, + "required": ["defined", "code", "status", "message"] + } + ] + } + } + } + } + } + } + }, + "/ai/parse-pdf": { + "post": { + "operationId": "parseResumePdf", + "summary": "Parse a PDF file into resume data", + "description": "Extracts structured resume data from a PDF file using the specified AI provider. The file should be sent as a base64-encoded string along with AI provider credentials. Returns a complete ResumeData object. Requires authentication.", + "tags": ["AI"], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "provider": { "enum": ["ollama", "openai", "gemini", "anthropic", "vercel-ai-gateway"] }, + "model": { "type": "string" }, + "apiKey": { "type": "string" }, + "baseURL": { "type": "string" }, + "file": { + "type": "object", + "properties": { "name": { "type": "string" }, "data": { "type": "string" } }, + "required": ["name", "data"] + } + }, + "required": ["provider", "model", "apiKey", "baseURL", "file"] + } + } + } + }, + "responses": { + "200": { + "description": "The PDF was successfully parsed into structured resume data.", + "content": { "application/json": { "schema": { "anyOf": [{}, { "not": {} }] } } } + }, + "502": { + "description": "502", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { "const": true }, + "code": { "const": "BAD_GATEWAY" }, + "status": { "const": 502 }, + "message": { + "type": "string", + "default": "The AI provider returned an error or is unreachable." + }, + "data": {} + }, + "required": ["defined", "code", "status", "message"] + }, + { + "type": "object", + "properties": { + "defined": { "const": false }, + "code": { "type": "string" }, + "status": { "type": "number" }, + "message": { "type": "string" }, + "data": {} + }, + "required": ["defined", "code", "status", "message"] + } + ] + } + } + } + } + } + } + }, + "/ai/parse-docx": { + "post": { + "operationId": "parseResumeDocx", + "summary": "Parse a DOCX file into resume data", + "description": "Extracts structured resume data from a DOCX or DOC file using the specified AI provider. The file should be sent as a base64-encoded string along with AI provider credentials and the document's media type. Returns a complete ResumeData object. Requires authentication.", + "tags": ["AI"], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "provider": { "enum": ["ollama", "openai", "gemini", "anthropic", "vercel-ai-gateway"] }, + "model": { "type": "string" }, + "apiKey": { "type": "string" }, + "baseURL": { "type": "string" }, + "file": { + "type": "object", + "properties": { "name": { "type": "string" }, "data": { "type": "string" } }, + "required": ["name", "data"] + }, + "mediaType": { + "enum": [ + "application/msword", + "application/vnd.openxmlformats-officedocument.wordprocessingml.document" + ] + } + }, + "required": ["provider", "model", "apiKey", "baseURL", "file", "mediaType"] + } + } + } + }, + "responses": { + "200": { + "description": "The DOCX was successfully parsed into structured resume data.", + "content": { "application/json": { "schema": { "anyOf": [{}, { "not": {} }] } } } + }, + "502": { + "description": "502", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { "const": true }, + "code": { "const": "BAD_GATEWAY" }, + "status": { "const": 502 }, + "message": { + "type": "string", + "default": "The AI provider returned an error or is unreachable." + }, + "data": {} + }, + "required": ["defined", "code", "status", "message"] + }, + { + "type": "object", + "properties": { + "defined": { "const": false }, + "code": { "type": "string" }, + "status": { "type": "number" }, + "message": { "type": "string" }, + "data": {} + }, + "required": ["defined", "code", "status", "message"] + } + ] + } + } + } + } + } + } + }, + "/ai/chat": { + "post": { + "operationId": "aiChat", + "summary": "Chat with AI to modify resume", + "description": "Streams a chat response from the configured AI provider. The LLM can call the patch_resume tool to generate JSON Patch operations that modify the resume. Requires authentication and AI provider credentials.", + "tags": ["AI"], + "responses": { + "200": { + "description": "OK", + "content": { "application/json": { "schema": { "anyOf": [{}, { "not": {} }] } } } + } + } + } + }, + "/auth/providers": { + "get": { + "operationId": "listAuthProviders", + "summary": "List authentication providers", + "description": "Returns a list of all authentication providers enabled on this Reactive Resume instance, along with their display names. Possible providers include password-based credentials, Google, GitHub, and custom OAuth. No authentication required.", + "tags": ["Authentication"], + "responses": { + "200": { + "description": "A map of enabled authentication provider identifiers to their display names.", + "content": { "application/json": { "schema": { "anyOf": [{}, { "not": {} }] } } } + } + } + } + }, + "/auth/account": { + "delete": { + "operationId": "deleteAccount", + "summary": "Delete user account", + "description": "Permanently deletes the authenticated user's account, including all resumes, uploaded files (profile pictures, screenshots, PDFs), and associated data. This action is irreversible. Requires authentication.", + "tags": ["Authentication"], + "responses": { + "200": { + "description": "The user account and all associated data have been successfully deleted.", + "content": { "application/json": { "schema": { "anyOf": [{}, { "not": {} }] } } } + } + } + } + }, + "/flags": { + "get": { + "operationId": "getFeatureFlags", + "summary": "Get feature flags", + "description": "Returns the current feature flags for this Reactive Resume instance. Feature flags control instance-wide settings such as whether new user signups or email-based authentication are disabled. No authentication required.", + "tags": ["Feature Flags"], + "responses": { + "200": { + "description": "The current feature flags for this instance.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "disableSignups": { + "type": "boolean", + "description": "Whether new user signups are disabled on this instance." + }, + "disableEmailAuth": { + "type": "boolean", + "description": "Whether email-based authentication is disabled on this instance." + } + }, + "required": ["disableSignups", "disableEmailAuth"] + } + } + } + } + } + } + }, + "/resumes/tags": { + "get": { + "operationId": "listResumeTags", + "summary": "List all resume tags", + "description": "Returns a sorted list of all unique tags across the authenticated user's resumes. Useful for populating tag filters in the dashboard. Requires authentication.", + "tags": ["Resumes"], + "responses": { + "200": { + "description": "A sorted array of unique tag strings.", + "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string" } } } } + } + } + } + }, + "/resumes/{id}/statistics": { + "get": { + "operationId": "getResumeStatistics", + "summary": "Get resume statistics", + "description": "Returns view and download statistics for the specified resume, including total counts and the timestamps of the last view and download. Requires authentication.", + "tags": ["Resume Statistics"], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { "type": "string", "description": "The unique identifier of the resume." } + } + ], + "responses": { + "200": { + "description": "The resume's view and download statistics.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "isPublic": { "type": "boolean", "description": "Whether the resume is currently public." }, + "views": { "type": "number", "description": "Total number of times the resume has been viewed." }, + "downloads": { + "type": "number", + "description": "Total number of times the resume has been downloaded." + }, + "lastViewedAt": { + "anyOf": [ + { "type": "string", "format": "date-time", "x-native-type": "date" }, + { "type": "null" } + ], + "description": "Timestamp of the last view, or null if never viewed." + }, + "lastDownloadedAt": { + "anyOf": [ + { "type": "string", "format": "date-time", "x-native-type": "date" }, + { "type": "null" } + ], + "description": "Timestamp of the last download, or null if never downloaded." + } + }, + "required": ["isPublic", "views", "downloads", "lastViewedAt", "lastDownloadedAt"] + } + } + } + } + } + } + }, + "/resumes": { + "get": { + "operationId": "listResumes", + "summary": "List all resumes", + "description": "Returns a list of all resumes belonging to the authenticated user. Results can be filtered by tags and sorted by last updated date, creation date, or name. Resume data is not included in the response for performance; use the get endpoint to fetch full resume data. Requires authentication.", + "tags": ["Resumes"], + "parameters": [ + { + "name": "tags", + "in": "query", + "schema": { "type": "array", "items": { "type": "string" }, "default": [] }, + "style": "deepObject", + "explode": true, + "allowEmptyValue": true, + "allowReserved": true + }, + { + "name": "sort", + "in": "query", + "schema": { "enum": ["lastUpdatedAt", "createdAt", "name"], "default": "lastUpdatedAt" }, + "style": "deepObject", + "explode": true, + "allowEmptyValue": true, + "allowReserved": true + } + ], + "responses": { + "200": { + "description": "A list of resumes with their metadata (without full resume data).", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "string", "description": "The ID of the resume." }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "description": "The name of the resume." + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "description": "The slug of the resume." + }, + "tags": { + "type": "array", + "items": { "type": "string" }, + "description": "The tags of the resume." + }, + "isPublic": { "type": "boolean", "description": "Whether the resume is public." }, + "isLocked": { "type": "boolean", "description": "Whether the resume is locked." }, + "createdAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date", + "description": "The date and time the resume was created." + }, + "updatedAt": { + "type": "string", + "format": "date-time", + "x-native-type": "date", + "description": "The date and time the resume was last updated." + } + }, + "required": ["id", "name", "slug", "tags", "isPublic", "isLocked", "createdAt", "updatedAt"] + } + } + } + } + } + } + }, + "post": { + "operationId": "createResume", + "summary": "Create a new resume", + "description": "Creates a new resume with the given name, slug, and tags. Optionally initializes the resume with sample data by setting withSampleData to true. The slug must be unique across the user's resumes. Returns the ID of the newly created resume. Requires authentication.", + "tags": ["Resumes"], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "description": "The name of the resume." + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "description": "The slug of the resume." + }, + "tags": { "type": "array", "items": { "type": "string" }, "description": "The tags of the resume." }, + "withSampleData": { "type": "boolean", "default": false } + }, + "required": ["name", "slug", "tags"] + } + } + } + }, + "responses": { + "200": { + "description": "The ID of the newly created resume.", + "content": { + "application/json": { "schema": { "type": "string", "description": "The ID of the created resume." } } + } + }, + "400": { + "description": "400", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { "const": true }, + "code": { "const": "RESUME_SLUG_ALREADY_EXISTS" }, + "status": { "const": 400 }, + "message": { "type": "string", "default": "A resume with this slug already exists." }, + "data": {} + }, + "required": ["defined", "code", "status", "message"] + }, + { + "type": "object", + "properties": { + "defined": { "const": false }, + "code": { "type": "string" }, + "status": { "type": "number" }, + "message": { "type": "string" }, + "data": {} + }, + "required": ["defined", "code", "status", "message"] + } + ] + } + } + } + } + } + } + }, + "/resumes/{id}": { + "get": { + "operationId": "getResume", + "summary": "Get resume by ID", + "description": "Returns a single resume with its full data, identified by its unique ID. Only resumes belonging to the authenticated user can be retrieved. Requires authentication.", + "tags": ["Resumes"], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { "type": "string", "description": "The ID of the resume." } + } + ], + "responses": { + "200": { + "description": "The resume with its full data.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { "type": "string", "description": "The ID of the resume." }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "description": "The name of the resume." + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "description": "The slug of the resume." + }, + "tags": { + "type": "array", + "items": { "type": "string" }, + "description": "The tags of the resume." + }, + "isPublic": { "type": "boolean", "description": "Whether the resume is public." }, + "isLocked": { "type": "boolean", "description": "Whether the resume is locked." }, + "data": { "$ref": "#/components/schemas/ResumeData" }, + "hasPassword": { "type": "boolean" } + }, + "required": ["id", "name", "slug", "tags", "isPublic", "isLocked", "data", "hasPassword"] + } + } + } + } + } + }, + "put": { + "operationId": "updateResume", + "summary": "Update a resume", + "description": "Updates one or more fields of a resume identified by its ID. All fields are optional; only provided fields will be updated. Locked resumes cannot be updated. Requires authentication.", + "tags": ["Resumes"], + "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "description": "The name of the resume." + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "description": "The slug of the resume." + }, + "tags": { "type": "array", "items": { "type": "string" }, "description": "The tags of the resume." }, + "data": { "$ref": "#/components/schemas/ResumeData" }, + "isPublic": { "type": "boolean", "description": "Whether the resume is public." } + }, + "required": [] + } + } + } + }, + "responses": { + "200": { + "description": "The updated resume with its full data.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { "type": "string", "description": "The ID of the resume." }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "description": "The name of the resume." + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "description": "The slug of the resume." + }, + "tags": { + "type": "array", + "items": { "type": "string" }, + "description": "The tags of the resume." + }, + "isPublic": { "type": "boolean", "description": "Whether the resume is public." }, + "isLocked": { "type": "boolean", "description": "Whether the resume is locked." }, + "data": { "$ref": "#/components/schemas/ResumeData" } + }, + "required": ["id", "name", "slug", "tags", "isPublic", "isLocked", "data"] + } + } + } + }, + "400": { + "description": "400", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { "const": true }, + "code": { "const": "RESUME_SLUG_ALREADY_EXISTS" }, + "status": { "const": 400 }, + "message": { "type": "string", "default": "A resume with this slug already exists." }, + "data": {} + }, + "required": ["defined", "code", "status", "message"] + }, + { + "type": "object", + "properties": { + "defined": { "const": false }, + "code": { "type": "string" }, + "status": { "type": "number" }, + "message": { "type": "string" }, + "data": {} + }, + "required": ["defined", "code", "status", "message"] + } + ] + } + } + } + } + } + }, + "patch": { + "operationId": "patchResume", + "summary": "Patch resume data", + "description": "Applies JSON Patch (RFC 6902) operations to partially update a resume's data. This allows small, targeted changes (e.g. updating a single field) without sending the entire resume object. Locked resumes cannot be patched. Requires authentication.", + "tags": ["Resumes"], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { "type": "string", "description": "The ID of the resume to patch." } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "operations": { + "type": "array", + "minItems": 1, + "items": { + "anyOf": [ + { + "type": "object", + "properties": { "op": { "const": "add" }, "path": { "type": "string" }, "value": {} }, + "required": ["op", "path"] + }, + { + "type": "object", + "properties": { "op": { "const": "remove" }, "path": { "type": "string" } }, + "required": ["op", "path"] + }, + { + "type": "object", + "properties": { "op": { "const": "replace" }, "path": { "type": "string" }, "value": {} }, + "required": ["op", "path"] + }, + { + "type": "object", + "properties": { + "op": { "const": "move" }, + "path": { "type": "string" }, + "from": { "type": "string" } + }, + "required": ["op", "path", "from"] + }, + { + "type": "object", + "properties": { + "op": { "const": "copy" }, + "path": { "type": "string" }, + "from": { "type": "string" } + }, + "required": ["op", "path", "from"] + }, + { + "type": "object", + "properties": { "op": { "const": "test" }, "path": { "type": "string" }, "value": {} }, + "required": ["op", "path"] + } + ] + }, + "description": "An array of JSON Patch (RFC 6902) operations to apply to the resume data." + } + }, + "required": ["operations"] + } + } + } + }, + "responses": { + "200": { + "description": "The patched resume with its full data.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { "type": "string", "description": "The ID of the resume." }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "description": "The name of the resume." + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "description": "The slug of the resume." + }, + "tags": { + "type": "array", + "items": { "type": "string" }, + "description": "The tags of the resume." + }, + "isPublic": { "type": "boolean", "description": "Whether the resume is public." }, + "isLocked": { "type": "boolean", "description": "Whether the resume is locked." }, + "data": { "$ref": "#/components/schemas/ResumeData" }, + "hasPassword": { "type": "boolean" } + }, + "required": ["id", "name", "slug", "tags", "isPublic", "isLocked", "data", "hasPassword"] + } + } + } + }, + "400": { + "description": "400", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { "const": true }, + "code": { "const": "INVALID_PATCH_OPERATIONS" }, + "status": { "const": 400 }, + "message": { + "type": "string", + "default": "The patch operations are invalid or produced an invalid resume." + }, + "data": {} + }, + "required": ["defined", "code", "status", "message"] + }, + { + "type": "object", + "properties": { + "defined": { "const": false }, + "code": { "type": "string" }, + "status": { "type": "number" }, + "message": { "type": "string" }, + "data": {} + }, + "required": ["defined", "code", "status", "message"] + } + ] + } + } + } + } + } + }, + "delete": { + "operationId": "deleteResume", + "summary": "Delete a resume", + "description": "Permanently deletes a resume and its associated files (screenshots, PDFs) from storage. Locked resumes cannot be deleted; unlock the resume first. Requires authentication.", + "tags": ["Resumes"], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { "type": "string", "description": "The ID of the resume." } + } + ], + "requestBody": { + "required": false, + "content": { "application/json": { "schema": { "type": "object", "properties": {}, "required": [] } } } + }, + "responses": { + "200": { + "description": "The resume and its associated files were deleted successfully.", + "content": { "application/json": { "schema": { "anyOf": [{ "not": {} }, { "not": {} }] } } } + } + } + } + }, + "/resumes/{username}/{slug}": { + "get": { + "operationId": "getResumeBySlug", + "summary": "Get public resume by username and slug", + "description": "Returns a publicly shared resume identified by the owner's username and the resume's slug. If the resume is password-protected and the viewer has not yet verified the password, a 401 error with code NEED_PASSWORD is returned. No authentication required for public resumes; if authenticated as the owner, private resumes are also accessible.", + "tags": ["Resume Sharing"], + "parameters": [ + { "name": "username", "in": "path", "required": true, "schema": { "type": "string" } }, + { "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } } + ], + "responses": { + "200": { + "description": "The public resume with its full data.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { "type": "string", "description": "The ID of the resume." }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "description": "The name of the resume." + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "description": "The slug of the resume." + }, + "tags": { + "type": "array", + "items": { "type": "string" }, + "description": "The tags of the resume." + }, + "isPublic": { "type": "boolean", "description": "Whether the resume is public." }, + "isLocked": { "type": "boolean", "description": "Whether the resume is locked." }, + "data": { "$ref": "#/components/schemas/ResumeData" } + }, + "required": ["id", "name", "slug", "tags", "isPublic", "isLocked", "data"] + } + } + } + } + } + } + }, + "/resumes/import": { + "post": { + "operationId": "importResume", + "summary": "Import a resume", + "description": "Creates a new resume from an existing ResumeData object (e.g. from a previously exported JSON file). A random name and slug are generated automatically. Returns the ID of the imported resume. Requires authentication.", + "tags": ["Resumes"], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "data": { "$ref": "#/components/schemas/ResumeData" } }, + "required": ["data"] + } + } + } + }, + "responses": { + "200": { + "description": "The ID of the imported resume.", + "content": { + "application/json": { "schema": { "type": "string", "description": "The ID of the imported resume." } } + } + }, + "400": { + "description": "400", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "defined": { "const": true }, + "code": { "const": "RESUME_SLUG_ALREADY_EXISTS" }, + "status": { "const": 400 }, + "message": { "type": "string", "default": "A resume with this slug already exists." }, + "data": {} + }, + "required": ["defined", "code", "status", "message"] + }, + { + "type": "object", + "properties": { + "defined": { "const": false }, + "code": { "type": "string" }, + "status": { "type": "number" }, + "message": { "type": "string" }, + "data": {} + }, + "required": ["defined", "code", "status", "message"] + } + ] + } + } + } + } + } + } + }, + "/resumes/{id}/lock": { + "post": { + "operationId": "setResumeLocked", + "summary": "Set resume lock status", + "description": "Toggles the locked status of a resume. When locked, a resume cannot be updated, patched, or deleted. Useful for protecting finalized resumes from accidental edits. Requires authentication.", + "tags": ["Resumes"], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { "type": "string", "description": "The ID of the resume." } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "isLocked": { "type": "boolean", "description": "Whether the resume is locked." } }, + "required": ["isLocked"] + } + } + } + }, + "responses": { + "200": { + "description": "The resume lock status was updated successfully.", + "content": { "application/json": { "schema": { "anyOf": [{ "not": {} }, { "not": {} }] } } } + } + } + } + }, + "/resumes/{id}/password": { + "put": { + "operationId": "setResumePassword", + "summary": "Set resume password", + "description": "Sets or updates a password on a resume. When a password is set, viewers of the public resume must enter the password before the resume data is revealed. The password must be between 6 and 64 characters. Requires authentication.", + "tags": ["Resume Sharing"], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { "type": "string", "description": "The ID of the resume." } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { "password": { "type": "string", "minLength": 6, "maxLength": 64 } }, + "required": ["password"] + } + } + } + }, + "responses": { + "200": { + "description": "The resume password was set successfully.", + "content": { "application/json": { "schema": { "anyOf": [{ "not": {} }, { "not": {} }] } } } + } + } + }, + "delete": { + "operationId": "removeResumePassword", + "summary": "Remove resume password", + "description": "Removes password protection from a resume. After removal, the resume (if public) can be viewed without entering a password. Requires authentication.", + "tags": ["Resume Sharing"], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { "type": "string", "description": "The ID of the resume." } + } + ], + "requestBody": { + "required": false, + "content": { "application/json": { "schema": { "type": "object", "properties": {}, "required": [] } } } + }, + "responses": { + "200": { + "description": "The resume password was removed successfully.", + "content": { "application/json": { "schema": { "anyOf": [{ "not": {} }, { "not": {} }] } } } + } + } + } + }, + "/resumes/{username}/{slug}/password/verify": { + "post": { + "operationId": "verifyResumePassword", + "summary": "Verify resume password", + "description": "Verifies a password for a password-protected public resume. On success, the viewer is granted access to view the resume data for the duration of their session. No authentication required.", + "tags": ["Resume Sharing"], + "parameters": [ + { + "name": "username", + "in": "path", + "required": true, + "schema": { "type": "string", "minLength": 1, "description": "The username of the resume owner." } + }, + { + "name": "slug", + "in": "path", + "required": true, + "schema": { "type": "string", "minLength": 1, "description": "The slug of the resume." } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "password": { "type": "string", "minLength": 1, "description": "The password to verify." } + }, + "required": ["password"] + } + } + } + }, + "responses": { + "200": { + "description": "The password was verified successfully and access has been granted.", + "content": { "application/json": { "schema": { "type": "boolean" } } } + } + } + } + }, + "/resumes/{id}/duplicate": { + "post": { + "operationId": "duplicateResume", + "summary": "Duplicate a resume", + "description": "Creates a copy of an existing resume with the same data. Optionally override the name, slug, and tags for the duplicate. If not provided, the original resume's name, slug, and tags are used. Returns the ID of the duplicated resume. Requires authentication.", + "tags": ["Resumes"], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { "type": "string", "description": "The ID of the resume." } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "description": "The name of the resume." + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "description": "The slug of the resume." + }, + "tags": { "type": "array", "items": { "type": "string" }, "description": "The tags of the resume." } + }, + "required": ["name", "slug", "tags"] + } + } + } + }, + "responses": { + "200": { + "description": "The ID of the duplicated resume.", + "content": { + "application/json": { "schema": { "type": "string", "description": "The ID of the duplicated resume." } } + } + } + } + } + }, + "/resumes/{id}/pdf": { + "get": { + "operationId": "exportResumePdf", + "summary": "Export resume as PDF", + "description": "Generates a PDF from the specified resume and uploads it to storage. Returns a URL to download the generated PDF file. If the request is made by an unauthenticated user (e.g. via a public share link), the resume's download count is incremented. Authentication is optional.", + "tags": ["Resume Export"], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { "type": "string", "description": "The unique identifier of the resume to export." } + } + ], + "responses": { + "200": { + "description": "The PDF was generated successfully. Returns a URL to download the file.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "url": { "type": "string", "description": "The URL to download the generated PDF file." } + }, + "required": ["url"] + } + } + } + } + } + } + }, + "/resumes/{id}/screenshot": { + "get": { + "operationId": "getResumeScreenshot", + "summary": "Get resume screenshot", + "description": "Returns a URL to a screenshot image of the first page of the specified resume. Screenshots are cached for up to 6 hours and regenerated automatically when the resume is updated. Returns null if the screenshot cannot be generated. Requires authentication.", + "tags": ["Resume Export"], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { "type": "string", "description": "The unique identifier of the resume." } + } + ], + "responses": { + "200": { + "description": "The screenshot URL, or null if the screenshot could not be generated.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "url": { + "anyOf": [{ "type": "string" }, { "type": "null" }], + "description": "The URL to the screenshot image, or null." + } + }, + "required": ["url"] + } + } + } + } + } + } + }, + "/statistics/users": { + "get": { + "operationId": "getUserCount", + "summary": "Get total number of users", + "description": "Returns the total number of registered users on this Reactive Resume instance. The count is cached for up to 6 hours for performance. No authentication required.", + "tags": ["Platform Statistics"], + "responses": { + "200": { + "description": "The total number of registered users.", + "content": { + "application/json": { + "schema": { "type": "number", "description": "The total number of registered users." } + } + } + } + } + } + }, + "/statistics/resumes": { + "get": { + "operationId": "getResumeCount", + "summary": "Get total number of resumes", + "description": "Returns the total number of resumes created on this Reactive Resume instance. The count is cached for up to 6 hours for performance. No authentication required.", + "tags": ["Platform Statistics"], + "responses": { + "200": { + "description": "The total number of resumes created.", + "content": { + "application/json": { + "schema": { "type": "number", "description": "The total number of resumes created." } + } + } + } + } + } + }, + "/statistics/github/stars": { + "get": { + "operationId": "getGitHubStarCount", + "summary": "Get GitHub star count", + "description": "Returns the number of GitHub stars for the Reactive Resume repository. The count is cached for up to 6 hours and falls back to a last-known value if the GitHub API is unavailable. No authentication required.", + "tags": ["Platform Statistics"], + "responses": { + "200": { + "description": "The number of GitHub stars for the Reactive Resume repository.", + "content": { + "application/json": { "schema": { "type": "number", "description": "The number of GitHub stars." } } + } + } + } + } + } + } } diff --git a/drizzle.config.ts b/drizzle.config.ts index d670bcc32..e6b26bac2 100644 --- a/drizzle.config.ts +++ b/drizzle.config.ts @@ -1,10 +1,10 @@ import { defineConfig } from "drizzle-kit"; export default defineConfig({ - out: "./migrations", - dialect: "postgresql", - schema: "./src/integrations/drizzle/schema.ts", - dbCredentials: { - url: process.env.DATABASE_URL, - }, + out: "./migrations", + dialect: "postgresql", + schema: "./src/integrations/drizzle/schema.ts", + dbCredentials: { + url: process.env.DATABASE_URL, + }, }); diff --git a/knip.json b/knip.json index 4cd9a4f15..1f4bf446e 100644 --- a/knip.json +++ b/knip.json @@ -1,9 +1,9 @@ { - "$schema": "https://unpkg.com/knip@5/schema.json", - "entry": ["src/server.ts", "src/router.tsx"], - "tailwind": { "entry": ["src/styles.css"] }, - "project": ["src/**/*.{js,jsx,ts,tsx,mdx,css}"], - "ignoreBinaries": ["mint"], - "ignoreDependencies": ["npm-check-updates", "node-addon-api", "node-gyp"], - "ignore": ["src/schema/**/*.ts", "src/components/**/*.{ts,tsx}"] + "$schema": "https://unpkg.com/knip@5/schema.json", + "entry": ["src/server.ts", "src/router.tsx"], + "tailwind": { "entry": ["src/styles.css"] }, + "project": ["src/**/*.{js,jsx,ts,tsx,mdx,css}"], + "ignoreBinaries": ["mint"], + "ignoreDependencies": ["npm-check-updates", "node-addon-api", "node-gyp"], + "ignore": ["src/schema/**/*.ts", "src/components/**/*.{ts,tsx}"] } diff --git a/lingui.config.ts b/lingui.config.ts index 6b80a82f0..ff87d6729 100644 --- a/lingui.config.ts +++ b/lingui.config.ts @@ -1,76 +1,76 @@ import { defineConfig } from "@lingui/cli"; export default defineConfig({ - sourceLocale: "en-US", - pseudoLocale: "zu-ZA", - locales: [ - "af-ZA", - "am-ET", - "ar-SA", - "az-AZ", - "bg-BG", - "bn-BD", - "ca-ES", - "cs-CZ", - "da-DK", - "de-DE", - "el-GR", - "en-US", - "en-GB", - "es-ES", - "fa-IR", - "fi-FI", - "fr-FR", - "he-IL", - "hi-IN", - "hu-HU", - "id-ID", - "it-IT", - "ja-JP", - "km-KH", - "kn-IN", - "ko-KR", - "lt-LT", - "lv-LV", - "ml-IN", - "mr-IN", - "ms-MY", - "ne-NP", - "nl-NL", - "no-NO", - "or-IN", - "pl-PL", - "pt-BR", - "pt-PT", - "ro-RO", - "ru-RU", - "sk-SK", - "sl-SI", - "sq-AL", - "sr-SP", - "sv-SE", - "ta-IN", - "te-IN", - "th-TH", - "tr-TR", - "uk-UA", - "uz-UZ", - "vi-VN", - "zh-CN", - "zh-TW", - "zu-ZA", - ], - fallbackLocales: { - "zu-ZA": "en-US", - default: "en-US", - }, - formatOptions: { - lineNumbers: false, - }, - catalogs: [ - { - path: "/locales/{locale}", - include: ["src"], - }, - ], + sourceLocale: "en-US", + pseudoLocale: "zu-ZA", + locales: [ + "af-ZA", + "am-ET", + "ar-SA", + "az-AZ", + "bg-BG", + "bn-BD", + "ca-ES", + "cs-CZ", + "da-DK", + "de-DE", + "el-GR", + "en-US", + "en-GB", + "es-ES", + "fa-IR", + "fi-FI", + "fr-FR", + "he-IL", + "hi-IN", + "hu-HU", + "id-ID", + "it-IT", + "ja-JP", + "km-KH", + "kn-IN", + "ko-KR", + "lt-LT", + "lv-LV", + "ml-IN", + "mr-IN", + "ms-MY", + "ne-NP", + "nl-NL", + "no-NO", + "or-IN", + "pl-PL", + "pt-BR", + "pt-PT", + "ro-RO", + "ru-RU", + "sk-SK", + "sl-SI", + "sq-AL", + "sr-SP", + "sv-SE", + "ta-IN", + "te-IN", + "th-TH", + "tr-TR", + "uk-UA", + "uz-UZ", + "vi-VN", + "zh-CN", + "zh-TW", + "zu-ZA", + ], + fallbackLocales: { + "zu-ZA": "en-US", + default: "en-US", + }, + formatOptions: { + lineNumbers: false, + }, + catalogs: [ + { + path: "/locales/{locale}", + include: ["src"], + }, + ], }); diff --git a/locales/af-ZA.po b/locales/af-ZA.po index ab2ef6c38..abab4982a 100644 --- a/locales/af-ZA.po +++ b/locales/af-ZA.po @@ -2908,4 +2908,3 @@ msgstr "Zoem uit" #: src/utils/locale.ts msgid "Zulu" msgstr "Zoeloe" - diff --git a/locales/am-ET.po b/locales/am-ET.po index 12cbabd0d..0817030a3 100644 --- a/locales/am-ET.po +++ b/locales/am-ET.po @@ -2908,4 +2908,3 @@ msgstr "አጉር" #: src/utils/locale.ts msgid "Zulu" msgstr "ዙሉ" - diff --git a/locales/ar-SA.po b/locales/ar-SA.po index 90495b706..c9c866b04 100644 --- a/locales/ar-SA.po +++ b/locales/ar-SA.po @@ -2908,4 +2908,3 @@ msgstr "تصغير" #: src/utils/locale.ts msgid "Zulu" msgstr "الزولو" - diff --git a/locales/az-AZ.po b/locales/az-AZ.po index 3d67b608a..44ac2a8d7 100644 --- a/locales/az-AZ.po +++ b/locales/az-AZ.po @@ -2908,4 +2908,3 @@ msgstr "Uzaqlaşdır" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/bg-BG.po b/locales/bg-BG.po index 255e77477..a6150171a 100644 --- a/locales/bg-BG.po +++ b/locales/bg-BG.po @@ -2908,4 +2908,3 @@ msgstr "Намаляване" #: src/utils/locale.ts msgid "Zulu" msgstr "Зулуски" - diff --git a/locales/bn-BD.po b/locales/bn-BD.po index cff0d03a1..8beaf942d 100644 --- a/locales/bn-BD.po +++ b/locales/bn-BD.po @@ -2908,4 +2908,3 @@ msgstr "জুম আউট" #: src/utils/locale.ts msgid "Zulu" msgstr "জুলু" - diff --git a/locales/ca-ES.po b/locales/ca-ES.po index f0623ad01..dec2fe86d 100644 --- a/locales/ca-ES.po +++ b/locales/ca-ES.po @@ -2908,4 +2908,3 @@ msgstr "Allunya" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulú" - diff --git a/locales/cs-CZ.po b/locales/cs-CZ.po index 4985da581..9369f97c3 100644 --- a/locales/cs-CZ.po +++ b/locales/cs-CZ.po @@ -2908,4 +2908,3 @@ msgstr "Oddálit" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/da-DK.po b/locales/da-DK.po index 2d0752c40..57b13be82 100644 --- a/locales/da-DK.po +++ b/locales/da-DK.po @@ -2908,4 +2908,3 @@ msgstr "Zoom ud" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/de-DE.po b/locales/de-DE.po index fbdc9279a..75f1286ea 100644 --- a/locales/de-DE.po +++ b/locales/de-DE.po @@ -2908,4 +2908,3 @@ msgstr "Herauszoomen" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/el-GR.po b/locales/el-GR.po index c18c4271c..17815accf 100644 --- a/locales/el-GR.po +++ b/locales/el-GR.po @@ -2908,4 +2908,3 @@ msgstr "Σμίκρυνση" #: src/utils/locale.ts msgid "Zulu" msgstr "Ζουλού" - diff --git a/locales/en-GB.po b/locales/en-GB.po index 47092d932..004b4f9d8 100644 --- a/locales/en-GB.po +++ b/locales/en-GB.po @@ -2908,4 +2908,3 @@ msgstr "Zoom out" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/es-ES.po b/locales/es-ES.po index ef5a54456..3cd05431d 100644 --- a/locales/es-ES.po +++ b/locales/es-ES.po @@ -2908,4 +2908,3 @@ msgstr "Alejar" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulú" - diff --git a/locales/fa-IR.po b/locales/fa-IR.po index b2d1df374..86c4d893b 100644 --- a/locales/fa-IR.po +++ b/locales/fa-IR.po @@ -2908,4 +2908,3 @@ msgstr "کوچک‌نمایی" #: src/utils/locale.ts msgid "Zulu" msgstr "زولو" - diff --git a/locales/fi-FI.po b/locales/fi-FI.po index a858c2421..1214e2e3a 100644 --- a/locales/fi-FI.po +++ b/locales/fi-FI.po @@ -2908,4 +2908,3 @@ msgstr "Loitonna" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/fr-FR.po b/locales/fr-FR.po index 9cc8fdf89..b5f7cf918 100644 --- a/locales/fr-FR.po +++ b/locales/fr-FR.po @@ -2908,4 +2908,3 @@ msgstr "Zoom arrière" #: src/utils/locale.ts msgid "Zulu" msgstr "Zoulou" - diff --git a/locales/he-IL.po b/locales/he-IL.po index 76a9cad36..58022878c 100644 --- a/locales/he-IL.po +++ b/locales/he-IL.po @@ -2908,4 +2908,3 @@ msgstr "התרחקות" #: src/utils/locale.ts msgid "Zulu" msgstr "זולו" - diff --git a/locales/hi-IN.po b/locales/hi-IN.po index fb981b0ea..24187a9de 100644 --- a/locales/hi-IN.po +++ b/locales/hi-IN.po @@ -2908,4 +2908,3 @@ msgstr "ज़ूम आउट" #: src/utils/locale.ts msgid "Zulu" msgstr "ज़ुलु" - diff --git a/locales/hu-HU.po b/locales/hu-HU.po index 4c24a1a2e..736d34530 100644 --- a/locales/hu-HU.po +++ b/locales/hu-HU.po @@ -2908,4 +2908,3 @@ msgstr "Kicsinyítés" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/id-ID.po b/locales/id-ID.po index d19f64250..2f2354d01 100644 --- a/locales/id-ID.po +++ b/locales/id-ID.po @@ -2908,4 +2908,3 @@ msgstr "Perkecil" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/it-IT.po b/locales/it-IT.po index a140017a0..a5fa7672a 100644 --- a/locales/it-IT.po +++ b/locales/it-IT.po @@ -2908,4 +2908,3 @@ msgstr "Rimpicciolisci" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/ja-JP.po b/locales/ja-JP.po index e8e027944..9768c1ce8 100644 --- a/locales/ja-JP.po +++ b/locales/ja-JP.po @@ -2908,4 +2908,3 @@ msgstr "ズームアウト" #: src/utils/locale.ts msgid "Zulu" msgstr "ズールー語" - diff --git a/locales/km-KH.po b/locales/km-KH.po index 218139368..537e22641 100644 --- a/locales/km-KH.po +++ b/locales/km-KH.po @@ -2908,4 +2908,3 @@ msgstr "បង្រួម" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/kn-IN.po b/locales/kn-IN.po index 2e4ac8927..ee2ca654d 100644 --- a/locales/kn-IN.po +++ b/locales/kn-IN.po @@ -2908,4 +2908,3 @@ msgstr "ಗಾತ್ರ ಕುಗ್ಗಿಸಿ" #: src/utils/locale.ts msgid "Zulu" msgstr "ಜೂಲೂ" - diff --git a/locales/ko-KR.po b/locales/ko-KR.po index 1879cb14c..7f158571f 100644 --- a/locales/ko-KR.po +++ b/locales/ko-KR.po @@ -2908,4 +2908,3 @@ msgstr "축소" #: src/utils/locale.ts msgid "Zulu" msgstr "줄루어" - diff --git a/locales/lt-LT.po b/locales/lt-LT.po index 2f72e4dad..2aa2b54e4 100644 --- a/locales/lt-LT.po +++ b/locales/lt-LT.po @@ -2908,4 +2908,3 @@ msgstr "Tolinti" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulų" - diff --git a/locales/lv-LV.po b/locales/lv-LV.po index 773b40999..06c6ef6a3 100644 --- a/locales/lv-LV.po +++ b/locales/lv-LV.po @@ -2908,4 +2908,3 @@ msgstr "Tālināt" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/ml-IN.po b/locales/ml-IN.po index 6c04b3839..43cdc91d8 100644 --- a/locales/ml-IN.po +++ b/locales/ml-IN.po @@ -2908,4 +2908,3 @@ msgstr "സൂം ഔട്ട് ചെയ്യുക" #: src/utils/locale.ts msgid "Zulu" msgstr "സൂളു" - diff --git a/locales/mr-IN.po b/locales/mr-IN.po index d8aab7dc5..efff90fa6 100644 --- a/locales/mr-IN.po +++ b/locales/mr-IN.po @@ -2908,4 +2908,3 @@ msgstr "बाहेर झूम करा" #: src/utils/locale.ts msgid "Zulu" msgstr "झुलू" - diff --git a/locales/ms-MY.po b/locales/ms-MY.po index 7bcf4811b..d31e432e0 100644 --- a/locales/ms-MY.po +++ b/locales/ms-MY.po @@ -2908,4 +2908,3 @@ msgstr "Zum keluar" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/ne-NP.po b/locales/ne-NP.po index 1a6570b55..b844b30ab 100644 --- a/locales/ne-NP.po +++ b/locales/ne-NP.po @@ -2908,4 +2908,3 @@ msgstr "जूम आउट" #: src/utils/locale.ts msgid "Zulu" msgstr "जुलु" - diff --git a/locales/nl-NL.po b/locales/nl-NL.po index 73685acfd..f77bb2c28 100644 --- a/locales/nl-NL.po +++ b/locales/nl-NL.po @@ -2908,4 +2908,3 @@ msgstr "Uitzoomen" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/no-NO.po b/locales/no-NO.po index 6d5306f3a..63e7e3317 100644 --- a/locales/no-NO.po +++ b/locales/no-NO.po @@ -2908,4 +2908,3 @@ msgstr "Zoom ut" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/or-IN.po b/locales/or-IN.po index b34d7059f..2532bdfe7 100644 --- a/locales/or-IN.po +++ b/locales/or-IN.po @@ -2908,4 +2908,3 @@ msgstr "ଜୁମ୍ ଆଉଟ୍ କରନ୍ତୁ" #: src/utils/locale.ts msgid "Zulu" msgstr "ଜୁଲୁ" - diff --git a/locales/pl-PL.po b/locales/pl-PL.po index 0ab39c051..7408c3fe7 100644 --- a/locales/pl-PL.po +++ b/locales/pl-PL.po @@ -2908,4 +2908,3 @@ msgstr "Pomniejsz" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/pt-BR.po b/locales/pt-BR.po index 8a31bd535..789ceac8a 100644 --- a/locales/pt-BR.po +++ b/locales/pt-BR.po @@ -2908,4 +2908,3 @@ msgstr "Diminuir zoom" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/pt-PT.po b/locales/pt-PT.po index d8bbc57ca..f3be9d2c2 100644 --- a/locales/pt-PT.po +++ b/locales/pt-PT.po @@ -2908,4 +2908,3 @@ msgstr "Afastar" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/ro-RO.po b/locales/ro-RO.po index 99de5027a..8e9ed9f4a 100644 --- a/locales/ro-RO.po +++ b/locales/ro-RO.po @@ -2908,4 +2908,3 @@ msgstr "Micșorează" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/ru-RU.po b/locales/ru-RU.po index b5f35de72..67c4ea494 100644 --- a/locales/ru-RU.po +++ b/locales/ru-RU.po @@ -2908,4 +2908,3 @@ msgstr "Уменьшить" #: src/utils/locale.ts msgid "Zulu" msgstr "Зулу" - diff --git a/locales/sk-SK.po b/locales/sk-SK.po index 09e844720..2efed4f21 100644 --- a/locales/sk-SK.po +++ b/locales/sk-SK.po @@ -2908,4 +2908,3 @@ msgstr "Oddialiť" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/sl-SI.po b/locales/sl-SI.po index 585845aa1..446c39495 100644 --- a/locales/sl-SI.po +++ b/locales/sl-SI.po @@ -2908,4 +2908,3 @@ msgstr "Oddalji" #: src/utils/locale.ts msgid "Zulu" msgstr "Zuluščina" - diff --git a/locales/sq-AL.po b/locales/sq-AL.po index be24f74f4..eafd87016 100644 --- a/locales/sq-AL.po +++ b/locales/sq-AL.po @@ -2908,4 +2908,3 @@ msgstr "Zvogëlo" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/sr-SP.po b/locales/sr-SP.po index 96dd10404..c7da83c03 100644 --- a/locales/sr-SP.po +++ b/locales/sr-SP.po @@ -2908,4 +2908,3 @@ msgstr "Умањи" #: src/utils/locale.ts msgid "Zulu" msgstr "Зулу" - diff --git a/locales/sv-SE.po b/locales/sv-SE.po index 057b190ee..38d0796ac 100644 --- a/locales/sv-SE.po +++ b/locales/sv-SE.po @@ -2908,4 +2908,3 @@ msgstr "Zooma ut" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/ta-IN.po b/locales/ta-IN.po index beb91be2f..13c6a0e05 100644 --- a/locales/ta-IN.po +++ b/locales/ta-IN.po @@ -2908,4 +2908,3 @@ msgstr "சிறிதாக்கு" #: src/utils/locale.ts msgid "Zulu" msgstr "ஜூலு" - diff --git a/locales/te-IN.po b/locales/te-IN.po index e5e481683..e6d94724a 100644 --- a/locales/te-IN.po +++ b/locales/te-IN.po @@ -2908,4 +2908,3 @@ msgstr "జూమ్ అవుట్ చేయండి" #: src/utils/locale.ts msgid "Zulu" msgstr "జులు" - diff --git a/locales/th-TH.po b/locales/th-TH.po index fe248eed9..0727c996a 100644 --- a/locales/th-TH.po +++ b/locales/th-TH.po @@ -2908,4 +2908,3 @@ msgstr "ซูมออก" #: src/utils/locale.ts msgid "Zulu" msgstr "ซูลู" - diff --git a/locales/tr-TR.po b/locales/tr-TR.po index 2210002d8..0f4dc9290 100644 --- a/locales/tr-TR.po +++ b/locales/tr-TR.po @@ -2908,4 +2908,3 @@ msgstr "Uzaklaştır" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/uk-UA.po b/locales/uk-UA.po index a9de28329..49a12e235 100644 --- a/locales/uk-UA.po +++ b/locales/uk-UA.po @@ -2908,4 +2908,3 @@ msgstr "Зменшити" #: src/utils/locale.ts msgid "Zulu" msgstr "Зулу" - diff --git a/locales/uz-UZ.po b/locales/uz-UZ.po index 26cadeaf7..018098eab 100644 --- a/locales/uz-UZ.po +++ b/locales/uz-UZ.po @@ -2908,4 +2908,3 @@ msgstr "Kichraytirish" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/vi-VN.po b/locales/vi-VN.po index f7f7fb7c7..3fc211467 100644 --- a/locales/vi-VN.po +++ b/locales/vi-VN.po @@ -2908,4 +2908,3 @@ msgstr "Thu nhỏ" #: src/utils/locale.ts msgid "Zulu" msgstr "Zulu" - diff --git a/locales/zh-CN.po b/locales/zh-CN.po index 69fbfe8e2..540ec0781 100644 --- a/locales/zh-CN.po +++ b/locales/zh-CN.po @@ -2908,4 +2908,3 @@ msgstr "缩小" #: src/utils/locale.ts msgid "Zulu" msgstr "祖鲁语" - diff --git a/locales/zh-TW.po b/locales/zh-TW.po index d5373d7a1..c4ecc32ad 100644 --- a/locales/zh-TW.po +++ b/locales/zh-TW.po @@ -2908,4 +2908,3 @@ msgstr "縮小" #: src/utils/locale.ts msgid "Zulu" msgstr "祖魯語" - diff --git a/migrations/20260114102228_peaceful_pestilence/snapshot.json b/migrations/20260114102228_peaceful_pestilence/snapshot.json index 0fd8f74b3..31f76665e 100644 --- a/migrations/20260114102228_peaceful_pestilence/snapshot.json +++ b/migrations/20260114102228_peaceful_pestilence/snapshot.json @@ -1,1650 +1,1650 @@ { - "version": "8", - "dialect": "postgres", - "id": "ce0ad8e6-02da-4a87-a914-7cbe9f3962b9", - "prevIds": ["00000000-0000-0000-0000-000000000000"], - "ddl": [ - { - "isRlsEnabled": false, - "name": "account", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "apikey", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "passkey", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "resume", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "resume_statistics", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "session", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "two_factor", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "user", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "verification", - "entityType": "tables", - "schema": "public" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "account_id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "'credential'", - "generated": null, - "identity": null, - "name": "provider_id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "scope", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id_token", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "password", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "access_token", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refresh_token", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "access_token_expires_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refresh_token_expires_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "start", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "prefix", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "key", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refill_interval", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refill_amount", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_refill_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "true", - "generated": null, - "identity": null, - "name": "enabled", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "rate_limit_enabled", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "rate_limit_time_window", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "rate_limit_max", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "request_count", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "remaining", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_request", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "expires_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "permissions", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "jsonb", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "metadata", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "aaguid", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "public_key", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "credential_id", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "counter", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "device_type", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "backed_up", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "transports", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "slug", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 1, - "default": "'{}'", - "generated": null, - "identity": null, - "name": "tags", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "is_public", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "is_locked", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "password", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "jsonb", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "data", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "views", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "downloads", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_viewed_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_downloaded_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "resume_id", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "token", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "ip_address", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_agent", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "expires_at", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "secret", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "backup_codes", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "image", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "email", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "email_verified", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "username", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "display_username", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "two_factor_enabled", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "identifier", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "value", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "expires_at", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "timestamp", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "account_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "account" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "passkey_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "passkey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "is_public", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "slug", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "resume_is_public_slug_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "token", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "session_token_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "session" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "two_factor_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "two_factor" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "account_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "account" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "apikey_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "passkey_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "passkey" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "resume_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": ["resume_id"], - "schemaTo": "public", - "tableTo": "resume", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "resume_statistics_resume_id_resume_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "resume_statistics" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "session_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "session" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "two_factor_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "two_factor" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "account_pkey", - "schema": "public", - "table": "account", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "apikey_pkey", - "schema": "public", - "table": "apikey", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "passkey_pkey", - "schema": "public", - "table": "passkey", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "resume_pkey", - "schema": "public", - "table": "resume", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "resume_statistics_pkey", - "schema": "public", - "table": "resume_statistics", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "session_pkey", - "schema": "public", - "table": "session", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "two_factor_pkey", - "schema": "public", - "table": "two_factor", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "user_pkey", - "schema": "public", - "table": "user", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "verification_pkey", - "schema": "public", - "table": "verification", - "entityType": "pks" - }, - { - "nameExplicit": false, - "columns": ["slug", "user_id"], - "nullsNotDistinct": false, - "name": "resume_slug_user_id_unique", - "entityType": "uniques", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": ["resume_id"], - "nullsNotDistinct": false, - "name": "resume_statistics_resume_id_key", - "schema": "public", - "table": "resume_statistics", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["token"], - "nullsNotDistinct": false, - "name": "session_token_key", - "schema": "public", - "table": "session", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["email"], - "nullsNotDistinct": false, - "name": "user_email_key", - "schema": "public", - "table": "user", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["username"], - "nullsNotDistinct": false, - "name": "user_username_key", - "schema": "public", - "table": "user", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["display_username"], - "nullsNotDistinct": false, - "name": "user_display_username_key", - "schema": "public", - "table": "user", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["identifier"], - "nullsNotDistinct": false, - "name": "verification_identifier_key", - "schema": "public", - "table": "verification", - "entityType": "uniques" - } - ], - "renames": [] + "version": "8", + "dialect": "postgres", + "id": "ce0ad8e6-02da-4a87-a914-7cbe9f3962b9", + "prevIds": ["00000000-0000-0000-0000-000000000000"], + "ddl": [ + { + "isRlsEnabled": false, + "name": "account", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "apikey", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "passkey", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "resume", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "resume_statistics", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "session", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "two_factor", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "user", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "verification", + "entityType": "tables", + "schema": "public" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "account_id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'credential'", + "generated": null, + "identity": null, + "name": "provider_id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "scope", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id_token", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "password", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "access_token", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refresh_token", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "access_token_expires_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refresh_token_expires_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "start", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "prefix", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "key", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refill_interval", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refill_amount", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_refill_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "true", + "generated": null, + "identity": null, + "name": "enabled", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "rate_limit_enabled", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "rate_limit_time_window", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "rate_limit_max", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "request_count", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "remaining", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_request", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "permissions", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "metadata", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "aaguid", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "public_key", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "credential_id", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "counter", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "device_type", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "backed_up", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "transports", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 1, + "default": "'{}'", + "generated": null, + "identity": null, + "name": "tags", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "is_public", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "is_locked", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "password", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "data", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "views", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "downloads", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_viewed_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_downloaded_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "resume_id", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "token", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ip_address", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_agent", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "secret", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "backup_codes", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "image", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "email", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "email_verified", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "username", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "display_username", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "two_factor_enabled", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "identifier", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "value", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "timestamp", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "account_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "account" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "passkey_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "passkey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "is_public", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "resume_is_public_slug_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "token", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "session_token_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "session" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "two_factor_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "two_factor" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "account_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "account" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "apikey_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "passkey_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "passkey" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "resume_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": ["resume_id"], + "schemaTo": "public", + "tableTo": "resume", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "resume_statistics_resume_id_resume_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "resume_statistics" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "session_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "session" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "two_factor_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "two_factor" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "account_pkey", + "schema": "public", + "table": "account", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "apikey_pkey", + "schema": "public", + "table": "apikey", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "passkey_pkey", + "schema": "public", + "table": "passkey", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "resume_pkey", + "schema": "public", + "table": "resume", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "resume_statistics_pkey", + "schema": "public", + "table": "resume_statistics", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "session_pkey", + "schema": "public", + "table": "session", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "two_factor_pkey", + "schema": "public", + "table": "two_factor", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "user_pkey", + "schema": "public", + "table": "user", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "verification_pkey", + "schema": "public", + "table": "verification", + "entityType": "pks" + }, + { + "nameExplicit": false, + "columns": ["slug", "user_id"], + "nullsNotDistinct": false, + "name": "resume_slug_user_id_unique", + "entityType": "uniques", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": ["resume_id"], + "nullsNotDistinct": false, + "name": "resume_statistics_resume_id_key", + "schema": "public", + "table": "resume_statistics", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["token"], + "nullsNotDistinct": false, + "name": "session_token_key", + "schema": "public", + "table": "session", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["email"], + "nullsNotDistinct": false, + "name": "user_email_key", + "schema": "public", + "table": "user", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["username"], + "nullsNotDistinct": false, + "name": "user_username_key", + "schema": "public", + "table": "user", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["display_username"], + "nullsNotDistinct": false, + "name": "user_display_username_key", + "schema": "public", + "table": "user", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["identifier"], + "nullsNotDistinct": false, + "name": "verification_identifier_key", + "schema": "public", + "table": "verification", + "entityType": "uniques" + } + ], + "renames": [] } diff --git a/migrations/20260115232736_nervous_maddog/snapshot.json b/migrations/20260115232736_nervous_maddog/snapshot.json index 8a61b4780..63e48405b 100644 --- a/migrations/20260115232736_nervous_maddog/snapshot.json +++ b/migrations/20260115232736_nervous_maddog/snapshot.json @@ -1,1895 +1,1895 @@ { - "version": "8", - "dialect": "postgres", - "id": "24141f99-af12-49b1-aa09-e9f5c75c083d", - "prevIds": ["ce0ad8e6-02da-4a87-a914-7cbe9f3962b9"], - "ddl": [ - { - "isRlsEnabled": false, - "name": "account", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "apikey", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "passkey", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "resume", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "resume_statistics", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "session", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "two_factor", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "user", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "verification", - "entityType": "tables", - "schema": "public" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "account_id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "'credential'", - "generated": null, - "identity": null, - "name": "provider_id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "scope", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id_token", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "password", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "access_token", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refresh_token", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "access_token_expires_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refresh_token_expires_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "start", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "prefix", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "key", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refill_interval", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refill_amount", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_refill_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "true", - "generated": null, - "identity": null, - "name": "enabled", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "rate_limit_enabled", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "rate_limit_time_window", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "rate_limit_max", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "request_count", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "remaining", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_request", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "expires_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "permissions", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "jsonb", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "metadata", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "aaguid", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "public_key", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "credential_id", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "counter", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "device_type", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "backed_up", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "transports", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "slug", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 1, - "default": "'{}'", - "generated": null, - "identity": null, - "name": "tags", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "is_public", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "is_locked", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "password", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "jsonb", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "data", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "views", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "downloads", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_viewed_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_downloaded_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "resume_id", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "token", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "ip_address", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_agent", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "expires_at", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "secret", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "backup_codes", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "image", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "email", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "email_verified", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "username", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "display_username", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "two_factor_enabled", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "identifier", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "value", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "expires_at", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "account_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "account" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "apikey_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "key", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "apikey_key_index", - "entityType": "indexes", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "enabled", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "apikey_enabled_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "passkey_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "passkey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "resume_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "updated_at", - "isExpression": false, - "asc": false, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "resume_user_id_updated_at_index", - "entityType": "indexes", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "is_public", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "slug", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "resume_is_public_slug_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "resume_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "resume_statistics_resume_id_index", - "entityType": "indexes", - "schema": "public", - "table": "resume_statistics" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "token", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "session_token_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "session" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "expires_at", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "session_expires_at_index", - "entityType": "indexes", - "schema": "public", - "table": "session" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "two_factor_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "two_factor" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "secret", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "two_factor_secret_index", - "entityType": "indexes", - "schema": "public", - "table": "two_factor" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "email", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "user_email_index", - "entityType": "indexes", - "schema": "public", - "table": "user" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "username", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "user_username_index", - "entityType": "indexes", - "schema": "public", - "table": "user" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "identifier", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "verification_identifier_index", - "entityType": "indexes", - "schema": "public", - "table": "verification" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "account_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "account" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "apikey_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "passkey_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "passkey" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "resume_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": ["resume_id"], - "schemaTo": "public", - "tableTo": "resume", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "resume_statistics_resume_id_resume_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "resume_statistics" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "session_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "session" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "two_factor_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "two_factor" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "account_pkey", - "schema": "public", - "table": "account", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "apikey_pkey", - "schema": "public", - "table": "apikey", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "passkey_pkey", - "schema": "public", - "table": "passkey", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "resume_pkey", - "schema": "public", - "table": "resume", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "resume_statistics_pkey", - "schema": "public", - "table": "resume_statistics", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "session_pkey", - "schema": "public", - "table": "session", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "two_factor_pkey", - "schema": "public", - "table": "two_factor", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "user_pkey", - "schema": "public", - "table": "user", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "verification_pkey", - "schema": "public", - "table": "verification", - "entityType": "pks" - }, - { - "nameExplicit": false, - "columns": ["slug", "user_id"], - "nullsNotDistinct": false, - "name": "resume_slug_user_id_unique", - "entityType": "uniques", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": ["resume_id"], - "nullsNotDistinct": false, - "name": "resume_statistics_resume_id_key", - "schema": "public", - "table": "resume_statistics", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["token"], - "nullsNotDistinct": false, - "name": "session_token_key", - "schema": "public", - "table": "session", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["email"], - "nullsNotDistinct": false, - "name": "user_email_key", - "schema": "public", - "table": "user", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["username"], - "nullsNotDistinct": false, - "name": "user_username_key", - "schema": "public", - "table": "user", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["display_username"], - "nullsNotDistinct": false, - "name": "user_display_username_key", - "schema": "public", - "table": "user", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["identifier"], - "nullsNotDistinct": false, - "name": "verification_identifier_key", - "schema": "public", - "table": "verification", - "entityType": "uniques" - } - ], - "renames": [] + "version": "8", + "dialect": "postgres", + "id": "24141f99-af12-49b1-aa09-e9f5c75c083d", + "prevIds": ["ce0ad8e6-02da-4a87-a914-7cbe9f3962b9"], + "ddl": [ + { + "isRlsEnabled": false, + "name": "account", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "apikey", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "passkey", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "resume", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "resume_statistics", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "session", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "two_factor", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "user", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "verification", + "entityType": "tables", + "schema": "public" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "account_id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'credential'", + "generated": null, + "identity": null, + "name": "provider_id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "scope", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id_token", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "password", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "access_token", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refresh_token", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "access_token_expires_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refresh_token_expires_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "start", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "prefix", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "key", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refill_interval", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refill_amount", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_refill_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "true", + "generated": null, + "identity": null, + "name": "enabled", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "rate_limit_enabled", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "rate_limit_time_window", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "rate_limit_max", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "request_count", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "remaining", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_request", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "permissions", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "metadata", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "aaguid", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "public_key", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "credential_id", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "counter", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "device_type", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "backed_up", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "transports", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 1, + "default": "'{}'", + "generated": null, + "identity": null, + "name": "tags", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "is_public", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "is_locked", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "password", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "data", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "views", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "downloads", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_viewed_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_downloaded_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "resume_id", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "token", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ip_address", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_agent", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "secret", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "backup_codes", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "image", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "email", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "email_verified", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "username", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "display_username", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "two_factor_enabled", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "identifier", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "value", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "account_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "account" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "apikey_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "key", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "apikey_key_index", + "entityType": "indexes", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "enabled", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "apikey_enabled_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "passkey_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "passkey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "resume_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "updated_at", + "isExpression": false, + "asc": false, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "resume_user_id_updated_at_index", + "entityType": "indexes", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "is_public", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "resume_is_public_slug_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "resume_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "resume_statistics_resume_id_index", + "entityType": "indexes", + "schema": "public", + "table": "resume_statistics" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "token", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "session_token_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "session" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "expires_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "session_expires_at_index", + "entityType": "indexes", + "schema": "public", + "table": "session" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "two_factor_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "two_factor" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "secret", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "two_factor_secret_index", + "entityType": "indexes", + "schema": "public", + "table": "two_factor" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "email", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "user_email_index", + "entityType": "indexes", + "schema": "public", + "table": "user" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "username", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "user_username_index", + "entityType": "indexes", + "schema": "public", + "table": "user" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "identifier", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "verification_identifier_index", + "entityType": "indexes", + "schema": "public", + "table": "verification" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "account_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "account" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "apikey_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "passkey_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "passkey" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "resume_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": ["resume_id"], + "schemaTo": "public", + "tableTo": "resume", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "resume_statistics_resume_id_resume_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "resume_statistics" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "session_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "session" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "two_factor_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "two_factor" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "account_pkey", + "schema": "public", + "table": "account", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "apikey_pkey", + "schema": "public", + "table": "apikey", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "passkey_pkey", + "schema": "public", + "table": "passkey", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "resume_pkey", + "schema": "public", + "table": "resume", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "resume_statistics_pkey", + "schema": "public", + "table": "resume_statistics", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "session_pkey", + "schema": "public", + "table": "session", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "two_factor_pkey", + "schema": "public", + "table": "two_factor", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "user_pkey", + "schema": "public", + "table": "user", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "verification_pkey", + "schema": "public", + "table": "verification", + "entityType": "pks" + }, + { + "nameExplicit": false, + "columns": ["slug", "user_id"], + "nullsNotDistinct": false, + "name": "resume_slug_user_id_unique", + "entityType": "uniques", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": ["resume_id"], + "nullsNotDistinct": false, + "name": "resume_statistics_resume_id_key", + "schema": "public", + "table": "resume_statistics", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["token"], + "nullsNotDistinct": false, + "name": "session_token_key", + "schema": "public", + "table": "session", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["email"], + "nullsNotDistinct": false, + "name": "user_email_key", + "schema": "public", + "table": "user", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["username"], + "nullsNotDistinct": false, + "name": "user_username_key", + "schema": "public", + "table": "user", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["display_username"], + "nullsNotDistinct": false, + "name": "user_display_username_key", + "schema": "public", + "table": "user", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["identifier"], + "nullsNotDistinct": false, + "name": "verification_identifier_key", + "schema": "public", + "table": "verification", + "entityType": "uniques" + } + ], + "renames": [] } diff --git a/migrations/20260210002937_overconfident_bulldozer/snapshot.json b/migrations/20260210002937_overconfident_bulldozer/snapshot.json index f7c57d2e3..cafe7f2ec 100644 --- a/migrations/20260210002937_overconfident_bulldozer/snapshot.json +++ b/migrations/20260210002937_overconfident_bulldozer/snapshot.json @@ -1,1853 +1,1853 @@ { - "version": "8", - "dialect": "postgres", - "id": "c0eba253-7de9-4b88-92ce-2ebeefccccc8", - "prevIds": ["24141f99-af12-49b1-aa09-e9f5c75c083d"], - "ddl": [ - { - "isRlsEnabled": false, - "name": "account", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "apikey", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "passkey", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "resume", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "resume_statistics", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "session", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "two_factor", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "user", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "verification", - "entityType": "tables", - "schema": "public" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "account_id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "'credential'", - "generated": null, - "identity": null, - "name": "provider_id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "scope", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id_token", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "password", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "access_token", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refresh_token", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "access_token_expires_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refresh_token_expires_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "start", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "prefix", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "key", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refill_interval", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refill_amount", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_refill_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "true", - "generated": null, - "identity": null, - "name": "enabled", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "rate_limit_enabled", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "rate_limit_time_window", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "rate_limit_max", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "request_count", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "remaining", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_request", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "expires_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "permissions", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "jsonb", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "metadata", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "aaguid", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "public_key", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "credential_id", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "counter", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "device_type", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "backed_up", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "transports", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "slug", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 1, - "default": "'{}'", - "generated": null, - "identity": null, - "name": "tags", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "is_public", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "is_locked", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "password", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "jsonb", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "data", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "views", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "downloads", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_viewed_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_downloaded_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "resume_id", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "token", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "ip_address", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_agent", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "expires_at", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "secret", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "backup_codes", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "image", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "email", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "email_verified", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "username", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "display_username", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "two_factor_enabled", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "identifier", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "value", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "expires_at", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "account_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "account" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "apikey_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "key", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "apikey_key_index", - "entityType": "indexes", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "enabled", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "apikey_enabled_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "passkey_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "passkey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "resume_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "created_at", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "resume_created_at_index", - "entityType": "indexes", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "updated_at", - "isExpression": false, - "asc": false, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "resume_user_id_updated_at_index", - "entityType": "indexes", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "is_public", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "slug", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "resume_is_public_slug_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "token", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "session_token_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "session" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "expires_at", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "session_expires_at_index", - "entityType": "indexes", - "schema": "public", - "table": "session" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "two_factor_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "two_factor" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "secret", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "two_factor_secret_index", - "entityType": "indexes", - "schema": "public", - "table": "two_factor" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "created_at", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "user_created_at_index", - "entityType": "indexes", - "schema": "public", - "table": "user" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "account_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "account" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "apikey_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "passkey_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "passkey" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "resume_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": ["resume_id"], - "schemaTo": "public", - "tableTo": "resume", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "resume_statistics_resume_id_resume_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "resume_statistics" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "session_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "session" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "two_factor_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "two_factor" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "account_pkey", - "schema": "public", - "table": "account", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "apikey_pkey", - "schema": "public", - "table": "apikey", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "passkey_pkey", - "schema": "public", - "table": "passkey", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "resume_pkey", - "schema": "public", - "table": "resume", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "resume_statistics_pkey", - "schema": "public", - "table": "resume_statistics", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "session_pkey", - "schema": "public", - "table": "session", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "two_factor_pkey", - "schema": "public", - "table": "two_factor", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "user_pkey", - "schema": "public", - "table": "user", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "verification_pkey", - "schema": "public", - "table": "verification", - "entityType": "pks" - }, - { - "nameExplicit": false, - "columns": ["slug", "user_id"], - "nullsNotDistinct": false, - "name": "resume_slug_user_id_unique", - "entityType": "uniques", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": ["resume_id"], - "nullsNotDistinct": false, - "name": "resume_statistics_resume_id_key", - "schema": "public", - "table": "resume_statistics", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["token"], - "nullsNotDistinct": false, - "name": "session_token_key", - "schema": "public", - "table": "session", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["email"], - "nullsNotDistinct": false, - "name": "user_email_key", - "schema": "public", - "table": "user", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["username"], - "nullsNotDistinct": false, - "name": "user_username_key", - "schema": "public", - "table": "user", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["display_username"], - "nullsNotDistinct": false, - "name": "user_display_username_key", - "schema": "public", - "table": "user", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["identifier"], - "nullsNotDistinct": false, - "name": "verification_identifier_key", - "schema": "public", - "table": "verification", - "entityType": "uniques" - } - ], - "renames": [] + "version": "8", + "dialect": "postgres", + "id": "c0eba253-7de9-4b88-92ce-2ebeefccccc8", + "prevIds": ["24141f99-af12-49b1-aa09-e9f5c75c083d"], + "ddl": [ + { + "isRlsEnabled": false, + "name": "account", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "apikey", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "passkey", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "resume", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "resume_statistics", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "session", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "two_factor", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "user", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "verification", + "entityType": "tables", + "schema": "public" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "account_id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'credential'", + "generated": null, + "identity": null, + "name": "provider_id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "scope", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id_token", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "password", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "access_token", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refresh_token", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "access_token_expires_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refresh_token_expires_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "start", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "prefix", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "key", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refill_interval", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refill_amount", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_refill_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "true", + "generated": null, + "identity": null, + "name": "enabled", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "rate_limit_enabled", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "rate_limit_time_window", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "rate_limit_max", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "request_count", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "remaining", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_request", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "permissions", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "metadata", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "aaguid", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "public_key", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "credential_id", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "counter", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "device_type", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "backed_up", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "transports", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 1, + "default": "'{}'", + "generated": null, + "identity": null, + "name": "tags", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "is_public", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "is_locked", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "password", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "data", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "views", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "downloads", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_viewed_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_downloaded_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "resume_id", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "token", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ip_address", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_agent", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "secret", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "backup_codes", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "image", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "email", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "email_verified", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "username", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "display_username", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "two_factor_enabled", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "identifier", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "value", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "account_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "account" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "apikey_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "key", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "apikey_key_index", + "entityType": "indexes", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "enabled", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "apikey_enabled_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "passkey_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "passkey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "resume_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "created_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "resume_created_at_index", + "entityType": "indexes", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "updated_at", + "isExpression": false, + "asc": false, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "resume_user_id_updated_at_index", + "entityType": "indexes", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "is_public", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "resume_is_public_slug_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "token", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "session_token_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "session" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "expires_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "session_expires_at_index", + "entityType": "indexes", + "schema": "public", + "table": "session" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "two_factor_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "two_factor" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "secret", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "two_factor_secret_index", + "entityType": "indexes", + "schema": "public", + "table": "two_factor" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "created_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "user_created_at_index", + "entityType": "indexes", + "schema": "public", + "table": "user" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "account_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "account" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "apikey_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "passkey_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "passkey" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "resume_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": ["resume_id"], + "schemaTo": "public", + "tableTo": "resume", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "resume_statistics_resume_id_resume_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "resume_statistics" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "session_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "session" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "two_factor_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "two_factor" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "account_pkey", + "schema": "public", + "table": "account", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "apikey_pkey", + "schema": "public", + "table": "apikey", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "passkey_pkey", + "schema": "public", + "table": "passkey", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "resume_pkey", + "schema": "public", + "table": "resume", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "resume_statistics_pkey", + "schema": "public", + "table": "resume_statistics", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "session_pkey", + "schema": "public", + "table": "session", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "two_factor_pkey", + "schema": "public", + "table": "two_factor", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "user_pkey", + "schema": "public", + "table": "user", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "verification_pkey", + "schema": "public", + "table": "verification", + "entityType": "pks" + }, + { + "nameExplicit": false, + "columns": ["slug", "user_id"], + "nullsNotDistinct": false, + "name": "resume_slug_user_id_unique", + "entityType": "uniques", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": ["resume_id"], + "nullsNotDistinct": false, + "name": "resume_statistics_resume_id_key", + "schema": "public", + "table": "resume_statistics", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["token"], + "nullsNotDistinct": false, + "name": "session_token_key", + "schema": "public", + "table": "session", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["email"], + "nullsNotDistinct": false, + "name": "user_email_key", + "schema": "public", + "table": "user", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["username"], + "nullsNotDistinct": false, + "name": "user_username_key", + "schema": "public", + "table": "user", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["display_username"], + "nullsNotDistinct": false, + "name": "user_display_username_key", + "schema": "public", + "table": "user", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["identifier"], + "nullsNotDistinct": false, + "name": "verification_identifier_key", + "schema": "public", + "table": "verification", + "entityType": "uniques" + } + ], + "renames": [] } diff --git a/migrations/20260305091016_closed_pixie/snapshot.json b/migrations/20260305091016_closed_pixie/snapshot.json index 9784be7e0..d783729e6 100644 --- a/migrations/20260305091016_closed_pixie/snapshot.json +++ b/migrations/20260305091016_closed_pixie/snapshot.json @@ -1,1866 +1,1866 @@ { - "version": "8", - "dialect": "postgres", - "id": "e438897b-e269-44d5-bbb2-92edab29c225", - "prevIds": ["c0eba253-7de9-4b88-92ce-2ebeefccccc8"], - "ddl": [ - { - "isRlsEnabled": false, - "name": "account", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "apikey", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "passkey", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "resume", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "resume_statistics", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "session", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "two_factor", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "user", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "verification", - "entityType": "tables", - "schema": "public" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "account_id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "'credential'", - "generated": null, - "identity": null, - "name": "provider_id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "scope", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id_token", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "password", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "access_token", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refresh_token", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "access_token_expires_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refresh_token_expires_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "start", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "prefix", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "key", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "config_id", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "reference_id", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refill_interval", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refill_amount", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_refill_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "true", - "generated": null, - "identity": null, - "name": "enabled", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "rate_limit_enabled", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "rate_limit_time_window", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "rate_limit_max", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "request_count", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "remaining", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_request", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "expires_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "permissions", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "jsonb", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "metadata", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "aaguid", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "public_key", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "credential_id", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "counter", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "device_type", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "backed_up", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "transports", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "slug", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 1, - "default": "'{}'", - "generated": null, - "identity": null, - "name": "tags", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "is_public", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "is_locked", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "password", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "jsonb", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "data", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "views", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "downloads", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_viewed_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_downloaded_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "resume_id", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "token", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "ip_address", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_agent", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "expires_at", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "secret", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "backup_codes", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "image", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "email", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "email_verified", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "username", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "display_username", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "two_factor_enabled", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "identifier", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "value", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "expires_at", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "account_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "account" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "reference_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "apikey_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "key", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "apikey_key_index", - "entityType": "indexes", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "enabled", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "reference_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "apikey_enabled_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "passkey_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "passkey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "resume_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "created_at", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "resume_created_at_index", - "entityType": "indexes", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "updated_at", - "isExpression": false, - "asc": false, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "resume_user_id_updated_at_index", - "entityType": "indexes", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "is_public", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "slug", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "resume_is_public_slug_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "token", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "session_token_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "session" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "expires_at", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "session_expires_at_index", - "entityType": "indexes", - "schema": "public", - "table": "session" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "two_factor_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "two_factor" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "secret", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "two_factor_secret_index", - "entityType": "indexes", - "schema": "public", - "table": "two_factor" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "created_at", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "user_created_at_index", - "entityType": "indexes", - "schema": "public", - "table": "user" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "account_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "account" - }, - { - "nameExplicit": false, - "columns": ["reference_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "apikey_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "passkey_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "passkey" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "resume_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": ["resume_id"], - "schemaTo": "public", - "tableTo": "resume", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "resume_statistics_resume_id_resume_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "resume_statistics" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "session_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "session" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "two_factor_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "two_factor" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "account_pkey", - "schema": "public", - "table": "account", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "apikey_pkey", - "schema": "public", - "table": "apikey", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "passkey_pkey", - "schema": "public", - "table": "passkey", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "resume_pkey", - "schema": "public", - "table": "resume", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "resume_statistics_pkey", - "schema": "public", - "table": "resume_statistics", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "session_pkey", - "schema": "public", - "table": "session", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "two_factor_pkey", - "schema": "public", - "table": "two_factor", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "user_pkey", - "schema": "public", - "table": "user", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "verification_pkey", - "schema": "public", - "table": "verification", - "entityType": "pks" - }, - { - "nameExplicit": false, - "columns": ["slug", "user_id"], - "nullsNotDistinct": false, - "name": "resume_slug_user_id_unique", - "entityType": "uniques", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": ["resume_id"], - "nullsNotDistinct": false, - "name": "resume_statistics_resume_id_key", - "schema": "public", - "table": "resume_statistics", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["token"], - "nullsNotDistinct": false, - "name": "session_token_key", - "schema": "public", - "table": "session", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["email"], - "nullsNotDistinct": false, - "name": "user_email_key", - "schema": "public", - "table": "user", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["username"], - "nullsNotDistinct": false, - "name": "user_username_key", - "schema": "public", - "table": "user", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["display_username"], - "nullsNotDistinct": false, - "name": "user_display_username_key", - "schema": "public", - "table": "user", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["identifier"], - "nullsNotDistinct": false, - "name": "verification_identifier_key", - "schema": "public", - "table": "verification", - "entityType": "uniques" - } - ], - "renames": ["public.apikey.user_id->public.apikey.reference_id"] + "version": "8", + "dialect": "postgres", + "id": "e438897b-e269-44d5-bbb2-92edab29c225", + "prevIds": ["c0eba253-7de9-4b88-92ce-2ebeefccccc8"], + "ddl": [ + { + "isRlsEnabled": false, + "name": "account", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "apikey", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "passkey", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "resume", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "resume_statistics", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "session", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "two_factor", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "user", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "verification", + "entityType": "tables", + "schema": "public" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "account_id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'credential'", + "generated": null, + "identity": null, + "name": "provider_id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "scope", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id_token", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "password", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "access_token", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refresh_token", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "access_token_expires_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refresh_token_expires_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "start", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "prefix", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "key", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "config_id", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "reference_id", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refill_interval", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refill_amount", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_refill_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "true", + "generated": null, + "identity": null, + "name": "enabled", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "rate_limit_enabled", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "rate_limit_time_window", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "rate_limit_max", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "request_count", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "remaining", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_request", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "permissions", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "metadata", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "aaguid", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "public_key", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "credential_id", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "counter", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "device_type", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "backed_up", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "transports", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 1, + "default": "'{}'", + "generated": null, + "identity": null, + "name": "tags", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "is_public", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "is_locked", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "password", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "data", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "views", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "downloads", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_viewed_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_downloaded_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "resume_id", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "token", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ip_address", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_agent", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "secret", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "backup_codes", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "image", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "email", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "email_verified", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "username", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "display_username", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "two_factor_enabled", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "identifier", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "value", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "account_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "account" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "reference_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "apikey_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "key", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "apikey_key_index", + "entityType": "indexes", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "enabled", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "reference_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "apikey_enabled_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "passkey_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "passkey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "resume_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "created_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "resume_created_at_index", + "entityType": "indexes", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "updated_at", + "isExpression": false, + "asc": false, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "resume_user_id_updated_at_index", + "entityType": "indexes", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "is_public", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "resume_is_public_slug_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "token", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "session_token_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "session" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "expires_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "session_expires_at_index", + "entityType": "indexes", + "schema": "public", + "table": "session" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "two_factor_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "two_factor" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "secret", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "two_factor_secret_index", + "entityType": "indexes", + "schema": "public", + "table": "two_factor" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "created_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "user_created_at_index", + "entityType": "indexes", + "schema": "public", + "table": "user" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "account_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "account" + }, + { + "nameExplicit": false, + "columns": ["reference_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "apikey_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "passkey_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "passkey" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "resume_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": ["resume_id"], + "schemaTo": "public", + "tableTo": "resume", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "resume_statistics_resume_id_resume_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "resume_statistics" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "session_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "session" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "two_factor_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "two_factor" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "account_pkey", + "schema": "public", + "table": "account", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "apikey_pkey", + "schema": "public", + "table": "apikey", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "passkey_pkey", + "schema": "public", + "table": "passkey", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "resume_pkey", + "schema": "public", + "table": "resume", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "resume_statistics_pkey", + "schema": "public", + "table": "resume_statistics", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "session_pkey", + "schema": "public", + "table": "session", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "two_factor_pkey", + "schema": "public", + "table": "two_factor", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "user_pkey", + "schema": "public", + "table": "user", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "verification_pkey", + "schema": "public", + "table": "verification", + "entityType": "pks" + }, + { + "nameExplicit": false, + "columns": ["slug", "user_id"], + "nullsNotDistinct": false, + "name": "resume_slug_user_id_unique", + "entityType": "uniques", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": ["resume_id"], + "nullsNotDistinct": false, + "name": "resume_statistics_resume_id_key", + "schema": "public", + "table": "resume_statistics", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["token"], + "nullsNotDistinct": false, + "name": "session_token_key", + "schema": "public", + "table": "session", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["email"], + "nullsNotDistinct": false, + "name": "user_email_key", + "schema": "public", + "table": "user", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["username"], + "nullsNotDistinct": false, + "name": "user_username_key", + "schema": "public", + "table": "user", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["display_username"], + "nullsNotDistinct": false, + "name": "user_display_username_key", + "schema": "public", + "table": "user", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["identifier"], + "nullsNotDistinct": false, + "name": "verification_identifier_key", + "schema": "public", + "table": "verification", + "entityType": "uniques" + } + ], + "renames": ["public.apikey.user_id->public.apikey.reference_id"] } diff --git a/migrations/20260318092152_rapid_harpoon/snapshot.json b/migrations/20260318092152_rapid_harpoon/snapshot.json index f9f7bb9c5..c04b25b13 100644 --- a/migrations/20260318092152_rapid_harpoon/snapshot.json +++ b/migrations/20260318092152_rapid_harpoon/snapshot.json @@ -1,1879 +1,1879 @@ { - "version": "8", - "dialect": "postgres", - "id": "7c228b2a-ff9f-4c57-bbcf-eed326f8ec04", - "prevIds": ["e438897b-e269-44d5-bbb2-92edab29c225"], - "ddl": [ - { - "isRlsEnabled": false, - "name": "account", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "apikey", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "passkey", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "resume", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "resume_statistics", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "session", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "two_factor", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "user", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "verification", - "entityType": "tables", - "schema": "public" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "account_id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "'credential'", - "generated": null, - "identity": null, - "name": "provider_id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "scope", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id_token", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "password", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "access_token", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refresh_token", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "access_token_expires_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refresh_token_expires_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "start", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "prefix", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "key", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "'default'", - "generated": null, - "identity": null, - "name": "config_id", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "reference_id", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refill_interval", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refill_amount", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_refill_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "true", - "generated": null, - "identity": null, - "name": "enabled", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "rate_limit_enabled", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "rate_limit_time_window", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "rate_limit_max", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "request_count", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "remaining", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_request", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "expires_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "permissions", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "jsonb", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "metadata", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "aaguid", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "public_key", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "credential_id", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "counter", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "device_type", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "backed_up", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "transports", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "slug", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 1, - "default": "'{}'", - "generated": null, - "identity": null, - "name": "tags", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "is_public", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "is_locked", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "password", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "jsonb", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "data", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "views", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "downloads", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_viewed_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_downloaded_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "resume_id", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "token", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "ip_address", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_agent", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "expires_at", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "secret", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "backup_codes", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "image", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "email", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "email_verified", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "username", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "display_username", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "two_factor_enabled", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_active_at", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "identifier", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "value", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "expires_at", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "account_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "account" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "reference_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "apikey_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "key", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "apikey_key_index", - "entityType": "indexes", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "enabled", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "reference_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "apikey_enabled_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "passkey_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "passkey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "resume_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "created_at", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "resume_created_at_index", - "entityType": "indexes", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "updated_at", - "isExpression": false, - "asc": false, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "resume_user_id_updated_at_index", - "entityType": "indexes", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "is_public", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "slug", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "resume_is_public_slug_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "token", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "session_token_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "session" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "expires_at", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "session_expires_at_index", - "entityType": "indexes", - "schema": "public", - "table": "session" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "two_factor_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "two_factor" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "secret", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "two_factor_secret_index", - "entityType": "indexes", - "schema": "public", - "table": "two_factor" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "created_at", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "user_created_at_index", - "entityType": "indexes", - "schema": "public", - "table": "user" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "account_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "account" - }, - { - "nameExplicit": false, - "columns": ["reference_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "apikey_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "passkey_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "passkey" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "resume_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": ["resume_id"], - "schemaTo": "public", - "tableTo": "resume", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "resume_statistics_resume_id_resume_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "resume_statistics" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "session_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "session" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "two_factor_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "two_factor" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "account_pkey", - "schema": "public", - "table": "account", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "apikey_pkey", - "schema": "public", - "table": "apikey", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "passkey_pkey", - "schema": "public", - "table": "passkey", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "resume_pkey", - "schema": "public", - "table": "resume", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "resume_statistics_pkey", - "schema": "public", - "table": "resume_statistics", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "session_pkey", - "schema": "public", - "table": "session", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "two_factor_pkey", - "schema": "public", - "table": "two_factor", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "user_pkey", - "schema": "public", - "table": "user", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "verification_pkey", - "schema": "public", - "table": "verification", - "entityType": "pks" - }, - { - "nameExplicit": false, - "columns": ["slug", "user_id"], - "nullsNotDistinct": false, - "name": "resume_slug_user_id_unique", - "entityType": "uniques", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": ["resume_id"], - "nullsNotDistinct": false, - "name": "resume_statistics_resume_id_key", - "schema": "public", - "table": "resume_statistics", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["token"], - "nullsNotDistinct": false, - "name": "session_token_key", - "schema": "public", - "table": "session", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["email"], - "nullsNotDistinct": false, - "name": "user_email_key", - "schema": "public", - "table": "user", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["username"], - "nullsNotDistinct": false, - "name": "user_username_key", - "schema": "public", - "table": "user", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["display_username"], - "nullsNotDistinct": false, - "name": "user_display_username_key", - "schema": "public", - "table": "user", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["identifier"], - "nullsNotDistinct": false, - "name": "verification_identifier_key", - "schema": "public", - "table": "verification", - "entityType": "uniques" - } - ], - "renames": [] + "version": "8", + "dialect": "postgres", + "id": "7c228b2a-ff9f-4c57-bbcf-eed326f8ec04", + "prevIds": ["e438897b-e269-44d5-bbb2-92edab29c225"], + "ddl": [ + { + "isRlsEnabled": false, + "name": "account", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "apikey", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "passkey", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "resume", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "resume_statistics", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "session", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "two_factor", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "user", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "verification", + "entityType": "tables", + "schema": "public" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "account_id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'credential'", + "generated": null, + "identity": null, + "name": "provider_id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "scope", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id_token", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "password", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "access_token", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refresh_token", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "access_token_expires_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refresh_token_expires_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "start", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "prefix", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "key", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'default'", + "generated": null, + "identity": null, + "name": "config_id", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "reference_id", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refill_interval", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refill_amount", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_refill_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "true", + "generated": null, + "identity": null, + "name": "enabled", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "rate_limit_enabled", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "rate_limit_time_window", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "rate_limit_max", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "request_count", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "remaining", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_request", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "permissions", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "metadata", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "aaguid", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "public_key", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "credential_id", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "counter", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "device_type", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "backed_up", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "transports", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 1, + "default": "'{}'", + "generated": null, + "identity": null, + "name": "tags", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "is_public", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "is_locked", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "password", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "data", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "views", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "downloads", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_viewed_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_downloaded_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "resume_id", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "token", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ip_address", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_agent", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "secret", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "backup_codes", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "image", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "email", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "email_verified", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "username", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "display_username", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "two_factor_enabled", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_active_at", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "identifier", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "value", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "account_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "account" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "reference_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "apikey_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "key", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "apikey_key_index", + "entityType": "indexes", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "enabled", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "reference_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "apikey_enabled_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "passkey_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "passkey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "resume_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "created_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "resume_created_at_index", + "entityType": "indexes", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "updated_at", + "isExpression": false, + "asc": false, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "resume_user_id_updated_at_index", + "entityType": "indexes", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "is_public", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "resume_is_public_slug_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "token", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "session_token_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "session" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "expires_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "session_expires_at_index", + "entityType": "indexes", + "schema": "public", + "table": "session" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "two_factor_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "two_factor" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "secret", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "two_factor_secret_index", + "entityType": "indexes", + "schema": "public", + "table": "two_factor" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "created_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "user_created_at_index", + "entityType": "indexes", + "schema": "public", + "table": "user" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "account_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "account" + }, + { + "nameExplicit": false, + "columns": ["reference_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "apikey_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "passkey_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "passkey" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "resume_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": ["resume_id"], + "schemaTo": "public", + "tableTo": "resume", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "resume_statistics_resume_id_resume_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "resume_statistics" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "session_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "session" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "two_factor_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "two_factor" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "account_pkey", + "schema": "public", + "table": "account", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "apikey_pkey", + "schema": "public", + "table": "apikey", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "passkey_pkey", + "schema": "public", + "table": "passkey", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "resume_pkey", + "schema": "public", + "table": "resume", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "resume_statistics_pkey", + "schema": "public", + "table": "resume_statistics", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "session_pkey", + "schema": "public", + "table": "session", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "two_factor_pkey", + "schema": "public", + "table": "two_factor", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "user_pkey", + "schema": "public", + "table": "user", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "verification_pkey", + "schema": "public", + "table": "verification", + "entityType": "pks" + }, + { + "nameExplicit": false, + "columns": ["slug", "user_id"], + "nullsNotDistinct": false, + "name": "resume_slug_user_id_unique", + "entityType": "uniques", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": ["resume_id"], + "nullsNotDistinct": false, + "name": "resume_statistics_resume_id_key", + "schema": "public", + "table": "resume_statistics", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["token"], + "nullsNotDistinct": false, + "name": "session_token_key", + "schema": "public", + "table": "session", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["email"], + "nullsNotDistinct": false, + "name": "user_email_key", + "schema": "public", + "table": "user", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["username"], + "nullsNotDistinct": false, + "name": "user_username_key", + "schema": "public", + "table": "user", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["display_username"], + "nullsNotDistinct": false, + "name": "user_display_username_key", + "schema": "public", + "table": "user", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["identifier"], + "nullsNotDistinct": false, + "name": "verification_identifier_key", + "schema": "public", + "table": "verification", + "entityType": "uniques" + } + ], + "renames": [] } diff --git a/migrations/20260318093141_puzzling_pride/snapshot.json b/migrations/20260318093141_puzzling_pride/snapshot.json index 65da49329..2ce2cfffe 100644 --- a/migrations/20260318093141_puzzling_pride/snapshot.json +++ b/migrations/20260318093141_puzzling_pride/snapshot.json @@ -1,1921 +1,1921 @@ { - "version": "8", - "dialect": "postgres", - "id": "c9c88ad3-f804-4cd4-883a-afe376d46792", - "prevIds": ["7c228b2a-ff9f-4c57-bbcf-eed326f8ec04"], - "ddl": [ - { - "isRlsEnabled": false, - "name": "account", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "apikey", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "passkey", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "resume", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "resume_statistics", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "session", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "two_factor", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "user", - "entityType": "tables", - "schema": "public" - }, - { - "isRlsEnabled": false, - "name": "verification", - "entityType": "tables", - "schema": "public" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "account_id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "'credential'", - "generated": null, - "identity": null, - "name": "provider_id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "scope", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id_token", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "password", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "access_token", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refresh_token", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "access_token_expires_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refresh_token_expires_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "account" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "start", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "prefix", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "key", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "'default'", - "generated": null, - "identity": null, - "name": "config_id", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "reference_id", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refill_interval", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "refill_amount", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_refill_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "true", - "generated": null, - "identity": null, - "name": "enabled", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "rate_limit_enabled", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": "86400000", - "generated": null, - "identity": null, - "name": "rate_limit_time_window", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": "10", - "generated": null, - "identity": null, - "name": "rate_limit_max", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "request_count", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "remaining", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_request", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "expires_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "permissions", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "jsonb", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "metadata", - "entityType": "columns", - "schema": "public", - "table": "apikey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "aaguid", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "public_key", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "credential_id", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "counter", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "device_type", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "backed_up", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "transports", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "passkey" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "slug", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 1, - "default": "'{}'", - "generated": null, - "identity": null, - "name": "tags", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "is_public", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "is_locked", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "password", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "jsonb", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "data", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "resume" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "views", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "integer", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "0", - "generated": null, - "identity": null, - "name": "downloads", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_viewed_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_downloaded_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "resume_id", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "resume_statistics" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "token", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "ip_address", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_agent", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "expires_at", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "session" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "user_id", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "secret", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "backup_codes", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "two_factor" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "image", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "name", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "email", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "email_verified", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "username", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "display_username", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "boolean", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "false", - "generated": null, - "identity": null, - "name": "two_factor_enabled", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": false, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "last_active_at", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "user" - }, - { - "type": "uuid", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "id", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "identifier", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "text", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "value", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": null, - "generated": null, - "identity": null, - "name": "expires_at", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "created_at", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "type": "timestamp with time zone", - "typeSchema": null, - "notNull": true, - "dimensions": 0, - "default": "now()", - "generated": null, - "identity": null, - "name": "updated_at", - "entityType": "columns", - "schema": "public", - "table": "verification" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "account_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "account" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "reference_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "apikey_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "key", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "apikey_key_index", - "entityType": "indexes", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "config_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "apikey_config_id_index", - "entityType": "indexes", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "enabled", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "reference_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "apikey_enabled_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "passkey_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "passkey" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "resume_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "created_at", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "resume_created_at_index", - "entityType": "indexes", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "updated_at", - "isExpression": false, - "asc": false, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "resume_user_id_updated_at_index", - "entityType": "indexes", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "is_public", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "slug", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "resume_is_public_slug_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "token", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - }, - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "session_token_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "session" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "expires_at", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "session_expires_at_index", - "entityType": "indexes", - "schema": "public", - "table": "session" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "user_id", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "two_factor_user_id_index", - "entityType": "indexes", - "schema": "public", - "table": "two_factor" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "secret", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "two_factor_secret_index", - "entityType": "indexes", - "schema": "public", - "table": "two_factor" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "created_at", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "user_created_at_index", - "entityType": "indexes", - "schema": "public", - "table": "user" - }, - { - "nameExplicit": false, - "columns": [ - { - "value": "identifier", - "isExpression": false, - "asc": true, - "nullsFirst": false, - "opclass": null - } - ], - "isUnique": false, - "where": null, - "with": "", - "method": "btree", - "concurrently": false, - "name": "verification_identifier_index", - "entityType": "indexes", - "schema": "public", - "table": "verification" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "account_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "account" - }, - { - "nameExplicit": false, - "columns": ["reference_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "apikey_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "apikey" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "passkey_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "passkey" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "resume_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": ["resume_id"], - "schemaTo": "public", - "tableTo": "resume", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "resume_statistics_resume_id_resume_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "resume_statistics" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "session_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "session" - }, - { - "nameExplicit": false, - "columns": ["user_id"], - "schemaTo": "public", - "tableTo": "user", - "columnsTo": ["id"], - "onUpdate": "NO ACTION", - "onDelete": "CASCADE", - "name": "two_factor_user_id_user_id_fkey", - "entityType": "fks", - "schema": "public", - "table": "two_factor" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "account_pkey", - "schema": "public", - "table": "account", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "apikey_pkey", - "schema": "public", - "table": "apikey", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "passkey_pkey", - "schema": "public", - "table": "passkey", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "resume_pkey", - "schema": "public", - "table": "resume", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "resume_statistics_pkey", - "schema": "public", - "table": "resume_statistics", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "session_pkey", - "schema": "public", - "table": "session", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "two_factor_pkey", - "schema": "public", - "table": "two_factor", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "user_pkey", - "schema": "public", - "table": "user", - "entityType": "pks" - }, - { - "columns": ["id"], - "nameExplicit": false, - "name": "verification_pkey", - "schema": "public", - "table": "verification", - "entityType": "pks" - }, - { - "nameExplicit": false, - "columns": ["slug", "user_id"], - "nullsNotDistinct": false, - "name": "resume_slug_user_id_unique", - "entityType": "uniques", - "schema": "public", - "table": "resume" - }, - { - "nameExplicit": false, - "columns": ["resume_id"], - "nullsNotDistinct": false, - "name": "resume_statistics_resume_id_key", - "schema": "public", - "table": "resume_statistics", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["token"], - "nullsNotDistinct": false, - "name": "session_token_key", - "schema": "public", - "table": "session", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["email"], - "nullsNotDistinct": false, - "name": "user_email_key", - "schema": "public", - "table": "user", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["username"], - "nullsNotDistinct": false, - "name": "user_username_key", - "schema": "public", - "table": "user", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["display_username"], - "nullsNotDistinct": false, - "name": "user_display_username_key", - "schema": "public", - "table": "user", - "entityType": "uniques" - }, - { - "nameExplicit": false, - "columns": ["identifier"], - "nullsNotDistinct": false, - "name": "verification_identifier_key", - "schema": "public", - "table": "verification", - "entityType": "uniques" - } - ], - "renames": [] + "version": "8", + "dialect": "postgres", + "id": "c9c88ad3-f804-4cd4-883a-afe376d46792", + "prevIds": ["7c228b2a-ff9f-4c57-bbcf-eed326f8ec04"], + "ddl": [ + { + "isRlsEnabled": false, + "name": "account", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "apikey", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "passkey", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "resume", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "resume_statistics", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "session", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "two_factor", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "user", + "entityType": "tables", + "schema": "public" + }, + { + "isRlsEnabled": false, + "name": "verification", + "entityType": "tables", + "schema": "public" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "account_id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'credential'", + "generated": null, + "identity": null, + "name": "provider_id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "scope", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id_token", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "password", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "access_token", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refresh_token", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "access_token_expires_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refresh_token_expires_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "account" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "start", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "prefix", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "key", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "'default'", + "generated": null, + "identity": null, + "name": "config_id", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "reference_id", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refill_interval", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "refill_amount", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_refill_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "true", + "generated": null, + "identity": null, + "name": "enabled", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "rate_limit_enabled", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": "86400000", + "generated": null, + "identity": null, + "name": "rate_limit_time_window", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": "10", + "generated": null, + "identity": null, + "name": "rate_limit_max", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "request_count", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "remaining", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_request", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "permissions", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "metadata", + "entityType": "columns", + "schema": "public", + "table": "apikey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "aaguid", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "public_key", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "credential_id", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "counter", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "device_type", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "backed_up", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "transports", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "passkey" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "slug", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 1, + "default": "'{}'", + "generated": null, + "identity": null, + "name": "tags", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "is_public", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "is_locked", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "password", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "jsonb", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "data", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "resume" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "views", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "integer", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "0", + "generated": null, + "identity": null, + "name": "downloads", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_viewed_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_downloaded_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "resume_id", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "resume_statistics" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "token", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "ip_address", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_agent", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "session" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "user_id", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "secret", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "backup_codes", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "two_factor" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "image", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "name", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "email", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "email_verified", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "username", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "display_username", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "boolean", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "false", + "generated": null, + "identity": null, + "name": "two_factor_enabled", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": false, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "last_active_at", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "user" + }, + { + "type": "uuid", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "id", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "identifier", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "text", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "value", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": null, + "generated": null, + "identity": null, + "name": "expires_at", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "created_at", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "type": "timestamp with time zone", + "typeSchema": null, + "notNull": true, + "dimensions": 0, + "default": "now()", + "generated": null, + "identity": null, + "name": "updated_at", + "entityType": "columns", + "schema": "public", + "table": "verification" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "account_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "account" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "reference_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "apikey_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "key", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "apikey_key_index", + "entityType": "indexes", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "config_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "apikey_config_id_index", + "entityType": "indexes", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "enabled", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "reference_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "apikey_enabled_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "passkey_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "passkey" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "resume_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "created_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "resume_created_at_index", + "entityType": "indexes", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "updated_at", + "isExpression": false, + "asc": false, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "resume_user_id_updated_at_index", + "entityType": "indexes", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "is_public", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "slug", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "resume_is_public_slug_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "token", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + }, + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "session_token_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "session" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "expires_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "session_expires_at_index", + "entityType": "indexes", + "schema": "public", + "table": "session" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "user_id", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "two_factor_user_id_index", + "entityType": "indexes", + "schema": "public", + "table": "two_factor" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "secret", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "two_factor_secret_index", + "entityType": "indexes", + "schema": "public", + "table": "two_factor" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "created_at", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "user_created_at_index", + "entityType": "indexes", + "schema": "public", + "table": "user" + }, + { + "nameExplicit": false, + "columns": [ + { + "value": "identifier", + "isExpression": false, + "asc": true, + "nullsFirst": false, + "opclass": null + } + ], + "isUnique": false, + "where": null, + "with": "", + "method": "btree", + "concurrently": false, + "name": "verification_identifier_index", + "entityType": "indexes", + "schema": "public", + "table": "verification" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "account_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "account" + }, + { + "nameExplicit": false, + "columns": ["reference_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "apikey_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "apikey" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "passkey_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "passkey" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "resume_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": ["resume_id"], + "schemaTo": "public", + "tableTo": "resume", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "resume_statistics_resume_id_resume_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "resume_statistics" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "session_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "session" + }, + { + "nameExplicit": false, + "columns": ["user_id"], + "schemaTo": "public", + "tableTo": "user", + "columnsTo": ["id"], + "onUpdate": "NO ACTION", + "onDelete": "CASCADE", + "name": "two_factor_user_id_user_id_fkey", + "entityType": "fks", + "schema": "public", + "table": "two_factor" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "account_pkey", + "schema": "public", + "table": "account", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "apikey_pkey", + "schema": "public", + "table": "apikey", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "passkey_pkey", + "schema": "public", + "table": "passkey", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "resume_pkey", + "schema": "public", + "table": "resume", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "resume_statistics_pkey", + "schema": "public", + "table": "resume_statistics", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "session_pkey", + "schema": "public", + "table": "session", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "two_factor_pkey", + "schema": "public", + "table": "two_factor", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "user_pkey", + "schema": "public", + "table": "user", + "entityType": "pks" + }, + { + "columns": ["id"], + "nameExplicit": false, + "name": "verification_pkey", + "schema": "public", + "table": "verification", + "entityType": "pks" + }, + { + "nameExplicit": false, + "columns": ["slug", "user_id"], + "nullsNotDistinct": false, + "name": "resume_slug_user_id_unique", + "entityType": "uniques", + "schema": "public", + "table": "resume" + }, + { + "nameExplicit": false, + "columns": ["resume_id"], + "nullsNotDistinct": false, + "name": "resume_statistics_resume_id_key", + "schema": "public", + "table": "resume_statistics", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["token"], + "nullsNotDistinct": false, + "name": "session_token_key", + "schema": "public", + "table": "session", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["email"], + "nullsNotDistinct": false, + "name": "user_email_key", + "schema": "public", + "table": "user", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["username"], + "nullsNotDistinct": false, + "name": "user_username_key", + "schema": "public", + "table": "user", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["display_username"], + "nullsNotDistinct": false, + "name": "user_display_username_key", + "schema": "public", + "table": "user", + "entityType": "uniques" + }, + { + "nameExplicit": false, + "columns": ["identifier"], + "nullsNotDistinct": false, + "name": "verification_identifier_key", + "schema": "public", + "table": "verification", + "entityType": "uniques" + } + ], + "renames": [] } diff --git a/package.json b/package.json index 6b925bf40..e44e1b148 100644 --- a/package.json +++ b/package.json @@ -1,176 +1,176 @@ { - "name": "reactive-resume", - "version": "5.0.12", - "description": "Reactive Resume is a free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.", - "license": "MIT", - "author": { - "name": "Amruth Pillai", - "email": "hello@amruthpillai.com", - "url": "https://amruthpillai.com" - }, - "repository": { - "type": "git", - "url": "https://github.com/amruthpillai/reactive-resume.git" - }, - "type": "module", - "scripts": { - "build": "vite build", - "db:generate": "drizzle-kit generate", - "db:migrate": "drizzle-kit migrate", - "db:pull": "drizzle-kit pull", - "db:push": "drizzle-kit push", - "db:studio": "drizzle-kit studio", - "dev": "vite dev", - "docs:dev": "cd docs && npx mint dev", - "fmt": "oxfmt --check", - "fmt:fix": "oxfmt", - "knip": "knip", - "lingui:extract": "lingui extract --clean --overwrite", - "lint": "oxlint --type-aware", - "lint:fix": "oxlint --type-aware --fix", - "preview": "vite preview", - "start": "node .output/server/index.mjs", - "typecheck": "tsgo --noEmit" - }, - "dependencies": { - "@ai-sdk/anthropic": "^3.0.58", - "@ai-sdk/google": "^3.0.43", - "@ai-sdk/openai": "^3.0.41", - "@ai-sdk/react": "^3.0.118", - "@aws-sdk/client-s3": "^3.1011.0", - "@base-ui/react": "^1.3.0", - "@better-auth/api-key": "^1.5.5", - "@better-auth/drizzle-adapter": "^1.5.5", - "@better-auth/infra": "^0.1.12", - "@dnd-kit/core": "^6.3.1", - "@dnd-kit/sortable": "^10.0.0", - "@dnd-kit/utilities": "^3.2.2", - "@fontsource-variable/ibm-plex-sans": "^5.2.8", - "@hookform/resolvers": "^5.2.2", - "@lingui/core": "^5.9.3", - "@lingui/react": "^5.9.3", - "@modelcontextprotocol/sdk": "^1.27.1", - "@monaco-editor/react": "4.8.0-rc.3", - "@orpc/client": "^1.13.8", - "@orpc/json-schema": "^1.13.8", - "@orpc/openapi": "^1.13.8", - "@orpc/server": "^1.13.8", - "@orpc/tanstack-query": "^1.13.8", - "@orpc/zod": "^1.13.8", - "@phosphor-icons/react": "^2.1.10", - "@phosphor-icons/web": "^2.1.2", - "@sindresorhus/slugify": "^3.0.0", - "@t3-oss/env-core": "^0.13.10", - "@tanstack/react-query": "^5.90.21", - "@tanstack/react-router": "^1.167.4", - "@tanstack/react-router-ssr-query": "^1.166.9", - "@tanstack/react-start": "^1.166.16", - "@tanstack/zod-adapter": "^1.166.9", - "@tiptap/extension-highlight": "^3.20.4", - "@tiptap/extension-table": "^3.20.4", - "@tiptap/extension-text-align": "^3.20.4", - "@tiptap/pm": "^3.20.4", - "@tiptap/react": "^3.20.4", - "@tiptap/starter-kit": "^3.20.4", - "@uiw/color-convert": "^2.9.6", - "@uiw/react-color-colorful": "^2.9.6", - "ai": "^6.0.116", - "ai-sdk-ollama": "^3.8.1", - "bcrypt": "^6.0.0", - "better-auth": "^1.5.5", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "cmdk": "^1.1.1", - "dompurify": "^3.3.3", - "drizzle-orm": "1.0.0-beta.18-7eb39f0", - "drizzle-zod": "^1.0.0-beta.14-a36c63d", - "es-toolkit": "^1.45.1", - "fast-deep-equal": "^3.1.3", - "fast-json-patch": "^3.1.1", - "fuse.js": "^7.1.0", - "immer": "^11.1.4", - "input-otp": "^1.4.2", - "js-cookie": "^3.0.5", - "jsonrepair": "^3.13.3", - "monaco-editor": "^0.55.1", - "motion": "^12.38.0", - "nodemailer": "^8.0.3", - "pg": "^8.20.0", - "puppeteer-core": "^24.39.1", - "qrcode.react": "^4.2.0", - "react": "^19.2.4", - "react-dom": "^19.2.4", - "react-hook-form": "^7.71.2", - "react-hotkeys-hook": "^5.2.4", - "react-resizable-panels": "^4.7.3", - "react-window": "^2.2.7", - "react-zoom-pan-pinch": "^3.7.0", - "shadcn": "^4.0.8", - "sharp": "^0.34.5", - "sonner": "^2.0.7", - "srvx": "^0.11.12", - "tailwind-merge": "^3.5.0", - "tailwindcss": "^4.2.1", - "ts-pattern": "^5.9.0", - "tw-animate-css": "^1.4.0", - "unique-names-generator": "^4.7.1", - "usehooks-ts": "^3.1.1", - "uuid": "^13.0.0", - "workbox-window": "^7.4.0", - "zod": "^4.3.6", - "zundo": "^2.3.0", - "zustand": "^5.0.12" - }, - "devDependencies": { - "@babel/core": "^7.29.0", - "@lingui/babel-plugin-lingui-macro": "^5.9.3", - "@lingui/cli": "^5.9.3", - "@lingui/vite-plugin": "^5.9.3", - "@rolldown/plugin-babel": "^0.2.2", - "@tailwindcss/vite": "^4.2.1", - "@types/bcrypt": "^6.0.0", - "@types/js-cookie": "^3.0.6", - "@types/node": "^25.5.0", - "@types/nodemailer": "^7.0.11", - "@types/pg": "^8.18.0", - "@types/react": "^19.2.14", - "@types/react-dom": "^19.2.3", - "@typescript/native-preview": "7.0.0-dev.20260318.1", - "@vitejs/plugin-react": "^6.0.1", - "babel-plugin-macros": "^3.1.0", - "drizzle-kit": "1.0.0-beta.18-7eb39f0", - "knip": "^5.88.0", - "nitro": "3.0.260311-beta", - "node-addon-api": "^8.6.0", - "node-gyp": "^12.2.0", - "npm-check-updates": "^19.6.5", - "oxfmt": "^0.41.0", - "oxlint": "^1.56.0", - "oxlint-tsgolint": "^0.17.0", - "tsx": "^4.21.0", - "vite": "^8.0.0", - "vite-plugin-pwa": "^1.2.0" - }, - "packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be", - "pnpm": { - "overrides": { - "@isaacs/brace-expansion": "^5.0.1", - "ajv": "^8.18.0", - "basic-ftp": "^5.2.0", - "fast-xml-parser": "^5.3.8", - "hono": "^4.11.10", - "lodash": "^4.17.23", - "markdown-it": "^14.1.1", - "minimatch": "^10.2.3", - "qs": "^6.14.2", - "serialize-javascript": "^7.0.4", - "vite": "^8.0.0" - }, - "onlyBuiltDependencies": [ - "bcrypt", - "esbuild", - "msw", - "sharp" - ] - } + "name": "reactive-resume", + "version": "5.0.12", + "description": "Reactive Resume is a free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.", + "license": "MIT", + "author": { + "name": "Amruth Pillai", + "email": "hello@amruthpillai.com", + "url": "https://amruthpillai.com" + }, + "repository": { + "type": "git", + "url": "https://github.com/amruthpillai/reactive-resume.git" + }, + "type": "module", + "scripts": { + "build": "vp build", + "db:generate": "drizzle-kit generate", + "db:migrate": "drizzle-kit migrate", + "db:pull": "drizzle-kit pull", + "db:push": "drizzle-kit push", + "db:studio": "drizzle-kit studio", + "dev": "vp dev", + "docs:dev": "cd docs && npx mint dev", + "fmt": "vp fmt --check", + "fmt:fix": "vp fmt", + "knip": "knip", + "lingui:extract": "lingui extract --clean --overwrite", + "lint": "vp lint --type-aware", + "lint:fix": "vp lint --type-aware --fix", + "prepare": "vp config", + "preview": "vp preview", + "start": "node .output/server/index.mjs", + "typecheck": "tsgo --noEmit" + }, + "dependencies": { + "@ai-sdk/anthropic": "^3.0.58", + "@ai-sdk/google": "^3.0.43", + "@ai-sdk/openai": "^3.0.41", + "@ai-sdk/react": "^3.0.118", + "@aws-sdk/client-s3": "^3.1012.0", + "@base-ui/react": "^1.3.0", + "@better-auth/api-key": "^1.5.5", + "@better-auth/drizzle-adapter": "^1.5.5", + "@better-auth/infra": "^0.1.12", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", + "@fontsource-variable/ibm-plex-sans": "^5.2.8", + "@hookform/resolvers": "^5.2.2", + "@lingui/core": "^5.9.3", + "@lingui/react": "^5.9.3", + "@modelcontextprotocol/sdk": "^1.27.1", + "@monaco-editor/react": "4.8.0-rc.3", + "@orpc/client": "^1.13.8", + "@orpc/json-schema": "^1.13.8", + "@orpc/openapi": "^1.13.8", + "@orpc/server": "^1.13.8", + "@orpc/tanstack-query": "^1.13.8", + "@orpc/zod": "^1.13.8", + "@phosphor-icons/react": "^2.1.10", + "@phosphor-icons/web": "^2.1.2", + "@sindresorhus/slugify": "^3.0.0", + "@t3-oss/env-core": "^0.13.10", + "@tanstack/react-query": "^5.91.0", + "@tanstack/react-router": "^1.167.5", + "@tanstack/react-router-ssr-query": "^1.166.9", + "@tanstack/react-start": "^1.166.17", + "@tanstack/zod-adapter": "^1.166.9", + "@tiptap/extension-highlight": "^3.20.4", + "@tiptap/extension-table": "^3.20.4", + "@tiptap/extension-text-align": "^3.20.4", + "@tiptap/pm": "^3.20.4", + "@tiptap/react": "^3.20.4", + "@tiptap/starter-kit": "^3.20.4", + "@uiw/color-convert": "^2.9.6", + "@uiw/react-color-colorful": "^2.9.6", + "ai": "^6.0.116", + "ai-sdk-ollama": "^3.8.1", + "bcrypt": "^6.0.0", + "better-auth": "^1.5.5", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.1.1", + "dompurify": "^3.3.3", + "drizzle-orm": "1.0.0-beta.18-7eb39f0", + "drizzle-zod": "^1.0.0-beta.14-a36c63d", + "es-toolkit": "^1.45.1", + "fast-deep-equal": "^3.1.3", + "fast-json-patch": "^3.1.1", + "fuse.js": "^7.1.0", + "immer": "^11.1.4", + "input-otp": "^1.4.2", + "js-cookie": "^3.0.5", + "jsonrepair": "^3.13.3", + "monaco-editor": "^0.55.1", + "motion": "^12.38.0", + "nodemailer": "^8.0.3", + "pg": "^8.20.0", + "puppeteer-core": "^24.39.1", + "qrcode.react": "^4.2.0", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "react-hook-form": "^7.71.2", + "react-hotkeys-hook": "^5.2.4", + "react-resizable-panels": "^4.7.3", + "react-window": "^2.2.7", + "react-zoom-pan-pinch": "^3.7.0", + "shadcn": "^4.0.8", + "sharp": "^0.34.5", + "sonner": "^2.0.7", + "srvx": "^0.11.12", + "tailwind-merge": "^3.5.0", + "tailwindcss": "^4.2.2", + "ts-pattern": "^5.9.0", + "tw-animate-css": "^1.4.0", + "unique-names-generator": "^4.7.1", + "usehooks-ts": "^3.1.1", + "uuid": "^13.0.0", + "workbox-window": "^7.4.0", + "zod": "^4.3.6", + "zundo": "^2.3.0", + "zustand": "^5.0.12" + }, + "devDependencies": { + "@babel/core": "^7.29.0", + "@lingui/babel-plugin-lingui-macro": "^5.9.3", + "@lingui/cli": "^5.9.3", + "@lingui/vite-plugin": "^5.9.3", + "@rolldown/plugin-babel": "^0.2.2", + "@tailwindcss/vite": "^4.2.2", + "@types/bcrypt": "^6.0.0", + "@types/js-cookie": "^3.0.6", + "@types/node": "^25.5.0", + "@types/nodemailer": "^7.0.11", + "@types/pg": "^8.18.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@typescript/native-preview": "7.0.0-dev.20260318.1", + "@vitejs/plugin-react": "^6.0.1", + "babel-plugin-macros": "^3.1.0", + "drizzle-kit": "1.0.0-beta.18-7eb39f0", + "knip": "^5.88.0", + "nitro": "3.0.260311-beta", + "node-addon-api": "^8.6.0", + "node-gyp": "^12.2.0", + "npm-check-updates": "^19.6.5", + "tsx": "^4.21.0", + "vite": "npm:@voidzero-dev/vite-plus-core@latest", + "vite-plugin-pwa": "^1.2.0", + "vite-plus": "latest" + }, + "packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be", + "pnpm": { + "overrides": { + "@isaacs/brace-expansion": "^5.0.1", + "ajv": "^8.18.0", + "basic-ftp": "^5.2.0", + "fast-xml-parser": "^5.3.8", + "hono": "^4.11.10", + "lodash": "^4.17.23", + "markdown-it": "^14.1.1", + "minimatch": "^10.2.3", + "qs": "^6.14.2", + "serialize-javascript": "^7.0.4", + "vite": "npm:@voidzero-dev/vite-plus-core@latest", + "vitest": "npm:@voidzero-dev/vite-plus-test@latest" + }, + "onlyBuiltDependencies": [ + "bcrypt", + "esbuild", + "msw", + "sharp" + ] + } } diff --git a/plugins/1.migrate.ts b/plugins/1.migrate.ts index f00fd626c..c1861ed02 100644 --- a/plugins/1.migrate.ts +++ b/plugins/1.migrate.ts @@ -4,28 +4,28 @@ import { definePlugin } from "nitro"; import { Pool } from "pg"; async function migrateDatabase() { - console.log("⌛ Running database migrations..."); + console.log("⌛ Running database migrations..."); - const connectionString = process.env.DATABASE_URL; + const connectionString = process.env.DATABASE_URL; - if (!connectionString) { - throw new Error("DATABASE_URL is not set"); - } + if (!connectionString) { + throw new Error("DATABASE_URL is not set"); + } - const pool = new Pool({ connectionString }); - const db = drizzle({ client: pool }); + const pool = new Pool({ connectionString }); + const db = drizzle({ client: pool }); - try { - await migrate(db, { migrationsFolder: "./migrations" }); - console.log("✅ Database migrations completed"); - } catch (error) { - console.error("🚨 Database migrations failed:", error); - throw error; - } finally { - await pool.end(); - } + try { + await migrate(db, { migrationsFolder: "./migrations" }); + console.log("✅ Database migrations completed"); + } catch (error) { + console.error("🚨 Database migrations failed:", error); + throw error; + } finally { + await pool.end(); + } } export default definePlugin(async () => { - await migrateDatabase(); + await migrateDatabase(); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4e3930b06..7808b60f5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,17 +5,18 @@ settings: excludeLinksFromLockfile: false overrides: - vite: ^8.0.0 - hono: ^4.11.10 - minimatch: ^10.2.3 '@isaacs/brace-expansion': ^5.0.1 + ajv: ^8.18.0 basic-ftp: ^5.2.0 + fast-xml-parser: ^5.3.8 + hono: ^4.11.10 lodash: ^4.17.23 markdown-it: ^14.1.1 - ajv: ^8.18.0 + minimatch: ^10.2.3 qs: ^6.14.2 serialize-javascript: ^7.0.4 - fast-xml-parser: ^5.3.8 + vite: npm:@voidzero-dev/vite-plus-core@latest + vitest: npm:@voidzero-dev/vite-plus-test@latest importers: @@ -34,20 +35,20 @@ importers: specifier: ^3.0.118 version: 3.0.118(react@19.2.4)(zod@4.3.6) '@aws-sdk/client-s3': - specifier: ^3.1011.0 - version: 3.1011.0 + specifier: ^3.1012.0 + version: 3.1012.0 '@base-ui/react': specifier: ^1.3.0 version: 1.3.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@better-auth/api-key': specifier: ^1.5.5 - version: 1.5.5(0deaaa17e1a55d22e058ad78990ab47b) + version: 1.5.5(0fd0105ce52682e995ba26b97ede8d85) '@better-auth/drizzle-adapter': specifier: ^1.5.5 version: 1.5.5(@better-auth/core@1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(drizzle-orm@1.0.0-beta.18-7eb39f0(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(@azure/core-client@1.10.1))(@types/pg@8.18.0)(mssql@11.0.1(@azure/core-client@1.10.1))(mysql2@3.15.3)(pg@8.20.0)(postgres@3.4.7)(valibot@1.2.0(typescript@5.9.3))(zod@4.3.6)) '@better-auth/infra': specifier: ^0.1.12 - version: 0.1.12(81efdf05db11483743fdd6d8339b7ec2) + version: 0.1.12(655c67ab3f6329590b1caf6edd8bb58e) '@dnd-kit/core': specifier: ^6.3.1 version: 6.3.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -89,7 +90,7 @@ importers: version: 1.13.8(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0) '@orpc/tanstack-query': specifier: ^1.13.8 - version: 1.13.8(@opentelemetry/api@1.9.0)(@orpc/client@1.13.8(@opentelemetry/api@1.9.0))(@tanstack/query-core@5.90.20) + version: 1.13.8(@opentelemetry/api@1.9.0)(@orpc/client@1.13.8(@opentelemetry/api@1.9.0))(@tanstack/query-core@5.91.0) '@orpc/zod': specifier: ^1.13.8 version: 1.13.8(@opentelemetry/api@1.9.0)(@orpc/contract@1.13.8(@opentelemetry/api@1.9.0))(@orpc/server@1.13.8(@opentelemetry/api@1.9.0)(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0))(crossws@0.4.4(srvx@0.11.12))(ws@8.19.0)(zod@4.3.6) @@ -106,20 +107,20 @@ importers: specifier: ^0.13.10 version: 0.13.10(typescript@5.9.3)(valibot@1.2.0(typescript@5.9.3))(zod@4.3.6) '@tanstack/react-query': - specifier: ^5.90.21 - version: 5.90.21(react@19.2.4) + specifier: ^5.91.0 + version: 5.91.0(react@19.2.4) '@tanstack/react-router': - specifier: ^1.167.4 - version: 1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + specifier: ^1.167.5 + version: 1.167.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@tanstack/react-router-ssr-query': specifier: ^1.166.9 - version: 1.166.9(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.21(react@19.2.4))(@tanstack/react-router@1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.167.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.166.9(@tanstack/query-core@5.91.0)(@tanstack/react-query@5.91.0(react@19.2.4))(@tanstack/react-router@1.167.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.167.5)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@tanstack/react-start': - specifier: ^1.166.16 - version: 1.166.16(crossws@0.4.4(srvx@0.11.12))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + specifier: ^1.166.17 + version: 1.166.17(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(crossws@0.4.4(srvx@0.11.12))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@tanstack/zod-adapter': specifier: ^1.166.9 - version: 1.166.9(@tanstack/react-router@1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(zod@4.3.6) + version: 1.166.9(@tanstack/react-router@1.167.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(zod@4.3.6) '@tiptap/extension-highlight': specifier: ^3.20.4 version: 3.20.4(@tiptap/core@3.20.4(@tiptap/pm@3.20.4)) @@ -155,7 +156,7 @@ importers: version: 6.0.0 better-auth: specifier: ^1.5.5 - version: 1.5.5(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.166.16(crossws@0.4.4(srvx@0.11.12))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)))(drizzle-kit@1.0.0-beta.18-7eb39f0)(drizzle-orm@1.0.0-beta.18-7eb39f0(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(@azure/core-client@1.10.1))(@types/pg@8.18.0)(mssql@11.0.1(@azure/core-client@1.10.1))(mysql2@3.15.3)(pg@8.20.0)(postgres@3.4.7)(valibot@1.2.0(typescript@5.9.3))(zod@4.3.6))(mongodb@7.1.0(socks@2.8.7))(mysql2@3.15.3)(pg@8.20.0)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.5.5(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.166.17(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(crossws@0.4.4(srvx@0.11.12))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(drizzle-kit@1.0.0-beta.18-7eb39f0)(drizzle-orm@1.0.0-beta.18-7eb39f0(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(@azure/core-client@1.10.1))(@types/pg@8.18.0)(mssql@11.0.1(@azure/core-client@1.10.1))(mysql2@3.15.3)(pg@8.20.0)(postgres@3.4.7)(valibot@1.2.0(typescript@5.9.3))(zod@4.3.6))(mongodb@7.1.0(socks@2.8.7))(mysql2@3.15.3)(pg@8.20.0)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -253,8 +254,8 @@ importers: specifier: ^3.5.0 version: 3.5.0 tailwindcss: - specifier: ^4.2.1 - version: 4.2.1 + specifier: ^4.2.2 + version: 4.2.2 ts-pattern: specifier: ^5.9.0 version: 5.9.0 @@ -294,13 +295,13 @@ importers: version: 5.9.3(babel-plugin-macros@3.1.0)(typescript@5.9.3) '@lingui/vite-plugin': specifier: ^5.9.3 - version: 5.9.3(babel-plugin-macros@3.1.0)(typescript@5.9.3)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + version: 5.9.3(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(babel-plugin-macros@3.1.0)(typescript@5.9.3) '@rolldown/plugin-babel': specifier: ^0.2.2 - version: 0.2.2(@babel/core@7.29.0)(@babel/runtime@7.28.6)(rolldown@1.0.0-rc.9)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + version: 0.2.2(@babel/core@7.29.0)(@babel/runtime@7.28.6)(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(rolldown@1.0.0-rc.9) '@tailwindcss/vite': - specifier: ^4.2.1 - version: 4.2.1(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + specifier: ^4.2.2 + version: 4.2.2(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)) '@types/bcrypt': specifier: ^6.0.0 version: 6.0.0 @@ -327,7 +328,7 @@ importers: version: 7.0.0-dev.20260318.1 '@vitejs/plugin-react': specifier: ^6.0.1 - version: 6.0.1(@rolldown/plugin-babel@0.2.2(@babel/core@7.29.0)(@babel/runtime@7.28.6)(rolldown@1.0.0-rc.9)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)))(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + version: 6.0.1(@rolldown/plugin-babel@0.2.2(@babel/core@7.29.0)(@babel/runtime@7.28.6)(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(rolldown@1.0.0-rc.9))(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)) babel-plugin-macros: specifier: ^3.1.0 version: 3.1.0 @@ -339,7 +340,7 @@ importers: version: 5.88.0(@types/node@25.5.0)(typescript@5.9.3) nitro: specifier: 3.0.260311-beta - version: 3.0.260311-beta(@azure/identity@4.13.0)(@electric-sql/pglite@0.3.15)(chokidar@4.0.3)(dotenv@17.3.1)(drizzle-orm@1.0.0-beta.18-7eb39f0(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(@azure/core-client@1.10.1))(@types/pg@8.18.0)(mssql@11.0.1(@azure/core-client@1.10.1))(mysql2@3.15.3)(pg@8.20.0)(postgres@3.4.7)(valibot@1.2.0(typescript@5.9.3))(zod@4.3.6))(giget@2.0.0)(jiti@2.6.1)(lru-cache@11.2.6)(mongodb@7.1.0(socks@2.8.7))(mysql2@3.15.3)(rollup@2.80.0)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + version: 3.0.260311-beta(@azure/identity@4.13.0)(@electric-sql/pglite@0.3.15)(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(chokidar@4.0.3)(dotenv@17.3.1)(drizzle-orm@1.0.0-beta.18-7eb39f0(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(@azure/core-client@1.10.1))(@types/pg@8.18.0)(mssql@11.0.1(@azure/core-client@1.10.1))(mysql2@3.15.3)(pg@8.20.0)(postgres@3.4.7)(valibot@1.2.0(typescript@5.9.3))(zod@4.3.6))(giget@2.0.0)(jiti@2.6.1)(lru-cache@11.2.6)(mongodb@7.1.0(socks@2.8.7))(mysql2@3.15.3)(rollup@2.80.0) node-addon-api: specifier: ^8.6.0 version: 8.6.0 @@ -349,24 +350,18 @@ importers: npm-check-updates: specifier: ^19.6.5 version: 19.6.5 - oxfmt: - specifier: ^0.41.0 - version: 0.41.0 - oxlint: - specifier: ^1.56.0 - version: 1.56.0(oxlint-tsgolint@0.17.0) - oxlint-tsgolint: - specifier: ^0.17.0 - version: 0.17.0 tsx: specifier: ^4.21.0 version: 4.21.0 vite: - specifier: ^8.0.0 - version: 8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + specifier: npm:@voidzero-dev/vite-plus-core@latest + version: '@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)' vite-plugin-pwa: specifier: ^1.2.0 - version: 1.2.0(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))(workbox-build@7.4.0(@types/babel__core@7.20.5))(workbox-window@7.4.0) + version: 1.2.0(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(workbox-build@7.4.0(@types/babel__core@7.20.5))(workbox-window@7.4.0) + vite-plus: + specifier: latest + version: 0.1.12(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) packages: @@ -443,48 +438,48 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-s3@3.1011.0': - resolution: {integrity: sha512-jY7CGX+vfM/DSi4K8UwaZKoXnhqchmAbKFB1kIuHMfPPqW7l3jC/fUVDb95/njMsB2ymYOTusZEzoCTeUB/4qA==} + '@aws-sdk/client-s3@3.1012.0': + resolution: {integrity: sha512-YB44c/NVLwyLw2x8hYSIdMFRwFJyZRuaq1HCTS2RiUWmHucSGxohuKwQdQn/XWh+NILugB+RnXrBkSqTlR3ypw==} engines: {node: '>=20.0.0'} - '@aws-sdk/core@3.973.20': - resolution: {integrity: sha512-i3GuX+lowD892F3IuJf8o6AbyDupMTdyTxQrCJGcn71ni5hTZ82L4nQhcdumxZ7XPJRJJVHS/CR3uYOIIs0PVA==} + '@aws-sdk/core@3.973.21': + resolution: {integrity: sha512-OTUcDX9Yfz/FLKbHjiMaP9D4Hs44lYJzN7zBcrK2nDmBt0Wr8D6nYt12QoBkZsW0nVMFsTIGaZCrsU9zCcIMXQ==} engines: {node: '>=20.0.0'} '@aws-sdk/crc64-nvme@3.972.5': resolution: {integrity: sha512-2VbTstbjKdT+yKi8m7b3a9CiVac+pL/IY2PHJwsaGkkHmuuqkJZIErPck1h6P3T9ghQMLSdMPyW6Qp7Di5swFg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-env@3.972.18': - resolution: {integrity: sha512-X0B8AlQY507i5DwjLByeU2Af4ARsl9Vr84koDcXCbAkplmU+1xBFWxEPrWRAoh56waBne/yJqEloSwvRf4x6XA==} + '@aws-sdk/credential-provider-env@3.972.19': + resolution: {integrity: sha512-33NpkQtmnsjLr9QdZvL3w8bjy+WoBJ+jY8JwuzxIq38rDNi1kwpBWW7Yjh+8bMlksd+ZAWW0fH4S/6OeoAdU5A==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-http@3.972.20': - resolution: {integrity: sha512-ey9Lelj001+oOfrbKmS6R2CJAiXX7QKY4Vj9VJv6L2eE6/VjD8DocHIoYqztTm70xDLR4E1jYPTKfIui+eRNDA==} + '@aws-sdk/credential-provider-http@3.972.21': + resolution: {integrity: sha512-xFke7yjbON4unNOG0TApQwz+o1LH5VhVLgWlUuiLRWNDyBfeHIFje2ck8qHybvJ8Fkm5m3SsN+pvHtVo6PGWlQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-ini@3.972.20': - resolution: {integrity: sha512-5flXSnKHMloObNF+9N0cupKegnH1Z37cdVlpETVgx8/rAhCe+VNlkcZH3HDg2SDn9bI765S+rhNPXGDJJPfbtA==} + '@aws-sdk/credential-provider-ini@3.972.21': + resolution: {integrity: sha512-fmJN7KhB7CoG65w9fC2LVOd2wZbR2d1yJIpZNe2J5CeDPu7nUHSmavuJAeGEoE3OL5UIBVPNhmK/fV/NQrs3Hw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-login@3.972.20': - resolution: {integrity: sha512-gEWo54nfqp2jABMu6HNsjVC4hDLpg9HC8IKSJnp0kqWtxIJYHTmiLSsIfI4ScQjxEwpB+jOOH8dOLax1+hy/Hw==} + '@aws-sdk/credential-provider-login@3.972.21': + resolution: {integrity: sha512-ENU+YCiuQocQjfIf9bPxZ+ZY0wIBkl3SMH22optBQwy8UFpSfonHynXzGT27xQxer4cYTNOpwDqbfo57BusbpQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-node@3.972.21': - resolution: {integrity: sha512-hah8if3/B/Q+LBYN5FukyQ1Mym6PLPDsBOBsIgNEYD6wLyZg0UmUF/OKIVC3nX9XH8TfTPuITK+7N/jenVACWA==} + '@aws-sdk/credential-provider-node@3.972.22': + resolution: {integrity: sha512-VE6i8nkmrRyhKut7nnfCWRbdDf+CfyRr8ixSwdaPDguYlgvkAO2pHu9oK11XzbSuatB0io1ozI/vpYhelXn8Pg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-process@3.972.18': - resolution: {integrity: sha512-Tpl7SRaPoOLT32jbTWchPsn52hYYgJ0kpiFgnwk8pxTANQdUymVSZkzFvv1+oOgZm1CrbQUP9MBeoMZ9IzLZjA==} + '@aws-sdk/credential-provider-process@3.972.19': + resolution: {integrity: sha512-hjj5bFo4kf5/WzAMjDEFByVOMbq5gZiagIpJexf7Kp9nIDaGzhCphMsx03NCA8s9zUJzHlD1lXazd7MS+e03Lg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-sso@3.972.20': - resolution: {integrity: sha512-p+R+PYR5Z7Gjqf/6pvbCnzEHcqPCpLzR7Yf127HjJ6EAb4hUcD+qsNRnuww1sB/RmSeCLxyay8FMyqREw4p1RA==} + '@aws-sdk/credential-provider-sso@3.972.21': + resolution: {integrity: sha512-9jWRCuMZpZKlqCZ46bvievqdfswsyB2yPAr9rOiN+FxaGgf8jrR5iYDqJgscvk1jrbAxiK4cIjHv3XjIAWAhzQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-web-identity@3.972.20': - resolution: {integrity: sha512-rWCmh8o7QY4CsUj63qopzMzkDq/yPpkrpb+CnjBEFSOg/02T/we7sSTVg4QsDiVS9uwZ8VyONhq98qt+pIh3KA==} + '@aws-sdk/credential-provider-web-identity@3.972.21': + resolution: {integrity: sha512-ShWQO/cQVZ+j3zUDK7Kj+m7grPzQCVA2iaZdJ+hJTGvVH5lR32Ip/rgZZ+zBdH6D6wczP9Upa4NMXoqJdGpK1g==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-bucket-endpoint@3.972.8': @@ -495,8 +490,8 @@ packages: resolution: {integrity: sha512-5DTBTiotEES1e2jOHAq//zyzCjeMB78lEHd35u15qnrid4Nxm7diqIf9fQQ3Ov0ChH1V3Vvt13thOnrACmfGVQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.974.0': - resolution: {integrity: sha512-BmdDjqvnuYaC4SY7ypHLXfCSsGYGUZkjCLSZyUAAYn1YT28vbNMJNDwhlfkvvE+hQHG5RJDlEmYuvBxcB9jX1g==} + '@aws-sdk/middleware-flexible-checksums@3.974.1': + resolution: {integrity: sha512-1MQ8czTjW8b8SpM+ZoQ0k5yD4rd19G9ALPlGgbFdRS7bwlm9ArxXWu2M22mUgSjsGJwzDkpV8e9tjUnre6adAw==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-host-header@3.972.8': @@ -515,32 +510,32 @@ packages: resolution: {integrity: sha512-BnnvYs2ZEpdlmZ2PNlV2ZyQ8j8AEkMTjN79y/YA475ER1ByFYrkVR85qmhni8oeTaJcDqbx364wDpitDAA/wCA==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-sdk-s3@3.972.20': - resolution: {integrity: sha512-yhva/xL5H4tWQgsBjwV+RRD0ByCzg0TcByDCLp3GXdn/wlyRNfy8zsswDtCvr1WSKQkSQYlyEzPuWkJG0f5HvQ==} + '@aws-sdk/middleware-sdk-s3@3.972.21': + resolution: {integrity: sha512-SXkHy8OET88y4NaSui3gMfoTpg4jHvcbAVXYJuP74vsgsJKCv/vzWM+0hVJ1W+EBOghd+qFIud80ZiuPt2RXRw==} engines: {node: '>=20.0.0'} '@aws-sdk/middleware-ssec@3.972.8': resolution: {integrity: sha512-wqlK0yO/TxEC2UsY9wIlqeeutF6jjLe0f96Pbm40XscTo57nImUk9lBcw0dPgsm0sppFtAkSlDrfpK+pC30Wqw==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-user-agent@3.972.21': - resolution: {integrity: sha512-62XRl1GDYPpkt7cx1AX1SPy9wgNE9Iw/NPuurJu4lmhCWS7sGKO+kS53TQ8eRmIxy3skmvNInnk0ZbWrU5Dpyg==} + '@aws-sdk/middleware-user-agent@3.972.22': + resolution: {integrity: sha512-pZPNGWZVQvgUIO/P9PXZNz7ciq9mLYb/wQEurg3phKTa3DiBIunIRcgA0eBNwmog6S3oy0KR1bv4EJ4ld9A5sQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/nested-clients@3.996.10': - resolution: {integrity: sha512-SlDol5Z+C7Ivnc2rKGqiqfSUmUZzY1qHfVs9myt/nxVwswgfpjdKahyTzLTx802Zfq0NFRs7AejwKzzzl5Co2w==} + '@aws-sdk/nested-clients@3.996.11': + resolution: {integrity: sha512-i7SwoSR4JB/79JoGDUACnFUQOZwXGLWNX35lIb1Pq72nUGlVV+RFZp+BLa8S+mog2pbXU9+6Kc5YwGiMi5bKhQ==} engines: {node: '>=20.0.0'} '@aws-sdk/region-config-resolver@3.972.8': resolution: {integrity: sha512-1eD4uhTDeambO/PNIDVG19A6+v4NdD7xzwLHDutHsUqz0B+i661MwQB2eYO4/crcCvCiQG4SRm1k81k54FEIvw==} engines: {node: '>=20.0.0'} - '@aws-sdk/signature-v4-multi-region@3.996.8': - resolution: {integrity: sha512-n1qYFD+tbqZuyskVaxUE+t10AUz9g3qzDw3Tp6QZDKmqsjfDmZBd4GIk2EKJJNtcCBtE5YiUjDYA+3djFAFBBg==} + '@aws-sdk/signature-v4-multi-region@3.996.9': + resolution: {integrity: sha512-2aAUwudVQ3uNkCfkBLQwNVD2jkfb299NSeDueXsT2NcNdFrWtHRkiQzX3wk47UFYbm87BkdxrsAJcQO7PdQOhA==} engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.1009.0': - resolution: {integrity: sha512-KCPLuTqN9u0Rr38Arln78fRG9KXpzsPWmof+PZzfAHMMQq2QED6YjQrkrfiH7PDefLWEposY1o4/eGwrmKA4JA==} + '@aws-sdk/token-providers@3.1012.0': + resolution: {integrity: sha512-vzKwy020zjuiF4WTJzejx5nYcXJnRhHpb6i3lyZHIwfFwXG1yX4bzBVNMWYWF+bz1i2Pp2VhJbPyzpqj4VuJXQ==} engines: {node: '>=20.0.0'} '@aws-sdk/types@3.973.6': @@ -562,8 +557,8 @@ packages: '@aws-sdk/util-user-agent-browser@3.972.8': resolution: {integrity: sha512-B3KGXJviV2u6Cdw2SDY2aDhoJkVfY/Q/Trwk2CMSkikE1Oi6gRzxhvhIfiRpHfmIsAhV4EA54TVEX8K6CbHbkA==} - '@aws-sdk/util-user-agent-node@3.973.7': - resolution: {integrity: sha512-Hz6EZMUAEzqUd7e+vZ9LE7mn+5gMbxltXy18v+YSFY+9LBJz15wkNZvw5JqfX3z0FS9n3bgUtz3L5rAsfh4YlA==} + '@aws-sdk/util-user-agent-node@3.973.8': + resolution: {integrity: sha512-Kvb96TafGPLYo4Z2GRCzQTne77epXgiZEo0DDXwavzkWmgDV/1XD1tMA766gzRcHHFUraWsE+4T8DKtPTZUxgQ==} engines: {node: '>=20.0.0'} peerDependencies: aws-crt: '>=1.0.0' @@ -571,8 +566,8 @@ packages: aws-crt: optional: true - '@aws-sdk/xml-builder@3.972.11': - resolution: {integrity: sha512-iitV/gZKQMvY9d7ovmyFnFuTHbBAtrmLnvaSb/3X8vOKyevwtpmEtyc8AdhVWZe0pI/1GsHxlEvQeOePFzy7KQ==} + '@aws-sdk/xml-builder@3.972.12': + resolution: {integrity: sha512-xjyucfn+F+kMf25c+LIUnvX3oyLSlj9T0Vncs5WMQI6G36JdnSwC8g0qf8RajfmSClXr660EpTz7FFKluZ4BqQ==} engines: {node: '>=20.0.0'} '@aws/lambda-invoke-store@0.2.3': @@ -1995,7 +1990,7 @@ packages: resolution: {integrity: sha512-9L9GqWjGWU+WrJAb5MBiH9QxkfagrOOWOFucBSwQgEf9k9612o9TIw8eiFqLnnPhi64ktVUaBgDJhkAYo4XLjw==} engines: {node: '>=20.0.0'} peerDependencies: - vite: ^8.0.0 + vite: ^3 || ^4 || ^5.0.9 || ^6 || ^7 || ^8 '@messageformat/parser@5.1.1': resolution: {integrity: sha512-3p0YRGCcTUCYvBKLIxtDDyrJ0YijGIwrTRu1DT8gIviIDZru8H23+FkY6MJBzM1n9n20CiM4VeDYuBsrrwnLjg==} @@ -2291,124 +2286,124 @@ packages: cpu: [x64] os: [win32] - '@oxfmt/binding-android-arm-eabi@0.41.0': - resolution: {integrity: sha512-REfrqeMKGkfMP+m/ScX4f5jJBSmVNYcpoDF8vP8f8eYPDuPGZmzp56NIUsYmx3h7f6NzC6cE3gqh8GDWrJHCKw==} + '@oxfmt/binding-android-arm-eabi@0.40.0': + resolution: {integrity: sha512-S6zd5r1w/HmqR8t0CTnGjFTBLDq2QKORPwriCHxo4xFNuhmOTABGjPaNvCJJVnrKBLsohOeiDX3YqQfJPF+FXw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxfmt/binding-android-arm64@0.41.0': - resolution: {integrity: sha512-s0b1dxNgb2KomspFV2LfogC2XtSJB42POXF4bMCLJyvQmAGos4ZtjGPfQreToQEaY0FQFjz3030ggI36rF1q5g==} + '@oxfmt/binding-android-arm64@0.40.0': + resolution: {integrity: sha512-/mbS9UUP/5Vbl2D6osIdcYiP0oie63LKMoTyGj5hyMCK/SFkl3EhtyRAfdjPvuvHC0SXdW6ePaTKkBSq1SNcIw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxfmt/binding-darwin-arm64@0.41.0': - resolution: {integrity: sha512-EGXGualADbv/ZmamE7/2DbsrYmjoPlAmHEpTL4vapLF4EfVD6fr8/uQDFnPJkUBjiSWFJZtFNsGeN1B6V3owmA==} + '@oxfmt/binding-darwin-arm64@0.40.0': + resolution: {integrity: sha512-wRt8fRdfLiEhnRMBonlIbKrJWixoEmn6KCjKE9PElnrSDSXETGZfPb8ee+nQNTobXkCVvVLytp2o0obAsxl78Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxfmt/binding-darwin-x64@0.41.0': - resolution: {integrity: sha512-WxySJEvdQQYMmyvISH3qDpTvoS0ebnIP63IMxLLWowJyPp/AAH0hdWtlo+iGNK5y3eVfa5jZguwNaQkDKWpGSw==} + '@oxfmt/binding-darwin-x64@0.40.0': + resolution: {integrity: sha512-fzowhqbOE/NRy+AE5ob0+Y4X243WbWzDb00W+pKwD7d9tOqsAFbtWUwIyqqCoCLxj791m2xXIEeLH/3uz7zCCg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxfmt/binding-freebsd-x64@0.41.0': - resolution: {integrity: sha512-Y2kzMkv3U3oyuYaR4wTfGjOTYTXiFC/hXmG0yVASKkbh02BJkvD98Ij8bIevr45hNZ0DmZEgqiXF+9buD4yMYQ==} + '@oxfmt/binding-freebsd-x64@0.40.0': + resolution: {integrity: sha512-agZ9ITaqdBjcerRRFEHB8s0OyVcQW8F9ZxsszjxzeSthQ4fcN2MuOtQFWec1ed8/lDa50jSLHVE2/xPmTgtCfQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxfmt/binding-linux-arm-gnueabihf@0.41.0': - resolution: {integrity: sha512-ptazDjdUyhket01IjPTT6ULS1KFuBfTUU97osTP96X5y/0oso+AgAaJzuH81oP0+XXyrWIHbRzozSAuQm4p48g==} + '@oxfmt/binding-linux-arm-gnueabihf@0.40.0': + resolution: {integrity: sha512-ZM2oQ47p28TP1DVIp7HL1QoMUgqlBFHey0ksHct7tMXoU5BqjNvPWw7888azzMt25lnyPODVuye1wvNbvVUFOA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm-musleabihf@0.41.0': - resolution: {integrity: sha512-UkoL2OKxFD+56bPEBcdGn+4juTW4HRv/T6w1dIDLnvKKWr6DbarB/mtHXlADKlFiJubJz8pRkttOR7qjYR6lTA==} + '@oxfmt/binding-linux-arm-musleabihf@0.40.0': + resolution: {integrity: sha512-RBFPAxRAIsMisKM47Oe6Lwdv6agZYLz02CUhVCD1sOv5ajAcRMrnwCFBPWwGXpazToW2mjnZxFos8TuFjTU15A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm64-gnu@0.41.0': - resolution: {integrity: sha512-gofu0PuumSOHYczD8p62CPY4UF6ee+rSLZJdUXkpwxg6pILiwSDBIouPskjF/5nF3A7QZTz2O9KFNkNxxFN9tA==} + '@oxfmt/binding-linux-arm64-gnu@0.40.0': + resolution: {integrity: sha512-Nb2XbQ+wV3W2jSIihXdPj7k83eOxeSgYP3N/SRXvQ6ZYPIk6Q86qEh5Gl/7OitX3bQoQrESqm1yMLvZV8/J7dA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-arm64-musl@0.41.0': - resolution: {integrity: sha512-VfVZxL0+6RU86T8F8vKiDBa+iHsr8PAjQmKGBzSCAX70b6x+UOMFl+2dNihmKmUwqkCazCPfYjt6SuAPOeQJ3g==} + '@oxfmt/binding-linux-arm64-musl@0.40.0': + resolution: {integrity: sha512-tGmWhLD/0YMotCdfezlT6tC/MJG/wKpo4vnQ3Cq+4eBk/BwNv7EmkD0VkD5F/dYkT3b8FNU01X2e8vvJuWoM1w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-ppc64-gnu@0.41.0': - resolution: {integrity: sha512-bwzokz2eGvdfJbc0i+zXMJ4BBjQPqg13jyWpEEZDOrBCQ91r8KeY2Mi2kUeuMTZNFXju+jcAbAbpyJxRGla0eg==} + '@oxfmt/binding-linux-ppc64-gnu@0.40.0': + resolution: {integrity: sha512-rVbFyM3e7YhkVnp0IVYjaSHfrBWcTRWb60LEcdNAJcE2mbhTpbqKufx0FrhWfoxOrW/+7UJonAOShoFFLigDqQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-gnu@0.41.0': - resolution: {integrity: sha512-POLM//PCH9uqDeNDwWL3b3DkMmI3oI2cU6hwc2lnztD1o7dzrQs3R9nq555BZ6wI7t2lyhT9CS+CRaz5X0XqLA==} + '@oxfmt/binding-linux-riscv64-gnu@0.40.0': + resolution: {integrity: sha512-3ZqBw14JtWeEoLiioJcXSJz8RQyPE+3jLARnYM1HdPzZG4vk+Ua8CUupt2+d+vSAvMyaQBTN2dZK+kbBS/j5mA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-musl@0.41.0': - resolution: {integrity: sha512-NNK7PzhFqLUwx/G12Xtm6scGv7UITvyGdAR5Y+TlqsG+essnuRWR4jRNODWRjzLZod0T3SayRbnkSIWMBov33w==} + '@oxfmt/binding-linux-riscv64-musl@0.40.0': + resolution: {integrity: sha512-JJ4PPSdcbGBjPvb+O7xYm2FmAsKCyuEMYhqatBAHMp/6TA6rVlf9Z/sYPa4/3Bommb+8nndm15SPFRHEPU5qFA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-s390x-gnu@0.41.0': - resolution: {integrity: sha512-qVf/zDC5cN9eKe4qI/O/m445er1IRl6swsSl7jHkqmOSVfknwCe5JXitYjZca+V/cNJSU/xPlC5EFMabMMFDpw==} + '@oxfmt/binding-linux-s390x-gnu@0.40.0': + resolution: {integrity: sha512-Kp0zNJoX9Ik77wUya2tpBY3W9f40VUoMQLWVaob5SgCrblH/t2xr/9B2bWHfs0WCefuGmqXcB+t0Lq77sbBmZw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-gnu@0.41.0': - resolution: {integrity: sha512-ojxYWu7vUb6ysYqVCPHuAPVZHAI40gfZ0PDtZAMwVmh2f0V8ExpPIKoAKr7/8sNbAXJBBpZhs2coypIo2jJX4w==} + '@oxfmt/binding-linux-x64-gnu@0.40.0': + resolution: {integrity: sha512-7YTCNzleWTaQTqNGUNQ66qVjpoV6DjbCOea+RnpMBly2bpzrI/uu7Rr+2zcgRfNxyjXaFTVQKaRKjqVdeUfeVA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-musl@0.41.0': - resolution: {integrity: sha512-O2exZLBxoCMIv2vlvcbkdedazJPTdG0VSup+0QUCfYQtx751zCZNboX2ZUOiQ/gDTdhtXvSiot0h6GEGkOyalA==} + '@oxfmt/binding-linux-x64-musl@0.40.0': + resolution: {integrity: sha512-hWnSzJ0oegeOwfOEeejYXfBqmnRGHusgtHfCPzmvJvHTwy1s3Neo59UKc1CmpE3zxvrCzJoVHos0rr97GHMNPw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxfmt/binding-openharmony-arm64@0.41.0': - resolution: {integrity: sha512-N+31/VoL+z+NNBt8viy3I4NaIdPbiYeOnB884LKqvXldaE2dRztdPv3q5ipfZYv0RwFp7JfqS4I27K/DSHCakg==} + '@oxfmt/binding-openharmony-arm64@0.40.0': + resolution: {integrity: sha512-28sJC1lR4qtBJGzSRRbPnSW3GxU2+4YyQFE6rCmsUYqZ5XYH8jg0/w+CvEzQ8TuAQz5zLkcA25nFQGwoU0PT3Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxfmt/binding-win32-arm64-msvc@0.41.0': - resolution: {integrity: sha512-Z7NAtu/RN8kjCQ1y5oDD0nTAeRswh3GJ93qwcW51srmidP7XPBmZbLlwERu1W5veCevQJtPS9xmkpcDTYsGIwQ==} + '@oxfmt/binding-win32-arm64-msvc@0.40.0': + resolution: {integrity: sha512-cDkRnyT0dqwF5oIX1Cv59HKCeZQFbWWdUpXa3uvnHFT2iwYSSZspkhgjXjU6iDp5pFPaAEAe9FIbMoTgkTmKPg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxfmt/binding-win32-ia32-msvc@0.41.0': - resolution: {integrity: sha512-uNxxP3l4bJ6VyzIeRqCmBU2Q0SkCFgIhvx9/9dJ9V8t/v+jP1IBsuaLwCXGR8JPHtkj4tFp+RHtUmU2ZYAUpMA==} + '@oxfmt/binding-win32-ia32-msvc@0.40.0': + resolution: {integrity: sha512-7rPemBJjqm5Gkv6ZRCPvK8lE6AqQ/2z31DRdWazyx2ZvaSgL7QGofHXHNouRpPvNsT9yxRNQJgigsWkc+0qg4w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxfmt/binding-win32-x64-msvc@0.41.0': - resolution: {integrity: sha512-49ZSpbZ1noozyPapE8SUOSm3IN0Ze4b5nkO+4+7fq6oEYQQJFhE0saj5k/Gg4oewVPdjn0L3ZFeWk2Vehjcw7A==} + '@oxfmt/binding-win32-x64-msvc@0.40.0': + resolution: {integrity: sha512-/Zmj0yTYSvmha6TG1QnoLqVT7ZMRDqXvFXXBQpIjteEwx9qvUYMBH2xbiOFhDeMUJkGwC3D6fdKsFtaqUvkwNA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -2443,124 +2438,124 @@ packages: cpu: [x64] os: [win32] - '@oxlint/binding-android-arm-eabi@1.56.0': - resolution: {integrity: sha512-IyfYPthZyiSKwAv/dLjeO18SaK8MxLI9Yss2JrRDyweQAkuL3LhEy7pwIwI7uA3KQc1Vdn20kdmj3q0oUIQL6A==} + '@oxlint/binding-android-arm-eabi@1.55.0': + resolution: {integrity: sha512-NhvgAhncTSOhRahQSCnkK/4YIGPjTmhPurQQ2dwt2IvwCMTvZRW5vF2K10UBOxFve4GZDMw6LtXZdC2qeuYIVQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxlint/binding-android-arm64@1.56.0': - resolution: {integrity: sha512-Ga5zYrzH6vc/VFxhn6MmyUnYEfy9vRpwTIks99mY3j6Nz30yYpIkWryI0QKPCgvGUtDSXVLEaMum5nA+WrNOSg==} + '@oxlint/binding-android-arm64@1.55.0': + resolution: {integrity: sha512-P9iWRh+Ugqhg+D7rkc7boHX8o3H2h7YPcZHQIgvVBgnua5tk4LR2L+IBlreZs58/95cd2x3/004p5VsQM9z4SA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxlint/binding-darwin-arm64@1.56.0': - resolution: {integrity: sha512-ogmbdJysnw/D4bDcpf1sPLpFThZ48lYp4aKYm10Z/6Nh1SON6NtnNhTNOlhEY296tDFItsZUz+2tgcSYqh8Eyw==} + '@oxlint/binding-darwin-arm64@1.55.0': + resolution: {integrity: sha512-esakkJIt7WFAhT30P/Qzn96ehFpzdZ1mNuzpOb8SCW7lI4oB8VsyQnkSHREM671jfpuBb/o2ppzBCx5l0jpgMA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxlint/binding-darwin-x64@1.56.0': - resolution: {integrity: sha512-x8QE1h+RAtQ2g+3KPsP6Fk/tdz6zJQUv5c7fTrJxXV3GHOo+Ry5p/PsogU4U+iUZg0rj6hS+E4xi+mnwwlDCWQ==} + '@oxlint/binding-darwin-x64@1.55.0': + resolution: {integrity: sha512-xDMFRCCAEK9fOH6As2z8ELsC+VDGSFRHwIKVSilw+xhgLwTDFu37rtmRbmUlx8rRGS6cWKQPTc47AVxAZEVVPQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxlint/binding-freebsd-x64@1.56.0': - resolution: {integrity: sha512-6G+WMZvwJpMvY7my+/SHEjb7BTk/PFbePqLpmVmUJRIsJMy/UlyYqjpuh0RCgYYkPLcnXm1rUM04kbTk8yS1Yg==} + '@oxlint/binding-freebsd-x64@1.55.0': + resolution: {integrity: sha512-mYZqnwUD7ALCRxGenyLd1uuG+rHCL+OTT6S8FcAbVm/ZT2AZMGjvibp3F6k1SKOb2aeqFATmwRykrE41Q0GWVw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxlint/binding-linux-arm-gnueabihf@1.56.0': - resolution: {integrity: sha512-YYHBsk/sl7fYwQOok+6W5lBPeUEvisznV/HZD2IfZmF3Bns6cPC3Z0vCtSEOaAWTjYWN3jVsdu55jMxKlsdlhg==} + '@oxlint/binding-linux-arm-gnueabihf@1.55.0': + resolution: {integrity: sha512-LcX6RYcF9vL9ESGwJW3yyIZ/d/ouzdOKXxCdey1q0XJOW1asrHsIg5MmyKdEBR4plQx+shvYeQne7AzW5f3T1w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm-musleabihf@1.56.0': - resolution: {integrity: sha512-+AZK8rOUr78y8WT6XkDb04IbMRqauNV+vgT6f8ZLOH8wnpQ9i7Nol0XLxAu+Cq7Sb+J9wC0j6Km5hG8rj47/yQ==} + '@oxlint/binding-linux-arm-musleabihf@1.55.0': + resolution: {integrity: sha512-C+8GS1rPtK+dI7mJFkqoRBkDuqbrNihnyYQsJPS9ez+8zF9JzfvU19lawqt4l/Y23o5uQswE/DORa8aiXUih3w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm64-gnu@1.56.0': - resolution: {integrity: sha512-urse2SnugwJRojUkGSSeH2LPMaje5Q50yQtvtL9HFckiyeqXzoFwOAZqD5TR29R2lq7UHidfFDM9EGcchcbb8A==} + '@oxlint/binding-linux-arm64-gnu@1.55.0': + resolution: {integrity: sha512-ErLE4XbmcCopA4/CIDiH6J1IAaDOMnf/KSx/aFObs4/OjAAM3sFKWGZ57pNOMxhhyBdcmcXwYymph9GwcpcqgQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-arm64-musl@1.56.0': - resolution: {integrity: sha512-rkTZkBfJ4TYLjansjSzL6mgZOdN5IvUnSq3oNJSLwBcNvy3dlgQtpHPrRxrCEbbcp7oQ6If0tkNaqfOsphYZ9g==} + '@oxlint/binding-linux-arm64-musl@1.55.0': + resolution: {integrity: sha512-/kp65avi6zZfqEng56TTuhiy3P/3pgklKIdf38yvYeJ9/PgEeRA2A2AqKAKbZBNAqUzrzHhz9jF6j/PZvhJzTQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxlint/binding-linux-ppc64-gnu@1.56.0': - resolution: {integrity: sha512-uqL1kMH3u69/e1CH2EJhP3CP28jw2ExLsku4o8RVAZ7fySo9zOyI2fy9pVlTAp4voBLVgzndXi3SgtdyCTa2aA==} + '@oxlint/binding-linux-ppc64-gnu@1.55.0': + resolution: {integrity: sha512-A6pTdXwcEEwL/nmz0eUJ6WxmxcoIS+97GbH96gikAyre3s5deC7sts38ZVVowjS2QQFuSWkpA4ZmQC0jZSNvJQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-gnu@1.56.0': - resolution: {integrity: sha512-j0CcMBOgV6KsRaBdsebIeiy7hCjEvq2KdEsiULf2LZqAq0v1M1lWjelhCV57LxsqaIGChXFuFJ0RiFrSRHPhSg==} + '@oxlint/binding-linux-riscv64-gnu@1.55.0': + resolution: {integrity: sha512-clj0lnIN+V52G9tdtZl0LbdTSurnZ1NZj92Je5X4lC7gP5jiCSW+Y/oiDiSauBAD4wrHt2S7nN3pA0zfKYK/6Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-musl@1.56.0': - resolution: {integrity: sha512-7VDOiL8cDG3DQ/CY3yKjbV1c4YPvc4vH8qW09Vv+5ukq3l/Kcyr6XGCd5NvxUmxqDb2vjMpM+eW/4JrEEsUetA==} + '@oxlint/binding-linux-riscv64-musl@1.55.0': + resolution: {integrity: sha512-NNu08pllN5x/O94/sgR3DA8lbrGBnTHsINZZR0hcav1sj79ksTiKKm1mRzvZvacwQ0hUnGinFo+JO75ok2PxYg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxlint/binding-linux-s390x-gnu@1.56.0': - resolution: {integrity: sha512-JGRpX0M+ikD3WpwJ7vKcHKV6Kg0dT52BW2Eu2BupXotYeqGXBrbY+QPkAyKO6MNgKozyTNaRh3r7g+VWgyAQYQ==} + '@oxlint/binding-linux-s390x-gnu@1.55.0': + resolution: {integrity: sha512-BvfQz3PRlWZRoEZ17dZCqgQsMRdpzGZomJkVATwCIGhHVVeHJMQdmdXPSjcT1DCNUrOjXnVyj1RGDj5+/Je2+Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-gnu@1.56.0': - resolution: {integrity: sha512-dNaICPvtmuxFP/VbqdofrLqdS3bM/AKJN3LMJD52si44ea7Be1cBk6NpfIahaysG9Uo+L98QKddU9CD5L8UHnQ==} + '@oxlint/binding-linux-x64-gnu@1.55.0': + resolution: {integrity: sha512-ngSOoFCSBMKVQd24H8zkbcBNc7EHhjnF1sv3mC9NNXQ/4rRjI/4Dj9+9XoDZeFEkF1SX1COSBXF1b2Pr9rqdEw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-musl@1.56.0': - resolution: {integrity: sha512-pF1vOtM+GuXmbklM1hV8WMsn6tCNPvkUzklj/Ej98JhlanbmA2RB1BILgOpwSuCTRTIYx2MXssmEyQQ90QF5aA==} + '@oxlint/binding-linux-x64-musl@1.55.0': + resolution: {integrity: sha512-BDpP7W8GlaG7BR6QjGZAleYzxoyKc/D24spZIF2mB3XsfALQJJT/OBmP8YpeTb1rveFSBHzl8T7l0aqwkWNdGA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxlint/binding-openharmony-arm64@1.56.0': - resolution: {integrity: sha512-bp8NQ4RE6fDIFLa4bdBiOA+TAvkNkg+rslR+AvvjlLTYXLy9/uKAYLQudaQouWihLD/hgkrXIKKzXi5IXOewwg==} + '@oxlint/binding-openharmony-arm64@1.55.0': + resolution: {integrity: sha512-PS6GFvmde/pc3fCA2Srt51glr8Lcxhpf6WIBFfLphndjRrD34NEcses4TSxQrEcxYo6qVywGfylM0ZhSCF2gGA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxlint/binding-win32-arm64-msvc@1.56.0': - resolution: {integrity: sha512-PxT4OJDfMOQBzo3OlzFb9gkoSD+n8qSBxyVq2wQSZIHFQYGEqIRTo9M0ZStvZm5fdhMqaVYpOnJvH2hUMEDk/g==} + '@oxlint/binding-win32-arm64-msvc@1.55.0': + resolution: {integrity: sha512-P6JcLJGs/q1UOvDLzN8otd9JsH4tsuuPDv+p7aHqHM3PrKmYdmUvkNj4K327PTd35AYcznOCN+l4ZOaq76QzSw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxlint/binding-win32-ia32-msvc@1.56.0': - resolution: {integrity: sha512-PTRy6sIEPqy2x8PTP1baBNReN/BNEFmde0L+mYeHmjXE1Vlcc9+I5nsqENsB2yAm5wLkzPoTNCMY/7AnabT4/A==} + '@oxlint/binding-win32-ia32-msvc@1.55.0': + resolution: {integrity: sha512-gzkk4zE2zsE+WmRxFOiAZHpCpUNDFytEakqNXoNHW+PnYEOTPKDdW6nrzgSeTbGKVPXNAKQnRnMgrh7+n3Xueg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxlint/binding-win32-x64-msvc@1.56.0': - resolution: {integrity: sha512-ZHa0clocjLmIDr+1LwoWtxRcoYniAvERotvwKUYKhH41NVfl0Y4LNbyQkwMZzwDvKklKGvGZ5+DAG58/Ik47tQ==} + '@oxlint/binding-win32-x64-msvc@1.55.0': + resolution: {integrity: sha512-ZFALNow2/og75gvYzNP7qe+rREQ5xunktwA+lgykoozHZ6hw9bqg4fn5j2UvG4gIn1FXqrZHkOAXuPf5+GOYTQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -2575,6 +2570,9 @@ packages: '@phosphor-icons/web@2.1.2': resolution: {integrity: sha512-rPAR9o/bEcp4Cw4DEeZHXf+nlGCMNGkNDRizYHM47NLxz9vvEHp/Tt6FMK1NcWadzw/pFDPnRBGi/ofRya958A==} + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@prisma/client-runtime-utils@7.4.2': resolution: {integrity: sha512-cID+rzOEb38VyMsx5LwJMEY4NGIrWCNpKu/0ImbeooQ2Px7TI+kOt7cm0NelxUzF2V41UVVXAmYjANZQtCu1/Q==} @@ -3258,69 +3256,69 @@ packages: zod: optional: true - '@tailwindcss/node@4.2.1': - resolution: {integrity: sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==} + '@tailwindcss/node@4.2.2': + resolution: {integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==} - '@tailwindcss/oxide-android-arm64@4.2.1': - resolution: {integrity: sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==} + '@tailwindcss/oxide-android-arm64@4.2.2': + resolution: {integrity: sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==} engines: {node: '>= 20'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.2.1': - resolution: {integrity: sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==} + '@tailwindcss/oxide-darwin-arm64@4.2.2': + resolution: {integrity: sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==} engines: {node: '>= 20'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.2.1': - resolution: {integrity: sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==} + '@tailwindcss/oxide-darwin-x64@4.2.2': + resolution: {integrity: sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==} engines: {node: '>= 20'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.2.1': - resolution: {integrity: sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==} + '@tailwindcss/oxide-freebsd-x64@4.2.2': + resolution: {integrity: sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==} engines: {node: '>= 20'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': - resolution: {integrity: sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==} + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': + resolution: {integrity: sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==} engines: {node: '>= 20'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': - resolution: {integrity: sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==} + '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': + resolution: {integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] libc: [glibc] - '@tailwindcss/oxide-linux-arm64-musl@4.2.1': - resolution: {integrity: sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==} + '@tailwindcss/oxide-linux-arm64-musl@4.2.2': + resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] libc: [musl] - '@tailwindcss/oxide-linux-x64-gnu@4.2.1': - resolution: {integrity: sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==} + '@tailwindcss/oxide-linux-x64-gnu@4.2.2': + resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==} engines: {node: '>= 20'} cpu: [x64] os: [linux] libc: [glibc] - '@tailwindcss/oxide-linux-x64-musl@4.2.1': - resolution: {integrity: sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==} + '@tailwindcss/oxide-linux-x64-musl@4.2.2': + resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==} engines: {node: '>= 20'} cpu: [x64] os: [linux] libc: [musl] - '@tailwindcss/oxide-wasm32-wasi@4.2.1': - resolution: {integrity: sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==} + '@tailwindcss/oxide-wasm32-wasi@4.2.2': + resolution: {integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -3331,36 +3329,36 @@ packages: - '@emnapi/wasi-threads' - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': - resolution: {integrity: sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==} + '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': + resolution: {integrity: sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==} engines: {node: '>= 20'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.2.1': - resolution: {integrity: sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==} + '@tailwindcss/oxide-win32-x64-msvc@4.2.2': + resolution: {integrity: sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==} engines: {node: '>= 20'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.2.1': - resolution: {integrity: sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==} + '@tailwindcss/oxide@4.2.2': + resolution: {integrity: sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==} engines: {node: '>= 20'} - '@tailwindcss/vite@4.2.1': - resolution: {integrity: sha512-TBf2sJjYeb28jD2U/OhwdW0bbOsxkWPwQ7SrqGf9sVcoYwZj7rkXljroBO9wKBut9XnmQLXanuDUeqQK0lGg/w==} + '@tailwindcss/vite@4.2.2': + resolution: {integrity: sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==} peerDependencies: - vite: ^8.0.0 + vite: ^5.2.0 || ^6 || ^7 || ^8 '@tanstack/history@1.161.6': resolution: {integrity: sha512-NaOGLRrddszbQj9upGat6HG/4TKvXLvu+osAIgfxPYA+eIvYKv8GKDJOrY2D3/U9MRnKfMWD7bU4jeD4xmqyIg==} engines: {node: '>=20.19'} - '@tanstack/query-core@5.90.20': - resolution: {integrity: sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==} + '@tanstack/query-core@5.91.0': + resolution: {integrity: sha512-FYXN8Kk9Q5VKuV6AIVaNwMThSi0nvAtR4X7HQoigf6ePOtFcavJYVIzgFhOVdtbBQtCJE3KimDIMMJM2DR1hjw==} - '@tanstack/react-query@5.90.21': - resolution: {integrity: sha512-0Lu6y5t+tvlTJMTO7oh5NSpJfpg/5D41LlThfepTixPYkJ0sE2Jj0m0f6yYqujBwIXlId87e234+MxG3D3g7kg==} + '@tanstack/react-query@5.91.0': + resolution: {integrity: sha512-S8FODsDTNv0Ym+o/JVBvA6EWiWVhg6K2Q4qFehZyFKk6uW4H9OPbXl4kyiN9hAly0uHJ/1GEbR6kAI4MZWfjEA==} peerDependencies: react: ^18 || ^19 @@ -3374,35 +3372,35 @@ packages: react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - '@tanstack/react-router@1.167.4': - resolution: {integrity: sha512-VpbZh382zX3WF4+X2Z+EUyd8eJhJyjg9C6ByYwrVZiWbhgbMK4+zQQIG2+lCAlIlDi7SV8fDcGL09NA8Z2kpGQ==} + '@tanstack/react-router@1.167.5': + resolution: {integrity: sha512-s1nP6l/7BYZfSwhoNbB7/rUmZ07q/AvkmhBoiDQl3tgy5dpb9Q1qjtIapYdvCOrao1aA/QCaWqxcbGc2Ct1bvQ==} engines: {node: '>=20.19'} peerDependencies: react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - '@tanstack/react-start-client@1.166.13': - resolution: {integrity: sha512-JLEdkoM+v+sHAmCW7AJ10jHsLBSZWmSOV8xDVAgI2uI5r/VvUd+ZIheupy9YNLSD3mOXN4j+FR8Q3ZS38AwtOg==} + '@tanstack/react-start-client@1.166.14': + resolution: {integrity: sha512-sCnvtT9O7Jn4ne3ZJZnRFaz/Qr9mn0zHRCjl0RrSYb6Ntd93FPHWan1mbKbgtTOfq3ITUVIE81MjKwDuO7IDhQ==} engines: {node: '>=22.12.0'} peerDependencies: react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - '@tanstack/react-start-server@1.166.13': - resolution: {integrity: sha512-a8JIJo3ltcrWmSmeM0XG3lsz2qhUGeVosVjprm2sElr60Cz9XwtzNLG2iofeu1/GcHCeMnI3HcTledWNV7vmSg==} + '@tanstack/react-start-server@1.166.14': + resolution: {integrity: sha512-Xoq75bQSVVo2wBvORmyyLSFsTFGvUkBoBWQF78wvH3ZTu6rjlGX26huzFgoM584GtxircFXcj+RJFbo+0fy4+g==} engines: {node: '>=22.12.0'} peerDependencies: react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - '@tanstack/react-start@1.166.16': - resolution: {integrity: sha512-WHISr2Y/lr8L8VF8l8VsnpRutd1kElsU1z/58SxbMwIosu9X05KrrDFI+hqONyIygUfVakpYW084U0hw5eapTQ==} + '@tanstack/react-start@1.166.17': + resolution: {integrity: sha512-hsbFpVDznGYsPLY/DJLtukhZt8UqrZpOEGi0fv8gC+UNZx/bHp+mJYGkYKUhDFgqIJcpv0Nvbxi/wbBhoz0ePg==} engines: {node: '>=22.12.0'} hasBin: true peerDependencies: react: '>=18.0.0 || >=19.0.0' react-dom: '>=18.0.0 || >=19.0.0' - vite: ^8.0.0 + vite: '>=7.0.0' '@tanstack/react-store@0.9.1': resolution: {integrity: sha512-YzJLnRvy5lIEFTLWBAZmcOjK3+2AepnBv/sr6NZmiqJvq7zTQggyK99Gw8fqYdMdHPQWXjz0epFKJXC+9V2xDA==} @@ -3410,23 +3408,23 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@tanstack/router-core@1.167.4': - resolution: {integrity: sha512-Gk5V9Zr5JFJ4SbLyCheQLJ3MnXddccENPA+DJRz+9g3QxtN8DJB8w8KCUCgDeYlWp4LvmO4nX3fy3tupqVP2Pw==} + '@tanstack/router-core@1.167.5': + resolution: {integrity: sha512-8fRgJ0zNJf77R4grCaJQ5Imatjyc4YT5v8rlsPkYYYeUlcFNLbuFRhLlAMdND9gRUMznpnbRDXngpTPgx2K7HQ==} engines: {node: '>=20.19'} hasBin: true - '@tanstack/router-generator@1.166.12': - resolution: {integrity: sha512-2HdxSTbCkbU9JeYogKVigIlXoLtIJE1x5rbEov+ZLTPjGCO9kicNQuljqg9Js+u2/ahtWewNrE5u1QCAyxmpIg==} + '@tanstack/router-generator@1.166.13': + resolution: {integrity: sha512-ALxSs6OzimiSgpOuIm+AXmc7eUx/oGPwSPpdQbpZ/kX7WHRh6qM7lv8DAN0K3jWcBpzF8eeOIdryWryX8gH+Yg==} engines: {node: '>=20.19'} - '@tanstack/router-plugin@1.166.13': - resolution: {integrity: sha512-xG3ND3AlMe6DN9PihJAYUbQJevqJvVdzN1QpZbfU1/jkHurL97ynP2yXfmMTh8Qgi1K+SWRko4bi7iZlYP9SUw==} + '@tanstack/router-plugin@1.166.14': + resolution: {integrity: sha512-hypyj0qlsAbJf60/glmVYqSVwnRB4hKRrMCUsSXjrPdO2g6gs3z6xHmcWsHQ831C4G9+bSFEK9Uy5EjO3A4THQ==} engines: {node: '>=20.19'} hasBin: true peerDependencies: '@rsbuild/core': '>=1.0.2' - '@tanstack/react-router': ^1.167.4 - vite: ^8.0.0 + '@tanstack/react-router': ^1.167.5 + vite: '>=5.0.0 || >=6.0.0 || >=7.0.0' vite-plugin-solid: ^2.11.10 webpack: '>=5.92.0' peerDependenciesMeta: @@ -3452,8 +3450,8 @@ packages: resolution: {integrity: sha512-nRcYw+w2OEgK6VfjirYvGyPLOK+tZQz1jkYcmH5AjMamQ9PycnlxZF2aEZtPpNoUsaceX2bHptn6Ub5hGXqNvw==} engines: {node: '>=20.19'} - '@tanstack/start-client-core@1.166.12': - resolution: {integrity: sha512-E09jnzmoU3yKzrhCvnGqhDzzPDThmiK9wLXS37UhZdmttd0LRc+BeavwWoWNrtTEykIoc3W3l4rA/izRXXDFKw==} + '@tanstack/start-client-core@1.166.13': + resolution: {integrity: sha512-fbuh5Snoj3/XvfAI+RL1MsqloJYepg244Kp2wYLerUOtT/jqKRQ+mTBkGMURo+cAPkyHC1nDB1dhFcKqfnJMEA==} engines: {node: '>=22.12.0'} hasBin: true @@ -3461,19 +3459,19 @@ packages: resolution: {integrity: sha512-Y6QSlGiLga8cHfvxGGaonXIlt2bIUTVdH6AMjmpMp7+ANNCp+N96GQbjjhLye3JkaxDfP68x5iZA8NK4imgRig==} engines: {node: '>=22.12.0'} - '@tanstack/start-plugin-core@1.167.0': - resolution: {integrity: sha512-6kJmojXjGE7rIhEnnVmk/uPAHHY469OWMRZeHm9bDk9oxm6toIoT0a7M/rqeqC2elg+J20b2rWSVERd63M1hqA==} + '@tanstack/start-plugin-core@1.167.1': + resolution: {integrity: sha512-iZM0MEfHhKNEtt65S4PVPEv0VV7WPE9oo/nMg/yiqTsGSmBFhKNljvyH22Sw/D5/dVgQfeRw4s9Sl2kj9TgJWw==} engines: {node: '>=22.12.0'} peerDependencies: - vite: ^8.0.0 + vite: '>=7.0.0' - '@tanstack/start-server-core@1.166.12': - resolution: {integrity: sha512-DVONJRVegeO1FMQYWRWlIc17zq3nbjixBq/uTv01K+iy3CnV9rjvqF5Df2ifvuHB6sNLFNhX7Mic7CFGfQLktQ==} + '@tanstack/start-server-core@1.166.13': + resolution: {integrity: sha512-Cw5H08MRN5Gt0xg1C9Km6bOuPkU9A1ZlWeg31OmdWocaLaT60R+T0lqNtFqp9TT8r68tBx+zHxOrvfe1CubKVA==} engines: {node: '>=22.12.0'} hasBin: true - '@tanstack/start-storage-context@1.166.12': - resolution: {integrity: sha512-5MJfWpGoCbblYiOEA+5WS84cDYMPUGp+q29g5LttHsNJ5PsjiO7Lyw/zwp3rJJiMUI1QM2KCQ1d03XrSUmla3Q==} + '@tanstack/start-storage-context@1.166.13': + resolution: {integrity: sha512-9y5BEMA2FluQETc82OOLK/+YirpQ42lempUO3K/rZ//CRfCP3ZHX3GGG8yY/F+3sU7Gh7OsxBNXcqE6ktRBbzw==} engines: {node: '>=22.12.0'} '@tanstack/store@0.9.1': @@ -3683,6 +3681,12 @@ packages: '@types/bcrypt@6.0.0': resolution: {integrity: sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/estree@0.0.39': resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} @@ -3866,6 +3870,129 @@ packages: babel-plugin-react-compiler: optional: true + '@voidzero-dev/vite-plus-core@0.1.12': + resolution: {integrity: sha512-j8YNe7A+8JcSoddztf5whvom/yJ7OKUO3Y5a3UoLIUmOL8YEKVv5nPANrxJ7eaFfHJoMnBEwzBpq1YVZ+H3uPA==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@arethetypeswrong/core': ^0.18.1 + '@tsdown/css': 0.21.3 + '@tsdown/exe': 0.21.3 + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.0.0-alpha.31 + esbuild: ^0.27.0 + jiti: '>=1.21.0' + less: ^4.0.0 + publint: ^0.3.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + typescript: ^5.0.0 + unplugin-unused: ^0.5.0 + yaml: ^2.4.2 + peerDependenciesMeta: + '@arethetypeswrong/core': + optional: true + '@tsdown/css': + optional: true + '@tsdown/exe': + optional: true + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + publint: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + typescript: + optional: true + unplugin-unused: + optional: true + yaml: + optional: true + + '@voidzero-dev/vite-plus-darwin-arm64@0.1.12': + resolution: {integrity: sha512-tYQrfmcLxIqqr/de00oN7ayu+rYobEOjyR9AxoeJoNUqRyNQCdT0A5vg78kJNPaQCyL6ctgRRvpEKr0WHVmduQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@voidzero-dev/vite-plus-darwin-x64@0.1.12': + resolution: {integrity: sha512-852hO/Onx9Z5u0tOYOVEUVzYJUmWdlHeqYnNT6pj0IClgVp0+KSabxr7A2paTWEFWp6XbKWvqw5Y5cVwUV3A6Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@voidzero-dev/vite-plus-linux-arm64-gnu@0.1.12': + resolution: {integrity: sha512-/gTh4tGyJKCNBn9SZUs3sq9QVRUmyuyseZefBgS223QRxdwFaxc7tIKaw91X59WXXYOzUYZOD5zsTcaIF4hc9A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@voidzero-dev/vite-plus-linux-x64-gnu@0.1.12': + resolution: {integrity: sha512-9oN9ITjK/Xq9Werx+6G6jnI3+F1S3g9lB36J1VAHyRlAEtuiCDV0E3YMoW2O7KzM/PlodZIZ8LStVkH7aA5ZCw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@voidzero-dev/vite-plus-test@0.1.12': + resolution: {integrity: sha512-EE8Y2vQvqS4c/1qSa7qlhUY9koAG6wYev0NFAtDZsijQCHUqE7nYXGJYnyUInAE6GX4zlQDGg7tf2DAl+CISYw==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/ui': 4.1.0 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + '@voidzero-dev/vite-plus-win32-arm64-msvc@0.1.12': + resolution: {integrity: sha512-JanAb6Y+6BmPhKNLvpZB/syeyY99bt7EPJCaLlbaCt3V0Y2Iw7c7dWBM4Sg4GZ7szGYdGw385fRz0n2M32f1rg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@voidzero-dev/vite-plus-win32-x64-msvc@0.1.12': + resolution: {integrity: sha512-Ei/UtTTp7UgeEGyV83jhDpSMXhwaZZzfS7Xiaj+zj80GGOwsBre0i+oHGZ7+TuVsZ7Im0sD8IZ9enCpKpV//AQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@xmldom/is-dom-node@1.0.1': resolution: {integrity: sha512-CJDxIgE5I0FH+ttq/Fxy6nRpxP70+e2O048EPe85J2use3XKdatVM7dDVvFNjQudd9B49NPoZ+8PG49zj4Er8Q==} engines: {node: '>= 16'} @@ -3960,6 +4087,10 @@ packages: asn1@0.2.6: resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + ast-types@0.13.4: resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} engines: {node: '>=4'} @@ -4221,6 +4352,10 @@ packages: magicast: optional: true + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + cacache@20.0.3: resolution: {integrity: sha512-3pUp4e8hv07k1QlijZu6Kn7c9+ZpWWk4j3F8N3xPuCExULobqJydKYOTj1FTq58srkJsXvO7LbGAH4C0ZU3WGw==} engines: {node: ^20.17.0 || >=22.9.0} @@ -4838,6 +4973,9 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -5663,73 +5801,36 @@ packages: libphonenumber-js@1.12.40: resolution: {integrity: sha512-HKGs7GowShNls3Zh+7DTr6wYpPk5jC78l508yQQY3e8ZgJChM3A9JZghmMJZuK+5bogSfuTafpjksGSR3aMIEg==} - lightningcss-android-arm64@1.31.1: - resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [android] - lightningcss-android-arm64@1.32.0: resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] - lightningcss-darwin-arm64@1.31.1: - resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [darwin] - lightningcss-darwin-arm64@1.32.0: resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] - lightningcss-darwin-x64@1.31.1: - resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [darwin] - lightningcss-darwin-x64@1.32.0: resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] - lightningcss-freebsd-x64@1.31.1: - resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [freebsd] - lightningcss-freebsd-x64@1.32.0: resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] - lightningcss-linux-arm-gnueabihf@1.31.1: - resolution: {integrity: sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==} - engines: {node: '>= 12.0.0'} - cpu: [arm] - os: [linux] - lightningcss-linux-arm-gnueabihf@1.32.0: resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] - lightningcss-linux-arm64-gnu@1.31.1: - resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - libc: [glibc] - lightningcss-linux-arm64-gnu@1.32.0: resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} engines: {node: '>= 12.0.0'} @@ -5737,13 +5838,6 @@ packages: os: [linux] libc: [glibc] - lightningcss-linux-arm64-musl@1.31.1: - resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - libc: [musl] - lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} @@ -5751,13 +5845,6 @@ packages: os: [linux] libc: [musl] - lightningcss-linux-x64-gnu@1.31.1: - resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - libc: [glibc] - lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} @@ -5765,13 +5852,6 @@ packages: os: [linux] libc: [glibc] - lightningcss-linux-x64-musl@1.31.1: - resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - libc: [musl] - lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} @@ -5779,34 +5859,18 @@ packages: os: [linux] libc: [musl] - lightningcss-win32-arm64-msvc@1.31.1: - resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [win32] - lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] - lightningcss-win32-x64-msvc@1.31.1: - resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [win32] - lightningcss-win32-x64-msvc@1.32.0: resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] - lightningcss@1.31.1: - resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==} - engines: {node: '>= 12.0.0'} - lightningcss@1.32.0: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} @@ -6043,6 +6107,10 @@ packages: react-dom: optional: true + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -6105,7 +6173,7 @@ packages: giget: '*' jiti: ^2.6.1 rollup: ^4.59.0 - vite: ^8.0.0 + vite: ^7 || ^8 || >=8.0.0-0 xml2js: ^0.6.2 zephyr-agent: ^0.1.15 peerDependenciesMeta: @@ -6212,6 +6280,9 @@ packages: observable-fns@0.6.1: resolution: {integrity: sha512-9gRK4+sRWzeN6AOewNBTLXir7Zl/i3GB6Yl26gK4flxz8BXVpD3kt8amREmWNb0mxYOGDotvE5a4N+PtGGKdkg==} + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + ocache@0.1.2: resolution: {integrity: sha512-lI34wjM7cahEdrq2I5obbF7MEdE97vULf6vNj6ZCzwEadzyXO1w7QOl2qzzG4IL8cyO7wDtXPj9CqW/aG3mn7g==} @@ -6271,8 +6342,8 @@ packages: oxc-resolver@11.19.1: resolution: {integrity: sha512-qE/CIg/spwrTBFt5aKmwe3ifeDdLfA2NESN30E42X/lII5ClF8V7Wt6WIJhcGZjp0/Q+nQ+9vgxGk//xZNX2hg==} - oxfmt@0.41.0: - resolution: {integrity: sha512-sKLdJZdQ3bw6x9qKiT7+eID4MNEXlDHf5ZacfIircrq6Qwjk0L6t2/JQlZZrVHTXJawK3KaMuBoJnEJPcqCEdg==} + oxfmt@0.40.0: + resolution: {integrity: sha512-g0C3I7xUj4b4DcagevM9kgH6+pUHytikxUcn3/VUkvzTNaaXBeyZqb7IBsHwojeXm4mTBEC/aBjBTMVUkZwWUQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -6280,8 +6351,8 @@ packages: resolution: {integrity: sha512-TdrKhDZCgEYqONFo/j+KvGan7/k3tP5Ouz88wCqpOvJtI2QmcLfGsm1fcMvDnTik48Jj6z83IJBqlkmK9DnY1A==} hasBin: true - oxlint@1.56.0: - resolution: {integrity: sha512-Q+5Mj5PVaH/R6/fhMMFzw4dT+KPB+kQW4kaL8FOIq7tfhlnEVp6+3lcWqFruuTNlUo9srZUW3qH7Id4pskeR6g==} + oxlint@1.55.0: + resolution: {integrity: sha512-T+FjepiyWpaZMhekqRpH8Z3I4vNM610p6w+Vjfqgj5TZUxHXl7N8N5IPvmOU8U4XdTRxqtNNTh9Y4hLtr7yvFg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -6415,6 +6486,10 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + pixelmatch@7.1.0: + resolution: {integrity: sha512-1wrVzJ2STrpmONHKBy228LM1b84msXDUoAzVEl0R8Mz4Ce6EPr+IVtxm8+yvrqLYMHswREkjYFaMxnyGnaY3Ng==} + hasBin: true + pkce-challenge@5.0.1: resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} engines: {node: '>=16.20.0'} @@ -6422,6 +6497,10 @@ packages: pkg-types@2.3.0: resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} + pngjs@7.0.0: + resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} + engines: {node: '>=14.19.0'} + pofile@1.1.4: resolution: {integrity: sha512-r6Q21sKsY1AjTVVjOuU02VYKVNQGJNQHjTIvs4dEbeuuYfxgYk/DGD2mqqq4RDaVkwdSq0VEtmQUOPe/wH8X3g==} @@ -6956,6 +7035,10 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -7042,6 +7125,9 @@ packages: std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + std-env@4.0.0: + resolution: {integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==} + stdin-discarder@0.2.2: resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} engines: {node: '>=18'} @@ -7145,8 +7231,8 @@ packages: tailwind-merge@3.5.0: resolution: {integrity: sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==} - tailwindcss@4.2.1: - resolution: {integrity: sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==} + tailwindcss@4.2.2: + resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==} tapable@2.3.0: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} @@ -7209,6 +7295,9 @@ packages: tiny-worker@2.3.0: resolution: {integrity: sha512-pJ70wq5EAqTAEl9IkGzA+fN0836rycEuz2Cn6yeZ6FRzlVS5IDOkFHpIoEsksPRQV34GDqXm65+OlnZqUSyK2g==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinyexec@1.0.2: resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} engines: {node: '>=18'} @@ -7243,6 +7332,10 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + tough-cookie@6.0.0: resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} engines: {node: '>=16'} @@ -7533,60 +7626,22 @@ packages: engines: {node: '>=16.0.0'} peerDependencies: '@vite-pwa/assets-generator': ^1.0.0 - vite: ^8.0.0 + vite: ^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 workbox-build: ^7.4.0 workbox-window: ^7.4.0 peerDependenciesMeta: '@vite-pwa/assets-generator': optional: true - vite@8.0.0: - resolution: {integrity: sha512-fPGaRNj9Zytaf8LEiBhY7Z6ijnFKdzU/+mL8EFBaKr7Vw1/FWcTBAMW0wLPJAGMPX38ZPVCVgLceWiEqeoqL2Q==} + vite-plus@0.1.12: + resolution: {integrity: sha512-8s1RzomZkgrJRiwiYWGq3R0txFPYfBBJGp73XNHQnme0KTTVH5dNm/E2GNyBSMFJbeeF7eh1OSgqWVc2FpR6eA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - '@vitejs/devtools': ^0.0.0-alpha.31 - esbuild: ^0.27.0 - jiti: '>=1.21.0' - less: ^4.0.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - '@vitejs/devtools': - optional: true - esbuild: - optional: true - jiti: - optional: true - less: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true vitefu@1.1.2: resolution: {integrity: sha512-zpKATdUbzbsycPFBN71nS2uzBUQiVnFoOrr2rvqv34S1lcAgMKKkjWleLGeiJlZ8lwCXvtWaRn7R3ZC16SYRuw==} peerDependencies: - vite: ^8.0.0 + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-beta.0 peerDependenciesMeta: vite: optional: true @@ -7968,29 +8023,29 @@ snapshots: '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-s3@3.1011.0': + '@aws-sdk/client-s3@3.1012.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.973.20 - '@aws-sdk/credential-provider-node': 3.972.21 + '@aws-sdk/core': 3.973.21 + '@aws-sdk/credential-provider-node': 3.972.22 '@aws-sdk/middleware-bucket-endpoint': 3.972.8 '@aws-sdk/middleware-expect-continue': 3.972.8 - '@aws-sdk/middleware-flexible-checksums': 3.974.0 + '@aws-sdk/middleware-flexible-checksums': 3.974.1 '@aws-sdk/middleware-host-header': 3.972.8 '@aws-sdk/middleware-location-constraint': 3.972.8 '@aws-sdk/middleware-logger': 3.972.8 '@aws-sdk/middleware-recursion-detection': 3.972.8 - '@aws-sdk/middleware-sdk-s3': 3.972.20 + '@aws-sdk/middleware-sdk-s3': 3.972.21 '@aws-sdk/middleware-ssec': 3.972.8 - '@aws-sdk/middleware-user-agent': 3.972.21 + '@aws-sdk/middleware-user-agent': 3.972.22 '@aws-sdk/region-config-resolver': 3.972.8 - '@aws-sdk/signature-v4-multi-region': 3.996.8 + '@aws-sdk/signature-v4-multi-region': 3.996.9 '@aws-sdk/types': 3.973.6 '@aws-sdk/util-endpoints': 3.996.5 '@aws-sdk/util-user-agent-browser': 3.972.8 - '@aws-sdk/util-user-agent-node': 3.973.7 + '@aws-sdk/util-user-agent-node': 3.973.8 '@smithy/config-resolver': 4.4.11 '@smithy/core': 3.23.12 '@smithy/eventstream-serde-browser': 4.2.12 @@ -8028,10 +8083,10 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/core@3.973.20': + '@aws-sdk/core@3.973.21': dependencies: '@aws-sdk/types': 3.973.6 - '@aws-sdk/xml-builder': 3.972.11 + '@aws-sdk/xml-builder': 3.972.12 '@smithy/core': 3.23.12 '@smithy/node-config-provider': 4.3.12 '@smithy/property-provider': 4.2.12 @@ -8049,17 +8104,17 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.972.18': + '@aws-sdk/credential-provider-env@3.972.19': dependencies: - '@aws-sdk/core': 3.973.20 + '@aws-sdk/core': 3.973.21 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.972.20': + '@aws-sdk/credential-provider-http@3.972.21': dependencies: - '@aws-sdk/core': 3.973.20 + '@aws-sdk/core': 3.973.21 '@aws-sdk/types': 3.973.6 '@smithy/fetch-http-handler': 5.3.15 '@smithy/node-http-handler': 4.5.0 @@ -8070,16 +8125,16 @@ snapshots: '@smithy/util-stream': 4.5.20 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.972.20': + '@aws-sdk/credential-provider-ini@3.972.21': dependencies: - '@aws-sdk/core': 3.973.20 - '@aws-sdk/credential-provider-env': 3.972.18 - '@aws-sdk/credential-provider-http': 3.972.20 - '@aws-sdk/credential-provider-login': 3.972.20 - '@aws-sdk/credential-provider-process': 3.972.18 - '@aws-sdk/credential-provider-sso': 3.972.20 - '@aws-sdk/credential-provider-web-identity': 3.972.20 - '@aws-sdk/nested-clients': 3.996.10 + '@aws-sdk/core': 3.973.21 + '@aws-sdk/credential-provider-env': 3.972.19 + '@aws-sdk/credential-provider-http': 3.972.21 + '@aws-sdk/credential-provider-login': 3.972.21 + '@aws-sdk/credential-provider-process': 3.972.19 + '@aws-sdk/credential-provider-sso': 3.972.21 + '@aws-sdk/credential-provider-web-identity': 3.972.21 + '@aws-sdk/nested-clients': 3.996.11 '@aws-sdk/types': 3.973.6 '@smithy/credential-provider-imds': 4.2.12 '@smithy/property-provider': 4.2.12 @@ -8089,10 +8144,10 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-login@3.972.20': + '@aws-sdk/credential-provider-login@3.972.21': dependencies: - '@aws-sdk/core': 3.973.20 - '@aws-sdk/nested-clients': 3.996.10 + '@aws-sdk/core': 3.973.21 + '@aws-sdk/nested-clients': 3.996.11 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/protocol-http': 5.3.12 @@ -8102,14 +8157,14 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-node@3.972.21': + '@aws-sdk/credential-provider-node@3.972.22': dependencies: - '@aws-sdk/credential-provider-env': 3.972.18 - '@aws-sdk/credential-provider-http': 3.972.20 - '@aws-sdk/credential-provider-ini': 3.972.20 - '@aws-sdk/credential-provider-process': 3.972.18 - '@aws-sdk/credential-provider-sso': 3.972.20 - '@aws-sdk/credential-provider-web-identity': 3.972.20 + '@aws-sdk/credential-provider-env': 3.972.19 + '@aws-sdk/credential-provider-http': 3.972.21 + '@aws-sdk/credential-provider-ini': 3.972.21 + '@aws-sdk/credential-provider-process': 3.972.19 + '@aws-sdk/credential-provider-sso': 3.972.21 + '@aws-sdk/credential-provider-web-identity': 3.972.21 '@aws-sdk/types': 3.973.6 '@smithy/credential-provider-imds': 4.2.12 '@smithy/property-provider': 4.2.12 @@ -8119,20 +8174,20 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-process@3.972.18': + '@aws-sdk/credential-provider-process@3.972.19': dependencies: - '@aws-sdk/core': 3.973.20 + '@aws-sdk/core': 3.973.21 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/shared-ini-file-loader': 4.4.7 '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.972.20': + '@aws-sdk/credential-provider-sso@3.972.21': dependencies: - '@aws-sdk/core': 3.973.20 - '@aws-sdk/nested-clients': 3.996.10 - '@aws-sdk/token-providers': 3.1009.0 + '@aws-sdk/core': 3.973.21 + '@aws-sdk/nested-clients': 3.996.11 + '@aws-sdk/token-providers': 3.1012.0 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/shared-ini-file-loader': 4.4.7 @@ -8141,10 +8196,10 @@ snapshots: transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-web-identity@3.972.20': + '@aws-sdk/credential-provider-web-identity@3.972.21': dependencies: - '@aws-sdk/core': 3.973.20 - '@aws-sdk/nested-clients': 3.996.10 + '@aws-sdk/core': 3.973.21 + '@aws-sdk/nested-clients': 3.996.11 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/shared-ini-file-loader': 4.4.7 @@ -8170,12 +8225,12 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-flexible-checksums@3.974.0': + '@aws-sdk/middleware-flexible-checksums@3.974.1': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.973.20 + '@aws-sdk/core': 3.973.21 '@aws-sdk/crc64-nvme': 3.972.5 '@aws-sdk/types': 3.973.6 '@smithy/is-array-buffer': 4.2.2 @@ -8214,9 +8269,9 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-sdk-s3@3.972.20': + '@aws-sdk/middleware-sdk-s3@3.972.21': dependencies: - '@aws-sdk/core': 3.973.20 + '@aws-sdk/core': 3.973.21 '@aws-sdk/types': 3.973.6 '@aws-sdk/util-arn-parser': 3.972.3 '@smithy/core': 3.23.12 @@ -8237,9 +8292,9 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.972.21': + '@aws-sdk/middleware-user-agent@3.972.22': dependencies: - '@aws-sdk/core': 3.973.20 + '@aws-sdk/core': 3.973.21 '@aws-sdk/types': 3.973.6 '@aws-sdk/util-endpoints': 3.996.5 '@smithy/core': 3.23.12 @@ -8248,20 +8303,20 @@ snapshots: '@smithy/util-retry': 4.2.12 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.996.10': + '@aws-sdk/nested-clients@3.996.11': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.973.20 + '@aws-sdk/core': 3.973.21 '@aws-sdk/middleware-host-header': 3.972.8 '@aws-sdk/middleware-logger': 3.972.8 '@aws-sdk/middleware-recursion-detection': 3.972.8 - '@aws-sdk/middleware-user-agent': 3.972.21 + '@aws-sdk/middleware-user-agent': 3.972.22 '@aws-sdk/region-config-resolver': 3.972.8 '@aws-sdk/types': 3.973.6 '@aws-sdk/util-endpoints': 3.996.5 '@aws-sdk/util-user-agent-browser': 3.972.8 - '@aws-sdk/util-user-agent-node': 3.973.7 + '@aws-sdk/util-user-agent-node': 3.973.8 '@smithy/config-resolver': 4.4.11 '@smithy/core': 3.23.12 '@smithy/fetch-http-handler': 5.3.15 @@ -8299,19 +8354,19 @@ snapshots: '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/signature-v4-multi-region@3.996.8': + '@aws-sdk/signature-v4-multi-region@3.996.9': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.972.20 + '@aws-sdk/middleware-sdk-s3': 3.972.21 '@aws-sdk/types': 3.973.6 '@smithy/protocol-http': 5.3.12 '@smithy/signature-v4': 5.3.12 '@smithy/types': 4.13.1 tslib: 2.8.1 - '@aws-sdk/token-providers@3.1009.0': + '@aws-sdk/token-providers@3.1012.0': dependencies: - '@aws-sdk/core': 3.973.20 - '@aws-sdk/nested-clients': 3.996.10 + '@aws-sdk/core': 3.973.21 + '@aws-sdk/nested-clients': 3.996.11 '@aws-sdk/types': 3.973.6 '@smithy/property-provider': 4.2.12 '@smithy/shared-ini-file-loader': 4.4.7 @@ -8348,16 +8403,16 @@ snapshots: bowser: 2.14.1 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.973.7': + '@aws-sdk/util-user-agent-node@3.973.8': dependencies: - '@aws-sdk/middleware-user-agent': 3.972.21 + '@aws-sdk/middleware-user-agent': 3.972.22 '@aws-sdk/types': 3.973.6 '@smithy/node-config-provider': 4.3.12 '@smithy/types': 4.13.1 '@smithy/util-config-provider': 4.2.2 tslib: 2.8.1 - '@aws-sdk/xml-builder@3.972.11': + '@aws-sdk/xml-builder@3.972.12': dependencies: '@smithy/types': 4.13.1 fast-xml-parser: 5.4.2 @@ -9225,11 +9280,11 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@better-auth/api-key@1.5.5(0deaaa17e1a55d22e058ad78990ab47b)': + '@better-auth/api-key@1.5.5(0fd0105ce52682e995ba26b97ede8d85)': dependencies: '@better-auth/core': 1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/utils': 0.3.1 - better-auth: 1.5.5(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.166.16(crossws@0.4.4(srvx@0.11.12))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)))(drizzle-kit@1.0.0-beta.18-7eb39f0)(drizzle-orm@1.0.0-beta.18-7eb39f0(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(@azure/core-client@1.10.1))(@types/pg@8.18.0)(mssql@11.0.1(@azure/core-client@1.10.1))(mysql2@3.15.3)(pg@8.20.0)(postgres@3.4.7)(valibot@1.2.0(typescript@5.9.3))(zod@4.3.6))(mongodb@7.1.0(socks@2.8.7))(mysql2@3.15.3)(pg@8.20.0)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + better-auth: 1.5.5(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.166.17(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(crossws@0.4.4(srvx@0.11.12))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(drizzle-kit@1.0.0-beta.18-7eb39f0)(drizzle-orm@1.0.0-beta.18-7eb39f0(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(@azure/core-client@1.10.1))(@types/pg@8.18.0)(mssql@11.0.1(@azure/core-client@1.10.1))(mysql2@3.15.3)(pg@8.20.0)(postgres@3.4.7)(valibot@1.2.0(typescript@5.9.3))(zod@4.3.6))(mongodb@7.1.0(socks@2.8.7))(mysql2@3.15.3)(pg@8.20.0)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) zod: 4.3.6 '@better-auth/core@1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1)': @@ -9250,12 +9305,12 @@ snapshots: optionalDependencies: drizzle-orm: 1.0.0-beta.18-7eb39f0(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(@azure/core-client@1.10.1))(@types/pg@8.18.0)(mssql@11.0.1(@azure/core-client@1.10.1))(mysql2@3.15.3)(pg@8.20.0)(postgres@3.4.7)(valibot@1.2.0(typescript@5.9.3))(zod@4.3.6) - '@better-auth/infra@0.1.12(81efdf05db11483743fdd6d8339b7ec2)': + '@better-auth/infra@0.1.12(655c67ab3f6329590b1caf6edd8bb58e)': dependencies: '@better-auth/core': 1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) - '@better-auth/sso': 1.5.5(75622cc3afe235c4546eb52a0908fe5d) + '@better-auth/sso': 1.5.5(150088a4c353f15c147b355651f19fda) '@better-fetch/fetch': 1.1.21 - better-auth: 1.5.5(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.166.16(crossws@0.4.4(srvx@0.11.12))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)))(drizzle-kit@1.0.0-beta.18-7eb39f0)(drizzle-orm@1.0.0-beta.18-7eb39f0(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(@azure/core-client@1.10.1))(@types/pg@8.18.0)(mssql@11.0.1(@azure/core-client@1.10.1))(mysql2@3.15.3)(pg@8.20.0)(postgres@3.4.7)(valibot@1.2.0(typescript@5.9.3))(zod@4.3.6))(mongodb@7.1.0(socks@2.8.7))(mysql2@3.15.3)(pg@8.20.0)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + better-auth: 1.5.5(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.166.17(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(crossws@0.4.4(srvx@0.11.12))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(drizzle-kit@1.0.0-beta.18-7eb39f0)(drizzle-orm@1.0.0-beta.18-7eb39f0(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(@azure/core-client@1.10.1))(@types/pg@8.18.0)(mssql@11.0.1(@azure/core-client@1.10.1))(mysql2@3.15.3)(pg@8.20.0)(postgres@3.4.7)(valibot@1.2.0(typescript@5.9.3))(zod@4.3.6))(mongodb@7.1.0(socks@2.8.7))(mysql2@3.15.3)(pg@8.20.0)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) better-call: 1.3.4(zod@4.3.6) jose: 6.1.3 libphonenumber-js: 1.12.40 @@ -9286,12 +9341,12 @@ snapshots: '@prisma/client': 7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3) prisma: 7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) - '@better-auth/sso@1.5.5(75622cc3afe235c4546eb52a0908fe5d)': + '@better-auth/sso@1.5.5(150088a4c353f15c147b355651f19fda)': dependencies: '@better-auth/core': 1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/utils': 0.3.1 '@better-fetch/fetch': 1.1.21 - better-auth: 1.5.5(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.166.16(crossws@0.4.4(srvx@0.11.12))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)))(drizzle-kit@1.0.0-beta.18-7eb39f0)(drizzle-orm@1.0.0-beta.18-7eb39f0(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(@azure/core-client@1.10.1))(@types/pg@8.18.0)(mssql@11.0.1(@azure/core-client@1.10.1))(mysql2@3.15.3)(pg@8.20.0)(postgres@3.4.7)(valibot@1.2.0(typescript@5.9.3))(zod@4.3.6))(mongodb@7.1.0(socks@2.8.7))(mysql2@3.15.3)(pg@8.20.0)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + better-auth: 1.5.5(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.166.17(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(crossws@0.4.4(srvx@0.11.12))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(drizzle-kit@1.0.0-beta.18-7eb39f0)(drizzle-orm@1.0.0-beta.18-7eb39f0(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(@azure/core-client@1.10.1))(@types/pg@8.18.0)(mssql@11.0.1(@azure/core-client@1.10.1))(mysql2@3.15.3)(pg@8.20.0)(postgres@3.4.7)(valibot@1.2.0(typescript@5.9.3))(zod@4.3.6))(mongodb@7.1.0(socks@2.8.7))(mysql2@3.15.3)(pg@8.20.0)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) better-call: 1.3.2(zod@4.3.6) fast-xml-parser: 5.4.2 jose: 6.1.3 @@ -9857,11 +9912,11 @@ snapshots: '@lingui/babel-plugin-lingui-macro': 5.9.3(babel-plugin-macros@3.1.0)(typescript@5.9.3) babel-plugin-macros: 3.1.0 - '@lingui/vite-plugin@5.9.3(babel-plugin-macros@3.1.0)(typescript@5.9.3)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': + '@lingui/vite-plugin@5.9.3(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(babel-plugin-macros@3.1.0)(typescript@5.9.3)': dependencies: '@lingui/cli': 5.9.3(babel-plugin-macros@3.1.0)(typescript@5.9.3) '@lingui/conf': 5.9.3(typescript@5.9.3) - vite: 8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: '@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)' transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -10150,11 +10205,11 @@ snapshots: transitivePeerDependencies: - '@opentelemetry/api' - '@orpc/tanstack-query@1.13.8(@opentelemetry/api@1.9.0)(@orpc/client@1.13.8(@opentelemetry/api@1.9.0))(@tanstack/query-core@5.90.20)': + '@orpc/tanstack-query@1.13.8(@opentelemetry/api@1.9.0)(@orpc/client@1.13.8(@opentelemetry/api@1.9.0))(@tanstack/query-core@5.91.0)': dependencies: '@orpc/client': 1.13.8(@opentelemetry/api@1.9.0) '@orpc/shared': 1.13.8(@opentelemetry/api@1.9.0) - '@tanstack/query-core': 5.90.20 + '@tanstack/query-core': 5.91.0 transitivePeerDependencies: - '@opentelemetry/api' @@ -10240,61 +10295,61 @@ snapshots: '@oxc-resolver/binding-win32-x64-msvc@11.19.1': optional: true - '@oxfmt/binding-android-arm-eabi@0.41.0': + '@oxfmt/binding-android-arm-eabi@0.40.0': optional: true - '@oxfmt/binding-android-arm64@0.41.0': + '@oxfmt/binding-android-arm64@0.40.0': optional: true - '@oxfmt/binding-darwin-arm64@0.41.0': + '@oxfmt/binding-darwin-arm64@0.40.0': optional: true - '@oxfmt/binding-darwin-x64@0.41.0': + '@oxfmt/binding-darwin-x64@0.40.0': optional: true - '@oxfmt/binding-freebsd-x64@0.41.0': + '@oxfmt/binding-freebsd-x64@0.40.0': optional: true - '@oxfmt/binding-linux-arm-gnueabihf@0.41.0': + '@oxfmt/binding-linux-arm-gnueabihf@0.40.0': optional: true - '@oxfmt/binding-linux-arm-musleabihf@0.41.0': + '@oxfmt/binding-linux-arm-musleabihf@0.40.0': optional: true - '@oxfmt/binding-linux-arm64-gnu@0.41.0': + '@oxfmt/binding-linux-arm64-gnu@0.40.0': optional: true - '@oxfmt/binding-linux-arm64-musl@0.41.0': + '@oxfmt/binding-linux-arm64-musl@0.40.0': optional: true - '@oxfmt/binding-linux-ppc64-gnu@0.41.0': + '@oxfmt/binding-linux-ppc64-gnu@0.40.0': optional: true - '@oxfmt/binding-linux-riscv64-gnu@0.41.0': + '@oxfmt/binding-linux-riscv64-gnu@0.40.0': optional: true - '@oxfmt/binding-linux-riscv64-musl@0.41.0': + '@oxfmt/binding-linux-riscv64-musl@0.40.0': optional: true - '@oxfmt/binding-linux-s390x-gnu@0.41.0': + '@oxfmt/binding-linux-s390x-gnu@0.40.0': optional: true - '@oxfmt/binding-linux-x64-gnu@0.41.0': + '@oxfmt/binding-linux-x64-gnu@0.40.0': optional: true - '@oxfmt/binding-linux-x64-musl@0.41.0': + '@oxfmt/binding-linux-x64-musl@0.40.0': optional: true - '@oxfmt/binding-openharmony-arm64@0.41.0': + '@oxfmt/binding-openharmony-arm64@0.40.0': optional: true - '@oxfmt/binding-win32-arm64-msvc@0.41.0': + '@oxfmt/binding-win32-arm64-msvc@0.40.0': optional: true - '@oxfmt/binding-win32-ia32-msvc@0.41.0': + '@oxfmt/binding-win32-ia32-msvc@0.40.0': optional: true - '@oxfmt/binding-win32-x64-msvc@0.41.0': + '@oxfmt/binding-win32-x64-msvc@0.40.0': optional: true '@oxlint-tsgolint/darwin-arm64@0.17.0': @@ -10315,61 +10370,61 @@ snapshots: '@oxlint-tsgolint/win32-x64@0.17.0': optional: true - '@oxlint/binding-android-arm-eabi@1.56.0': + '@oxlint/binding-android-arm-eabi@1.55.0': optional: true - '@oxlint/binding-android-arm64@1.56.0': + '@oxlint/binding-android-arm64@1.55.0': optional: true - '@oxlint/binding-darwin-arm64@1.56.0': + '@oxlint/binding-darwin-arm64@1.55.0': optional: true - '@oxlint/binding-darwin-x64@1.56.0': + '@oxlint/binding-darwin-x64@1.55.0': optional: true - '@oxlint/binding-freebsd-x64@1.56.0': + '@oxlint/binding-freebsd-x64@1.55.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.56.0': + '@oxlint/binding-linux-arm-gnueabihf@1.55.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.56.0': + '@oxlint/binding-linux-arm-musleabihf@1.55.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.56.0': + '@oxlint/binding-linux-arm64-gnu@1.55.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.56.0': + '@oxlint/binding-linux-arm64-musl@1.55.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.56.0': + '@oxlint/binding-linux-ppc64-gnu@1.55.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.56.0': + '@oxlint/binding-linux-riscv64-gnu@1.55.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.56.0': + '@oxlint/binding-linux-riscv64-musl@1.55.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.56.0': + '@oxlint/binding-linux-s390x-gnu@1.55.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.56.0': + '@oxlint/binding-linux-x64-gnu@1.55.0': optional: true - '@oxlint/binding-linux-x64-musl@1.56.0': + '@oxlint/binding-linux-x64-musl@1.55.0': optional: true - '@oxlint/binding-openharmony-arm64@1.56.0': + '@oxlint/binding-openharmony-arm64@1.55.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.56.0': + '@oxlint/binding-win32-arm64-msvc@1.55.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.56.0': + '@oxlint/binding-win32-ia32-msvc@1.55.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.56.0': + '@oxlint/binding-win32-x64-msvc@1.55.0': optional: true '@phosphor-icons/react@2.1.10(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': @@ -10379,6 +10434,8 @@ snapshots: '@phosphor-icons/web@2.1.2': {} + '@polka/url@1.0.0-next.29': {} + '@prisma/client-runtime-utils@7.4.2': optional: true @@ -10690,14 +10747,14 @@ snapshots: '@rolldown/binding-win32-x64-msvc@1.0.0-rc.9': optional: true - '@rolldown/plugin-babel@0.2.2(@babel/core@7.29.0)(@babel/runtime@7.28.6)(rolldown@1.0.0-rc.9)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': + '@rolldown/plugin-babel@0.2.2(@babel/core@7.29.0)(@babel/runtime@7.28.6)(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(rolldown@1.0.0-rc.9)': dependencies: '@babel/core': 7.29.0 picomatch: 4.0.3 rolldown: 1.0.0-rc.9 optionalDependencies: '@babel/runtime': 7.28.6 - vite: 8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: '@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)' '@rolldown/pluginutils@1.0.0-beta.40': {} @@ -11124,140 +11181,140 @@ snapshots: valibot: 1.2.0(typescript@5.9.3) zod: 4.3.6 - '@tailwindcss/node@4.2.1': + '@tailwindcss/node@4.2.2': dependencies: '@jridgewell/remapping': 2.3.5 enhanced-resolve: 5.20.0 jiti: 2.6.1 - lightningcss: 1.31.1 + lightningcss: 1.32.0 magic-string: 0.30.21 source-map-js: 1.2.1 - tailwindcss: 4.2.1 + tailwindcss: 4.2.2 - '@tailwindcss/oxide-android-arm64@4.2.1': + '@tailwindcss/oxide-android-arm64@4.2.2': optional: true - '@tailwindcss/oxide-darwin-arm64@4.2.1': + '@tailwindcss/oxide-darwin-arm64@4.2.2': optional: true - '@tailwindcss/oxide-darwin-x64@4.2.1': + '@tailwindcss/oxide-darwin-x64@4.2.2': optional: true - '@tailwindcss/oxide-freebsd-x64@4.2.1': + '@tailwindcss/oxide-freebsd-x64@4.2.2': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': + '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.2.1': + '@tailwindcss/oxide-linux-arm64-musl@4.2.2': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.2.1': + '@tailwindcss/oxide-linux-x64-gnu@4.2.2': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.2.1': + '@tailwindcss/oxide-linux-x64-musl@4.2.2': optional: true - '@tailwindcss/oxide-wasm32-wasi@4.2.1': + '@tailwindcss/oxide-wasm32-wasi@4.2.2': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': + '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.2.1': + '@tailwindcss/oxide-win32-x64-msvc@4.2.2': optional: true - '@tailwindcss/oxide@4.2.1': + '@tailwindcss/oxide@4.2.2': optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.2.1 - '@tailwindcss/oxide-darwin-arm64': 4.2.1 - '@tailwindcss/oxide-darwin-x64': 4.2.1 - '@tailwindcss/oxide-freebsd-x64': 4.2.1 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.1 - '@tailwindcss/oxide-linux-arm64-gnu': 4.2.1 - '@tailwindcss/oxide-linux-arm64-musl': 4.2.1 - '@tailwindcss/oxide-linux-x64-gnu': 4.2.1 - '@tailwindcss/oxide-linux-x64-musl': 4.2.1 - '@tailwindcss/oxide-wasm32-wasi': 4.2.1 - '@tailwindcss/oxide-win32-arm64-msvc': 4.2.1 - '@tailwindcss/oxide-win32-x64-msvc': 4.2.1 + '@tailwindcss/oxide-android-arm64': 4.2.2 + '@tailwindcss/oxide-darwin-arm64': 4.2.2 + '@tailwindcss/oxide-darwin-x64': 4.2.2 + '@tailwindcss/oxide-freebsd-x64': 4.2.2 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.2 + '@tailwindcss/oxide-linux-arm64-gnu': 4.2.2 + '@tailwindcss/oxide-linux-arm64-musl': 4.2.2 + '@tailwindcss/oxide-linux-x64-gnu': 4.2.2 + '@tailwindcss/oxide-linux-x64-musl': 4.2.2 + '@tailwindcss/oxide-wasm32-wasi': 4.2.2 + '@tailwindcss/oxide-win32-arm64-msvc': 4.2.2 + '@tailwindcss/oxide-win32-x64-msvc': 4.2.2 - '@tailwindcss/vite@4.2.1(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': + '@tailwindcss/vite@4.2.2(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))': dependencies: - '@tailwindcss/node': 4.2.1 - '@tailwindcss/oxide': 4.2.1 - tailwindcss: 4.2.1 - vite: 8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + '@tailwindcss/node': 4.2.2 + '@tailwindcss/oxide': 4.2.2 + tailwindcss: 4.2.2 + vite: '@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)' '@tanstack/history@1.161.6': {} - '@tanstack/query-core@5.90.20': {} + '@tanstack/query-core@5.91.0': {} - '@tanstack/react-query@5.90.21(react@19.2.4)': + '@tanstack/react-query@5.91.0(react@19.2.4)': dependencies: - '@tanstack/query-core': 5.90.20 + '@tanstack/query-core': 5.91.0 react: 19.2.4 - '@tanstack/react-router-ssr-query@1.166.9(@tanstack/query-core@5.90.20)(@tanstack/react-query@5.90.21(react@19.2.4))(@tanstack/react-router@1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.167.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@tanstack/react-router-ssr-query@1.166.9(@tanstack/query-core@5.91.0)(@tanstack/react-query@5.91.0(react@19.2.4))(@tanstack/react-router@1.167.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@tanstack/router-core@1.167.5)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: - '@tanstack/query-core': 5.90.20 - '@tanstack/react-query': 5.90.21(react@19.2.4) - '@tanstack/react-router': 1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@tanstack/router-ssr-query-core': 1.166.9(@tanstack/query-core@5.90.20)(@tanstack/router-core@1.167.4) + '@tanstack/query-core': 5.91.0 + '@tanstack/react-query': 5.91.0(react@19.2.4) + '@tanstack/react-router': 1.167.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@tanstack/router-ssr-query-core': 1.166.9(@tanstack/query-core@5.91.0)(@tanstack/router-core@1.167.5) react: 19.2.4 react-dom: 19.2.4(react@19.2.4) transitivePeerDependencies: - '@tanstack/router-core' - '@tanstack/react-router@1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@tanstack/react-router@1.167.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@tanstack/history': 1.161.6 '@tanstack/react-store': 0.9.1(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@tanstack/router-core': 1.167.4 + '@tanstack/router-core': 1.167.5 isbot: 5.1.35 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/react-start-client@1.166.13(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@tanstack/react-start-client@1.166.14(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: - '@tanstack/react-router': 1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@tanstack/router-core': 1.167.4 - '@tanstack/start-client-core': 1.166.12 + '@tanstack/react-router': 1.167.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@tanstack/router-core': 1.167.5 + '@tanstack/start-client-core': 1.166.13 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/react-start-server@1.166.13(crossws@0.4.4(srvx@0.11.12))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@tanstack/react-start-server@1.166.14(crossws@0.4.4(srvx@0.11.12))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: '@tanstack/history': 1.161.6 - '@tanstack/react-router': 1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@tanstack/router-core': 1.167.4 - '@tanstack/start-client-core': 1.166.12 - '@tanstack/start-server-core': 1.166.12(crossws@0.4.4(srvx@0.11.12)) + '@tanstack/react-router': 1.167.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@tanstack/router-core': 1.167.5 + '@tanstack/start-client-core': 1.166.13 + '@tanstack/start-server-core': 1.166.13(crossws@0.4.4(srvx@0.11.12)) react: 19.2.4 react-dom: 19.2.4(react@19.2.4) transitivePeerDependencies: - crossws - '@tanstack/react-start@1.166.16(crossws@0.4.4(srvx@0.11.12))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': + '@tanstack/react-start@1.166.17(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(crossws@0.4.4(srvx@0.11.12))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: - '@tanstack/react-router': 1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@tanstack/react-start-client': 1.166.13(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@tanstack/react-start-server': 1.166.13(crossws@0.4.4(srvx@0.11.12))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@tanstack/react-router': 1.167.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@tanstack/react-start-client': 1.166.14(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@tanstack/react-start-server': 1.166.14(crossws@0.4.4(srvx@0.11.12))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@tanstack/router-utils': 1.161.6 - '@tanstack/start-client-core': 1.166.12 - '@tanstack/start-plugin-core': 1.167.0(@tanstack/react-router@1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(crossws@0.4.4(srvx@0.11.12))(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) - '@tanstack/start-server-core': 1.166.12(crossws@0.4.4(srvx@0.11.12)) + '@tanstack/start-client-core': 1.166.13 + '@tanstack/start-plugin-core': 1.167.1(@tanstack/react-router@1.167.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(crossws@0.4.4(srvx@0.11.12)) + '@tanstack/start-server-core': 1.166.13(crossws@0.4.4(srvx@0.11.12)) pathe: 2.0.3 react: 19.2.4 react-dom: 19.2.4(react@19.2.4) - vite: 8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: '@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)' transitivePeerDependencies: - '@rsbuild/core' - crossws @@ -11272,7 +11329,7 @@ snapshots: react-dom: 19.2.4(react@19.2.4) use-sync-external-store: 1.6.0(react@19.2.4) - '@tanstack/router-core@1.167.4': + '@tanstack/router-core@1.167.5': dependencies: '@tanstack/history': 1.161.6 '@tanstack/store': 0.9.1 @@ -11282,9 +11339,9 @@ snapshots: tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/router-generator@1.166.12': + '@tanstack/router-generator@1.166.13': dependencies: - '@tanstack/router-core': 1.167.4 + '@tanstack/router-core': 1.167.5 '@tanstack/router-utils': 1.161.6 '@tanstack/virtual-file-routes': 1.161.7 prettier: 3.8.1 @@ -11295,7 +11352,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/router-plugin@1.166.13(@tanstack/react-router@1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': + '@tanstack/router-plugin@1.166.14(@tanstack/react-router@1.167.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) @@ -11303,23 +11360,23 @@ snapshots: '@babel/template': 7.28.6 '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 - '@tanstack/router-core': 1.167.4 - '@tanstack/router-generator': 1.166.12 + '@tanstack/router-core': 1.167.5 + '@tanstack/router-generator': 1.166.13 '@tanstack/router-utils': 1.161.6 '@tanstack/virtual-file-routes': 1.161.7 chokidar: 3.6.0 unplugin: 2.3.11 zod: 3.25.76 optionalDependencies: - '@tanstack/react-router': 1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - vite: 8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + '@tanstack/react-router': 1.167.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + vite: '@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)' transitivePeerDependencies: - supports-color - '@tanstack/router-ssr-query-core@1.166.9(@tanstack/query-core@5.90.20)(@tanstack/router-core@1.167.4)': + '@tanstack/router-ssr-query-core@1.166.9(@tanstack/query-core@5.91.0)(@tanstack/router-core@1.167.5)': dependencies: - '@tanstack/query-core': 5.90.20 - '@tanstack/router-core': 1.167.4 + '@tanstack/query-core': 5.91.0 + '@tanstack/router-core': 1.167.5 '@tanstack/router-utils@1.161.6': dependencies: @@ -11335,29 +11392,29 @@ snapshots: transitivePeerDependencies: - supports-color - '@tanstack/start-client-core@1.166.12': + '@tanstack/start-client-core@1.166.13': dependencies: - '@tanstack/router-core': 1.167.4 + '@tanstack/router-core': 1.167.5 '@tanstack/start-fn-stubs': 1.161.6 - '@tanstack/start-storage-context': 1.166.12 + '@tanstack/start-storage-context': 1.166.13 seroval: 1.5.0 tiny-invariant: 1.3.3 tiny-warning: 1.0.3 '@tanstack/start-fn-stubs@1.161.6': {} - '@tanstack/start-plugin-core@1.167.0(@tanstack/react-router@1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(crossws@0.4.4(srvx@0.11.12))(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': + '@tanstack/start-plugin-core@1.167.1(@tanstack/react-router@1.167.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(crossws@0.4.4(srvx@0.11.12))': dependencies: '@babel/code-frame': 7.27.1 '@babel/core': 7.29.0 '@babel/types': 7.29.0 '@rolldown/pluginutils': 1.0.0-beta.40 - '@tanstack/router-core': 1.167.4 - '@tanstack/router-generator': 1.166.12 - '@tanstack/router-plugin': 1.166.13(@tanstack/react-router@1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + '@tanstack/router-core': 1.167.5 + '@tanstack/router-generator': 1.166.13 + '@tanstack/router-plugin': 1.166.14(@tanstack/react-router@1.167.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)) '@tanstack/router-utils': 1.161.6 - '@tanstack/start-client-core': 1.166.12 - '@tanstack/start-server-core': 1.166.12(crossws@0.4.4(srvx@0.11.12)) + '@tanstack/start-client-core': 1.166.13 + '@tanstack/start-server-core': 1.166.13(crossws@0.4.4(srvx@0.11.12)) cheerio: 1.2.0 exsolve: 1.0.8 pathe: 2.0.3 @@ -11366,8 +11423,8 @@ snapshots: srvx: 0.11.12 tinyglobby: 0.2.15 ufo: 1.6.3 - vite: 8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) - vitefu: 1.1.2(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + vite: '@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)' + vitefu: 1.1.2(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)) xmlbuilder2: 4.0.3 zod: 3.25.76 transitivePeerDependencies: @@ -11378,29 +11435,29 @@ snapshots: - vite-plugin-solid - webpack - '@tanstack/start-server-core@1.166.12(crossws@0.4.4(srvx@0.11.12))': + '@tanstack/start-server-core@1.166.13(crossws@0.4.4(srvx@0.11.12))': dependencies: '@tanstack/history': 1.161.6 - '@tanstack/router-core': 1.167.4 - '@tanstack/start-client-core': 1.166.12 - '@tanstack/start-storage-context': 1.166.12 + '@tanstack/router-core': 1.167.5 + '@tanstack/start-client-core': 1.166.13 + '@tanstack/start-storage-context': 1.166.13 h3-v2: h3@2.0.1-rc.16(crossws@0.4.4(srvx@0.11.12)) seroval: 1.5.0 tiny-invariant: 1.3.3 transitivePeerDependencies: - crossws - '@tanstack/start-storage-context@1.166.12': + '@tanstack/start-storage-context@1.166.13': dependencies: - '@tanstack/router-core': 1.167.4 + '@tanstack/router-core': 1.167.5 '@tanstack/store@0.9.1': {} '@tanstack/virtual-file-routes@1.161.7': {} - '@tanstack/zod-adapter@1.166.9(@tanstack/react-router@1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(zod@4.3.6)': + '@tanstack/zod-adapter@1.166.9(@tanstack/react-router@1.167.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(zod@4.3.6)': dependencies: - '@tanstack/react-router': 1.167.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@tanstack/react-router': 1.167.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) zod: 4.3.6 '@tediousjs/connection-string@0.5.0': {} @@ -11637,6 +11694,13 @@ snapshots: dependencies: '@types/node': 25.5.0 + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + '@types/estree@0.0.39': {} '@types/estree@1.0.8': {} @@ -11811,12 +11875,86 @@ snapshots: '@vercel/oidc@3.1.0': {} - '@vitejs/plugin-react@6.0.1(@rolldown/plugin-babel@0.2.2(@babel/core@7.29.0)(@babel/runtime@7.28.6)(rolldown@1.0.0-rc.9)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)))(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))': + '@vitejs/plugin-react@6.0.1(@rolldown/plugin-babel@0.2.2(@babel/core@7.29.0)(@babel/runtime@7.28.6)(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(rolldown@1.0.0-rc.9))(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))': dependencies: '@rolldown/pluginutils': 1.0.0-rc.7 - vite: 8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: '@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)' optionalDependencies: - '@rolldown/plugin-babel': 0.2.2(@babel/core@7.29.0)(@babel/runtime@7.28.6)(rolldown@1.0.0-rc.9)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + '@rolldown/plugin-babel': 0.2.2(@babel/core@7.29.0)(@babel/runtime@7.28.6)(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(rolldown@1.0.0-rc.9) + + '@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)': + dependencies: + '@oxc-project/runtime': 0.115.0 + '@oxc-project/types': 0.115.0 + lightningcss: 1.32.0 + postcss: 8.5.8 + optionalDependencies: + '@types/node': 25.5.0 + esbuild: 0.27.3 + fsevents: 2.3.3 + jiti: 2.6.1 + terser: 5.46.0 + tsx: 4.21.0 + typescript: 5.9.3 + yaml: 2.8.2 + + '@voidzero-dev/vite-plus-darwin-arm64@0.1.12': + optional: true + + '@voidzero-dev/vite-plus-darwin-x64@0.1.12': + optional: true + + '@voidzero-dev/vite-plus-linux-arm64-gnu@0.1.12': + optional: true + + '@voidzero-dev/vite-plus-linux-x64-gnu@0.1.12': + optional: true + + '@voidzero-dev/vite-plus-test@0.1.12(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@voidzero-dev/vite-plus-core': 0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) + es-module-lexer: 1.7.0 + obug: 2.1.1 + pixelmatch: 7.1.0 + pngjs: 7.0.0 + sirv: 3.0.2 + std-env: 4.0.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + vite: '@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)' + ws: 8.19.0 + optionalDependencies: + '@opentelemetry/api': 1.9.0 + '@types/node': 25.5.0 + transitivePeerDependencies: + - '@arethetypeswrong/core' + - '@tsdown/css' + - '@tsdown/exe' + - '@vitejs/devtools' + - bufferutil + - esbuild + - jiti + - less + - publint + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - typescript + - unplugin-unused + - utf-8-validate + - yaml + + '@voidzero-dev/vite-plus-win32-arm64-msvc@0.1.12': + optional: true + + '@voidzero-dev/vite-plus-win32-x64-msvc@0.1.12': + optional: true '@xmldom/is-dom-node@1.0.1': {} @@ -11908,6 +12046,8 @@ snapshots: dependencies: safer-buffer: 2.1.2 + assertion-error@2.0.1: {} + ast-types@0.13.4: dependencies: tslib: 2.8.1 @@ -12016,7 +12156,7 @@ snapshots: node-addon-api: 8.6.0 node-gyp-build: 4.8.4 - better-auth@1.5.5(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.166.16(crossws@0.4.4(srvx@0.11.12))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)))(drizzle-kit@1.0.0-beta.18-7eb39f0)(drizzle-orm@1.0.0-beta.18-7eb39f0(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(@azure/core-client@1.10.1))(@types/pg@8.18.0)(mssql@11.0.1(@azure/core-client@1.10.1))(mysql2@3.15.3)(pg@8.20.0)(postgres@3.4.7)(valibot@1.2.0(typescript@5.9.3))(zod@4.3.6))(mongodb@7.1.0(socks@2.8.7))(mysql2@3.15.3)(pg@8.20.0)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + better-auth@1.5.5(@prisma/client@7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3))(@tanstack/react-start@1.166.17(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(crossws@0.4.4(srvx@0.11.12))(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(drizzle-kit@1.0.0-beta.18-7eb39f0)(drizzle-orm@1.0.0-beta.18-7eb39f0(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(@azure/core-client@1.10.1))(@types/pg@8.18.0)(mssql@11.0.1(@azure/core-client@1.10.1))(mysql2@3.15.3)(pg@8.20.0)(postgres@3.4.7)(valibot@1.2.0(typescript@5.9.3))(zod@4.3.6))(mongodb@7.1.0(socks@2.8.7))(mysql2@3.15.3)(pg@8.20.0)(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: '@better-auth/core': 1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1) '@better-auth/drizzle-adapter': 1.5.5(@better-auth/core@1.5.5(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(better-call@1.3.2(zod@4.3.6))(jose@6.1.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(drizzle-orm@1.0.0-beta.18-7eb39f0(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(@azure/core-client@1.10.1))(@types/pg@8.18.0)(mssql@11.0.1(@azure/core-client@1.10.1))(mysql2@3.15.3)(pg@8.20.0)(postgres@3.4.7)(valibot@1.2.0(typescript@5.9.3))(zod@4.3.6)) @@ -12037,7 +12177,7 @@ snapshots: zod: 4.3.6 optionalDependencies: '@prisma/client': 7.4.2(prisma@7.4.2(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3) - '@tanstack/react-start': 1.166.16(crossws@0.4.4(srvx@0.11.12))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)) + '@tanstack/react-start': 1.166.17(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(crossws@0.4.4(srvx@0.11.12))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) drizzle-kit: 1.0.0-beta.18-7eb39f0 drizzle-orm: 1.0.0-beta.18-7eb39f0(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(@azure/core-client@1.10.1))(@types/pg@8.18.0)(mssql@11.0.1(@azure/core-client@1.10.1))(mysql2@3.15.3)(pg@8.20.0)(postgres@3.4.7)(valibot@1.2.0(typescript@5.9.3))(zod@4.3.6) mongodb: 7.1.0(socks@2.8.7) @@ -12156,6 +12296,8 @@ snapshots: rc9: 2.1.2 optional: true + cac@6.7.14: {} + cacache@20.0.3: dependencies: '@npmcli/fs': 5.0.0 @@ -12709,6 +12851,8 @@ snapshots: es-errors@1.3.0: {} + es-module-lexer@1.7.0: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -13573,88 +13717,39 @@ snapshots: libphonenumber-js@1.12.40: {} - lightningcss-android-arm64@1.31.1: - optional: true - lightningcss-android-arm64@1.32.0: optional: true - lightningcss-darwin-arm64@1.31.1: - optional: true - lightningcss-darwin-arm64@1.32.0: optional: true - lightningcss-darwin-x64@1.31.1: - optional: true - lightningcss-darwin-x64@1.32.0: optional: true - lightningcss-freebsd-x64@1.31.1: - optional: true - lightningcss-freebsd-x64@1.32.0: optional: true - lightningcss-linux-arm-gnueabihf@1.31.1: - optional: true - lightningcss-linux-arm-gnueabihf@1.32.0: optional: true - lightningcss-linux-arm64-gnu@1.31.1: - optional: true - lightningcss-linux-arm64-gnu@1.32.0: optional: true - lightningcss-linux-arm64-musl@1.31.1: - optional: true - lightningcss-linux-arm64-musl@1.32.0: optional: true - lightningcss-linux-x64-gnu@1.31.1: - optional: true - lightningcss-linux-x64-gnu@1.32.0: optional: true - lightningcss-linux-x64-musl@1.31.1: - optional: true - lightningcss-linux-x64-musl@1.32.0: optional: true - lightningcss-win32-arm64-msvc@1.31.1: - optional: true - lightningcss-win32-arm64-msvc@1.32.0: optional: true - lightningcss-win32-x64-msvc@1.31.1: - optional: true - lightningcss-win32-x64-msvc@1.32.0: optional: true - lightningcss@1.31.1: - dependencies: - detect-libc: 2.1.2 - optionalDependencies: - lightningcss-android-arm64: 1.31.1 - lightningcss-darwin-arm64: 1.31.1 - lightningcss-darwin-x64: 1.31.1 - lightningcss-freebsd-x64: 1.31.1 - lightningcss-linux-arm-gnueabihf: 1.31.1 - lightningcss-linux-arm64-gnu: 1.31.1 - lightningcss-linux-arm64-musl: 1.31.1 - lightningcss-linux-x64-gnu: 1.31.1 - lightningcss-linux-x64-musl: 1.31.1 - lightningcss-win32-arm64-msvc: 1.31.1 - lightningcss-win32-x64-msvc: 1.31.1 - lightningcss@1.32.0: dependencies: detect-libc: 2.1.2 @@ -13866,6 +13961,8 @@ snapshots: react: 19.2.4 react-dom: 19.2.4(react@19.2.4) + mrmime@2.0.1: {} + ms@2.1.3: {} mssql@11.0.1(@azure/core-client@1.10.1): @@ -13937,7 +14034,7 @@ snapshots: nf3@0.3.11: {} - nitro@3.0.260311-beta(@azure/identity@4.13.0)(@electric-sql/pglite@0.3.15)(chokidar@4.0.3)(dotenv@17.3.1)(drizzle-orm@1.0.0-beta.18-7eb39f0(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(@azure/core-client@1.10.1))(@types/pg@8.18.0)(mssql@11.0.1(@azure/core-client@1.10.1))(mysql2@3.15.3)(pg@8.20.0)(postgres@3.4.7)(valibot@1.2.0(typescript@5.9.3))(zod@4.3.6))(giget@2.0.0)(jiti@2.6.1)(lru-cache@11.2.6)(mongodb@7.1.0(socks@2.8.7))(mysql2@3.15.3)(rollup@2.80.0)(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)): + nitro@3.0.260311-beta(@azure/identity@4.13.0)(@electric-sql/pglite@0.3.15)(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(chokidar@4.0.3)(dotenv@17.3.1)(drizzle-orm@1.0.0-beta.18-7eb39f0(@electric-sql/pglite@0.3.15)(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(@azure/core-client@1.10.1))(@types/pg@8.18.0)(mssql@11.0.1(@azure/core-client@1.10.1))(mysql2@3.15.3)(pg@8.20.0)(postgres@3.4.7)(valibot@1.2.0(typescript@5.9.3))(zod@4.3.6))(giget@2.0.0)(jiti@2.6.1)(lru-cache@11.2.6)(mongodb@7.1.0(socks@2.8.7))(mysql2@3.15.3)(rollup@2.80.0): dependencies: consola: 3.4.2 crossws: 0.4.4(srvx@0.11.12) @@ -13958,7 +14055,7 @@ snapshots: giget: 2.0.0 jiti: 2.6.1 rollup: 2.80.0 - vite: 8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: '@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)' transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -14074,6 +14171,8 @@ snapshots: observable-fns@0.6.1: {} + obug@2.1.1: {} + ocache@0.1.2: dependencies: ohash: 2.0.11 @@ -14177,29 +14276,29 @@ snapshots: '@oxc-resolver/binding-win32-ia32-msvc': 11.19.1 '@oxc-resolver/binding-win32-x64-msvc': 11.19.1 - oxfmt@0.41.0: + oxfmt@0.40.0: dependencies: tinypool: 2.1.0 optionalDependencies: - '@oxfmt/binding-android-arm-eabi': 0.41.0 - '@oxfmt/binding-android-arm64': 0.41.0 - '@oxfmt/binding-darwin-arm64': 0.41.0 - '@oxfmt/binding-darwin-x64': 0.41.0 - '@oxfmt/binding-freebsd-x64': 0.41.0 - '@oxfmt/binding-linux-arm-gnueabihf': 0.41.0 - '@oxfmt/binding-linux-arm-musleabihf': 0.41.0 - '@oxfmt/binding-linux-arm64-gnu': 0.41.0 - '@oxfmt/binding-linux-arm64-musl': 0.41.0 - '@oxfmt/binding-linux-ppc64-gnu': 0.41.0 - '@oxfmt/binding-linux-riscv64-gnu': 0.41.0 - '@oxfmt/binding-linux-riscv64-musl': 0.41.0 - '@oxfmt/binding-linux-s390x-gnu': 0.41.0 - '@oxfmt/binding-linux-x64-gnu': 0.41.0 - '@oxfmt/binding-linux-x64-musl': 0.41.0 - '@oxfmt/binding-openharmony-arm64': 0.41.0 - '@oxfmt/binding-win32-arm64-msvc': 0.41.0 - '@oxfmt/binding-win32-ia32-msvc': 0.41.0 - '@oxfmt/binding-win32-x64-msvc': 0.41.0 + '@oxfmt/binding-android-arm-eabi': 0.40.0 + '@oxfmt/binding-android-arm64': 0.40.0 + '@oxfmt/binding-darwin-arm64': 0.40.0 + '@oxfmt/binding-darwin-x64': 0.40.0 + '@oxfmt/binding-freebsd-x64': 0.40.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.40.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.40.0 + '@oxfmt/binding-linux-arm64-gnu': 0.40.0 + '@oxfmt/binding-linux-arm64-musl': 0.40.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.40.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.40.0 + '@oxfmt/binding-linux-riscv64-musl': 0.40.0 + '@oxfmt/binding-linux-s390x-gnu': 0.40.0 + '@oxfmt/binding-linux-x64-gnu': 0.40.0 + '@oxfmt/binding-linux-x64-musl': 0.40.0 + '@oxfmt/binding-openharmony-arm64': 0.40.0 + '@oxfmt/binding-win32-arm64-msvc': 0.40.0 + '@oxfmt/binding-win32-ia32-msvc': 0.40.0 + '@oxfmt/binding-win32-x64-msvc': 0.40.0 oxlint-tsgolint@0.17.0: optionalDependencies: @@ -14210,27 +14309,27 @@ snapshots: '@oxlint-tsgolint/win32-arm64': 0.17.0 '@oxlint-tsgolint/win32-x64': 0.17.0 - oxlint@1.56.0(oxlint-tsgolint@0.17.0): + oxlint@1.55.0(oxlint-tsgolint@0.17.0): optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.56.0 - '@oxlint/binding-android-arm64': 1.56.0 - '@oxlint/binding-darwin-arm64': 1.56.0 - '@oxlint/binding-darwin-x64': 1.56.0 - '@oxlint/binding-freebsd-x64': 1.56.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.56.0 - '@oxlint/binding-linux-arm-musleabihf': 1.56.0 - '@oxlint/binding-linux-arm64-gnu': 1.56.0 - '@oxlint/binding-linux-arm64-musl': 1.56.0 - '@oxlint/binding-linux-ppc64-gnu': 1.56.0 - '@oxlint/binding-linux-riscv64-gnu': 1.56.0 - '@oxlint/binding-linux-riscv64-musl': 1.56.0 - '@oxlint/binding-linux-s390x-gnu': 1.56.0 - '@oxlint/binding-linux-x64-gnu': 1.56.0 - '@oxlint/binding-linux-x64-musl': 1.56.0 - '@oxlint/binding-openharmony-arm64': 1.56.0 - '@oxlint/binding-win32-arm64-msvc': 1.56.0 - '@oxlint/binding-win32-ia32-msvc': 1.56.0 - '@oxlint/binding-win32-x64-msvc': 1.56.0 + '@oxlint/binding-android-arm-eabi': 1.55.0 + '@oxlint/binding-android-arm64': 1.55.0 + '@oxlint/binding-darwin-arm64': 1.55.0 + '@oxlint/binding-darwin-x64': 1.55.0 + '@oxlint/binding-freebsd-x64': 1.55.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.55.0 + '@oxlint/binding-linux-arm-musleabihf': 1.55.0 + '@oxlint/binding-linux-arm64-gnu': 1.55.0 + '@oxlint/binding-linux-arm64-musl': 1.55.0 + '@oxlint/binding-linux-ppc64-gnu': 1.55.0 + '@oxlint/binding-linux-riscv64-gnu': 1.55.0 + '@oxlint/binding-linux-riscv64-musl': 1.55.0 + '@oxlint/binding-linux-s390x-gnu': 1.55.0 + '@oxlint/binding-linux-x64-gnu': 1.55.0 + '@oxlint/binding-linux-x64-musl': 1.55.0 + '@oxlint/binding-openharmony-arm64': 1.55.0 + '@oxlint/binding-win32-arm64-msvc': 1.55.0 + '@oxlint/binding-win32-ia32-msvc': 1.55.0 + '@oxlint/binding-win32-x64-msvc': 1.55.0 oxlint-tsgolint: 0.17.0 p-map@7.0.4: {} @@ -14352,6 +14451,10 @@ snapshots: picomatch@4.0.3: {} + pixelmatch@7.1.0: + dependencies: + pngjs: 7.0.0 + pkce-challenge@5.0.1: {} pkg-types@2.3.0: @@ -14361,6 +14464,8 @@ snapshots: pathe: 2.0.3 optional: true + pngjs@7.0.0: {} + pofile@1.1.4: {} possible-typed-array-names@1.1.0: {} @@ -15063,6 +15168,12 @@ snapshots: signal-exit@4.1.0: {} + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + sisteransi@1.0.5: {} smart-buffer@4.2.0: {} @@ -15130,6 +15241,8 @@ snapshots: std-env@3.10.0: optional: true + std-env@4.0.0: {} + stdin-discarder@0.2.2: {} stop-iteration-iterator@1.1.0: @@ -15253,7 +15366,7 @@ snapshots: tailwind-merge@3.5.0: {} - tailwindcss@4.2.1: {} + tailwindcss@4.2.2: {} tapable@2.3.0: {} @@ -15373,8 +15486,9 @@ snapshots: esm: 3.2.25 optional: true - tinyexec@1.0.2: - optional: true + tinybench@2.9.0: {} + + tinyexec@1.0.2: {} tinyglobby@0.2.15: dependencies: @@ -15401,6 +15515,8 @@ snapshots: toidentifier@1.0.1: {} + totalist@3.0.1: {} + tough-cookie@6.0.0: dependencies: tldts: 7.0.25 @@ -15603,37 +15719,66 @@ snapshots: vary@1.1.2: {} - vite-plugin-pwa@1.2.0(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2))(workbox-build@7.4.0(@types/babel__core@7.20.5))(workbox-window@7.4.0): + vite-plugin-pwa@1.2.0(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(workbox-build@7.4.0(@types/babel__core@7.20.5))(workbox-window@7.4.0): dependencies: debug: 4.4.3 pretty-bytes: 6.1.1 tinyglobby: 0.2.15 - vite: 8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: '@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)' workbox-build: 7.4.0(@types/babel__core@7.20.5) workbox-window: 7.4.0 transitivePeerDependencies: - supports-color - vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2): + vite-plus@0.1.12(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2): dependencies: - '@oxc-project/runtime': 0.115.0 - lightningcss: 1.32.0 - picomatch: 4.0.3 - postcss: 8.5.8 - rolldown: 1.0.0-rc.9 - tinyglobby: 0.2.15 + '@oxc-project/types': 0.115.0 + '@voidzero-dev/vite-plus-core': 0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) + '@voidzero-dev/vite-plus-test': 0.1.12(@opentelemetry/api@1.9.0)(@types/node@25.5.0)(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2))(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) + cac: 6.7.14 + cross-spawn: 7.0.6 + oxfmt: 0.40.0 + oxlint: 1.55.0(oxlint-tsgolint@0.17.0) + oxlint-tsgolint: 0.17.0 + picocolors: 1.1.1 optionalDependencies: - '@types/node': 25.5.0 - esbuild: 0.27.3 - fsevents: 2.3.3 - jiti: 2.6.1 - terser: 5.46.0 - tsx: 4.21.0 - yaml: 2.8.2 + '@voidzero-dev/vite-plus-darwin-arm64': 0.1.12 + '@voidzero-dev/vite-plus-darwin-x64': 0.1.12 + '@voidzero-dev/vite-plus-linux-arm64-gnu': 0.1.12 + '@voidzero-dev/vite-plus-linux-x64-gnu': 0.1.12 + '@voidzero-dev/vite-plus-win32-arm64-msvc': 0.1.12 + '@voidzero-dev/vite-plus-win32-x64-msvc': 0.1.12 + transitivePeerDependencies: + - '@arethetypeswrong/core' + - '@edge-runtime/vm' + - '@opentelemetry/api' + - '@tsdown/css' + - '@tsdown/exe' + - '@types/node' + - '@vitejs/devtools' + - '@vitest/ui' + - bufferutil + - esbuild + - happy-dom + - jiti + - jsdom + - less + - publint + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - typescript + - unplugin-unused + - utf-8-validate + - vite + - yaml - vitefu@1.1.2(vite@8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2)): + vitefu@1.1.2(@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)): optionalDependencies: - vite: 8.0.0(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(yaml@2.8.2) + vite: '@voidzero-dev/vite-plus-core@0.1.12(@types/node@25.5.0)(esbuild@0.27.3)(jiti@2.6.1)(terser@5.46.0)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2)' w3c-keyname@2.2.8: {} diff --git a/public/funding.json b/public/funding.json index b58de0dc5..bc2b25393 100644 --- a/public/funding.json +++ b/public/funding.json @@ -1,68 +1,68 @@ { - "$schema": "https://fundingjson.org/schema/v1.1.0.json", - "version": "v1.1.0", - "entity": { - "type": "individual", - "role": "maintainer", - "name": "Amruth Pillai", - "email": "im.amruth@gmail.com", - "description": "Software Engineer", - "webpageUrl": { - "url": "https://rxresu.me/funding.json" - } - }, - "projects": [ - { - "guid": "reactive-resume", - "name": "Reactive Resume", - "description": "A free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.", - "webpageUrl": { - "url": "https://rxresu.me" - }, - "repositoryUrl": { - "url": "https://github.com/amruthpillai/reactive-resume", - "wellKnown": "https://github.com/amruthpillai/reactive-resume/blob/main/.github/.well-known/funding-manifest-urls" - }, - "licenses": ["spdx:MIT"], - "tags": ["data", "design", "productivity", "resume-builder"] - } - ], - "funding": { - "plans": [ - { - "guid": "sponsor", - "status": "active", - "name": "Sponsor", - "description": "Support the project on a recurring basis by becoming a sponsor.", - "amount": 10, - "currency": "EUR", - "frequency": "monthly", - "channels": ["github", "open-collective"] - }, - { - "guid": "donation", - "status": "active", - "name": "Donation", - "description": "Show your support for the project by making a one-time donation.", - "amount": 0, - "currency": "EUR", - "frequency": "one-time", - "channels": ["github", "open-collective"] - } - ], - "channels": [ - { - "guid": "github", - "type": "payment-provider", - "description": "GitHub Sponsors", - "address": "https://github.com/sponsors/AmruthPillai" - }, - { - "guid": "open-collective", - "type": "payment-provider", - "description": "Open Collective", - "address": "https://opencollective.com/reactive-resume" - } - ] - } + "$schema": "https://fundingjson.org/schema/v1.1.0.json", + "version": "v1.1.0", + "entity": { + "type": "individual", + "role": "maintainer", + "name": "Amruth Pillai", + "email": "im.amruth@gmail.com", + "description": "Software Engineer", + "webpageUrl": { + "url": "https://rxresu.me/funding.json" + } + }, + "projects": [ + { + "guid": "reactive-resume", + "name": "Reactive Resume", + "description": "A free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.", + "webpageUrl": { + "url": "https://rxresu.me" + }, + "repositoryUrl": { + "url": "https://github.com/amruthpillai/reactive-resume", + "wellKnown": "https://github.com/amruthpillai/reactive-resume/blob/main/.github/.well-known/funding-manifest-urls" + }, + "licenses": ["spdx:MIT"], + "tags": ["data", "design", "productivity", "resume-builder"] + } + ], + "funding": { + "plans": [ + { + "guid": "sponsor", + "status": "active", + "name": "Sponsor", + "description": "Support the project on a recurring basis by becoming a sponsor.", + "amount": 10, + "currency": "EUR", + "frequency": "monthly", + "channels": ["github", "open-collective"] + }, + { + "guid": "donation", + "status": "active", + "name": "Donation", + "description": "Show your support for the project by making a one-time donation.", + "amount": 0, + "currency": "EUR", + "frequency": "one-time", + "channels": ["github", "open-collective"] + } + ], + "channels": [ + { + "guid": "github", + "type": "payment-provider", + "description": "GitHub Sponsors", + "address": "https://github.com/sponsors/AmruthPillai" + }, + { + "guid": "open-collective", + "type": "payment-provider", + "description": "Open Collective", + "address": "https://opencollective.com/reactive-resume" + } + ] + } } diff --git a/scripts/database/migrate.ts b/scripts/database/migrate.ts index f0d9049eb..61af917c7 100644 --- a/scripts/database/migrate.ts +++ b/scripts/database/migrate.ts @@ -5,21 +5,21 @@ import { Pool } from "pg"; import { env } from "@/utils/env"; export async function migrateDatabase() { - console.log("⌛ Running database migrations..."); + console.log("⌛ Running database migrations..."); - const pool = new Pool({ connectionString: env.DATABASE_URL }); - const db = drizzle({ client: pool }); + const pool = new Pool({ connectionString: env.DATABASE_URL }); + const db = drizzle({ client: pool }); - try { - await migrate(db, { migrationsFolder: "./migrations" }); - console.log("✅ Database migrations completed"); - } catch (error) { - console.error("🚨 Database migrations failed:", error); - } finally { - await pool.end(); - } + try { + await migrate(db, { migrationsFolder: "./migrations" }); + console.log("✅ Database migrations completed"); + } catch (error) { + console.error("🚨 Database migrations failed:", error); + } finally { + await pool.end(); + } } if (import.meta.main) { - await migrateDatabase(); + await migrateDatabase(); } diff --git a/scripts/database/reset.ts b/scripts/database/reset.ts index 4eb90ee14..d1e9aecc3 100644 --- a/scripts/database/reset.ts +++ b/scripts/database/reset.ts @@ -5,30 +5,30 @@ import { Pool } from "pg"; import { env } from "@/utils/env"; export async function resetDatabase() { - console.log("⌛ Resetting database..."); + console.log("⌛ Resetting database..."); - const pool = new Pool({ connectionString: env.DATABASE_URL }); - const db = drizzle({ client: pool }); + const pool = new Pool({ connectionString: env.DATABASE_URL }); + const db = drizzle({ client: pool }); - try { - await db.transaction(async (tx) => { - await tx.execute(sql`DROP SCHEMA drizzle CASCADE`); - await tx.execute(sql`CREATE SCHEMA drizzle`); - await tx.execute(sql`GRANT ALL ON SCHEMA drizzle TO postgres`); + try { + await db.transaction(async (tx) => { + await tx.execute(sql`DROP SCHEMA drizzle CASCADE`); + await tx.execute(sql`CREATE SCHEMA drizzle`); + await tx.execute(sql`GRANT ALL ON SCHEMA drizzle TO postgres`); - await tx.execute(sql`DROP SCHEMA public CASCADE`); - await tx.execute(sql`CREATE SCHEMA public`); - await tx.execute(sql`GRANT ALL ON SCHEMA public TO postgres`); - }); + await tx.execute(sql`DROP SCHEMA public CASCADE`); + await tx.execute(sql`CREATE SCHEMA public`); + await tx.execute(sql`GRANT ALL ON SCHEMA public TO postgres`); + }); - console.log("✅ Database reset completed"); - } catch (error) { - console.error("🚨 Database reset failed:", error); - } finally { - await pool.end(); - } + console.log("✅ Database reset completed"); + } catch (error) { + console.error("🚨 Database reset failed:", error); + } finally { + await pool.end(); + } } if (import.meta.main) { - await resetDatabase(); + await resetDatabase(); } diff --git a/scripts/fonts/generate.ts b/scripts/fonts/generate.ts index e6a2253ba..a10664c10 100644 --- a/scripts/fonts/generate.ts +++ b/scripts/fonts/generate.ts @@ -25,35 +25,35 @@ const WEBFONTLIST_FILE = `${FONTS_DIR}/webfontlist.json`; /** Returns JSON from Google Fonts API or (unless --force) from local cache if it exists */ async function getGoogleFontsJSON() { - let contents: string | null = null; + let contents: string | null = null; - try { - contents = await readFile(RESPONSE_FILE, "utf-8"); - } catch { - // If the file doesn't exist or there's an error reading, just continue. - } + try { + contents = await readFile(RESPONSE_FILE, "utf-8"); + } catch { + // If the file doesn't exist or there's an error reading, just continue. + } - if (!argForce && contents) return JSON.parse(contents) as APIResponse; + if (!argForce && contents) return JSON.parse(contents) as APIResponse; - const apiKey = process.env.GOOGLE_CLOUD_API_KEY; - if (!apiKey) throw new Error("GOOGLE_CLOUD_API_KEY is not set"); + const apiKey = process.env.GOOGLE_CLOUD_API_KEY; + if (!apiKey) throw new Error("GOOGLE_CLOUD_API_KEY is not set"); - const url = `https://www.googleapis.com/webfonts/v1/webfonts?key=${apiKey}&sort=popularity`; - const response = await fetch(url); - const data = (await response.json()) as APIResponse; + const url = `https://www.googleapis.com/webfonts/v1/webfonts?key=${apiKey}&sort=popularity`; + const response = await fetch(url); + const data = (await response.json()) as APIResponse; - const jsonString = argCompress ? JSON.stringify(data) : JSON.stringify(data, null, 2); - await mkdir(FONTS_DIR, { recursive: true }); - await writeFile(RESPONSE_FILE, jsonString, "utf-8"); + const jsonString = argCompress ? JSON.stringify(data) : JSON.stringify(data, null, 2); + await mkdir(FONTS_DIR, { recursive: true }); + await writeFile(RESPONSE_FILE, jsonString, "utf-8"); - return data; + return data; } /** Map Google Fonts API variant strings to simple weights */ function variantToWeight(variant: Variant): Weight | null { - if (["100", "200", "300", "500", "600", "700", "800", "900"].includes(variant)) return variant as Weight; - if (variant === "regular") return "400"; - return null; + if (["100", "200", "300", "500", "600", "700", "800", "900"].includes(variant)) return variant as Weight; + if (variant === "regular") return "400"; + return null; } /** @@ -62,124 +62,124 @@ function variantToWeight(variant: Variant): Weight | null { * Files are delivered via jsDelivr CDN. */ function getComputerModernWebFonts(): WebFont[] { - const CDN = "https://cdn.jsdelivr.net/gh/bitmaks/cm-web-fonts@latest/font"; + const CDN = "https://cdn.jsdelivr.net/gh/bitmaks/cm-web-fonts@latest/font"; - return [ - { - type: "web", - category: "display", - family: "Computer Modern Bright", - weights: ["400", "700"], - preview: `${CDN}/Bright/cmunbmr.woff`, - files: { - "400": `${CDN}/Bright/cmunbmr.woff`, - "400italic": `${CDN}/Bright/cmunbmo.woff`, - "700": `${CDN}/Bright/cmunbbx.woff`, - "700italic": `${CDN}/Bright/cmunbxo.woff`, - }, - }, - { - type: "web", - category: "serif", - family: "Computer Modern Concrete", - weights: ["400", "700"], - preview: `${CDN}/Concrete/cmunorm.woff`, - files: { - "400": `${CDN}/Concrete/cmunorm.woff`, - "400italic": `${CDN}/Concrete/cmunobi.woff`, - "700": `${CDN}/Concrete/cmunobx.woff`, - "700italic": `${CDN}/Concrete/cmunoti.woff`, - }, - }, - { - type: "web", - category: "sans-serif", - family: "Computer Modern Sans", - weights: ["400", "700"], - preview: `${CDN}/Sans/cmunss.woff`, - files: { - "400": `${CDN}/Sans/cmunss.woff`, - "400italic": `${CDN}/Sans/cmunsl.woff`, - "700": `${CDN}/Sans/cmunsx.woff`, - "700italic": `${CDN}/Sans/cmunsi.woff`, - }, - }, - { - type: "web", - category: "serif", - family: "Computer Modern Serif", - weights: ["400", "700"], - preview: `${CDN}/Serif/cmunrm.woff`, - files: { - "400": `${CDN}/Serif/cmunrm.woff`, - "400italic": `${CDN}/Serif/cmunti.woff`, - "700": `${CDN}/Serif/cmunbx.woff`, - "700italic": `${CDN}/Serif/cmunbi.woff`, - }, - }, - { - type: "web", - category: "monospace", - family: "Computer Modern Typewriter", - weights: ["400", "700"], - preview: `${CDN}/Typewriter/cmuntt.woff`, - files: { - "400": `${CDN}/Typewriter/cmuntt.woff`, - "400italic": `${CDN}/Typewriter/cmunit.woff`, - "700": `${CDN}/Typewriter/cmuntx.woff`, - "700italic": `${CDN}/Typewriter/cmuntb.woff`, - }, - }, - ]; + return [ + { + type: "web", + category: "display", + family: "Computer Modern Bright", + weights: ["400", "700"], + preview: `${CDN}/Bright/cmunbmr.woff`, + files: { + "400": `${CDN}/Bright/cmunbmr.woff`, + "400italic": `${CDN}/Bright/cmunbmo.woff`, + "700": `${CDN}/Bright/cmunbbx.woff`, + "700italic": `${CDN}/Bright/cmunbxo.woff`, + }, + }, + { + type: "web", + category: "serif", + family: "Computer Modern Concrete", + weights: ["400", "700"], + preview: `${CDN}/Concrete/cmunorm.woff`, + files: { + "400": `${CDN}/Concrete/cmunorm.woff`, + "400italic": `${CDN}/Concrete/cmunobi.woff`, + "700": `${CDN}/Concrete/cmunobx.woff`, + "700italic": `${CDN}/Concrete/cmunoti.woff`, + }, + }, + { + type: "web", + category: "sans-serif", + family: "Computer Modern Sans", + weights: ["400", "700"], + preview: `${CDN}/Sans/cmunss.woff`, + files: { + "400": `${CDN}/Sans/cmunss.woff`, + "400italic": `${CDN}/Sans/cmunsl.woff`, + "700": `${CDN}/Sans/cmunsx.woff`, + "700italic": `${CDN}/Sans/cmunsi.woff`, + }, + }, + { + type: "web", + category: "serif", + family: "Computer Modern Serif", + weights: ["400", "700"], + preview: `${CDN}/Serif/cmunrm.woff`, + files: { + "400": `${CDN}/Serif/cmunrm.woff`, + "400italic": `${CDN}/Serif/cmunti.woff`, + "700": `${CDN}/Serif/cmunbx.woff`, + "700italic": `${CDN}/Serif/cmunbi.woff`, + }, + }, + { + type: "web", + category: "monospace", + family: "Computer Modern Typewriter", + weights: ["400", "700"], + preview: `${CDN}/Typewriter/cmuntt.woff`, + files: { + "400": `${CDN}/Typewriter/cmuntt.woff`, + "400italic": `${CDN}/Typewriter/cmunit.woff`, + "700": `${CDN}/Typewriter/cmuntx.woff`, + "700italic": `${CDN}/Typewriter/cmuntb.woff`, + }, + }, + ]; } export async function generateFonts() { - const response = await getGoogleFontsJSON(); - console.log(`Found ${response.items.length} fonts in total (Google Fonts).`); + const response = await getGoogleFontsJSON(); + console.log(`Found ${response.items.length} fonts in total (Google Fonts).`); - const filteredItems = response.items.filter( - (item) => !skippedFamilies.some((family) => item.family.includes(family)), - ); + const filteredItems = response.items.filter( + (item) => !skippedFamilies.some((family) => item.family.includes(family)), + ); - const googleFontResults: WebFont[] = filteredItems.slice(0, argLimit).map((item) => { - // 1. weights: Only non-italic, convert "regular" to "400" - const weights: Weight[] = item.variants.map((v) => variantToWeight(v)).filter((w): w is Weight => !!w); + const googleFontResults: WebFont[] = filteredItems.slice(0, argLimit).map((item) => { + // 1. weights: Only non-italic, convert "regular" to "400" + const weights: Weight[] = item.variants.map((v) => variantToWeight(v)).filter((w): w is Weight => !!w); - // 2. files: all files, but change "regular"->"400", "italic"->"400italic" - const files: Record = {}; + // 2. files: all files, but change "regular"->"400", "italic"->"400italic" + const files: Record = {}; - for (const [variant, url] of Object.entries(item.files)) { - let key = variant; - if (variant === "regular") key = "400"; - else if (variant === "italic") key = "400italic"; - files[key] = url; - } + for (const [variant, url] of Object.entries(item.files)) { + let key = variant; + if (variant === "regular") key = "400"; + else if (variant === "italic") key = "400italic"; + files[key] = url; + } - return { - type: "web", - category: item.category, - family: item.family, - weights, - preview: item.menu, - files, - } satisfies WebFont; - }); + return { + type: "web", + category: item.category, + family: item.family, + weights, + preview: item.menu, + files, + } satisfies WebFont; + }); - // Manually append Computer Modern web fonts - const computerModernFonts = getComputerModernWebFonts(); - const allWebFonts: WebFont[] = [...computerModernFonts, ...googleFontResults]; + // Manually append Computer Modern web fonts + const computerModernFonts = getComputerModernWebFonts(); + const allWebFonts: WebFont[] = [...computerModernFonts, ...googleFontResults]; - console.log( - `Added ${computerModernFonts.length} Computer Modern Web Fonts. Total output: ${allWebFonts.length} web fonts.`, - ); + console.log( + `Added ${computerModernFonts.length} Computer Modern Web Fonts. Total output: ${allWebFonts.length} web fonts.`, + ); - const jsonString = argCompress ? JSON.stringify(allWebFonts) : JSON.stringify(allWebFonts, null, 2); - await mkdir(FONTS_DIR, { recursive: true }); - await writeFile(WEBFONTLIST_FILE, jsonString, "utf-8"); + const jsonString = argCompress ? JSON.stringify(allWebFonts) : JSON.stringify(allWebFonts, null, 2); + await mkdir(FONTS_DIR, { recursive: true }); + await writeFile(WEBFONTLIST_FILE, jsonString, "utf-8"); - console.log(`Generated ${allWebFonts.length} fonts in the list (including Computer Modern web fonts).`); + console.log(`Generated ${allWebFonts.length} fonts in the list (including Computer Modern web fonts).`); } if (import.meta.main) { - await generateFonts(); + await generateFonts(); } diff --git a/scripts/fonts/types.ts b/scripts/fonts/types.ts index c8024bd89..9d6c79a57 100644 --- a/scripts/fonts/types.ts +++ b/scripts/fonts/types.ts @@ -1,51 +1,51 @@ type Category = "display" | "handwriting" | "monospace" | "serif" | "sans-serif"; export type Variant = - | "100" - | "100italic" - | "200" - | "200italic" - | "300" - | "300italic" - | "regular" - | "italic" - | "500" - | "500italic" - | "600" - | "600italic" - | "700" - | "700italic" - | "800" - | "800italic" - | "900" - | "900italic"; + | "100" + | "100italic" + | "200" + | "200italic" + | "300" + | "300italic" + | "regular" + | "italic" + | "500" + | "500italic" + | "600" + | "600italic" + | "700" + | "700italic" + | "800" + | "800italic" + | "900" + | "900italic"; type Item = { - kind: "webfonts#webfont"; - menu: string; - family: string; - version: string; - category: Category; - lastModified: string; - subsets: string[]; - variants: Variant[]; - colorCapabilities?: string[]; - files: Partial>; + kind: "webfonts#webfont"; + menu: string; + family: string; + version: string; + category: Category; + lastModified: string; + subsets: string[]; + variants: Variant[]; + colorCapabilities?: string[]; + files: Partial>; }; export type APIResponse = { - kind: "webfonts#webfontList"; - items: Item[]; + kind: "webfonts#webfontList"; + items: Item[]; }; export type Weight = "100" | "200" | "300" | "400" | "500" | "600" | "700" | "800" | "900"; type FileWeight = Weight | `${Weight}italic`; export type WebFont = { - type: "web"; - category: Category; - family: string; - weights: Weight[]; - preview: string; - files: Partial>; + type: "web"; + category: Category; + family: string; + weights: Weight[]; + preview: string; + files: Partial>; }; diff --git a/scripts/migration/resume.ts b/scripts/migration/resume.ts index 8dae96f65..b2fb34bc7 100644 --- a/scripts/migration/resume.ts +++ b/scripts/migration/resume.ts @@ -12,24 +12,24 @@ import { generateId } from "@/utils/string"; type Visibility = "public" | "private"; interface ProductionResume { - id: string; - title: string; - slug: string; - data: unknown; // JSON data - visibility: Visibility; - locked: boolean; - userId: string; - createdAt: Date; - updatedAt: Date; + id: string; + title: string; + slug: string; + data: unknown; // JSON data + visibility: Visibility; + locked: boolean; + userId: string; + createdAt: Date; + updatedAt: Date; } interface ProductionStatistics { - id: string; - views: number; - downloads: number; - resumeId: string; - createdAt: Date; - updatedAt: Date; + id: string; + views: number; + downloads: number; + resumeId: string; + createdAt: Date; + updatedAt: Date; } const productionUrl = process.env.PRODUCTION_DATABASE_URL; @@ -62,444 +62,444 @@ const INSERT_CHUNK_SIZE = 1000; // == Progress checkpoint interface == // Uses cursor-based pagination with (createdAt, id) composite key for efficiency interface MigrationProgress { - // Cursor for pagination - last seen createdAt timestamp - lastSeenCreatedAt: string | null; - // Cursor for pagination - last seen id (for tiebreaker when timestamps are equal) - lastSeenId: string | null; - resumesCreated: number; - statisticsCreated: number; - skipped: number; - totalResumesProcessed: number; - errors: number; - lastUpdated: string; + // Cursor for pagination - last seen createdAt timestamp + lastSeenCreatedAt: string | null; + // Cursor for pagination - last seen id (for tiebreaker when timestamps are equal) + lastSeenId: string | null; + resumesCreated: number; + statisticsCreated: number; + skipped: number; + totalResumesProcessed: number; + errors: number; + lastUpdated: string; } // Flag to track if shutdown was requested let shutdownRequested = false; async function loadProgress(): Promise { - try { - const text = await fs.readFile(PROGRESS_FILE, { encoding: "utf-8" }); - const progress = JSON.parse(text) as MigrationProgress; - console.log(`📂 Found existing progress file. Last updated: ${progress.lastUpdated}`); - console.log(` Resuming from cursor (createdAt: ${progress.lastSeenCreatedAt}, id: ${progress.lastSeenId})...`); - return progress; - } catch (e) { - console.warn("⚠️ Failed to load progress file, starting from beginning.", e); - } - return null; + try { + const text = await fs.readFile(PROGRESS_FILE, { encoding: "utf-8" }); + const progress = JSON.parse(text) as MigrationProgress; + console.log(`📂 Found existing progress file. Last updated: ${progress.lastUpdated}`); + console.log(` Resuming from cursor (createdAt: ${progress.lastSeenCreatedAt}, id: ${progress.lastSeenId})...`); + return progress; + } catch (e) { + console.warn("⚠️ Failed to load progress file, starting from beginning.", e); + } + return null; } async function saveProgress(progress: MigrationProgress): Promise { - try { - progress.lastUpdated = new Date().toISOString(); - await fs.writeFile(PROGRESS_FILE, JSON.stringify(progress, null, 2), { encoding: "utf-8" }); - console.log(`💾 Progress saved at cursor (createdAt: ${progress.lastSeenCreatedAt}, id: ${progress.lastSeenId})`); - } catch (e) { - console.error("🚨 Failed to save progress:", e); - } + try { + progress.lastUpdated = new Date().toISOString(); + await fs.writeFile(PROGRESS_FILE, JSON.stringify(progress, null, 2), { encoding: "utf-8" }); + console.log(`💾 Progress saved at cursor (createdAt: ${progress.lastSeenCreatedAt}, id: ${progress.lastSeenId})`); + } catch (e) { + console.error("🚨 Failed to save progress:", e); + } } async function clearProgress(): Promise { - try { - await fs.unlink(PROGRESS_FILE); - console.log("🗑️ Progress file cleared."); - } catch { - // Ignore errors when clearing - } + try { + await fs.unlink(PROGRESS_FILE); + console.log("🗑️ Progress file cleared."); + } catch { + // Ignore errors when clearing + } } async function loadUserIdMapFromFile(): Promise> { - try { - const text = await fs.readFile(USER_ID_MAP_FILE, { encoding: "utf-8" }); - const obj = JSON.parse(text); - return new Map(Object.entries(obj)); - } catch (e) { - console.warn("⚠️ Failed to load userIdMap from disk, continuing with empty map.", e); - } - return new Map(); + try { + const text = await fs.readFile(USER_ID_MAP_FILE, { encoding: "utf-8" }); + const obj = JSON.parse(text); + return new Map(Object.entries(obj)); + } catch (e) { + console.warn("⚠️ Failed to load userIdMap from disk, continuing with empty map.", e); + } + return new Map(); } async function loadResumeIdMapFromFile(): Promise> { - try { - const text = await fs.readFile(RESUME_ID_MAP_FILE, { encoding: "utf-8" }); - const obj = JSON.parse(text); - return new Map(Object.entries(obj)); - } catch (e) { - console.warn("⚠️ Failed to load resumeIdMap from disk, continuing with empty map.", e); - } - return new Map(); + try { + const text = await fs.readFile(RESUME_ID_MAP_FILE, { encoding: "utf-8" }); + const obj = JSON.parse(text); + return new Map(Object.entries(obj)); + } catch (e) { + console.warn("⚠️ Failed to load resumeIdMap from disk, continuing with empty map.", e); + } + return new Map(); } async function saveResumeIdMapToFile(resumeIdMap: Map) { - const obj: Record = Object.fromEntries(resumeIdMap.entries()); - await fs.writeFile(RESUME_ID_MAP_FILE, JSON.stringify(obj, null, "\t"), { encoding: "utf-8" }); + const obj: Record = Object.fromEntries(resumeIdMap.entries()); + await fs.writeFile(RESUME_ID_MAP_FILE, JSON.stringify(obj, null, "\t"), { encoding: "utf-8" }); } export async function migrateResumes() { - const migrationStart = performance.now(); - console.log("⌛ Starting resume migration..."); + const migrationStart = performance.now(); + console.log("⌛ Starting resume migration..."); - let hasMore = true; + let hasMore = true; - // Cursor-based pagination state - let lastSeenCreatedAt: string | null = null; - let lastSeenId: string | null = null; + // Cursor-based pagination state + let lastSeenCreatedAt: string | null = null; + let lastSeenId: string | null = null; - // Load persistent ID maps from file - const userIdMap = await loadUserIdMapFromFile(); - const resumeIdMap = await loadResumeIdMapFromFile(); + // Load persistent ID maps from file + const userIdMap = await loadUserIdMapFromFile(); + const resumeIdMap = await loadResumeIdMapFromFile(); - // Track migration stats - let resumesCreated = 0; - let statisticsCreated = 0; - let skipped = 0; - let totalResumesProcessed = 0; - let errors = 0; + // Track migration stats + let resumesCreated = 0; + let statisticsCreated = 0; + let skipped = 0; + let totalResumesProcessed = 0; + let errors = 0; - // Load saved progress if exists - const savedProgress = await loadProgress(); - if (savedProgress) { - lastSeenCreatedAt = savedProgress.lastSeenCreatedAt; - lastSeenId = savedProgress.lastSeenId; - resumesCreated = savedProgress.resumesCreated; - statisticsCreated = savedProgress.statisticsCreated; - skipped = savedProgress.skipped; - totalResumesProcessed = savedProgress.totalResumesProcessed; - errors = savedProgress.errors; - } + // Load saved progress if exists + const savedProgress = await loadProgress(); + if (savedProgress) { + lastSeenCreatedAt = savedProgress.lastSeenCreatedAt; + lastSeenId = savedProgress.lastSeenId; + resumesCreated = savedProgress.resumesCreated; + statisticsCreated = savedProgress.statisticsCreated; + skipped = savedProgress.skipped; + totalResumesProcessed = savedProgress.totalResumesProcessed; + errors = savedProgress.errors; + } - // Helper to get current progress object - const getCurrentProgress = (): MigrationProgress => ({ - lastSeenCreatedAt, - lastSeenId, - resumesCreated, - statisticsCreated, - skipped, - totalResumesProcessed, - errors, - lastUpdated: new Date().toISOString(), - }); + // Helper to get current progress object + const getCurrentProgress = (): MigrationProgress => ({ + lastSeenCreatedAt, + lastSeenId, + resumesCreated, + statisticsCreated, + skipped, + totalResumesProcessed, + errors, + lastUpdated: new Date().toISOString(), + }); - // Setup graceful shutdown handler - const handleShutdown = async () => { - if (shutdownRequested) return; - shutdownRequested = true; - console.log("\n⚠️ Shutdown requested. Saving progress..."); - await saveProgress(getCurrentProgress()); - await saveResumeIdMapToFile(resumeIdMap); - console.log("👋 Exiting. Run the script again to resume from where you left off."); - process.exit(0); - }; + // Setup graceful shutdown handler + const handleShutdown = async () => { + if (shutdownRequested) return; + shutdownRequested = true; + console.log("\n⚠️ Shutdown requested. Saving progress..."); + await saveProgress(getCurrentProgress()); + await saveResumeIdMapToFile(resumeIdMap); + console.log("👋 Exiting. Run the script again to resume from where you left off."); + process.exit(0); + }; - process.on("SIGINT", handleShutdown); - process.on("SIGTERM", handleShutdown); + process.on("SIGINT", handleShutdown); + process.on("SIGTERM", handleShutdown); - // Initialize the importer - const importer = new ReactiveResumeV4JSONImporter(); + // Initialize the importer + const importer = new ReactiveResumeV4JSONImporter(); - while (hasMore) { - // Check if shutdown was requested - if (shutdownRequested) break; + while (hasMore) { + // Check if shutdown was requested + if (shutdownRequested) break; - console.log( - `📥 Fetching resumes batch from production database (cursor: createdAt=${lastSeenCreatedAt}, id=${lastSeenId})...`, - ); + console.log( + `📥 Fetching resumes batch from production database (cursor: createdAt=${lastSeenCreatedAt}, id=${lastSeenId})...`, + ); - // Use cursor-based pagination for better performance - // Tuple comparison syntax allows Postgres to use composite index efficiently - let resumes: ProductionResume[]; + // Use cursor-based pagination for better performance + // Tuple comparison syntax allows Postgres to use composite index efficiently + let resumes: ProductionResume[]; - if (lastSeenCreatedAt && lastSeenId) { - const result = (await productionDb.execute(sql` + if (lastSeenCreatedAt && lastSeenId) { + const result = (await productionDb.execute(sql` SELECT id, title, slug, data, visibility, locked, "userId", "createdAt", "updatedAt" FROM "Resume" WHERE ("createdAt", id) < (${lastSeenCreatedAt}::timestamp, ${lastSeenId}) ORDER BY "createdAt" DESC, id DESC LIMIT ${BATCH_SIZE} `)) as unknown as QueryResult; - resumes = result.rows; - } else { - const result = (await productionDb.execute(sql` + resumes = result.rows; + } else { + const result = (await productionDb.execute(sql` SELECT id, title, slug, data, visibility, locked, "userId", "createdAt", "updatedAt" FROM "Resume" ORDER BY "createdAt" DESC, id DESC LIMIT ${BATCH_SIZE} `)) as unknown as QueryResult; - resumes = result.rows; - } + resumes = result.rows; + } - console.log(`📋 Found ${resumes.length} resumes in this batch.`); + console.log(`📋 Found ${resumes.length} resumes in this batch.`); - if (resumes.length === 0) { - hasMore = false; - break; - } + if (resumes.length === 0) { + hasMore = false; + break; + } - // Fetch statistics only for these resumes in this batch - const resumeIds = resumes.map((r) => r.id); + // Fetch statistics only for these resumes in this batch + const resumeIds = resumes.map((r) => r.id); - // Drizzle does not interpolate arrays, so we join and use a custom SQL string - // Escape single quotes in IDs (though UUIDs shouldn't contain them, this is safer) - const resumeIdsForSql = resumeIds.map((id) => `'${id.replace(/'/g, "''")}'`).join(", "); + // Drizzle does not interpolate arrays, so we join and use a custom SQL string + // Escape single quotes in IDs (though UUIDs shouldn't contain them, this is safer) + const resumeIdsForSql = resumeIds.map((id) => `'${id.replace(/'/g, "''")}'`).join(", "); - const { rows: statistics } = (await productionDb.execute(sql` + const { rows: statistics } = (await productionDb.execute(sql` SELECT id, views, downloads, "resumeId", "createdAt", "updatedAt" FROM "Statistics" WHERE "resumeId" IN (${sql.raw(resumeIdsForSql)}) `)) as unknown as QueryResult; - // Create a map of resumeId -> statistics for quick lookup - const statisticsMap = new Map(); - for (const stat of statistics) { - statisticsMap.set(stat.resumeId, stat); - } + // Create a map of resumeId -> statistics for quick lookup + const statisticsMap = new Map(); + for (const stat of statistics) { + statisticsMap.set(stat.resumeId, stat); + } - // Filter out resumes where userId is not in userIdMap - const resumesToProcess = resumes - .map((resume) => { - const newUserId = userIdMap.get(resume.userId); - if (!newUserId) { - skipped++; - return null; - } - return { resume, newUserId }; - }) - .filter((item): item is NonNullable => item !== null); + // Filter out resumes where userId is not in userIdMap + const resumesToProcess = resumes + .map((resume) => { + const newUserId = userIdMap.get(resume.userId); + if (!newUserId) { + skipped++; + return null; + } + return { resume, newUserId }; + }) + .filter((item): item is NonNullable => item !== null); - if (resumesToProcess.length === 0) { - console.log(`⏭️ All resumes in this batch have userIds not found in userIdMap.`); - // Update cursor to the last resume in this batch - const lastResume = resumes[resumes.length - 1]; - if (lastResume) { - lastSeenCreatedAt = - lastResume.createdAt instanceof Date ? lastResume.createdAt.toISOString() : String(lastResume.createdAt); - lastSeenId = lastResume.id; - } - totalResumesProcessed += resumes.length; - await saveProgress(getCurrentProgress()); - continue; - } + if (resumesToProcess.length === 0) { + console.log(`⏭️ All resumes in this batch have userIds not found in userIdMap.`); + // Update cursor to the last resume in this batch + const lastResume = resumes[resumes.length - 1]; + if (lastResume) { + lastSeenCreatedAt = + lastResume.createdAt instanceof Date ? lastResume.createdAt.toISOString() : String(lastResume.createdAt); + lastSeenId = lastResume.id; + } + totalResumesProcessed += resumes.length; + await saveProgress(getCurrentProgress()); + continue; + } - // Get unique userIds and bulk check if they exist in local database - const uniqueUserIds = [...new Set(resumesToProcess.map((r) => r.newUserId))]; - const existingUsers = await localDb.select().from(schema.user).where(inArray(schema.user.id, uniqueUserIds)); + // Get unique userIds and bulk check if they exist in local database + const uniqueUserIds = [...new Set(resumesToProcess.map((r) => r.newUserId))]; + const existingUsers = await localDb.select().from(schema.user).where(inArray(schema.user.id, uniqueUserIds)); - const existingUserIds = new Set(existingUsers.map((u) => u.id)); + const existingUserIds = new Set(existingUsers.map((u) => u.id)); - // Filter out resumes where user doesn't exist - const resumesWithValidUsers = resumesToProcess.filter(({ newUserId }) => { - if (!existingUserIds.has(newUserId)) { - skipped++; - return false; - } - return true; - }); + // Filter out resumes where user doesn't exist + const resumesWithValidUsers = resumesToProcess.filter(({ newUserId }) => { + if (!existingUserIds.has(newUserId)) { + skipped++; + return false; + } + return true; + }); - if (resumesWithValidUsers.length === 0) { - console.log(`⏭️ All resumes in this batch have userIds not found in local database.`); - // Update cursor to the last resume in this batch - const lastResume = resumes[resumes.length - 1]; - if (lastResume) { - lastSeenCreatedAt = - lastResume.createdAt instanceof Date ? lastResume.createdAt.toISOString() : String(lastResume.createdAt); - lastSeenId = lastResume.id; - } - totalResumesProcessed += resumes.length; - await saveProgress(getCurrentProgress()); - continue; - } + if (resumesWithValidUsers.length === 0) { + console.log(`⏭️ All resumes in this batch have userIds not found in local database.`); + // Update cursor to the last resume in this batch + const lastResume = resumes[resumes.length - 1]; + if (lastResume) { + lastSeenCreatedAt = + lastResume.createdAt instanceof Date ? lastResume.createdAt.toISOString() : String(lastResume.createdAt); + lastSeenId = lastResume.id; + } + totalResumesProcessed += resumes.length; + await saveProgress(getCurrentProgress()); + continue; + } - // Bulk check for existing resumes (by slug + userId) - // We need to check each unique combination - const slugUserIdPairs = resumesWithValidUsers.map(({ resume, newUserId }) => ({ - slug: resume.slug, - userId: newUserId, - })); + // Bulk check for existing resumes (by slug + userId) + // We need to check each unique combination + const slugUserIdPairs = resumesWithValidUsers.map(({ resume, newUserId }) => ({ + slug: resume.slug, + userId: newUserId, + })); - // Get all unique slugs and userIds - const uniqueSlugs = [...new Set(slugUserIdPairs.map((p) => p.slug))]; - const userIdsForSlugCheck = [...new Set(slugUserIdPairs.map((p) => p.userId))]; + // Get all unique slugs and userIds + const uniqueSlugs = [...new Set(slugUserIdPairs.map((p) => p.slug))]; + const userIdsForSlugCheck = [...new Set(slugUserIdPairs.map((p) => p.userId))]; - // Fetch all existing resumes that match any of our slugs and userIds - const existingResumes = await localDb - .select() - .from(schema.resume) - .where(and(inArray(schema.resume.slug, uniqueSlugs), inArray(schema.resume.userId, userIdsForSlugCheck))); + // Fetch all existing resumes that match any of our slugs and userIds + const existingResumes = await localDb + .select() + .from(schema.resume) + .where(and(inArray(schema.resume.slug, uniqueSlugs), inArray(schema.resume.userId, userIdsForSlugCheck))); - // Create a set of existing slug+userId combinations - const existingResumeKeys = new Set(existingResumes.map((r) => `${r.slug}:${r.userId}`)); + // Create a set of existing slug+userId combinations + const existingResumeKeys = new Set(existingResumes.map((r) => `${r.slug}:${r.userId}`)); - // Filter out resumes that already exist - const resumesToInsert = resumesWithValidUsers.filter(({ resume, newUserId }) => { - const key = `${resume.slug}:${newUserId}`; - if (existingResumeKeys.has(key)) { - skipped++; - return false; - } - return true; - }); + // Filter out resumes that already exist + const resumesToInsert = resumesWithValidUsers.filter(({ resume, newUserId }) => { + const key = `${resume.slug}:${newUserId}`; + if (existingResumeKeys.has(key)) { + skipped++; + return false; + } + return true; + }); - if (resumesToInsert.length === 0) { - console.log(`⏭️ All resumes in this batch already exist in target DB.`); - // Update cursor to the last resume in this batch - const lastResume = resumes[resumes.length - 1]; - if (lastResume) { - lastSeenCreatedAt = - lastResume.createdAt instanceof Date ? lastResume.createdAt.toISOString() : String(lastResume.createdAt); - lastSeenId = lastResume.id; - } - totalResumesProcessed += resumes.length; - await saveProgress(getCurrentProgress()); - continue; - } + if (resumesToInsert.length === 0) { + console.log(`⏭️ All resumes in this batch already exist in target DB.`); + // Update cursor to the last resume in this batch + const lastResume = resumes[resumes.length - 1]; + if (lastResume) { + lastSeenCreatedAt = + lastResume.createdAt instanceof Date ? lastResume.createdAt.toISOString() : String(lastResume.createdAt); + lastSeenId = lastResume.id; + } + totalResumesProcessed += resumes.length; + await saveProgress(getCurrentProgress()); + continue; + } - console.log(`📝 Preparing to bulk insert ${resumesToInsert.length} resumes...`); + console.log(`📝 Preparing to bulk insert ${resumesToInsert.length} resumes...`); - // Prepare bulk insert data - const batchStart = performance.now(); - try { - const resumesToInsertData = resumesToInsert.map(({ resume, newUserId }) => { - // Transform the data using the V4 importer - let transformedData = defaultResumeData; - try { - const dataJson = typeof resume.data === "string" ? resume.data : JSON.stringify(resume.data); - transformedData = importer.parse(dataJson); - } catch (error) { - console.error(`⚠️ Failed to parse resume data for resume ${resume.id}, using default data:`, error); - // Use default data if parsing fails - transformedData = defaultResumeData; - } + // Prepare bulk insert data + const batchStart = performance.now(); + try { + const resumesToInsertData = resumesToInsert.map(({ resume, newUserId }) => { + // Transform the data using the V4 importer + let transformedData = defaultResumeData; + try { + const dataJson = typeof resume.data === "string" ? resume.data : JSON.stringify(resume.data); + transformedData = importer.parse(dataJson); + } catch (error) { + console.error(`⚠️ Failed to parse resume data for resume ${resume.id}, using default data:`, error); + // Use default data if parsing fails + transformedData = defaultResumeData; + } - // Map visibility to isPublic (visibility === "public" -> isPublic = true) - const isPublic = resume.visibility === "public"; + // Map visibility to isPublic (visibility === "public" -> isPublic = true) + const isPublic = resume.visibility === "public"; - const newResumeId = generateId(); + const newResumeId = generateId(); - // Track the ID mapping for future reference - resumeIdMap.set(resume.id, newResumeId); + // Track the ID mapping for future reference + resumeIdMap.set(resume.id, newResumeId); - return { - resumeData: { - id: newResumeId, - name: resume.title, - slug: resume.slug, - tags: [], // Default empty array - isPublic: isPublic, - isLocked: resume.locked, - password: null, // No password in old schema - data: transformedData, - userId: newUserId, - createdAt: resume.createdAt, - updatedAt: resume.updatedAt, - }, - originalResumeId: resume.id, - newResumeId: newResumeId, - }; - }); + return { + resumeData: { + id: newResumeId, + name: resume.title, + slug: resume.slug, + tags: [], // Default empty array + isPublic: isPublic, + isLocked: resume.locked, + password: null, // No password in old schema + data: transformedData, + userId: newUserId, + createdAt: resume.createdAt, + updatedAt: resume.updatedAt, + }, + originalResumeId: resume.id, + newResumeId: newResumeId, + }; + }); - // Bulk insert resumes (chunked to avoid PostgreSQL message size limits) - // Resumes contain large JSONB data, so we use smaller chunks - const resumeDataList = resumesToInsertData.map(({ resumeData }) => resumeData); - for (let i = 0; i < resumeDataList.length; i += INSERT_CHUNK_SIZE) { - const chunk = resumeDataList.slice(i, i + INSERT_CHUNK_SIZE); - await localDb.insert(schema.resume).values(chunk); - } - resumesCreated += resumesToInsertData.length; + // Bulk insert resumes (chunked to avoid PostgreSQL message size limits) + // Resumes contain large JSONB data, so we use smaller chunks + const resumeDataList = resumesToInsertData.map(({ resumeData }) => resumeData); + for (let i = 0; i < resumeDataList.length; i += INSERT_CHUNK_SIZE) { + const chunk = resumeDataList.slice(i, i + INSERT_CHUNK_SIZE); + await localDb.insert(schema.resume).values(chunk); + } + resumesCreated += resumesToInsertData.length; - // Prepare statistics for bulk insert - const statisticsToInsert = resumesToInsertData - .map(({ originalResumeId, newResumeId }) => { - const resumeStatistics = statisticsMap.get(originalResumeId); - if (!resumeStatistics) return null; + // Prepare statistics for bulk insert + const statisticsToInsert = resumesToInsertData + .map(({ originalResumeId, newResumeId }) => { + const resumeStatistics = statisticsMap.get(originalResumeId); + if (!resumeStatistics) return null; - return { - id: generateId(), - views: resumeStatistics.views, - downloads: resumeStatistics.downloads, - lastViewedAt: null, // Not available in old schema - lastDownloadedAt: null, // Not available in old schema - resumeId: newResumeId, - createdAt: resumeStatistics.createdAt, - updatedAt: resumeStatistics.updatedAt, - }; - }) - .filter((stat): stat is NonNullable => stat !== null); + return { + id: generateId(), + views: resumeStatistics.views, + downloads: resumeStatistics.downloads, + lastViewedAt: null, // Not available in old schema + lastDownloadedAt: null, // Not available in old schema + resumeId: newResumeId, + createdAt: resumeStatistics.createdAt, + updatedAt: resumeStatistics.updatedAt, + }; + }) + .filter((stat): stat is NonNullable => stat !== null); - // Bulk insert statistics (chunked) - if (statisticsToInsert.length > 0) { - for (let i = 0; i < statisticsToInsert.length; i += INSERT_CHUNK_SIZE) { - const chunk = statisticsToInsert.slice(i, i + INSERT_CHUNK_SIZE); - await localDb.insert(schema.resumeStatistics).values(chunk); - } - statisticsCreated += statisticsToInsert.length; - } + // Bulk insert statistics (chunked) + if (statisticsToInsert.length > 0) { + for (let i = 0; i < statisticsToInsert.length; i += INSERT_CHUNK_SIZE) { + const chunk = statisticsToInsert.slice(i, i + INSERT_CHUNK_SIZE); + await localDb.insert(schema.resumeStatistics).values(chunk); + } + statisticsCreated += statisticsToInsert.length; + } - const batchEnd = performance.now(); - const batchTimeMs = batchEnd - batchStart; - console.log( - `✅ Bulk inserted ${resumesToInsertData.length} resumes in ${batchTimeMs.toFixed(1)} ms (avg ${(batchTimeMs / resumesToInsertData.length).toFixed(1)} ms/resume)`, - ); + const batchEnd = performance.now(); + const batchTimeMs = batchEnd - batchStart; + console.log( + `✅ Bulk inserted ${resumesToInsertData.length} resumes in ${batchTimeMs.toFixed(1)} ms (avg ${(batchTimeMs / resumesToInsertData.length).toFixed(1)} ms/resume)`, + ); - // Save resume ID map after each successful batch - await saveResumeIdMapToFile(resumeIdMap); - } catch (error) { - console.error(`🚨 Failed to bulk insert resumes batch:`, error); - errors++; - // Continue with next batch even if this one fails - } + // Save resume ID map after each successful batch + await saveResumeIdMapToFile(resumeIdMap); + } catch (error) { + console.error(`🚨 Failed to bulk insert resumes batch:`, error); + errors++; + // Continue with next batch even if this one fails + } - // Update cursor to the last resume in this batch - const lastResume = resumes[resumes.length - 1]; - if (lastResume) { - lastSeenCreatedAt = - lastResume.createdAt instanceof Date ? lastResume.createdAt.toISOString() : String(lastResume.createdAt); - lastSeenId = lastResume.id; - } + // Update cursor to the last resume in this batch + const lastResume = resumes[resumes.length - 1]; + if (lastResume) { + lastSeenCreatedAt = + lastResume.createdAt instanceof Date ? lastResume.createdAt.toISOString() : String(lastResume.createdAt); + lastSeenId = lastResume.id; + } - totalResumesProcessed += resumes.length; - console.log(`📦 Processed ${totalResumesProcessed} resumes so far...\n`); + totalResumesProcessed += resumes.length; + console.log(`📦 Processed ${totalResumesProcessed} resumes so far...\n`); - // Save progress after each batch - await saveProgress(getCurrentProgress()); - } + // Save progress after each batch + await saveProgress(getCurrentProgress()); + } - // Remove signal handlers - process.off("SIGINT", handleShutdown); - process.off("SIGTERM", handleShutdown); + // Remove signal handlers + process.off("SIGINT", handleShutdown); + process.off("SIGTERM", handleShutdown); - const migrationEnd = performance.now(); - const migrationDurationMs = migrationEnd - migrationStart; + const migrationEnd = performance.now(); + const migrationDurationMs = migrationEnd - migrationStart; - console.log("\n📊 Migration Summary:"); - console.log(` Resumes created: ${resumesCreated}`); - console.log(` Statistics created: ${statisticsCreated}`); - console.log(` Skipped (userId not found or already exist): ${skipped}`); - console.log(` Errors: ${errors}`); - console.log( - `⏱️ Total migration time: ${migrationDurationMs.toFixed(1)} ms (${(migrationDurationMs / 1000).toFixed(2)} seconds)`, - ); + console.log("\n📊 Migration Summary:"); + console.log(` Resumes created: ${resumesCreated}`); + console.log(` Statistics created: ${statisticsCreated}`); + console.log(` Skipped (userId not found or already exist): ${skipped}`); + console.log(` Errors: ${errors}`); + console.log( + `⏱️ Total migration time: ${migrationDurationMs.toFixed(1)} ms (${(migrationDurationMs / 1000).toFixed(2)} seconds)`, + ); - // Final save of the mapping (ensures up-to-date state) - await saveResumeIdMapToFile(resumeIdMap); + // Final save of the mapping (ensures up-to-date state) + await saveResumeIdMapToFile(resumeIdMap); - // Clear progress file on successful completion (only if not interrupted) - if (!shutdownRequested) { - await clearProgress(); - console.log("✅ Resume migration complete!"); - } else { - console.log("⏸️ Migration paused. Run again to resume."); - } + // Clear progress file on successful completion (only if not interrupted) + if (!shutdownRequested) { + await clearProgress(); + console.log("✅ Resume migration complete!"); + } else { + console.log("⏸️ Migration paused. Run again to resume."); + } } if (import.meta.main) { - // Reset shutdown flag for fresh run - shutdownRequested = false; + // Reset shutdown flag for fresh run + shutdownRequested = false; - try { - await migrateResumes(); - } finally { - await productionPool.end(); - await localPool.end(); - } + try { + await migrateResumes(); + } finally { + await productionPool.end(); + await localPool.end(); + } } diff --git a/scripts/migration/test-database.ts b/scripts/migration/test-database.ts index c8a6618bf..f80042075 100644 --- a/scripts/migration/test-database.ts +++ b/scripts/migration/test-database.ts @@ -13,16 +13,16 @@ const localPool = new Pool({ connectionString: localUrl }); const localDb = drizzle({ client: localPool }); try { - const productionResult = await productionDb.execute(sql`SELECT 1 as connected`); - console.log("✅ Production database connection successful", JSON.stringify(productionResult)); + const productionResult = await productionDb.execute(sql`SELECT 1 as connected`); + console.log("✅ Production database connection successful", JSON.stringify(productionResult)); - const localResult = await localDb.execute(sql`SELECT 1 as connected`); - console.log("✅ Local database connection successful", JSON.stringify(localResult)); + const localResult = await localDb.execute(sql`SELECT 1 as connected`); + console.log("✅ Local database connection successful", JSON.stringify(localResult)); } catch (error) { - console.error("🚨 Database connection failed:", error); - process.exit(1); + console.error("🚨 Database connection failed:", error); + process.exit(1); } finally { - await productionPool.end(); - await localPool.end(); - process.exit(0); + await productionPool.end(); + await localPool.end(); + process.exit(0); } diff --git a/scripts/migration/user.ts b/scripts/migration/user.ts index ffc527fd1..646fc961a 100644 --- a/scripts/migration/user.ts +++ b/scripts/migration/user.ts @@ -12,37 +12,37 @@ type Provider = "credential" | "google" | "github" | "custom"; type ProductionProvider = "email" | "github" | "google" | "openid"; interface ProductionUser { - id: string; - name: string; - picture: string | null; - username: string; - email: string; - locale: string; - emailVerified: boolean; - createdAt: Date; - updatedAt: Date; - provider: ProductionProvider; + id: string; + name: string; + picture: string | null; + username: string; + email: string; + locale: string; + emailVerified: boolean; + createdAt: Date; + updatedAt: Date; + provider: ProductionProvider; } interface ProductionSecrets { - id: string; - password: string | null; - lastSignedIn: Date; - userId: string; + id: string; + password: string | null; + lastSignedIn: Date; + userId: string; } // Map old provider to new providerId function mapProviderId(provider: ProductionProvider): Provider { - switch (provider) { - case "email": - return "credential"; - case "google": - return "google"; - case "github": - return "github"; - default: - return "custom"; - } + switch (provider) { + case "email": + return "credential"; + case "google": + return "google"; + case "github": + return "github"; + default: + return "custom"; + } } const productionUrl = process.env.PRODUCTION_DATABASE_URL; @@ -72,380 +72,380 @@ const INSERT_CHUNK_SIZE = 1000; // == Progress checkpoint interface == // Uses cursor-based pagination with (createdAt, id) composite key for efficiency interface MigrationProgress { - // Cursor for pagination - last seen createdAt timestamp - lastSeenCreatedAt: string | null; - // Cursor for pagination - last seen id (for tiebreaker when timestamps are equal) - lastSeenId: string | null; - usersCreated: number; - accountsCreated: number; - skipped: number; - totalUsersProcessed: number; - lastUpdated: string; + // Cursor for pagination - last seen createdAt timestamp + lastSeenCreatedAt: string | null; + // Cursor for pagination - last seen id (for tiebreaker when timestamps are equal) + lastSeenId: string | null; + usersCreated: number; + accountsCreated: number; + skipped: number; + totalUsersProcessed: number; + lastUpdated: string; } // Flag to track if shutdown was requested let shutdownRequested = false; async function loadProgress(): Promise { - try { - const text = await fs.readFile(PROGRESS_FILE, { encoding: "utf-8" }); - const progress = JSON.parse(text) as MigrationProgress; - console.log(`📂 Found existing progress file. Last updated: ${progress.lastUpdated}`); - console.log(` Resuming from cursor (createdAt: ${progress.lastSeenCreatedAt}, id: ${progress.lastSeenId})...`); - return progress; - } catch (e) { - console.warn("⚠️ Failed to load progress file, starting from beginning.", e); - } - return null; + try { + const text = await fs.readFile(PROGRESS_FILE, { encoding: "utf-8" }); + const progress = JSON.parse(text) as MigrationProgress; + console.log(`📂 Found existing progress file. Last updated: ${progress.lastUpdated}`); + console.log(` Resuming from cursor (createdAt: ${progress.lastSeenCreatedAt}, id: ${progress.lastSeenId})...`); + return progress; + } catch (e) { + console.warn("⚠️ Failed to load progress file, starting from beginning.", e); + } + return null; } async function saveProgress(progress: MigrationProgress): Promise { - try { - progress.lastUpdated = new Date().toISOString(); - await fs.writeFile(PROGRESS_FILE, JSON.stringify(progress, null, 2), { encoding: "utf-8" }); - console.log(`💾 Progress saved at cursor (createdAt: ${progress.lastSeenCreatedAt}, id: ${progress.lastSeenId})`); - } catch (e) { - console.error("🚨 Failed to save progress:", e); - } + try { + progress.lastUpdated = new Date().toISOString(); + await fs.writeFile(PROGRESS_FILE, JSON.stringify(progress, null, 2), { encoding: "utf-8" }); + console.log(`💾 Progress saved at cursor (createdAt: ${progress.lastSeenCreatedAt}, id: ${progress.lastSeenId})`); + } catch (e) { + console.error("🚨 Failed to save progress:", e); + } } async function clearProgress(): Promise { - try { - await fs.unlink(PROGRESS_FILE); - console.log("🗑️ Progress file cleared."); - } catch { - // Ignore errors when clearing - } + try { + await fs.unlink(PROGRESS_FILE); + console.log("🗑️ Progress file cleared."); + } catch { + // Ignore errors when clearing + } } async function loadUserIdMapFromFile(): Promise> { - try { - const text = await fs.readFile(USER_ID_MAP_FILE, { encoding: "utf-8" }); - const obj = JSON.parse(text); - return new Map(Object.entries(obj)); - } catch (e) { - console.warn("⚠️ Failed to load userIdMap from disk, continuing with empty map.", e); - } - return new Map(); + try { + const text = await fs.readFile(USER_ID_MAP_FILE, { encoding: "utf-8" }); + const obj = JSON.parse(text); + return new Map(Object.entries(obj)); + } catch (e) { + console.warn("⚠️ Failed to load userIdMap from disk, continuing with empty map.", e); + } + return new Map(); } async function saveUserIdMapToFile(userIdMap: Map) { - const obj: Record = Object.fromEntries(userIdMap.entries()); - await fs.writeFile(USER_ID_MAP_FILE, JSON.stringify(obj, null, "\t"), { encoding: "utf-8" }); + const obj: Record = Object.fromEntries(userIdMap.entries()); + await fs.writeFile(USER_ID_MAP_FILE, JSON.stringify(obj, null, "\t"), { encoding: "utf-8" }); } export async function migrateUsers() { - const migrationStart = performance.now(); - console.log("⌛ Starting user migration..."); + const migrationStart = performance.now(); + console.log("⌛ Starting user migration..."); - let hasMore = true; + let hasMore = true; - // Cursor-based pagination state - let lastSeenCreatedAt: string | null = null; - let lastSeenId: string | null = null; + // Cursor-based pagination state + let lastSeenCreatedAt: string | null = null; + let lastSeenId: string | null = null; - // Load persistent userIdMap from file - const userIdMap = await loadUserIdMapFromFile(); + // Load persistent userIdMap from file + const userIdMap = await loadUserIdMapFromFile(); - // Track migration stats - let usersCreated = 0; - let accountsCreated = 0; - let skipped = 0; - let totalUsersProcessed = 0; + // Track migration stats + let usersCreated = 0; + let accountsCreated = 0; + let skipped = 0; + let totalUsersProcessed = 0; - // Load saved progress if exists - const savedProgress = await loadProgress(); - if (savedProgress) { - lastSeenCreatedAt = savedProgress.lastSeenCreatedAt; - lastSeenId = savedProgress.lastSeenId; - usersCreated = savedProgress.usersCreated; - accountsCreated = savedProgress.accountsCreated; - skipped = savedProgress.skipped; - totalUsersProcessed = savedProgress.totalUsersProcessed; - } + // Load saved progress if exists + const savedProgress = await loadProgress(); + if (savedProgress) { + lastSeenCreatedAt = savedProgress.lastSeenCreatedAt; + lastSeenId = savedProgress.lastSeenId; + usersCreated = savedProgress.usersCreated; + accountsCreated = savedProgress.accountsCreated; + skipped = savedProgress.skipped; + totalUsersProcessed = savedProgress.totalUsersProcessed; + } - // Helper to get current progress object - const getCurrentProgress = (): MigrationProgress => ({ - lastSeenCreatedAt, - lastSeenId, - usersCreated, - accountsCreated, - skipped, - totalUsersProcessed, - lastUpdated: new Date().toISOString(), - }); + // Helper to get current progress object + const getCurrentProgress = (): MigrationProgress => ({ + lastSeenCreatedAt, + lastSeenId, + usersCreated, + accountsCreated, + skipped, + totalUsersProcessed, + lastUpdated: new Date().toISOString(), + }); - // Setup graceful shutdown handler - const handleShutdown = async () => { - if (shutdownRequested) return; - shutdownRequested = true; - console.log("\n⚠️ Shutdown requested. Saving progress..."); - await saveProgress(getCurrentProgress()); - await saveUserIdMapToFile(userIdMap); - console.log("👋 Exiting. Run the script again to resume from where you left off."); - process.exit(0); - }; + // Setup graceful shutdown handler + const handleShutdown = async () => { + if (shutdownRequested) return; + shutdownRequested = true; + console.log("\n⚠️ Shutdown requested. Saving progress..."); + await saveProgress(getCurrentProgress()); + await saveUserIdMapToFile(userIdMap); + console.log("👋 Exiting. Run the script again to resume from where you left off."); + process.exit(0); + }; - process.on("SIGINT", handleShutdown); - process.on("SIGTERM", handleShutdown); + process.on("SIGINT", handleShutdown); + process.on("SIGTERM", handleShutdown); - while (hasMore) { - // Check if shutdown was requested - if (shutdownRequested) break; + while (hasMore) { + // Check if shutdown was requested + if (shutdownRequested) break; - console.log( - `📥 Fetching users batch from production database (cursor: createdAt=${lastSeenCreatedAt}, id=${lastSeenId})...`, - ); + console.log( + `📥 Fetching users batch from production database (cursor: createdAt=${lastSeenCreatedAt}, id=${lastSeenId})...`, + ); - let users: ProductionUser[]; + let users: ProductionUser[]; - if (lastSeenCreatedAt && lastSeenId) { - const result = (await productionDb.execute(sql` + if (lastSeenCreatedAt && lastSeenId) { + const result = (await productionDb.execute(sql` SELECT id, name, picture, username, email, locale, "emailVerified", "createdAt", "updatedAt", provider FROM "User" WHERE ("createdAt", id) < (${lastSeenCreatedAt}::timestamp, ${lastSeenId}) ORDER BY "createdAt" DESC, id DESC LIMIT ${BATCH_SIZE} `)) as unknown as QueryResult; - users = result.rows; - } else { - const result = (await productionDb.execute(sql` + users = result.rows; + } else { + const result = (await productionDb.execute(sql` SELECT id, name, picture, username, email, locale, "emailVerified", "createdAt", "updatedAt", provider FROM "User" ORDER BY "createdAt" DESC, id DESC LIMIT ${BATCH_SIZE} `)) as unknown as QueryResult; - users = result.rows; - } + users = result.rows; + } - console.log(`📋 Found ${users.length} users in this batch.`); + console.log(`📋 Found ${users.length} users in this batch.`); - if (users.length === 0) { - hasMore = false; - break; - } + if (users.length === 0) { + hasMore = false; + break; + } - // Fetch secrets only for these users in this batch - const userIds = users.map((u) => u.id); + // Fetch secrets only for these users in this batch + const userIds = users.map((u) => u.id); - // Drizzle does not interpolate arrays, so we join and use a custom SQL string - // Escape single quotes in IDs (though UUIDs shouldn't contain them, this is safer) - const userIdsForSql = userIds.map((id) => `'${id.replace(/'/g, "''")}'`).join(", "); + // Drizzle does not interpolate arrays, so we join and use a custom SQL string + // Escape single quotes in IDs (though UUIDs shouldn't contain them, this is safer) + const userIdsForSql = userIds.map((id) => `'${id.replace(/'/g, "''")}'`).join(", "); - const { rows: secrets } = (await productionDb.execute(sql` + const { rows: secrets } = (await productionDb.execute(sql` SELECT id, password, "lastSignedIn", "userId" FROM "Secrets" WHERE "userId" IN (${sql.raw(userIdsForSql)}) `)) as unknown as QueryResult; - // Create a map of userId -> secrets for quick lookup - const secretsMap = new Map(); - for (const secret of secrets) { - secretsMap.set(secret.userId, secret); - } + // Create a map of userId -> secrets for quick lookup + const secretsMap = new Map(); + for (const secret of secrets) { + secretsMap.set(secret.userId, secret); + } - // Filter out users already in userIdMap (previously migrated) - const usersToProcess = users.filter((user) => { - if (userIdMap.has(user.id)) { - skipped++; - return false; - } - return true; - }); + // Filter out users already in userIdMap (previously migrated) + const usersToProcess = users.filter((user) => { + if (userIdMap.has(user.id)) { + skipped++; + return false; + } + return true; + }); - if (usersToProcess.length === 0) { - console.log(`⏭️ All users in this batch were already migrated.`); - // Update cursor to the last user in this batch - const lastUser = users[users.length - 1]; - if (lastUser) { - lastSeenCreatedAt = - lastUser.createdAt instanceof Date ? lastUser.createdAt.toISOString() : String(lastUser.createdAt); - lastSeenId = lastUser.id; - } - totalUsersProcessed += users.length; - await saveProgress(getCurrentProgress()); - continue; - } + if (usersToProcess.length === 0) { + console.log(`⏭️ All users in this batch were already migrated.`); + // Update cursor to the last user in this batch + const lastUser = users[users.length - 1]; + if (lastUser) { + lastSeenCreatedAt = + lastUser.createdAt instanceof Date ? lastUser.createdAt.toISOString() : String(lastUser.createdAt); + lastSeenId = lastUser.id; + } + totalUsersProcessed += users.length; + await saveProgress(getCurrentProgress()); + continue; + } - // Prepare usernames for all users - const userData = usersToProcess.map((user) => ({ - user, - username: toUsername(user.username), - displayUsername: user.username, - })); + // Prepare usernames for all users + const userData = usersToProcess.map((user) => ({ + user, + username: toUsername(user.username), + displayUsername: user.username, + })); - // Bulk check for existing users (by email or username) - const emails = userData.map((u) => u.user.email); - const usernames = userData.map((u) => u.username); - const displayUsernames = userData.map((u) => u.displayUsername); + // Bulk check for existing users (by email or username) + const emails = userData.map((u) => u.user.email); + const usernames = userData.map((u) => u.username); + const displayUsernames = userData.map((u) => u.displayUsername); - const existingUsers = await localDb - .select() - .from(schema.user) - .where( - or( - inArray(schema.user.email, emails), - inArray(schema.user.username, usernames), - inArray(schema.user.displayUsername, displayUsernames), - ), - ); + const existingUsers = await localDb + .select() + .from(schema.user) + .where( + or( + inArray(schema.user.email, emails), + inArray(schema.user.username, usernames), + inArray(schema.user.displayUsername, displayUsernames), + ), + ); - const existingEmails = new Set(existingUsers.map((u) => u.email)); - const existingUsernames = new Set(existingUsers.map((u) => u.username)); - const existingDisplayUsernames = new Set(existingUsers.map((u) => u.displayUsername)); + const existingEmails = new Set(existingUsers.map((u) => u.email)); + const existingUsernames = new Set(existingUsers.map((u) => u.username)); + const existingDisplayUsernames = new Set(existingUsers.map((u) => u.displayUsername)); - // Filter out users that already exist - const usersToInsert = userData.filter(({ user, username, displayUsername }) => { - if ( - existingEmails.has(user.email) || - existingUsernames.has(username) || - existingDisplayUsernames.has(displayUsername) - ) { - skipped++; - return false; - } - return true; - }); + // Filter out users that already exist + const usersToInsert = userData.filter(({ user, username, displayUsername }) => { + if ( + existingEmails.has(user.email) || + existingUsernames.has(username) || + existingDisplayUsernames.has(displayUsername) + ) { + skipped++; + return false; + } + return true; + }); - if (usersToInsert.length === 0) { - console.log(`⏭️ All users in this batch already exist in target DB.`); - // Update cursor to the last user in this batch - const lastUser = users[users.length - 1]; - if (lastUser) { - lastSeenCreatedAt = - lastUser.createdAt instanceof Date ? lastUser.createdAt.toISOString() : String(lastUser.createdAt); - lastSeenId = lastUser.id; - } - totalUsersProcessed += users.length; - await saveUserIdMapToFile(userIdMap); - await saveProgress(getCurrentProgress()); - continue; - } + if (usersToInsert.length === 0) { + console.log(`⏭️ All users in this batch already exist in target DB.`); + // Update cursor to the last user in this batch + const lastUser = users[users.length - 1]; + if (lastUser) { + lastSeenCreatedAt = + lastUser.createdAt instanceof Date ? lastUser.createdAt.toISOString() : String(lastUser.createdAt); + lastSeenId = lastUser.id; + } + totalUsersProcessed += users.length; + await saveUserIdMapToFile(userIdMap); + await saveProgress(getCurrentProgress()); + continue; + } - console.log(`📝 Preparing to bulk insert ${usersToInsert.length} users...`); + console.log(`📝 Preparing to bulk insert ${usersToInsert.length} users...`); - // Prepare bulk insert data - const usersToInsertData = usersToInsert.map(({ user, username, displayUsername }) => { - const newUserId = generateId(); - userIdMap.set(user.id, newUserId); - return { - userData: { - id: newUserId, - name: user.name, - email: user.email, - image: user.picture, - username: username, - displayUsername: displayUsername, - emailVerified: user.emailVerified, - twoFactorEnabled: false, // All users start with 2FA disabled in the new system - createdAt: user.createdAt, - updatedAt: user.updatedAt, - }, - originalUser: user, - newUserId: newUserId, - }; - }); + // Prepare bulk insert data + const usersToInsertData = usersToInsert.map(({ user, username, displayUsername }) => { + const newUserId = generateId(); + userIdMap.set(user.id, newUserId); + return { + userData: { + id: newUserId, + name: user.name, + email: user.email, + image: user.picture, + username: username, + displayUsername: displayUsername, + emailVerified: user.emailVerified, + twoFactorEnabled: false, // All users start with 2FA disabled in the new system + createdAt: user.createdAt, + updatedAt: user.updatedAt, + }, + originalUser: user, + newUserId: newUserId, + }; + }); - // Bulk insert users (chunked to avoid PostgreSQL message size limits) - const batchStart = performance.now(); - try { - const userDataList = usersToInsertData.map(({ userData }) => userData); - for (let i = 0; i < userDataList.length; i += INSERT_CHUNK_SIZE) { - const chunk = userDataList.slice(i, i + INSERT_CHUNK_SIZE); - await localDb.insert(schema.user).values(chunk); - } - usersCreated += usersToInsertData.length; + // Bulk insert users (chunked to avoid PostgreSQL message size limits) + const batchStart = performance.now(); + try { + const userDataList = usersToInsertData.map(({ userData }) => userData); + for (let i = 0; i < userDataList.length; i += INSERT_CHUNK_SIZE) { + const chunk = userDataList.slice(i, i + INSERT_CHUNK_SIZE); + await localDb.insert(schema.user).values(chunk); + } + usersCreated += usersToInsertData.length; - // Prepare accounts for bulk insert - const accountsToInsert = usersToInsertData.map(({ originalUser, newUserId, userData }) => { - const userSecrets = secretsMap.get(originalUser.id); - const providerId = mapProviderId(originalUser.provider); - const accountId = providerId === "credential" ? newUserId : originalUser.id; + // Prepare accounts for bulk insert + const accountsToInsert = usersToInsertData.map(({ originalUser, newUserId, userData }) => { + const userSecrets = secretsMap.get(originalUser.id); + const providerId = mapProviderId(originalUser.provider); + const accountId = providerId === "credential" ? newUserId : originalUser.id; - return { - id: generateId(), - userId: newUserId, - accountId: accountId, - providerId: providerId, - password: userSecrets?.password ?? null, - createdAt: userData.createdAt, - updatedAt: userData.updatedAt, - }; - }); + return { + id: generateId(), + userId: newUserId, + accountId: accountId, + providerId: providerId, + password: userSecrets?.password ?? null, + createdAt: userData.createdAt, + updatedAt: userData.updatedAt, + }; + }); - // Bulk insert accounts (chunked) - for (let i = 0; i < accountsToInsert.length; i += INSERT_CHUNK_SIZE) { - const chunk = accountsToInsert.slice(i, i + INSERT_CHUNK_SIZE); - await localDb.insert(schema.account).values(chunk); - } - accountsCreated += accountsToInsert.length; + // Bulk insert accounts (chunked) + for (let i = 0; i < accountsToInsert.length; i += INSERT_CHUNK_SIZE) { + const chunk = accountsToInsert.slice(i, i + INSERT_CHUNK_SIZE); + await localDb.insert(schema.account).values(chunk); + } + accountsCreated += accountsToInsert.length; - const batchEnd = performance.now(); - const batchTimeMs = batchEnd - batchStart; - console.log( - `✅ Bulk inserted ${usersToInsertData.length} users in ${batchTimeMs.toFixed(1)} ms (avg ${(batchTimeMs / usersToInsertData.length).toFixed(1)} ms/user)`, - ); + const batchEnd = performance.now(); + const batchTimeMs = batchEnd - batchStart; + console.log( + `✅ Bulk inserted ${usersToInsertData.length} users in ${batchTimeMs.toFixed(1)} ms (avg ${(batchTimeMs / usersToInsertData.length).toFixed(1)} ms/user)`, + ); - // Save progress after each batch - await saveUserIdMapToFile(userIdMap); - } catch (error) { - console.error(`🚨 Failed to bulk insert users batch:`, error); - // Continue with next batch even if this one fails - } + // Save progress after each batch + await saveUserIdMapToFile(userIdMap); + } catch (error) { + console.error(`🚨 Failed to bulk insert users batch:`, error); + // Continue with next batch even if this one fails + } - // Update cursor to the last user in this batch - const lastUser = users[users.length - 1]; - if (lastUser) { - lastSeenCreatedAt = - lastUser.createdAt instanceof Date ? lastUser.createdAt.toISOString() : String(lastUser.createdAt); - lastSeenId = lastUser.id; - } + // Update cursor to the last user in this batch + const lastUser = users[users.length - 1]; + if (lastUser) { + lastSeenCreatedAt = + lastUser.createdAt instanceof Date ? lastUser.createdAt.toISOString() : String(lastUser.createdAt); + lastSeenId = lastUser.id; + } - totalUsersProcessed += users.length; - console.log(`📦 Processed ${totalUsersProcessed} users so far...\n`); + totalUsersProcessed += users.length; + console.log(`📦 Processed ${totalUsersProcessed} users so far...\n`); - // Save progress after updating cursor - await saveProgress(getCurrentProgress()); - } + // Save progress after updating cursor + await saveProgress(getCurrentProgress()); + } - // Remove signal handlers - process.off("SIGINT", handleShutdown); - process.off("SIGTERM", handleShutdown); + // Remove signal handlers + process.off("SIGINT", handleShutdown); + process.off("SIGTERM", handleShutdown); - const migrationEnd = performance.now(); - const migrationDurationMs = migrationEnd - migrationStart; + const migrationEnd = performance.now(); + const migrationDurationMs = migrationEnd - migrationStart; - console.log("\n📊 Migration Summary:"); - console.log(` Users created: ${usersCreated}`); - console.log(` Accounts created: ${accountsCreated}`); - console.log(` Skipped (already exist): ${skipped}`); - console.log( - `⏱️ Total migration time: ${migrationDurationMs.toFixed(1)} ms (${(migrationDurationMs / 1000).toFixed(2)} seconds)`, - ); + console.log("\n📊 Migration Summary:"); + console.log(` Users created: ${usersCreated}`); + console.log(` Accounts created: ${accountsCreated}`); + console.log(` Skipped (already exist): ${skipped}`); + console.log( + `⏱️ Total migration time: ${migrationDurationMs.toFixed(1)} ms (${(migrationDurationMs / 1000).toFixed(2)} seconds)`, + ); - // Final save of the mapping (ensures up-to-date state) - await saveUserIdMapToFile(userIdMap); + // Final save of the mapping (ensures up-to-date state) + await saveUserIdMapToFile(userIdMap); - // Clear progress file on successful completion (only if not interrupted) - if (!shutdownRequested) { - await clearProgress(); - console.log("✅ User migration complete!"); - } else { - console.log("⏸️ Migration paused. Run again to resume."); - } + // Clear progress file on successful completion (only if not interrupted) + if (!shutdownRequested) { + await clearProgress(); + console.log("✅ User migration complete!"); + } else { + console.log("⏸️ Migration paused. Run again to resume."); + } - // Return the ID mapping for use in other migrations (e.g., resumes) - return userIdMap; + // Return the ID mapping for use in other migrations (e.g., resumes) + return userIdMap; } if (import.meta.main) { - // Reset shutdown flag for fresh run - shutdownRequested = false; + // Reset shutdown flag for fresh run + shutdownRequested = false; - try { - await migrateUsers(); - } finally { - await productionPool.end(); - await localPool.end(); - } + try { + await migrateUsers(); + } finally { + await productionPool.end(); + await localPool.end(); + } } diff --git a/skills/resume-builder/references/schema.md b/skills/resume-builder/references/schema.md index 3a04ae003..76108be11 100644 --- a/skills/resume-builder/references/schema.md +++ b/skills/resume-builder/references/schema.md @@ -21,16 +21,16 @@ This reference provides key structural information for generating valid resume d ```json { - "hidden": false, - "url": "https://example.com/photo.jpg", - "size": 80, - "rotation": 0, - "aspectRatio": 1, - "borderRadius": 0, - "borderColor": "rgba(0, 0, 0, 0.5)", - "borderWidth": 0, - "shadowColor": "rgba(0, 0, 0, 0.5)", - "shadowWidth": 0 + "hidden": false, + "url": "https://example.com/photo.jpg", + "size": 80, + "rotation": 0, + "aspectRatio": 1, + "borderRadius": 0, + "borderColor": "rgba(0, 0, 0, 0.5)", + "borderWidth": 0, + "shadowColor": "rgba(0, 0, 0, 0.5)", + "shadowWidth": 0 } ``` @@ -38,13 +38,13 @@ This reference provides key structural information for generating valid resume d ```json { - "name": "John Doe", - "headline": "Senior Software Engineer", - "email": "john@example.com", - "phone": "+1 (555) 123-4567", - "location": "San Francisco, CA", - "website": { "url": "https://johndoe.com", "label": "Portfolio" }, - "customFields": [] + "name": "John Doe", + "headline": "Senior Software Engineer", + "email": "john@example.com", + "phone": "+1 (555) 123-4567", + "location": "San Francisco, CA", + "website": { "url": "https://johndoe.com", "label": "Portfolio" }, + "customFields": [] } ``` @@ -52,10 +52,10 @@ This reference provides key structural information for generating valid resume d ```json { - "title": "Summary", - "columns": 1, - "hidden": false, - "content": "

HTML-formatted summary content here.

" + "title": "Summary", + "columns": 1, + "hidden": false, + "content": "

HTML-formatted summary content here.

" } ``` @@ -76,14 +76,14 @@ All sections share a common structure: ```json { - "id": "uuid-here", - "hidden": false, - "company": "Acme Corp", - "position": "Software Engineer", - "location": "San Francisco, CA", - "period": "Jan 2020 - Present", - "website": { "url": "https://acme.com", "label": "" }, - "description": "
  • Built scalable microservices
  • Led team of 5 engineers
" + "id": "uuid-here", + "hidden": false, + "company": "Acme Corp", + "position": "Software Engineer", + "location": "San Francisco, CA", + "period": "Jan 2020 - Present", + "website": { "url": "https://acme.com", "label": "" }, + "description": "
  • Built scalable microservices
  • Led team of 5 engineers
" } ``` @@ -91,16 +91,16 @@ All sections share a common structure: ```json { - "id": "uuid-here", - "hidden": false, - "school": "Stanford University", - "degree": "Bachelor of Science", - "area": "Computer Science", - "grade": "3.8 GPA", - "location": "Stanford, CA", - "period": "2012 - 2016", - "website": { "url": "", "label": "" }, - "description": "" + "id": "uuid-here", + "hidden": false, + "school": "Stanford University", + "degree": "Bachelor of Science", + "area": "Computer Science", + "grade": "3.8 GPA", + "location": "Stanford, CA", + "period": "2012 - 2016", + "website": { "url": "", "label": "" }, + "description": "" } ``` @@ -108,13 +108,13 @@ All sections share a common structure: ```json { - "id": "uuid-here", - "hidden": false, - "icon": "", - "name": "JavaScript", - "proficiency": "Expert", - "level": 5, - "keywords": ["React", "Node.js", "TypeScript"] + "id": "uuid-here", + "hidden": false, + "icon": "", + "name": "JavaScript", + "proficiency": "Expert", + "level": 5, + "keywords": ["React", "Node.js", "TypeScript"] } ``` @@ -124,12 +124,12 @@ Level: 0-5 (0 hides the visual indicator) ```json { - "id": "uuid-here", - "hidden": false, - "name": "Open Source Project", - "period": "2023 - Present", - "website": { "url": "https://github.com/user/project", "label": "GitHub" }, - "description": "

Description of the project and your contributions.

" + "id": "uuid-here", + "hidden": false, + "name": "Open Source Project", + "period": "2023 - Present", + "website": { "url": "https://github.com/user/project", "label": "GitHub" }, + "description": "

Description of the project and your contributions.

" } ``` @@ -137,11 +137,11 @@ Level: 0-5 (0 hides the visual indicator) ```json { - "id": "uuid-here", - "hidden": false, - "language": "English", - "fluency": "Native", - "level": 5 + "id": "uuid-here", + "hidden": false, + "language": "English", + "fluency": "Native", + "level": 5 } ``` @@ -151,13 +151,13 @@ Fluency examples: Native, Fluent, Conversational, Basic, or CEFR levels (A1-C2) ```json { - "id": "uuid-here", - "hidden": false, - "title": "AWS Solutions Architect", - "issuer": "Amazon Web Services", - "date": "March 2023", - "website": { "url": "https://aws.amazon.com/certification/", "label": "Verify" }, - "description": "" + "id": "uuid-here", + "hidden": false, + "title": "AWS Solutions Architect", + "issuer": "Amazon Web Services", + "date": "March 2023", + "website": { "url": "https://aws.amazon.com/certification/", "label": "Verify" }, + "description": "" } ``` @@ -165,13 +165,13 @@ Fluency examples: Native, Fluent, Conversational, Basic, or CEFR levels (A1-C2) ```json { - "id": "uuid-here", - "hidden": false, - "title": "Employee of the Year", - "awarder": "Acme Corp", - "date": "2022", - "website": { "url": "", "label": "" }, - "description": "" + "id": "uuid-here", + "hidden": false, + "title": "Employee of the Year", + "awarder": "Acme Corp", + "date": "2022", + "website": { "url": "", "label": "" }, + "description": "" } ``` @@ -179,13 +179,13 @@ Fluency examples: Native, Fluent, Conversational, Basic, or CEFR levels (A1-C2) ```json { - "id": "uuid-here", - "hidden": false, - "title": "Research Paper Title", - "publisher": "IEEE", - "date": "2021", - "website": { "url": "https://doi.org/...", "label": "DOI" }, - "description": "" + "id": "uuid-here", + "hidden": false, + "title": "Research Paper Title", + "publisher": "IEEE", + "date": "2021", + "website": { "url": "https://doi.org/...", "label": "DOI" }, + "description": "" } ``` @@ -193,13 +193,13 @@ Fluency examples: Native, Fluent, Conversational, Basic, or CEFR levels (A1-C2) ```json { - "id": "uuid-here", - "hidden": false, - "organization": "Local Food Bank", - "location": "San Francisco, CA", - "period": "2020 - Present", - "website": { "url": "", "label": "" }, - "description": "

Volunteer activities and impact.

" + "id": "uuid-here", + "hidden": false, + "organization": "Local Food Bank", + "location": "San Francisco, CA", + "period": "2020 - Present", + "website": { "url": "", "label": "" }, + "description": "

Volunteer activities and impact.

" } ``` @@ -207,11 +207,11 @@ Fluency examples: Native, Fluent, Conversational, Basic, or CEFR levels (A1-C2) ```json { - "id": "uuid-here", - "hidden": false, - "icon": "", - "name": "Photography", - "keywords": ["Landscape", "Portrait", "Street"] + "id": "uuid-here", + "hidden": false, + "icon": "", + "name": "Photography", + "keywords": ["Landscape", "Portrait", "Street"] } ``` @@ -219,13 +219,13 @@ Fluency examples: Native, Fluent, Conversational, Basic, or CEFR levels (A1-C2) ```json { - "id": "uuid-here", - "hidden": false, - "name": "Jane Smith", - "position": "Engineering Manager at Acme Corp", - "website": { "url": "https://linkedin.com/in/janesmith", "label": "LinkedIn" }, - "phone": "+1 (555) 987-6543", - "description": "

Optional testimonial or quote.

" + "id": "uuid-here", + "hidden": false, + "name": "Jane Smith", + "position": "Engineering Manager at Acme Corp", + "website": { "url": "https://linkedin.com/in/janesmith", "label": "LinkedIn" }, + "phone": "+1 (555) 987-6543", + "description": "

Optional testimonial or quote.

" } ``` @@ -233,12 +233,12 @@ Fluency examples: Native, Fluent, Conversational, Basic, or CEFR levels (A1-C2) ```json { - "id": "uuid-here", - "hidden": false, - "icon": "linkedin-logo", - "network": "LinkedIn", - "username": "johndoe", - "website": { "url": "https://linkedin.com/in/johndoe", "label": "" } + "id": "uuid-here", + "hidden": false, + "icon": "linkedin-logo", + "network": "LinkedIn", + "username": "johndoe", + "website": { "url": "https://linkedin.com/in/johndoe", "label": "" } } ``` @@ -254,14 +254,14 @@ Available templates: `azurill`, `bronzor`, `chikorita`, `ditto`, `ditgar`, `geng ```json { - "sidebarWidth": 35, - "pages": [ - { - "fullWidth": false, - "main": ["profiles", "summary", "experience", "education", "projects"], - "sidebar": ["skills", "languages", "certifications", "interests"] - } - ] + "sidebarWidth": 35, + "pages": [ + { + "fullWidth": false, + "main": ["profiles", "summary", "experience", "education", "projects"], + "sidebar": ["skills", "languages", "certifications", "interests"] + } + ] } ``` @@ -271,13 +271,13 @@ Section IDs for layout: `profiles`, `experience`, `education`, `projects`, `skil ```json { - "gapX": 4, - "gapY": 6, - "marginX": 14, - "marginY": 12, - "format": "a4", - "locale": "en-US", - "hideIcons": false + "gapX": 4, + "gapY": 6, + "marginX": 14, + "marginY": 12, + "format": "a4", + "locale": "en-US", + "hideIcons": false } ``` @@ -287,15 +287,15 @@ Format options: `a4`, `letter` ```json { - "colors": { - "primary": "rgba(220, 38, 38, 1)", - "text": "rgba(0, 0, 0, 1)", - "background": "rgba(255, 255, 255, 1)" - }, - "level": { - "icon": "star", - "type": "circle" - } + "colors": { + "primary": "rgba(220, 38, 38, 1)", + "text": "rgba(0, 0, 0, 1)", + "background": "rgba(255, 255, 255, 1)" + }, + "level": { + "icon": "star", + "type": "circle" + } } ``` @@ -305,18 +305,18 @@ Level types: `hidden`, `circle`, `square`, `rectangle`, `rectangle-full`, `progr ```json { - "body": { - "fontFamily": "IBM Plex Serif", - "fontWeights": ["400", "500"], - "fontSize": 10, - "lineHeight": 1.5 - }, - "heading": { - "fontFamily": "IBM Plex Serif", - "fontWeights": ["600"], - "fontSize": 14, - "lineHeight": 1.5 - } + "body": { + "fontFamily": "IBM Plex Serif", + "fontWeights": ["400", "500"], + "fontSize": 10, + "lineHeight": 1.5 + }, + "heading": { + "fontFamily": "IBM Plex Serif", + "fontWeights": ["600"], + "fontSize": 14, + "lineHeight": 1.5 + } } ``` @@ -326,8 +326,8 @@ Font family must be available on Google Fonts. Font weights: 100-900. ```json { - "enabled": false, - "value": "" + "enabled": false, + "value": "" } ``` diff --git a/src/components/ai/chat.tsx b/src/components/ai/chat.tsx index 519b6d4ec..b324edf94 100644 --- a/src/components/ai/chat.tsx +++ b/src/components/ai/chat.tsx @@ -6,12 +6,12 @@ import { t } from "@lingui/core/macro"; import { Trans } from "@lingui/react/macro"; import { eventIteratorToUnproxiedDataStream } from "@orpc/client"; import { - CheckCircleIcon, - CircleNotchIcon, - PaperPlaneRightIcon, - SparkleIcon, - StopIcon, - TrashSimpleIcon, + CheckCircleIcon, + CircleNotchIcon, + PaperPlaneRightIcon, + SparkleIcon, + StopIcon, + TrashSimpleIcon, } from "@phosphor-icons/react"; import { useCallback, useEffect, useRef, useState } from "react"; import { toast } from "sonner"; @@ -31,32 +31,32 @@ import { useResumeStore } from "../resume/store/resume"; * Returns operations that haven't been applied yet (tracked by processedToolCallIds). */ function extractNewPatchOperations( - messages: UIMessage[], - processedIds: Set, + messages: UIMessage[], + processedIds: Set, ): { operations: Operation[]; newIds: string[] } { - const operations: Operation[] = []; - const newIds: string[] = []; + const operations: Operation[] = []; + const newIds: string[] = []; - for (const message of messages) { - if (message.role !== "assistant") continue; - for (const part of message.parts) { - if (part.type !== "tool-patch_resume") continue; + for (const message of messages) { + if (message.role !== "assistant") continue; + for (const part of message.parts) { + if (part.type !== "tool-patch_resume") continue; - const toolPart = part as any; - if (toolPart.state !== "output-available") continue; + const toolPart = part as any; + if (toolPart.state !== "output-available") continue; - const callId = toolPart.toolCallId as string; - if (processedIds.has(callId)) continue; + const callId = toolPart.toolCallId as string; + if (processedIds.has(callId)) continue; - const result = toolPart.output as { success: boolean; appliedOperations?: Operation[] } | undefined; - if (result?.success && result.appliedOperations) { - operations.push(...result.appliedOperations); - newIds.push(callId); - } - } - } + const result = toolPart.output as { success: boolean; appliedOperations?: Operation[] } | undefined; + if (result?.success && result.appliedOperations) { + operations.push(...result.appliedOperations); + newIds.push(callId); + } + } + } - return { operations, newIds }; + return { operations, newIds }; } /** @@ -65,346 +65,346 @@ function extractNewPatchOperations( const STORAGE_KEY_PREFIX = "ai-chat-messages"; function getStorageKey(resumeId: string): string { - return `${STORAGE_KEY_PREFIX}:${resumeId}`; + return `${STORAGE_KEY_PREFIX}:${resumeId}`; } function loadStoredMessages(resumeId: string): UIMessage[] { - try { - const stored = localStorage.getItem(getStorageKey(resumeId)); - if (!stored) return []; - return JSON.parse(stored) as UIMessage[]; - } catch { - return []; - } + try { + const stored = localStorage.getItem(getStorageKey(resumeId)); + if (!stored) return []; + return JSON.parse(stored) as UIMessage[]; + } catch { + return []; + } } function saveStoredMessages(resumeId: string, messages: UIMessage[]): void { - try { - localStorage.setItem(getStorageKey(resumeId), JSON.stringify(messages)); - } catch { - // Silently fail if localStorage is full or unavailable - } + try { + localStorage.setItem(getStorageKey(resumeId), JSON.stringify(messages)); + } catch { + // Silently fail if localStorage is full or unavailable + } } function clearStoredMessages(resumeId: string): void { - localStorage.removeItem(getStorageKey(resumeId)); + localStorage.removeItem(getStorageKey(resumeId)); } function formatPath(path: string): string { - return path.replace(/^\//, "").replace(/\//g, " › "); + return path.replace(/^\//, "").replace(/\//g, " › "); } function formatOperationLabel(op: string): string { - switch (op) { - case "replace": - return t`Updated`; - case "add": - return t`Added`; - case "remove": - return t`Removed`; - default: - return op; - } + switch (op) { + case "replace": + return t`Updated`; + case "add": + return t`Added`; + case "remove": + return t`Removed`; + default: + return op; + } } function ToolBadge({ state, operations }: { state: string; operations?: { op: string; path: string }[] }) { - const isDone = state === "output-available"; + const isDone = state === "output-available"; - return ( -
- - {isDone ? ( - - ) : ( - - )} - {isDone ? "Changes applied" : "Applying changes..."} - + return ( +
+ + {isDone ? ( + + ) : ( + + )} + {isDone ? "Changes applied" : "Applying changes..."} + - {isDone && operations && operations.length > 0 && ( -
- {operations.map((op, i) => ( - - - {formatOperationLabel(op.op)} {formatPath(op.path)} - - ))} -
- )} -
- ); + {isDone && operations && operations.length > 0 && ( +
+ {operations.map((op, i) => ( + + + {formatOperationLabel(op.op)} {formatPath(op.path)} + + ))} +
+ )} +
+ ); } function MessageParts({ message }: { message: UIMessage }) { - const parts = message.parts; + const parts = message.parts; - if (parts.length === 0) return null; + if (parts.length === 0) return null; - return ( -
- {parts.map((part, i) => { - if (part.type === "step-start") { - if (i === 0) return null; - return
; - } + return ( +
+ {parts.map((part, i) => { + if (part.type === "step-start") { + if (i === 0) return null; + return
; + } - if (part.type === "text" && part.text.trim()) { - return ( -

- {part.text} -

- ); - } + if (part.type === "text" && part.text.trim()) { + return ( +

+ {part.text} +

+ ); + } - if (part.type === "reasoning" && part.text.trim()) { - return ( -

- {part.text} -

- ); - } + if (part.type === "reasoning" && part.text.trim()) { + return ( +

+ {part.text} +

+ ); + } - if (part.type === "tool-patch_resume") { - const toolPart = part as any; - const state = toolPart.state as string; - const operations = toolPart.input?.operations as { op: string; path: string }[] | undefined; - return ; - } + if (part.type === "tool-patch_resume") { + const toolPart = part as any; + const state = toolPart.state as string; + const operations = toolPart.input?.operations as { op: string; path: string }[] | undefined; + return ; + } - return null; - })} -
- ); + return null; + })} +
+ ); } export function AIChat() { - const enabled = useAIStore((s) => s.enabled); - const provider = useAIStore((s) => s.provider); - const model = useAIStore((s) => s.model); - const apiKey = useAIStore((s) => s.apiKey); - const baseURL = useAIStore((s) => s.baseURL); + const enabled = useAIStore((s) => s.enabled); + const provider = useAIStore((s) => s.provider); + const model = useAIStore((s) => s.model); + const apiKey = useAIStore((s) => s.apiKey); + const baseURL = useAIStore((s) => s.baseURL); - const resumeId = useResumeStore((s) => s.resume.id); - const resumeData = useResumeStore((s) => s.resume.data); - const updateResumeData = useResumeStore((s) => s.updateResumeData); + const resumeId = useResumeStore((s) => s.resume.id); + const resumeData = useResumeStore((s) => s.resume.data); + const updateResumeData = useResumeStore((s) => s.updateResumeData); - const [input, setInput] = useState(""); - const [open, setOpen] = useState(false); - const bottomRef = useRef(null); - const inputRef = useRef(null); - const processedToolCallIds = useRef(new Set()); + const [input, setInput] = useState(""); + const [open, setOpen] = useState(false); + const bottomRef = useRef(null); + const inputRef = useRef(null); + const processedToolCallIds = useRef(new Set()); - // Load stored messages once on mount and pre-populate processedToolCallIds - // so that already-applied patches are not re-applied. - const [initialMessages] = useState(() => { - const stored = loadStoredMessages(resumeId); + // Load stored messages once on mount and pre-populate processedToolCallIds + // so that already-applied patches are not re-applied. + const [initialMessages] = useState(() => { + const stored = loadStoredMessages(resumeId); - for (const msg of stored) { - if (msg.role !== "assistant") continue; - for (const part of msg.parts) { - if (part.type !== "tool-patch_resume") continue; - const toolPart = part as any; - if (toolPart.state === "output-available") { - processedToolCallIds.current.add(toolPart.toolCallId as string); - } - } - } + for (const msg of stored) { + if (msg.role !== "assistant") continue; + for (const part of msg.parts) { + if (part.type !== "tool-patch_resume") continue; + const toolPart = part as any; + if (toolPart.state === "output-available") { + processedToolCallIds.current.add(toolPart.toolCallId as string); + } + } + } - return stored; - }); + return stored; + }); - const { messages, sendMessage, status, stop, setMessages } = useChat({ - messages: initialMessages, - transport: { - async sendMessages(options) { - return eventIteratorToUnproxiedDataStream( - await client.ai.chat( - { - provider, - model, - apiKey, - baseURL, - messages: options.messages, - resumeData, - }, - { signal: options.abortSignal }, - ), - ); - }, - reconnectToStream() { - throw new Error("Unsupported"); - }, - }, - onError(error) { - toast.error("AI chat error", { description: error.message }); - }, - }); + const { messages, sendMessage, status, stop, setMessages } = useChat({ + messages: initialMessages, + transport: { + async sendMessages(options) { + return eventIteratorToUnproxiedDataStream( + await client.ai.chat( + { + provider, + model, + apiKey, + baseURL, + messages: options.messages, + resumeData, + }, + { signal: options.abortSignal }, + ), + ); + }, + reconnectToStream() { + throw new Error("Unsupported"); + }, + }, + onError(error) { + toast.error("AI chat error", { description: error.message }); + }, + }); - // Apply patches when new tool results arrive - useEffect(() => { - const { operations, newIds } = extractNewPatchOperations(messages, processedToolCallIds.current); - if (operations.length === 0) return; + // Apply patches when new tool results arrive + useEffect(() => { + const { operations, newIds } = extractNewPatchOperations(messages, processedToolCallIds.current); + if (operations.length === 0) return; - try { - const patched = applyResumePatches(resumeData, operations); - updateResumeData((draft) => { - Object.assign(draft, patched); - }); - for (const id of newIds) processedToolCallIds.current.add(id); - } catch (error) { - toast.error("Failed to apply resume changes", { - description: error instanceof Error ? error.message : "Unknown error", - }); - } - }, [messages, resumeData, updateResumeData]); + try { + const patched = applyResumePatches(resumeData, operations); + updateResumeData((draft) => { + Object.assign(draft, patched); + }); + for (const id of newIds) processedToolCallIds.current.add(id); + } catch (error) { + toast.error("Failed to apply resume changes", { + description: error instanceof Error ? error.message : "Unknown error", + }); + } + }, [messages, resumeData, updateResumeData]); - // Persist messages to localStorage whenever they change - useEffect(() => { - saveStoredMessages(resumeId, messages); - }, [resumeId, messages]); + // Persist messages to localStorage whenever they change + useEffect(() => { + saveStoredMessages(resumeId, messages); + }, [resumeId, messages]); - // Auto-scroll to bottom (sentinel so it works with ScrollArea viewport) - useEffect(() => { - bottomRef.current?.scrollIntoView({ behavior: "smooth" }); - }, [messages]); + // Auto-scroll to bottom (sentinel so it works with ScrollArea viewport) + useEffect(() => { + bottomRef.current?.scrollIntoView({ behavior: "smooth" }); + }, [messages]); - // Refocus the input when the AI finishes responding - useEffect(() => { - if (status === "ready") inputRef.current?.focus(); - }, [status]); + // Refocus the input when the AI finishes responding + useEffect(() => { + if (status === "ready") inputRef.current?.focus(); + }, [status]); - const handleOpenChange = useCallback((nextOpen: boolean) => { - setOpen(nextOpen); - }, []); + const handleOpenChange = useCallback((nextOpen: boolean) => { + setOpen(nextOpen); + }, []); - const handleClearMessages = useCallback(() => { - setMessages([]); - clearStoredMessages(resumeId); - processedToolCallIds.current.clear(); - }, [resumeId, setMessages]); + const handleClearMessages = useCallback(() => { + setMessages([]); + clearStoredMessages(resumeId); + processedToolCallIds.current.clear(); + }, [resumeId, setMessages]); - const handleSubmit = useCallback( - async (e: React.SubmitEvent) => { - e.preventDefault(); - if (!input.trim() || status !== "ready") return; - await sendMessage({ text: input }); - setInput(""); - }, - [input, status, sendMessage], - ); + const handleSubmit = useCallback( + async (e: React.SubmitEvent) => { + e.preventDefault(); + if (!input.trim() || status !== "ready") return; + await sendMessage({ text: input }); + setInput(""); + }, + [input, status, sendMessage], + ); - if (!enabled) return null; + if (!enabled) return null; - const isLoading = status === "submitted" || status === "streaming"; + const isLoading = status === "submitted" || status === "streaming"; - return ( - - - - - } - /> + return ( + + + + + } + /> - - {/* Header with clear button */} -
-

- AI Chat -

- -
+ + {/* Header with clear button */} +
+

+ AI Chat +

+ +
- {/* Message list — min-h-0 so flex constrains height and viewport scrolls */} - -
- {messages.length === 0 && ( -
-

- Ask me to update your resume... -

-
- )} + {/* Message list — min-h-0 so flex constrains height and viewport scrolls */} + +
+ {messages.length === 0 && ( +
+

+ Ask me to update your resume... +

+
+ )} -
- {messages.map((message) => ( -
-
- {message.role === "user" ? ( -

- {message.parts.map((part, i) => - part.type === "text" ? {part.text} : null, - )} -

- ) : ( - - )} -
-
- ))} +
+ {messages.map((message) => ( +
+
+ {message.role === "user" ? ( +

+ {message.parts.map((part, i) => + part.type === "text" ? {part.text} : null, + )} +

+ ) : ( + + )} +
+
+ ))} - {status === "submitted" && ( -
-
-
- - - Thinking... - -
-
-
- )} + {status === "submitted" && ( +
+
+
+ + + Thinking... + +
+
+
+ )} -
-
-
- +
+
+
+ - {/* Input form — shrink-0 so it stays visible when content scrolls */} -
- setInput(e.target.value)} - placeholder={t`e.g. Change my name to...`} - className="flex-1 bg-transparent text-[13px] outline-none placeholder:text-muted-foreground" - /> - {isLoading ? ( - - ) : ( - - )} -
- - - ); + {/* Input form — shrink-0 so it stays visible when content scrolls */} +
+ setInput(e.target.value)} + placeholder={t`e.g. Change my name to...`} + className="flex-1 bg-transparent text-[13px] outline-none placeholder:text-muted-foreground" + /> + {isLoading ? ( + + ) : ( + + )} +
+ + + ); } diff --git a/src/components/animation/comet-card.tsx b/src/components/animation/comet-card.tsx index 0e0e62042..5f6c335f0 100644 --- a/src/components/animation/comet-card.tsx +++ b/src/components/animation/comet-card.tsx @@ -6,83 +6,83 @@ import { useRef } from "react"; import { cn } from "@/utils/style"; type Props = { - rotateDepth?: number; - translateDepth?: number; - glareOpacity?: number; - scaleFactor?: number; - className?: string; - children: React.ReactNode; + rotateDepth?: number; + translateDepth?: number; + glareOpacity?: number; + scaleFactor?: number; + className?: string; + children: React.ReactNode; }; export const CometCard = ({ - rotateDepth = 17.5, - translateDepth = 20, - glareOpacity = 0.4, - scaleFactor = 1.05, - className, - children, + rotateDepth = 17.5, + translateDepth = 20, + glareOpacity = 0.4, + scaleFactor = 1.05, + className, + children, }: Props) => { - const ref = useRef(null); + const ref = useRef(null); - const x = useMotionValue(0); - const y = useMotionValue(0); + const x = useMotionValue(0); + const y = useMotionValue(0); - const mouseXSpring = useSpring(x); - const mouseYSpring = useSpring(y); + const mouseXSpring = useSpring(x); + const mouseYSpring = useSpring(y); - const rotateX = useTransform(mouseYSpring, [-0.5, 0.5], [`-${rotateDepth}deg`, `${rotateDepth}deg`]); - const rotateY = useTransform(mouseXSpring, [-0.5, 0.5], [`${rotateDepth}deg`, `-${rotateDepth}deg`]); + const rotateX = useTransform(mouseYSpring, [-0.5, 0.5], [`-${rotateDepth}deg`, `${rotateDepth}deg`]); + const rotateY = useTransform(mouseXSpring, [-0.5, 0.5], [`${rotateDepth}deg`, `-${rotateDepth}deg`]); - const translateX = useTransform(mouseXSpring, [-0.5, 0.5], [`-${translateDepth}px`, `${translateDepth}px`]); - const translateY = useTransform(mouseYSpring, [-0.5, 0.5], [`${translateDepth}px`, `-${translateDepth}px`]); + const translateX = useTransform(mouseXSpring, [-0.5, 0.5], [`-${translateDepth}px`, `${translateDepth}px`]); + const translateY = useTransform(mouseYSpring, [-0.5, 0.5], [`${translateDepth}px`, `-${translateDepth}px`]); - const glareX = useTransform(mouseXSpring, [-0.5, 0.5], [0, 100]); - const glareY = useTransform(mouseYSpring, [-0.5, 0.5], [0, 100]); + const glareX = useTransform(mouseXSpring, [-0.5, 0.5], [0, 100]); + const glareY = useTransform(mouseYSpring, [-0.5, 0.5], [0, 100]); - const glareBackground = useMotionTemplate`radial-gradient(circle at ${glareX}% ${glareY}%, rgba(255, 255, 255, 0.9) 10%, rgba(255, 255, 255, 0.75) 20%, rgba(255, 255, 255, 0) 80%)`; + const glareBackground = useMotionTemplate`radial-gradient(circle at ${glareX}% ${glareY}%, rgba(255, 255, 255, 0.9) 10%, rgba(255, 255, 255, 0.75) 20%, rgba(255, 255, 255, 0) 80%)`; - const handleMouseMove = (e: React.MouseEvent) => { - if (!ref.current) return; + const handleMouseMove = (e: React.MouseEvent) => { + if (!ref.current) return; - const rect = ref.current.getBoundingClientRect(); + const rect = ref.current.getBoundingClientRect(); - const width = rect.width; - const height = rect.height; + const width = rect.width; + const height = rect.height; - const mouseX = e.clientX - rect.left; - const mouseY = e.clientY - rect.top; + const mouseX = e.clientX - rect.left; + const mouseY = e.clientY - rect.top; - const xPct = mouseX / width - 0.5; - const yPct = mouseY / height - 0.5; + const xPct = mouseX / width - 0.5; + const yPct = mouseY / height - 0.5; - x.set(xPct); - y.set(yPct); - }; + x.set(xPct); + y.set(yPct); + }; - const handleMouseLeave = () => { - x.set(0); - y.set(0); - }; + const handleMouseLeave = () => { + x.set(0); + y.set(0); + }; - return ( -
- - {children} + return ( +
+ + {children} - - -
- ); + + +
+ ); }; diff --git a/src/components/animation/count-up.tsx b/src/components/animation/count-up.tsx index 577911ecf..996881e67 100644 --- a/src/components/animation/count-up.tsx +++ b/src/components/animation/count-up.tsx @@ -2,128 +2,128 @@ import { useInView, useMotionValue, useSpring } from "motion/react"; import { useCallback, useEffect, useRef } from "react"; type CountUpProps = { - to: number; - from?: number; - direction?: "up" | "down"; - delay?: number; - duration?: number; - className?: string; - startWhen?: boolean; - separator?: string; - onStart?: () => void; - onEnd?: () => void; - "aria-hidden"?: boolean | "true" | "false"; - "aria-live"?: "off" | "polite" | "assertive"; - "aria-atomic"?: boolean | "true" | "false"; + to: number; + from?: number; + direction?: "up" | "down"; + delay?: number; + duration?: number; + className?: string; + startWhen?: boolean; + separator?: string; + onStart?: () => void; + onEnd?: () => void; + "aria-hidden"?: boolean | "true" | "false"; + "aria-live"?: "off" | "polite" | "assertive"; + "aria-atomic"?: boolean | "true" | "false"; }; export function CountUp({ - to, - from = 0, - direction = "up", - delay = 0, - duration = 2, - className = "", - startWhen = true, - separator = "", - onStart, - onEnd, - "aria-hidden": ariaHidden, - "aria-live": ariaLive = "polite", - "aria-atomic": ariaAtomic = "true", + to, + from = 0, + direction = "up", + delay = 0, + duration = 2, + className = "", + startWhen = true, + separator = "", + onStart, + onEnd, + "aria-hidden": ariaHidden, + "aria-live": ariaLive = "polite", + "aria-atomic": ariaAtomic = "true", }: CountUpProps) { - const ref = useRef(null); - const motionValue = useMotionValue(direction === "down" ? to : from); + const ref = useRef(null); + const motionValue = useMotionValue(direction === "down" ? to : from); - const damping = 20 + 40 * (1 / duration); - const stiffness = 100 * (1 / duration); + const damping = 20 + 40 * (1 / duration); + const stiffness = 100 * (1 / duration); - const springValue = useSpring(motionValue, { - damping, - stiffness, - }); + const springValue = useSpring(motionValue, { + damping, + stiffness, + }); - const isInView = useInView(ref, { once: true, margin: "0px" }); + const isInView = useInView(ref, { once: true, margin: "0px" }); - const getDecimalPlaces = (num: number): number => { - const str = num.toString(); + const getDecimalPlaces = (num: number): number => { + const str = num.toString(); - if (str.includes(".")) { - const decimals = str.split(".")[1]; - if (Number.parseInt(decimals, 10) !== 0) return decimals.length; - } + if (str.includes(".")) { + const decimals = str.split(".")[1]; + if (Number.parseInt(decimals, 10) !== 0) return decimals.length; + } - return 0; - }; + return 0; + }; - const maxDecimals = Math.max(getDecimalPlaces(from), getDecimalPlaces(to)); + const maxDecimals = Math.max(getDecimalPlaces(from), getDecimalPlaces(to)); - const formatValue = useCallback( - (latest: number) => { - const hasDecimals = maxDecimals > 0; + const formatValue = useCallback( + (latest: number) => { + const hasDecimals = maxDecimals > 0; - const options: Intl.NumberFormatOptions = { - useGrouping: !!separator, - minimumFractionDigits: hasDecimals ? maxDecimals : 0, - maximumFractionDigits: hasDecimals ? maxDecimals : 0, - }; + const options: Intl.NumberFormatOptions = { + useGrouping: !!separator, + minimumFractionDigits: hasDecimals ? maxDecimals : 0, + maximumFractionDigits: hasDecimals ? maxDecimals : 0, + }; - const formattedNumber = Intl.NumberFormat("en-US", options).format(latest); + const formattedNumber = Intl.NumberFormat("en-US", options).format(latest); - return separator ? formattedNumber.replace(/,/g, separator) : formattedNumber; - }, - [maxDecimals, separator], - ); + return separator ? formattedNumber.replace(/,/g, separator) : formattedNumber; + }, + [maxDecimals, separator], + ); - useEffect(() => { - if (ref.current) { - ref.current.textContent = formatValue(direction === "down" ? to : from); - } - }, [from, to, direction, formatValue]); + useEffect(() => { + if (ref.current) { + ref.current.textContent = formatValue(direction === "down" ? to : from); + } + }, [from, to, direction, formatValue]); - useEffect(() => { - if (isInView && startWhen) { - if (typeof onStart === "function") { - onStart(); - } + useEffect(() => { + if (isInView && startWhen) { + if (typeof onStart === "function") { + onStart(); + } - const timeoutId = setTimeout(() => { - motionValue.set(direction === "down" ? from : to); - }, delay * 1000); + const timeoutId = setTimeout(() => { + motionValue.set(direction === "down" ? from : to); + }, delay * 1000); - const durationTimeoutId = setTimeout( - () => { - if (typeof onEnd === "function") { - onEnd(); - } - }, - delay * 1000 + duration * 1000, - ); + const durationTimeoutId = setTimeout( + () => { + if (typeof onEnd === "function") { + onEnd(); + } + }, + delay * 1000 + duration * 1000, + ); - return () => { - clearTimeout(timeoutId); - clearTimeout(durationTimeoutId); - }; - } - }, [isInView, startWhen, motionValue, direction, from, to, delay, onStart, onEnd, duration]); + return () => { + clearTimeout(timeoutId); + clearTimeout(durationTimeoutId); + }; + } + }, [isInView, startWhen, motionValue, direction, from, to, delay, onStart, onEnd, duration]); - useEffect(() => { - const unsubscribe = springValue.on("change", (latest: number) => { - if (ref.current) { - ref.current.textContent = formatValue(latest); - } - }); + useEffect(() => { + const unsubscribe = springValue.on("change", (latest: number) => { + if (ref.current) { + ref.current.textContent = formatValue(latest); + } + }); - return () => unsubscribe(); - }, [springValue, formatValue]); + return () => unsubscribe(); + }, [springValue, formatValue]); - return ( - - ); + return ( + + ); } diff --git a/src/components/animation/spotlight.tsx b/src/components/animation/spotlight.tsx index e429997d5..54cf20b60 100644 --- a/src/components/animation/spotlight.tsx +++ b/src/components/animation/spotlight.tsx @@ -1,111 +1,111 @@ import { motion } from "motion/react"; type SpotlightProps = { - duration?: number; - gradientFirst?: string; - gradientSecond?: string; - gradientThird?: string; - width?: number; - height?: number; - smallWidth?: number; - translateY?: number; - xOffset?: number; + duration?: number; + gradientFirst?: string; + gradientSecond?: string; + gradientThird?: string; + width?: number; + height?: number; + smallWidth?: number; + translateY?: number; + xOffset?: number; }; export const Spotlight = ({ - duration = 7, - gradientFirst = "radial-gradient(68.54% 68.72% at 55.02% 31.46%, hsla(210, 100%, 85%, .08) 0, hsla(210, 100%, 55%, .02) 50%, hsla(210, 100%, 45%, 0) 80%)", - gradientSecond = "radial-gradient(50% 50% at 50% 50%, hsla(210, 100%, 85%, .06) 0, hsla(210, 100%, 55%, .02) 80%, transparent 100%)", - gradientThird = "radial-gradient(50% 50% at 50% 50%, hsla(210, 100%, 85%, .04) 0, hsla(210, 100%, 45%, .02) 80%, transparent 100%)", - width = 560, - height = 1380, - smallWidth = 240, - translateY = -350, - xOffset = 100, + duration = 7, + gradientFirst = "radial-gradient(68.54% 68.72% at 55.02% 31.46%, hsla(210, 100%, 85%, .08) 0, hsla(210, 100%, 55%, .02) 50%, hsla(210, 100%, 45%, 0) 80%)", + gradientSecond = "radial-gradient(50% 50% at 50% 50%, hsla(210, 100%, 85%, .06) 0, hsla(210, 100%, 55%, .02) 80%, transparent 100%)", + gradientThird = "radial-gradient(50% 50% at 50% 50%, hsla(210, 100%, 85%, .04) 0, hsla(210, 100%, 45%, .02) 80%, transparent 100%)", + width = 560, + height = 1380, + smallWidth = 240, + translateY = -350, + xOffset = 100, }: SpotlightProps) => { - return ( - - -
+ return ( + + +
-
+
-
- +
+ - -
+ +
-
+
-
- - - ); +
+ + + ); }; diff --git a/src/components/animation/text-mask.tsx b/src/components/animation/text-mask.tsx index eab49d41b..73554a689 100644 --- a/src/components/animation/text-mask.tsx +++ b/src/components/animation/text-mask.tsx @@ -6,109 +6,109 @@ import { cn } from "@/utils/style"; const textClassName = cn("fill-transparent text-3xl leading-none font-bold tracking-tight"); type TextMaskEffectProps = { - text: string; - duration?: number; - className?: string; - "aria-hidden"?: boolean | "true" | "false"; + text: string; + duration?: number; + className?: string; + "aria-hidden"?: boolean | "true" | "false"; }; export const TextMaskEffect = ({ text, duration = 6, className, "aria-hidden": ariaHidden }: TextMaskEffectProps) => { - const svgRef = useRef(null); - const [cursor, setCursor] = useState({ x: 0, y: 0 }); - const [hovered, setHovered] = useState(false); - const [maskPosition, setMaskPosition] = useState({ cx: "50%", cy: "50%" }); + const svgRef = useRef(null); + const [cursor, setCursor] = useState({ x: 0, y: 0 }); + const [hovered, setHovered] = useState(false); + const [maskPosition, setMaskPosition] = useState({ cx: "50%", cy: "50%" }); - useEffect(() => { - if (svgRef.current && cursor.x !== null && cursor.y !== null) { - const svgRect = svgRef.current.getBoundingClientRect(); - const cxPercentage = ((cursor.x - svgRect.left) / svgRect.width) * 100; - const cyPercentage = ((cursor.y - svgRect.top) / svgRect.height) * 100; + useEffect(() => { + if (svgRef.current && cursor.x !== null && cursor.y !== null) { + const svgRect = svgRef.current.getBoundingClientRect(); + const cxPercentage = ((cursor.x - svgRect.left) / svgRect.width) * 100; + const cyPercentage = ((cursor.y - svgRect.top) / svgRect.height) * 100; - setMaskPosition({ cx: `${cxPercentage}%`, cy: `${cyPercentage}%` }); - } - }, [cursor]); + setMaskPosition({ cx: `${cxPercentage}%`, cy: `${cyPercentage}%` }); + } + }, [cursor]); - return ( - setHovered(true)} - onMouseLeave={() => setHovered(false)} - onMouseMove={(e) => setCursor({ x: e.clientX, y: e.clientY })} - > - - - {hovered && ( - <> - - - - - - - )} - + return ( + setHovered(true)} + onMouseLeave={() => setHovered(false)} + onMouseMove={(e) => setCursor({ x: e.clientX, y: e.clientY })} + > + + + {hovered && ( + <> + + + + + + + )} + - - - - + + + + - - - - + + + + - - {text} - + + {text} + - - {text} - + + {text} + - - {text} - - - ); + + {text} + + + ); }; diff --git a/src/components/command-palette/index.tsx b/src/components/command-palette/index.tsx index e0aec237d..bf82cc973 100644 --- a/src/components/command-palette/index.tsx +++ b/src/components/command-palette/index.tsx @@ -11,111 +11,111 @@ import { ResumesCommandGroup } from "./pages/resumes"; import { useCommandPaletteStore } from "./store"; export function CommandPalette() { - const inputRef = useRef(null); - const { open, search, pages, setOpen, setSearch, goBack } = useCommandPaletteStore(); + const inputRef = useRef(null); + const { open, search, pages, setOpen, setSearch, goBack } = useCommandPaletteStore(); - const isFirstPage = pages.length === 0; - const currentPage = pages[pages.length - 1]; + const isFirstPage = pages.length === 0; + const currentPage = pages[pages.length - 1]; - // Toggle command palette with Cmd+K / Ctrl+K - useHotkeys( - ["meta+k", "ctrl+k"], - (e) => { - e.preventDefault(); - setOpen(!open); - }, - { preventDefault: true, enableOnFormTags: true }, - [open], - ); + // Toggle command palette with Cmd+K / Ctrl+K + useHotkeys( + ["meta+k", "ctrl+k"], + (e) => { + e.preventDefault(); + setOpen(!open); + }, + { preventDefault: true, enableOnFormTags: true }, + [open], + ); - // Handle backspace: delete text if input has text, go back if empty, close if first page - useHotkeys( - "backspace", - (e) => { - // Only handle if the command palette is open - if (!open) return; + // Handle backspace: delete text if input has text, go back if empty, close if first page + useHotkeys( + "backspace", + (e) => { + // Only handle if the command palette is open + if (!open) return; - const input = inputRef.current; - if (!input) return; + const input = inputRef.current; + if (!input) return; - // Only handle if input is focused - if (document.activeElement !== input) return; + // Only handle if input is focused + if (document.activeElement !== input) return; - // If input has text, let the default behavior handle it (delete character) - if (search.length > 0) return; + // If input has text, let the default behavior handle it (delete character) + if (search.length > 0) return; - // If input is empty, prevent default and go back - e.preventDefault(); - goBack(); - }, - { - preventDefault: false, // We'll prevent it conditionally - enableOnFormTags: true, - }, - [open, search], - ); + // If input is empty, prevent default and go back + e.preventDefault(); + goBack(); + }, + { + preventDefault: false, // We'll prevent it conditionally + enableOnFormTags: true, + }, + [open, search], + ); - // Close with Escape - useHotkeys( - "escape", - () => { - if (!open) return; - setOpen(false); - }, - { - preventDefault: true, - enableOnFormTags: true, - }, - [open], - ); + // Close with Escape + useHotkeys( + "escape", + () => { + if (!open) return; + setOpen(false); + }, + { + preventDefault: true, + enableOnFormTags: true, + }, + [open], + ); - const handleOpenChange = (newOpen: boolean) => { - setOpen(newOpen); - }; + const handleOpenChange = (newOpen: boolean) => { + setOpen(newOpen); + }; - const handleSearchChange = (value: string) => { - setSearch(value); - }; + const handleSearchChange = (value: string) => { + setSearch(value); + }; - return ( - - - - Builder Command Palette - - - Type a command or search... - - + return ( + + + + Builder Command Palette + + + Type a command or search... + + - - - + + + - - - The command you're looking for doesn't exist. - + + + The command you're looking for doesn't exist. + - - - - - - - - ); + + + + + + + + ); } diff --git a/src/components/command-palette/pages/base.tsx b/src/components/command-palette/pages/base.tsx index ec3d45905..cfe46629b 100644 --- a/src/components/command-palette/pages/base.tsx +++ b/src/components/command-palette/pages/base.tsx @@ -5,20 +5,20 @@ import { CommandGroup } from "@/components/ui/command"; import { useCommandPaletteStore } from "../store"; type Props = { - page?: string; - heading: React.ReactNode; - children: React.ReactNode; + page?: string; + heading: React.ReactNode; + children: React.ReactNode; }; export const BaseCommandGroup = ({ page, heading, children }: Props) => { - const pages = useCommandPaletteStore((state) => state.pages); - const currentPage = pages[pages.length - 1]; + const pages = useCommandPaletteStore((state) => state.pages); + const currentPage = pages[pages.length - 1]; - const isEnabled = useMemo(() => { - return currentPage === page; - }, [currentPage, page]); + const isEnabled = useMemo(() => { + return currentPage === page; + }, [currentPage, page]); - if (!isEnabled) return null; + if (!isEnabled) return null; - return {children}; + return {children}; }; diff --git a/src/components/command-palette/pages/navigation.tsx b/src/components/command-palette/pages/navigation.tsx index 80dc49e1b..f324efbd0 100644 --- a/src/components/command-palette/pages/navigation.tsx +++ b/src/components/command-palette/pages/navigation.tsx @@ -1,14 +1,14 @@ import { t } from "@lingui/core/macro"; import { Trans } from "@lingui/react/macro"; import { - GearIcon, - HouseSimpleIcon, - KeyIcon, - OpenAiLogoIcon, - ReadCvLogoIcon, - ShieldCheckIcon, - UserCircleIcon, - WarningIcon, + GearIcon, + HouseSimpleIcon, + KeyIcon, + OpenAiLogoIcon, + ReadCvLogoIcon, + ShieldCheckIcon, + UserCircleIcon, + WarningIcon, } from "@phosphor-icons/react"; import { useNavigate, useRouteContext } from "@tanstack/react-router"; @@ -18,100 +18,100 @@ import { useCommandPaletteStore } from "../store"; import { BaseCommandGroup } from "./base"; export function NavigationCommandGroup() { - const navigate = useNavigate(); - const { session } = useRouteContext({ strict: false }); - const reset = useCommandPaletteStore((state) => state.reset); - const pushPage = useCommandPaletteStore((state) => state.pushPage); + const navigate = useNavigate(); + const { session } = useRouteContext({ strict: false }); + const reset = useCommandPaletteStore((state) => state.reset); + const pushPage = useCommandPaletteStore((state) => state.pushPage); - const onNavigate = async (path: string) => { - await navigate({ to: path }); - reset(); - }; + const onNavigate = async (path: string) => { + await navigate({ to: path }); + reset(); + }; - return ( - <> - Go to...}> - onNavigate("/")}> - - Home - + return ( + <> + Go to...}> + onNavigate("/")}> + + Home + - onNavigate("/dashboard/resumes")} - > - - Resumes - + onNavigate("/dashboard/resumes")} + > + + Resumes + - pushPage("settings")} - > - - Settings - - + pushPage("settings")} + > + + Settings + + - Settings}> - onNavigate("/dashboard/settings/profile")} - > - - Profile - + Settings}> + onNavigate("/dashboard/settings/profile")} + > + + Profile + - onNavigate("/dashboard/settings/preferences")} - > - - Preferences - + onNavigate("/dashboard/settings/preferences")} + > + + Preferences + - onNavigate("/dashboard/settings/authentication")} - > - - Authentication - + onNavigate("/dashboard/settings/authentication")} + > + + Authentication + - onNavigate("/dashboard/settings/api-keys")} - > - - API Keys - + onNavigate("/dashboard/settings/api-keys")} + > + + API Keys + - onNavigate("/dashboard/settings/ai")} - > - - Artificial Intelligence - + onNavigate("/dashboard/settings/ai")} + > + + Artificial Intelligence + - onNavigate("/dashboard/settings/danger-zone")} - > - - Danger Zone - - - - ); + onNavigate("/dashboard/settings/danger-zone")} + > + + Danger Zone + + + + ); } diff --git a/src/components/command-palette/pages/preferences/index.tsx b/src/components/command-palette/pages/preferences/index.tsx index f02a3cc8d..76b15d311 100644 --- a/src/components/command-palette/pages/preferences/index.tsx +++ b/src/components/command-palette/pages/preferences/index.tsx @@ -9,24 +9,24 @@ import { LanguageCommandPage } from "./language"; import { ThemeCommandPage } from "./theme"; export function PreferencesCommandGroup() { - const pushPage = useCommandPaletteStore((state) => state.pushPage); + const pushPage = useCommandPaletteStore((state) => state.pushPage); - return ( - <> - Preferences}> - pushPage("theme")}> - - Change theme to... - + return ( + <> + Preferences}> + pushPage("theme")}> + + Change theme to... + - pushPage("language")}> - - Change language to... - - + pushPage("language")}> + + Change language to... + + - - - - ); + + + + ); } diff --git a/src/components/command-palette/pages/preferences/language.tsx b/src/components/command-palette/pages/preferences/language.tsx index bed9b5b95..046f71fb8 100644 --- a/src/components/command-palette/pages/preferences/language.tsx +++ b/src/components/command-palette/pages/preferences/language.tsx @@ -7,22 +7,22 @@ import { isLocale, loadLocale, localeMap, setLocaleServerFn } from "@/utils/loca import { BaseCommandGroup } from "../base"; export function LanguageCommandPage() { - const { i18n } = useLingui(); + const { i18n } = useLingui(); - const handleLocaleChange = async (value: string) => { - if (!value || !isLocale(value)) return; - await Promise.all([loadLocale(value), setLocaleServerFn({ data: value })]); - window.location.reload(); - }; + const handleLocaleChange = async (value: string) => { + if (!value || !isLocale(value)) return; + await Promise.all([loadLocale(value), setLocaleServerFn({ data: value })]); + window.location.reload(); + }; - return ( - Language}> - {Object.entries(localeMap).map(([value, label]) => ( - handleLocaleChange(value)}> - {value} - {i18n.t(label)} - - ))} - - ); + return ( + Language}> + {Object.entries(localeMap).map(([value, label]) => ( + handleLocaleChange(value)}> + {value} + {i18n.t(label)} + + ))} + + ); } diff --git a/src/components/command-palette/pages/preferences/theme.tsx b/src/components/command-palette/pages/preferences/theme.tsx index 34d935aac..84e627e74 100644 --- a/src/components/command-palette/pages/preferences/theme.tsx +++ b/src/components/command-palette/pages/preferences/theme.tsx @@ -8,25 +8,25 @@ import { useCommandPaletteStore } from "../../store"; import { BaseCommandGroup } from "../base"; export function ThemeCommandPage() { - const { setTheme } = useTheme(); - const setOpen = useCommandPaletteStore((state) => state.setOpen); + const { setTheme } = useTheme(); + const setOpen = useCommandPaletteStore((state) => state.setOpen); - const handleThemeChange = (theme: "light" | "dark") => { - setTheme(theme, { playSound: false }); - setOpen(false); - }; + const handleThemeChange = (theme: "light" | "dark") => { + setTheme(theme, { playSound: false }); + setOpen(false); + }; - return ( - Theme}> - handleThemeChange("light")}> - - Light theme - + return ( + Theme}> + handleThemeChange("light")}> + + Light theme + - handleThemeChange("dark")}> - - Dark theme - - - ); + handleThemeChange("dark")}> + + Dark theme + + + ); } diff --git a/src/components/command-palette/pages/resumes.tsx b/src/components/command-palette/pages/resumes.tsx index d0f381e9c..c28d93298 100644 --- a/src/components/command-palette/pages/resumes.tsx +++ b/src/components/command-palette/pages/resumes.tsx @@ -14,71 +14,71 @@ import { useCommandPaletteStore } from "../store"; import { BaseCommandGroup } from "./base"; export function ResumesCommandGroup() { - const navigate = useNavigate(); - const { openDialog } = useDialogStore(); - const { session } = useRouteContext({ strict: false }); - const reset = useCommandPaletteStore((state) => state.reset); - const peekPage = useCommandPaletteStore((state) => state.peekPage); - const pushPage = useCommandPaletteStore((state) => state.pushPage); + const navigate = useNavigate(); + const { openDialog } = useDialogStore(); + const { session } = useRouteContext({ strict: false }); + const reset = useCommandPaletteStore((state) => state.reset); + const peekPage = useCommandPaletteStore((state) => state.peekPage); + const pushPage = useCommandPaletteStore((state) => state.pushPage); - const isResumesPage = peekPage() === "resumes"; + const isResumesPage = peekPage() === "resumes"; - const { data: resumes, isLoading } = useQuery( - orpc.resume.list.queryOptions({ - enabled: !!session && isResumesPage, - }), - ); + const { data: resumes, isLoading } = useQuery( + orpc.resume.list.queryOptions({ + enabled: !!session && isResumesPage, + }), + ); - const onCreate = async () => { - await navigate({ to: "/dashboard/resumes" }); - openDialog("resume.create", undefined); - reset(); - }; + const onCreate = async () => { + await navigate({ to: "/dashboard/resumes" }); + openDialog("resume.create", undefined); + reset(); + }; - const onNavigate = async (path: string) => { - await navigate({ to: path }); - reset(); - }; + const onNavigate = async (path: string) => { + await navigate({ to: path }); + reset(); + }; - if (!session) return null; + if (!session) return null; - return ( - <> - Search for...}> - pushPage("resumes")}> - - Resumes - - + return ( + <> + Search for...}> + pushPage("resumes")}> + + Resumes + + - Resumes}> - - - Create a new resume - + Resumes}> + + + Create a new resume + - {isLoading ? ( - - Loading resumes... - - ) : ( - resumes?.map((resume) => ( - onNavigate(`/builder/${resume.id}`)} - > - - {resume.name} + {isLoading ? ( + + Loading resumes... + + ) : ( + resumes?.map((resume) => ( + onNavigate(`/builder/${resume.id}`)} + > + + {resume.name} - - Press Enter to open - - - )) - )} - - - ); + + Press Enter to open + + + )) + )} + + + ); } diff --git a/src/components/command-palette/store.ts b/src/components/command-palette/store.ts index cd68aeb15..c9baf7487 100644 --- a/src/components/command-palette/store.ts +++ b/src/components/command-palette/store.ts @@ -1,59 +1,59 @@ import { create } from "zustand/react"; type CommandPaletteState = { - open: boolean; - search: string; - pages: string[]; + open: boolean; + search: string; + pages: string[]; }; type CommandPaletteActions = { - setOpen: (open: boolean) => void; - setSearch: (search: string) => void; - pushPage: (page: string) => void; - peekPage: () => string | undefined; - popPage: () => void; - reset: () => void; - goBack: () => void; + setOpen: (open: boolean) => void; + setSearch: (search: string) => void; + pushPage: (page: string) => void; + peekPage: () => string | undefined; + popPage: () => void; + reset: () => void; + goBack: () => void; }; type CommandPaletteStore = CommandPaletteState & CommandPaletteActions; const initialState: CommandPaletteState = { - open: false, - search: "", - pages: [], + open: false, + search: "", + pages: [], }; export const useCommandPaletteStore = create((set, get) => ({ - ...initialState, + ...initialState, - setOpen: (open) => { - set({ open }); - if (!open) set(initialState); - }, + setOpen: (open) => { + set({ open }); + if (!open) set(initialState); + }, - setSearch: (search) => set({ search }), + setSearch: (search) => set({ search }), - peekPage: () => get().pages[get().pages.length - 1], + peekPage: () => get().pages[get().pages.length - 1], - pushPage: (page) => set((state) => ({ pages: [...state.pages, page], search: "" })), + pushPage: (page) => set((state) => ({ pages: [...state.pages, page], search: "" })), - popPage: () => set((state) => ({ pages: state.pages.slice(0, -1), search: "" })), + popPage: () => set((state) => ({ pages: state.pages.slice(0, -1), search: "" })), - reset: () => set(initialState), + reset: () => set(initialState), - goBack: () => { - set((state) => { - if (state.search.length > 0) { - // If there's text, just clear the search - return { search: "" }; - } - if (state.pages.length > 0) { - // If on a sub-page, go back - return { pages: state.pages.slice(0, -1), search: "" }; - } - // If on first page, close - return { open: false, search: "", pages: [] }; - }); - }, + goBack: () => { + set((state) => { + if (state.search.length > 0) { + // If there's text, just clear the search + return { search: "" }; + } + if (state.pages.length > 0) { + // If on a sub-page, go back + return { pages: state.pages.slice(0, -1), search: "" }; + } + // If on first page, close + return { open: false, search: "", pages: [] }; + }); + }, })); diff --git a/src/components/input/chip-input.tsx b/src/components/input/chip-input.tsx index 85842b906..18d1592fb 100644 --- a/src/components/input/chip-input.tsx +++ b/src/components/input/chip-input.tsx @@ -1,17 +1,17 @@ import { - closestCenter, - DndContext, - type DragEndEvent, - KeyboardSensor, - PointerSensor, - useSensor, - useSensors, + closestCenter, + DndContext, + type DragEndEvent, + KeyboardSensor, + PointerSensor, + useSensor, + useSensors, } from "@dnd-kit/core"; import { - horizontalListSortingStrategy, - SortableContext, - sortableKeyboardCoordinates, - useSortable, + horizontalListSortingStrategy, + SortableContext, + sortableKeyboardCoordinates, + useSortable, } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; import { Trans } from "@lingui/react/macro"; @@ -30,275 +30,275 @@ const RETURN_KEY = "Enter"; const COMMA_KEY = ","; type ChipItemProps = { - id: string; - chip: string; - index: number; - isEditing: boolean; - onEdit: (index: number) => void; - onRemove: (index: number) => void; + id: string; + chip: string; + index: number; + isEditing: boolean; + onEdit: (index: number) => void; + onRemove: (index: number) => void; }; function ChipItem({ id, chip, index, isEditing, onEdit, onRemove }: ChipItemProps) { - const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id }); + const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id }); - const [isHovered, setIsHovered] = React.useState(false); + const [isHovered, setIsHovered] = React.useState(false); - const style = { - transform: CSS.Transform.toString(transform), - transition, - zIndex: isDragging ? 10 : undefined, - opacity: isDragging ? 0.6 : 1, - }; + const style = { + transform: CSS.Transform.toString(transform), + transition, + zIndex: isDragging ? 10 : undefined, + opacity: isDragging ? 0.6 : 1, + }; - return ( -
setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)} - {...attributes} - {...listeners} - > - - {chip} - - - - - -
- ); + return ( +
setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + {...attributes} + {...listeners} + > + + {chip} + + + + + +
+ ); } type Props = Omit, "value" | "onChange"> & { - value?: string[]; - defaultValue?: string[]; - onChange?: (value: string[]) => void; + value?: string[]; + defaultValue?: string[]; + onChange?: (value: string[]) => void; }; export function ChipInput({ value, defaultValue = [], onChange, className, ...props }: Props) { - const [chips, setChips] = useControlledState({ - value, - defaultValue, - onChange, - }); + const [chips, setChips] = useControlledState({ + value, + defaultValue, + onChange, + }); - const [input, setInput] = React.useState(""); - const [editingIndex, setEditingIndex] = React.useState(null); - const inputRef = React.useRef(null); + const [input, setInput] = React.useState(""); + const [editingIndex, setEditingIndex] = React.useState(null); + const inputRef = React.useRef(null); - const addChip = React.useCallback( - (chip: string) => { - const trimmed = chip.trim(); - if (!trimmed) return; - const newChips = Array.from(new Set([...chips, trimmed])); - setChips(newChips); - }, - [chips, setChips], - ); + const addChip = React.useCallback( + (chip: string) => { + const trimmed = chip.trim(); + if (!trimmed) return; + const newChips = Array.from(new Set([...chips, trimmed])); + setChips(newChips); + }, + [chips, setChips], + ); - const updateChip = React.useCallback( - (index: number, newValue: string) => { - const trimmed = newValue.trim(); - if (!trimmed || index < 0 || index >= chips.length) return; + const updateChip = React.useCallback( + (index: number, newValue: string) => { + const trimmed = newValue.trim(); + if (!trimmed || index < 0 || index >= chips.length) return; - const existingIndex = chips.findIndex((c, i) => c === trimmed && i !== index); - if (existingIndex !== -1) return; + const existingIndex = chips.findIndex((c, i) => c === trimmed && i !== index); + if (existingIndex !== -1) return; - const newChips = [...chips]; - newChips[index] = trimmed; - setChips(newChips); - }, - [chips, setChips], - ); + const newChips = [...chips]; + newChips[index] = trimmed; + setChips(newChips); + }, + [chips, setChips], + ); - const removeChip = React.useCallback( - (index: number) => { - if (index < 0 || index >= chips.length) return; - const newChips = chips.slice(0, index).concat(chips.slice(index + 1)); - setChips(newChips); + const removeChip = React.useCallback( + (index: number) => { + if (index < 0 || index >= chips.length) return; + const newChips = chips.slice(0, index).concat(chips.slice(index + 1)); + setChips(newChips); - if (editingIndex === index) { - setEditingIndex(null); - setInput(""); - } else if (editingIndex !== null && editingIndex > index) { - setEditingIndex(editingIndex - 1); - } - }, - [chips, setChips, editingIndex], - ); + if (editingIndex === index) { + setEditingIndex(null); + setInput(""); + } else if (editingIndex !== null && editingIndex > index) { + setEditingIndex(editingIndex - 1); + } + }, + [chips, setChips, editingIndex], + ); - const handleEdit = React.useCallback( - (index: number) => { - setEditingIndex(index); - setInput(chips[index]); - inputRef.current?.focus(); - }, - [chips], - ); + const handleEdit = React.useCallback( + (index: number) => { + setEditingIndex(index); + setInput(chips[index]); + inputRef.current?.focus(); + }, + [chips], + ); - const handleReorder = React.useCallback( - (newOrder: string[]) => { - if (editingIndex !== null) { - const editingChip = chips[editingIndex]; - const newIndex = newOrder.indexOf(editingChip); - if (newIndex !== -1 && newIndex !== editingIndex) { - setEditingIndex(newIndex); - } - } - setChips(newOrder); - }, - [chips, editingIndex, setChips], - ); + const handleReorder = React.useCallback( + (newOrder: string[]) => { + if (editingIndex !== null) { + const editingChip = chips[editingIndex]; + const newIndex = newOrder.indexOf(editingChip); + if (newIndex !== -1 && newIndex !== editingIndex) { + setEditingIndex(newIndex); + } + } + setChips(newOrder); + }, + [chips, editingIndex, setChips], + ); - const sensors = useSensors( - useSensor(PointerSensor, { - activationConstraint: { distance: 3 }, - }), - useSensor(KeyboardSensor, { - coordinateGetter: sortableKeyboardCoordinates, - }), - ); + const sensors = useSensors( + useSensor(PointerSensor, { + activationConstraint: { distance: 3 }, + }), + useSensor(KeyboardSensor, { + coordinateGetter: sortableKeyboardCoordinates, + }), + ); - const handleDragEnd = React.useCallback( - (event: DragEndEvent) => { - const { active, over } = event; - if (!over || active.id === over.id) return; - const oldIndex = chips.indexOf(active.id as string); - const newIndex = chips.indexOf(over.id as string); - if (oldIndex !== -1 && newIndex !== -1 && oldIndex !== newIndex) { - const newOrder = Array.from(chips); - const [removed] = newOrder.splice(oldIndex, 1); - newOrder.splice(newIndex, 0, removed); - handleReorder(newOrder); - } - }, - [chips, handleReorder], - ); + const handleDragEnd = React.useCallback( + (event: DragEndEvent) => { + const { active, over } = event; + if (!over || active.id === over.id) return; + const oldIndex = chips.indexOf(active.id as string); + const newIndex = chips.indexOf(over.id as string); + if (oldIndex !== -1 && newIndex !== -1 && oldIndex !== newIndex) { + const newOrder = Array.from(chips); + const [removed] = newOrder.splice(oldIndex, 1); + newOrder.splice(newIndex, 0, removed); + handleReorder(newOrder); + } + }, + [chips, handleReorder], + ); - const handleInputChange = React.useCallback( - (e: React.ChangeEvent) => { - const newValue = e.target.value; + const handleInputChange = React.useCallback( + (e: React.ChangeEvent) => { + const newValue = e.target.value; - if (editingIndex !== null) { - if (newValue.includes(",")) { - updateChip(editingIndex, newValue.replace(",", "")); - setEditingIndex(null); - setInput(""); - } else { - setInput(newValue); - } - return; - } + if (editingIndex !== null) { + if (newValue.includes(",")) { + updateChip(editingIndex, newValue.replace(",", "")); + setEditingIndex(null); + setInput(""); + } else { + setInput(newValue); + } + return; + } - if (newValue.includes(",")) { - const parts = newValue.split(","); - parts.slice(0, -1).forEach(addChip); - setInput(parts[parts.length - 1]); - } else { - setInput(newValue); - } - }, - [addChip, editingIndex, updateChip], - ); + if (newValue.includes(",")) { + const parts = newValue.split(","); + parts.slice(0, -1).forEach(addChip); + setInput(parts[parts.length - 1]); + } else { + setInput(newValue); + } + }, + [addChip, editingIndex, updateChip], + ); - const handleKeyDown = React.useCallback( - (e: React.KeyboardEvent) => { - if (e.key === "Enter" || e.key === ",") { - e.preventDefault(); + const handleKeyDown = React.useCallback( + (e: React.KeyboardEvent) => { + if (e.key === "Enter" || e.key === ",") { + e.preventDefault(); - if (editingIndex !== null) { - if (input.trim()) { - updateChip(editingIndex, input); - } - setEditingIndex(null); - setInput(""); - } else if (input.trim()) { - addChip(input); - setInput(""); - } - } else if (e.key === "Escape" && editingIndex !== null) { - setEditingIndex(null); - setInput(""); - } - }, - [input, addChip, editingIndex, updateChip], - ); + if (editingIndex !== null) { + if (input.trim()) { + updateChip(editingIndex, input); + } + setEditingIndex(null); + setInput(""); + } else if (input.trim()) { + addChip(input); + setInput(""); + } + } else if (e.key === "Escape" && editingIndex !== null) { + setEditingIndex(null); + setInput(""); + } + }, + [input, addChip, editingIndex, updateChip], + ); - return ( -
- {chips.length > 0 && ( - - -
- {chips.map((chip, idx) => ( - - ))} -
-
-
- )} + return ( +
+ {chips.length > 0 && ( + + +
+ {chips.map((chip, idx) => ( + + ))} +
+
+
+ )} -
- -

- - Press {RETURN_KEY} or {COMMA_KEY} to add or save the current keyword. - -

-
-
- ); +
+ +

+ + Press {RETURN_KEY} or {COMMA_KEY} to add or save the current keyword. + +

+
+
+ ); } diff --git a/src/components/input/color-picker.tsx b/src/components/input/color-picker.tsx index 869a8effb..4d4ba3ad2 100644 --- a/src/components/input/color-picker.tsx +++ b/src/components/input/color-picker.tsx @@ -7,37 +7,37 @@ import { useControlledState } from "@/hooks/use-controlled-state"; import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover"; type ColorPickerProps = { - value?: string; - defaultValue?: string; - onChange?: (value: string) => void; + value?: string; + defaultValue?: string; + onChange?: (value: string) => void; }; export function ColorPicker({ value, defaultValue, onChange }: ColorPickerProps) { - const [currentValue, setCurrentValue] = useControlledState({ - value, - defaultValue, - onChange, - }); + const [currentValue, setCurrentValue] = useControlledState({ + value, + defaultValue, + onChange, + }); - const color = useMemo(() => rgbaStringToHsva(currentValue), [currentValue]); + const color = useMemo(() => rgbaStringToHsva(currentValue), [currentValue]); - function onColorChange(color: ColorResult) { - const rgbaString = hsvaToRgbaString(color.hsva); - setCurrentValue(rgbaString); - } + function onColorChange(color: ColorResult) { + const rgbaString = hsvaToRgbaString(color.hsva); + setCurrentValue(rgbaString); + } - return ( - - -
- + return ( + + +
+ - - - - - ); + + + + + ); } diff --git a/src/components/input/github-stars-button.tsx b/src/components/input/github-stars-button.tsx index 157768ba9..dd1e35233 100644 --- a/src/components/input/github-stars-button.tsx +++ b/src/components/input/github-stars-button.tsx @@ -8,26 +8,26 @@ import { orpc } from "@/integrations/orpc/client"; import { CountUp } from "../animation/count-up"; export function GithubStarsButton() { - const { data: starCount } = useQuery(orpc.statistics.github.getStarCount.queryOptions()); + const { data: starCount } = useQuery(orpc.statistics.github.getStarCount.queryOptions()); - const ariaLabel = - starCount != null - ? t`Star us on GitHub, currently ${starCount.toLocaleString()} stars (opens in new tab)` - : t`Star us on GitHub (opens in new tab)`; + const ariaLabel = + starCount != null + ? t`Star us on GitHub, currently ${starCount.toLocaleString()} stars (opens in new tab)` + : t`Star us on GitHub (opens in new tab)`; - return ( - - ); + return ( + + ); } function useIconSearch() { - const fuse = useMemo(() => new Fuse(icons, { threshold: 0.35 }), []); + const fuse = useMemo(() => new Fuse(icons, { threshold: 0.35 }), []); - const search = useCallback( - (query: string): IconName[] => { - if (!query.trim()) return Array.from(icons); - return fuse.search(query).map((result) => result.item); - }, - [fuse], - ); + const search = useCallback( + (query: string): IconName[] => { + if (!query.trim()) return Array.from(icons); + return fuse.search(query).map((result) => result.item); + }, + [fuse], + ); - return search; + return search; } type IconPickerProps = Omit, "value" | "onChange"> & { - value: string; - onChange: (icon: string) => void; - popoverProps?: React.ComponentProps; + value: string; + onChange: (icon: string) => void; + popoverProps?: React.ComponentProps; }; export function IconPicker({ value, onChange, popoverProps, ...props }: IconPickerProps) { - const searchIcons = useIconSearch(); + const searchIcons = useIconSearch(); - const [search, setSearch] = useState(""); + const [search, setSearch] = useState(""); - const searchedIcons = useMemo(() => searchIcons(search), [search, searchIcons]); - const rowCount = useMemo(() => Math.ceil(searchedIcons.length / columnCount), [searchedIcons]); + const searchedIcons = useMemo(() => searchIcons(search), [search, searchIcons]); + const rowCount = useMemo(() => Math.ceil(searchedIcons.length / columnCount), [searchedIcons]); - return ( - - - - - } - /> + return ( + + + + + } + /> - - + + -
- -
-
-
- ); +
+ +
+ +
+ ); } diff --git a/src/components/input/rich-input.module.css b/src/components/input/rich-input.module.css index 75a539d85..39a210b75 100644 --- a/src/components/input/rich-input.module.css +++ b/src/components/input/rich-input.module.css @@ -1,178 +1,178 @@ .tiptap_content { - --list-margin: 0.25rem; - --table-border-color: var(--page-text-color); - word-wrap: break-word; - overflow-wrap: break-word; + --list-margin: 0.25rem; + --table-border-color: var(--page-text-color); + word-wrap: break-word; + overflow-wrap: break-word; - :where(p) { - margin-top: 0.5rem; - margin-bottom: 0.5rem; - } + :where(p) { + margin-top: 0.5rem; + margin-bottom: 0.5rem; + } - :where(p):first-child { - margin-top: 0; - } + :where(p):first-child { + margin-top: 0; + } - :where(p):last-child { - margin-bottom: 0; - } + :where(p):last-child { + margin-bottom: 0; + } - :where(ul, ol) { - margin-top: var(--list-margin); - margin-bottom: var(--list-margin); - list-style-position: inside; - } + :where(ul, ol) { + margin-top: var(--list-margin); + margin-bottom: var(--list-margin); + list-style-position: inside; + } - :where(ul) { - list-style-type: disc; - } + :where(ul) { + list-style-type: disc; + } - :where(ol) { - list-style-type: decimal; - } + :where(ol) { + list-style-type: decimal; + } - :where(li) { - margin-inline-start: 1rem; - margin-top: var(--list-margin); - margin-bottom: var(--list-margin); - list-style-position: outside; - } + :where(li) { + margin-inline-start: 1rem; + margin-top: var(--list-margin); + margin-bottom: var(--list-margin); + list-style-position: outside; + } - :where(li ul li, li ol li) { - margin-block: var(--list-margin); - margin-inline: 1rem; - } + :where(li ul li, li ol li) { + margin-block: var(--list-margin); + margin-inline: 1rem; + } - :where(mark) { - color: var(--page-background-color); - background-color: var(--page-primary-color); - } + :where(mark) { + color: var(--page-background-color); + background-color: var(--page-primary-color); + } - :where(table) { - width: 100%; + :where(table) { + width: 100%; - border-top: 1px solid var(--table-border-color); - border-left: 1px solid var(--table-border-color); - } + border-top: 1px solid var(--table-border-color); + border-left: 1px solid var(--table-border-color); + } - :where(th) { - font-weight: inherit; - text-align: start; - } + :where(th) { + font-weight: inherit; + text-align: start; + } - :where(th, td) { - padding: 0.5rem; - border-right: 1px solid var(--table-border-color); - border-bottom: 1px solid var(--table-border-color); - } + :where(th, td) { + padding: 0.5rem; + border-right: 1px solid var(--table-border-color); + border-bottom: 1px solid var(--table-border-color); + } - /* RTL Support: Apply RTL direction when text is right-aligned */ - p[style*="text-align: right"], - h1[style*="text-align: right"], - h2[style*="text-align: right"], - h3[style*="text-align: right"], - h4[style*="text-align: right"], - h5[style*="text-align: right"], - h6[style*="text-align: right"], - li[style*="text-align: right"], - ul[style*="text-align: right"], - ol[style*="text-align: right"] { - direction: rtl; - word-wrap: break-word; - overflow-wrap: break-word; - white-space: normal; - } + /* RTL Support: Apply RTL direction when text is right-aligned */ + p[style*="text-align: right"], + h1[style*="text-align: right"], + h2[style*="text-align: right"], + h3[style*="text-align: right"], + h4[style*="text-align: right"], + h5[style*="text-align: right"], + h6[style*="text-align: right"], + li[style*="text-align: right"], + ul[style*="text-align: right"], + ol[style*="text-align: right"] { + direction: rtl; + word-wrap: break-word; + overflow-wrap: break-word; + white-space: normal; + } - /* LTR Support: Apply LTR direction when text is left-aligned */ - p[style*="text-align: left"], - h1[style*="text-align: left"], - h2[style*="text-align: left"], - h3[style*="text-align: left"], - h4[style*="text-align: left"], - h5[style*="text-align: left"], - h6[style*="text-align: left"], - li[style*="text-align: left"], - ul[style*="text-align: left"], - ol[style*="text-align: left"] { - direction: ltr; - word-wrap: break-word; - overflow-wrap: break-word; - white-space: normal; - } + /* LTR Support: Apply LTR direction when text is left-aligned */ + p[style*="text-align: left"], + h1[style*="text-align: left"], + h2[style*="text-align: left"], + h3[style*="text-align: left"], + h4[style*="text-align: left"], + h5[style*="text-align: left"], + h6[style*="text-align: left"], + li[style*="text-align: left"], + ul[style*="text-align: left"], + ol[style*="text-align: left"] { + direction: ltr; + word-wrap: break-word; + overflow-wrap: break-word; + white-space: normal; + } - /* When list items are RTL, make bullets/numbers appear on the right */ - li[style*="text-align: right"] { - text-align: right; - } + /* When list items are RTL, make bullets/numbers appear on the right */ + li[style*="text-align: right"] { + text-align: right; + } - /* Parent list inherits direction from its items */ - ul:has(li[style*="text-align: right"]), - ol:has(li[style*="text-align: right"]) { - direction: rtl; - } + /* Parent list inherits direction from its items */ + ul:has(li[style*="text-align: right"]), + ol:has(li[style*="text-align: right"]) { + direction: rtl; + } - ul:has(li[style*="text-align: left"]), - ol:has(li[style*="text-align: left"]) { - direction: ltr; - } + ul:has(li[style*="text-align: left"]), + ol:has(li[style*="text-align: left"]) { + direction: ltr; + } } .editor_content { - font-family: var(--page-body-font-family); - font-size: calc(var(--page-body-font-size) * 1pt); - font-weight: var(--page-body-font-weight); - line-height: var(--page-body-line-height); - color: var(--page-text-color); - word-wrap: break-word; - overflow-wrap: break-word; + font-family: var(--page-body-font-family); + font-size: calc(var(--page-body-font-size) * 1pt); + font-weight: var(--page-body-font-weight); + line-height: var(--page-body-line-height); + color: var(--page-text-color); + word-wrap: break-word; + overflow-wrap: break-word; - [class*="dark"] & { - --table-border-color: var(--color-foreground); - color: var(--color-foreground); - } + [class*="dark"] & { + --table-border-color: var(--color-foreground); + color: var(--color-foreground); + } - h1, - h2, - h3, - h4, - h5, - h6 { - font-family: var(--page-heading-font-family); - font-weight: var(--page-heading-font-weight); - line-height: var(--page-heading-line-height); - } + h1, + h2, + h3, + h4, + h5, + h6 { + font-family: var(--page-heading-font-family); + font-weight: var(--page-heading-font-weight); + line-height: var(--page-heading-line-height); + } - h1 { - font-size: calc(var(--page-heading-font-size) * 1.5pt); - } + h1 { + font-size: calc(var(--page-heading-font-size) * 1.5pt); + } - h2 { - font-size: calc(var(--page-heading-font-size) * 1.25pt); - } + h2 { + font-size: calc(var(--page-heading-font-size) * 1.25pt); + } - h3 { - font-size: calc(var(--page-heading-font-size) * 1.125pt); - } + h3 { + font-size: calc(var(--page-heading-font-size) * 1.125pt); + } - h4 { - font-size: calc(var(--page-heading-font-size) * 1pt); - } + h4 { + font-size: calc(var(--page-heading-font-size) * 1pt); + } - h5 { - font-size: calc(var(--page-heading-font-size) * 0.875pt); - } + h5 { + font-size: calc(var(--page-heading-font-size) * 0.875pt); + } - h6 { - font-size: calc(var(--page-heading-font-size) * 0.75pt); - } + h6 { + font-size: calc(var(--page-heading-font-size) * 0.75pt); + } - strong { - font-weight: var(--page-body-font-weight-bold, bold); - } + strong { + font-weight: var(--page-body-font-weight-bold, bold); + } - a { - font-weight: var(--page-body-font-weight-bold, bold); - text-decoration: underline; - text-underline-offset: 0.15rem; - } + a { + font-weight: var(--page-body-font-weight-bold, bold); + text-decoration: underline; + text-underline-offset: 0.15rem; + } } diff --git a/src/components/input/rich-input.tsx b/src/components/input/rich-input.tsx index a4d9c5555..7c02a8c28 100644 --- a/src/components/input/rich-input.tsx +++ b/src/components/input/rich-input.tsx @@ -2,52 +2,52 @@ import { t } from "@lingui/core/macro"; import { useLingui } from "@lingui/react"; import { Trans } from "@lingui/react/macro"; import { - ArrowsInSimpleIcon, - ArrowsOutSimpleIcon, - CodeBlockIcon, - CodeSimpleIcon, - ColumnsPlusLeftIcon, - ColumnsPlusRightIcon, - HighlighterCircleIcon, - KeyReturnIcon, - LinkBreakIcon, - LinkIcon, - ListBulletsIcon, - ListNumbersIcon, - MinusIcon, - ParagraphIcon, - PlusIcon, - RowsPlusBottomIcon, - RowsPlusTopIcon, - TableIcon, - TextAlignCenterIcon, - TextAlignJustifyIcon, - TextAlignLeftIcon, - TextAlignRightIcon, - TextBolderIcon, - TextHFiveIcon, - TextHFourIcon, - TextHOneIcon, - TextHSixIcon, - TextHThreeIcon, - TextHTwoIcon, - TextIndentIcon, - TextItalicIcon, - TextOutdentIcon, - TextStrikethroughIcon, - TextUnderlineIcon, - TrashSimpleIcon, + ArrowsInSimpleIcon, + ArrowsOutSimpleIcon, + CodeBlockIcon, + CodeSimpleIcon, + ColumnsPlusLeftIcon, + ColumnsPlusRightIcon, + HighlighterCircleIcon, + KeyReturnIcon, + LinkBreakIcon, + LinkIcon, + ListBulletsIcon, + ListNumbersIcon, + MinusIcon, + ParagraphIcon, + PlusIcon, + RowsPlusBottomIcon, + RowsPlusTopIcon, + TableIcon, + TextAlignCenterIcon, + TextAlignJustifyIcon, + TextAlignLeftIcon, + TextAlignRightIcon, + TextBolderIcon, + TextHFiveIcon, + TextHFourIcon, + TextHOneIcon, + TextHSixIcon, + TextHThreeIcon, + TextHTwoIcon, + TextIndentIcon, + TextItalicIcon, + TextOutdentIcon, + TextStrikethroughIcon, + TextUnderlineIcon, + TrashSimpleIcon, } from "@phosphor-icons/react"; import Highlight from "@tiptap/extension-highlight"; import { TableKit } from "@tiptap/extension-table"; import TextAlign from "@tiptap/extension-text-align"; import { - type Editor, - EditorContent, - EditorContext, - type UseEditorOptions, - useEditor, - useEditorState, + type Editor, + EditorContent, + EditorContext, + type UseEditorOptions, + useEditor, + useEditorState, } from "@tiptap/react"; import StarterKit from "@tiptap/starter-kit"; import { useMemo, useState } from "react"; @@ -58,12 +58,12 @@ import z from "zod"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogTitle } from "@/components/ui/dialog"; import { - DropdownMenu, - DropdownMenuCheckboxItem, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuSeparator, - DropdownMenuTrigger, + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { usePrompt } from "@/hooks/use-prompt"; import { isRTL } from "@/utils/locale"; @@ -74,661 +74,661 @@ import { Toggle } from "../ui/toggle"; import styles from "./rich-input.module.css"; const extensions = [ - StarterKit.configure({ - heading: { - levels: [1, 2, 3, 4, 5, 6], - }, - codeBlock: { - enableTabIndentation: true, - }, - link: { - openOnClick: false, - enableClickSelection: true, - defaultProtocol: "https", - protocols: ["http", "https"], - }, - }), - Highlight.configure({ - HTMLAttributes: { - class: "rounded-md px-0.5 py-px", - }, - }), - TextAlign.configure({ types: ["heading", "paragraph", "listItem"] }), - TableKit.configure(), + StarterKit.configure({ + heading: { + levels: [1, 2, 3, 4, 5, 6], + }, + codeBlock: { + enableTabIndentation: true, + }, + link: { + openOnClick: false, + enableClickSelection: true, + defaultProtocol: "https", + protocols: ["http", "https"], + }, + }), + Highlight.configure({ + HTMLAttributes: { + class: "rounded-md px-0.5 py-px", + }, + }), + TextAlign.configure({ types: ["heading", "paragraph", "listItem"] }), + TableKit.configure(), ]; type Props = UseEditorOptions & { - value: string; - onChange: (value: string) => void; - style?: React.CSSProperties; - className?: string; - editorClassName?: string; + value: string; + onChange: (value: string) => void; + style?: React.CSSProperties; + className?: string; + editorClassName?: string; }; export function RichInput({ value, onChange, style, className, editorClassName, ...options }: Props) { - const { i18n } = useLingui(); - const textDirection = isRTL(i18n.locale) ? "rtl" : undefined; - const [isFullscreen, setIsFullscreen] = useState(false); + const { i18n } = useLingui(); + const textDirection = isRTL(i18n.locale) ? "rtl" : undefined; + const [isFullscreen, setIsFullscreen] = useState(false); - const editor = useEditor({ - ...options, - extensions, - textDirection, - content: value, - immediatelyRender: false, - shouldRerenderOnTransaction: false, - editorProps: { - attributes: { - spellcheck: "false", - "data-editor": "true", - "data-fullscreen": isFullscreen ? "true" : "false", - class: cn( - "group/editor overflow-y-auto p-3 pb-4", - "rounded-md rounded-t-none border outline-none focus-visible:border-ring", - "[td:has(.selectedCell)]:bg-primary", - "data-[fullscreen=false]:max-h-[400px] data-[fullscreen=false]:min-h-[100px]", - "data-[fullscreen=true]:max-h-none data-[fullscreen=true]:min-h-full", - styles.tiptap_content, - styles.editor_content, - editorClassName, - ), - }, - }, - onUpdate: ({ editor }) => { - onChange(editor.getHTML()); - }, - }); + const editor = useEditor({ + ...options, + extensions, + textDirection, + content: value, + immediatelyRender: false, + shouldRerenderOnTransaction: false, + editorProps: { + attributes: { + spellcheck: "false", + "data-editor": "true", + "data-fullscreen": isFullscreen ? "true" : "false", + class: cn( + "group/editor overflow-y-auto p-3 pb-4", + "rounded-md rounded-t-none border outline-none focus-visible:border-ring", + "[td:has(.selectedCell)]:bg-primary", + "data-[fullscreen=false]:max-h-[400px] data-[fullscreen=false]:min-h-[100px]", + "data-[fullscreen=true]:max-h-none data-[fullscreen=true]:min-h-full", + styles.tiptap_content, + styles.editor_content, + editorClassName, + ), + }, + }, + onUpdate: ({ editor }) => { + onChange(editor.getHTML()); + }, + }); - const providerValue = useMemo(() => ({ editor }), [editor]); + const providerValue = useMemo(() => ({ editor }), [editor]); - if (!editor) return null; + if (!editor) return null; - const editorElement = ( -
- + const editorElement = ( +
+ - + - -
- ); + +
+ ); - if (isFullscreen) { - return ( - -
- {/* Placeholder to maintain layout */} -
-
+ if (isFullscreen) { + return ( + +
+ {/* Placeholder to maintain layout */} +
+
- - -
- Fullscreen Editor - Edit content in fullscreen mode -
- {editorElement} -
-
- - ); - } + + +
+ Fullscreen Editor + Edit content in fullscreen mode +
+ {editorElement} +
+
+ + ); + } - return ( - -
- {editorElement} -
-
- ); + return ( + +
+ {editorElement} +
+
+ ); } function EditorToolbar({ editor, isFullscreen }: { editor: Editor; isFullscreen: boolean }) { - const prompt = usePrompt(); + const prompt = usePrompt(); - const state = useEditorState({ - editor, - selector: (ctx) => { - return { - // Bold - isBold: ctx.editor.isActive("bold") ?? false, - canBold: ctx.editor.can().chain().toggleBold().run() ?? false, - toggleBold: () => ctx.editor.chain().focus().toggleBold().run(), + const state = useEditorState({ + editor, + selector: (ctx) => { + return { + // Bold + isBold: ctx.editor.isActive("bold") ?? false, + canBold: ctx.editor.can().chain().toggleBold().run() ?? false, + toggleBold: () => ctx.editor.chain().focus().toggleBold().run(), - // Italic - isItalic: ctx.editor.isActive("italic") ?? false, - canItalic: ctx.editor.can().chain().toggleItalic().run() ?? false, - toggleItalic: () => ctx.editor.chain().focus().toggleItalic().run(), + // Italic + isItalic: ctx.editor.isActive("italic") ?? false, + canItalic: ctx.editor.can().chain().toggleItalic().run() ?? false, + toggleItalic: () => ctx.editor.chain().focus().toggleItalic().run(), - // Underline - isUnderline: ctx.editor.isActive("underline") ?? false, - canUnderline: ctx.editor.can().chain().toggleUnderline().run() ?? false, - toggleUnderline: () => ctx.editor.chain().focus().toggleUnderline().run(), + // Underline + isUnderline: ctx.editor.isActive("underline") ?? false, + canUnderline: ctx.editor.can().chain().toggleUnderline().run() ?? false, + toggleUnderline: () => ctx.editor.chain().focus().toggleUnderline().run(), - // Strike - isStrike: ctx.editor.isActive("strike") ?? false, - canStrike: ctx.editor.can().chain().toggleStrike().run() ?? false, - toggleStrike: () => ctx.editor.chain().focus().toggleStrike().run(), + // Strike + isStrike: ctx.editor.isActive("strike") ?? false, + canStrike: ctx.editor.can().chain().toggleStrike().run() ?? false, + toggleStrike: () => ctx.editor.chain().focus().toggleStrike().run(), - // Highlight - isHighlight: ctx.editor.isActive("highlight") ?? false, - canHighlight: ctx.editor.can().chain().toggleHighlight().run() ?? false, - toggleHighlight: () => ctx.editor.chain().focus().toggleHighlight().run(), + // Highlight + isHighlight: ctx.editor.isActive("highlight") ?? false, + canHighlight: ctx.editor.can().chain().toggleHighlight().run() ?? false, + toggleHighlight: () => ctx.editor.chain().focus().toggleHighlight().run(), - // Heading 1 - isHeading1: ctx.editor.isActive("heading", { level: 1 }) ?? false, - canHeading1: ctx.editor.can().chain().toggleHeading({ level: 1 }).run() ?? false, - toggleHeading1: () => ctx.editor.chain().focus().toggleHeading({ level: 1 }).run(), + // Heading 1 + isHeading1: ctx.editor.isActive("heading", { level: 1 }) ?? false, + canHeading1: ctx.editor.can().chain().toggleHeading({ level: 1 }).run() ?? false, + toggleHeading1: () => ctx.editor.chain().focus().toggleHeading({ level: 1 }).run(), - // Heading 2 - isHeading2: ctx.editor.isActive("heading", { level: 2 }) ?? false, - canHeading2: ctx.editor.can().chain().toggleHeading({ level: 2 }).run() ?? false, - toggleHeading2: () => ctx.editor.chain().focus().toggleHeading({ level: 2 }).run(), + // Heading 2 + isHeading2: ctx.editor.isActive("heading", { level: 2 }) ?? false, + canHeading2: ctx.editor.can().chain().toggleHeading({ level: 2 }).run() ?? false, + toggleHeading2: () => ctx.editor.chain().focus().toggleHeading({ level: 2 }).run(), - // Heading 3 - isHeading3: ctx.editor.isActive("heading", { level: 3 }) ?? false, - canHeading3: ctx.editor.can().chain().toggleHeading({ level: 3 }).run() ?? false, - toggleHeading3: () => ctx.editor.chain().focus().toggleHeading({ level: 3 }).run(), + // Heading 3 + isHeading3: ctx.editor.isActive("heading", { level: 3 }) ?? false, + canHeading3: ctx.editor.can().chain().toggleHeading({ level: 3 }).run() ?? false, + toggleHeading3: () => ctx.editor.chain().focus().toggleHeading({ level: 3 }).run(), - // Heading 4 - isHeading4: ctx.editor.isActive("heading", { level: 4 }) ?? false, - canHeading4: ctx.editor.can().chain().toggleHeading({ level: 4 }).run() ?? false, - toggleHeading4: () => ctx.editor.chain().focus().toggleHeading({ level: 4 }).run(), + // Heading 4 + isHeading4: ctx.editor.isActive("heading", { level: 4 }) ?? false, + canHeading4: ctx.editor.can().chain().toggleHeading({ level: 4 }).run() ?? false, + toggleHeading4: () => ctx.editor.chain().focus().toggleHeading({ level: 4 }).run(), - // Heading 5 - isHeading5: ctx.editor.isActive("heading", { level: 5 }) ?? false, - canHeading5: ctx.editor.can().chain().toggleHeading({ level: 5 }).run() ?? false, - toggleHeading5: () => ctx.editor.chain().focus().toggleHeading({ level: 5 }).run(), + // Heading 5 + isHeading5: ctx.editor.isActive("heading", { level: 5 }) ?? false, + canHeading5: ctx.editor.can().chain().toggleHeading({ level: 5 }).run() ?? false, + toggleHeading5: () => ctx.editor.chain().focus().toggleHeading({ level: 5 }).run(), - // Heading 6 - isHeading6: ctx.editor.isActive("heading", { level: 6 }) ?? false, - canHeading6: ctx.editor.can().chain().toggleHeading({ level: 6 }).run() ?? false, - toggleHeading6: () => ctx.editor.chain().focus().toggleHeading({ level: 6 }).run(), + // Heading 6 + isHeading6: ctx.editor.isActive("heading", { level: 6 }) ?? false, + canHeading6: ctx.editor.can().chain().toggleHeading({ level: 6 }).run() ?? false, + toggleHeading6: () => ctx.editor.chain().focus().toggleHeading({ level: 6 }).run(), - // Paragraph - isParagraph: ctx.editor.isActive("paragraph") ?? false, - canParagraph: ctx.editor.can().chain().setParagraph().run() ?? false, - setParagraph: () => ctx.editor.chain().focus().setParagraph().run(), + // Paragraph + isParagraph: ctx.editor.isActive("paragraph") ?? false, + canParagraph: ctx.editor.can().chain().setParagraph().run() ?? false, + setParagraph: () => ctx.editor.chain().focus().setParagraph().run(), - // Left Align - isLeftAlign: ctx.editor.isActive({ textAlign: "left" }) ?? false, - canLeftAlign: ctx.editor.can().chain().toggleTextAlign("left").run() ?? false, - toggleLeftAlign: () => ctx.editor.chain().focus().toggleTextAlign("left").run(), + // Left Align + isLeftAlign: ctx.editor.isActive({ textAlign: "left" }) ?? false, + canLeftAlign: ctx.editor.can().chain().toggleTextAlign("left").run() ?? false, + toggleLeftAlign: () => ctx.editor.chain().focus().toggleTextAlign("left").run(), - // Center Align - isCenterAlign: ctx.editor.isActive({ textAlign: "center" }) ?? false, - canCenterAlign: ctx.editor.can().chain().toggleTextAlign("center").run() ?? false, - toggleCenterAlign: () => ctx.editor.chain().focus().toggleTextAlign("center").run(), + // Center Align + isCenterAlign: ctx.editor.isActive({ textAlign: "center" }) ?? false, + canCenterAlign: ctx.editor.can().chain().toggleTextAlign("center").run() ?? false, + toggleCenterAlign: () => ctx.editor.chain().focus().toggleTextAlign("center").run(), - // Right Align - isRightAlign: ctx.editor.isActive({ textAlign: "right" }) ?? false, - canRightAlign: ctx.editor.can().chain().toggleTextAlign("right").run() ?? false, - toggleRightAlign: () => ctx.editor.chain().focus().toggleTextAlign("right").run(), + // Right Align + isRightAlign: ctx.editor.isActive({ textAlign: "right" }) ?? false, + canRightAlign: ctx.editor.can().chain().toggleTextAlign("right").run() ?? false, + toggleRightAlign: () => ctx.editor.chain().focus().toggleTextAlign("right").run(), - // Justify Align - isJustifyAlign: ctx.editor.isActive({ textAlign: "justify" }) ?? false, - canJustifyAlign: ctx.editor.can().chain().toggleTextAlign("justify").run() ?? false, - toggleJustifyAlign: () => ctx.editor.chain().focus().toggleTextAlign("justify").run(), + // Justify Align + isJustifyAlign: ctx.editor.isActive({ textAlign: "justify" }) ?? false, + canJustifyAlign: ctx.editor.can().chain().toggleTextAlign("justify").run() ?? false, + toggleJustifyAlign: () => ctx.editor.chain().focus().toggleTextAlign("justify").run(), - // Bullet List - isBulletList: ctx.editor.isActive("bulletList") ?? false, - canBulletList: ctx.editor.can().chain().toggleBulletList().run() ?? false, - toggleBulletList: () => ctx.editor.chain().focus().toggleBulletList().run(), + // Bullet List + isBulletList: ctx.editor.isActive("bulletList") ?? false, + canBulletList: ctx.editor.can().chain().toggleBulletList().run() ?? false, + toggleBulletList: () => ctx.editor.chain().focus().toggleBulletList().run(), - // Ordered List - isOrderedList: ctx.editor.isActive("orderedList") ?? false, - canOrderedList: ctx.editor.can().chain().toggleOrderedList().run() ?? false, - toggleOrderedList: () => ctx.editor.chain().focus().toggleOrderedList().run(), + // Ordered List + isOrderedList: ctx.editor.isActive("orderedList") ?? false, + canOrderedList: ctx.editor.can().chain().toggleOrderedList().run() ?? false, + toggleOrderedList: () => ctx.editor.chain().focus().toggleOrderedList().run(), - // Outdent List Item - canLiftListItem: ctx.editor.can().chain().liftListItem("listItem").run() ?? false, - liftListItem: () => ctx.editor.chain().focus().liftListItem("listItem").run(), + // Outdent List Item + canLiftListItem: ctx.editor.can().chain().liftListItem("listItem").run() ?? false, + liftListItem: () => ctx.editor.chain().focus().liftListItem("listItem").run(), - // Indent List Item - canSinkListItem: ctx.editor.can().chain().sinkListItem("listItem").run() ?? false, - sinkListItem: () => ctx.editor.chain().focus().sinkListItem("listItem").run(), + // Indent List Item + canSinkListItem: ctx.editor.can().chain().sinkListItem("listItem").run() ?? false, + sinkListItem: () => ctx.editor.chain().focus().sinkListItem("listItem").run(), - // Link - isLink: ctx.editor.isActive("link") ?? false, - setLink: async () => { - const url = await prompt(t`Please enter the URL you want to link to:`, { - defaultValue: "https://", - }); + // Link + isLink: ctx.editor.isActive("link") ?? false, + setLink: async () => { + const url = await prompt(t`Please enter the URL you want to link to:`, { + defaultValue: "https://", + }); - if (!url || url.trim() === "") { - ctx.editor.chain().focus().unsetLink().run(); - return; - } + if (!url || url.trim() === "") { + ctx.editor.chain().focus().unsetLink().run(); + return; + } - if (!z.url({ protocol: /^https?$/ }).safeParse(url).success) { - toast.error(t`The URL you entered is not valid.`, { - description: t`Valid URLs must start with http:// or https://.`, - }); - return; - } + if (!z.url({ protocol: /^https?$/ }).safeParse(url).success) { + toast.error(t`The URL you entered is not valid.`, { + description: t`Valid URLs must start with http:// or https://.`, + }); + return; + } - ctx.editor.chain().focus().setLink({ href: url, target: "_blank", rel: "noopener nofollow" }).run(); - }, - unsetLink: () => ctx.editor.chain().focus().unsetLink().run(), + ctx.editor.chain().focus().setLink({ href: url, target: "_blank", rel: "noopener nofollow" }).run(); + }, + unsetLink: () => ctx.editor.chain().focus().unsetLink().run(), - // Inline Code - isInlineCode: ctx.editor.isActive("code") ?? false, - canInlineCode: ctx.editor.can().chain().toggleCode().run() ?? false, - toggleInlineCode: () => ctx.editor.chain().focus().toggleCode().run(), + // Inline Code + isInlineCode: ctx.editor.isActive("code") ?? false, + canInlineCode: ctx.editor.can().chain().toggleCode().run() ?? false, + toggleInlineCode: () => ctx.editor.chain().focus().toggleCode().run(), - // Code Block - isCodeBlock: ctx.editor.isActive("codeBlock") ?? false, - canCodeBlock: ctx.editor.can().chain().toggleCodeBlock().run() ?? false, - toggleCodeBlock: () => ctx.editor.chain().focus().toggleCodeBlock().run(), + // Code Block + isCodeBlock: ctx.editor.isActive("codeBlock") ?? false, + canCodeBlock: ctx.editor.can().chain().toggleCodeBlock().run() ?? false, + toggleCodeBlock: () => ctx.editor.chain().focus().toggleCodeBlock().run(), - // Table - insertTable: () => ctx.editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run(), - canInsertTable: ctx.editor.can().chain().insertTable().run() ?? false, - addColumnBefore: () => ctx.editor.chain().focus().addColumnBefore().run(), - canAddColumnBefore: ctx.editor.can().chain().addColumnBefore().run() ?? false, - addColumnAfter: () => ctx.editor.chain().focus().addColumnAfter().run(), - canAddColumnAfter: ctx.editor.can().chain().addColumnAfter().run() ?? false, - addRowBefore: () => ctx.editor.chain().focus().addRowBefore().run(), - canAddRowBefore: ctx.editor.can().chain().addRowBefore().run() ?? false, - addRowAfter: () => ctx.editor.chain().focus().addRowAfter().run(), - canAddRowAfter: ctx.editor.can().chain().addRowAfter().run() ?? false, - deleteColumn: () => ctx.editor.chain().focus().deleteColumn().run(), - canDeleteColumn: ctx.editor.can().chain().deleteColumn().run() ?? false, - deleteRow: () => ctx.editor.chain().focus().deleteRow().run(), - canDeleteRow: ctx.editor.can().chain().deleteRow().run() ?? false, - deleteTable: () => ctx.editor.chain().focus().deleteTable().run(), - canDeleteTable: ctx.editor.can().chain().deleteTable().run() ?? false, + // Table + insertTable: () => ctx.editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run(), + canInsertTable: ctx.editor.can().chain().insertTable().run() ?? false, + addColumnBefore: () => ctx.editor.chain().focus().addColumnBefore().run(), + canAddColumnBefore: ctx.editor.can().chain().addColumnBefore().run() ?? false, + addColumnAfter: () => ctx.editor.chain().focus().addColumnAfter().run(), + canAddColumnAfter: ctx.editor.can().chain().addColumnAfter().run() ?? false, + addRowBefore: () => ctx.editor.chain().focus().addRowBefore().run(), + canAddRowBefore: ctx.editor.can().chain().addRowBefore().run() ?? false, + addRowAfter: () => ctx.editor.chain().focus().addRowAfter().run(), + canAddRowAfter: ctx.editor.can().chain().addRowAfter().run() ?? false, + deleteColumn: () => ctx.editor.chain().focus().deleteColumn().run(), + canDeleteColumn: ctx.editor.can().chain().deleteColumn().run() ?? false, + deleteRow: () => ctx.editor.chain().focus().deleteRow().run(), + canDeleteRow: ctx.editor.can().chain().deleteRow().run() ?? false, + deleteTable: () => ctx.editor.chain().focus().deleteTable().run(), + canDeleteTable: ctx.editor.can().chain().deleteTable().run() ?? false, - // Hard Break - setHardBreak: () => ctx.editor.chain().focus().setHardBreak().run(), + // Hard Break + setHardBreak: () => ctx.editor.chain().focus().setHardBreak().run(), - // Horizontal Rule - setHorizontalRule: () => ctx.editor.chain().focus().setHorizontalRule().run(), - }; - }, - }); + // Horizontal Rule + setHorizontalRule: () => ctx.editor.chain().focus().setHorizontalRule().run(), + }; + }, + }); - return ( -
- - - + return ( +
+ + + - - - + + + - - - + + + - - - + + + - - - + + + -
+
- - - {match(state) - .with({ isParagraph: true }, () => ) - .with({ isHeading1: true }, () => ) - .with({ isHeading2: true }, () => ) - .with({ isHeading3: true }, () => ) - .with({ isHeading4: true }, () => ) - .with({ isHeading5: true }, () => ) - .with({ isHeading6: true }, () => ) - .otherwise(() => ( - - ))} - - } - /> + + + {match(state) + .with({ isParagraph: true }, () => ) + .with({ isHeading1: true }, () => ) + .with({ isHeading2: true }, () => ) + .with({ isHeading3: true }, () => ) + .with({ isHeading4: true }, () => ) + .with({ isHeading5: true }, () => ) + .with({ isHeading6: true }, () => ) + .otherwise(() => ( + + ))} + + } + /> - - - Paragraph - - - - Heading 1 - - - Heading 2 - - - Heading 3 - - - Heading 4 - - - Heading 5 - - - Heading 6 - - - + + + Paragraph + + + + Heading 1 + + + Heading 2 + + + Heading 3 + + + Heading 4 + + + Heading 5 + + + Heading 6 + + + - - - {match(state) - .with({ isLeftAlign: true }, () => ) - .with({ isCenterAlign: true }, () => ) - .with({ isRightAlign: true }, () => ) - .with({ isJustifyAlign: true }, () => ) - .otherwise(() => ( - - ))} - - } - /> + + + {match(state) + .with({ isLeftAlign: true }, () => ) + .with({ isCenterAlign: true }, () => ) + .with({ isRightAlign: true }, () => ) + .with({ isJustifyAlign: true }, () => ) + .otherwise(() => ( + + ))} + + } + /> - - - Left Align - - - Center Align - - - Right Align - - - Justify Align - - - + + + Left Align + + + Center Align + + + Right Align + + + Justify Align + + + -
+
- - - + + + - - - + + + - + - + -
+
- {state.isLink ? ( - - ) : ( - - )} + {state.isLink ? ( + + ) : ( + + )} - - - + + + - - - + + + - - - - - } - /> + + + + + } + /> - - - - Insert Table - - - - - Add Column Before - - - - Add Column After - - - - - Add Row Before - - - - Add Row After - - - - - Delete Column - - - - Delete Row - - - - - Delete Table - - - + + + + Insert Table + + + + + Add Column Before + + + + Add Column After + + + + + Add Row Before + + + + Add Row After + + + + + Delete Column + + + + Delete Row + + + + + Delete Table + + + - + - -
- ); + +
+ ); } type TiptapContentProps = React.ComponentProps<"div"> & { - content: string; + content: string; }; export function TiptapContent({ content, className, ...props }: TiptapContentProps) { - const sanitizedContent = useMemo(() => sanitizeHtml(content), [content]); + const sanitizedContent = useMemo(() => sanitizeHtml(content), [content]); - return ( -
- ); + return ( +
+ ); } diff --git a/src/components/input/url-input.tsx b/src/components/input/url-input.tsx index 7d786ee2c..64ef6f175 100644 --- a/src/components/input/url-input.tsx +++ b/src/components/input/url-input.tsx @@ -17,75 +17,75 @@ import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover"; const PREFIX = "https://"; function stripPrefix(url: string) { - return url.startsWith(PREFIX) ? url.slice(PREFIX.length) : url; + return url.startsWith(PREFIX) ? url.slice(PREFIX.length) : url; } function ensurePrefix(url: string) { - if (url === "") return ""; - return url.startsWith(PREFIX) ? url : PREFIX + url; + if (url === "") return ""; + return url.startsWith(PREFIX) ? url : PREFIX + url; } type Props = Omit, "value" | "onChange"> & { - value: z.infer; - onChange: (value: z.infer) => void; - hideLabelButton?: boolean; + value: z.infer; + onChange: (value: z.infer) => void; + hideLabelButton?: boolean; }; export function URLInput({ value, onChange, hideLabelButton, ...props }: Props) { - const handleUrlChange = useCallback( - (e: React.ChangeEvent) => { - onChange({ - url: ensurePrefix(e.target.value), - label: value.label, - }); - }, - [onChange, value.label], - ); + const handleUrlChange = useCallback( + (e: React.ChangeEvent) => { + onChange({ + url: ensurePrefix(e.target.value), + label: value.label, + }); + }, + [onChange, value.label], + ); - const handleLabelChange = useCallback( - (e: React.ChangeEvent) => { - onChange({ url: value.url, label: e.target.value }); - }, - [onChange, value.url], - ); + const handleLabelChange = useCallback( + (e: React.ChangeEvent) => { + onChange({ url: value.url, label: e.target.value }); + }, + [onChange, value.url], + ); - const urlValue = useMemo(() => stripPrefix(value.url), [value.url]); + const urlValue = useMemo(() => stripPrefix(value.url), [value.url]); - return ( - - - {PREFIX} - + return ( + + + {PREFIX} + - + - {!hideLabelButton && ( - - - - - - } - /> + {!hideLabelButton && ( + + + + + + } + /> - -
e.stopPropagation()}> - - -
-
-
-
- )} -
- ); + +
e.stopPropagation()}> + + +
+
+ + + )} +
+ ); } diff --git a/src/components/input/visually-hidden-input.tsx b/src/components/input/visually-hidden-input.tsx index e8488fa40..eeaa0c5d5 100644 --- a/src/components/input/visually-hidden-input.tsx +++ b/src/components/input/visually-hidden-input.tsx @@ -3,136 +3,136 @@ import * as React from "react"; type InputValue = string[] | string; interface VisuallyHiddenInputProps extends Omit< - React.InputHTMLAttributes, - "value" | "checked" | "onReset" + React.InputHTMLAttributes, + "value" | "checked" | "onReset" > { - value?: T; - checked?: boolean; - control: HTMLElement | null; - bubbles?: boolean; + value?: T; + checked?: boolean; + control: HTMLElement | null; + bubbles?: boolean; } export function VisuallyHiddenInput(props: VisuallyHiddenInputProps) { - const { control, value, checked, bubbles = true, type = "hidden", style, ...inputProps } = props; + const { control, value, checked, bubbles = true, type = "hidden", style, ...inputProps } = props; - const isCheckInput = React.useMemo(() => type === "checkbox" || type === "radio" || type === "switch", [type]); - const inputRef = React.useRef(null); + const isCheckInput = React.useMemo(() => type === "checkbox" || type === "radio" || type === "switch", [type]); + const inputRef = React.useRef(null); - const prevValueRef = React.useRef<{ - value: T | boolean | undefined; - previous: T | boolean | undefined; - }>({ - value: isCheckInput ? checked : value, - previous: isCheckInput ? checked : value, - }); + const prevValueRef = React.useRef<{ + value: T | boolean | undefined; + previous: T | boolean | undefined; + }>({ + value: isCheckInput ? checked : value, + previous: isCheckInput ? checked : value, + }); - const prevValue = React.useMemo(() => { - const currentValue = isCheckInput ? checked : value; - if (prevValueRef.current.value !== currentValue) { - prevValueRef.current.previous = prevValueRef.current.value; - prevValueRef.current.value = currentValue; - } - return prevValueRef.current.previous; - }, [isCheckInput, value, checked]); + const prevValue = React.useMemo(() => { + const currentValue = isCheckInput ? checked : value; + if (prevValueRef.current.value !== currentValue) { + prevValueRef.current.previous = prevValueRef.current.value; + prevValueRef.current.value = currentValue; + } + return prevValueRef.current.previous; + }, [isCheckInput, value, checked]); - const [controlSize, setControlSize] = React.useState<{ - width?: number; - height?: number; - }>({}); + const [controlSize, setControlSize] = React.useState<{ + width?: number; + height?: number; + }>({}); - React.useLayoutEffect(() => { - if (!control) { - setControlSize({}); - return; - } + React.useLayoutEffect(() => { + if (!control) { + setControlSize({}); + return; + } - setControlSize({ - width: control.offsetWidth, - height: control.offsetHeight, - }); + setControlSize({ + width: control.offsetWidth, + height: control.offsetHeight, + }); - if (typeof window === "undefined") return; + if (typeof window === "undefined") return; - const resizeObserver = new ResizeObserver((entries) => { - if (!Array.isArray(entries) || !entries.length) return; + const resizeObserver = new ResizeObserver((entries) => { + if (!Array.isArray(entries) || !entries.length) return; - const entry = entries[0]; - if (!entry) return; + const entry = entries[0]; + if (!entry) return; - let width: number; - let height: number; + let width: number; + let height: number; - if ("borderBoxSize" in entry) { - const borderSizeEntry = entry.borderBoxSize; - const borderSize = Array.isArray(borderSizeEntry) ? borderSizeEntry[0] : borderSizeEntry; - width = borderSize.inlineSize; - height = borderSize.blockSize; - } else { - width = control.offsetWidth; - height = control.offsetHeight; - } + if ("borderBoxSize" in entry) { + const borderSizeEntry = entry.borderBoxSize; + const borderSize = Array.isArray(borderSizeEntry) ? borderSizeEntry[0] : borderSizeEntry; + width = borderSize.inlineSize; + height = borderSize.blockSize; + } else { + width = control.offsetWidth; + height = control.offsetHeight; + } - setControlSize({ width, height }); - }); + setControlSize({ width, height }); + }); - resizeObserver.observe(control, { box: "border-box" }); + resizeObserver.observe(control, { box: "border-box" }); - return () => { - resizeObserver.disconnect(); - }; - }, [control]); + return () => { + resizeObserver.disconnect(); + }; + }, [control]); - React.useEffect(() => { - const input = inputRef.current; - if (!input) return; + React.useEffect(() => { + const input = inputRef.current; + if (!input) return; - const inputProto = window.HTMLInputElement.prototype; - const propertyKey = isCheckInput ? "checked" : "value"; - const eventType = isCheckInput ? "click" : "input"; - const currentValue = isCheckInput ? checked : value; + const inputProto = window.HTMLInputElement.prototype; + const propertyKey = isCheckInput ? "checked" : "value"; + const eventType = isCheckInput ? "click" : "input"; + const currentValue = isCheckInput ? checked : value; - const serializedCurrentValue = isCheckInput - ? checked - : typeof value === "object" && value !== null - ? JSON.stringify(value) - : value; + const serializedCurrentValue = isCheckInput + ? checked + : typeof value === "object" && value !== null + ? JSON.stringify(value) + : value; - const descriptor = Object.getOwnPropertyDescriptor(inputProto, propertyKey); - const setter = descriptor?.set?.bind(input); + const descriptor = Object.getOwnPropertyDescriptor(inputProto, propertyKey); + const setter = descriptor?.set?.bind(input); - if (prevValue !== currentValue && setter) { - const event = new Event(eventType, { bubbles }); - setter.call(input, serializedCurrentValue); - input.dispatchEvent(event); - } - }, [prevValue, value, checked, bubbles, isCheckInput]); + if (prevValue !== currentValue && setter) { + const event = new Event(eventType, { bubbles }); + setter.call(input, serializedCurrentValue); + input.dispatchEvent(event); + } + }, [prevValue, value, checked, bubbles, isCheckInput]); - const composedStyle = React.useMemo(() => { - return { - ...style, - ...(controlSize.width !== undefined && controlSize.height !== undefined ? controlSize : {}), - border: 0, - clip: "rect(0 0 0 0)", - clipPath: "inset(50%)", - height: "1px", - margin: "-1px", - overflow: "hidden", - padding: 0, - position: "absolute", - whiteSpace: "nowrap", - width: "1px", - }; - }, [style, controlSize]); + const composedStyle = React.useMemo(() => { + return { + ...style, + ...(controlSize.width !== undefined && controlSize.height !== undefined ? controlSize : {}), + border: 0, + clip: "rect(0 0 0 0)", + clipPath: "inset(50%)", + height: "1px", + margin: "-1px", + overflow: "hidden", + padding: 0, + position: "absolute", + whiteSpace: "nowrap", + width: "1px", + }; + }, [style, controlSize]); - return ( - - ); + return ( + + ); } diff --git a/src/components/layout/breakpoint-indicator.tsx b/src/components/layout/breakpoint-indicator.tsx index b27964719..e25c9b32e 100644 --- a/src/components/layout/breakpoint-indicator.tsx +++ b/src/components/layout/breakpoint-indicator.tsx @@ -1,38 +1,38 @@ import { cn } from "@/utils/style"; type BreakpointIndicatorProps = { - position?: "top-right" | "bottom-right" | "top-left" | "bottom-left"; + position?: "top-right" | "bottom-right" | "top-left" | "bottom-left"; }; export function BreakpointIndicator({ position = "bottom-right" }: BreakpointIndicatorProps) { - const isTop = position.includes("top"); - const isRight = position.includes("right"); - const isLeft = position.includes("left"); - const isBottom = position.includes("bottom"); + const isTop = position.includes("top"); + const isRight = position.includes("right"); + const isLeft = position.includes("left"); + const isBottom = position.includes("bottom"); - const top = isTop ? "top-0" : "bottom-0"; - const right = isRight ? "inset-e-0" : "inset-s-0"; - const left = isLeft ? "inset-s-0" : "inset-e-0"; - const bottom = isBottom ? "bottom-0" : "top-0"; + const top = isTop ? "top-0" : "bottom-0"; + const right = isRight ? "inset-e-0" : "inset-s-0"; + const left = isLeft ? "inset-s-0" : "inset-e-0"; + const bottom = isBottom ? "bottom-0" : "top-0"; - return ( -
- XS - SM - MD - LG - XL - 2XL - 3XL - 4XL -
- ); + return ( +
+ XS + SM + MD + LG + XL + 2XL + 3XL + 4XL +
+ ); } diff --git a/src/components/layout/error-screen.tsx b/src/components/layout/error-screen.tsx index 08a85a687..570447be4 100644 --- a/src/components/layout/error-screen.tsx +++ b/src/components/layout/error-screen.tsx @@ -9,22 +9,22 @@ import { Alert, AlertDescription, AlertTitle } from "../ui/alert"; import { BrandIcon } from "../ui/brand-icon"; export function ErrorScreen({ error, reset }: ErrorComponentProps) { - return ( -
- + return ( +
+ - - - - An error occurred while loading the page. - - {error.message} - + + + + An error occurred while loading the page. + + {error.message} + - -
- ); + +
+ ); } diff --git a/src/components/layout/loading-screen.tsx b/src/components/layout/loading-screen.tsx index 869f90b98..1641ed7c0 100644 --- a/src/components/layout/loading-screen.tsx +++ b/src/components/layout/loading-screen.tsx @@ -3,12 +3,12 @@ import { Trans } from "@lingui/react/macro"; import { Spinner } from "@/components/ui/spinner"; export function LoadingScreen() { - return ( -
- -

- Loading... -

-
- ); + return ( +
+ +

+ Loading... +

+
+ ); } diff --git a/src/components/layout/not-found-screen.tsx b/src/components/layout/not-found-screen.tsx index d53a4f4c4..0fec70486 100644 --- a/src/components/layout/not-found-screen.tsx +++ b/src/components/layout/not-found-screen.tsx @@ -8,24 +8,24 @@ import { Alert, AlertDescription, AlertTitle } from "../ui/alert"; import { BrandIcon } from "../ui/brand-icon"; export function NotFoundScreen({ routeId }: NotFoundRouteProps) { - return ( -
- + return ( +
+ - - - - An error occurred while loading the page. - - {routeId} - + + + + An error occurred while loading the page. + + {routeId} + - -
- ); + +
+ ); } diff --git a/src/components/level/combobox.tsx b/src/components/level/combobox.tsx index 3d48cb207..deb8da145 100644 --- a/src/components/level/combobox.tsx +++ b/src/components/level/combobox.tsx @@ -13,24 +13,24 @@ type LevelType = z.infer["type"]; type LevelTypeComboboxProps = Omit; export const getLevelTypeName = (type: LevelType) => { - return match(type) - .with("hidden", () => t`Hidden`) - .with("circle", () => t`Circle`) - .with("square", () => t`Square`) - .with("rectangle", () => t`Rectangle`) - .with("rectangle-full", () => t`Rectangle (Full Width)`) - .with("progress-bar", () => t`Progress Bar`) - .with("icon", () => t`Icon`) - .exhaustive(); + return match(type) + .with("hidden", () => t`Hidden`) + .with("circle", () => t`Circle`) + .with("square", () => t`Square`) + .with("rectangle", () => t`Rectangle`) + .with("rectangle-full", () => t`Rectangle (Full Width)`) + .with("progress-bar", () => t`Progress Bar`) + .with("icon", () => t`Icon`) + .exhaustive(); }; export function LevelTypeCombobox({ ...props }: LevelTypeComboboxProps) { - const options = useMemo(() => { - return levelDesignSchema.shape.type.options.map((option) => ({ - value: option, - label: getLevelTypeName(option), - })); - }, []); + const options = useMemo(() => { + return levelDesignSchema.shape.type.options.map((option) => ({ + value: option, + label: getLevelTypeName(option), + })); + }, []); - return ; + return ; } diff --git a/src/components/level/display.tsx b/src/components/level/display.tsx index 3a208ba62..93ebb6e17 100644 --- a/src/components/level/display.tsx +++ b/src/components/level/display.tsx @@ -11,61 +11,61 @@ import { PageIcon } from "../resume/shared/page-icon"; type Props = z.infer & React.ComponentProps<"div"> & { level: number }; export function LevelDisplay({ icon, type, level, className, ...props }: Props) { - if (level === 0) return null; - if (type === "hidden" || icon === "") return null; + if (level === 0) return null; + if (type === "hidden" || icon === "") return null; - return ( -
- {Array.from({ length: 5 }).map((_, index) => { - const isActive = index < level; + return ( +
+ {Array.from({ length: 5 }).map((_, index) => { + const isActive = index < level; - if (type === "progress-bar") { - return ( -
- ); - } + if (type === "progress-bar") { + return ( +
+ ); + } - if (type === "icon") { - return ( - - ); - } + if (type === "icon") { + return ( + + ); + } - return ( -
- ); - })} -
- ); + return ( +
+ ); + })} +
+ ); } diff --git a/src/components/locale/combobox.tsx b/src/components/locale/combobox.tsx index 2a2561c98..9c6661f5e 100644 --- a/src/components/locale/combobox.tsx +++ b/src/components/locale/combobox.tsx @@ -8,29 +8,29 @@ import { Combobox, type SingleComboboxProps } from "../ui/combobox"; type Props = Omit; export const getLocaleOptions = () => { - return Object.entries(localeMap).map(([value, label]) => ({ - value: value as Locale, - label: i18n.t(label), - keywords: [i18n.t(label)], - })); + return Object.entries(localeMap).map(([value, label]) => ({ + value: value as Locale, + label: i18n.t(label), + keywords: [i18n.t(label)], + })); }; export function LocaleCombobox(props: Props) { - const { i18n } = useLingui(); + const { i18n } = useLingui(); - const onLocaleChange = async (value: string | null) => { - if (!value || !isLocale(value)) return; - await Promise.all([loadLocale(value), setLocaleServerFn({ data: value })]); - window.location.reload(); - }; + const onLocaleChange = async (value: string | null) => { + if (!value || !isLocale(value)) return; + await Promise.all([loadLocale(value), setLocaleServerFn({ data: value })]); + window.location.reload(); + }; - return ( - - ); + return ( + + ); } diff --git a/src/components/resume/hooks/use-css-variables.tsx b/src/components/resume/hooks/use-css-variables.tsx index 1ca2242a5..409bf8aac 100644 --- a/src/components/resume/hooks/use-css-variables.tsx +++ b/src/components/resume/hooks/use-css-variables.tsx @@ -9,37 +9,37 @@ import { pageDimensionsAsMillimeters } from "@/schema/page"; type UseCssVariablesProps = Pick, "picture" | "metadata">; export const useCSSVariables = ({ picture, metadata }: UseCssVariablesProps) => { - const fontWeightStyles = useMemo(() => { - const lowestBodyFontWeight = Math.min(...metadata.typography.body.fontWeights.map(Number)); - const lowestHeadingFontWeight = Math.min(...metadata.typography.heading.fontWeights.map(Number)); + const fontWeightStyles = useMemo(() => { + const lowestBodyFontWeight = Math.min(...metadata.typography.body.fontWeights.map(Number)); + const lowestHeadingFontWeight = Math.min(...metadata.typography.heading.fontWeights.map(Number)); - const highestBodyFontWeight = Math.max(...metadata.typography.body.fontWeights.map(Number)); - const highestHeadingFontWeight = Math.max(...metadata.typography.heading.fontWeights.map(Number)); + const highestBodyFontWeight = Math.max(...metadata.typography.body.fontWeights.map(Number)); + const highestHeadingFontWeight = Math.max(...metadata.typography.heading.fontWeights.map(Number)); - return { lowestBodyFontWeight, lowestHeadingFontWeight, highestBodyFontWeight, highestHeadingFontWeight }; - }, [metadata.typography.body.fontWeights, metadata.typography.heading.fontWeights]); + return { lowestBodyFontWeight, lowestHeadingFontWeight, highestBodyFontWeight, highestHeadingFontWeight }; + }, [metadata.typography.body.fontWeights, metadata.typography.heading.fontWeights]); - return { - "--picture-border-radius": `${picture.borderRadius}pt`, - "--page-width": pageDimensionsAsMillimeters[metadata.page.format].width, - "--page-height": pageDimensionsAsMillimeters[metadata.page.format].height, - "--page-sidebar-width": `${metadata.layout.sidebarWidth}%`, - "--page-text-color": metadata.design.colors.text, - "--page-primary-color": metadata.design.colors.primary, - "--page-background-color": metadata.design.colors.background, - "--page-body-font-family": `'${metadata.typography.body.fontFamily}', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif`, - "--page-body-font-weight": fontWeightStyles.lowestBodyFontWeight, - "--page-body-font-weight-bold": fontWeightStyles.highestBodyFontWeight, - "--page-body-font-size": metadata.typography.body.fontSize, - "--page-body-line-height": metadata.typography.body.lineHeight, - "--page-heading-font-family": `'${metadata.typography.heading.fontFamily}', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif`, - "--page-heading-font-weight": fontWeightStyles.lowestHeadingFontWeight, - "--page-heading-font-weight-bold": fontWeightStyles.highestHeadingFontWeight, - "--page-heading-font-size": metadata.typography.heading.fontSize, - "--page-heading-line-height": metadata.typography.heading.lineHeight, - "--page-margin-x": `${metadata.page.marginX}pt`, - "--page-margin-y": `${metadata.page.marginY}pt`, - "--page-gap-x": `${metadata.page.gapX}pt`, - "--page-gap-y": `${metadata.page.gapY}pt`, - } as React.CSSProperties; + return { + "--picture-border-radius": `${picture.borderRadius}pt`, + "--page-width": pageDimensionsAsMillimeters[metadata.page.format].width, + "--page-height": pageDimensionsAsMillimeters[metadata.page.format].height, + "--page-sidebar-width": `${metadata.layout.sidebarWidth}%`, + "--page-text-color": metadata.design.colors.text, + "--page-primary-color": metadata.design.colors.primary, + "--page-background-color": metadata.design.colors.background, + "--page-body-font-family": `'${metadata.typography.body.fontFamily}', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif`, + "--page-body-font-weight": fontWeightStyles.lowestBodyFontWeight, + "--page-body-font-weight-bold": fontWeightStyles.highestBodyFontWeight, + "--page-body-font-size": metadata.typography.body.fontSize, + "--page-body-line-height": metadata.typography.body.lineHeight, + "--page-heading-font-family": `'${metadata.typography.heading.fontFamily}', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif`, + "--page-heading-font-weight": fontWeightStyles.lowestHeadingFontWeight, + "--page-heading-font-weight-bold": fontWeightStyles.highestHeadingFontWeight, + "--page-heading-font-size": metadata.typography.heading.fontSize, + "--page-heading-line-height": metadata.typography.heading.lineHeight, + "--page-margin-x": `${metadata.page.marginX}pt`, + "--page-margin-y": `${metadata.page.marginY}pt`, + "--page-gap-x": `${metadata.page.gapX}pt`, + "--page-gap-y": `${metadata.page.gapY}pt`, + } as React.CSSProperties; }; diff --git a/src/components/resume/hooks/use-webfonts.tsx b/src/components/resume/hooks/use-webfonts.tsx index c48c9d158..6d307c4ac 100644 --- a/src/components/resume/hooks/use-webfonts.tsx +++ b/src/components/resume/hooks/use-webfonts.tsx @@ -8,53 +8,53 @@ import type { typographySchema } from "@/schema/resume/data"; import webfontlist from "@/components/typography/webfontlist.json"; export function useWebfonts(typography: z.infer) { - const isMounted = useIsMounted(); + const isMounted = useIsMounted(); - useEffect(() => { - if (!isMounted()) return; + useEffect(() => { + if (!isMounted()) return; - const body = document.body; - if (body) body.setAttribute("data-wf-loaded", "false"); + const body = document.body; + if (body) body.setAttribute("data-wf-loaded", "false"); - async function loadFont(family: string, weights: string[]) { - const font = webfontlist.find((font) => font.family === family); - if (!font) return; + async function loadFont(family: string, weights: string[]) { + const font = webfontlist.find((font) => font.family === family); + if (!font) return; - type FontUrl = { url: string; weight: string; style: "italic" | "normal" }; + type FontUrl = { url: string; weight: string; style: "italic" | "normal" }; - const fontUrls: FontUrl[] = []; + const fontUrls: FontUrl[] = []; - for (const weight of weights) { - for (const [fileWeight, url] of Object.entries(font.files)) { - if (weight === fileWeight) { - fontUrls.push({ url, weight, style: "normal" }); - } - if (fileWeight === `${weight}italic`) { - fontUrls.push({ url, weight, style: "italic" }); - } - } - } + for (const weight of weights) { + for (const [fileWeight, url] of Object.entries(font.files)) { + if (weight === fileWeight) { + fontUrls.push({ url, weight, style: "normal" }); + } + if (fileWeight === `${weight}italic`) { + fontUrls.push({ url, weight, style: "italic" }); + } + } + } - for (const { url, weight, style } of fontUrls) { - const fontFace = new FontFace(family, `url("${url}")`, { style, weight, display: "swap" }); - if (!document.fonts.has(fontFace)) document.fonts.add(await fontFace.load()); - } - } + for (const { url, weight, style } of fontUrls) { + const fontFace = new FontFace(family, `url("${url}")`, { style, weight, display: "swap" }); + if (!document.fonts.has(fontFace)) document.fonts.add(await fontFace.load()); + } + } - const bodyTypography = typography.body; - const headingTypography = typography.heading; + const bodyTypography = typography.body; + const headingTypography = typography.heading; - void Promise.all([ - loadFont(bodyTypography.fontFamily, bodyTypography.fontWeights), - loadFont(headingTypography.fontFamily, headingTypography.fontWeights), - ]).then(() => { - if (isMounted() && body) body.setAttribute("data-wf-loaded", "true"); - }); + void Promise.all([ + loadFont(bodyTypography.fontFamily, bodyTypography.fontWeights), + loadFont(headingTypography.fontFamily, headingTypography.fontWeights), + ]).then(() => { + if (isMounted() && body) body.setAttribute("data-wf-loaded", "true"); + }); - return () => { - if (isMounted()) { - if (body) body.removeAttribute("data-wf-loaded"); - } - }; - }, [isMounted, typography]); + return () => { + if (isMounted()) { + if (body) body.removeAttribute("data-wf-loaded"); + } + }; + }, [isMounted, typography]); } diff --git a/src/components/resume/preview.module.css b/src/components/resume/preview.module.css index f6e3084a0..889097d27 100644 --- a/src/components/resume/preview.module.css +++ b/src/components/resume/preview.module.css @@ -1,118 +1,118 @@ .page { - width: var(--page-width); - min-height: var(--page-height); - font-family: var(--page-body-font-family); - font-size: calc(var(--page-body-font-size) * 1pt); - font-weight: var(--page-body-font-weight); - line-height: var(--page-body-line-height); - color: var(--page-text-color); - background-color: var(--page-background-color); + width: var(--page-width); + min-height: var(--page-height); + font-family: var(--page-body-font-family); + font-size: calc(var(--page-body-font-size) * 1pt); + font-weight: var(--page-body-font-weight); + line-height: var(--page-body-line-height); + color: var(--page-text-color); + background-color: var(--page-background-color); - &:not(:first-child) { - content-visibility: auto; - contain-intrinsic-size: var(--page-width) var(--page-height); - } + &:not(:first-child) { + content-visibility: auto; + contain-intrinsic-size: var(--page-width) var(--page-height); + } - h1, - h2, - h3, - h4, - h5, - h6 { - font-family: var(--page-heading-font-family); - font-weight: var(--page-heading-font-weight); - line-height: var(--page-heading-line-height); - } + h1, + h2, + h3, + h4, + h5, + h6 { + font-family: var(--page-heading-font-family); + font-weight: var(--page-heading-font-weight); + line-height: var(--page-heading-line-height); + } - h1 { - font-size: calc(var(--page-heading-font-size) * 1.5pt); - } + h1 { + font-size: calc(var(--page-heading-font-size) * 1.5pt); + } - h2 { - font-size: calc(var(--page-heading-font-size) * 1.25pt); - } + h2 { + font-size: calc(var(--page-heading-font-size) * 1.25pt); + } - h3 { - font-size: calc(var(--page-heading-font-size) * 1.125pt); - } + h3 { + font-size: calc(var(--page-heading-font-size) * 1.125pt); + } - h4 { - font-size: calc(var(--page-heading-font-size) * 1pt); - } + h4 { + font-size: calc(var(--page-heading-font-size) * 1pt); + } - h5 { - font-size: calc(var(--page-heading-font-size) * 0.875pt); - } + h5 { + font-size: calc(var(--page-heading-font-size) * 0.875pt); + } - h6 { - font-size: calc(var(--page-heading-font-size) * 0.75pt); - } + h6 { + font-size: calc(var(--page-heading-font-size) * 0.75pt); + } - a { - text-decoration: underline; - text-underline-offset: 0.15rem; - } + a { + text-decoration: underline; + text-underline-offset: 0.15rem; + } - hr { - margin: 1rem 0; - border-color: var(--page-text-color); - } + hr { + margin: 1rem 0; + border-color: var(--page-text-color); + } - strong { - font-weight: var(--page-body-font-weight-bold); - } + strong { + font-weight: var(--page-body-font-weight-bold); + } - small { - font-size: calc(var(--page-body-font-size) * 0.9pt); - } + small { + font-size: calc(var(--page-body-font-size) * 0.9pt); + } - /* RTL Support: Apply RTL direction when text is right-aligned */ - p[style*="text-align: right"], - h1[style*="text-align: right"], - h2[style*="text-align: right"], - h3[style*="text-align: right"], - h4[style*="text-align: right"], - h5[style*="text-align: right"], - h6[style*="text-align: right"], - li[style*="text-align: right"], - ul[style*="text-align: right"], - ol[style*="text-align: right"] { - direction: rtl; - word-wrap: break-word; - overflow-wrap: break-word; - white-space: normal; - } + /* RTL Support: Apply RTL direction when text is right-aligned */ + p[style*="text-align: right"], + h1[style*="text-align: right"], + h2[style*="text-align: right"], + h3[style*="text-align: right"], + h4[style*="text-align: right"], + h5[style*="text-align: right"], + h6[style*="text-align: right"], + li[style*="text-align: right"], + ul[style*="text-align: right"], + ol[style*="text-align: right"] { + direction: rtl; + word-wrap: break-word; + overflow-wrap: break-word; + white-space: normal; + } - /* LTR Support: Apply LTR direction when text is left-aligned */ - p[style*="text-align: left"], - h1[style*="text-align: left"], - h2[style*="text-align: left"], - h3[style*="text-align: left"], - h4[style*="text-align: left"], - h5[style*="text-align: left"], - h6[style*="text-align: left"], - li[style*="text-align: left"], - ul[style*="text-align: left"], - ol[style*="text-align: left"] { - direction: ltr; - word-wrap: break-word; - overflow-wrap: break-word; - white-space: normal; - } + /* LTR Support: Apply LTR direction when text is left-aligned */ + p[style*="text-align: left"], + h1[style*="text-align: left"], + h2[style*="text-align: left"], + h3[style*="text-align: left"], + h4[style*="text-align: left"], + h5[style*="text-align: left"], + h6[style*="text-align: left"], + li[style*="text-align: left"], + ul[style*="text-align: left"], + ol[style*="text-align: left"] { + direction: ltr; + word-wrap: break-word; + overflow-wrap: break-word; + white-space: normal; + } - /* When list items are RTL, make bullets/numbers appear on the right */ - li[style*="text-align: right"] { - text-align: right; - } + /* When list items are RTL, make bullets/numbers appear on the right */ + li[style*="text-align: right"] { + text-align: right; + } - /* Parent list inherits direction from its items */ - ul:has(li[style*="text-align: right"]), - ol:has(li[style*="text-align: right"]) { - direction: rtl; - } + /* Parent list inherits direction from its items */ + ul:has(li[style*="text-align: right"]), + ol:has(li[style*="text-align: right"]) { + direction: rtl; + } - ul:has(li[style*="text-align: left"]), - ol:has(li[style*="text-align: left"]) { - direction: ltr; - } + ul:has(li[style*="text-align: left"]), + ol:has(li[style*="text-align: left"]) { + direction: ltr; + } } diff --git a/src/components/resume/preview.tsx b/src/components/resume/preview.tsx index 61ecfa45b..ebcebbf74 100644 --- a/src/components/resume/preview.tsx +++ b/src/components/resume/preview.tsx @@ -33,159 +33,159 @@ import { PikachuTemplate } from "./templates/pikachu"; import { RhyhornTemplate } from "./templates/rhyhorn"; export type ExtendedIconProps = IconProps & { - hidden?: boolean; + hidden?: boolean; }; const CSS_RULE_SPLIT_PATTERN = /\n(?=\s*[.#a-zA-Z])/; const CSS_SELECTOR_PATTERN = /^([^{]+)(\{)/; function getTemplateComponent(template: Template) { - return match(template) - .with("azurill", () => AzurillTemplate) - .with("bronzor", () => BronzorTemplate) - .with("chikorita", () => ChikoritaTemplate) - .with("ditto", () => DittoTemplate) - .with("ditgar", () => DitgarTemplate) - .with("gengar", () => GengarTemplate) - .with("glalie", () => GlalieTemplate) - .with("kakuna", () => KakunaTemplate) - .with("lapras", () => LaprasTemplate) - .with("leafish", () => LeafishTemplate) - .with("onyx", () => OnyxTemplate) - .with("pikachu", () => PikachuTemplate) - .with("rhyhorn", () => RhyhornTemplate) - .exhaustive(); + return match(template) + .with("azurill", () => AzurillTemplate) + .with("bronzor", () => BronzorTemplate) + .with("chikorita", () => ChikoritaTemplate) + .with("ditto", () => DittoTemplate) + .with("ditgar", () => DitgarTemplate) + .with("gengar", () => GengarTemplate) + .with("glalie", () => GlalieTemplate) + .with("kakuna", () => KakunaTemplate) + .with("lapras", () => LaprasTemplate) + .with("leafish", () => LeafishTemplate) + .with("onyx", () => OnyxTemplate) + .with("pikachu", () => PikachuTemplate) + .with("rhyhorn", () => RhyhornTemplate) + .exhaustive(); } type Props = React.ComponentProps<"div"> & { - pageClassName?: string; - showPageNumbers?: boolean; + pageClassName?: string; + showPageNumbers?: boolean; }; export const ResumePreview = ({ showPageNumbers = false, pageClassName, className, ...props }: Props) => { - const picture = useResumeStore((state) => state.resume.data.picture); - const metadata = useResumeStore((state) => state.resume.data.metadata); + const picture = useResumeStore((state) => state.resume.data.picture); + const metadata = useResumeStore((state) => state.resume.data.metadata); - useWebfonts(metadata.typography); - const style = useCSSVariables({ picture, metadata }); + useWebfonts(metadata.typography); + const style = useCSSVariables({ picture, metadata }); - const iconProps = useMemo(() => { - return { - weight: "regular", - hidden: metadata.page.hideIcons, - color: "var(--page-primary-color)", - size: metadata.typography.body.fontSize * 1.5, - } satisfies ExtendedIconProps; - }, [metadata.typography.body.fontSize, metadata.page.hideIcons]); + const iconProps = useMemo(() => { + return { + weight: "regular", + hidden: metadata.page.hideIcons, + color: "var(--page-primary-color)", + size: metadata.typography.body.fontSize * 1.5, + } satisfies ExtendedIconProps; + }, [metadata.typography.body.fontSize, metadata.page.hideIcons]); - const scopedCSS = useMemo(() => { - if (!metadata.css.enabled || !metadata.css.value.trim()) return null; + const scopedCSS = useMemo(() => { + if (!metadata.css.enabled || !metadata.css.value.trim()) return null; - const sanitizedCss = sanitizeCss(metadata.css.value); + const sanitizedCss = sanitizeCss(metadata.css.value); - const scoped = sanitizedCss - .split(CSS_RULE_SPLIT_PATTERN) - .map((rule) => { - const trimmed = rule.trim(); - if (!trimmed || trimmed.startsWith("@")) return trimmed; + const scoped = sanitizedCss + .split(CSS_RULE_SPLIT_PATTERN) + .map((rule) => { + const trimmed = rule.trim(); + if (!trimmed || trimmed.startsWith("@")) return trimmed; - return trimmed.replace(CSS_SELECTOR_PATTERN, (_match, selectors, brace) => { - const prefixed = selectors - .split(",") - .map((selector: string) => `.resume-preview-container ${selector.trim()} `) - .join(", "); - return `${prefixed}${brace}`; - }); - }) - .join("\n"); + return trimmed.replace(CSS_SELECTOR_PATTERN, (_match, selectors, brace) => { + const prefixed = selectors + .split(",") + .map((selector: string) => `.resume-preview-container ${selector.trim()} `) + .join(", "); + return `${prefixed}${brace}`; + }); + }) + .join("\n"); - return scoped; - }, [metadata.css.enabled, metadata.css.value]); + return scoped; + }, [metadata.css.enabled, metadata.css.value]); - return ( - - {scopedCSS &&