From 6f10381f1f087bb3196ba1d0602c867ac314b37f Mon Sep 17 00:00:00 2001 From: aXenDeveloper Date: Sun, 24 Aug 2025 22:22:46 +0200 Subject: [PATCH 1/4] feat!: Replace eslint & prettier to biome --- .github/copilot-instructions.md | 6 +- .github/docs/prd.md | 4 +- .prettierrc.mjs | 11 - .vscode/settings.json | 24 + apps/api/eslint.config.mjs | 20 - apps/api/package.json | 3 - apps/api/src/index.ts | 2 +- apps/api/src/vitnode.api.config.ts | 5 +- apps/docs/content/docs/ui/combobox.mdx | 122 +- apps/docs/e2e/homepage.spec.ts | 4 +- apps/docs/eslint.config.mjs | 20 - apps/docs/global.d.ts | 4 +- apps/docs/migrations/meta/0000_snapshot.json | 178 +- apps/docs/migrations/meta/0001_snapshot.json | 178 +- apps/docs/migrations/meta/0002_snapshot.json | 178 +- apps/docs/migrations/meta/_journal.json | 2 +- apps/docs/next.config.ts | 4 +- apps/docs/package.json | 3 - .../(docs)/docs/[[...slug]]/page.client.tsx | 1 - .../src/app/[locale]/(docs)/docs/layout.tsx | 5 +- .../src/app/[locale]/(main)/(home)/page.tsx | 4 +- .../(main)/(home)/sections/admin/admin.tsx | 2 +- .../sections/powering-by/logos/drizzleorm.tsx | 11 +- .../sections/powering-by/logos/honojs.tsx | 1 + .../sections/powering-by/logos/next-intl.tsx | 13 +- .../sections/powering-by/logos/nextjs.tsx | 16 +- .../sections/powering-by/logos/postgresql.tsx | 1 + .../powering-by/logos/tailwindcss.tsx | 1 + .../sections/powering-by/logos/turborepo.tsx | 27 +- .../sections/powering-by/powering-by.tsx | 4 +- .../(plugins)/(vitnode-core)/login/page.tsx | 4 +- .../login/reset-password/page.tsx | 4 +- .../(vitnode-core)/register/page.tsx | 4 +- .../(vitnode-blog)/blog/categories/page.tsx | 11 +- .../(vitnode-blog)/blog/posts/page.tsx | 15 +- .../(vitnode-core)/core/debug/page.tsx | 17 +- .../(vitnode-core)/core/users/page.tsx | 16 +- apps/docs/src/app/[locale]/layout.tsx | 3 +- apps/docs/src/app/global-error.tsx | 3 - apps/docs/src/app/global.css | 9 +- apps/docs/src/app/layout.tsx | 2 +- .../animated-beam/animated-beam-home.tsx | 4 +- .../animated-beam/animated-beam.tsx | 16 +- .../src/components/fumadocs/code-block.tsx | 12 +- apps/docs/src/components/fumadocs/img.tsx | 2 +- apps/docs/src/components/infinite-slider.tsx | 20 +- apps/docs/src/components/logo-vitnode.tsx | 2 + apps/docs/src/components/text-animate.tsx | 99 +- apps/docs/src/examples/sonner.tsx | 2 +- apps/docs/src/vitnode.api.config.ts | 1 - biome.json | 102 + package.json | 7 +- packages/create-vitnode-app/.npmignore | 1 - packages/create-vitnode-app/README.md | 2 +- .../api-single-app/src/vitnode.api.config.ts | 1 - .../copy-of-vitnode-app/api/src/index.ts | 2 +- .../api/src/vitnode.api.config.ts | 5 +- .../eslint/.prettierrc.mjs | 11 - .../eslint/eslint.config.mjs | 17 - .../copy-of-vitnode-app/root/global.d.ts | 2 +- .../copy-of-vitnode-app/root/next.config.ts | 2 +- .../root/src/app/[locale]/(main)/page.tsx | 1 + .../root/src/app/[locale]/layout.tsx | 3 +- .../root/src/app/global.css | 5 +- packages/create-vitnode-app/eslint.config.mjs | 25 - packages/create-vitnode-app/package.json | 5 +- .../src/create/create-package-json.ts | 14 +- .../src/create/create-vitnode.ts | 30 +- .../helpers/get-available-package-managers.ts | 4 +- .../src/helpers/get-package-json.ts | 2 +- .../src/helpers/init-vitnode.ts | 2 +- .../src/helpers/install-dependencies.ts | 66 +- .../src/helpers/is-folder-empty.ts | 6 +- .../src/helpers/is-online.ts | 34 +- .../src/helpers/is-writeable.ts | 4 +- packages/create-vitnode-app/src/index.ts | 5 +- .../create-vitnode-app/src/plugin/index.ts | 2 +- .../create-vitnode-app/src/prepare/prepare.ts | 7 +- packages/create-vitnode-app/src/questions.ts | 14 +- packages/create-vitnode-app/src/validation.ts | 5 +- packages/eslint/eslint.config.mjs | 174 -- packages/eslint/package.json | 16 +- packages/eslint/prettierrc.mjs | 14 - packages/vitnode/.npmignore | 1 - packages/vitnode/eslint.config.mjs | 17 - packages/vitnode/global.d.ts | 2 +- packages/vitnode/package.json | 3 - packages/vitnode/scripts/get-config.ts | 6 +- packages/vitnode/scripts/plugin.ts | 340 +-- packages/vitnode/scripts/prepare-database.ts | 25 +- .../vitnode/scripts/prepare-plugins-files.ts | 9 +- .../scripts/run-interactive-shell-command.ts | 4 +- packages/vitnode/scripts/scripts.ts | 6 +- packages/vitnode/scripts/shared/file-utils.ts | 22 +- .../src/api/adapters/email/nodemailer.ts | 2 +- .../vitnode/src/api/adapters/email/resend.ts | 2 +- .../vitnode/src/api/adapters/sso/discord.ts | 5 +- .../vitnode/src/api/adapters/sso/facebook.ts | 5 +- .../vitnode/src/api/adapters/sso/google.ts | 5 +- packages/vitnode/src/api/config.ts | 11 +- .../vitnode/src/api/lib/check-plugin-id.ts | 4 +- .../vitnode/src/api/lib/logger-middleware.ts | 2 +- packages/vitnode/src/api/lib/plugin.ts | 4 +- packages/vitnode/src/api/lib/route.ts | 3 +- .../vitnode/src/api/lib/with-pagination.ts | 152 +- .../src/api/middlewares/captcha.middleware.ts | 3 +- .../src/api/middlewares/global.middleware.ts | 20 +- .../middlewares/rate-limiter.middleware.ts | 2 +- packages/vitnode/src/api/models/device.ts | 5 +- packages/vitnode/src/api/models/email.ts | 31 +- packages/vitnode/src/api/models/password.ts | 8 +- .../vitnode/src/api/models/session-admin.ts | 3 +- packages/vitnode/src/api/models/session.ts | 5 +- packages/vitnode/src/api/models/sso.ts | 11 +- .../src/api/models/user/get-user-by-id.ts | 3 +- .../api/models/user/sign-in-with-passwords.ts | 3 +- .../vitnode/src/api/models/user/sign-up.ts | 4 +- .../vitnode/src/app_admin/core/debug/page.tsx | 2 +- .../vitnode/src/app_admin/core/users/page.tsx | 3 +- .../confirm-action-alert-dialog.tsx | 2 +- .../src/components/confirm-action/content.tsx | 1 - .../vitnode/src/components/date-format.tsx | 2 +- .../vitnode/src/components/form/auto-form.tsx | 37 +- .../src/components/form/fields/checkbox.tsx | 3 +- .../components/form/fields/combobox-async.tsx | 82 +- .../src/components/form/fields/combobox.tsx | 2 +- .../src/components/form/fields/input.tsx | 3 +- .../components/form/fields/radio-group.tsx | 2 +- .../src/components/form/fields/select.tsx | 2 +- .../src/components/form/fields/switch.tsx | 1 - .../src/components/form/fields/textarea.tsx | 2 +- .../vitnode/src/components/i18n-provider.tsx | 2 +- .../vitnode/src/components/logo-vitnode.tsx | 2 + .../switchers/langs/language-swietcher.tsx | 4 +- .../vitnode/src/components/table/content.tsx | 4 +- .../src/components/table/data-table.tsx | 84 +- .../src/components/table/order-table-head.tsx | 27 +- .../src/components/table/pagination.tsx | 2 +- .../vitnode/src/components/ui/accordion.tsx | 2 +- .../src/components/ui/alert-dialog.tsx | 5 +- packages/vitnode/src/components/ui/alert.tsx | 2 +- packages/vitnode/src/components/ui/badge.tsx | 2 +- packages/vitnode/src/components/ui/button.tsx | 6 +- .../vitnode/src/components/ui/checkbox.tsx | 2 +- .../vitnode/src/components/ui/command.tsx | 2 +- .../src/components/ui/context-menu.tsx | 2 +- packages/vitnode/src/components/ui/dialog.tsx | 5 +- packages/vitnode/src/components/ui/drawer.tsx | 2 +- .../src/components/ui/dropdown-menu.tsx | 2 +- packages/vitnode/src/components/ui/form.tsx | 7 +- .../vitnode/src/components/ui/hover-card.tsx | 2 +- .../vitnode/src/components/ui/input-otp.tsx | 4 +- packages/vitnode/src/components/ui/input.tsx | 2 +- packages/vitnode/src/components/ui/label.tsx | 2 +- .../vitnode/src/components/ui/menubar.tsx | 2 +- .../src/components/ui/navigation-menu.tsx | 2 +- .../vitnode/src/components/ui/popover.tsx | 2 +- .../vitnode/src/components/ui/progress.tsx | 2 +- .../vitnode/src/components/ui/radio-group.tsx | 2 +- packages/vitnode/src/components/ui/select.tsx | 2 +- .../vitnode/src/components/ui/separator.tsx | 2 +- packages/vitnode/src/components/ui/sheet.tsx | 2 +- .../vitnode/src/components/ui/sidebar.tsx | 8 +- packages/vitnode/src/components/ui/slider.tsx | 23 +- packages/vitnode/src/components/ui/sonner.tsx | 3 +- packages/vitnode/src/components/ui/switch.tsx | 2 +- packages/vitnode/src/components/ui/table.tsx | 2 +- packages/vitnode/src/components/ui/tabs.tsx | 2 +- .../vitnode/src/components/ui/textarea.tsx | 2 +- .../src/components/ui/toggle-group.tsx | 4 +- packages/vitnode/src/components/ui/toggle.tsx | 2 +- .../vitnode/src/components/ui/tooltip.tsx | 2 +- packages/vitnode/src/drizzle.config.ts | 22 +- .../vitnode/src/emails/default-template.tsx | 40 +- .../vitnode/src/emails/reset-password.tsx | 80 +- packages/vitnode/src/globals.css | 6 +- packages/vitnode/src/hooks/use-captcha.ts | 31 +- packages/vitnode/src/hooks/use-mobile.ts | 3 +- packages/vitnode/src/lib/colors.ts | 44 +- packages/vitnode/src/lib/fetcher-client.ts | 6 +- packages/vitnode/src/lib/fetcher.ts | 6 +- .../fetcher/cookie-from-string-to-object.ts | 1 - packages/vitnode/src/lib/fetcher/core.ts | 11 +- packages/vitnode/src/lib/fetcher/types.ts | 80 +- packages/vitnode/src/lib/helpers/auto-form.ts | 5 +- packages/vitnode/src/lib/navigation.ts | 2 +- .../src/views/admin/layouts/admin-layout.tsx | 18 +- .../views/admin/layouts/sidebar/nav/item.tsx | 3 +- .../views/admin/layouts/sidebar/sidebar.tsx | 7 +- .../views/admin/layouts/user-bar/user-bar.tsx | 3 +- .../system-logs/actions/more/content.tsx | 4 +- .../debug/system-logs/actions/more/more.tsx | 2 +- .../debug/system-logs/badges/badge-status.tsx | 22 +- .../debug/system-logs/system-logs-view.tsx | 2 +- .../src/views/admin/views/core/test.tsx | 2 +- .../views/core/users/users-admin-view.tsx | 1 - .../change-password-form/use-form.ts | 4 +- .../views/auth/password-reset/form/form.tsx | 3 +- .../views/auth/sign-in/form/mutation-api.ts | 3 +- .../sign-up/components/password-input.tsx | 1 - .../src/views/auth/sign-up/form/form.tsx | 3 +- .../views/auth/sign-up/form/mutation-api.ts | 3 +- .../src/views/auth/sign-up/wrapper.tsx | 6 +- .../views/auth/sso/callback/client/client.tsx | 4 +- .../src/views/error/global-error-view.tsx | 22 +- .../vitnode/src/views/layouts/provider.tsx | 6 +- .../vitnode/src/views/layouts/root-layout.tsx | 6 +- .../src/views/layouts/theme/header/header.tsx | 4 +- .../layouts/theme/header/user/auth/client.tsx | 4 +- packages/vitnode/src/vitnode.config.ts | 1 + packages/vitnode/vitest.config.ts | 2 +- plugins/blog/.npmignore | 1 - plugins/blog/eslint.config.mjs | 17 - plugins/blog/global.d.ts | 4 +- plugins/blog/package.json | 5 +- .../admin/categories/routes/create.route.ts | 8 +- .../admin/posts/routes/create.route.ts | 18 +- .../modules/categories/routes/get.route.ts | 17 +- .../src/app_admin/blog/categories/page.tsx | 5 +- .../blog/src/app_admin/blog/posts/page.tsx | 5 +- plugins/blog/src/emails/test-template.tsx | 6 +- plugins/blog/src/globals.css | 6 +- .../admin/categories/actions/actions.tsx | 2 +- .../actions/create-edit/mutation-api.ts | 7 +- .../categories/table/actions/edit-action.tsx | 2 +- .../src/views/admin/posts/actions/actions.tsx | 2 +- .../posts/actions/create-edit/create-edit.tsx | 4 +- .../posts/actions/create-edit/mutation-api.ts | 7 +- .../admin/posts/table/actions/edit-action.tsx | 2 +- pnpm-lock.yaml | 2156 +---------------- scripts/bump-version.ts | 3 +- scripts/environment.ts | 54 +- scripts/files/file-copy-manager.ts | 31 +- scripts/files/file-system.ts | 79 +- scripts/version-manager.ts | 36 +- 235 files changed, 1701 insertions(+), 4051 deletions(-) delete mode 100644 .prettierrc.mjs delete mode 100644 apps/api/eslint.config.mjs delete mode 100644 apps/docs/eslint.config.mjs create mode 100644 biome.json delete mode 100644 packages/create-vitnode-app/copy-of-vitnode-app/eslint/.prettierrc.mjs delete mode 100644 packages/create-vitnode-app/copy-of-vitnode-app/eslint/eslint.config.mjs delete mode 100644 packages/create-vitnode-app/eslint.config.mjs delete mode 100644 packages/eslint/eslint.config.mjs delete mode 100644 packages/eslint/prettierrc.mjs delete mode 100644 packages/vitnode/eslint.config.mjs delete mode 100644 plugins/blog/eslint.config.mjs diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 89dac734b..85b5245e8 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -2,11 +2,13 @@ The repository is a monorepo for the VitNode framework, which includes a backend API, frontend documentation site, and shared packages. The codebase uses modern web technologies and follows specific conventions for development based on Next.js 15 and Hono.js 4. +- Do not nest ternary operators, + ## Architecture & Key Patterns - **Monorepo Structure:** - `apps/` contains main apps (`api` for backend, `docs` for docs site) - - `packages/` holds shared code, core framework, ESLint/Prettier configs, and CLI tools + - `packages/` holds shared code, core framework, Biome configs, and CLI tools - `plugins/` for extendable features - **Frontend:** - Next.js 15, App Router, Server Components @@ -33,7 +35,7 @@ The repository is a monorepo for the VitNode framework, which includes a backend - `pnpm dev` (dev server), `pnpm build`, `pnpm lint`, `pnpm db:push`, `pnpm db:migrate`, `pnpm docker:dev` - **CLI:** - Create apps/plugins via `pnpm create vitnode-app@canary` (see `packages/create-vitnode-app`) - - CLI prompts for package manager, app mode, ESLint, Docker, install (see `questions.ts`) + - CLI prompts for package manager, app mode, Biome, Docker, install (see `questions.ts`) - **Linting/Formatting:** - Use configs from `packages/eslint/` - File names: snake_case, ESModule only diff --git a/.github/docs/prd.md b/.github/docs/prd.md index 727839c0c..44b46333d 100644 --- a/.github/docs/prd.md +++ b/.github/docs/prd.md @@ -46,7 +46,7 @@ VitNode is designed for individual developers and small teams who need a structu ### CI/CD - Automated workflows using GitHub Actions: - - Code quality checks (ESLint, Prettier, TypeScript) + - Code quality checks (Biome, TypeScript) - Test suite execution with Vitest and Playwright - Dependency security scanning with npm audit - Automated builds and deployments to Vercel @@ -165,7 +165,7 @@ VitNode is designed for individual developers and small teams who need a structu - Turborepo for monorepo management - Vitest for unit testing - Playwright for end-to-end testing -- ESLint and Prettier for code quality +- Biome for code quality - Docker for containerization ## Features Planned for Future Releases diff --git a/.prettierrc.mjs b/.prettierrc.mjs deleted file mode 100644 index 92fe83d4d..000000000 --- a/.prettierrc.mjs +++ /dev/null @@ -1,11 +0,0 @@ -import vitnodePrettier from '@vitnode/eslint-config/prettierrc'; - -/** - * @see https://prettier.io/docs/en/configuration.html - * @type {import("prettier").Config} - */ -const config = { - ...vitnodePrettier, -}; - -export default config; diff --git a/.vscode/settings.json b/.vscode/settings.json index 3fac19c54..3af39be10 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,5 +7,29 @@ ], "search.exclude": { "**/(plugins)/*": true + }, + "[javascriptreact]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[typescript]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[css]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[jsonc]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[html]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[json]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[javascript]": { + "editor.defaultFormatter": "biomejs.biome" } } diff --git a/apps/api/eslint.config.mjs b/apps/api/eslint.config.mjs deleted file mode 100644 index c1de0a4ee..000000000 --- a/apps/api/eslint.config.mjs +++ /dev/null @@ -1,20 +0,0 @@ -import eslintVitNode from '@vitnode/eslint-config/eslint'; -import { fileURLToPath } from 'node:url'; -import { dirname } from 'node:path'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -export default [ - ...eslintVitNode, - { - ignores: ['drizzle.config.ts'], - }, - { - languageOptions: { - parserOptions: { - project: './tsconfig.json', - tsconfigRootDir: __dirname, - }, - }, - }, -]; diff --git a/apps/api/package.json b/apps/api/package.json index 911621dce..e1555284f 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -11,8 +11,6 @@ "dev:email": "email dev --dir src/emails", "build": "tsc && tsc-alias -p tsconfig.json", "start": "node dist/index.js", - "lint": "eslint .", - "lint:fix": "eslint . --fix", "drizzle-kit": "drizzle-kit" }, "dependencies": { @@ -36,7 +34,6 @@ "@types/react-dom": "^19.1.7", "@vitnode/eslint-config": "workspace:*", "dotenv": "^17.2.1", - "eslint": "^9.33.0", "react-email": "^4.2.8", "tsc-alias": "^1.8.16", "tsx": "^4.20.4", diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index 28ac18442..fae3dfb34 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -19,7 +19,7 @@ serve( info => { const initMessage = '\x1b[34m[VitNode]\x1b[0m'; - // eslint-disable-next-line no-console + // biome-ignore lint/suspicious/noConsole: console.log( `${initMessage} API server is running on http://localhost:${info.port}`, ); diff --git a/apps/api/src/vitnode.api.config.ts b/apps/api/src/vitnode.api.config.ts index b0e127a3c..c39543133 100644 --- a/apps/api/src/vitnode.api.config.ts +++ b/apps/api/src/vitnode.api.config.ts @@ -1,14 +1,13 @@ import { NodemailerEmailAdapter } from '@vitnode/core/api/adapters/email/nodemailer'; import { buildApiConfig } from '@vitnode/core/vitnode.config'; -import * as dotenv from 'dotenv'; +import { config } from 'dotenv'; import { drizzle } from 'drizzle-orm/postgres-js'; -dotenv.config({ +config({ quiet: true, }); export const POSTGRES_URL = - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing process.env.POSTGRES_URL || 'postgresql://root:root@localhost:5432/vitnode'; export const vitNodeApiConfig = buildApiConfig({ diff --git a/apps/docs/content/docs/ui/combobox.mdx b/apps/docs/content/docs/ui/combobox.mdx index 3190414cc..47c20801a 100644 --- a/apps/docs/content/docs/ui/combobox.mdx +++ b/apps/docs/content/docs/ui/combobox.mdx @@ -9,20 +9,20 @@ description: Select from a list of options with a search input. ## Usage -import { Tab, Tabs } from 'fumadocs-ui/components/tabs'; +import { Tab, Tabs } from "fumadocs-ui/components/tabs"; ```ts -import { z } from 'zod'; -import { AutoForm } from '@vitnode/core/components/form/auto-form'; -import { AutoFormCombobox } from '@vitnode/core/components/form/fields/combobox'; +import { z } from "zod"; +import { AutoForm } from "@vitnode/core/components/form/auto-form"; +import { AutoFormCombobox } from "@vitnode/core/components/form/fields/combobox"; ``` ```ts const formSchema = z.object({ - type: z.enum(['option-one', 'option-two']), + type: z.enum(["option-one", "option-two"]) }); ``` @@ -31,25 +31,25 @@ const formSchema = z.object({ formSchema={formSchema} fields={[ { - id: 'type', - component: props => ( + id: "type", + component: (props) => ( - ), - }, + ) + } ]} /> ``` @@ -59,53 +59,49 @@ const formSchema = z.object({ ```tsx -'use client'; +"use client"; -import * as React from 'react'; -import { CheckIcon, ChevronsUpDownIcon } from 'lucide-react'; +import React from "react"; +import { CheckIcon, ChevronsUpDownIcon } from "lucide-react"; -import { cn } from '@vitnode/core/lib/utils'; -import { Button } from '@vitnode/core/components/ui/button'; +import { cn } from "@vitnode/core/lib/utils"; +import { Button } from "@vitnode/core/components/ui/button"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, - CommandList, -} from '@vitnode/core/components/ui/command'; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from '@vitnode/core/components/ui/popover'; + CommandList +} from "@vitnode/core/components/ui/command"; +import { Popover, PopoverContent, PopoverTrigger } from "@vitnode/core/components/ui/popover"; const frameworks = [ { - value: 'next.js', - label: 'Next.js', + value: "next.js", + label: "Next.js" }, { - value: 'sveltekit', - label: 'SvelteKit', + value: "sveltekit", + label: "SvelteKit" }, { - value: 'nuxt.js', - label: 'Nuxt.js', + value: "nuxt.js", + label: "Nuxt.js" }, { - value: 'remix', - label: 'Remix', + value: "remix", + label: "Remix" }, { - value: 'astro', - label: 'Astro', - }, + value: "astro", + label: "Astro" + } ]; export function ExampleCombobox() { const [open, setOpen] = React.useState(false); - const [value, setValue] = React.useState(''); + const [value, setValue] = React.useState(""); return ( @@ -117,8 +113,8 @@ export function ExampleCombobox() { className="w-[200px] justify-between" > {value - ? frameworks.find(framework => framework.value === value)?.label - : 'Select framework...'} + ? frameworks.find((framework) => framework.value === value)?.label + : "Select framework..."} @@ -128,19 +124,19 @@ export function ExampleCombobox() { No framework found. - {frameworks.map(framework => ( + {frameworks.map((framework) => ( { - setValue(currentValue === value ? '' : currentValue); + onSelect={(currentValue) => { + setValue(currentValue === value ? "" : currentValue); setOpen(false); }} > {framework.label} @@ -161,36 +157,34 @@ export function ExampleCombobox() { ## Props -import { TypeTable } from 'fumadocs-ui/components/type-table'; +import { TypeTable } from "fumadocs-ui/components/type-table"; ', - default: '[]', + description: "An array of options for the combobox, each with a value and label.", + type: "Array<{ value: string; label: string }>", + default: "[]" }, placeholder: { - description: 'Placeholder text for the combobox input.', - type: 'string', - default: 'Select an option', + description: "Placeholder text for the combobox input.", + type: "string", + default: "Select an option" }, searchPlaceholder: { - description: 'Placeholder text for the search input within the combobox.', - type: 'string', - default: 'Search...', - }, + description: "Placeholder text for the search input within the combobox.", + type: "string", + default: "Search..." + } }} /> diff --git a/apps/docs/e2e/homepage.spec.ts b/apps/docs/e2e/homepage.spec.ts index 6897e5e06..4538e2e5b 100644 --- a/apps/docs/e2e/homepage.spec.ts +++ b/apps/docs/e2e/homepage.spec.ts @@ -1,9 +1,11 @@ import { expect, test } from '@playwright/test'; +const vitNodeTitleRegex = /VitNode/; + test.describe('Homepage', () => { test('should load successfully', async ({ page }) => { await page.goto('/'); - await expect(page).toHaveTitle(/VitNode/); + await expect(page).toHaveTitle(vitNodeTitleRegex); await expect(page.getByText('Start Your Journey!')).toBeVisible(); }); }); diff --git a/apps/docs/eslint.config.mjs b/apps/docs/eslint.config.mjs deleted file mode 100644 index f049e77eb..000000000 --- a/apps/docs/eslint.config.mjs +++ /dev/null @@ -1,20 +0,0 @@ -import eslintVitNode from '@vitnode/eslint-config/eslint'; -import { fileURLToPath } from 'node:url'; -import { dirname } from 'node:path'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -export default [ - ...eslintVitNode, - { - ignores: ['.source'], - }, - { - languageOptions: { - parserOptions: { - project: './tsconfig.json', - tsconfigRootDir: __dirname, - }, - }, - }, -]; diff --git a/apps/docs/global.d.ts b/apps/docs/global.d.ts index 604002bd0..38b3f3b57 100644 --- a/apps/docs/global.d.ts +++ b/apps/docs/global.d.ts @@ -1,7 +1,7 @@ /// -import type core from './src/locales/@vitnode/core/en.json'; -import type blog from './src/locales/@vitnode/blog/en.json'; +import blog from './src/locales/@vitnode/blog/en.json' with { type: 'json' }; +import core from './src/locales/@vitnode/core/en.json' with { type: 'json' }; declare module 'next-intl' { interface AppConfig { diff --git a/apps/docs/migrations/meta/0000_snapshot.json b/apps/docs/migrations/meta/0000_snapshot.json index b92d2f949..674182859 100644 --- a/apps/docs/migrations/meta/0000_snapshot.json +++ b/apps/docs/migrations/meta/0000_snapshot.json @@ -84,12 +84,8 @@ "name": "core_admin_permissions_roleId_core_roles_id_fk", "tableFrom": "core_admin_permissions", "tableTo": "core_roles", - "columnsFrom": [ - "roleId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["roleId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -97,12 +93,8 @@ "name": "core_admin_permissions_userId_core_users_id_fk", "tableFrom": "core_admin_permissions", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -199,12 +191,8 @@ "name": "core_admin_sessions_userId_core_users_id_fk", "tableFrom": "core_admin_sessions", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -212,12 +200,8 @@ "name": "core_admin_sessions_deviceId_core_sessions_known_devices_id_fk", "tableFrom": "core_admin_sessions", "tableTo": "core_sessions_known_devices", - "columnsFrom": [ - "deviceId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["deviceId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -227,9 +211,7 @@ "core_admin_sessions_token_unique": { "name": "core_admin_sessions_token_unique", "nullsNotDistinct": false, - "columns": [ - "token" - ] + "columns": ["token"] } }, "policies": {}, @@ -345,9 +327,7 @@ "core_languages_code_unique": { "name": "core_languages_code_unique", "nullsNotDistinct": false, - "columns": [ - "code" - ] + "columns": ["code"] } }, "policies": {}, @@ -423,12 +403,8 @@ "name": "core_languages_words_languageCode_core_languages_code_fk", "tableFrom": "core_languages_words", "tableTo": "core_languages", - "columnsFrom": [ - "languageCode" - ], - "columnsTo": [ - "code" - ], + "columnsFrom": ["languageCode"], + "columnsTo": ["code"], "onDelete": "cascade", "onUpdate": "no action" } @@ -527,12 +503,8 @@ "name": "core_logs_userId_core_users_id_fk", "tableFrom": "core_logs", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "cascade" } @@ -623,12 +595,8 @@ "name": "core_moderators_permissions_roleId_core_roles_id_fk", "tableFrom": "core_moderators_permissions", "tableTo": "core_roles", - "columnsFrom": [ - "roleId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["roleId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -636,12 +604,8 @@ "name": "core_moderators_permissions_userId_core_users_id_fk", "tableFrom": "core_moderators_permissions", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -782,12 +746,8 @@ "name": "core_sessions_userId_core_users_id_fk", "tableFrom": "core_sessions", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -795,12 +755,8 @@ "name": "core_sessions_deviceId_core_sessions_known_devices_id_fk", "tableFrom": "core_sessions", "tableTo": "core_sessions_known_devices", - "columnsFrom": [ - "deviceId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["deviceId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -810,9 +766,7 @@ "core_sessions_token_unique": { "name": "core_sessions_token_unique", "nullsNotDistinct": false, - "columns": [ - "token" - ] + "columns": ["token"] } }, "policies": {}, @@ -878,9 +832,7 @@ "core_sessions_known_devices_publicId_unique": { "name": "core_sessions_known_devices_publicId_unique", "nullsNotDistinct": false, - "columns": [ - "publicId" - ] + "columns": ["publicId"] } }, "policies": {}, @@ -1026,12 +978,8 @@ "name": "core_users_roleId_core_roles_id_fk", "tableFrom": "core_users", "tableTo": "core_roles", - "columnsFrom": [ - "roleId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["roleId"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -1039,12 +987,8 @@ "name": "core_users_language_core_languages_code_fk", "tableFrom": "core_users", "tableTo": "core_languages", - "columnsFrom": [ - "language" - ], - "columnsTo": [ - "code" - ], + "columnsFrom": ["language"], + "columnsTo": ["code"], "onDelete": "set default", "onUpdate": "no action" } @@ -1054,23 +998,17 @@ "core_users_nameCode_unique": { "name": "core_users_nameCode_unique", "nullsNotDistinct": false, - "columns": [ - "nameCode" - ] + "columns": ["nameCode"] }, "core_users_name_unique": { "name": "core_users_name_unique", "nullsNotDistinct": false, - "columns": [ - "name" - ] + "columns": ["name"] }, "core_users_email_unique": { "name": "core_users_email_unique", "nullsNotDistinct": false, - "columns": [ - "email" - ] + "columns": ["email"] } }, "policies": {}, @@ -1119,12 +1057,8 @@ "name": "core_users_confirm_emails_userId_core_users_id_fk", "tableFrom": "core_users_confirm_emails", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -1134,9 +1068,7 @@ "core_users_confirm_emails_token_unique": { "name": "core_users_confirm_emails_token_unique", "nullsNotDistinct": false, - "columns": [ - "token" - ] + "columns": ["token"] } }, "policies": {}, @@ -1191,12 +1123,8 @@ "name": "core_users_forgot_password_userId_core_users_id_fk", "tableFrom": "core_users_forgot_password", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -1206,16 +1134,12 @@ "core_users_forgot_password_userId_unique": { "name": "core_users_forgot_password_userId_unique", "nullsNotDistinct": false, - "columns": [ - "userId" - ] + "columns": ["userId"] }, "core_users_forgot_password_token_unique": { "name": "core_users_forgot_password_token_unique", "nullsNotDistinct": false, - "columns": [ - "token" - ] + "columns": ["token"] } }, "policies": {}, @@ -1280,12 +1204,8 @@ "name": "core_users_sso_userId_core_users_id_fk", "tableFrom": "core_users_sso", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -1340,9 +1260,7 @@ "blog_categories_titleSeo_unique": { "name": "blog_categories_titleSeo_unique", "nullsNotDistinct": false, - "columns": [ - "titleSeo" - ] + "columns": ["titleSeo"] } }, "policies": {}, @@ -1403,12 +1321,8 @@ "name": "blog_posts_categoryId_blog_categories_id_fk", "tableFrom": "blog_posts", "tableTo": "blog_categories", - "columnsFrom": [ - "categoryId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["categoryId"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -1418,9 +1332,7 @@ "blog_posts_titleSeo_unique": { "name": "blog_posts_titleSeo_unique", "nullsNotDistinct": false, - "columns": [ - "titleSeo" - ] + "columns": ["titleSeo"] } }, "policies": {}, @@ -1439,4 +1351,4 @@ "schemas": {}, "tables": {} } -} \ No newline at end of file +} diff --git a/apps/docs/migrations/meta/0001_snapshot.json b/apps/docs/migrations/meta/0001_snapshot.json index 556d11ac4..6d9c544d5 100644 --- a/apps/docs/migrations/meta/0001_snapshot.json +++ b/apps/docs/migrations/meta/0001_snapshot.json @@ -84,12 +84,8 @@ "name": "core_admin_permissions_roleId_core_roles_id_fk", "tableFrom": "core_admin_permissions", "tableTo": "core_roles", - "columnsFrom": [ - "roleId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["roleId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -97,12 +93,8 @@ "name": "core_admin_permissions_userId_core_users_id_fk", "tableFrom": "core_admin_permissions", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -199,12 +191,8 @@ "name": "core_admin_sessions_userId_core_users_id_fk", "tableFrom": "core_admin_sessions", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -212,12 +200,8 @@ "name": "core_admin_sessions_deviceId_core_sessions_known_devices_id_fk", "tableFrom": "core_admin_sessions", "tableTo": "core_sessions_known_devices", - "columnsFrom": [ - "deviceId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["deviceId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -227,9 +211,7 @@ "core_admin_sessions_token_unique": { "name": "core_admin_sessions_token_unique", "nullsNotDistinct": false, - "columns": [ - "token" - ] + "columns": ["token"] } }, "policies": {}, @@ -345,9 +327,7 @@ "core_languages_code_unique": { "name": "core_languages_code_unique", "nullsNotDistinct": false, - "columns": [ - "code" - ] + "columns": ["code"] } }, "policies": {}, @@ -423,12 +403,8 @@ "name": "core_languages_words_languageCode_core_languages_code_fk", "tableFrom": "core_languages_words", "tableTo": "core_languages", - "columnsFrom": [ - "languageCode" - ], - "columnsTo": [ - "code" - ], + "columnsFrom": ["languageCode"], + "columnsTo": ["code"], "onDelete": "cascade", "onUpdate": "no action" } @@ -527,12 +503,8 @@ "name": "core_logs_userId_core_users_id_fk", "tableFrom": "core_logs", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "cascade" } @@ -623,12 +595,8 @@ "name": "core_moderators_permissions_roleId_core_roles_id_fk", "tableFrom": "core_moderators_permissions", "tableTo": "core_roles", - "columnsFrom": [ - "roleId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["roleId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -636,12 +604,8 @@ "name": "core_moderators_permissions_userId_core_users_id_fk", "tableFrom": "core_moderators_permissions", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -782,12 +746,8 @@ "name": "core_sessions_userId_core_users_id_fk", "tableFrom": "core_sessions", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -795,12 +755,8 @@ "name": "core_sessions_deviceId_core_sessions_known_devices_id_fk", "tableFrom": "core_sessions", "tableTo": "core_sessions_known_devices", - "columnsFrom": [ - "deviceId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["deviceId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -810,9 +766,7 @@ "core_sessions_token_unique": { "name": "core_sessions_token_unique", "nullsNotDistinct": false, - "columns": [ - "token" - ] + "columns": ["token"] } }, "policies": {}, @@ -878,9 +832,7 @@ "core_sessions_known_devices_publicId_unique": { "name": "core_sessions_known_devices_publicId_unique", "nullsNotDistinct": false, - "columns": [ - "publicId" - ] + "columns": ["publicId"] } }, "policies": {}, @@ -1026,12 +978,8 @@ "name": "core_users_roleId_core_roles_id_fk", "tableFrom": "core_users", "tableTo": "core_roles", - "columnsFrom": [ - "roleId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["roleId"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -1039,12 +987,8 @@ "name": "core_users_language_core_languages_code_fk", "tableFrom": "core_users", "tableTo": "core_languages", - "columnsFrom": [ - "language" - ], - "columnsTo": [ - "code" - ], + "columnsFrom": ["language"], + "columnsTo": ["code"], "onDelete": "set default", "onUpdate": "no action" } @@ -1054,23 +998,17 @@ "core_users_nameCode_unique": { "name": "core_users_nameCode_unique", "nullsNotDistinct": false, - "columns": [ - "nameCode" - ] + "columns": ["nameCode"] }, "core_users_name_unique": { "name": "core_users_name_unique", "nullsNotDistinct": false, - "columns": [ - "name" - ] + "columns": ["name"] }, "core_users_email_unique": { "name": "core_users_email_unique", "nullsNotDistinct": false, - "columns": [ - "email" - ] + "columns": ["email"] } }, "policies": {}, @@ -1125,12 +1063,8 @@ "name": "core_users_confirm_emails_userId_core_users_id_fk", "tableFrom": "core_users_confirm_emails", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -1140,9 +1074,7 @@ "core_users_confirm_emails_token_unique": { "name": "core_users_confirm_emails_token_unique", "nullsNotDistinct": false, - "columns": [ - "token" - ] + "columns": ["token"] } }, "policies": {}, @@ -1197,12 +1129,8 @@ "name": "core_users_forgot_password_userId_core_users_id_fk", "tableFrom": "core_users_forgot_password", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -1212,16 +1140,12 @@ "core_users_forgot_password_userId_unique": { "name": "core_users_forgot_password_userId_unique", "nullsNotDistinct": false, - "columns": [ - "userId" - ] + "columns": ["userId"] }, "core_users_forgot_password_token_unique": { "name": "core_users_forgot_password_token_unique", "nullsNotDistinct": false, - "columns": [ - "token" - ] + "columns": ["token"] } }, "policies": {}, @@ -1286,12 +1210,8 @@ "name": "core_users_sso_userId_core_users_id_fk", "tableFrom": "core_users_sso", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -1346,9 +1266,7 @@ "blog_categories_titleSeo_unique": { "name": "blog_categories_titleSeo_unique", "nullsNotDistinct": false, - "columns": [ - "titleSeo" - ] + "columns": ["titleSeo"] } }, "policies": {}, @@ -1409,12 +1327,8 @@ "name": "blog_posts_categoryId_blog_categories_id_fk", "tableFrom": "blog_posts", "tableTo": "blog_categories", - "columnsFrom": [ - "categoryId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["categoryId"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -1424,9 +1338,7 @@ "blog_posts_titleSeo_unique": { "name": "blog_posts_titleSeo_unique", "nullsNotDistinct": false, - "columns": [ - "titleSeo" - ] + "columns": ["titleSeo"] } }, "policies": {}, @@ -1445,4 +1357,4 @@ "schemas": {}, "tables": {} } -} \ No newline at end of file +} diff --git a/apps/docs/migrations/meta/0002_snapshot.json b/apps/docs/migrations/meta/0002_snapshot.json index cce646f3e..07b31e4e7 100644 --- a/apps/docs/migrations/meta/0002_snapshot.json +++ b/apps/docs/migrations/meta/0002_snapshot.json @@ -84,12 +84,8 @@ "name": "core_admin_permissions_roleId_core_roles_id_fk", "tableFrom": "core_admin_permissions", "tableTo": "core_roles", - "columnsFrom": [ - "roleId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["roleId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -97,12 +93,8 @@ "name": "core_admin_permissions_userId_core_users_id_fk", "tableFrom": "core_admin_permissions", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -199,12 +191,8 @@ "name": "core_admin_sessions_userId_core_users_id_fk", "tableFrom": "core_admin_sessions", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -212,12 +200,8 @@ "name": "core_admin_sessions_deviceId_core_sessions_known_devices_id_fk", "tableFrom": "core_admin_sessions", "tableTo": "core_sessions_known_devices", - "columnsFrom": [ - "deviceId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["deviceId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -227,9 +211,7 @@ "core_admin_sessions_token_unique": { "name": "core_admin_sessions_token_unique", "nullsNotDistinct": false, - "columns": [ - "token" - ] + "columns": ["token"] } }, "policies": {}, @@ -345,9 +327,7 @@ "core_languages_code_unique": { "name": "core_languages_code_unique", "nullsNotDistinct": false, - "columns": [ - "code" - ] + "columns": ["code"] } }, "policies": {}, @@ -423,12 +403,8 @@ "name": "core_languages_words_languageCode_core_languages_code_fk", "tableFrom": "core_languages_words", "tableTo": "core_languages", - "columnsFrom": [ - "languageCode" - ], - "columnsTo": [ - "code" - ], + "columnsFrom": ["languageCode"], + "columnsTo": ["code"], "onDelete": "cascade", "onUpdate": "no action" } @@ -527,12 +503,8 @@ "name": "core_logs_userId_core_users_id_fk", "tableFrom": "core_logs", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "set null", "onUpdate": "cascade" } @@ -623,12 +595,8 @@ "name": "core_moderators_permissions_roleId_core_roles_id_fk", "tableFrom": "core_moderators_permissions", "tableTo": "core_roles", - "columnsFrom": [ - "roleId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["roleId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -636,12 +604,8 @@ "name": "core_moderators_permissions_userId_core_users_id_fk", "tableFrom": "core_moderators_permissions", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -782,12 +746,8 @@ "name": "core_sessions_userId_core_users_id_fk", "tableFrom": "core_sessions", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" }, @@ -795,12 +755,8 @@ "name": "core_sessions_deviceId_core_sessions_known_devices_id_fk", "tableFrom": "core_sessions", "tableTo": "core_sessions_known_devices", - "columnsFrom": [ - "deviceId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["deviceId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -810,9 +766,7 @@ "core_sessions_token_unique": { "name": "core_sessions_token_unique", "nullsNotDistinct": false, - "columns": [ - "token" - ] + "columns": ["token"] } }, "policies": {}, @@ -878,9 +832,7 @@ "core_sessions_known_devices_publicId_unique": { "name": "core_sessions_known_devices_publicId_unique", "nullsNotDistinct": false, - "columns": [ - "publicId" - ] + "columns": ["publicId"] } }, "policies": {}, @@ -1026,12 +978,8 @@ "name": "core_users_roleId_core_roles_id_fk", "tableFrom": "core_users", "tableTo": "core_roles", - "columnsFrom": [ - "roleId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["roleId"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" }, @@ -1039,12 +987,8 @@ "name": "core_users_language_core_languages_code_fk", "tableFrom": "core_users", "tableTo": "core_languages", - "columnsFrom": [ - "language" - ], - "columnsTo": [ - "code" - ], + "columnsFrom": ["language"], + "columnsTo": ["code"], "onDelete": "set default", "onUpdate": "no action" } @@ -1054,23 +998,17 @@ "core_users_nameCode_unique": { "name": "core_users_nameCode_unique", "nullsNotDistinct": false, - "columns": [ - "nameCode" - ] + "columns": ["nameCode"] }, "core_users_name_unique": { "name": "core_users_name_unique", "nullsNotDistinct": false, - "columns": [ - "name" - ] + "columns": ["name"] }, "core_users_email_unique": { "name": "core_users_email_unique", "nullsNotDistinct": false, - "columns": [ - "email" - ] + "columns": ["email"] } }, "policies": {}, @@ -1125,12 +1063,8 @@ "name": "core_users_confirm_emails_userId_core_users_id_fk", "tableFrom": "core_users_confirm_emails", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -1140,9 +1074,7 @@ "core_users_confirm_emails_token_unique": { "name": "core_users_confirm_emails_token_unique", "nullsNotDistinct": false, - "columns": [ - "token" - ] + "columns": ["token"] } }, "policies": {}, @@ -1197,12 +1129,8 @@ "name": "core_users_forgot_password_userId_core_users_id_fk", "tableFrom": "core_users_forgot_password", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -1212,16 +1140,12 @@ "core_users_forgot_password_userId_unique": { "name": "core_users_forgot_password_userId_unique", "nullsNotDistinct": false, - "columns": [ - "userId" - ] + "columns": ["userId"] }, "core_users_forgot_password_token_unique": { "name": "core_users_forgot_password_token_unique", "nullsNotDistinct": false, - "columns": [ - "token" - ] + "columns": ["token"] } }, "policies": {}, @@ -1286,12 +1210,8 @@ "name": "core_users_sso_userId_core_users_id_fk", "tableFrom": "core_users_sso", "tableTo": "core_users", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["userId"], + "columnsTo": ["id"], "onDelete": "cascade", "onUpdate": "no action" } @@ -1346,9 +1266,7 @@ "blog_categories_titleSeo_unique": { "name": "blog_categories_titleSeo_unique", "nullsNotDistinct": false, - "columns": [ - "titleSeo" - ] + "columns": ["titleSeo"] } }, "policies": {}, @@ -1409,12 +1327,8 @@ "name": "blog_posts_categoryId_blog_categories_id_fk", "tableFrom": "blog_posts", "tableTo": "blog_categories", - "columnsFrom": [ - "categoryId" - ], - "columnsTo": [ - "id" - ], + "columnsFrom": ["categoryId"], + "columnsTo": ["id"], "onDelete": "no action", "onUpdate": "no action" } @@ -1424,9 +1338,7 @@ "blog_posts_titleSeo_unique": { "name": "blog_posts_titleSeo_unique", "nullsNotDistinct": false, - "columns": [ - "titleSeo" - ] + "columns": ["titleSeo"] } }, "policies": {}, @@ -1445,4 +1357,4 @@ "schemas": {}, "tables": {} } -} \ No newline at end of file +} diff --git a/apps/docs/migrations/meta/_journal.json b/apps/docs/migrations/meta/_journal.json index 7d21da2ed..adee1e171 100644 --- a/apps/docs/migrations/meta/_journal.json +++ b/apps/docs/migrations/meta/_journal.json @@ -24,4 +24,4 @@ "breakpoints": true } ] -} \ No newline at end of file +} diff --git a/apps/docs/next.config.ts b/apps/docs/next.config.ts index 9a1375d38..e5326f8eb 100644 --- a/apps/docs/next.config.ts +++ b/apps/docs/next.config.ts @@ -1,6 +1,6 @@ -import type { NextConfig } from 'next'; -import { createMDX } from 'fumadocs-mdx/next'; import { vitNodeNextConfig } from '@vitnode/core/config/next.config'; +import { createMDX } from 'fumadocs-mdx/next'; +import type { NextConfig } from 'next'; const withMDX = createMDX(); diff --git a/apps/docs/package.json b/apps/docs/package.json index fd7d33b73..ca9853c69 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -12,8 +12,6 @@ "dev:email": "email dev --dir src/emails", "build": "next build --turbopack", "start": "next start", - "lint": "eslint .", - "lint:fix": "eslint . --fix", "test:e2e": "playwright test", "test:e2e:ui": "playwright test --ui", "test:e2e:debug": "playwright test --debug", @@ -53,7 +51,6 @@ "@types/react-dom": "^19.1.7", "@vitnode/eslint-config": "workspace:*", "class-variance-authority": "^0.7.1", - "eslint": "^9.33.0", "postcss": "^8.5.6", "react-email": "^4.2.8", "shiki": "^3.11.0", diff --git a/apps/docs/src/app/[locale]/(docs)/docs/[[...slug]]/page.client.tsx b/apps/docs/src/app/[locale]/(docs)/docs/[[...slug]]/page.client.tsx index 3a1ae54d4..02db80128 100644 --- a/apps/docs/src/app/[locale]/(docs)/docs/[[...slug]]/page.client.tsx +++ b/apps/docs/src/app/[locale]/(docs)/docs/[[...slug]]/page.client.tsx @@ -12,7 +12,6 @@ import { import { ChevronDown } from 'fumadocs-ui/internal/icons'; import { cn } from 'fumadocs-ui/utils/cn'; import { ExternalLinkIcon, MessageCircleIcon } from 'lucide-react'; -import React from 'react'; const optionVariants = cva( 'text-sm p-2 rounded-lg inline-flex items-center gap-2 hover:text-fd-accent-foreground hover:bg-fd-accent [&_svg]:size-4', diff --git a/apps/docs/src/app/[locale]/(docs)/docs/layout.tsx b/apps/docs/src/app/[locale]/(docs)/docs/layout.tsx index 49a1f3fe9..068598349 100644 --- a/apps/docs/src/app/[locale]/(docs)/docs/layout.tsx +++ b/apps/docs/src/app/[locale]/(docs)/docs/layout.tsx @@ -1,6 +1,5 @@ -import type { ReactNode } from 'react'; - import { DocsLayout } from 'fumadocs-ui/layouts/notebook'; +import type { ReactNode } from 'react'; import { baseOptions } from '@/app/[locale]/(main)/layout.config'; import { source } from '@/lib/source'; @@ -14,7 +13,7 @@ export default function Layout({ children }: { children: ReactNode }) { tabs: { transform(option, node) { const meta = source.getNodeMeta(node); - if (!meta || !node.icon) return option; + if (!(meta && node.icon)) return option; const color = `var(--${meta.path.split('/')[0]}-color, var(--color-fd-foreground))`; diff --git a/apps/docs/src/app/[locale]/(main)/(home)/page.tsx b/apps/docs/src/app/[locale]/(main)/(home)/page.tsx index 7aa8558b3..07e93ec52 100644 --- a/apps/docs/src/app/[locale]/(main)/(home)/page.tsx +++ b/apps/docs/src/app/[locale]/(main)/(home)/page.tsx @@ -1,9 +1,8 @@ -import type { Metadata } from 'next'; - import { buttonVariants } from '@vitnode/core/components/ui/button'; import { cn } from '@vitnode/core/lib/utils'; import Link from 'fumadocs-core/link'; import { ChevronRight } from 'lucide-react'; +import type { Metadata } from 'next'; import { AnimatedBeamHome } from '../../../../components/animated-beam/animated-beam-home'; import { AdminSection } from './sections/admin/admin'; @@ -61,6 +60,7 @@ export default function HomePage() { viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" > + GitHub {
-
+
payments illustration dark { viewBox="0 0 202 72" xmlns="http://www.w3.org/2000/svg" > + DrizzleORM logotype + /> + /> + /> + /> + /> ); }; diff --git a/apps/docs/src/app/[locale]/(main)/(home)/sections/powering-by/logos/honojs.tsx b/apps/docs/src/app/[locale]/(main)/(home)/sections/powering-by/logos/honojs.tsx index c9c95c482..d0e870f06 100644 --- a/apps/docs/src/app/[locale]/(main)/(home)/sections/powering-by/logos/honojs.tsx +++ b/apps/docs/src/app/[locale]/(main)/(home)/sections/powering-by/logos/honojs.tsx @@ -8,6 +8,7 @@ export const HonoJSLogo = () => { viewBox="0 0 76 98" xmlns="http://www.w3.org/2000/svg" > + HonoJS logotype { viewBox="0 0 499 120" xmlns="http://www.w3.org/2000/svg" > + next-intl logotype + /> + /> + /> + /> { ry="57.12" stroke="currentColor" strokeWidth="5.4" - > + /> + /> diff --git a/apps/docs/src/app/[locale]/(main)/(home)/sections/powering-by/logos/nextjs.tsx b/apps/docs/src/app/[locale]/(main)/(home)/sections/powering-by/logos/nextjs.tsx index bfe26ff94..1e11bd94e 100644 --- a/apps/docs/src/app/[locale]/(main)/(home)/sections/powering-by/logos/nextjs.tsx +++ b/apps/docs/src/app/[locale]/(main)/(home)/sections/powering-by/logos/nextjs.tsx @@ -9,37 +9,37 @@ export const NextJSLogo = () => { + /> + /> + /> + /> + /> + /> + /> + /> ); }; diff --git a/apps/docs/src/app/[locale]/(main)/(home)/sections/powering-by/logos/postgresql.tsx b/apps/docs/src/app/[locale]/(main)/(home)/sections/powering-by/logos/postgresql.tsx index b85d7f8c5..2ed300066 100644 --- a/apps/docs/src/app/[locale]/(main)/(home)/sections/powering-by/logos/postgresql.tsx +++ b/apps/docs/src/app/[locale]/(main)/(home)/sections/powering-by/logos/postgresql.tsx @@ -7,6 +7,7 @@ export const PostgreSQLLogo = () => { viewBox="0 0 25.6 25.6" xmlns="http://www.w3.org/2000/svg" > + PostgreSQL logotype { viewBox="0 0 262 33" xmlns="http://www.w3.org/2000/svg" > + TailwindCSS logotype { viewBox="0 0 473 76" width="473" > + TurboRepo logotype + /> + /> + /> + /> + /> + /> + /> + /> + /> + /> + /> { y1="11.5372" y2="42.4236" > - - + + diff --git a/apps/docs/src/app/[locale]/(main)/(home)/sections/powering-by/powering-by.tsx b/apps/docs/src/app/[locale]/(main)/(home)/sections/powering-by/powering-by.tsx index c3e345f88..9d8c145a9 100644 --- a/apps/docs/src/app/[locale]/(main)/(home)/sections/powering-by/powering-by.tsx +++ b/apps/docs/src/app/[locale]/(main)/(home)/sections/powering-by/powering-by.tsx @@ -79,8 +79,8 @@ export const PoweringBySection = () => { -
-
+
+
diff --git a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/page.tsx b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/page.tsx index bbdd08cbf..37a0b89c1 100644 --- a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/page.tsx +++ b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/page.tsx @@ -1,9 +1,7 @@ +import { SignInView } from '@vitnode/core/views/auth/sign-in/sign-in-view'; import type { Metadata } from 'next/dist/types'; - import { getTranslations } from 'next-intl/server'; -import { SignInView } from '@vitnode/core/views/auth/sign-in/sign-in-view'; - export const generateMetadata = async ({ params, }: { diff --git a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/reset-password/page.tsx b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/reset-password/page.tsx index 069f6fdd1..6c8acefe7 100644 --- a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/reset-password/page.tsx +++ b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/reset-password/page.tsx @@ -1,9 +1,7 @@ +import { PasswordResetView } from '@vitnode/core/views/auth/password-reset/password-reset-view'; import type { Metadata } from 'next/dist/types'; - import { getTranslations } from 'next-intl/server'; -import { PasswordResetView } from '@vitnode/core/views/auth/password-reset/password-reset-view'; - export const generateMetadata = async ({ params, }: { diff --git a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/register/page.tsx b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/register/page.tsx index 893a243f6..f6cc0bfd7 100644 --- a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/register/page.tsx +++ b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/register/page.tsx @@ -1,9 +1,7 @@ +import { SignUpView } from '@vitnode/core/views/auth/sign-up/sign-up-view'; import type { Metadata } from 'next/dist/types'; - import { getTranslations } from 'next-intl/server'; -import { SignUpView } from '@vitnode/core/views/auth/sign-up/sign-up-view'; - export const generateMetadata = async ({ params, }: { diff --git a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-blog)/blog/categories/page.tsx b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-blog)/blog/categories/page.tsx index cab8e4d8a..b14339bf9 100644 --- a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-blog)/blog/categories/page.tsx +++ b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-blog)/blog/categories/page.tsx @@ -1,16 +1,17 @@ -import type { Metadata } from 'next'; +import { ActionsCategoriesAdmin } from '@vitnode/blog/views/admin/categories/actions/actions'; import { I18nProvider } from '@vitnode/core/components/i18n-provider'; import { DataTableSkeleton } from '@vitnode/core/components/table/data-table'; import { HeaderContent } from '@vitnode/core/components/ui/header-content'; -import { getTranslations } from 'next-intl/server'; +import type { Metadata } from 'next'; import dynamic from 'next/dynamic'; +import { getTranslations } from 'next-intl/server'; import React from 'react'; -import { ActionsCategoriesAdmin } from '@vitnode/blog/views/admin/categories/actions/actions'; - const CategoriesAdminView = dynamic(async () => - import('@vitnode/blog/views/admin/categories/table/categories-admin-view').then(mod => ({ + import( + '@vitnode/blog/views/admin/categories/table/categories-admin-view' + ).then(mod => ({ default: mod.CategoriesAdminView, })), ); diff --git a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-blog)/blog/posts/page.tsx b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-blog)/blog/posts/page.tsx index 7fa80aa1a..152bf2908 100644 --- a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-blog)/blog/posts/page.tsx +++ b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-blog)/blog/posts/page.tsx @@ -1,18 +1,19 @@ -import type { Metadata } from 'next'; +import { ActionsPostsAdmin } from '@vitnode/blog/views/admin/posts/actions/actions'; import { I18nProvider } from '@vitnode/core/components/i18n-provider'; import { DataTableSkeleton } from '@vitnode/core/components/table/data-table'; import { HeaderContent } from '@vitnode/core/components/ui/header-content'; -import { getTranslations } from 'next-intl/server'; +import type { Metadata } from 'next'; import dynamic from 'next/dynamic'; +import { getTranslations } from 'next-intl/server'; import React from 'react'; -import { ActionsPostsAdmin } from '@vitnode/blog/views/admin/posts/actions/actions'; - const PostsAdminView = dynamic(async () => - import('@vitnode/blog/views/admin/posts/table/posts-admin-view').then(mod => ({ - default: mod.PostsAdminView, - })), + import('@vitnode/blog/views/admin/posts/table/posts-admin-view').then( + mod => ({ + default: mod.PostsAdminView, + }), + ), ); export const generateMetadata = async (): Promise => { diff --git a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/debug/page.tsx b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/debug/page.tsx index 4c3e2ebcf..edaac0aab 100644 --- a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/debug/page.tsx +++ b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/debug/page.tsx @@ -1,18 +1,17 @@ -import { getTranslations } from 'next-intl/server'; -import dynamic from 'next/dynamic'; -import React from 'react'; - import { I18nProvider } from '@vitnode/core/components/i18n-provider'; import { DataTableSkeleton } from '@vitnode/core/components/table/data-table'; import { HeaderContent } from '@vitnode/core/components/ui/header-content'; import { ClearCacheAction } from '@vitnode/core/views/admin/views/core/debug/actions/clear-cache/clear-cache'; +import dynamic from 'next/dynamic'; +import { getTranslations } from 'next-intl/server'; +import React from 'react'; const SystemLogsView = dynamic(async () => - import('@vitnode/core/views/admin/views/core/debug/system-logs/system-logs-view').then( - module => ({ - default: module.SystemLogsView, - }), - ), + import( + '@vitnode/core/views/admin/views/core/debug/system-logs/system-logs-view' + ).then(module => ({ + default: module.SystemLogsView, + })), ); export const generateMetadata = async () => { diff --git a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/users/page.tsx b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/users/page.tsx index 654a5b4f0..9008d1034 100644 --- a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/users/page.tsx +++ b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/users/page.tsx @@ -1,16 +1,16 @@ +import { DataTableSkeleton } from '@vitnode/core/components/table/data-table'; +import { HeaderContent } from '@vitnode/core/components/ui/header-content'; import type { Metadata } from 'next/dist/types'; - -import { getTranslations } from 'next-intl/server'; import dynamic from 'next/dynamic'; +import { getTranslations } from 'next-intl/server'; import React from 'react'; -import { DataTableSkeleton } from '@vitnode/core/components/table/data-table'; -import { HeaderContent } from '@vitnode/core/components/ui/header-content'; - const UsersAdminView = dynamic(async () => - import('@vitnode/core/views/admin/views/core/users/users-admin-view').then(module => ({ - default: module.UsersAdminView, - })), + import('@vitnode/core/views/admin/views/core/users/users-admin-view').then( + module => ({ + default: module.UsersAdminView, + }), + ), ); export const generateMetadata = async (): Promise => { diff --git a/apps/docs/src/app/[locale]/layout.tsx b/apps/docs/src/app/[locale]/layout.tsx index 3c206b9e5..548c95479 100644 --- a/apps/docs/src/app/[locale]/layout.tsx +++ b/apps/docs/src/app/[locale]/layout.tsx @@ -1,11 +1,10 @@ import type { RootLayoutProps } from '@vitnode/core/views/layouts/root-layout'; -import type { Metadata } from 'next'; - import { generateMetadataRootLayout, RootLayout, } from '@vitnode/core/views/layouts/root-layout'; import { RootProvider } from 'fumadocs-ui/provider'; +import type { Metadata } from 'next'; import { Geist, Geist_Mono } from 'next/font/google'; import { vitNodeConfig } from '@/vitnode.config'; diff --git a/apps/docs/src/app/global-error.tsx b/apps/docs/src/app/global-error.tsx index 5b8a6452b..b78f0b2c0 100644 --- a/apps/docs/src/app/global-error.tsx +++ b/apps/docs/src/app/global-error.tsx @@ -5,8 +5,6 @@ import { Geist, Geist_Mono } from 'next/font/google'; import './global.css'; -import { vitNodeConfig } from '@/vitnode.config'; - const geistSans = Geist({ variable: '--font-geist-sans', subsets: ['latin'], @@ -21,7 +19,6 @@ export default function GlobalError() { return ( ); } diff --git a/apps/docs/src/app/global.css b/apps/docs/src/app/global.css index cc47703f0..edaaf0fc1 100644 --- a/apps/docs/src/app/global.css +++ b/apps/docs/src/app/global.css @@ -1,8 +1,9 @@ -@import 'tailwindcss'; -@import 'fumadocs-ui/css/shadcn.css'; -@import 'fumadocs-ui/css/preset.css'; +/** biome-ignore-all lint/suspicious/noUnknownAtRules: */ +@import "tailwindcss"; +@import "fumadocs-ui/css/shadcn.css"; +@import "fumadocs-ui/css/preset.css"; -@import 'tw-animate-css'; +@import "tw-animate-css"; @source '../../node_modules/fumadocs-ui/dist/**/*.js'; @source "../../node_modules/@vitnode/core/dist/src/components"; diff --git a/apps/docs/src/app/layout.tsx b/apps/docs/src/app/layout.tsx index 8ed5dbaf5..ca8ce53fb 100644 --- a/apps/docs/src/app/layout.tsx +++ b/apps/docs/src/app/layout.tsx @@ -1,6 +1,6 @@ import './global.css'; -export default async function RootLayout({ +export default function RootLayout({ children, }: { children: React.ReactNode; diff --git a/apps/docs/src/components/animated-beam/animated-beam-home.tsx b/apps/docs/src/components/animated-beam/animated-beam-home.tsx index 7de512a8a..a0bb65aa0 100644 --- a/apps/docs/src/components/animated-beam/animated-beam-home.tsx +++ b/apps/docs/src/components/animated-beam/animated-beam-home.tsx @@ -18,7 +18,8 @@ import { Sparkle, Users, } from 'lucide-react'; -import React, { useRef } from 'react'; +import type React from 'react'; +import { useRef } from 'react'; import { LogoVitNode } from '../logo-vitnode'; import { AnimatedBeam } from './animated-beam'; @@ -91,6 +92,7 @@ export function AnimatedBeamHome() { + diff --git a/apps/docs/src/components/animated-beam/animated-beam.tsx b/apps/docs/src/components/animated-beam/animated-beam.tsx index 074f22b95..bca0dc7de 100644 --- a/apps/docs/src/components/animated-beam/animated-beam.tsx +++ b/apps/docs/src/components/animated-beam/animated-beam.tsx @@ -69,7 +69,6 @@ export const AnimatedBeam = ({ const svgWidth = containerRect.width; const svgHeight = containerRect.height; - // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect setSvgDimensions({ width: svgWidth, height: svgHeight }); const startX = @@ -85,7 +84,6 @@ export const AnimatedBeam = ({ const d = `M ${startX},${startY} Q ${ (startX + endX) / 2 },${controlY} ${endX},${endY}`; - // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect setPathD(d); } }; @@ -93,7 +91,6 @@ export const AnimatedBeam = ({ // Initialize ResizeObserver const resizeObserver = new ResizeObserver(entries => { // For all entries, recalculate the path - // eslint-disable-next-line @typescript-eslint/no-unused-vars for (const _entry of entries) { updatePath(); } @@ -134,6 +131,7 @@ export const AnimatedBeam = ({ width={svgDimensions.width} xmlns="http://www.w3.org/2000/svg" > + Animated beam - - - - + + + + diff --git a/apps/docs/src/components/fumadocs/code-block.tsx b/apps/docs/src/components/fumadocs/code-block.tsx index f76022385..069a0f9b8 100644 --- a/apps/docs/src/components/fumadocs/code-block.tsx +++ b/apps/docs/src/components/fumadocs/code-block.tsx @@ -1,10 +1,14 @@ import { highlight } from 'fumadocs-core/highlight'; -import * as Base from 'fumadocs-ui/components/codeblock'; +import { + CodeBlock as BaseCodeBlock, + type CodeBlockProps as BaseCodeBlockProps, + Pre, +} from 'fumadocs-ui/components/codeblock'; export interface CodeBlockProps { code: string; lang: string; - wrapper?: Base.CodeBlockProps; + wrapper?: BaseCodeBlockProps; } export async function CodeBlock({ code, lang, wrapper }: CodeBlockProps) { @@ -15,9 +19,9 @@ export async function CodeBlock({ code, lang, wrapper }: CodeBlockProps) { dark: 'vesper', }, components: { - pre: Base.Pre, + pre: Pre, }, }); - return {rendered}; + return {rendered}; } diff --git a/apps/docs/src/components/fumadocs/img.tsx b/apps/docs/src/components/fumadocs/img.tsx index ddc66c543..eddfe601c 100644 --- a/apps/docs/src/components/fumadocs/img.tsx +++ b/apps/docs/src/components/fumadocs/img.tsx @@ -1,6 +1,6 @@ import { ImageZoom } from 'fumadocs-ui/components/image-zoom'; import { cn } from 'fumadocs-ui/utils/cn'; -import React from 'react'; +import type React from 'react'; export const ImgDocs = ({ className, diff --git a/apps/docs/src/components/infinite-slider.tsx b/apps/docs/src/components/infinite-slider.tsx index 1bc66e8be..7ac54177e 100644 --- a/apps/docs/src/components/infinite-slider.tsx +++ b/apps/docs/src/components/infinite-slider.tsx @@ -1,8 +1,13 @@ 'use client'; import { cn } from '@vitnode/core/lib/utils'; -import { animate, motion, useMotionValue } from 'motion/react'; -import { useEffect, useState } from 'react'; +import { + type AnimationPlaybackControlsWithThen, + animate, + motion, + useMotionValue, +} from 'motion/react'; +import React from 'react'; import { useMeasure } from 'react-use'; export interface InfiniteSliderProps { @@ -24,14 +29,15 @@ export function InfiniteSlider({ reverse = false, className, }: InfiniteSliderProps) { - const [currentSpeed, setCurrentSpeed] = useState(speed); + const [currentSpeed, setCurrentSpeed] = React.useState(speed); const [ref, { width, height }] = useMeasure(); const translation = useMotionValue(0); - const [isTransitioning, setIsTransitioning] = useState(false); - const [key, setKey] = useState(0); + const [isTransitioning, setIsTransitioning] = React.useState(false); + const [key, setKey] = React.useState(0); - useEffect(() => { - let controls; + // biome-ignore lint/correctness/useExhaustiveDependencies: + React.useEffect(() => { + let controls: AnimationPlaybackControlsWithThen | undefined; const size = direction === 'horizontal' ? width : height; const contentSize = size + gap; const from = reverse ? -contentSize / 2 : 0; diff --git a/apps/docs/src/components/logo-vitnode.tsx b/apps/docs/src/components/logo-vitnode.tsx index 6268f597e..74de91b45 100644 --- a/apps/docs/src/components/logo-vitnode.tsx +++ b/apps/docs/src/components/logo-vitnode.tsx @@ -17,6 +17,7 @@ export const LogoVitNode = ({ xmlns="http://www.w3.org/2000/svg" {...props} > + Logo VitNode + Logo VitNode @@ -398,8 +405,10 @@ const TextAnimateBase = ({ segmentClassName, )} custom={i * staggerTimings[by]} - // eslint-disable-next-line @eslint-react/no-array-index-key - key={`${by}-${segment}-${i}`} + key={`${by}-${segment}-${ + // biome-ignore lint/suspicious/noArrayIndexKey: + i + }`} variants={finalVariants.item} > {segment} diff --git a/apps/docs/src/examples/sonner.tsx b/apps/docs/src/examples/sonner.tsx index 0582fec81..dd7cec84e 100644 --- a/apps/docs/src/examples/sonner.tsx +++ b/apps/docs/src/examples/sonner.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ 'use client'; import { Button } from '@vitnode/core/components/ui/button'; @@ -12,6 +11,7 @@ export default function SonnerDemo() { description: 'Sunday, December 03, 2023 at 9:00 AM', action: { label: 'Undo', + // biome-ignore lint/suspicious/noConsole: onClick: () => console.log('Undo'), }, }) diff --git a/apps/docs/src/vitnode.api.config.ts b/apps/docs/src/vitnode.api.config.ts index 2a8ed33ec..e0be6e05b 100644 --- a/apps/docs/src/vitnode.api.config.ts +++ b/apps/docs/src/vitnode.api.config.ts @@ -7,7 +7,6 @@ import { buildApiConfig } from '@vitnode/core/vitnode.config'; import { drizzle } from 'drizzle-orm/postgres-js'; export const POSTGRES_URL = - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing process.env.POSTGRES_URL || 'postgresql://root:root@localhost:5432/vitnode'; export const vitNodeApiConfig = buildApiConfig({ diff --git a/biome.json b/biome.json new file mode 100644 index 000000000..3951392b8 --- /dev/null +++ b/biome.json @@ -0,0 +1,102 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.2.2/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "ignoreUnknown": true, + "includes": [ + "**", + "!node_modules", + "!.next", + "!dist", + "!build", + "!.turbo", + "!.source", + "!docker" + ] + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 80 + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "jsxQuoteStyle": "double", + "arrowParentheses": "asNeeded" + } + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "complexity": { + "useLiteralKeys": "off", + "noExcessiveCognitiveComplexity": "warn", + "noExcessiveNestedTestSuites": "warn", + "noUselessStringConcat": "error", + "useSimplifiedLogicExpression": "warn" + }, + "suspicious": { + "noConsole": "error", + "noAlert": "error", + "noSkippedTests": "warn", + "noVar": "error", + "useAwait": "error", + "useIterableCallbackReturn": "off" + }, + "correctness": { + "noNestedComponentDefinitions": "error", + "noReactPropAssignments": "error", + "noRenderReturnValue": "error", + "useJsonImportAttributes": "warn", + "useUniqueElementIds": "off" + }, + "performance": { + "noAwaitInLoops": "error", + "noDelete": "error", + "noNamespaceImport": "error" + }, + "style": { + "noCommonJs": "error", + "noEnum": "error", + "noExportedImports": "warn", + "noInferrableTypes": "error", + "noNegationElse": "warn", + "noNestedTernary": "error", + "noSubstr": "error", + "noUnusedTemplateLiteral": "warn", + "noUselessElse": "warn", + "noYodaExpression": "warn", + "useAtIndex": "warn", + "useCollapsedIf": "warn", + "useConsistentArrayType": "warn", + "useConsistentBuiltinInstantiation": "error", + "useExportsLast": "warn", + "useObjectSpread": "warn", + "useSelfClosingElements": "warn", + "useShorthandAssign": "warn", + "useSingleVarDeclarator": "error", + "useThrowNewError": "error", + "useTrimStartEnd": "warn" + } + }, + "domains": { + "next": "recommended", + "react": "recommended", + "test": "recommended" + } + }, + "assist": { + "actions": { + "source": { + "organizeImports": "on" + } + } + } +} diff --git a/package.json b/package.json index a5f04dc6d..b84f1c722 100644 --- a/package.json +++ b/package.json @@ -12,16 +12,15 @@ "start": "turbo start", "init": "turbo init", "dev": "pnpm build:scripts && turbo build:plugins && turbo dev", - "lint": "turbo lint", - "lint:fix": "turbo lint:fix", + "lint": "biome check", + "lint:fix": "biome check . --write", "test": "turbo test", "test:e2e": "turbo test:e2e" }, "devDependencies": { + "@biomejs/biome": "^2.2.2", "@types/node": "^24.3.0", "@vitnode/eslint-config": "workspace:*", - "prettier": "^3.6.2", - "prettier-plugin-tailwindcss": "^0.6.14", "tsx": "^4.20.4", "turbo": "^2.5.6", "typescript": "^5.9.2", diff --git a/packages/create-vitnode-app/.npmignore b/packages/create-vitnode-app/.npmignore index 0950cd240..c45c667b4 100644 --- a/packages/create-vitnode-app/.npmignore +++ b/packages/create-vitnode-app/.npmignore @@ -1,5 +1,4 @@ /src /node_modules /.turbo -/eslint.config.mjs /tsconfig.json \ No newline at end of file diff --git a/packages/create-vitnode-app/README.md b/packages/create-vitnode-app/README.md index e3b22fcb0..e8aabb07d 100644 --- a/packages/create-vitnode-app/README.md +++ b/packages/create-vitnode-app/README.md @@ -40,7 +40,7 @@ bun create vitnode-app@latest | Option | Description | | ------------------- | --------------------------------------------------------------------------------- | | `--package-manager` | Specify the package manager to use. Support `npm`, `pnpm`. | -| `--eslint` | Initialize with eslint config. | +| `--biome` | Initialize with Biome config. | | `--skip-install` | Skip installing packages after initializing the project. | | `--mode` | Specify the type of app to create. Support `singleApp`, `apiMonorepo`, `onlyApi`. | | `--monorepo` | Create project with monorepo structure. | diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/api-single-app/src/vitnode.api.config.ts b/packages/create-vitnode-app/copy-of-vitnode-app/api-single-app/src/vitnode.api.config.ts index ba53fa6d2..cbe624760 100644 --- a/packages/create-vitnode-app/copy-of-vitnode-app/api-single-app/src/vitnode.api.config.ts +++ b/packages/create-vitnode-app/copy-of-vitnode-app/api-single-app/src/vitnode.api.config.ts @@ -2,7 +2,6 @@ import { buildApiConfig } from '@vitnode/core/vitnode.config'; import { drizzle } from 'drizzle-orm/postgres-js'; export const POSTGRES_URL = - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing process.env.POSTGRES_URL || 'postgresql://root:root@localhost:5432/vitnode'; export const vitNodeApiConfig = buildApiConfig({ diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/api/src/index.ts b/packages/create-vitnode-app/copy-of-vitnode-app/api/src/index.ts index 28ac18442..55319d430 100644 --- a/packages/create-vitnode-app/copy-of-vitnode-app/api/src/index.ts +++ b/packages/create-vitnode-app/copy-of-vitnode-app/api/src/index.ts @@ -1,3 +1,4 @@ +/** biome-ignore-all lint/suspicious/noConsole: */ import { serve } from '@hono/node-server'; import { OpenAPIHono } from '@hono/zod-openapi'; import { VitNodeAPI } from '@vitnode/core/api/config'; @@ -19,7 +20,6 @@ serve( info => { const initMessage = '\x1b[34m[VitNode]\x1b[0m'; - // eslint-disable-next-line no-console console.log( `${initMessage} API server is running on http://localhost:${info.port}`, ); diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/api/src/vitnode.api.config.ts b/packages/create-vitnode-app/copy-of-vitnode-app/api/src/vitnode.api.config.ts index 19d053edd..d61dfc4b3 100644 --- a/packages/create-vitnode-app/copy-of-vitnode-app/api/src/vitnode.api.config.ts +++ b/packages/create-vitnode-app/copy-of-vitnode-app/api/src/vitnode.api.config.ts @@ -1,13 +1,12 @@ import { buildApiConfig } from '@vitnode/core/vitnode.config'; -import * as dotenv from 'dotenv'; +import { config } from 'dotenv'; import { drizzle } from 'drizzle-orm/postgres-js'; -dotenv.config({ +config({ quiet: true, }); export const POSTGRES_URL = - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing process.env.POSTGRES_URL || 'postgresql://root:root@localhost:5432/vitnode'; export const vitNodeApiConfig = buildApiConfig({ diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/eslint/.prettierrc.mjs b/packages/create-vitnode-app/copy-of-vitnode-app/eslint/.prettierrc.mjs deleted file mode 100644 index 92fe83d4d..000000000 --- a/packages/create-vitnode-app/copy-of-vitnode-app/eslint/.prettierrc.mjs +++ /dev/null @@ -1,11 +0,0 @@ -import vitnodePrettier from '@vitnode/eslint-config/prettierrc'; - -/** - * @see https://prettier.io/docs/en/configuration.html - * @type {import("prettier").Config} - */ -const config = { - ...vitnodePrettier, -}; - -export default config; diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/eslint/eslint.config.mjs b/packages/create-vitnode-app/copy-of-vitnode-app/eslint/eslint.config.mjs deleted file mode 100644 index 0098d1c8a..000000000 --- a/packages/create-vitnode-app/copy-of-vitnode-app/eslint/eslint.config.mjs +++ /dev/null @@ -1,17 +0,0 @@ -import eslintVitNode from '@vitnode/eslint-config/eslint'; -import { fileURLToPath } from 'node:url'; -import { dirname } from 'node:path'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -export default [ - ...eslintVitNode, - { - languageOptions: { - parserOptions: { - project: './tsconfig.json', - tsconfigRootDir: __dirname, - }, - }, - }, -]; diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/root/global.d.ts b/packages/create-vitnode-app/copy-of-vitnode-app/root/global.d.ts index 599b27bef..f33ce4917 100644 --- a/packages/create-vitnode-app/copy-of-vitnode-app/root/global.d.ts +++ b/packages/create-vitnode-app/copy-of-vitnode-app/root/global.d.ts @@ -1,6 +1,6 @@ /// -import type core from './src/locales/@vitnode/core/en.json'; +import core from './src/locales/@vitnode/core/en.json' with { type: 'json' }; declare module 'next-intl' { interface AppConfig { diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/root/next.config.ts b/packages/create-vitnode-app/copy-of-vitnode-app/root/next.config.ts index 6d893c582..b7aef51e2 100644 --- a/packages/create-vitnode-app/copy-of-vitnode-app/root/next.config.ts +++ b/packages/create-vitnode-app/copy-of-vitnode-app/root/next.config.ts @@ -1,5 +1,5 @@ -import type { NextConfig } from 'next'; import { vitNodeNextConfig } from '@vitnode/core/config/next.config'; +import type { NextConfig } from 'next'; const nextConfig: NextConfig = { experimental: { diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/(main)/page.tsx b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/(main)/page.tsx index 220dcc433..753c497aa 100644 --- a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/(main)/page.tsx +++ b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/(main)/page.tsx @@ -68,6 +68,7 @@ export default function Page() { viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" > + GitHub */ +@import "tailwindcss"; -@import 'tw-animate-css'; +@import "tw-animate-css"; @source "../../node_modules/@vitnode/core/dist/src/components"; @source "../../node_modules/@vitnode/core/dist/src/views"; diff --git a/packages/create-vitnode-app/eslint.config.mjs b/packages/create-vitnode-app/eslint.config.mjs deleted file mode 100644 index ee68cfb76..000000000 --- a/packages/create-vitnode-app/eslint.config.mjs +++ /dev/null @@ -1,25 +0,0 @@ -import eslintVitNode from '@vitnode/eslint-config/eslint'; -import { fileURLToPath } from 'node:url'; -import { dirname } from 'node:path'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -export default [ - ...eslintVitNode, - { - rules: { - 'no-console': 'off', - }, - }, - { - ignores: ['copy-of-vitnode-app'], - }, - { - languageOptions: { - parserOptions: { - project: './tsconfig.json', - tsconfigRootDir: __dirname, - }, - }, - }, -]; diff --git a/packages/create-vitnode-app/package.json b/packages/create-vitnode-app/package.json index 692d774e9..a306e6f5a 100644 --- a/packages/create-vitnode-app/package.json +++ b/packages/create-vitnode-app/package.json @@ -16,9 +16,7 @@ }, "scripts": { "build:scripts": "tsc && node dist/src/prepare/prepare.js", - "start:scripts": "node dist/src/index.js", - "lint": "eslint .", - "lint:fix": "eslint . --fix" + "start:scripts": "node dist/src/index.js" }, "keywords": [ "vitnode", @@ -39,7 +37,6 @@ "@types/prompts": "^2.4.9", "@types/validate-npm-package-name": "^4.0.2", "@vitnode/eslint-config": "workspace:*", - "eslint": "^9.33.0", "typescript": "^5.9.2" } } diff --git a/packages/create-vitnode-app/src/create/create-package-json.ts b/packages/create-vitnode-app/src/create/create-package-json.ts index 58a8def28..9411f56b2 100644 --- a/packages/create-vitnode-app/src/create/create-package-json.ts +++ b/packages/create-vitnode-app/src/create/create-package-json.ts @@ -1,12 +1,10 @@ -import { readFile, writeFile } from 'fs/promises'; -import { dirname, join } from 'path'; -import { fileURLToPath } from 'url'; - +import { readFile, writeFile } from 'node:fs/promises'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { getAvailablePackageManagers } from '../helpers/get-available-package-managers.js'; import type { PackageJSON } from '../helpers/packages-json.js'; import type { CreateCliReturn } from '../questions.js'; -import { getAvailablePackageManagers } from '../helpers/get-available-package-managers.js'; - const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -278,14 +276,14 @@ export const createPackageJSON = async ({ appName, packageManager, root, - eslint, + biome, docker, mode, monorepo, }: { appName: string; docker?: boolean; - eslint: boolean; + biome: boolean; mode: Mode; monorepo?: boolean; packageManager: string; diff --git a/packages/create-vitnode-app/src/create/create-vitnode.ts b/packages/create-vitnode-app/src/create/create-vitnode.ts index f1fc07106..72d89f585 100644 --- a/packages/create-vitnode-app/src/create/create-vitnode.ts +++ b/packages/create-vitnode-app/src/create/create-vitnode.ts @@ -1,25 +1,30 @@ -import { existsSync } from 'fs'; -import { copyFile, cp, mkdir, readFile, rename, writeFile } from 'fs/promises'; +import { existsSync } from 'node:fs'; +import { + copyFile, + cp, + mkdir, + readFile, + rename, + writeFile, +} from 'node:fs/promises'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; import ora from 'ora'; -import { dirname, join } from 'path'; import color from 'picocolors'; -import { fileURLToPath } from 'url'; - -import type { CreateCliReturn } from '../questions.js'; - import { generateMigrationsVitnode, initFilesVitnode, } from '../helpers/init-vitnode.js'; import { installDependencies } from '../helpers/install-dependencies.js'; import { isFolderEmpty } from '../helpers/is-folder-empty.js'; +import type { CreateCliReturn } from '../questions.js'; import { createPackageJSON } from './create-package-json.js'; export const createVitNode = async ({ root, appName, packageManager, - eslint, + biome, install, docker, mode, @@ -27,6 +32,7 @@ export const createVitNode = async ({ }: CreateCliReturn & { appName: string; root: string; + // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: }) => { const spinner = ora( `Creating a new VitNode app in ${color.green(root)}. Using ${color.green(packageManager)}...`, @@ -124,9 +130,9 @@ export const createVitNode = async ({ }); } - if (eslint) { - spinner.text = 'Copying eslint files...'; - await cp(join(templatePath, 'eslint'), root, { + if (biome) { + spinner.text = 'Copying Biome files...'; + await cp(join(templatePath, 'biome'), root, { recursive: true, }); } @@ -153,7 +159,7 @@ export const createVitNode = async ({ root, appName, packageManager, - eslint, + biome, docker, mode, monorepo, diff --git a/packages/create-vitnode-app/src/helpers/get-available-package-managers.ts b/packages/create-vitnode-app/src/helpers/get-available-package-managers.ts index 069050220..416cc0a13 100644 --- a/packages/create-vitnode-app/src/helpers/get-available-package-managers.ts +++ b/packages/create-vitnode-app/src/helpers/get-available-package-managers.ts @@ -1,11 +1,11 @@ -import { exec } from 'child_process'; +import { exec } from 'node:child_process'; export type PackageManager = 'bun' | 'npm' | 'pnpm'; export const execShellCommand = async ( cmd: string, ): Promise => { - return new Promise(resolve => { + return await new Promise(resolve => { exec(cmd, (error, stdout, stderr) => { if (error) { resolve(undefined); diff --git a/packages/create-vitnode-app/src/helpers/get-package-json.ts b/packages/create-vitnode-app/src/helpers/get-package-json.ts index ead3ff3ba..2797efe43 100644 --- a/packages/create-vitnode-app/src/helpers/get-package-json.ts +++ b/packages/create-vitnode-app/src/helpers/get-package-json.ts @@ -1,4 +1,4 @@ -import { readFileSync } from 'fs'; +import { readFileSync } from 'node:fs'; import type { PackageJSON } from './packages-json.js'; diff --git a/packages/create-vitnode-app/src/helpers/init-vitnode.ts b/packages/create-vitnode-app/src/helpers/init-vitnode.ts index 9f4c9af0c..ae6f48bae 100644 --- a/packages/create-vitnode-app/src/helpers/init-vitnode.ts +++ b/packages/create-vitnode-app/src/helpers/init-vitnode.ts @@ -1,4 +1,4 @@ -import { spawn } from 'child_process'; +import { spawn } from 'node:child_process'; import type { CreateCliReturn } from '../questions.js'; diff --git a/packages/create-vitnode-app/src/helpers/install-dependencies.ts b/packages/create-vitnode-app/src/helpers/install-dependencies.ts index 9e7cf64d4..7304f68fd 100644 --- a/packages/create-vitnode-app/src/helpers/install-dependencies.ts +++ b/packages/create-vitnode-app/src/helpers/install-dependencies.ts @@ -1,10 +1,45 @@ -import { spawn } from 'child_process'; +/** biome-ignore-all lint/suspicious/noConsole: */ +import { spawn } from 'node:child_process'; import color from 'picocolors'; import type { CreateCliReturn } from '../questions.js'; import { getOnline } from './is-online.js'; +function printInstallErrorSuggestions( + stderr: string, + color: typeof import('picocolors'), +) { + if (stderr.includes('ENOTFOUND') || stderr.includes('network')) { + console.error( + color.yellow( + '💡 Network error detected. Please check your internet connection.', + ), + ); + } else if ( + stderr.includes('EACCES') || + stderr.includes('permission denied') + ) { + console.error( + color.yellow( + '💡 Permission error detected. Try running with elevated privileges or check file permissions.', + ), + ); + } else if (stderr.includes('ENOSPC')) { + console.error( + color.yellow( + '💡 Disk space error detected. Please free up some disk space.', + ), + ); + } else if (stderr.includes('ERR_PNPM_PEER_DEP_ISSUES')) { + console.error( + color.yellow( + '💡 Peer dependency issues detected. Consider using --force flag or resolve conflicts manually.', + ), + ); + } +} + export const installDependencies = async ({ packageManager: pm, cwd, @@ -75,34 +110,7 @@ export const installDependencies = async ({ } // Provide helpful suggestions based on common errors - if (stderr.includes('ENOTFOUND') || stderr.includes('network')) { - console.error( - color.yellow( - '💡 Network error detected. Please check your internet connection.', - ), - ); - } else if ( - stderr.includes('EACCES') || - stderr.includes('permission denied') - ) { - console.error( - color.yellow( - '💡 Permission error detected. Try running with elevated privileges or check file permissions.', - ), - ); - } else if (stderr.includes('ENOSPC')) { - console.error( - color.yellow( - '💡 Disk space error detected. Please free up some disk space.', - ), - ); - } else if (stderr.includes('ERR_PNPM_PEER_DEP_ISSUES')) { - console.error( - color.yellow( - '💡 Peer dependency issues detected. Consider using --force flag or resolve conflicts manually.', - ), - ); - } + printInstallErrorSuggestions(stderr, color); reject( new Error( diff --git a/packages/create-vitnode-app/src/helpers/is-folder-empty.ts b/packages/create-vitnode-app/src/helpers/is-folder-empty.ts index ae16ca6f6..c7a4ecd63 100644 --- a/packages/create-vitnode-app/src/helpers/is-folder-empty.ts +++ b/packages/create-vitnode-app/src/helpers/is-folder-empty.ts @@ -1,3 +1,4 @@ +/** biome-ignore-all lint/suspicious/noConsole: */ import { lstatSync, readdirSync } from 'node:fs'; import { join } from 'node:path'; import colors from 'picocolors'; @@ -27,10 +28,7 @@ export function isFolderEmpty(root: string, name: string): boolean { ]; const conflicts = readdirSync(root).filter( - file => - !validFiles.includes(file) && - // Support IntelliJ IDEA-based editors - !file.endsWith('.iml'), + file => !(validFiles.includes(file) || file.endsWith('.iml')), ); if (conflicts.length > 0) { diff --git a/packages/create-vitnode-app/src/helpers/is-online.ts b/packages/create-vitnode-app/src/helpers/is-online.ts index 90046fa5b..d73c56139 100644 --- a/packages/create-vitnode-app/src/helpers/is-online.ts +++ b/packages/create-vitnode-app/src/helpers/is-online.ts @@ -1,6 +1,20 @@ -import { execSync } from 'child_process'; -import { lookup } from 'dns/promises'; -import { URL } from 'url'; +import { execSync } from 'node:child_process'; +import { lookup } from 'node:dns/promises'; +import { URL } from 'node:url'; + +function getProxy(): string | undefined { + if (process.env.https_proxy) { + return process.env.https_proxy; + } + + try { + const httpsProxy = execSync('npm config get https-proxy').toString().trim(); + + return httpsProxy !== 'null' ? httpsProxy : undefined; + } catch { + return; + } +} export async function getOnline(): Promise { try { @@ -32,17 +46,3 @@ export async function getOnline(): Promise { } } } - -function getProxy(): string | undefined { - if (process.env.https_proxy) { - return process.env.https_proxy; - } - - try { - const httpsProxy = execSync('npm config get https-proxy').toString().trim(); - - return httpsProxy !== 'null' ? httpsProxy : undefined; - } catch { - return; - } -} diff --git a/packages/create-vitnode-app/src/helpers/is-writeable.ts b/packages/create-vitnode-app/src/helpers/is-writeable.ts index a4455b22f..d587454c5 100644 --- a/packages/create-vitnode-app/src/helpers/is-writeable.ts +++ b/packages/create-vitnode-app/src/helpers/is-writeable.ts @@ -1,5 +1,5 @@ -import { W_OK } from 'constants'; -import { access } from 'fs/promises'; +import { W_OK } from 'node:constants'; +import { access } from 'node:fs/promises'; export async function isWriteable(directory: string): Promise { try { diff --git a/packages/create-vitnode-app/src/index.ts b/packages/create-vitnode-app/src/index.ts index 59da36abf..43a571d6a 100644 --- a/packages/create-vitnode-app/src/index.ts +++ b/packages/create-vitnode-app/src/index.ts @@ -1,7 +1,8 @@ #!/usr/bin/env node +/** biome-ignore-all lint/suspicious/noConsole: */ +import { basename, resolve } from 'node:path'; import { input } from '@inquirer/prompts'; import { Command, Option } from 'commander'; -import { basename, resolve } from 'path'; import color from 'picocolors'; import { createVitNode } from './create/create-vitnode.js'; @@ -49,7 +50,7 @@ const init = async () => { 'Specify the package manager to use', ).choices(['npm', 'pnpm', 'bun']), ); - program.option('--eslint', 'Initialize with eslint config.'); + program.option('--biome', 'Initialize with Biome config.'); program.option( '--skip-install', 'Skip installing packages after initializing the project.', diff --git a/packages/create-vitnode-app/src/plugin/index.ts b/packages/create-vitnode-app/src/plugin/index.ts index fe79cea94..5a9bc73ea 100644 --- a/packages/create-vitnode-app/src/plugin/index.ts +++ b/packages/create-vitnode-app/src/plugin/index.ts @@ -1,5 +1,5 @@ +import { basename, resolve } from 'node:path'; import { input } from '@inquirer/prompts'; -import { basename, resolve } from 'path'; import { validateNpmName } from '../helpers/validate-pkg.js'; diff --git a/packages/create-vitnode-app/src/prepare/prepare.ts b/packages/create-vitnode-app/src/prepare/prepare.ts index 3b9c74ed8..46b1e05c9 100644 --- a/packages/create-vitnode-app/src/prepare/prepare.ts +++ b/packages/create-vitnode-app/src/prepare/prepare.ts @@ -1,6 +1,7 @@ -import { existsSync } from 'fs'; -import { mkdir } from 'fs/promises'; -import { join } from 'path'; +/** biome-ignore-all lint/suspicious/noConsole: */ +import { existsSync } from 'node:fs'; +import { mkdir } from 'node:fs/promises'; +import { join } from 'node:path'; const prepare = async () => { const toRootPath = join(process.cwd(), 'copy-of-vitnode-app'); diff --git a/packages/create-vitnode-app/src/questions.ts b/packages/create-vitnode-app/src/questions.ts index a56a50d6b..6e874cde8 100644 --- a/packages/create-vitnode-app/src/questions.ts +++ b/packages/create-vitnode-app/src/questions.ts @@ -1,14 +1,12 @@ +import { confirm, select } from '@inquirer/prompts'; import type { Command } from 'commander'; - -import { select } from '@inquirer/prompts'; -import { confirm } from '@inquirer/prompts'; import color from 'picocolors'; import { getAvailablePackageManagers } from './helpers/get-available-package-managers.js'; export interface CreateCliReturn { docker?: boolean; - eslint: boolean; + biome: boolean; install: boolean; mode: 'apiMonorepo' | 'onlyApi' | 'singleApp'; monorepo?: boolean; @@ -21,7 +19,7 @@ export const createQuestionsCli = async ( const optionsFromProgram = program.opts(); const options: CreateCliReturn = { packageManager: optionsFromProgram.packageManager, - eslint: optionsFromProgram.eslint, + biome: optionsFromProgram.biome, install: !optionsFromProgram.skipInstall, docker: optionsFromProgram.docker, mode: optionsFromProgram.mode, @@ -88,9 +86,9 @@ export const createQuestionsCli = async ( }); } - if (optionsFromProgram.eslint === undefined) { - options.eslint = await confirm({ - message: `Would you like to use ${color.blue('ESLint')}?`, + if (optionsFromProgram.biome === undefined) { + options.biome = await confirm({ + message: `Would you like to use ${color.blue('Biome')}?`, }); } diff --git a/packages/create-vitnode-app/src/validation.ts b/packages/create-vitnode-app/src/validation.ts index cca1a81e2..9410bff7c 100644 --- a/packages/create-vitnode-app/src/validation.ts +++ b/packages/create-vitnode-app/src/validation.ts @@ -1,6 +1,7 @@ +/** biome-ignore-all lint/suspicious/noConsole: */ +import { existsSync } from 'node:fs'; +import { basename, dirname, resolve } from 'node:path'; import { program } from 'commander'; -import { existsSync } from 'fs'; -import { basename, dirname, resolve } from 'path'; import color from 'picocolors'; import { isFolderEmpty } from './helpers/is-folder-empty.js'; diff --git a/packages/eslint/eslint.config.mjs b/packages/eslint/eslint.config.mjs deleted file mode 100644 index 0f8c6c266..000000000 --- a/packages/eslint/eslint.config.mjs +++ /dev/null @@ -1,174 +0,0 @@ -// @ts-check - -import { dirname } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import eslint from '@eslint/js'; -import eslintReact from '@eslint-react/eslint-plugin'; -import jsxA11y from 'eslint-plugin-jsx-a11y'; -import perfectionist from 'eslint-plugin-perfectionist'; -import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'; -import reactPlugin from 'eslint-plugin-react'; -import reactCompiler from 'eslint-plugin-react-compiler'; -import hooksPlugin from 'eslint-plugin-react-hooks'; -import tsEslint from 'typescript-eslint'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -export default [ - { - ignores: [ - 'next-env.d.ts', - 'dist', - '**/\\(main\\)/\\(plugins\\)/**', - '**/\\(auth\\)/\\(plugins\\)/**', - '.prettierrc.mjs', - 'node_modules', - 'eslint.config.mjs', - 'next.config.ts', - 'config/next.config.ts', - 'postcss.config.mjs', - '.turbo', - '.next', - 'global.d.ts', - 'tsup.config.ts', - '*.test.tsx', - ], - }, - eslint.configs.recommended, - eslintReact.configs.recommended, - ...tsEslint.configs.stylisticTypeChecked, - ...tsEslint.configs.strictTypeChecked, - eslintPluginPrettierRecommended, - jsxA11y.flatConfigs.recommended, - reactPlugin.configs.flat.recommended, - perfectionist.configs['recommended-natural'], - { - plugins: { - 'react-compiler': reactCompiler, - }, - rules: { - 'react-compiler/react-compiler': 'error', - }, - }, - { - files: ['**/*.{js,mjs,cjs,ts,jsx,tsx}'], - settings: { - react: { - version: 'detect', - }, - }, - languageOptions: { - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - }, - }, - }, - { - plugins: { - 'react-hooks': hooksPlugin, - }, - rules: { - 'react/react-in-jsx-scope': 'off', - ...hooksPlugin.configs.recommended.rules, - }, - }, - { files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'] }, - { - rules: { - 'react-hooks/exhaustive-deps': 'off', - '@eslint-react/no-context-provider': 'off', - '@eslint-react/no-unstable-default-props': 'off', - 'perfectionist/sort-array-includes': 'warn', - '@typescript-eslint/consistent-type-imports': 'error', - '@typescript-eslint/no-confusing-void-expression': 'off', - '@typescript-eslint/no-unnecessary-type-parameters': 'off', - '@typescript-eslint/no-misused-spread': 'off', - 'perfectionist/sort-decorators': 'warn', - 'perfectionist/sort-modules': 'off', - 'perfectionist/sort-switch-case': 'warn', - '@typescript-eslint/no-unnecessary-condition': 'off', - 'perfectionist/sort-named-exports': 'warn', - 'perfectionist/sort-enums': 'warn', - 'perfectionist/sort-exports': 'warn', - '@typescript-eslint/no-dynamic-delete': 'off', - 'perfectionist/sort-named-imports': 'warn', - 'perfectionist/sort-intersection-types': 'warn', - 'perfectionist/sort-interfaces': 'warn', - 'perfectionist/sort-union-types': 'warn', - 'perfectionist/sort-object-types': 'warn', - 'perfectionist/sort-jsx-props': 'warn', - 'perfectionist/sort-imports': 'warn', - '@typescript-eslint/no-unsafe-call': 'off', - 'perfectionist/sort-objects': 'off', - 'perfectionist/sort-classes': [ - 'warn', - { - groups: [ - 'constructor', - 'static-block', - 'index-signature', - 'static-property', - ['protected-property', 'protected-accessor-property'], - ['private-property', 'private-accessor-property'], - ['property', 'accessor-property'], - 'static-method', - 'protected-method', - 'private-method', - 'method', - ['get-method', 'set-method'], - 'unknown', - ], - }, - ], - 'no-console': 'error', - 'consistent-return': 'off', - '@typescript-eslint/no-unused-vars': [ - 'warn', - { - ignoreRestSiblings: false, - caughtErrorsIgnorePattern: '^_', - }, - ], - '@typescript-eslint/no-empty-function': 'off', - '@typescript-eslint/unbound-method': 'off', - '@typescript-eslint/no-misused-promises': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/no-unsafe-assignment': 'off', - '@typescript-eslint/no-unsafe-member-access': 'off', - '@typescript-eslint/no-useless-constructor': 'off', - '@typescript-eslint/prefer-readonly': 'warn', - '@typescript-eslint/require-array-sort-compare': 'error', - '@typescript-eslint/promise-function-async': 'error', - '@typescript-eslint/no-extraneous-class': 'off', - '@typescript-eslint/restrict-template-expressions': 'off', - '@typescript-eslint/consistent-type-exports': 'error', - '@typescript-eslint/no-unnecessary-qualifier': 'error', - '@typescript-eslint/no-useless-empty-export': 'error', - '@typescript-eslint/method-signature-style': 'warn', - 'newline-before-return': 'warn', - 'no-restricted-imports': [ - 'error', - { - name: 'next/link', - message: 'Please import from `vitnode-frontend/navigation` instead.', - }, - { - name: 'drizzle-orm/mysql-core', - message: 'Please import from `drizzle-orm/pg-core` instead.', - }, - { - name: 'next/navigation', - importNames: [ - 'redirect', - 'permanentRedirect', - 'useRouter', - 'usePathname', - ], - message: 'Please import from `vitnode-frontend/navigation` instead.', - }, - ], - }, - }, -]; diff --git a/packages/eslint/package.json b/packages/eslint/package.json index 6c90eb13f..1fd06e5d3 100644 --- a/packages/eslint/package.json +++ b/packages/eslint/package.json @@ -12,11 +12,8 @@ }, "keywords": [ "vitnode", - "eslint", - "eslint-config", "typescript", - "tsconfig", - "prettier" + "tsconfig" ], "type": "module", "exports": { @@ -35,23 +32,12 @@ }, "peerDependencies": { "eslint": "^9.0.0", - "prettier": "^3.0.0", "typescript": "5.9.x" }, "devDependencies": { "typescript": "^5.9.2" }, "dependencies": { - "@eslint-react/eslint-plugin": "^1.52.6", - "@eslint/js": "^9.33.0", - "eslint-config-prettier": "^10.1.8", - "eslint-plugin-jsx-a11y": "^6.10.2", - "eslint-plugin-perfectionist": "^4.15.0", - "eslint-plugin-prettier": "^5.5.4", - "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-compiler": "19.1.0-rc.2", - "eslint-plugin-react-hooks": "6.0.0-rc1", - "prettier-plugin-tailwindcss": "^0.6.14", "typescript-eslint": "^8.40.0" } } diff --git a/packages/eslint/prettierrc.mjs b/packages/eslint/prettierrc.mjs deleted file mode 100644 index 013dc965b..000000000 --- a/packages/eslint/prettierrc.mjs +++ /dev/null @@ -1,14 +0,0 @@ -/** - * @see https://prettier.io/docs/en/configuration.html - * @type {import("prettier").Config} - */ -const config = { - singleQuote: true, - arrowParens: 'avoid', - trailingComma: 'all', - printWidth: 80, - plugins: ['prettier-plugin-tailwindcss'], - tailwindFunctions: ['cn'], -}; - -export default config; \ No newline at end of file diff --git a/packages/vitnode/.npmignore b/packages/vitnode/.npmignore index d47df62a2..3039a8f6c 100644 --- a/packages/vitnode/.npmignore +++ b/packages/vitnode/.npmignore @@ -8,7 +8,6 @@ /node_modules /.turbo -/eslint.config.mjs /tsconfig.json /.swcrc /components.json diff --git a/packages/vitnode/eslint.config.mjs b/packages/vitnode/eslint.config.mjs deleted file mode 100644 index 0098d1c8a..000000000 --- a/packages/vitnode/eslint.config.mjs +++ /dev/null @@ -1,17 +0,0 @@ -import eslintVitNode from '@vitnode/eslint-config/eslint'; -import { fileURLToPath } from 'node:url'; -import { dirname } from 'node:path'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -export default [ - ...eslintVitNode, - { - languageOptions: { - parserOptions: { - project: './tsconfig.json', - tsconfigRootDir: __dirname, - }, - }, - }, -]; diff --git a/packages/vitnode/global.d.ts b/packages/vitnode/global.d.ts index 1f50c6e9b..d7f578cf3 100644 --- a/packages/vitnode/global.d.ts +++ b/packages/vitnode/global.d.ts @@ -1,6 +1,6 @@ /// -import type plugin from './src/locales/en.json'; +import plugin from './src/locales/en.json' with { type: 'json' }; declare module 'next-intl' { interface AppConfig { diff --git a/packages/vitnode/package.json b/packages/vitnode/package.json index 9b8967422..dbfa25be0 100644 --- a/packages/vitnode/package.json +++ b/packages/vitnode/package.json @@ -57,7 +57,6 @@ "dotenv": "^17.2.1", "drizzle-kit": "^0.31.4", "drizzle-orm": "^0.44.4", - "eslint": "^9.33.0", "hono": "^4.9.2", "jsdom": "^26.1.0", "lucide-react": "^0.540.0", @@ -101,8 +100,6 @@ "build:plugins": "tsc && swc src -d dist --config-file .swcrc && tsc-alias -p tsconfig.json", "dev": "concurrently \"tsc -w --preserveWatchOutput\" \"swc src -d dist --config-file .swcrc -w\" \"tsc-alias -w\" \"node dist/scripts/scripts.js plugin --w\"", "dev:email": "email dev --dir src/emails", - "lint": "eslint .", - "lint:fix": "eslint . --fix", "test": "vitest run", "test:watch": "vitest", "test:coverage": "vitest run --coverage" diff --git a/packages/vitnode/scripts/get-config.ts b/packages/vitnode/scripts/get-config.ts index 033045cc7..48bd0fb0b 100644 --- a/packages/vitnode/scripts/get-config.ts +++ b/packages/vitnode/scripts/get-config.ts @@ -1,6 +1,5 @@ -/* eslint-disable no-console */ -import { join } from 'path'; -import { pathToFileURL } from 'url'; +import { join } from 'node:path'; +import { pathToFileURL } from 'node:url'; import type { VitNodeApiConfig, VitNodeConfig } from '../src/vitnode.config'; @@ -22,6 +21,7 @@ export const getConfig = async ({ return config as ConfigType; } catch (error) { + // biome-ignore lint/suspicious/noConsole: console.error('Failed to load config:', error); process.exit(1); } diff --git a/packages/vitnode/scripts/plugin.ts b/packages/vitnode/scripts/plugin.ts index 86c7109d5..463a82077 100644 --- a/packages/vitnode/scripts/plugin.ts +++ b/packages/vitnode/scripts/plugin.ts @@ -1,13 +1,13 @@ -/* eslint-disable no-console */ -import chokidar from 'chokidar'; +/** biome-ignore-all lint/suspicious/noConsole: */ import { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, -} from 'fs'; -import { basename, join, relative } from 'path'; +} from 'node:fs'; +import { basename, join, relative } from 'node:path'; +import chokidar from 'chokidar'; import { buildInitialRouteMap, @@ -20,51 +20,35 @@ import { type SourceConfig, } from './shared/file-utils'; -export const processPlugin = ({ initMessage }: { initMessage: string }) => { - const pluginDir = process.cwd(); - const repoRoot = findRepoRoot(pluginDir); - const localeRoot = findLocaleRoot(repoRoot); - const routeMap = buildInitialRouteMap(localeRoot); - - // Get the package name from package.json for imports - let pluginName = basename(pluginDir); - try { - const packageJsonPath = join(pluginDir, 'package.json'); - - if (existsSync(packageJsonPath)) { - const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')); - pluginName = packageJson.name ?? ''; - } - } catch (error) { - console.error(`\x1b[31mError reading package.json:\x1b[0m`, error); - - return; - } - - // Transform plugin name for path usage - const pluginPathName = pluginName.replace(/\//g, '-').replace(/@/g, ''); - - // Detect app types by checking for config files - const detectAppType = (appPath: string) => { - const hasWebConfig = existsSync(join(appPath, 'src', 'vitnode.config.ts')); - const hasApiConfig = existsSync( - join(appPath, 'src', 'vitnode.api.config.ts'), - ); - - if (hasApiConfig && !hasWebConfig) return 'api'; - if (hasWebConfig) return 'web'; +/** + * Helper: detect if an app path is web, api, or null + */ +const detectAppType = (appPath: string) => { + const hasWebConfig = existsSync(join(appPath, 'src', 'vitnode.config.ts')); + const hasApiConfig = existsSync( + join(appPath, 'src', 'vitnode.api.config.ts'), + ); - return null; - }; + if (hasApiConfig && !hasWebConfig) return 'api'; + if (hasWebConfig) return 'web'; + return null; +}; - // Check if we're in a monorepo by looking for apps directories +/** + * Helper: collect source/destination mappings for a plugin + */ +const collectSources = ( + pluginDir: string, + repoRoot: string, + pluginName: string, + pluginPathName: string, + // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: +): SourceConfig[] => { + const sources: SourceConfig[] = []; const appsDir = join(repoRoot, 'apps'); const isMonorepo = existsSync(appsDir); - const sources: SourceConfig[] = []; - if (isMonorepo) { - // Monorepo: scan all apps and detect their types const appDirs = existsSync(appsDir) ? readdirSync(appsDir, { withFileTypes: true }) .filter(dirent => dirent.isDirectory()) @@ -76,105 +60,91 @@ export const processPlugin = ({ initMessage }: { initMessage: string }) => { const appType = detectAppType(appPath); if (appType === 'web') { - // Web app: copy app, app_admin, and locales - const mainDest = join( - appPath, - 'src', - 'app', - '[locale]', - '(main)', - join('(plugins)', `(${pluginPathName})`), - ); - const adminDest = join( - appPath, - 'src', - 'app', - '[locale]', - 'admin', - '(auth)', - join('(plugins)', `(${pluginPathName})`), - ); - const langDest = join(appPath, 'src', 'locales', pluginName); - sources.push( { sourceDir: join(pluginDir, 'src', 'app_admin'), - destinationDir: adminDest, + destinationDir: join( + appPath, + 'src', + 'app', + '[locale]', + 'admin', + '(auth)', + join('(plugins)', `(${pluginPathName})`), + ), }, { sourceDir: join(pluginDir, 'src', 'app'), - destinationDir: mainDest, + destinationDir: join( + appPath, + 'src', + 'app', + '[locale]', + '(main)', + join('(plugins)', `(${pluginPathName})`), + ), }, { sourceDir: join(pluginDir, 'src', 'locales'), - destinationDir: langDest, + destinationDir: join(appPath, 'src', 'locales', pluginName), }, ); } else if (appType === 'api') { - // API app: copy only locales - const apiLangDest = join(appPath, 'src', 'locales', pluginName); - sources.push({ sourceDir: join(pluginDir, 'src', 'locales'), - destinationDir: apiLangDest, + destinationDir: join(appPath, 'src', 'locales', pluginName), }); } } } else { - // Standalone project: check if we're running from within an app directory - // or if we need to copy to the current working directory const cwd = process.cwd(); const projectType = detectAppType(cwd); if (projectType === 'web') { - // Web project: copy all files to current working directory - const mainDest = join( - cwd, - 'src', - 'app', - '[locale]', - '(main)', - join('(plugins)', `(${pluginPathName})`), - ); - const adminDest = join( - cwd, - 'src', - 'app', - '[locale]', - 'admin', - '(auth)', - join('(plugins)', `(${pluginPathName})`), - ); - const langDest = join(cwd, 'src', 'locales', pluginName); - sources.push( { sourceDir: join(pluginDir, 'src', 'app_admin'), - destinationDir: adminDest, + destinationDir: join( + cwd, + 'src', + 'app', + '[locale]', + 'admin', + '(auth)', + join('(plugins)', `(${pluginPathName})`), + ), }, { sourceDir: join(pluginDir, 'src', 'app'), - destinationDir: mainDest, + destinationDir: join( + cwd, + 'src', + 'app', + '[locale]', + '(main)', + join('(plugins)', `(${pluginPathName})`), + ), }, { sourceDir: join(pluginDir, 'src', 'locales'), - destinationDir: langDest, + destinationDir: join(cwd, 'src', 'locales', pluginName), }, ); } else if (projectType === 'api') { - // API project: copy only locales to current working directory - const langDest = join(cwd, 'src', 'locales', pluginName); - sources.push({ sourceDir: join(pluginDir, 'src', 'locales'), - destinationDir: langDest, + destinationDir: join(cwd, 'src', 'locales', pluginName), }); } } - // tell the copier about both trees + return sources; +}; - // Create destination directories if they don't exist and source directories are not empty +/** + * Ensure destination directories exist for non-empty source dirs + */ +const ensureDestinationDirs = (sources: SourceConfig[]) => { for (const { sourceDir, destinationDir } of sources) { if ( existsSync(sourceDir) && @@ -184,7 +154,17 @@ export const processPlugin = ({ initMessage }: { initMessage: string }) => { mkdirSync(destinationDir, { recursive: true }); } } +}; +/** + * Create wrappers for copy and remove operations that close over routeMap/localeRoot/repoRoot + */ +const createFileOps = ( + pluginName: string, + routeMap: Map, + localeRoot: string, + repoRoot: string, +) => { const copyFileWrapper = (srcPath: string, destPath: string) => { copyFile(srcPath, destPath, pluginName, routeMap, localeRoot, repoRoot); }; @@ -197,7 +177,6 @@ export const processPlugin = ({ initMessage }: { initMessage: string }) => { if (/^page\.(tsx|ts|jsx|js)$/i.test(basename(filePath))) { const key = routeKey(filePath, localeRoot); - // only delete if this exact file is the one in the map if (routeMap.get(key) === filePath) { routeMap.delete(key); } @@ -208,56 +187,45 @@ export const processPlugin = ({ initMessage }: { initMessage: string }) => { } }; - const cleanupDeletedFiles = (sourceDir: string, destinationDir: string) => { - if (!existsSync(destinationDir)) return; - - // Check if this is a locale directory - if so, skip cleanup to preserve other language files - const isLocaleDir = destinationDir.includes(join('src', 'locales')); - if (isLocaleDir) { - return; // Skip cleanup for locale directories to preserve files from other plugins/languages - } - - const destFiles = getAllFiles(destinationDir); - for (const destFile of destFiles) { - const relativePath = relative(destinationDir, destFile); - const sourceFile = join(sourceDir, relativePath); + return { copyFileWrapper, removeFile }; +}; - if (!existsSync(sourceFile)) { - removeFile(destFile); - } +/** + * Remove files in destination that no longer exist in source (skip locales) + */ +const cleanupDeletedFiles = ( + sourceDir: string, + destinationDir: string, + removeFileFn: (p: string) => void, +) => { + if (!existsSync(destinationDir)) return; + + const isLocaleDir = destinationDir.includes(join('src', 'locales')); + if (isLocaleDir) return; + + const destFiles = getAllFiles(destinationDir); + for (const destFile of destFiles) { + const relativePath = relative(destinationDir, destFile); + const sourceFile = join(sourceDir, relativePath); + + if (!existsSync(sourceFile)) { + removeFileFn(destFile); } - }; - - // Clean up any files that were deleted while the script wasn't running - // Clean up deleted files for each source directory - for (const { sourceDir, destinationDir } of sources) { - cleanupDeletedFiles(sourceDir, destinationDir); } +}; - console.log( - `${initMessage} \x1b[34mWatching for changes in plugins...\x1b[0m`, - ); - - const sourceDirs = sources - .map(s => s.sourceDir) - .filter(dir => existsSync(dir)); - - const watcher = chokidar.watch(sourceDirs, { - ignoreInitial: false, - persistent: true, - }); - - const getDestinationPaths = (srcPath: string): string[] => { - // collect all matching sourceConfigs +/** + * Resolve destination paths for a given source path across all source configs + */ +const makeGetDestinationPaths = (sources: SourceConfig[]) => { + return (srcPath: string): string[] => { const candidates = sources.filter(({ sourceDir }) => { - // Ensure exact directory matching by checking if the path starts with sourceDir - // followed by a path separator (or is exactly the sourceDir) const normalizedSrcPath = srcPath.replace(/\\/g, '/'); const normalizedSourceDir = sourceDir.replace(/\\/g, '/'); return ( normalizedSrcPath === normalizedSourceDir || - normalizedSrcPath.startsWith(normalizedSourceDir + '/') + normalizedSrcPath.startsWith(`${normalizedSourceDir}/`) ); }); @@ -265,34 +233,100 @@ export const processPlugin = ({ initMessage }: { initMessage: string }) => { throw new Error(`No matching source directory for: ${srcPath}`); } - // Return all matching destination paths instead of just one return candidates.map(sourceConfig => { const relativePath = relative(sourceConfig.sourceDir, srcPath); - return join(sourceConfig.destinationDir, relativePath); }); }; +}; + +/** + * Setup chokidar watcher with handlers + */ +const setupWatcher = ( + sourceDirs: string[], + getDestinationPaths: (p: string) => string[], + copyFileWrapper: (s: string, d: string) => void, + removeFile: (p: string) => void, +) => { + const watcher = chokidar.watch(sourceDirs, { + ignoreInitial: false, + persistent: true, + }); watcher .on('add', filePath => { const destPaths = getDestinationPaths(filePath); - destPaths.forEach(destPath => { - copyFileWrapper(filePath, destPath); - }); + destPaths.forEach(destPath => copyFileWrapper(filePath, destPath)); }) .on('change', filePath => { const destPaths = getDestinationPaths(filePath); - destPaths.forEach(destPath => { - copyFileWrapper(filePath, destPath); - }); + destPaths.forEach(destPath => copyFileWrapper(filePath, destPath)); }) .on('unlink', filePath => { const destPaths = getDestinationPaths(filePath); - destPaths.forEach(destPath => { - removeFile(destPath); - }); + destPaths.forEach(destPath => removeFile(destPath)); }) .on('error', error => { console.error('\x1b[31mWatcher error:\x1b[0m', error); }); + + return watcher; +}; + +/** + * Main exported function (kept small and delegating to helpers) + */ +export const processPlugin = ({ initMessage }: { initMessage: string }) => { + const pluginDir = process.cwd(); + const repoRoot = findRepoRoot(pluginDir); + const localeRoot = findLocaleRoot(repoRoot); + const routeMap = buildInitialRouteMap(localeRoot); + + let pluginName = basename(pluginDir); + try { + const packageJsonPath = join(pluginDir, 'package.json'); + if (existsSync(packageJsonPath)) { + const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')); + pluginName = packageJson.name ?? ''; + } + } catch (error) { + console.error('\x1b[31mError reading package.json:\x1b[0m', error); + return; + } + + const pluginPathName = pluginName.replace(/\//g, '-').replace(/@/g, ''); + + const sources = collectSources( + pluginDir, + repoRoot, + pluginName, + pluginPathName, + ); + + ensureDestinationDirs(sources); + + const { copyFileWrapper, removeFile } = createFileOps( + pluginName, + routeMap, + localeRoot, + repoRoot, + ); + + // Cleanup deleted files that might have been removed while this script wasn't running + for (const { sourceDir, destinationDir } of sources) { + cleanupDeletedFiles(sourceDir, destinationDir, removeFile); + } + + console.log( + `${initMessage} \x1b[34mWatching for changes in plugins...\x1b[0m`, + ); + + const sourceDirs = sources + .map(s => s.sourceDir) + .filter(dir => existsSync(dir)); + + const getDestinationPaths = makeGetDestinationPaths(sources); + + setupWatcher(sourceDirs, getDestinationPaths, copyFileWrapper, removeFile); }; diff --git a/packages/vitnode/scripts/prepare-database.ts b/packages/vitnode/scripts/prepare-database.ts index da82196a1..ffbe8195d 100644 --- a/packages/vitnode/scripts/prepare-database.ts +++ b/packages/vitnode/scripts/prepare-database.ts @@ -1,5 +1,4 @@ -/* eslint-disable no-console */ - +/** biome-ignore-all lint/suspicious/noConsole: */ import { count } from 'drizzle-orm'; import { core_admin_permissions } from '@/database/admins.js'; @@ -202,16 +201,18 @@ export const prepareDatabase = async ({ ); } - for (let i = 0; i < steps.length; i++) { - const step = steps[i]; - const stepNum = `[${i + 1}/${steps.length}]`; - if (step.label === 'Insert initial data...') { - console.log(`\n${initMessage} ${stepNum} ${step.label}`); - } else { - console.log(`${initMessage} ${stepNum} ${step.label}`); - } - await step.action(); - } + await Promise.all( + steps.map((step, i) => { + const stepNum = `[${i + 1}/${steps.length}]`; + if (step.label === 'Insert initial data...') { + console.log(`\n${initMessage} ${stepNum} ${step.label}`); + } else { + console.log(`${initMessage} ${stepNum} ${step.label}`); + } + + return step.action(); + }), + ); console.log(`${initMessage} \x1b[32mInitial setup completed.\x1b[0m`); process.exit(0); diff --git a/packages/vitnode/scripts/prepare-plugins-files.ts b/packages/vitnode/scripts/prepare-plugins-files.ts index 156b8bef6..48457749e 100644 --- a/packages/vitnode/scripts/prepare-plugins-files.ts +++ b/packages/vitnode/scripts/prepare-plugins-files.ts @@ -1,7 +1,7 @@ -/* eslint-disable no-console */ -import { existsSync, readdirSync } from 'fs'; -import { readFile } from 'fs/promises'; -import { join, relative } from 'path'; +/** biome-ignore-all lint/suspicious/noConsole: */ +import { existsSync, readdirSync } from 'node:fs'; +import { readFile } from 'node:fs/promises'; +import { join, relative } from 'node:path'; import { getConfig } from './get-config'; import { @@ -71,6 +71,7 @@ export const preparePluginsFiles = async (flag?: string) => { }; await Promise.all( + // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: plugins.map(async pluginName => { const pluginPath = findPluginPath(pluginName); diff --git a/packages/vitnode/scripts/run-interactive-shell-command.ts b/packages/vitnode/scripts/run-interactive-shell-command.ts index 1fcd7ae12..5c36f5672 100644 --- a/packages/vitnode/scripts/run-interactive-shell-command.ts +++ b/packages/vitnode/scripts/run-interactive-shell-command.ts @@ -1,10 +1,10 @@ -import { spawn } from 'child_process'; +import { spawn } from 'node:child_process'; export const runInteractiveShellCommand = async ( cmd: string, args: string[] = [], ) => { - return new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { const child = spawn(cmd, args, { stdio: 'inherit', shell: true, diff --git a/packages/vitnode/scripts/scripts.ts b/packages/vitnode/scripts/scripts.ts index 6c957d714..cea35f2e1 100644 --- a/packages/vitnode/scripts/scripts.ts +++ b/packages/vitnode/scripts/scripts.ts @@ -1,7 +1,7 @@ #!/usr/bin/env node -/* eslint-disable no-console */ +/** biome-ignore-all lint/suspicious/noConsole: */ -import * as dotenv from 'dotenv'; +import { config } from 'dotenv'; import { processPlugin } from './plugin.js'; import { @@ -13,7 +13,7 @@ import { } from './prepare-database.js'; import { preparePluginsFiles } from './prepare-plugins-files.js'; -dotenv.config({ +config({ quiet: true, }); diff --git a/packages/vitnode/scripts/shared/file-utils.ts b/packages/vitnode/scripts/shared/file-utils.ts index b818bd0b0..9503406c5 100644 --- a/packages/vitnode/scripts/shared/file-utils.ts +++ b/packages/vitnode/scripts/shared/file-utils.ts @@ -1,4 +1,4 @@ -/* eslint-disable no-console */ +/** biome-ignore-all lint/suspicious/noConsole: */ import { copyFileSync, existsSync, @@ -6,7 +6,7 @@ import { readdirSync, readFileSync, writeFileSync, -} from 'fs'; +} from 'node:fs'; import { basename, dirname, @@ -16,7 +16,7 @@ import { relative, resolve, sep, -} from 'path'; +} from 'node:path'; // Regex patterns for import statements const relativeImportRegex = @@ -168,6 +168,7 @@ export function findLocaleRoot(repoRoot: string): string { const localeDirectories: string[] = []; if (!existsSync(searchDir)) return localeDirectories; + // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: const visit = (currentDir: string, depth = 0) => { // Limit search depth to avoid infinite recursion and performance issues if (depth > 4) return; @@ -263,6 +264,7 @@ export const copyFile = ( localeRoot: string, repoRoot: string, verbose = true, + // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: ) => { const fileName = basename(srcPath); if (pageFileRegex.test(fileName)) { @@ -308,12 +310,14 @@ export const copyFile = ( // Show even shorter, project-rooted paths for clarity // Remove everything before '/src/app' in the source path if present const srcAppIdx = srcPath.indexOf(join('src', 'app')); - const shortSrc = - srcAppIdx !== -1 - ? srcPath.substring(srcAppIdx) - : srcPath.startsWith(repoRoot) - ? relative(repoRoot, srcPath) - : srcPath; + let shortSrc: string; + if (srcAppIdx !== -1) { + shortSrc = srcPath.substring(srcAppIdx); + } else if (srcPath.startsWith(repoRoot)) { + shortSrc = relative(repoRoot, srcPath); + } else { + shortSrc = srcPath; + } const shortDest = destPath.startsWith(repoRoot) ? relative(repoRoot, destPath) : destPath; diff --git a/packages/vitnode/src/api/adapters/email/nodemailer.ts b/packages/vitnode/src/api/adapters/email/nodemailer.ts index 57e856203..02dbb45d7 100644 --- a/packages/vitnode/src/api/adapters/email/nodemailer.ts +++ b/packages/vitnode/src/api/adapters/email/nodemailer.ts @@ -19,7 +19,7 @@ export const NodemailerEmailAdapter = ({ }): EmailApiPlugin => { return { sendEmail: async ({ metadata, to, subject, html, replyTo }) => { - if (!host || !user || !password || !from) { + if (!(host && user && password && from)) { throw new Error('Missing nodemailer configuration'); } diff --git a/packages/vitnode/src/api/adapters/email/resend.ts b/packages/vitnode/src/api/adapters/email/resend.ts index 26dd0986d..4f4c82a16 100644 --- a/packages/vitnode/src/api/adapters/email/resend.ts +++ b/packages/vitnode/src/api/adapters/email/resend.ts @@ -11,7 +11,7 @@ export const ResendEmailAdapter = ({ }): EmailApiPlugin => { return { sendEmail: async ({ to, subject, replyTo, metadata, html }) => { - if (!apiKey || !from) { + if (!(apiKey && from)) { throw new Error('Missing Resend configuration'); } diff --git a/packages/vitnode/src/api/adapters/sso/discord.ts b/packages/vitnode/src/api/adapters/sso/discord.ts index 3ffd266ae..0119e947f 100644 --- a/packages/vitnode/src/api/adapters/sso/discord.ts +++ b/packages/vitnode/src/api/adapters/sso/discord.ts @@ -1,6 +1,5 @@ -import type { ContentfulStatusCode } from 'hono/utils/http-status'; - import { HTTPException } from 'hono/http-exception'; +import type { ContentfulStatusCode } from 'hono/utils/http-status'; import { z } from 'zod'; import type { SSOApiPlugin } from '@/api/models/sso'; @@ -28,7 +27,7 @@ export const DiscordSSOApiPlugin = ({ return { fetchToken: async code => { - if (!clientId || !clientSecret) { + if (!(clientId && clientSecret)) { throw new Error('Missing Discord client ID or secret'); } diff --git a/packages/vitnode/src/api/adapters/sso/facebook.ts b/packages/vitnode/src/api/adapters/sso/facebook.ts index d6e352a6e..aa921e7e8 100644 --- a/packages/vitnode/src/api/adapters/sso/facebook.ts +++ b/packages/vitnode/src/api/adapters/sso/facebook.ts @@ -1,6 +1,5 @@ -import type { ContentfulStatusCode } from 'hono/utils/http-status'; - import { HTTPException } from 'hono/http-exception'; +import type { ContentfulStatusCode } from 'hono/utils/http-status'; import { z } from 'zod'; import type { SSOApiPlugin } from '@/api/models/sso'; @@ -30,7 +29,7 @@ export const FacebookSSOApiPlugin = ({ id, name: 'Facebook', fetchToken: async code => { - if (!clientId || !clientSecret) { + if (!(clientId && clientSecret)) { throw new Error('Missing Facebook client ID or secret'); } diff --git a/packages/vitnode/src/api/adapters/sso/google.ts b/packages/vitnode/src/api/adapters/sso/google.ts index 597514b3e..6260ab89e 100644 --- a/packages/vitnode/src/api/adapters/sso/google.ts +++ b/packages/vitnode/src/api/adapters/sso/google.ts @@ -1,6 +1,5 @@ -import type { ContentfulStatusCode } from 'hono/utils/http-status'; - import { HTTPException } from 'hono/http-exception'; +import type { ContentfulStatusCode } from 'hono/utils/http-status'; import { z } from 'zod'; import type { SSOApiPlugin } from '@/api/models/sso'; @@ -31,7 +30,7 @@ export const GoogleSSOApiPlugin = ({ id, name: 'Google', fetchToken: async code => { - if (!clientId || !clientSecret) { + if (!(clientId && clientSecret)) { throw new Error('Missing Google client ID or secret'); } diff --git a/packages/vitnode/src/api/config.ts b/packages/vitnode/src/api/config.ts index f97de73b4..26f18cbce 100644 --- a/packages/vitnode/src/api/config.ts +++ b/packages/vitnode/src/api/config.ts @@ -1,15 +1,12 @@ +import { swaggerUI } from '@hono/swagger-ui'; import type { OpenAPIHono } from '@hono/zod-openapi'; import type { Context, Env, Schema } from 'hono'; - -import { swaggerUI } from '@hono/swagger-ui'; import { cors } from 'hono/cors'; import { csrf } from 'hono/csrf'; import { HTTPException } from 'hono/http-exception'; - -import type { VitNodeApiConfig } from '@/vitnode.config'; - import { newBuildPluginApiCore } from '@/api/plugin'; import { CONFIG_PLUGIN } from '@/config'; +import type { VitNodeApiConfig } from '@/vitnode.config'; import { globalAdminMiddleware, @@ -55,7 +52,7 @@ export function VitNodeAPI({ app.use(cors(corsOptions)); app.use(csrf(csrfOptions)); app.use('*', rateLimiterMiddleware(vitNodeApiConfig.rateLimiter)); - app.get('/swagger', swaggerUI({ url: `/api/swagger/doc` })); + app.get('/swagger', swaggerUI({ url: '/api/swagger/doc' })); app.use( '*', globalMiddleware({ @@ -70,7 +67,7 @@ export function VitNodeAPI({ ); app.use(async (c, next) => { if (c.req.path.includes('/admin/')) { - return globalAdminMiddleware()(c, next); + return await globalAdminMiddleware()(c, next); } return next(); diff --git a/packages/vitnode/src/api/lib/check-plugin-id.ts b/packages/vitnode/src/api/lib/check-plugin-id.ts index d937c2aab..3159b95a9 100644 --- a/packages/vitnode/src/api/lib/check-plugin-id.ts +++ b/packages/vitnode/src/api/lib/check-plugin-id.ts @@ -1,5 +1,5 @@ -import { existsSync, readFileSync } from 'fs'; -import { join, resolve } from 'path'; +import { existsSync, readFileSync } from 'node:fs'; +import { join, resolve } from 'node:path'; import { CONFIG } from '../../lib/config'; diff --git a/packages/vitnode/src/api/lib/logger-middleware.ts b/packages/vitnode/src/api/lib/logger-middleware.ts index fdfb9fcae..d67d76fec 100644 --- a/packages/vitnode/src/api/lib/logger-middleware.ts +++ b/packages/vitnode/src/api/lib/logger-middleware.ts @@ -1,4 +1,4 @@ -/* eslint-disable no-console */ +/** biome-ignore-all lint/suspicious/noConsole: */ import type { Context } from 'hono'; import { core_logs } from '@/database/logs'; diff --git a/packages/vitnode/src/api/lib/plugin.ts b/packages/vitnode/src/api/lib/plugin.ts index 5c1031680..feff79633 100644 --- a/packages/vitnode/src/api/lib/plugin.ts +++ b/packages/vitnode/src/api/lib/plugin.ts @@ -1,8 +1,6 @@ import { OpenAPIHono } from '@hono/zod-openapi'; - -import type { BuildModuleReturn } from './module'; - import { checkPluginId } from './check-plugin-id'; +import type { BuildModuleReturn } from './module'; export interface BuildPluginApiReturn { hono: OpenAPIHono; diff --git a/packages/vitnode/src/api/lib/route.ts b/packages/vitnode/src/api/lib/route.ts index b29681fc7..50670873d 100644 --- a/packages/vitnode/src/api/lib/route.ts +++ b/packages/vitnode/src/api/lib/route.ts @@ -55,7 +55,8 @@ export const buildRoute = < ...(route.withCaptcha ? [captchaMiddleware()] : []), ...(Array.isArray(route.middleware) ? route.middleware - : route.middleware + : // biome-ignore lint/style/noNestedTernary: + route.middleware ? [route.middleware] : []), ], diff --git a/packages/vitnode/src/api/lib/with-pagination.ts b/packages/vitnode/src/api/lib/with-pagination.ts index a7173613c..ecdecb2d2 100644 --- a/packages/vitnode/src/api/lib/with-pagination.ts +++ b/packages/vitnode/src/api/lib/with-pagination.ts @@ -1,4 +1,6 @@ +import { z } from '@hono/zod-openapi'; import type { ColumnBaseConfig, Placeholder, SQL } from 'drizzle-orm'; +import { and, asc, count, desc, gt, lt } from 'drizzle-orm'; import type { PgColumn, PgTable, @@ -7,8 +9,75 @@ import type { } from 'drizzle-orm/pg-core'; import type { Context } from 'hono'; -import { z } from '@hono/zod-openapi'; -import { and, asc, count, desc, gt, lt } from 'drizzle-orm'; +function parsePaginationParams(params: { + query: { cursor?: string; first?: string; last?: string }; +}): { cursor?: number; first?: number; last?: number } { + const cursor = params.query.cursor + ? parseInt(params.query.cursor, 10) + : undefined; + const first = params.query.first + ? Math.min(parseInt(params.query.first, 10), 100) + : undefined; + const last = params.query.last + ? Math.min(parseInt(params.query.last, 10), 100) + : undefined; + + if (first !== undefined && last !== undefined) { + throw new Error('Cannot specify both first and last'); + } + if (first !== undefined && first < 0) { + throw new Error('first must be positive'); + } + if (last !== undefined && last < 0) { + throw new Error('last must be positive'); + } + + return { cursor, first, last }; +} + +function getOrderFn( + isForward: boolean, + order: 'asc' | 'desc', +): typeof asc | typeof desc { + if (isForward) { + return order === 'asc' ? asc : desc; + } + return order === 'asc' ? desc : asc; +} + +function buildWhereWithCursor< + Primary extends ColumnBaseConfig<'number', string>, +>( + baseWhere: SQL | undefined, + cursor: number | undefined, + isForward: boolean, + order: 'asc' | 'desc', + table: PgTable, + primaryCursor: PgColumn, +): SQL | undefined { + if (!cursor) return baseWhere; + + const cursorFilter = + (isForward && order === 'asc') || (!isForward && order === 'desc') + ? gt + : lt; + + const cursorWhere = cursorFilter(table[primaryCursor.name], cursor); + return baseWhere ? and(baseWhere, cursorWhere) : cursorWhere; +} + +async function fetchTotalCount( + c: Context, + table: PgTable, + where: SQL | undefined, +): Promise { + const [{ count: totalCount }] = await c + .get('db') + .select({ count: count() }) + .from(table) + .where(where); + return totalCount; +} export async function withPagination< QueryMin extends Record, @@ -54,78 +123,39 @@ export async function withPagination< totalCount: number; }; }> { - // Parse and validate pagination parameters - const cursor = params.query.cursor - ? parseInt(params.query.cursor, 10) - : undefined; - const first = params.query.first - ? Math.min(parseInt(params.query.first, 10), 100) - : undefined; - const last = params.query.last - ? Math.min(parseInt(params.query.last, 10), 100) - : undefined; - - if (first !== undefined && last !== undefined) { - throw new Error('Cannot specify both first and last'); - } - - if (first !== undefined && first < 0) { - throw new Error('first must be positive'); - } - - if (last !== undefined && last < 0) { - throw new Error('last must be positive'); - } + const { cursor, first, last } = parsePaginationParams(params); - // Determine sort direction based on pagination parameters const isForward = last === undefined; - const orderFn = isForward - ? orderByFromParams.order === 'asc' - ? asc - : desc - : orderByFromParams.order === 'asc' - ? desc - : asc; - + const orderFn = getOrderFn(isForward, orderByFromParams.order); const orderBy: SQL = orderFn(table[orderByFromParams.column.name]); - // Build where clause with cursor - let where: SQL | undefined = whereFromParams; - if (cursor) { - const cursorFilter = isForward - ? orderByFromParams.order === 'asc' - ? gt - : lt - : orderByFromParams.order === 'asc' - ? lt - : gt; - - const cursorWhere = cursorFilter(table[primaryCursor.name], cursor); - where = where ? and(where, cursorWhere) : cursorWhere; - } - - // Get total count - const [{ count: totalCount }] = await c - .get('db') - .select({ count: count() }) - .from(table as PgTable) - .where(whereFromParams); + const where = buildWhereWithCursor( + whereFromParams, + cursor, + isForward, + orderByFromParams.order, + table, + primaryCursor, + ); + + const totalCount = await fetchTotalCount( + c, + table as PgTable, + whereFromParams, + ); - // Fetch one extra item to determine if there are more pages const limit = (first ?? last ?? 50) + 1; const edges = await query({ limit, where, orderBy }); - // Process results - const hasMore = edges.length > (first ?? last ?? edges.length); - const slicedEdges = edges.slice(0, first ?? last ?? edges.length); + const requested = first ?? last ?? edges.length; + const hasMore = edges.length > requested; + const slicedEdges = edges.slice(0, requested); const finalEdges = isForward ? slicedEdges : slicedEdges.reverse(); - // Prepare cursors const startCursor: null | number = (finalEdges[0]?.[primaryCursor.name] as number) ?? null; - const endCursor: null | number = - (finalEdges[finalEdges.length - 1]?.[primaryCursor.name] as number) ?? null; + (finalEdges.at(-1)?.[primaryCursor.name] as number) ?? null; return { pageInfo: { diff --git a/packages/vitnode/src/api/middlewares/captcha.middleware.ts b/packages/vitnode/src/api/middlewares/captcha.middleware.ts index 9cc1da440..6dc9deba3 100644 --- a/packages/vitnode/src/api/middlewares/captcha.middleware.ts +++ b/packages/vitnode/src/api/middlewares/captcha.middleware.ts @@ -39,7 +39,8 @@ const getResFromReCaptcha = async ({ score: data.success ? 1 : 0, 'error-codes': data['error-codes'], }; - } else if (captchaConfig.type === 'recaptcha_v3') { + } + if (captchaConfig.type === 'recaptcha_v3') { const res = await fetch( `https://www.google.com/recaptcha/api/siteverify?secret=${captchaConfig.secretKey}&response=${token}&remoteip=${userIp}`, { diff --git a/packages/vitnode/src/api/middlewares/global.middleware.ts b/packages/vitnode/src/api/middlewares/global.middleware.ts index f051f6462..d1cb7112e 100644 --- a/packages/vitnode/src/api/middlewares/global.middleware.ts +++ b/packages/vitnode/src/api/middlewares/global.middleware.ts @@ -1,19 +1,19 @@ import type { Context, Env, Next } from 'hono'; import { HTTPException } from 'hono/http-exception'; - -import type { VitNodeApiConfig, VitNodeConfig } from '@/vitnode.config'; - import { EmailModel, type EmailModelSendArgs } from '@/api/models/email'; import { SessionModel } from '@/api/models/session'; import { SessionAdminModel } from '@/api/models/session-admin'; - -import type { SSOApiPlugin } from '../models/sso'; - +import type { VitNodeApiConfig, VitNodeConfig } from '@/vitnode.config'; import { - loggerMiddleware, type LoggerMiddlewareType, + loggerMiddleware, } from '../lib/logger-middleware'; +import type { SSOApiPlugin } from '../models/sso'; + +declare module 'hono' { + interface ContextVariableMap extends EnvVariablesVitNode {} +} export interface EnvVitNode extends Env { Variables: EnvVariablesVitNode; @@ -77,11 +77,6 @@ export interface EnvVariablesVitNode { }; } -declare module 'hono' { - // eslint-disable-next-line @typescript-eslint/no-empty-object-type - interface ContextVariableMap extends EnvVariablesVitNode {} -} - export const globalMiddleware = ({ authorization, metadata, @@ -100,6 +95,7 @@ export const globalMiddleware = ({ | 'plugins' > & Pick) => { + // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: return async (c: Context, next: Next) => { // Collect possible IP header keys in order of trust/preference const ipHeaderKeys = [ diff --git a/packages/vitnode/src/api/middlewares/rate-limiter.middleware.ts b/packages/vitnode/src/api/middlewares/rate-limiter.middleware.ts index 3b3f1852e..37a38c175 100644 --- a/packages/vitnode/src/api/middlewares/rate-limiter.middleware.ts +++ b/packages/vitnode/src/api/middlewares/rate-limiter.middleware.ts @@ -29,7 +29,7 @@ export const rateLimiterMiddleware = ( ) => { if (CONFIG.node_development) { // In development, we disable the rate limiter for easier testing - return async (c: Context, next: Next) => { + return async (_c: Context, next: Next) => { await next(); }; } diff --git a/packages/vitnode/src/api/models/device.ts b/packages/vitnode/src/api/models/device.ts index 178c514b5..20fae1887 100644 --- a/packages/vitnode/src/api/models/device.ts +++ b/packages/vitnode/src/api/models/device.ts @@ -1,7 +1,6 @@ -import type { Context } from 'hono'; - -import { randomBytes } from 'crypto'; +import { randomBytes } from 'node:crypto'; import { eq } from 'drizzle-orm'; +import type { Context } from 'hono'; import { getCookie, setCookie } from 'hono/cookie'; import { core_sessions_known_devices } from '@/database/sessions'; diff --git a/packages/vitnode/src/api/models/email.ts b/packages/vitnode/src/api/models/email.ts index 1cbe31fed..6df130d9a 100644 --- a/packages/vitnode/src/api/models/email.ts +++ b/packages/vitnode/src/api/models/email.ts @@ -1,23 +1,11 @@ -import type { Context, ContextVariableMap } from 'hono'; -import type React from 'react'; - import { render } from '@react-email/components'; +import type { Context, ContextVariableMap } from 'hono'; import { HTTPException } from 'hono/http-exception'; +import type React from 'react'; -import { type DefaultTemplateEmailProps } from '../../emails/default-template'; +import type { DefaultTemplateEmailProps } from '../../emails/default-template'; import { CONFIG } from '../../lib/config'; -export interface EmailApiPlugin { - sendEmail: (args: { - html: string; - metadata: ContextVariableMap['core']['metadata']; - replyTo?: string; - subject: string; - text: string; - to: string; - }) => Promise; -} - interface EmailModelSendArgsWithUser { locale?: never; to?: never; @@ -36,6 +24,17 @@ interface EmailModelSendArgsWithEmail { user?: never; } +export interface EmailApiPlugin { + sendEmail: (args: { + html: string; + metadata: ContextVariableMap['core']['metadata']; + replyTo?: string; + subject: string; + text: string; + to: string; + }) => Promise; +} + export type EmailModelSendArgs = { content: ( props: Omit & @@ -47,7 +46,6 @@ export type EmailModelSendArgs = { subject: | ((props: Pick) => string) | string; - // eslint-disable-next-line perfectionist/sort-intersection-types } & (EmailModelSendArgsWithEmail | EmailModelSendArgsWithUser); export class EmailModel { @@ -93,6 +91,7 @@ export class EmailModel { const allMessages = await Promise.all(messagesPromises); const messages = allMessages.reduce( + // biome-ignore lint/performance/noAccumulatingSpread: (acc, curr) => ({ ...acc, ...curr }), {}, ) as Record; diff --git a/packages/vitnode/src/api/models/password.ts b/packages/vitnode/src/api/models/password.ts index d84de7cd5..82c0fb9b6 100644 --- a/packages/vitnode/src/api/models/password.ts +++ b/packages/vitnode/src/api/models/password.ts @@ -1,20 +1,20 @@ -import crypto from 'crypto'; +import crypto from 'node:crypto'; export class PasswordModel { async encryptPassword(password: string): Promise { - return new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { const salt = crypto.randomBytes(8).toString('hex'); crypto.scrypt(password, salt, 64, (err, derivedKey) => { if (err) reject(err); - resolve(salt + ':' + derivedKey.toString('hex')); + resolve(`${salt}:${derivedKey.toString('hex')}`); }); }); } async verifyPassword(password: string, hash: string): Promise { - return new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { const [salt, key] = hash.split(':'); const keyBuffer = Buffer.from(key, 'hex'); diff --git a/packages/vitnode/src/api/models/session-admin.ts b/packages/vitnode/src/api/models/session-admin.ts index acbb70aa9..b0f1303d4 100644 --- a/packages/vitnode/src/api/models/session-admin.ts +++ b/packages/vitnode/src/api/models/session-admin.ts @@ -1,6 +1,5 @@ -import type { Context } from 'hono'; - import { and, eq, gt, or } from 'drizzle-orm'; // Removed 'or' as it's safer not to use it here +import type { Context } from 'hono'; import { deleteCookie, getCookie, setCookie } from 'hono/cookie'; import { HTTPException } from 'hono/http-exception'; diff --git a/packages/vitnode/src/api/models/session.ts b/packages/vitnode/src/api/models/session.ts index 8bff250af..fdd2364b8 100644 --- a/packages/vitnode/src/api/models/session.ts +++ b/packages/vitnode/src/api/models/session.ts @@ -1,6 +1,5 @@ -import type { Context } from 'hono'; - import { and, eq, gt } from 'drizzle-orm'; +import type { Context } from 'hono'; import { deleteCookie, getCookie, setCookie } from 'hono/cookie'; import { core_sessions } from '@/database/sessions'; @@ -70,7 +69,7 @@ export class SessionModel { const device = await new DeviceModel(this.c).getDeviceId(); // Ensure both token and deviceId exist before proceeding - if (!token || !device.id) { + if (!(token && device.id)) { deleteCookie(this.c, this.c.get('core').authorization.cookieName); return; diff --git a/packages/vitnode/src/api/models/sso.ts b/packages/vitnode/src/api/models/sso.ts index 47bb743d7..8dbb095ff 100644 --- a/packages/vitnode/src/api/models/sso.ts +++ b/packages/vitnode/src/api/models/sso.ts @@ -1,7 +1,6 @@ -import type { Context } from 'hono'; - -import crypto from 'crypto'; +import crypto from 'node:crypto'; import { and, eq } from 'drizzle-orm'; +import type { Context } from 'hono'; import { deleteCookie, getCookie, setCookie } from 'hono/cookie'; import { HTTPException } from 'hono/http-exception'; @@ -131,10 +130,6 @@ export class SSOModel { // providerAccountId: userFromSSO.id, // userId: userWithEmail.id, // }); - - return { - userId: userWithEmail.id, - }; } return { @@ -151,7 +146,7 @@ export class SSOModel { crypto.scrypt(state, salt, 16, (err, derivedKey) => { if (err) reject(err); - resolve(salt + ':' + derivedKey.toString('hex')); + resolve(`${salt}:${derivedKey.toString('hex')}`); }); }); diff --git a/packages/vitnode/src/api/models/user/get-user-by-id.ts b/packages/vitnode/src/api/models/user/get-user-by-id.ts index 2f9e37b4d..c84840801 100644 --- a/packages/vitnode/src/api/models/user/get-user-by-id.ts +++ b/packages/vitnode/src/api/models/user/get-user-by-id.ts @@ -1,6 +1,5 @@ -import type { Context } from 'hono'; - import { eq } from 'drizzle-orm'; +import type { Context } from 'hono'; import { core_users } from '@/database/users'; diff --git a/packages/vitnode/src/api/models/user/sign-in-with-passwords.ts b/packages/vitnode/src/api/models/user/sign-in-with-passwords.ts index 531598603..3ace4c6f4 100644 --- a/packages/vitnode/src/api/models/user/sign-in-with-passwords.ts +++ b/packages/vitnode/src/api/models/user/sign-in-with-passwords.ts @@ -1,6 +1,5 @@ -import type { Context } from 'hono'; - import { eq } from 'drizzle-orm'; +import type { Context } from 'hono'; import { HTTPException } from 'hono/http-exception'; import { core_users } from '@/database/users'; diff --git a/packages/vitnode/src/api/models/user/sign-up.ts b/packages/vitnode/src/api/models/user/sign-up.ts index 47079e125..f3fa19a51 100644 --- a/packages/vitnode/src/api/models/user/sign-up.ts +++ b/packages/vitnode/src/api/models/user/sign-up.ts @@ -1,6 +1,5 @@ -import type { Context } from 'hono'; - import { and, count, eq, or } from 'drizzle-orm'; +import type { Context } from 'hono'; import { HTTPException } from 'hono/http-exception'; import { generateAvatarColor } from '@/api/modules/users/avatar-color'; @@ -127,7 +126,6 @@ export const signUp = async ( }) .returning(); - // eslint-disable-next-line @typescript-eslint/no-unused-vars const { password: _, ...user } = data; return user; diff --git a/packages/vitnode/src/app_admin/core/debug/page.tsx b/packages/vitnode/src/app_admin/core/debug/page.tsx index 92d64acd3..6ecb54b24 100644 --- a/packages/vitnode/src/app_admin/core/debug/page.tsx +++ b/packages/vitnode/src/app_admin/core/debug/page.tsx @@ -1,5 +1,5 @@ -import { getTranslations } from 'next-intl/server'; import dynamic from 'next/dynamic'; +import { getTranslations } from 'next-intl/server'; import React from 'react'; import { I18nProvider } from '@/components/i18n-provider'; diff --git a/packages/vitnode/src/app_admin/core/users/page.tsx b/packages/vitnode/src/app_admin/core/users/page.tsx index 5e5b7cf50..d8639cf32 100644 --- a/packages/vitnode/src/app_admin/core/users/page.tsx +++ b/packages/vitnode/src/app_admin/core/users/page.tsx @@ -1,7 +1,6 @@ import type { Metadata } from 'next/dist/types'; - -import { getTranslations } from 'next-intl/server'; import dynamic from 'next/dynamic'; +import { getTranslations } from 'next-intl/server'; import React from 'react'; import { DataTableSkeleton } from '@/components/table/data-table'; diff --git a/packages/vitnode/src/components/confirm-action/confirm-action-alert-dialog.tsx b/packages/vitnode/src/components/confirm-action/confirm-action-alert-dialog.tsx index 4eed1835d..b5ea0d08f 100644 --- a/packages/vitnode/src/components/confirm-action/confirm-action-alert-dialog.tsx +++ b/packages/vitnode/src/components/confirm-action/confirm-action-alert-dialog.tsx @@ -1,7 +1,7 @@ 'use client'; -import { useTranslations } from 'next-intl'; import dynamic from 'next/dynamic'; +import { useTranslations } from 'next-intl'; import React from 'react'; import { diff --git a/packages/vitnode/src/components/confirm-action/content.tsx b/packages/vitnode/src/components/confirm-action/content.tsx index ea7424fa4..f09325979 100644 --- a/packages/vitnode/src/components/confirm-action/content.tsx +++ b/packages/vitnode/src/components/confirm-action/content.tsx @@ -18,7 +18,6 @@ export const ContentConfirmAction = ({ const t = useTranslations('core.global.confirm_action'); const { setOpen } = useAlertDialog(); - // eslint-disable-next-line @typescript-eslint/no-unused-vars const [_, formAction, isLoading] = React.useActionState(async () => { await onSubmit({ onClose: () => setOpen?.(false) }); }, null); diff --git a/packages/vitnode/src/components/date-format.tsx b/packages/vitnode/src/components/date-format.tsx index 039c0480f..6ecabd830 100644 --- a/packages/vitnode/src/components/date-format.tsx +++ b/packages/vitnode/src/components/date-format.tsx @@ -24,7 +24,7 @@ export const DateFormat = ({ updateInterval: updateInterval ?? // Update it every 1 minute if the date is from today, otherwise don't update - (new Date().getTime() - dateToFormat.getTime() < 86400000 ? 60000 : 0), + (Date.now() - dateToFormat.getTime() < 86400000 ? 60000 : 0), }); const fullDate = format.dateTime(dateToFormat, { diff --git a/packages/vitnode/src/components/form/auto-form.tsx b/packages/vitnode/src/components/form/auto-form.tsx index b7a7a227c..23e0e23c8 100644 --- a/packages/vitnode/src/components/form/auto-form.tsx +++ b/packages/vitnode/src/components/form/auto-form.tsx @@ -7,13 +7,11 @@ import { type FieldPath, type FieldValues, type Mode, - useForm, type UseFormReturn, + useForm, } from 'react-hook-form'; -import * as z from 'zod'; - +import z from 'zod'; import type { routeMiddlewareSchema } from '../../api/modules/middleware/route'; - import { useCaptcha } from '../../hooks/use-captcha'; import { getDefaults, @@ -24,21 +22,6 @@ import { Button } from '../ui/button'; import { DialogClose, DialogFooter, useDialog } from '../ui/dialog'; import { Form, FormField } from '../ui/form'; -export interface ItemAutoFormComponentProps { - description?: React.ReactNode; - field: ControllerRenderProps; - label?: React.ReactNode; - labelRight?: React.ReactNode; - otherProps: { - enum?: string[]; - isOptional?: boolean; - maxLength?: number; - minLength?: number; - pattern?: string; - type?: string; - }; -} - type ItemAutoFormProps< T extends z.ZodObject = z.ZodObject, TName extends FieldPath> = FieldPath>, @@ -54,6 +37,21 @@ type ItemAutoFormProps< label?: React.ReactNode; }; +export interface ItemAutoFormComponentProps { + description?: React.ReactNode; + field: ControllerRenderProps; + label?: React.ReactNode; + labelRight?: React.ReactNode; + otherProps: { + enum?: string[]; + isOptional?: boolean; + maxLength?: number; + minLength?: number; + pattern?: string; + type?: string; + }; +} + export type AutoFormOnSubmit< T extends z.ZodObject, TContext = unknown, @@ -161,6 +159,7 @@ export function AutoForm< render={({ field }) => { return ( <> diff --git a/packages/vitnode/src/components/form/fields/checkbox.tsx b/packages/vitnode/src/components/form/fields/checkbox.tsx index d6d01ab63..49c34fd1e 100644 --- a/packages/vitnode/src/components/form/fields/checkbox.tsx +++ b/packages/vitnode/src/components/form/fields/checkbox.tsx @@ -1,7 +1,6 @@ -import type { ItemAutoFormComponentProps } from '../auto-form'; - import { Checkbox } from '../../ui/checkbox'; import { FormControl, FormItem, FormMessage } from '../../ui/form'; +import type { ItemAutoFormComponentProps } from '../auto-form'; import { AutoFormDesc } from '../common/desc'; import { AutoFormLabel } from '../common/label'; diff --git a/packages/vitnode/src/components/form/fields/combobox-async.tsx b/packages/vitnode/src/components/form/fields/combobox-async.tsx index 3df0449cf..428460d38 100644 --- a/packages/vitnode/src/components/form/fields/combobox-async.tsx +++ b/packages/vitnode/src/components/form/fields/combobox-async.tsx @@ -20,10 +20,8 @@ import { PopoverTrigger, } from '@/components/ui/popover'; import { cn } from '@/lib/utils'; - -import type { ItemAutoFormComponentProps } from '../auto-form'; - import { Skeleton } from '../../ui/skeleton'; +import type { ItemAutoFormComponentProps } from '../auto-form'; import { AutoFormDesc } from '../common/desc'; import { AutoFormLabel } from '../common/label'; @@ -107,43 +105,47 @@ export const AutoFormComboboxAsync = ({ /> - {isLoading ? ( -
- - -
- ) : ( - <> - {data?.length === 0 ? ( - {t('results_not_found')} - ) : ( - - {(data ?? []).map(({ label, value }) => ( - { - field.onChange({ - label, - value, - }); - }} - value={label} - > - {label} - - - ))} - - )} - - )} + {(() => { + if (isLoading) { + return ( +
+ + +
+ ); + } + + if ((data ?? []).length === 0) { + return {t('results_not_found')}; + } + + return ( + + {(data ?? []).map(({ label, value }) => ( + { + field.onChange({ + label, + value, + }); + }} + value={label} + > + {label} + + + ))} + + ); + })()}
diff --git a/packages/vitnode/src/components/form/fields/combobox.tsx b/packages/vitnode/src/components/form/fields/combobox.tsx index d44e6e5b7..5bd6317d7 100644 --- a/packages/vitnode/src/components/form/fields/combobox.tsx +++ b/packages/vitnode/src/components/form/fields/combobox.tsx @@ -1,6 +1,6 @@ import { Check, ChevronsUpDown } from 'lucide-react'; import { useTranslations } from 'next-intl'; -import React from 'react'; +import type React from 'react'; import { Button } from '@/components/ui/button'; import { diff --git a/packages/vitnode/src/components/form/fields/input.tsx b/packages/vitnode/src/components/form/fields/input.tsx index 3e9beed8f..6dc8671d7 100644 --- a/packages/vitnode/src/components/form/fields/input.tsx +++ b/packages/vitnode/src/components/form/fields/input.tsx @@ -1,7 +1,6 @@ -import type { ItemAutoFormComponentProps } from '../auto-form'; - import { FormControl, FormItem, FormMessage } from '../../ui/form'; import { Input } from '../../ui/input'; +import type { ItemAutoFormComponentProps } from '../auto-form'; import { AutoFormDesc } from '../common/desc'; import { AutoFormLabel } from '../common/label'; diff --git a/packages/vitnode/src/components/form/fields/radio-group.tsx b/packages/vitnode/src/components/form/fields/radio-group.tsx index ffb742c8f..04e3afddd 100644 --- a/packages/vitnode/src/components/form/fields/radio-group.tsx +++ b/packages/vitnode/src/components/form/fields/radio-group.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import type React from 'react'; import { FormControl, diff --git a/packages/vitnode/src/components/form/fields/select.tsx b/packages/vitnode/src/components/form/fields/select.tsx index c21008117..cfa13ff11 100644 --- a/packages/vitnode/src/components/form/fields/select.tsx +++ b/packages/vitnode/src/components/form/fields/select.tsx @@ -1,5 +1,5 @@ import { useTranslations } from 'next-intl'; -import React from 'react'; +import type React from 'react'; import { FormControl, FormItem, FormMessage } from '@/components/ui/form'; import { diff --git a/packages/vitnode/src/components/form/fields/switch.tsx b/packages/vitnode/src/components/form/fields/switch.tsx index 1b9d9ccd0..c592a78d1 100644 --- a/packages/vitnode/src/components/form/fields/switch.tsx +++ b/packages/vitnode/src/components/form/fields/switch.tsx @@ -17,7 +17,6 @@ export const AutoFormSwitch = ({ Omit, 'checked'>) => { return ( - {/* eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing */} {(label || description) && (
{label && ( diff --git a/packages/vitnode/src/components/form/fields/textarea.tsx b/packages/vitnode/src/components/form/fields/textarea.tsx index 6ee42e7b6..833edac9d 100644 --- a/packages/vitnode/src/components/form/fields/textarea.tsx +++ b/packages/vitnode/src/components/form/fields/textarea.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import type React from 'react'; import { FormControl, FormItem, FormMessage } from '@/components/ui/form'; import { Textarea } from '@/components/ui/textarea'; diff --git a/packages/vitnode/src/components/i18n-provider.tsx b/packages/vitnode/src/components/i18n-provider.tsx index a49aea6a8..893cdf66a 100644 --- a/packages/vitnode/src/components/i18n-provider.tsx +++ b/packages/vitnode/src/components/i18n-provider.tsx @@ -12,7 +12,7 @@ const pick = (obj: object, paths: string[]) => { let dest = result; for (let i = 0; i < keys.length; i++) { const key = keys[i]; - if (src && Object.prototype.hasOwnProperty.call(src, key)) { + if (src && Object.hasOwn(src, key)) { if (i === keys.length - 1) { dest[key] = src[key]; } else { diff --git a/packages/vitnode/src/components/logo-vitnode.tsx b/packages/vitnode/src/components/logo-vitnode.tsx index abb0ed17d..94549c568 100644 --- a/packages/vitnode/src/components/logo-vitnode.tsx +++ b/packages/vitnode/src/components/logo-vitnode.tsx @@ -17,6 +17,7 @@ export const LogoVitNode = ({ xmlns="http://www.w3.org/2000/svg" {...props} > + Logo VitNode + Logo VitNode { orderBy?: keyof T; } -export const DataTableSkeleton = ({ columns }: { columns: number }) => ( -
-
- - - - {Array.from({ length: columns }).map((_, i) => ( - - - - ))} - - - - {Array.from({ length: 6 }).map((_, i) => ( - - {Array.from({ length: columns }).map((_, j) => ( - +export const DataTableSkeleton = ({ columns }: { columns: number }) => { + const headerIds = React.useMemo( + () => + Array.from({ length: columns }).map( + () => `s-head-${Math.random().toString(36).slice(2, 9)}`, + ), + [columns], + ); + + const rowIds = React.useMemo( + () => + Array.from({ length: 6 }).map( + () => `s-row-${Math.random().toString(36).slice(2, 9)}`, + ), + [], + ); + + return ( +
+
+
- -
+ + + {headerIds.map(hid => ( + + + ))} - ))} - -
-
+ + + {rowIds.map(rid => ( + + {headerIds.map((_, j) => { + const cellId = `s-cell-${rid}-${j}`; + return ( + + + + ); + })} + + ))} + + +
-
- +
+ +
-
-); + ); +}; export function DataTable( props: Omit, 'columns'> & @@ -79,7 +97,7 @@ export function DataTable( }; }, ) { - if (!props.edges || !props.pageInfo) { + if (!(props.edges && props.pageInfo)) { return ; } diff --git a/packages/vitnode/src/components/table/order-table-head.tsx b/packages/vitnode/src/components/table/order-table-head.tsx index ed67db5d6..7a38fc273 100644 --- a/packages/vitnode/src/components/table/order-table-head.tsx +++ b/packages/vitnode/src/components/table/order-table-head.tsx @@ -5,11 +5,9 @@ import { useSearchParams } from 'next/navigation'; import React from 'react'; import { usePathname, useRouter } from '@/lib/navigation'; - -import type { DataTable, DataTableTMin } from './data-table'; - import { Button } from '../ui/button'; import { Loader } from '../ui/loader'; +import type { DataTable, DataTableTMin } from './data-table'; export function OrderTableHeadDataTable({ id, @@ -48,17 +46,18 @@ export function OrderTableHeadDataTable({ variant="ghost" > {children} - {isPending ? ( - - ) : isActive ? ( - currentOrder === 'asc' ? ( - - ) : ( - - ) - ) : ( - - )} + {(() => { + if (isPending) { + return ; + } + if (isActive) { + if (currentOrder === 'asc') { + return ; + } + return ; + } + return ; + })()} ); } diff --git a/packages/vitnode/src/components/table/pagination.tsx b/packages/vitnode/src/components/table/pagination.tsx index 02e85d901..652d9f2a3 100644 --- a/packages/vitnode/src/components/table/pagination.tsx +++ b/packages/vitnode/src/components/table/pagination.tsx @@ -1,8 +1,8 @@ 'use client'; import { ChevronLeftIcon, ChevronRightIcon } from 'lucide-react'; -import { useTranslations } from 'next-intl'; import { useSearchParams } from 'next/navigation'; +import { useTranslations } from 'next-intl'; import React from 'react'; import { usePathname, useRouter } from '@/lib/navigation'; diff --git a/packages/vitnode/src/components/ui/accordion.tsx b/packages/vitnode/src/components/ui/accordion.tsx index 3a5341429..7b8353d94 100644 --- a/packages/vitnode/src/components/ui/accordion.tsx +++ b/packages/vitnode/src/components/ui/accordion.tsx @@ -2,7 +2,7 @@ import { ChevronDownIcon } from 'lucide-react'; import { Accordion as AccordionPrimitive } from 'radix-ui'; -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/alert-dialog.tsx b/packages/vitnode/src/components/ui/alert-dialog.tsx index 744096358..aec5538cc 100644 --- a/packages/vitnode/src/components/ui/alert-dialog.tsx +++ b/packages/vitnode/src/components/ui/alert-dialog.tsx @@ -1,7 +1,7 @@ 'use client'; import { AlertDialog as AlertDialogPrimitive } from 'radix-ui'; -import * as React from 'react'; +import React from 'react'; import { buttonVariants } from '@/components/ui/button'; import { cn } from '@/lib/utils'; @@ -16,7 +16,7 @@ const AlertDialogContext = React.createContext<{ setOpen: () => {}, }); -export const useAlertDialog = () => React.use(AlertDialogContext); +const useAlertDialog = () => React.use(AlertDialogContext); function AlertDialog({ onOpenChange, @@ -206,4 +206,5 @@ export { AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, + useAlertDialog, }; diff --git a/packages/vitnode/src/components/ui/alert.tsx b/packages/vitnode/src/components/ui/alert.tsx index 82f4cc011..966cdac4b 100644 --- a/packages/vitnode/src/components/ui/alert.tsx +++ b/packages/vitnode/src/components/ui/alert.tsx @@ -1,5 +1,5 @@ import { cva, type VariantProps } from 'class-variance-authority'; -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/badge.tsx b/packages/vitnode/src/components/ui/badge.tsx index fd169c079..d4f4cc762 100644 --- a/packages/vitnode/src/components/ui/badge.tsx +++ b/packages/vitnode/src/components/ui/badge.tsx @@ -1,6 +1,6 @@ import { cva, type VariantProps } from 'class-variance-authority'; import { Slot } from 'radix-ui'; -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/button.tsx b/packages/vitnode/src/components/ui/button.tsx index dc0a2ddef..59c37af8f 100644 --- a/packages/vitnode/src/components/ui/button.tsx +++ b/packages/vitnode/src/components/ui/button.tsx @@ -1,5 +1,5 @@ import { cva, type VariantProps } from 'class-variance-authority'; -import * as React from 'react'; +import type * as React from 'react'; import { ClientButton } from './button-client'; @@ -36,7 +36,7 @@ const buttonVariants = cva( }, ); -export type ButtonProps = React.ComponentProps<'button'> & +type ButtonProps = React.ComponentProps<'button'> & VariantProps & { asChild?: boolean; isLoading?: boolean; @@ -49,4 +49,4 @@ function Button(props: ButtonProps) { return ; } -export { Button, buttonVariants }; +export { Button, buttonVariants, type ButtonProps }; diff --git a/packages/vitnode/src/components/ui/checkbox.tsx b/packages/vitnode/src/components/ui/checkbox.tsx index 03e070b14..018dce3ec 100644 --- a/packages/vitnode/src/components/ui/checkbox.tsx +++ b/packages/vitnode/src/components/ui/checkbox.tsx @@ -2,7 +2,7 @@ import { CheckIcon } from 'lucide-react'; import { Checkbox as CheckboxPrimitive } from 'radix-ui'; -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/command.tsx b/packages/vitnode/src/components/ui/command.tsx index 1f2dbc726..1651713b9 100644 --- a/packages/vitnode/src/components/ui/command.tsx +++ b/packages/vitnode/src/components/ui/command.tsx @@ -2,7 +2,7 @@ import { Command as CommandPrimitive } from 'cmdk'; import { SearchIcon } from 'lucide-react'; -import * as React from 'react'; +import type * as React from 'react'; import { Dialog, diff --git a/packages/vitnode/src/components/ui/context-menu.tsx b/packages/vitnode/src/components/ui/context-menu.tsx index 7221b0b76..250d1ba09 100644 --- a/packages/vitnode/src/components/ui/context-menu.tsx +++ b/packages/vitnode/src/components/ui/context-menu.tsx @@ -2,7 +2,7 @@ import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react'; import { ContextMenu as ContextMenuPrimitive } from 'radix-ui'; -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/dialog.tsx b/packages/vitnode/src/components/ui/dialog.tsx index c2e96953a..258c6b92a 100644 --- a/packages/vitnode/src/components/ui/dialog.tsx +++ b/packages/vitnode/src/components/ui/dialog.tsx @@ -3,7 +3,7 @@ import { XIcon } from 'lucide-react'; import { useTranslations } from 'next-intl'; import { Dialog as DialogPrimitive } from 'radix-ui'; -import * as React from 'react'; +import React from 'react'; import { cn } from '@/lib/utils'; @@ -31,7 +31,7 @@ const DialogContext = React.createContext<{ setOpenAlertDialogBeforeClose: () => {}, }); -export const useDialog = () => React.use(DialogContext); +const useDialog = () => React.use(DialogContext); function Dialog({ onOpenChange, @@ -271,4 +271,5 @@ export { DialogPortal, DialogTitle, DialogTrigger, + useDialog, }; diff --git a/packages/vitnode/src/components/ui/drawer.tsx b/packages/vitnode/src/components/ui/drawer.tsx index c404c4381..bb5cf3781 100644 --- a/packages/vitnode/src/components/ui/drawer.tsx +++ b/packages/vitnode/src/components/ui/drawer.tsx @@ -1,6 +1,6 @@ 'use client'; -import * as React from 'react'; +import type * as React from 'react'; import { Drawer as DrawerPrimitive } from 'vaul'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/dropdown-menu.tsx b/packages/vitnode/src/components/ui/dropdown-menu.tsx index 7f508e955..d6ba426a6 100644 --- a/packages/vitnode/src/components/ui/dropdown-menu.tsx +++ b/packages/vitnode/src/components/ui/dropdown-menu.tsx @@ -2,7 +2,7 @@ import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react'; import { DropdownMenu as DropdownMenuPrimitive } from 'radix-ui'; -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/form.tsx b/packages/vitnode/src/components/ui/form.tsx index 08dca7814..c2ad16aef 100644 --- a/packages/vitnode/src/components/ui/form.tsx +++ b/packages/vitnode/src/components/ui/form.tsx @@ -1,10 +1,9 @@ 'use client'; -import type { Label as LabelPrimitive } from 'radix-ui'; - import { useTranslations } from 'next-intl'; +import type { Label as LabelPrimitive } from 'radix-ui'; import { Slot } from 'radix-ui'; -import * as React from 'react'; +import React from 'react'; import { Controller, type ControllerProps, @@ -179,7 +178,7 @@ function FormControl({ ...props }: React.ComponentProps) { return ( ) { return ( -
+
); diff --git a/packages/vitnode/src/components/ui/input.tsx b/packages/vitnode/src/components/ui/input.tsx index 393113375..af77abad4 100644 --- a/packages/vitnode/src/components/ui/input.tsx +++ b/packages/vitnode/src/components/ui/input.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/label.tsx b/packages/vitnode/src/components/ui/label.tsx index 8e27e3ac9..04d6e67d0 100644 --- a/packages/vitnode/src/components/ui/label.tsx +++ b/packages/vitnode/src/components/ui/label.tsx @@ -1,7 +1,7 @@ 'use client'; import { Label as LabelPrimitive } from 'radix-ui'; -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/menubar.tsx b/packages/vitnode/src/components/ui/menubar.tsx index 28f6dcbb8..b50dd5dc7 100644 --- a/packages/vitnode/src/components/ui/menubar.tsx +++ b/packages/vitnode/src/components/ui/menubar.tsx @@ -2,7 +2,7 @@ import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react'; import { Menubar as MenubarPrimitive } from 'radix-ui'; -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/navigation-menu.tsx b/packages/vitnode/src/components/ui/navigation-menu.tsx index a94c1bee2..a5e7e6f6d 100644 --- a/packages/vitnode/src/components/ui/navigation-menu.tsx +++ b/packages/vitnode/src/components/ui/navigation-menu.tsx @@ -1,7 +1,7 @@ import { cva } from 'class-variance-authority'; import { ChevronDownIcon } from 'lucide-react'; import { NavigationMenu as NavigationMenuPrimitive } from 'radix-ui'; -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/popover.tsx b/packages/vitnode/src/components/ui/popover.tsx index 4cd18eac1..551bb5bfe 100644 --- a/packages/vitnode/src/components/ui/popover.tsx +++ b/packages/vitnode/src/components/ui/popover.tsx @@ -1,7 +1,7 @@ 'use client'; import { Popover as PopoverPrimitive } from 'radix-ui'; -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/progress.tsx b/packages/vitnode/src/components/ui/progress.tsx index 0228cef16..380e13b65 100644 --- a/packages/vitnode/src/components/ui/progress.tsx +++ b/packages/vitnode/src/components/ui/progress.tsx @@ -1,7 +1,7 @@ 'use client'; import { Progress as ProgressPrimitive } from 'radix-ui'; -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/radio-group.tsx b/packages/vitnode/src/components/ui/radio-group.tsx index 84e0fb1a2..14a704f31 100644 --- a/packages/vitnode/src/components/ui/radio-group.tsx +++ b/packages/vitnode/src/components/ui/radio-group.tsx @@ -2,7 +2,7 @@ import { CircleIcon } from 'lucide-react'; import { RadioGroup as RadioGroupPrimitive } from 'radix-ui'; -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/select.tsx b/packages/vitnode/src/components/ui/select.tsx index 66589b552..c575a09d5 100644 --- a/packages/vitnode/src/components/ui/select.tsx +++ b/packages/vitnode/src/components/ui/select.tsx @@ -2,7 +2,7 @@ import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from 'lucide-react'; import { Select as SelectPrimitive } from 'radix-ui'; -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/separator.tsx b/packages/vitnode/src/components/ui/separator.tsx index 624811f57..ee6e73f05 100644 --- a/packages/vitnode/src/components/ui/separator.tsx +++ b/packages/vitnode/src/components/ui/separator.tsx @@ -1,7 +1,7 @@ 'use client'; import { Separator as SeparatorPrimitive } from 'radix-ui'; -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/sheet.tsx b/packages/vitnode/src/components/ui/sheet.tsx index c3ddd8339..6c489adad 100644 --- a/packages/vitnode/src/components/ui/sheet.tsx +++ b/packages/vitnode/src/components/ui/sheet.tsx @@ -2,7 +2,7 @@ import { XIcon } from 'lucide-react'; import { Dialog as SheetPrimitive } from 'radix-ui'; -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/sidebar.tsx b/packages/vitnode/src/components/ui/sidebar.tsx index ef43c346d..455fe965b 100644 --- a/packages/vitnode/src/components/ui/sidebar.tsx +++ b/packages/vitnode/src/components/ui/sidebar.tsx @@ -4,7 +4,7 @@ import { cva, type VariantProps } from 'class-variance-authority'; import { PanelLeftIcon } from 'lucide-react'; import { useTranslations } from 'next-intl'; import { Slot } from 'radix-ui'; -import * as React from 'react'; +import React from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -84,7 +84,7 @@ function SidebarProvider({ } // This sets the cookie to keep the sidebar state. - // eslint-disable-next-line react-compiler/react-compiler + // biome-ignore lint/suspicious/noDocumentCookie: document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`; }, [setOpenProp, open], @@ -93,7 +93,7 @@ function SidebarProvider({ // Helper to toggle the sidebar. const toggleSidebar = React.useCallback(() => { return isMobile ? setOpenMobile(open => !open) : setOpen(open => !open); - }, [isMobile, setOpen, setOpenMobile]); + }, [isMobile, setOpen]); // Adds a keyboard shortcut to toggle the sidebar. React.useEffect(() => { @@ -126,7 +126,7 @@ function SidebarProvider({ setOpenMobile, toggleSidebar, }), - [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar], + [state, open, setOpen, isMobile, openMobile, toggleSidebar], ); return ( diff --git a/packages/vitnode/src/components/ui/slider.tsx b/packages/vitnode/src/components/ui/slider.tsx index 6dafd1066..5d2b88f4f 100644 --- a/packages/vitnode/src/components/ui/slider.tsx +++ b/packages/vitnode/src/components/ui/slider.tsx @@ -1,7 +1,7 @@ 'use client'; import { Slider as SliderPrimitive } from 'radix-ui'; -import * as React from 'react'; +import React from 'react'; import { cn } from '@/lib/utils'; @@ -13,15 +13,15 @@ function Slider({ max = 100, ...props }: React.ComponentProps) { - const _values = React.useMemo( - () => - Array.isArray(value) - ? value - : Array.isArray(defaultValue) - ? defaultValue - : [min, max], - [value, defaultValue, min, max], - ); + const values = React.useMemo(() => { + if (Array.isArray(value)) { + return value; + } + if (Array.isArray(defaultValue)) { + return defaultValue; + } + return [min, max]; + }, [value, defaultValue, min, max]); return ( - {Array.from({ length: _values.length }, (_, index) => ( + {Array.from({ length: values.length }, (_, index) => ( key={index} /> ))} diff --git a/packages/vitnode/src/components/ui/sonner.tsx b/packages/vitnode/src/components/ui/sonner.tsx index f4f6c7cae..6b4e60bd4 100644 --- a/packages/vitnode/src/components/ui/sonner.tsx +++ b/packages/vitnode/src/components/ui/sonner.tsx @@ -1,8 +1,7 @@ 'use client'; -import type { ToasterProps } from 'sonner'; - import { useTheme } from 'next-themes'; +import type { ToasterProps } from 'sonner'; import { Toaster as Sonner } from 'sonner'; import { cn } from '../../lib/utils'; diff --git a/packages/vitnode/src/components/ui/switch.tsx b/packages/vitnode/src/components/ui/switch.tsx index c48e281e3..227c71f94 100644 --- a/packages/vitnode/src/components/ui/switch.tsx +++ b/packages/vitnode/src/components/ui/switch.tsx @@ -1,7 +1,7 @@ 'use client'; import { Switch as SwitchPrimitive } from 'radix-ui'; -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/table.tsx b/packages/vitnode/src/components/ui/table.tsx index 531790f8d..abcda20af 100644 --- a/packages/vitnode/src/components/ui/table.tsx +++ b/packages/vitnode/src/components/ui/table.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/tabs.tsx b/packages/vitnode/src/components/ui/tabs.tsx index 042814df3..34f52145f 100644 --- a/packages/vitnode/src/components/ui/tabs.tsx +++ b/packages/vitnode/src/components/ui/tabs.tsx @@ -1,7 +1,7 @@ 'use client'; import { Tabs as TabsPrimitive } from 'radix-ui'; -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/textarea.tsx b/packages/vitnode/src/components/ui/textarea.tsx index a066b1c23..676957a9c 100644 --- a/packages/vitnode/src/components/ui/textarea.tsx +++ b/packages/vitnode/src/components/ui/textarea.tsx @@ -1,4 +1,4 @@ -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/toggle-group.tsx b/packages/vitnode/src/components/ui/toggle-group.tsx index 1fa08ca57..d96e16f82 100644 --- a/packages/vitnode/src/components/ui/toggle-group.tsx +++ b/packages/vitnode/src/components/ui/toggle-group.tsx @@ -1,8 +1,8 @@ 'use client'; -import { type VariantProps } from 'class-variance-authority'; +import type { VariantProps } from 'class-variance-authority'; import { ToggleGroup as ToggleGroupPrimitive } from 'radix-ui'; -import * as React from 'react'; +import React from 'react'; import { toggleVariants } from '@/components/ui/toggle'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/toggle.tsx b/packages/vitnode/src/components/ui/toggle.tsx index c5aaa59f4..042a42169 100644 --- a/packages/vitnode/src/components/ui/toggle.tsx +++ b/packages/vitnode/src/components/ui/toggle.tsx @@ -2,7 +2,7 @@ import { cva, type VariantProps } from 'class-variance-authority'; import { Toggle as TogglePrimitive } from 'radix-ui'; -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/components/ui/tooltip.tsx b/packages/vitnode/src/components/ui/tooltip.tsx index b903aac74..1390d320c 100644 --- a/packages/vitnode/src/components/ui/tooltip.tsx +++ b/packages/vitnode/src/components/ui/tooltip.tsx @@ -1,7 +1,7 @@ 'use client'; import { Tooltip as TooltipPrimitive } from 'radix-ui'; -import * as React from 'react'; +import type * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/packages/vitnode/src/drizzle.config.ts b/packages/vitnode/src/drizzle.config.ts index 7fc4e2532..d9680e6a9 100644 --- a/packages/vitnode/src/drizzle.config.ts +++ b/packages/vitnode/src/drizzle.config.ts @@ -1,8 +1,7 @@ +import { existsSync, readdirSync } from 'node:fs'; +import { join, resolve } from 'node:path'; import type { Config } from 'drizzle-kit'; - import { defineConfig } from 'drizzle-kit'; -import { existsSync, readdirSync } from 'fs'; -import { join, resolve } from 'path'; import type { VitNodeApiConfig } from './vitnode.config'; @@ -77,15 +76,16 @@ export const defineVitNodeDrizzleConfig = ({ }) .filter((pluginPath): pluginPath is string => pluginPath !== null); + // Normalize args.schema into an array without nested ternary expressions + let baseSchemas: string[] = []; + if (Array.isArray(args.schema)) { + baseSchemas = args.schema; + } else if (args.schema) { + baseSchemas = [args.schema]; + } + return defineConfig({ ...args, - schema: [ - ...(Array.isArray(args.schema) - ? args.schema - : args.schema - ? [args.schema] - : []), - ...pluginPaths, - ], + schema: [...baseSchemas, ...pluginPaths], }); }; diff --git a/packages/vitnode/src/emails/default-template.tsx b/packages/vitnode/src/emails/default-template.tsx index 6ba543107..05dca9c3a 100644 --- a/packages/vitnode/src/emails/default-template.tsx +++ b/packages/vitnode/src/emails/default-template.tsx @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/dot-notation */ import { Body, Container, @@ -17,11 +16,29 @@ import type { EmailModelSendArgs } from '@/api/models/email'; import { CONFIG } from '../lib/config'; +DefaultTemplateEmail.PreviewProps = { + children: 'This is a preview text for the email template.', + templateProps: { + metadata: { + title: 'VitNode - Email Template', + shortTitle: 'VitNode', + url: CONFIG.web.href, + }, + logo: { + src: 'http://localhost:3000/logo_vitnode_dark.png', + }, + }, + i18n: { + messages: {}, + locale: 'en', + }, +} satisfies DefaultTemplateEmailProps & { children: React.ReactNode }; + export interface DefaultTemplateEmailProps extends Pick { i18n: { locale: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // biome-ignore lint/suspicious/noExplicitAny: messages: Record; }; templateProps: { @@ -40,6 +57,7 @@ export interface DefaultTemplateEmailProps }; } +// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: export default function DefaultTemplateEmail({ children, i18n: { locale }, @@ -149,21 +167,3 @@ export default function DefaultTemplateEmail({ ); } - -DefaultTemplateEmail.PreviewProps = { - children: 'This is a preview text for the email template.', - templateProps: { - metadata: { - title: 'VitNode - Email Template', - shortTitle: 'VitNode', - url: CONFIG.web.href, - }, - logo: { - src: 'http://localhost:3000/logo_vitnode_dark.png', - }, - }, - i18n: { - messages: {}, - locale: 'en', - }, -} satisfies DefaultTemplateEmailProps & { children: React.ReactNode }; diff --git a/packages/vitnode/src/emails/reset-password.tsx b/packages/vitnode/src/emails/reset-password.tsx index ed569171c..54b742f18 100644 --- a/packages/vitnode/src/emails/reset-password.tsx +++ b/packages/vitnode/src/emails/reset-password.tsx @@ -20,6 +20,46 @@ interface ResetPasswordEmailTemplateProps extends DefaultTemplateEmailProps { userIpAddress?: string; } +ResetPasswordEmailTemplate.PreviewProps = { + ...DefaultTemplateEmail.PreviewProps, + resetUrl: 'https://example.com/reset-password?token=abc123', + expiryDate: new Date(Date.now() + 30 * 60 * 1000), // 30 minutes from now + userIpAddress: '192.168.1.1', + user: { + id: 1, + name: 'John Doe', + email: 'john@example.com', + language: 'en', + nameCode: 'john-doe', + }, + i18n: { + ...DefaultTemplateEmail.PreviewProps.i18n, + messages: { + core: { + auth: { + reset_password: { + email: { + subject: 'Reset your password', + greeting: 'Hello {name}!', + intro: + 'We received a request to reset your password for your account.', + button: 'Reset Password', + instructions: + 'Click the button above to reset your password. This link will expire in 30 minutes for security reasons.', + no_action: + "If you didn't request a password reset, you can safely ignore this email. Your password will remain unchanged.", + security_note: + 'For your security, this request was made from IP address: {ip}', + help: "If you're having trouble with the button above, copy and paste the URL below into your web browser:", + expire_time: 'This link will expire on {date}', + }, + }, + }, + }, + }, + }, +} satisfies ResetPasswordEmailTemplateProps; + export default function ResetPasswordEmailTemplate({ i18n, resetUrl, @@ -108,43 +148,3 @@ export default function ResetPasswordEmailTemplate({ ); } - -ResetPasswordEmailTemplate.PreviewProps = { - ...DefaultTemplateEmail.PreviewProps, - resetUrl: 'https://example.com/reset-password?token=abc123', - expiryDate: new Date(Date.now() + 30 * 60 * 1000), // 30 minutes from now - userIpAddress: '192.168.1.1', - user: { - id: 1, - name: 'John Doe', - email: 'john@example.com', - language: 'en', - nameCode: 'john-doe', - }, - i18n: { - ...DefaultTemplateEmail.PreviewProps.i18n, - messages: { - core: { - auth: { - reset_password: { - email: { - subject: 'Reset your password', - greeting: 'Hello {name}!', - intro: - 'We received a request to reset your password for your account.', - button: 'Reset Password', - instructions: - 'Click the button above to reset your password. This link will expire in 30 minutes for security reasons.', - no_action: - "If you didn't request a password reset, you can safely ignore this email. Your password will remain unchanged.", - security_note: - 'For your security, this request was made from IP address: {ip}', - help: "If you're having trouble with the button above, copy and paste the URL below into your web browser:", - expire_time: 'This link will expire on {date}', - }, - }, - }, - }, - }, - }, -} satisfies ResetPasswordEmailTemplateProps; diff --git a/packages/vitnode/src/globals.css b/packages/vitnode/src/globals.css index e2c711cdd..4b2df1e8c 100644 --- a/packages/vitnode/src/globals.css +++ b/packages/vitnode/src/globals.css @@ -1,8 +1,10 @@ -@import 'tailwindcss' source('./src'); +/** biome-ignore-all lint/correctness/noInvalidPositionAtImportRule: */ +/** biome-ignore-all lint/suspicious/noUnknownAtRules: */ +@import "tailwindcss"; @custom-variant dark (&:is(.dark *)); -@import 'tw-animate-css'; +@import "tw-animate-css"; @theme inline { --radius-sm: calc(var(--radius) - 4px); diff --git a/packages/vitnode/src/hooks/use-captcha.ts b/packages/vitnode/src/hooks/use-captcha.ts index a389c223c..08507d348 100644 --- a/packages/vitnode/src/hooks/use-captcha.ts +++ b/packages/vitnode/src/hooks/use-captcha.ts @@ -1,11 +1,9 @@ -/* eslint-disable @eslint-react/hooks-extra/no-direct-set-state-in-use-effect */ - -import type { z } from 'zod'; - +/** biome-ignore-all lint/suspicious/noConsole: */ import { useLocale, useTranslations } from 'next-intl'; import { useTheme } from 'next-themes'; import React from 'react'; import { toast } from 'sonner'; +import type { z } from 'zod'; import { usePathname } from '@/lib/navigation'; @@ -27,7 +25,6 @@ export const useCaptcha = ( const elementId = 'vitnode_captcha'; if (captcha.type === 'cloudflare_turnstile') { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error window.turnstile.render(`#${elementId}`, { sitekey: captcha.siteKey, @@ -55,6 +52,7 @@ export const useCaptcha = ( }); }; + // biome-ignore lint/correctness/useExhaustiveDependencies: React.useEffect(() => { if (!captcha) { // If no captcha is required, consider it "ready" @@ -102,14 +100,18 @@ export const useCaptcha = ( const onReset = () => { if (!captcha) return; - if (captcha.type === 'cloudflare_turnstile') { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment + if ( + captcha.type === 'cloudflare_turnstile' && + ( + window as { + turnstile?: { + reset: () => void; + }; + } + ).turnstile + ) { // @ts-expect-error - if (window.turnstile) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - window.turnstile.reset(); - } + window.turnstile.reset(); } setToken(''); @@ -120,12 +122,10 @@ export const useCaptcha = ( if (!captcha) return ''; if (captcha.type === 'recaptcha_v3') { - return new Promise(resolve => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment + return await new Promise(resolve => { // @ts-expect-error window.grecaptcha.ready(async () => { try { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error const token: string = await window.grecaptcha.execute( captcha.siteKey, @@ -135,7 +135,6 @@ export const useCaptcha = ( ); resolve(token); } catch (error) { - // eslint-disable-next-line no-console console.error('Captcha error', error); resolve(''); } diff --git a/packages/vitnode/src/hooks/use-mobile.ts b/packages/vitnode/src/hooks/use-mobile.ts index d1ce20388..5f798c0d2 100644 --- a/packages/vitnode/src/hooks/use-mobile.ts +++ b/packages/vitnode/src/hooks/use-mobile.ts @@ -1,4 +1,4 @@ -import * as React from 'react'; +import React from 'react'; const MOBILE_BREAKPOINT = 768; @@ -13,7 +13,6 @@ export function useIsMobile() { setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); }; mql.addEventListener('change', onChange); - // eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); return () => mql.removeEventListener('change', onChange); diff --git a/packages/vitnode/src/lib/colors.ts b/packages/vitnode/src/lib/colors.ts index a4894c789..2abd624ff 100644 --- a/packages/vitnode/src/lib/colors.ts +++ b/packages/vitnode/src/lib/colors.ts @@ -23,18 +23,18 @@ export const convertColor = { hexToHSL: (hex: string): HslColor | undefined => { if (!hexRegex.test(hex)) return undefined; - let b = 0, - g = 0, - r = 0; + let b = 0; + let g = 0; + let r = 0; // 3 digits - if (hex.length == 4) { + if (hex.length === 4) { r = parseInt(hex[1] + hex[1], 16); g = parseInt(hex[2] + hex[2], 16); b = parseInt(hex[3] + hex[3], 16); } // 6 digits - else if (hex.length == 7) { + else if (hex.length === 7) { r = parseInt(hex[1] + hex[2], 16); g = parseInt(hex[3] + hex[4], 16); b = parseInt(hex[5] + hex[6], 16); @@ -43,7 +43,7 @@ export const convertColor = { } // Check if any of the parsed values are NaN - if (isNaN(r) || isNaN(g) || isNaN(b)) { + if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) { return undefined; } @@ -51,13 +51,13 @@ export const convertColor = { g /= 255; b /= 255; - const max = Math.max(r, g, b), - min = Math.min(r, g, b), - l = (max + min) / 2; - let h = 0, - s = 0; + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + const l = (max + min) / 2; + let h = 0; + let s = 0; - if (max == min) { + if (max === min) { h = s = 0; // achromatic } else { const d = max - min; @@ -90,18 +90,18 @@ export const convertColor = { b /= 255; // Find greatest and smallest channel values - const cmax = Math.max(r, g, b), - cmin = Math.min(r, g, b), - delta = cmax - cmin; + const cmax = Math.max(r, g, b); + const cmin = Math.min(r, g, b); + const delta = cmax - cmin; - let h = 0, - l = 0, - s = 0; + let h = 0; + let l = 0; + let s = 0; // Calculate hue - if (delta == 0) h = 0; - else if (cmax == r) h = ((g - b) / delta) % 6; - else if (cmax == g) h = (b - r) / delta + 2; + if (delta === 0) h = 0; + else if (cmax === r) h = ((g - b) / delta) % 6; + else if (cmax === g) h = (b - r) / delta + 2; else h = (r - g) / delta + 4; h = Math.round(h * 60); @@ -113,7 +113,7 @@ export const convertColor = { l = (cmax + cmin) / 2; // Calculate saturation - s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1)); + s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1)); // Multiply l and s by 100 s = +(s * 100).toFixed(1); diff --git a/packages/vitnode/src/lib/fetcher-client.ts b/packages/vitnode/src/lib/fetcher-client.ts index 0a6c4c4b4..e6ab98b41 100644 --- a/packages/vitnode/src/lib/fetcher-client.ts +++ b/packages/vitnode/src/lib/fetcher-client.ts @@ -3,7 +3,7 @@ import type { BuildModuleReturn, } from '@/api/lib/module'; import type { Route } from '@/api/lib/route'; - +import { coreFetcher } from './fetcher/core'; import type { FetcherParams, GetModulePaths, @@ -12,8 +12,6 @@ import type { InferResponseType, } from './fetcher/types'; -import { coreFetcher } from './fetcher/core'; - export async function fetcherClient< M extends string, Routes extends Route[], @@ -53,7 +51,7 @@ export async function fetcherClient< additionalHeaders['x-vitnode-captcha-token'] = captchaToken; } - return coreFetcher(moduleReturn, { + return await coreFetcher(moduleReturn, { path, method, module, diff --git a/packages/vitnode/src/lib/fetcher.ts b/packages/vitnode/src/lib/fetcher.ts index 3537a469c..d86133a18 100644 --- a/packages/vitnode/src/lib/fetcher.ts +++ b/packages/vitnode/src/lib/fetcher.ts @@ -6,7 +6,8 @@ import type { BuildModuleReturn, } from '@/api/lib/module'; import type { Route } from '@/api/lib/route'; - +import { coreFetcher } from './fetcher/core'; +import { handleSetCookiesFetcher } from './fetcher/helpers-server'; import type { FetcherParams, GetModulePaths, @@ -15,9 +16,6 @@ import type { InferResponseType, } from './fetcher/types'; -import { coreFetcher } from './fetcher/core'; -import { handleSetCookiesFetcher } from './fetcher/helpers-server'; - export async function fetcher< M extends string, Routes extends Route[], diff --git a/packages/vitnode/src/lib/fetcher/cookie-from-string-to-object.ts b/packages/vitnode/src/lib/fetcher/cookie-from-string-to-object.ts index ea17f8fb3..8adc3a7cc 100644 --- a/packages/vitnode/src/lib/fetcher/cookie-from-string-to-object.ts +++ b/packages/vitnode/src/lib/fetcher/cookie-from-string-to-object.ts @@ -1,7 +1,6 @@ export const cookieFromStringToObject = ( str: string[], ): { - // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents [key: string]: 'lax' | 'none' | 'strict' | boolean | string | undefined; Domain: string; Expires: string; diff --git a/packages/vitnode/src/lib/fetcher/core.ts b/packages/vitnode/src/lib/fetcher/core.ts index 065206795..4ee55f23b 100644 --- a/packages/vitnode/src/lib/fetcher/core.ts +++ b/packages/vitnode/src/lib/fetcher/core.ts @@ -3,7 +3,8 @@ import type { BuildModuleReturn, } from '@/api/lib/module'; import type { Route } from '@/api/lib/route'; - +import { CONFIG } from '../config'; +import { buildSearchParams } from './helpers'; import type { FetcherParams, GetModulePaths, @@ -12,9 +13,6 @@ import type { InferResponseType, } from './types'; -import { CONFIG } from '../config'; -import { buildSearchParams } from './helpers'; - interface CoreFetcherOptions< M extends string, Routes extends Route[], @@ -46,6 +44,7 @@ interface CoreFetcherOptions< withPagination?: boolean; } +// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: export async function coreFetcher< M extends string, Routes extends Route[], @@ -102,7 +101,7 @@ export async function coreFetcher< const searchParams = buildSearchParams({ ...(args.query as Record), ...(withPagination && { - first: !queryParams.last ? (queryParams.first ?? '10') : undefined, + first: queryParams.last ? undefined : (queryParams.first ?? '10'), search: queryParams.search ?? '', }), }); @@ -131,7 +130,7 @@ export async function coreFetcher< if (response.status >= 400) { const errorText = await response.text(); - // eslint-disable-next-line no-console + // biome-ignore lint/suspicious/noConsole: console.error( `\x1b[34m[VitNode - API]\x1b[0m \x1b[31m${response.status}\x1b[0m - \x1b[33m${url.toString()}\x1b[0m\n\x1b[36mError: ${errorText}\x1b[0m`, ); diff --git a/packages/vitnode/src/lib/fetcher/types.ts b/packages/vitnode/src/lib/fetcher/types.ts index f706a3f49..b24187def 100644 --- a/packages/vitnode/src/lib/fetcher/types.ts +++ b/packages/vitnode/src/lib/fetcher/types.ts @@ -71,17 +71,6 @@ type FindModuleNested< : never : M; -export type GetModulePaths< - MainModule extends string, - Modules extends readonly ModuleSpec[], -> = - | `${MainModule}/${Modules[number]['name']}/${Extract< - Modules[number]['modules'], - readonly ModuleSpec[] - >[number]['name']}` - | `${MainModule}/${Modules[number]['name']}` - | MainModule; - type GetTargetModule< ModulePath extends string, MainModuleName extends string, @@ -148,30 +137,11 @@ type BuildArgsType = { : K]: InferInputType; }; -export type GetValidPathsForModule< - ModulePath extends string, - MainModuleName extends string, - MainRoutes extends readonly RouteShape[], - SubModules extends readonly ModuleSpec[], -> = ExtractPaths< - GetTargetModule ->; - -export type GetValidMethodForPath< - ModulePath extends string, - Path extends string, - MainModuleName extends string, - MainRoutes extends readonly RouteShape[], - SubModules extends readonly ModuleSpec[], -> = Lowercase< - Extract< - ExtractMethodForPath< - GetTargetModule, - Path - >, - string - > ->; +type InferStatusCode = K extends `${infer N extends number}` + ? N + : K extends number + ? K + : never; interface BaseFetcherParams< M extends string, @@ -212,11 +182,41 @@ export type FetcherParams< > = BaseFetcherParams & (keyof ArgsType extends never ? { args?: undefined } : { args: ArgsType }); -type InferStatusCode = K extends `${infer N extends number}` - ? N - : K extends number - ? K - : never; +export type GetValidPathsForModule< + ModulePath extends string, + MainModuleName extends string, + MainRoutes extends readonly RouteShape[], + SubModules extends readonly ModuleSpec[], +> = ExtractPaths< + GetTargetModule +>; + +export type GetModulePaths< + MainModule extends string, + Modules extends readonly ModuleSpec[], +> = + | `${MainModule}/${Modules[number]['name']}/${Extract< + Modules[number]['modules'], + readonly ModuleSpec[] + >[number]['name']}` + | `${MainModule}/${Modules[number]['name']}` + | MainModule; + +export type GetValidMethodForPath< + ModulePath extends string, + Path extends string, + MainModuleName extends string, + MainRoutes extends readonly RouteShape[], + SubModules extends readonly ModuleSpec[], +> = Lowercase< + Extract< + ExtractMethodForPath< + GetTargetModule, + Path + >, + string + > +>; export type InferResponseType< M extends string, diff --git a/packages/vitnode/src/lib/helpers/auto-form.ts b/packages/vitnode/src/lib/helpers/auto-form.ts index a943952a9..6a921c541 100644 --- a/packages/vitnode/src/lib/helpers/auto-form.ts +++ b/packages/vitnode/src/lib/helpers/auto-form.ts @@ -1,6 +1,8 @@ import type { DefaultValues } from 'react-hook-form'; import type { z } from 'zod'; +type NestedParamValue = InputParams[string] | undefined; + export function getDefaults( jsonSchema?: z.core.JSONSchema.JSONSchema, ): DefaultValues> { @@ -51,6 +53,7 @@ export interface InputParams { }; } +// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: export function getZodInputParams( jsonSchema?: z.core.JSONSchema.JSONSchema, parentRequired: string[] = [], @@ -130,8 +133,6 @@ export function getZodInputParams( return extractedParams; } -type NestedParamValue = InputParams[string] | undefined; - export function getNestedParam( obj: InputParams, path: string, diff --git a/packages/vitnode/src/lib/navigation.ts b/packages/vitnode/src/lib/navigation.ts index b37c7b448..5e6f5f676 100644 --- a/packages/vitnode/src/lib/navigation.ts +++ b/packages/vitnode/src/lib/navigation.ts @@ -1,5 +1,5 @@ -import type { QueryParams } from 'next-intl/navigation'; import type { RedirectType } from 'next/navigation'; +import type { QueryParams } from 'next-intl/navigation'; import { createNavigation } from 'next-intl/navigation'; import { getLocale } from 'next-intl/server'; diff --git a/packages/vitnode/src/views/admin/layouts/admin-layout.tsx b/packages/vitnode/src/views/admin/layouts/admin-layout.tsx index f33d3d6e3..9c858b4cb 100644 --- a/packages/vitnode/src/views/admin/layouts/admin-layout.tsx +++ b/packages/vitnode/src/views/admin/layouts/admin-layout.tsx @@ -1,16 +1,17 @@ -import { getTranslations } from 'next-intl/server'; import { cookies } from 'next/headers'; +import { getTranslations } from 'next-intl/server'; import { ThemeSwitcher } from '@/components/switchers/themes/theme-switcher'; -import { SidebarProvider, SidebarTrigger } from '@/components/ui/sidebar'; -import { SidebarInset } from '@/components/ui/sidebar'; +import { + SidebarInset, + SidebarProvider, + SidebarTrigger, +} from '@/components/ui/sidebar'; import { getSessionAdminApi } from '@/lib/api/get-session-admin-api'; - -import type { VitNodeConfig } from '../../../vitnode.config'; -import type { NavAdminParent } from './sidebar/nav/nav'; - import { I18nProvider } from '../../../components/i18n-provider'; import { LanguageSwitcher } from '../../../components/switchers/langs/language-swietcher'; +import type { VitNodeConfig } from '../../../vitnode.config'; +import type { NavAdminParent } from './sidebar/nav/nav'; import { SidebarAdmin } from './sidebar/sidebar'; import { UserBarAdmin } from './user-bar/user-bar'; @@ -36,18 +37,15 @@ export const AdminLayout = async ({ .filter(plugin => plugin.admin?.nav) .map(plugin => ({ id: plugin.pluginId, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error title: t(`${plugin.pluginId}.title`), items: (plugin.admin?.nav ?? []).map(item => ({ ...item, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error title: t(`${plugin.pluginId}.admin.nav.${item.id}`), items: item.items?.map(subItem => ({ ...subItem, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error title: t(`${plugin.pluginId}.admin.nav.${item.id}.${subItem.id}`), })) ?? [], diff --git a/packages/vitnode/src/views/admin/layouts/sidebar/nav/item.tsx b/packages/vitnode/src/views/admin/layouts/sidebar/nav/item.tsx index 2eb10d912..875725a0a 100644 --- a/packages/vitnode/src/views/admin/layouts/sidebar/nav/item.tsx +++ b/packages/vitnode/src/views/admin/layouts/sidebar/nav/item.tsx @@ -7,12 +7,13 @@ import { CollapsibleContent, CollapsibleTrigger, } from '@/components/ui/collapsible'; -import { SidebarMenuButton, useSidebar } from '@/components/ui/sidebar'; import { + SidebarMenuButton, SidebarMenuItem, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, + useSidebar, } from '@/components/ui/sidebar'; import { useIsMobile } from '@/hooks/use-mobile'; import { Link, usePathname } from '@/lib/navigation'; diff --git a/packages/vitnode/src/views/admin/layouts/sidebar/sidebar.tsx b/packages/vitnode/src/views/admin/layouts/sidebar/sidebar.tsx index cef9b64b0..3c5feb136 100644 --- a/packages/vitnode/src/views/admin/layouts/sidebar/sidebar.tsx +++ b/packages/vitnode/src/views/admin/layouts/sidebar/sidebar.tsx @@ -1,6 +1,9 @@ import { LogoVitNode } from '@/components/logo-vitnode'; -import { Sidebar } from '@/components/ui/sidebar'; -import { SidebarContent, SidebarHeader } from '@/components/ui/sidebar'; +import { + Sidebar, + SidebarContent, + SidebarHeader, +} from '@/components/ui/sidebar'; import { Link } from '@/lib/navigation'; import { NavSidebarAdmin } from './nav/nav'; diff --git a/packages/vitnode/src/views/admin/layouts/user-bar/user-bar.tsx b/packages/vitnode/src/views/admin/layouts/user-bar/user-bar.tsx index cbf9927b7..91f4f7318 100644 --- a/packages/vitnode/src/views/admin/layouts/user-bar/user-bar.tsx +++ b/packages/vitnode/src/views/admin/layouts/user-bar/user-bar.tsx @@ -1,7 +1,5 @@ 'use client'; -import type { getSessionAdminApi } from '@/lib/api/get-session-admin-api'; - import { Avatar } from '@/components/avatar'; import { Button } from '@/components/ui/button'; import { @@ -9,6 +7,7 @@ import { DropdownMenuContent, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; +import type { getSessionAdminApi } from '@/lib/api/get-session-admin-api'; import { ClientUserBarAdmin } from './client'; diff --git a/packages/vitnode/src/views/admin/views/core/debug/system-logs/actions/more/content.tsx b/packages/vitnode/src/views/admin/views/core/debug/system-logs/actions/more/content.tsx index 8f44d0458..4c31e42e7 100644 --- a/packages/vitnode/src/views/admin/views/core/debug/system-logs/actions/more/content.tsx +++ b/packages/vitnode/src/views/admin/views/core/debug/system-logs/actions/more/content.tsx @@ -15,11 +15,9 @@ import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Link } from '@/lib/navigation'; - -import type { getSystemLogsData } from '../../system-logs-view'; - import { BadgeStatus } from '../../badges/badge-status'; import { BadgeTypeLog } from '../../badges/badge-type-log'; +import type { getSystemLogsData } from '../../system-logs-view'; export const ContentMoreActionSystemLogs = ({ content, diff --git a/packages/vitnode/src/views/admin/views/core/debug/system-logs/actions/more/more.tsx b/packages/vitnode/src/views/admin/views/core/debug/system-logs/actions/more/more.tsx index 3623ef3ef..62a634b5e 100644 --- a/packages/vitnode/src/views/admin/views/core/debug/system-logs/actions/more/more.tsx +++ b/packages/vitnode/src/views/admin/views/core/debug/system-logs/actions/more/more.tsx @@ -1,8 +1,8 @@ 'use client'; import { SearchIcon } from 'lucide-react'; -import { useTranslations } from 'next-intl'; import dynamic from 'next/dynamic'; +import { useTranslations } from 'next-intl'; import React from 'react'; import { Button } from '@/components/ui/button'; diff --git a/packages/vitnode/src/views/admin/views/core/debug/system-logs/badges/badge-status.tsx b/packages/vitnode/src/views/admin/views/core/debug/system-logs/badges/badge-status.tsx index 7fb5e285c..d2f9cf90b 100644 --- a/packages/vitnode/src/views/admin/views/core/debug/system-logs/badges/badge-status.tsx +++ b/packages/vitnode/src/views/admin/views/core/debug/system-logs/badges/badge-status.tsx @@ -1,17 +1,13 @@ import { Badge } from '@/components/ui/badge'; export const BadgeStatus = ({ statusCode }: { statusCode: number }) => { - return ( - = 200 && statusCode < 300 - ? 'default' - : statusCode >= 400 - ? 'destructive' - : 'secondary' - } - > - {statusCode} - - ); + let variant: 'default' | 'secondary' | 'destructive' = 'secondary'; + + if (statusCode >= 200 && statusCode < 300) { + variant = 'default'; + } else if (statusCode >= 400) { + variant = 'destructive'; + } + + return {statusCode}; }; diff --git a/packages/vitnode/src/views/admin/views/core/debug/system-logs/system-logs-view.tsx b/packages/vitnode/src/views/admin/views/core/debug/system-logs/system-logs-view.tsx index 825f25058..ed8cc771a 100644 --- a/packages/vitnode/src/views/admin/views/core/debug/system-logs/system-logs-view.tsx +++ b/packages/vitnode/src/views/admin/views/core/debug/system-logs/system-logs-view.tsx @@ -69,7 +69,7 @@ export const SystemLogsView = async ({ const content = row.content; const isLong = content.length > CHARACTERS; const displayContent = isLong - ? content.slice(0, CHARACTERS) + '...' + ? `${content.slice(0, CHARACTERS)}...` : content; return {displayContent}; diff --git a/packages/vitnode/src/views/admin/views/core/test.tsx b/packages/vitnode/src/views/admin/views/core/test.tsx index e00386a31..6ee2a9c4b 100644 --- a/packages/vitnode/src/views/admin/views/core/test.tsx +++ b/packages/vitnode/src/views/admin/views/core/test.tsx @@ -158,7 +158,7 @@ export const TestView = () => { ]} formSchema={formSchema} onSubmit={async values => { - // eslint-disable-next-line no-console + // biome-ignore lint/suspicious/noConsole: console.log('Form submitted', values); await new Promise(resolve => setTimeout(resolve, 3000)); }} diff --git a/packages/vitnode/src/views/admin/views/core/users/users-admin-view.tsx b/packages/vitnode/src/views/admin/views/core/users/users-admin-view.tsx index 9d6761007..6bec611bf 100644 --- a/packages/vitnode/src/views/admin/views/core/users/users-admin-view.tsx +++ b/packages/vitnode/src/views/admin/views/core/users/users-admin-view.tsx @@ -1,6 +1,5 @@ import { MailIcon } from 'lucide-react'; import { getTranslations } from 'next-intl/server'; -import React from 'react'; import { adminModule } from '@/api/modules/admin/admin.module'; import { Avatar } from '@/components/avatar'; diff --git a/packages/vitnode/src/views/auth/password-reset/change-password-form/use-form.ts b/packages/vitnode/src/views/auth/password-reset/change-password-form/use-form.ts index f9e9b2cde..0a9c7ac64 100644 --- a/packages/vitnode/src/views/auth/password-reset/change-password-form/use-form.ts +++ b/packages/vitnode/src/views/auth/password-reset/change-password-form/use-form.ts @@ -3,10 +3,8 @@ import { toast } from 'sonner'; import z from 'zod'; import { useRouter } from '@/lib/navigation'; - -import type { ChangePasswordForm } from './form'; - import { usePasswordZodSchema } from '../../sign-up/form/use-form'; +import type { ChangePasswordForm } from './form'; import { mutationApi } from './mutation-api'; export const useForm = ({ diff --git a/packages/vitnode/src/views/auth/password-reset/form/form.tsx b/packages/vitnode/src/views/auth/password-reset/form/form.tsx index 3f0e16a77..43ebae92c 100644 --- a/packages/vitnode/src/views/auth/password-reset/form/form.tsx +++ b/packages/vitnode/src/views/auth/password-reset/form/form.tsx @@ -1,9 +1,8 @@ 'use client'; -import type z from 'zod'; - import { MailCheckIcon } from 'lucide-react'; import { useTranslations } from 'next-intl'; +import type z from 'zod'; import type { routeMiddlewareSchema } from '@/api/modules/middleware/route'; diff --git a/packages/vitnode/src/views/auth/sign-in/form/mutation-api.ts b/packages/vitnode/src/views/auth/sign-in/form/mutation-api.ts index a737784ad..732ec8e61 100644 --- a/packages/vitnode/src/views/auth/sign-in/form/mutation-api.ts +++ b/packages/vitnode/src/views/auth/sign-in/form/mutation-api.ts @@ -1,8 +1,7 @@ 'use server'; -import type { z } from 'zod'; - import { revalidatePath } from 'next/cache'; +import type { z } from 'zod'; import type { zodSignInSchema } from '@/api/modules/users/routes/sign-in.route'; diff --git a/packages/vitnode/src/views/auth/sign-up/components/password-input.tsx b/packages/vitnode/src/views/auth/sign-up/components/password-input.tsx index 7770918b3..0f18ceee5 100644 --- a/packages/vitnode/src/views/auth/sign-up/components/password-input.tsx +++ b/packages/vitnode/src/views/auth/sign-up/components/password-input.tsx @@ -16,7 +16,6 @@ import { export const PasswordInput = ({ label, - // eslint-disable-next-line @typescript-eslint/no-unused-vars description: _, field, otherProps: { maxLength, minLength, pattern }, diff --git a/packages/vitnode/src/views/auth/sign-up/form/form.tsx b/packages/vitnode/src/views/auth/sign-up/form/form.tsx index 4f178f2e3..1626d9197 100644 --- a/packages/vitnode/src/views/auth/sign-up/form/form.tsx +++ b/packages/vitnode/src/views/auth/sign-up/form/form.tsx @@ -1,8 +1,7 @@ 'use client'; -import type { z } from 'zod'; - import { useTranslations } from 'next-intl'; +import type { z } from 'zod'; import type { routeMiddlewareSchema } from '@/api/modules/middleware/route'; diff --git a/packages/vitnode/src/views/auth/sign-up/form/mutation-api.ts b/packages/vitnode/src/views/auth/sign-up/form/mutation-api.ts index 520cab90d..2cbd23fe8 100644 --- a/packages/vitnode/src/views/auth/sign-up/form/mutation-api.ts +++ b/packages/vitnode/src/views/auth/sign-up/form/mutation-api.ts @@ -1,8 +1,7 @@ 'use server'; -import type { z } from 'zod'; - import { revalidatePath } from 'next/cache'; +import type { z } from 'zod'; import type { zodSignUpSchema } from '@/api/modules/users/routes/sign-up.route'; diff --git a/packages/vitnode/src/views/auth/sign-up/wrapper.tsx b/packages/vitnode/src/views/auth/sign-up/wrapper.tsx index a949e42c2..e4b4bc531 100644 --- a/packages/vitnode/src/views/auth/sign-up/wrapper.tsx +++ b/packages/vitnode/src/views/auth/sign-up/wrapper.tsx @@ -14,13 +14,9 @@ export const useWrapperSignUp = () => React.use(WrapperSignUpContext); export const WrapperSignUp = ({ children }: { children: React.ReactNode }) => { const [sendingEmail, setShowSendingEmail] = React.useState(''); - const contextValue = React.useMemo( - () => ({ setShowSendingEmail }), - [setShowSendingEmail], - ); return ( - + {sendingEmail ? : children} ); diff --git a/packages/vitnode/src/views/auth/sso/callback/client/client.tsx b/packages/vitnode/src/views/auth/sso/callback/client/client.tsx index 2680ac297..41dd0fdfb 100644 --- a/packages/vitnode/src/views/auth/sso/callback/client/client.tsx +++ b/packages/vitnode/src/views/auth/sso/callback/client/client.tsx @@ -6,10 +6,8 @@ import { useTranslations } from 'next-intl'; import { Loader } from '@/components/ui/loader'; import { Link, useRouter } from '@/lib/navigation'; import { ErrorView } from '@/views/error/error-view'; - -import type { getMiddlewareApi } from '../../../../../lib/api/get-middleware-api'; - import { Button } from '../../../../../components/ui/button'; +import type { getMiddlewareApi } from '../../../../../lib/api/get-middleware-api'; import { mutationApi } from './mutation-api'; export const ClientCallbackSSO = ({ diff --git a/packages/vitnode/src/views/error/global-error-view.tsx b/packages/vitnode/src/views/error/global-error-view.tsx index 93ae7995c..586fe9c74 100644 --- a/packages/vitnode/src/views/error/global-error-view.tsx +++ b/packages/vitnode/src/views/error/global-error-view.tsx @@ -1,24 +1,16 @@ -import { ThemeProvider } from 'next-themes'; -// eslint-disable-next-line no-restricted-imports +import type { Metadata } from 'next/dist/types'; import Link from 'next/link'; - -import type { VitNodeConfig } from '@/vitnode.config'; - +import { ThemeProvider } from 'next-themes'; import { LogoVitNode } from '@/components/logo-vitnode'; import { Card, CardContent } from '@/components/ui/card'; -export const GlobalErrorView = ({ - className, - config, -}: { - className?: string; - config: VitNodeConfig; -}) => { +export const metadata: Metadata = { + title: 'Error 500!', +}; + +export const GlobalErrorView = ({ className }: { className?: string }) => { return ( - - Error 500! - {config.metadata.title} -
diff --git a/packages/vitnode/src/views/layouts/provider.tsx b/packages/vitnode/src/views/layouts/provider.tsx index 9511f4287..1962f1a1d 100644 --- a/packages/vitnode/src/views/layouts/provider.tsx +++ b/packages/vitnode/src/views/layouts/provider.tsx @@ -4,10 +4,8 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { ThemeProvider } from 'next-themes'; import React from 'react'; import { scan } from 'react-scan'; - -import type { VitNodeConfig } from '@/vitnode.config'; - import { CONFIG } from '@/lib/config'; +import type { VitNodeConfig } from '@/vitnode.config'; import { Toaster } from '../../components/ui/sonner'; @@ -26,7 +24,7 @@ export const RootProvider = ({ toaster?: React.ComponentProps; }) => { React.useEffect(() => { - if (!debug || !CONFIG.node_development) return; + if (!(debug && CONFIG.node_development)) return; scan({ enabled: true, diff --git a/packages/vitnode/src/views/layouts/root-layout.tsx b/packages/vitnode/src/views/layouts/root-layout.tsx index 8e209ea4b..ab29eaab3 100644 --- a/packages/vitnode/src/views/layouts/root-layout.tsx +++ b/packages/vitnode/src/views/layouts/root-layout.tsx @@ -1,11 +1,9 @@ import type { Metadata } from 'next/dist/types'; import { setRequestLocale } from 'next-intl/server'; -import React from 'react'; - -import type { VitNodeConfig } from '@/vitnode.config'; - +import type React from 'react'; import { I18nProvider } from '@/components/i18n-provider'; +import type { VitNodeConfig } from '@/vitnode.config'; import { RootProvider } from './provider'; diff --git a/packages/vitnode/src/views/layouts/theme/header/header.tsx b/packages/vitnode/src/views/layouts/theme/header/header.tsx index 8d25b49ba..82b8d8b29 100644 --- a/packages/vitnode/src/views/layouts/theme/header/header.tsx +++ b/packages/vitnode/src/views/layouts/theme/header/header.tsx @@ -1,12 +1,10 @@ import React from 'react'; - -import type { VitNodeConfig } from '@/vitnode.config'; - import { LanguageSwitcher } from '@/components/switchers/langs/language-swietcher'; import { ThemeSwitcher } from '@/components/switchers/themes/theme-switcher'; import { Skeleton } from '@/components/ui/skeleton'; import { Link } from '@/lib/navigation'; import { cn } from '@/lib/utils'; +import type { VitNodeConfig } from '@/vitnode.config'; import { UserHeader } from './user/user'; diff --git a/packages/vitnode/src/views/layouts/theme/header/user/auth/client.tsx b/packages/vitnode/src/views/layouts/theme/header/user/auth/client.tsx index 53eaff726..d3d363748 100644 --- a/packages/vitnode/src/views/layouts/theme/header/user/auth/client.tsx +++ b/packages/vitnode/src/views/layouts/theme/header/user/auth/client.tsx @@ -2,14 +2,12 @@ import { KeyRoundIcon, LogOutIcon } from 'lucide-react'; import { useTranslations } from 'next-intl'; - -import type { SessionApi } from '@/lib/api/get-session-api'; - import { DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, } from '@/components/ui/dropdown-menu'; +import type { SessionApi } from '@/lib/api/get-session-api'; import { Link } from '@/lib/navigation'; import { logOutMutationApi } from './log-out-mutation-api'; diff --git a/packages/vitnode/src/vitnode.config.ts b/packages/vitnode/src/vitnode.config.ts index d4b8c1c93..0825e0715 100644 --- a/packages/vitnode/src/vitnode.config.ts +++ b/packages/vitnode/src/vitnode.config.ts @@ -112,6 +112,7 @@ export const handleRequestConfig = async ({ }); const allMessages = await Promise.all(messagesPromises); + // biome-ignore lint/performance/noAccumulatingSpread: const messages = allMessages.reduce((acc, curr) => ({ ...acc, ...curr }), {}); return { diff --git a/packages/vitnode/vitest.config.ts b/packages/vitnode/vitest.config.ts index bed22e0ef..f02e4df1f 100644 --- a/packages/vitnode/vitest.config.ts +++ b/packages/vitnode/vitest.config.ts @@ -1,5 +1,5 @@ +import { resolve } from 'node:path'; import react from '@vitejs/plugin-react'; -import { resolve } from 'path'; import tsconfigPaths from 'vite-tsconfig-paths'; import { defineConfig } from 'vitest/config'; diff --git a/plugins/blog/.npmignore b/plugins/blog/.npmignore index 47d43e60f..2fb2a5651 100644 --- a/plugins/blog/.npmignore +++ b/plugins/blog/.npmignore @@ -8,7 +8,6 @@ /node_modules /.turbo -/eslint.config.mjs /tsconfig.json /.swcrc /components.json diff --git a/plugins/blog/eslint.config.mjs b/plugins/blog/eslint.config.mjs deleted file mode 100644 index 0098d1c8a..000000000 --- a/plugins/blog/eslint.config.mjs +++ /dev/null @@ -1,17 +0,0 @@ -import eslintVitNode from '@vitnode/eslint-config/eslint'; -import { fileURLToPath } from 'node:url'; -import { dirname } from 'node:path'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -export default [ - ...eslintVitNode, - { - languageOptions: { - parserOptions: { - project: './tsconfig.json', - tsconfigRootDir: __dirname, - }, - }, - }, -]; diff --git a/plugins/blog/global.d.ts b/plugins/blog/global.d.ts index 34e4feaf6..532ae6c52 100644 --- a/plugins/blog/global.d.ts +++ b/plugins/blog/global.d.ts @@ -1,7 +1,7 @@ /// -import type plugin from './src/locales/en.json'; -import type core from '@vitnode/core/locales/en.json'; +import core from '@vitnode/core/locales/en.json' with { type: 'json' }; +import plugin from './src/locales/en.json' with { type: 'json' }; declare module 'next-intl' { interface AppConfig { diff --git a/plugins/blog/package.json b/plugins/blog/package.json index 57d5ab3e4..668c9801e 100644 --- a/plugins/blog/package.json +++ b/plugins/blog/package.json @@ -29,9 +29,7 @@ "scripts": { "build:plugins": "tsc && swc src -d dist --config-file .swcrc && tsc-alias -p tsconfig.json", "dev": "concurrently \"tsc -w --preserveWatchOutput\" \"swc src -d dist --config-file .swcrc -w\" \"tsc-alias -w\" \"vitnode plugin --w\"", - "dev:email": "email dev --dir src/emails", - "lint": "eslint .", - "lint:fix": "eslint . --fix" + "dev:email": "email dev --dir src/emails" }, "dependencies": { "@hono/zod-openapi": "^1.1.0", @@ -58,7 +56,6 @@ "@types/react-dom": "^19.1.7", "@vitnode/eslint-config": "workspace:*", "concurrently": "^9.2.0", - "eslint": "^9.33.0", "tsc-alias": "^1.8.16", "typescript": "^5.9.2" } diff --git a/plugins/blog/src/api/modules/admin/categories/routes/create.route.ts b/plugins/blog/src/api/modules/admin/categories/routes/create.route.ts index 78c718afe..b187a2b57 100644 --- a/plugins/blog/src/api/modules/admin/categories/routes/create.route.ts +++ b/plugins/blog/src/api/modules/admin/categories/routes/create.route.ts @@ -7,10 +7,6 @@ import { HTTPException } from 'hono/http-exception'; import { CONFIG_PLUGIN } from '@/const'; import { blog_categories } from '@/database/categories'; -export const zodCreateCategorySchema = z.object({ - title: z.string(), -}); - const zodCategoryResponseSchema = z.object({ id: z.number(), title: z.string(), @@ -18,6 +14,10 @@ const zodCategoryResponseSchema = z.object({ updatedAt: z.date(), }); +export const zodCreateCategorySchema = z.object({ + title: z.string(), +}); + export const createCategoryRoute = buildRoute({ ...CONFIG_PLUGIN, route: { diff --git a/plugins/blog/src/api/modules/admin/posts/routes/create.route.ts b/plugins/blog/src/api/modules/admin/posts/routes/create.route.ts index 6c297be2e..11d942b28 100644 --- a/plugins/blog/src/api/modules/admin/posts/routes/create.route.ts +++ b/plugins/blog/src/api/modules/admin/posts/routes/create.route.ts @@ -8,15 +8,6 @@ import { CONFIG_PLUGIN } from '@/const'; import { blog_categories } from '@/database/categories'; import { blog_posts } from '@/database/posts'; -export const zodCreatePostSchema = z.object({ - title: z - .string() - .min(3, 'Title must be at least 3 characters long') - .max(255, 'Title must not exceed 255 characters'), - content: z.string(), - categoryId: z.number(), -}); - const zodPostResponseSchema = z.object({ id: z.number(), title: z.string(), @@ -27,6 +18,15 @@ const zodPostResponseSchema = z.object({ updatedAt: z.date(), }); +export const zodCreatePostSchema = z.object({ + title: z + .string() + .min(3, 'Title must be at least 3 characters long') + .max(255, 'Title must not exceed 255 characters'), + content: z.string(), + categoryId: z.number(), +}); + export const createPostRoute = buildRoute({ ...CONFIG_PLUGIN, route: { diff --git a/plugins/blog/src/api/modules/categories/routes/get.route.ts b/plugins/blog/src/api/modules/categories/routes/get.route.ts index 3a44ad14a..d95162666 100644 --- a/plugins/blog/src/api/modules/categories/routes/get.route.ts +++ b/plugins/blog/src/api/modules/categories/routes/get.route.ts @@ -5,7 +5,7 @@ import { zodPaginationPageInfo, zodPaginationQuery, } from '@vitnode/core/api/lib/with-pagination'; -import { and, ilike } from 'drizzle-orm'; +import { and, ilike, type SQL } from 'drizzle-orm'; import { CONFIG_PLUGIN } from '@/const'; import { blog_categories } from '@/database/categories'; @@ -57,11 +57,16 @@ export const categoriesRoute = buildRoute({ ? ilike(blog_categories.title, `%${query.search}%`) : undefined; - const combinedWhere = searchCondition - ? where - ? and(where, searchCondition) - : searchCondition - : where; + let combinedWhere: SQL | undefined; + if (searchCondition) { + if (where) { + combinedWhere = and(where, searchCondition); + } else { + combinedWhere = searchCondition; + } + } else { + combinedWhere = where; + } return await c .get('db') diff --git a/plugins/blog/src/app_admin/blog/categories/page.tsx b/plugins/blog/src/app_admin/blog/categories/page.tsx index 10cffe4b6..1e3584687 100644 --- a/plugins/blog/src/app_admin/blog/categories/page.tsx +++ b/plugins/blog/src/app_admin/blog/categories/page.tsx @@ -1,10 +1,9 @@ -import type { Metadata } from 'next'; - import { I18nProvider } from '@vitnode/core/components/i18n-provider'; import { DataTableSkeleton } from '@vitnode/core/components/table/data-table'; import { HeaderContent } from '@vitnode/core/components/ui/header-content'; -import { getTranslations } from 'next-intl/server'; +import type { Metadata } from 'next'; import dynamic from 'next/dynamic'; +import { getTranslations } from 'next-intl/server'; import React from 'react'; import { ActionsCategoriesAdmin } from '@/views/admin/categories/actions/actions'; diff --git a/plugins/blog/src/app_admin/blog/posts/page.tsx b/plugins/blog/src/app_admin/blog/posts/page.tsx index 46794e730..e5e52cb30 100644 --- a/plugins/blog/src/app_admin/blog/posts/page.tsx +++ b/plugins/blog/src/app_admin/blog/posts/page.tsx @@ -1,10 +1,9 @@ -import type { Metadata } from 'next'; - import { I18nProvider } from '@vitnode/core/components/i18n-provider'; import { DataTableSkeleton } from '@vitnode/core/components/table/data-table'; import { HeaderContent } from '@vitnode/core/components/ui/header-content'; -import { getTranslations } from 'next-intl/server'; +import type { Metadata } from 'next'; import dynamic from 'next/dynamic'; +import { getTranslations } from 'next-intl/server'; import React from 'react'; import { ActionsPostsAdmin } from '@/views/admin/posts/actions/actions'; diff --git a/plugins/blog/src/emails/test-template.tsx b/plugins/blog/src/emails/test-template.tsx index 7c9ff7f13..4ddf0a6e1 100644 --- a/plugins/blog/src/emails/test-template.tsx +++ b/plugins/blog/src/emails/test-template.tsx @@ -3,6 +3,9 @@ import DefaultTemplateEmail, { type DefaultTemplateEmailProps, } from '@vitnode/core/emails/default-template'; +TestTemplateEmail.PreviewProps = + DefaultTemplateEmail.PreviewProps satisfies DefaultTemplateEmailProps; + export default function TestTemplateEmail({ user, ...props @@ -15,6 +18,3 @@ export default function TestTemplateEmail({ ); } - -TestTemplateEmail.PreviewProps = - DefaultTemplateEmail.PreviewProps satisfies DefaultTemplateEmailProps; diff --git a/plugins/blog/src/globals.css b/plugins/blog/src/globals.css index e2c711cdd..4b2df1e8c 100644 --- a/plugins/blog/src/globals.css +++ b/plugins/blog/src/globals.css @@ -1,8 +1,10 @@ -@import 'tailwindcss' source('./src'); +/** biome-ignore-all lint/correctness/noInvalidPositionAtImportRule: */ +/** biome-ignore-all lint/suspicious/noUnknownAtRules: */ +@import "tailwindcss"; @custom-variant dark (&:is(.dark *)); -@import 'tw-animate-css'; +@import "tw-animate-css"; @theme inline { --radius-sm: calc(var(--radius) - 4px); diff --git a/plugins/blog/src/views/admin/categories/actions/actions.tsx b/plugins/blog/src/views/admin/categories/actions/actions.tsx index 1a4bf97b3..7214f0742 100644 --- a/plugins/blog/src/views/admin/categories/actions/actions.tsx +++ b/plugins/blog/src/views/admin/categories/actions/actions.tsx @@ -11,8 +11,8 @@ import { } from '@vitnode/core/components/ui/dialog'; import { Loader } from '@vitnode/core/components/ui/loader'; import { PlusIcon } from 'lucide-react'; -import { useTranslations } from 'next-intl'; import dynamic from 'next/dynamic'; +import { useTranslations } from 'next-intl'; import React from 'react'; const CreateEditActionCategoriesAdmin = dynamic(async () => diff --git a/plugins/blog/src/views/admin/categories/actions/create-edit/mutation-api.ts b/plugins/blog/src/views/admin/categories/actions/create-edit/mutation-api.ts index e68dfd31c..9a0f007ad 100644 --- a/plugins/blog/src/views/admin/categories/actions/create-edit/mutation-api.ts +++ b/plugins/blog/src/views/admin/categories/actions/create-edit/mutation-api.ts @@ -1,13 +1,10 @@ 'use server'; -import type { z } from 'zod'; - import { fetcher } from '@vitnode/core/lib/fetcher'; import { revalidatePath } from 'next/cache'; - -import type { zodCreateCategorySchema } from '../../../../../api/modules/admin/categories/routes/create.route'; - +import type { z } from 'zod'; import { categoriesAdminModule } from '../../../../../api/modules/admin/categories/categories.admin.module'; +import type { zodCreateCategorySchema } from '../../../../../api/modules/admin/categories/routes/create.route'; export const createMutationApi = async ( body: z.infer, diff --git a/plugins/blog/src/views/admin/categories/table/actions/edit-action.tsx b/plugins/blog/src/views/admin/categories/table/actions/edit-action.tsx index ab09ef373..2c95e13fc 100644 --- a/plugins/blog/src/views/admin/categories/table/actions/edit-action.tsx +++ b/plugins/blog/src/views/admin/categories/table/actions/edit-action.tsx @@ -17,8 +17,8 @@ import { TooltipTrigger, } from '@vitnode/core/components/ui/tooltip'; import { PencilIcon } from 'lucide-react'; -import { useTranslations } from 'next-intl'; import dynamic from 'next/dynamic'; +import { useTranslations } from 'next-intl'; import React from 'react'; const CreateEditActionCategoriesAdmin = dynamic(async () => diff --git a/plugins/blog/src/views/admin/posts/actions/actions.tsx b/plugins/blog/src/views/admin/posts/actions/actions.tsx index ad6ffd5bf..12157b42d 100644 --- a/plugins/blog/src/views/admin/posts/actions/actions.tsx +++ b/plugins/blog/src/views/admin/posts/actions/actions.tsx @@ -11,8 +11,8 @@ import { } from '@vitnode/core/components/ui/dialog'; import { Loader } from '@vitnode/core/components/ui/loader'; import { PlusIcon } from 'lucide-react'; -import { useTranslations } from 'next-intl'; import dynamic from 'next/dynamic'; +import { useTranslations } from 'next-intl'; import React from 'react'; const CreateEditActionPostsAdmin = dynamic(async () => diff --git a/plugins/blog/src/views/admin/posts/actions/create-edit/create-edit.tsx b/plugins/blog/src/views/admin/posts/actions/create-edit/create-edit.tsx index 1adb899dc..f039a14f9 100644 --- a/plugins/blog/src/views/admin/posts/actions/create-edit/create-edit.tsx +++ b/plugins/blog/src/views/admin/posts/actions/create-edit/create-edit.tsx @@ -11,10 +11,8 @@ import { usePathname, useRouter } from '@vitnode/core/lib/navigation'; import { useTranslations } from 'next-intl'; import { toast } from 'sonner'; import { z } from 'zod'; - -import type { zodPostSchema } from '@/api/modules/posts/routes/get.route'; - import { categoriesModule } from '@/api/modules/categories/categories.module'; +import type { zodPostSchema } from '@/api/modules/posts/routes/get.route'; import { createMutationApi, editMutationApi } from './mutation-api'; diff --git a/plugins/blog/src/views/admin/posts/actions/create-edit/mutation-api.ts b/plugins/blog/src/views/admin/posts/actions/create-edit/mutation-api.ts index 270e90ac8..6f197e4da 100644 --- a/plugins/blog/src/views/admin/posts/actions/create-edit/mutation-api.ts +++ b/plugins/blog/src/views/admin/posts/actions/create-edit/mutation-api.ts @@ -1,13 +1,10 @@ 'use server'; -import type { z } from 'zod'; - import { fetcher } from '@vitnode/core/lib/fetcher'; import { revalidatePath } from 'next/cache'; - -import type { zodCreatePostSchema } from '@/api/modules/admin/posts/routes/create.route'; - +import type { z } from 'zod'; import { postsAdminModule } from '@/api/modules/admin/posts/posts.admin.module'; +import type { zodCreatePostSchema } from '@/api/modules/admin/posts/routes/create.route'; export const createMutationApi = async ( body: z.infer, diff --git a/plugins/blog/src/views/admin/posts/table/actions/edit-action.tsx b/plugins/blog/src/views/admin/posts/table/actions/edit-action.tsx index 4bccda450..3a05eb03c 100644 --- a/plugins/blog/src/views/admin/posts/table/actions/edit-action.tsx +++ b/plugins/blog/src/views/admin/posts/table/actions/edit-action.tsx @@ -17,8 +17,8 @@ import { TooltipTrigger, } from '@vitnode/core/components/ui/tooltip'; import { PencilIcon } from 'lucide-react'; -import { useTranslations } from 'next-intl'; import dynamic from 'next/dynamic'; +import { useTranslations } from 'next-intl'; import React from 'react'; const CreateEditActionPostsAdmin = dynamic(async () => diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index feecd4b9a..53852bd37 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,18 +8,15 @@ importers: .: devDependencies: + '@biomejs/biome': + specifier: ^2.2.2 + version: 2.2.2 '@types/node': specifier: ^24.3.0 version: 24.3.0 '@vitnode/eslint-config': specifier: workspace:* version: link:packages/eslint - prettier: - specifier: ^3.6.2 - version: 3.6.2 - prettier-plugin-tailwindcss: - specifier: ^0.6.14 - version: 0.6.14(prettier-plugin-astro@0.7.2)(prettier@3.6.2) tsx: specifier: ^4.20.4 version: 4.20.4 @@ -55,7 +52,7 @@ importers: version: 4.9.2 next-intl: specifier: ^4.3.4 - version: 4.3.4(next@15.5.0(@playwright/test@1.55.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + version: 4.3.4(next@15.5.0(@playwright/test@1.55.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(typescript@5.9.2) react: specifier: ^19.1.1 version: 19.1.1 @@ -90,9 +87,6 @@ importers: dotenv: specifier: ^17.2.1 version: 17.2.1 - eslint: - specifier: ^9.33.0 - version: 9.33.0(jiti@2.5.1) react-email: specifier: ^4.2.8 version: 4.2.8 @@ -152,7 +146,7 @@ importers: version: 15.5.0(@playwright/test@1.55.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) next-intl: specifier: ^4.3.4 - version: 4.3.4(next@15.5.0(@playwright/test@1.55.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(typescript@5.9.2) + version: 4.3.4(next@15.5.0(@playwright/test@1.55.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(typescript@5.9.2) react: specifier: ^19.1.1 version: 19.1.1 @@ -196,9 +190,6 @@ importers: class-variance-authority: specifier: ^0.7.1 version: 0.7.1 - eslint: - specifier: ^9.33.0 - version: 9.33.0(jiti@2.5.1) postcss: specifier: ^8.5.6 version: 8.5.6 @@ -254,51 +245,15 @@ importers: '@vitnode/eslint-config': specifier: workspace:* version: link:../eslint - eslint: - specifier: ^9.33.0 - version: 9.33.0(jiti@2.5.1) typescript: specifier: ^5.9.2 version: 5.9.2 packages/eslint: dependencies: - '@eslint-react/eslint-plugin': - specifier: ^1.52.6 - version: 1.52.6(eslint@9.33.0(jiti@2.5.1))(ts-api-utils@2.1.0(typescript@5.9.2))(typescript@5.9.2) - '@eslint/js': - specifier: ^9.33.0 - version: 9.33.0 eslint: specifier: ^9.0.0 version: 9.33.0(jiti@2.5.1) - eslint-config-prettier: - specifier: ^10.1.8 - version: 10.1.8(eslint@9.33.0(jiti@2.5.1)) - eslint-plugin-jsx-a11y: - specifier: ^6.10.2 - version: 6.10.2(eslint@9.33.0(jiti@2.5.1)) - eslint-plugin-perfectionist: - specifier: ^4.15.0 - version: 4.15.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - eslint-plugin-prettier: - specifier: ^5.5.4 - version: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.33.0(jiti@2.5.1)))(eslint@9.33.0(jiti@2.5.1))(prettier@3.6.2) - eslint-plugin-react: - specifier: ^7.37.5 - version: 7.37.5(eslint@9.33.0(jiti@2.5.1)) - eslint-plugin-react-compiler: - specifier: 19.1.0-rc.2 - version: 19.1.0-rc.2(eslint@9.33.0(jiti@2.5.1)) - eslint-plugin-react-hooks: - specifier: 6.0.0-rc1 - version: 6.0.0-rc1(eslint@9.33.0(jiti@2.5.1)) - prettier: - specifier: ^3.0.0 - version: 3.6.2 - prettier-plugin-tailwindcss: - specifier: ^0.6.14 - version: 0.6.14(prettier-plugin-astro@0.7.2)(prettier@3.6.2) typescript-eslint: specifier: ^8.40.0 version: 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) @@ -430,9 +385,6 @@ importers: drizzle-orm: specifier: ^0.44.4 version: 0.44.4(@neondatabase/serverless@0.10.4)(@types/pg@8.11.10)(gel@2.1.0)(pg@8.13.1)(postgres@3.4.7) - eslint: - specifier: ^9.33.0 - version: 9.33.0(jiti@2.5.1) hono: specifier: ^4.9.2 version: 4.9.2 @@ -560,9 +512,6 @@ importers: concurrently: specifier: ^9.2.0 version: 9.2.0 - eslint: - specifier: ^9.33.0 - version: 9.33.0(jiti@2.5.1) tsc-alias: specifier: ^1.8.16 version: 1.8.16 @@ -588,9 +537,6 @@ packages: peerDependencies: zod: ^4.0.0 - '@astrojs/compiler@0.31.4': - resolution: {integrity: sha512-6bBFeDTtPOn4jZaiD3p0f05MEGQL9pw2Zbfj546oFETNmjJFWO3nzHz6/m+P53calknCvyVzZ5YhoBLIvzn5iw==} - '@aws-crypto/sha256-browser@5.2.0': resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} @@ -732,28 +678,14 @@ packages: resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} engines: {node: '>=6.9.0'} - '@babel/helper-annotate-as-pure@7.27.3': - resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} - engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.27.2': resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} - '@babel/helper-create-class-features-plugin@7.28.3': - resolution: {integrity: sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - '@babel/helper-globals@7.28.0': resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} engines: {node: '>=6.9.0'} - '@babel/helper-member-expression-to-functions@7.27.1': - resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==} - engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.27.1': resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} @@ -764,24 +696,10 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-optimise-call-expression@7.27.1': - resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} - engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.27.1': resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} engines: {node: '>=6.9.0'} - '@babel/helper-replace-supers@7.27.1': - resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': - resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} - engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} @@ -808,19 +726,6 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-proposal-private-methods@7.18.6': - resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} - engines: {node: '>=6.9.0'} - deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead. - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-private-methods@7.27.1': - resolution: {integrity: sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-self@7.27.1': resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} engines: {node: '>=6.9.0'} @@ -857,6 +762,59 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} + '@biomejs/biome@2.2.2': + resolution: {integrity: sha512-j1omAiQWCkhuLgwpMKisNKnsM6W8Xtt1l0WZmqY/dFj8QPNkIoTvk4tSsi40FaAAkBE1PU0AFG2RWFBWenAn+w==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@2.2.2': + resolution: {integrity: sha512-6ePfbCeCPryWu0CXlzsWNZgVz/kBEvHiPyNpmViSt6A2eoDf4kXs3YnwQPzGjy8oBgQulrHcLnJL0nkCh80mlQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@2.2.2': + resolution: {integrity: sha512-Tn4JmVO+rXsbRslml7FvKaNrlgUeJot++FkvYIhl1OkslVCofAtS35MPlBMhXgKWF9RNr9cwHanrPTUUXcYGag==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@2.2.2': + resolution: {integrity: sha512-/MhYg+Bd6renn6i1ylGFL5snYUn/Ct7zoGVKhxnro3bwekiZYE8Kl39BSb0MeuqM+72sThkQv4TnNubU9njQRw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-arm64@2.2.2': + resolution: {integrity: sha512-JfrK3gdmWWTh2J5tq/rcWCOsImVyzUnOS2fkjhiYKCQ+v8PqM+du5cfB7G1kXas+7KQeKSWALv18iQqdtIMvzw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-x64-musl@2.2.2': + resolution: {integrity: sha512-ZCLXcZvjZKSiRY/cFANKg+z6Fhsf9MHOzj+NrDQcM+LbqYRT97LyCLWy2AS+W2vP+i89RyRM+kbGpUzbRTYWig==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-linux-x64@2.2.2': + resolution: {integrity: sha512-Ogb+77edO5LEP/xbNicACOWVLt8mgC+E1wmpUakr+O4nKwLt9vXe74YNuT3T1dUBxC/SnrVmlzZFC7kQJEfquQ==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-win32-arm64@2.2.2': + resolution: {integrity: sha512-wBe2wItayw1zvtXysmHJQoQqXlTzHSpQRyPpJKiNIR21HzH/CrZRDFic1C1jDdp+zAPtqhNExa0owKMbNwW9cQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@2.2.2': + resolution: {integrity: sha512-DAuHhHekGfiGb6lCcsT4UyxQmVwQiBCBUMwVra/dcOSs9q8OhfaZgey51MlekT3p8UwRqtXQfFuEJBhJNdLZwg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + '@borewit/text-codec@0.1.1': resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==} @@ -1372,40 +1330,6 @@ packages: resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint-react/ast@1.52.6': - resolution: {integrity: sha512-yBJ8dVflLezQslQ15YN2tc792ceYpXUQWR/VefN508mWMpZ4wUEwf5/BKm33nzcMdLc8IyoUhKjmgW2HZCrboA==} - engines: {node: '>=18.18.0'} - - '@eslint-react/core@1.52.6': - resolution: {integrity: sha512-Nas0c5E9wwvHaD78YDTr6VB9M6xhWICtn1nWn2ChoqKHnbw3UNveYErVUwcuUcfbAGn9taVE0fqaj+MY6zQlag==} - engines: {node: '>=18.18.0'} - - '@eslint-react/eff@1.52.6': - resolution: {integrity: sha512-UpiV0zSIHRFCx6rmDu48gDwrS4wn/+5Ciimukxt3c0PoTGOI/kKpPuHXsQBlP15CqvPOCD6wt8VxOnNug/cKmA==} - engines: {node: '>=18.18.0'} - - '@eslint-react/eslint-plugin@1.52.6': - resolution: {integrity: sha512-Tj2pyYQC4795tfun6u5QIXXS80wSKMFF4Su+t4eLGhLXPX2d1heZX2lgztbaIj9ToRqiX/Mk+9PvYCbAiE+zZw==} - engines: {node: '>=18.18.0'} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: ^4.9.5 || ^5.3.3 - peerDependenciesMeta: - typescript: - optional: true - - '@eslint-react/kit@1.52.6': - resolution: {integrity: sha512-4xkVhPQkeGcyjdoM9mocbjCF96lFP1jXXE2XrsThiy+U/e/BQEz0oOdHBFXdzVmmMGGFjHsbQo6MAIZCoVAAGg==} - engines: {node: '>=18.18.0'} - - '@eslint-react/shared@1.52.6': - resolution: {integrity: sha512-gIvwDQtRXqxa5IoRQDjKZBGZSj7GlGOwwKUqgaLmerlmNbrEyFn/AG0E6e1NBh80WdAmFSiuJG+2Lct1p8SnZg==} - engines: {node: '>=18.18.0'} - - '@eslint-react/var@1.52.6': - resolution: {integrity: sha512-oeAexe8FhImk3RstFvSSbVBFYRMPAVvuUscOrKBbhf9xc0/3drYpLXSPceA++2VaOk/M1mD91ceca9+V0UfNkw==} - engines: {node: '>=18.18.0'} - '@eslint/config-array@0.21.0': resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2219,14 +2143,6 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@pkgr/core@0.1.2': - resolution: {integrity: sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - - '@pkgr/core@0.2.9': - resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - '@playwright/test@1.55.0': resolution: {integrity: sha512-04IXzPwHrW69XusN/SIdDdKZBzMfOT9UNT/YiJit/xpy2VuAoB8NHc8Aplb96zsWDddLnbkPL3TsmrS04ZU2xQ==} engines: {node: '>=18'} @@ -4283,49 +4199,14 @@ packages: aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} - aria-query@5.3.2: - resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} - engines: {node: '>= 0.4'} - - array-buffer-byte-length@1.0.2: - resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} - engines: {node: '>= 0.4'} - - array-includes@3.1.9: - resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} - engines: {node: '>= 0.4'} - array-union@2.1.0: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} - array.prototype.findlast@1.2.5: - resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} - engines: {node: '>= 0.4'} - - array.prototype.flat@1.3.3: - resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} - engines: {node: '>= 0.4'} - - array.prototype.flatmap@1.3.3: - resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} - engines: {node: '>= 0.4'} - - array.prototype.tosorted@1.1.4: - resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} - engines: {node: '>= 0.4'} - - arraybuffer.prototype.slice@1.0.4: - resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} - engines: {node: '>= 0.4'} - assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - ast-types-flow@0.0.8: - resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} - ast-v8-to-istanbul@0.3.4: resolution: {integrity: sha512-cxrAnZNLBnQwBPByK4CeDaw5sWZtMilJE/Q3iDA0aamgaIVNDF9T6K2/8DfYDZEejZ2jNnDrG9m8MY72HFd0KA==} @@ -4333,10 +4214,6 @@ packages: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true - async-function@1.0.0: - resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} - engines: {node: '>= 0.4'} - autoprefixer@10.4.21: resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} engines: {node: ^10 || ^12 || >=14} @@ -4344,18 +4221,6 @@ packages: peerDependencies: postcss: ^8.1.0 - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - - axe-core@4.10.3: - resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==} - engines: {node: '>=4'} - - axobject-query@4.1.0: - resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} - engines: {node: '>= 0.4'} - b4a@1.6.7: resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==} @@ -4395,9 +4260,6 @@ packages: peerDependencies: react: '>=17.0.1' - birecord@0.1.1: - resolution: {integrity: sha512-VUpsf/qykW0heRlC8LooCq28Kxn3mAqKohhDG/49rrsQ1dT1CXyj/pgXS+5BSRzFTR/3DyIBOqQOrGyZOh71Aw==} - bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} @@ -4449,18 +4311,6 @@ packages: resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} engines: {node: '>=14.16'} - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - call-bind@1.0.8: - resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} - engines: {node: '>= 0.4'} - - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -4612,9 +4462,6 @@ packages: resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} engines: {node: ^12.20.0 || >=14} - compare-versions@6.1.1: - resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} - compute-scroll-into-view@3.1.1: resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} @@ -4684,25 +4531,10 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - damerau-levenshtein@1.0.8: - resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} - data-urls@5.0.0: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} engines: {node: '>=18'} - data-view-buffer@1.0.2: - resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} - engines: {node: '>= 0.4'} - - data-view-byte-length@1.0.2: - resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} - engines: {node: '>= 0.4'} - - data-view-byte-offset@1.0.1: - resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} - engines: {node: '>= 0.4'} - debounce@2.2.0: resolution: {integrity: sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw==} engines: {node: '>=18'} @@ -4757,14 +4589,6 @@ packages: resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} engines: {node: '>=10'} - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -4789,10 +4613,6 @@ packages: dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} - dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} @@ -4909,10 +4729,6 @@ packages: sqlite3: optional: true - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -4958,41 +4774,9 @@ packages: error-stack-parser@2.1.4: resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} - es-abstract@1.24.0: - resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} - engines: {node: '>= 0.4'} - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-iterator-helpers@1.2.1: - resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} - 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'} - - es-set-tostringtag@2.1.0: - resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} - engines: {node: '>= 0.4'} - - es-shim-unscopables@1.1.0: - resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} - engines: {node: '>= 0.4'} - - es-to-primitive@1.3.0: - resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} - engines: {node: '>= 0.4'} - esast-util-from-estree@2.0.0: resolution: {integrity: sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==} @@ -5031,119 +4815,6 @@ packages: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} - eslint-config-prettier@10.1.8: - resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' - - eslint-plugin-jsx-a11y@6.10.2: - resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} - engines: {node: '>=4.0'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 - - eslint-plugin-perfectionist@4.15.0: - resolution: {integrity: sha512-pC7PgoXyDnEXe14xvRUhBII8A3zRgggKqJFx2a82fjrItDs1BSI7zdZnQtM2yQvcyod6/ujmzb7ejKPx8lZTnw==} - engines: {node: ^18.0.0 || >=20.0.0} - peerDependencies: - eslint: '>=8.45.0' - - eslint-plugin-prettier@5.5.4: - resolution: {integrity: sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - '@types/eslint': '>=8.0.0' - eslint: '>=8.0.0' - eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' - prettier: '>=3.0.0' - peerDependenciesMeta: - '@types/eslint': - optional: true - eslint-config-prettier: - optional: true - - eslint-plugin-react-compiler@19.1.0-rc.2: - resolution: {integrity: sha512-oKalwDGcD+RX9mf3NEO4zOoUMeLvjSvcbbEOpquzmzqEEM2MQdp7/FY/Hx9NzmUwFzH1W9SKTz5fihfMldpEYw==} - engines: {node: ^14.17.0 || ^16.0.0 || >= 18.0.0} - peerDependencies: - eslint: '>=7' - - eslint-plugin-react-debug@1.52.6: - resolution: {integrity: sha512-RNSlh8Ss3O5HmMpyVsp/rcoe/h3FR0PyNEUa6aCGdXDmNRFZsjOc3YCbwZ25dkBd+avWLOvldDpX70G1VykfGw==} - engines: {node: '>=18.18.0'} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: ^4.9.5 || ^5.3.3 - peerDependenciesMeta: - typescript: - optional: true - - eslint-plugin-react-dom@1.52.6: - resolution: {integrity: sha512-5C8jW9JesuVIkaDQw/70Vj5L2xcHIQW8wiUbjkKdCN0aiyMU0dyCwId8O3V4Vt8r+spVriEmYnmx0mFSWXbuEw==} - engines: {node: '>=18.18.0'} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: ^4.9.5 || ^5.3.3 - peerDependenciesMeta: - typescript: - optional: true - - eslint-plugin-react-hooks-extra@1.52.6: - resolution: {integrity: sha512-5yu8iWsghYmIJ8IuAPp+kySFjqNaqpVlbkMADULkfrwUd5PoGC1oau0xiZdKrHDbw40SJJvtUeArgJZXLs2IsQ==} - engines: {node: '>=18.18.0'} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: ^4.9.5 || ^5.3.3 - peerDependenciesMeta: - typescript: - optional: true - - eslint-plugin-react-hooks@6.0.0-rc1: - resolution: {integrity: sha512-I4ntWyjqgGemGtOU85FUdVo00h0i0Y5xvQ7a8EVxyzjOZsxXaxvkKBcYoXbP97QDvDjMzY/nGIvfdB/WRLTGxQ==} - engines: {node: '>=18'} - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - - eslint-plugin-react-naming-convention@1.52.6: - resolution: {integrity: sha512-V5YzlJI1RaWfrGttVcXH7YEqt+dwW6Av4ge9ckuApldMrtdLmRpJXhCDMazUidX3nho4rGQHkjTT84hLWvUXGg==} - engines: {node: '>=18.18.0'} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: ^4.9.5 || ^5.3.3 - peerDependenciesMeta: - typescript: - optional: true - - eslint-plugin-react-web-api@1.52.6: - resolution: {integrity: sha512-pOwdBoV7Wa/Ahsg4yUunUKVmXI2OP+d0jBICN0pANQ9QTXp/7YbDGWZ7c9HmQZizVfBHr3NKSqgdl/mxmh5M1Q==} - engines: {node: '>=18.18.0'} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: ^4.9.5 || ^5.3.3 - peerDependenciesMeta: - typescript: - optional: true - - eslint-plugin-react-x@1.52.6: - resolution: {integrity: sha512-xLW8UG66c16p9WnROysfYkomflVEry/bxPnB1Ef0YZikpCCMDvvoPT6nAUUy4byVvq3c6CJWENT3O85twwkY8w==} - engines: {node: '>=18.18.0'} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - ts-api-utils: ^2.1.0 - typescript: ^4.9.5 || ^5.3.3 - peerDependenciesMeta: - ts-api-utils: - optional: true - typescript: - optional: true - - eslint-plugin-react@7.37.5: - resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} engines: {node: '>=8.0.0'} @@ -5253,9 +4924,6 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - fast-fifo@1.3.2: resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} @@ -5335,10 +5003,6 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - for-each@0.3.5: - resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} - engines: {node: '>= 0.4'} - foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} @@ -5441,13 +5105,6 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - function.prototype.name@1.1.8: - resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} - engines: {node: '>= 0.4'} - - functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - gel@2.1.0: resolution: {integrity: sha512-HCeRqInCt6BjbMmeghJ6BKeYwOj7WJT5Db6IWWAA3IMUUa7or7zJfTUEkUWCxiOtoXnwnm96sFK9Fr47Yh2hOA==} engines: {node: '>= 18.0.0'} @@ -5465,26 +5122,14 @@ packages: resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} engines: {node: '>=18'} - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - get-nonce@1.0.1: resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} engines: {node: '>=6'} - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} - get-symbol-description@1.1.0: - resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} - engines: {node: '>= 0.4'} - get-tsconfig@4.10.1: resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} @@ -5519,10 +5164,6 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} - globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -5530,10 +5171,6 @@ packages: globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - got@13.0.0: resolution: {integrity: sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==} engines: {node: '>=16'} @@ -5544,29 +5181,10 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - has-bigints@1.1.0: - resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} - engines: {node: '>= 0.4'} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - - has-proto@1.2.0: - resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} - engines: {node: '>= 0.4'} - - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} @@ -5590,12 +5208,6 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true - hermes-estree@0.25.1: - resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} - - hermes-parser@0.25.1: - resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} - hono@4.9.2: resolution: {integrity: sha512-UG2jXGS/gkLH42l/1uROnwXpkjvvxkl3kpopL3LBo27NuaDPI6xHNfuUSilIHcrBkPfl4y0z6y2ByI455TjNRw==} engines: {node: '>=16.9.0'} @@ -5685,10 +5297,6 @@ packages: inspect-with-kind@1.0.5: resolution: {integrity: sha512-MAQUJuIo7Xqk8EVNP+6d3CKq9c80hi4tjIbIAT6lmGW9W6WzlHiu9PS8uSuUYU+Do+j1baiFp3H25XEVxDIG2g==} - internal-slot@1.1.0: - resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} - engines: {node: '>= 0.4'} - intl-messageformat@10.7.16: resolution: {integrity: sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==} @@ -5698,45 +5306,17 @@ packages: is-alphanumerical@2.0.1: resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} - is-array-buffer@3.0.5: - resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} - engines: {node: '>= 0.4'} - is-arrayish@0.3.2: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - is-async-function@2.1.1: - resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} - engines: {node: '>= 0.4'} - - is-bigint@1.1.0: - resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} - engines: {node: '>= 0.4'} - is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} - is-boolean-object@1.2.2: - resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} - engines: {node: '>= 0.4'} - - is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - is-core-module@2.16.1: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} - is-data-view@1.0.2: - resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} - engines: {node: '>= 0.4'} - - is-date-object@1.1.0: - resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} - engines: {node: '>= 0.4'} - is-decimal@2.0.1: resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} @@ -5744,18 +5324,10 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - is-finalizationregistry@1.1.1: - resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} - engines: {node: '>= 0.4'} - is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - is-generator-function@1.1.0: - resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} - engines: {node: '>= 0.4'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -5763,12 +5335,6 @@ packages: is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} - is-immutable-type@5.0.1: - resolution: {integrity: sha512-LkHEOGVZZXxGl8vDs+10k3DvP++SEoYEAJLRk6buTFi6kD7QekThV7xHS0j6gpnUCQ0zpud/gMDGiV4dQneLTg==} - peerDependencies: - eslint: '*' - typescript: '>=4.7.4' - is-interactive@1.0.0: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} @@ -5777,18 +5343,6 @@ packages: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} - is-map@2.0.3: - resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} - engines: {node: '>= 0.4'} - - is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} - engines: {node: '>= 0.4'} - - is-number-object@1.1.1: - resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} - engines: {node: '>= 0.4'} - is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -5804,34 +5358,10 @@ packages: is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} - is-regex@1.2.1: - resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} - engines: {node: '>= 0.4'} - - is-set@2.0.3: - resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} - engines: {node: '>= 0.4'} - - is-shared-array-buffer@1.0.4: - resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} - engines: {node: '>= 0.4'} - is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - is-string@1.1.1: - resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} - engines: {node: '>= 0.4'} - - is-symbol@1.1.1: - resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} - engines: {node: '>= 0.4'} - - is-typed-array@1.1.15: - resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} - engines: {node: '>= 0.4'} - is-unicode-supported@0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} @@ -5844,21 +5374,6 @@ packages: resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} engines: {node: '>=18'} - is-weakmap@2.0.2: - resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} - engines: {node: '>= 0.4'} - - is-weakref@1.1.1: - resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} - engines: {node: '>= 0.4'} - - is-weakset@2.0.4: - resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} - engines: {node: '>= 0.4'} - - isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -5882,10 +5397,6 @@ packages: resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} engines: {node: '>=8'} - iterator.prototype@1.1.5: - resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} - engines: {node: '>= 0.4'} - jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} @@ -5960,10 +5471,6 @@ packages: engines: {node: '>=6'} hasBin: true - jsx-ast-utils@3.3.5: - resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} - engines: {node: '>=4.0'} - keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -5979,13 +5486,6 @@ packages: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} - language-subtag-registry@0.3.23: - resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} - - language-tags@1.0.9: - resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} - engines: {node: '>=0.10'} - leac@0.6.0: resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} @@ -6104,10 +5604,6 @@ packages: longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - loupe@3.2.0: resolution: {integrity: sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==} @@ -6156,10 +5652,6 @@ packages: engines: {node: '>= 16'} hasBin: true - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - md-to-react-email@5.0.5: resolution: {integrity: sha512-OvAXqwq57uOk+WZqFFNCMZz8yDp8BD3WazW1wAKHUrPbbdr89K9DWS6JXY09vd9xNdPNeurI8DU/X4flcfaD8A==} peerDependencies: @@ -6446,10 +5938,6 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - natural-orderby@5.0.0: - resolution: {integrity: sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==} - engines: {node: '>=18'} - negotiator@0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} @@ -6589,30 +6077,6 @@ packages: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - - object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - object.assign@4.1.7: - resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} - engines: {node: '>= 0.4'} - - object.entries@1.1.9: - resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} - engines: {node: '>= 0.4'} - - object.fromentries@2.0.8: - resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} - engines: {node: '>= 0.4'} - - object.values@1.2.1: - resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} - engines: {node: '>= 0.4'} - obuf@1.1.2: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} @@ -6645,10 +6109,6 @@ packages: resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} engines: {node: '>=18'} - own-keys@1.0.1: - resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} - engines: {node: '>= 0.4'} - p-cancelable@3.0.0: resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} engines: {node: '>=12.20'} @@ -6797,10 +6257,6 @@ packages: resolution: {integrity: sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==} engines: {node: '>=12'} - possible-typed-array-names@1.1.0: - resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} - engines: {node: '>= 0.4'} - postcss-import@15.1.0: resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} engines: {node: '>=14.0.0'} @@ -6914,80 +6370,6 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier-linter-helpers@1.0.0: - resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} - engines: {node: '>=6.0.0'} - - prettier-plugin-astro@0.7.2: - resolution: {integrity: sha512-mmifnkG160BtC727gqoimoxnZT/dwr8ASxpoGGl6EHevhfblSOeu+pwH1LAm5Qu1MynizktztFujHHaijLCkww==} - engines: {node: ^14.15.0 || >=16.0.0, pnpm: '>=7.14.0'} - - prettier-plugin-tailwindcss@0.6.14: - resolution: {integrity: sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg==} - engines: {node: '>=14.21.3'} - peerDependencies: - '@ianvs/prettier-plugin-sort-imports': '*' - '@prettier/plugin-hermes': '*' - '@prettier/plugin-oxc': '*' - '@prettier/plugin-pug': '*' - '@shopify/prettier-plugin-liquid': '*' - '@trivago/prettier-plugin-sort-imports': '*' - '@zackad/prettier-plugin-twig': '*' - prettier: ^3.0 - prettier-plugin-astro: '*' - prettier-plugin-css-order: '*' - prettier-plugin-import-sort: '*' - prettier-plugin-jsdoc: '*' - prettier-plugin-marko: '*' - prettier-plugin-multiline-arrays: '*' - prettier-plugin-organize-attributes: '*' - prettier-plugin-organize-imports: '*' - prettier-plugin-sort-imports: '*' - prettier-plugin-style-order: '*' - prettier-plugin-svelte: '*' - peerDependenciesMeta: - '@ianvs/prettier-plugin-sort-imports': - optional: true - '@prettier/plugin-hermes': - optional: true - '@prettier/plugin-oxc': - optional: true - '@prettier/plugin-pug': - optional: true - '@shopify/prettier-plugin-liquid': - optional: true - '@trivago/prettier-plugin-sort-imports': - optional: true - '@zackad/prettier-plugin-twig': - optional: true - prettier-plugin-astro: - optional: true - prettier-plugin-css-order: - optional: true - prettier-plugin-import-sort: - optional: true - prettier-plugin-jsdoc: - optional: true - prettier-plugin-marko: - optional: true - prettier-plugin-multiline-arrays: - optional: true - prettier-plugin-organize-attributes: - optional: true - prettier-plugin-organize-imports: - optional: true - prettier-plugin-sort-imports: - optional: true - prettier-plugin-style-order: - optional: true - prettier-plugin-svelte: - optional: true - - prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} - hasBin: true - prettier@3.6.2: resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} engines: {node: '>=14'} @@ -7014,9 +6396,6 @@ packages: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} - prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} @@ -7075,9 +6454,6 @@ packages: peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 - react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} @@ -7193,10 +6569,6 @@ packages: recma-stringify@1.0.0: resolution: {integrity: sha512-cjwII1MdIIVloKvC9ErQ+OgAtwHBmcZ0Bg4ciz78FtbT8In39aAYbaA7zvxQ61xVMSPE8WxhLwLbhif4Js2C+g==} - reflect.getprototypeof@1.0.10: - resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} - engines: {node: '>= 0.4'} - regex-recursion@6.0.2: resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} @@ -7206,10 +6578,6 @@ packages: regex@6.0.1: resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==} - regexp.prototype.flags@1.5.4: - resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} - engines: {node: '>= 0.4'} - rehype-recma@1.0.0: resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==} @@ -7270,10 +6638,6 @@ packages: engines: {node: '>= 0.4'} hasBin: true - resolve@2.0.0-next.5: - resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} - hasBin: true - responselike@3.0.0: resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} engines: {node: '>=14.16'} @@ -7307,30 +6671,12 @@ packages: rxjs@7.8.2: resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} - s.color@0.0.15: - resolution: {integrity: sha512-AUNrbEUHeKY8XsYr/DYpl+qk5+aM+DChopnWOPEzn8YKzOhv4l2zH6LzZms3tOZP3wwdOyc0RmTciyi46HLIuA==} - - safe-array-concat@1.1.3: - resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} - engines: {node: '>=0.4'} - safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safe-push-apply@1.0.0: - resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} - engines: {node: '>= 0.4'} - - safe-regex-test@1.1.0: - resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} - engines: {node: '>= 0.4'} - safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sass-formatter@0.7.9: - resolution: {integrity: sha512-CWZ8XiSim+fJVG0cFLStwDvft1VI7uvXdCNJYXhDvowiv+DsbD1nXLiQ4zrE5UBvj5DWZJ93cwN0NX5PMsr1Pw==} - saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} @@ -7379,22 +6725,10 @@ packages: serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - - set-function-name@2.0.2: - resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} - engines: {node: '>= 0.4'} - set-harmonic-interval@1.0.1: resolution: {integrity: sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==} engines: {node: '>=6.9'} - set-proto@1.0.0: - resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} - engines: {node: '>= 0.4'} - sharp@0.34.1: resolution: {integrity: sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -7418,22 +6752,6 @@ packages: shiki@3.11.0: resolution: {integrity: sha512-VgKumh/ib38I1i3QkMn6mAQA6XjjQubqaAYhfge71glAll0/4xnt8L2oSuC45Qcr/G5Kbskj4RliMQddGmy/Og==} - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} - siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -7549,16 +6867,9 @@ packages: resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} engines: {node: '>=18'} - stop-iteration-iterator@1.1.0: - resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} - engines: {node: '>= 0.4'} - streamx@2.22.1: resolution: {integrity: sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==} - string-ts@2.2.1: - resolution: {integrity: sha512-Q2u0gko67PLLhbte5HmPfdOjNvUKbKQM+mCNQae6jE91DmoFHY6HH9GcdqCeNx87DZ2KKjiFxmA0R/42OneGWw==} - string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -7571,29 +6882,6 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} - string.prototype.includes@2.0.1: - resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} - engines: {node: '>= 0.4'} - - string.prototype.matchall@4.0.12: - resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} - engines: {node: '>= 0.4'} - - string.prototype.repeat@1.0.0: - resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} - - string.prototype.trim@1.2.10: - resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} - engines: {node: '>= 0.4'} - - string.prototype.trimend@1.0.9: - resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} - engines: {node: '>= 0.4'} - - string.prototype.trimstart@1.0.8: - resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} - engines: {node: '>= 0.4'} - string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -7660,9 +6948,6 @@ packages: engines: {node: '>=16 || 14 >=14.17'} hasBin: true - suf-log@2.5.3: - resolution: {integrity: sha512-KvC8OPjzdNOe+xQ4XWJV2whQA0aM1kGVczMQ8+dStAO6KfEB140JEVQ9dE76ONZ0/Ylf67ni4tILPJB41U0eow==} - supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -7678,14 +6963,6 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - synckit@0.11.11: - resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} - engines: {node: ^14.18.0 || >=16.0.0} - - synckit@0.8.8: - resolution: {integrity: sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==} - engines: {node: ^14.18.0 || >=16.0.0} - tailwind-merge@3.2.0: resolution: {integrity: sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA==} @@ -7823,20 +7100,12 @@ packages: peerDependencies: typescript: '>=4.8.4' - ts-declaration-location@1.0.7: - resolution: {integrity: sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==} - peerDependencies: - typescript: '>=4.0.0' - ts-easing@0.2.0: resolution: {integrity: sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==} ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - ts-pattern@5.8.0: - resolution: {integrity: sha512-kIjN2qmWiHnhgr5DAkAafF9fwb0T5OhMVSWrm8XEdTFnX6+wfXwYOFjeF86UZ54vduqiR7BfqScFmXSzSaH8oA==} - tsc-alias@1.8.16: resolution: {integrity: sha512-QjCyu55NFyRSBAl6+MTFwplpFcnm2Pq01rR/uxfqJoLMm6X3O14KEGtaSDZpJYaE1bJBGDjD0eSuiIWPe2T58g==} engines: {node: '>=16.20.2'} @@ -7932,22 +7201,6 @@ packages: resolution: {integrity: sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==} engines: {node: '>=8'} - typed-array-buffer@1.0.3: - resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} - engines: {node: '>= 0.4'} - - typed-array-byte-length@1.0.3: - resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} - engines: {node: '>= 0.4'} - - typed-array-byte-offset@1.0.4: - resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} - engines: {node: '>= 0.4'} - - typed-array-length@1.0.7: - resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} - engines: {node: '>= 0.4'} - typescript-eslint@8.40.0: resolution: {integrity: sha512-Xvd2l+ZmFDPEt4oj1QEXzA4A2uUK6opvKu3eGN9aGjB8au02lIVcLyi375w94hHyejTOmzIU77L8ol2sRg9n7Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -7967,10 +7220,6 @@ packages: resolution: {integrity: sha512-+NWHrac9dvilNgme+gP4YrBSumsaMZP0fNBtXXFIf33RLLKEcBUKaQZ7ULUbS0sBfcjxIZ4V96OTRkCbM7hxpw==} engines: {node: '>=18'} - unbox-primitive@1.1.0: - resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} - engines: {node: '>= 0.4'} - unbzip2-stream@1.4.3: resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} @@ -8214,22 +7463,6 @@ packages: whatwg-url@7.1.0: resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} - which-boxed-primitive@1.1.1: - resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} - engines: {node: '>= 0.4'} - - which-builtin-type@1.2.1: - resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} - engines: {node: '>= 0.4'} - - which-collection@1.0.2: - resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} - engines: {node: '>= 0.4'} - - which-typed-array@1.1.19: - resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} - engines: {node: '>= 0.4'} - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -8340,18 +7573,9 @@ packages: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} - zod-validation-error@3.5.3: - resolution: {integrity: sha512-OT5Y8lbUadqVZCsnyFaTQ4/O2mys4tj7PqhdbBCp7McPwvIEKfPtdA6QfPeFQK2/Rz5LgwmAXRJTugBNBi0btw==} - engines: {node: '>=18.0.0'} - peerDependencies: - zod: ^3.25.0 || ^4.0.0 - zod@3.24.3: resolution: {integrity: sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==} - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - zod@4.0.17: resolution: {integrity: sha512-1PHjlYRevNxxdy2JZ8JcNAw7rX8V9P1AKkP+x/xZfxB0K5FYfuV+Ug6P/6NVSR2jHQ+FzDDoDHS04nYUsOIyLQ==} @@ -8380,9 +7604,6 @@ snapshots: openapi3-ts: 4.5.0 zod: 4.0.17 - '@astrojs/compiler@0.31.4': - optional: true - '@aws-crypto/sha256-browser@5.2.0': dependencies: '@aws-crypto/sha256-js': 5.2.0 @@ -8821,10 +8042,6 @@ snapshots: '@jridgewell/trace-mapping': 0.3.30 jsesc: 3.1.0 - '@babel/helper-annotate-as-pure@7.27.3': - dependencies: - '@babel/types': 7.28.2 - '@babel/helper-compilation-targets@7.27.2': dependencies: '@babel/compat-data': 7.28.0 @@ -8833,28 +8050,8 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.28.3(@babel/core@7.28.3)': - dependencies: - '@babel/core': 7.28.3 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-member-expression-to-functions': 7.27.1 - '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.3) - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.28.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - '@babel/helper-globals@7.28.0': {} - '@babel/helper-member-expression-to-functions@7.27.1': - dependencies: - '@babel/traverse': 7.28.3 - '@babel/types': 7.28.2 - transitivePeerDependencies: - - supports-color - '@babel/helper-module-imports@7.27.1': dependencies: '@babel/traverse': 7.28.3 @@ -8880,28 +8077,8 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-optimise-call-expression@7.27.1': - dependencies: - '@babel/types': 7.28.2 - '@babel/helper-plugin-utils@7.27.1': {} - '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.3)': - dependencies: - '@babel/core': 7.28.3 - '@babel/helper-member-expression-to-functions': 7.27.1 - '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.3 - transitivePeerDependencies: - - supports-color - - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': - dependencies: - '@babel/traverse': 7.28.3 - '@babel/types': 7.28.2 - transitivePeerDependencies: - - supports-color - '@babel/helper-string-parser@7.27.1': {} '@babel/helper-validator-identifier@7.27.1': {} @@ -8921,22 +8098,6 @@ snapshots: dependencies: '@babel/types': 7.28.2 - '@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.28.3)': - dependencies: - '@babel/core': 7.28.3 - '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.3) - '@babel/helper-plugin-utils': 7.27.1 - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.28.3)': - dependencies: - '@babel/core': 7.28.3 - '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.3) - '@babel/helper-plugin-utils': 7.27.1 - transitivePeerDependencies: - - supports-color - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.3)': dependencies: '@babel/core': 7.28.3 @@ -8986,6 +8147,41 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} + '@biomejs/biome@2.2.2': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.2.2 + '@biomejs/cli-darwin-x64': 2.2.2 + '@biomejs/cli-linux-arm64': 2.2.2 + '@biomejs/cli-linux-arm64-musl': 2.2.2 + '@biomejs/cli-linux-x64': 2.2.2 + '@biomejs/cli-linux-x64-musl': 2.2.2 + '@biomejs/cli-win32-arm64': 2.2.2 + '@biomejs/cli-win32-x64': 2.2.2 + + '@biomejs/cli-darwin-arm64@2.2.2': + optional: true + + '@biomejs/cli-darwin-x64@2.2.2': + optional: true + + '@biomejs/cli-linux-arm64-musl@2.2.2': + optional: true + + '@biomejs/cli-linux-arm64@2.2.2': + optional: true + + '@biomejs/cli-linux-x64-musl@2.2.2': + optional: true + + '@biomejs/cli-linux-x64@2.2.2': + optional: true + + '@biomejs/cli-win32-arm64@2.2.2': + optional: true + + '@biomejs/cli-win32-x64@2.2.2': + optional: true + '@borewit/text-codec@0.1.1': {} '@clack/core@0.3.5': @@ -9280,98 +8476,6 @@ snapshots: '@eslint-community/regexpp@4.12.1': {} - '@eslint-react/ast@1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)': - dependencies: - '@eslint-react/eff': 1.52.6 - '@typescript-eslint/types': 8.40.0 - '@typescript-eslint/typescript-estree': 8.40.0(typescript@5.9.2) - '@typescript-eslint/utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - string-ts: 2.2.1 - ts-pattern: 5.8.0 - transitivePeerDependencies: - - eslint - - supports-color - - typescript - - '@eslint-react/core@1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)': - dependencies: - '@eslint-react/ast': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/eff': 1.52.6 - '@eslint-react/kit': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/shared': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/var': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@typescript-eslint/scope-manager': 8.40.0 - '@typescript-eslint/type-utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@typescript-eslint/types': 8.40.0 - '@typescript-eslint/utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - birecord: 0.1.1 - ts-pattern: 5.8.0 - transitivePeerDependencies: - - eslint - - supports-color - - typescript - - '@eslint-react/eff@1.52.6': {} - - '@eslint-react/eslint-plugin@1.52.6(eslint@9.33.0(jiti@2.5.1))(ts-api-utils@2.1.0(typescript@5.9.2))(typescript@5.9.2)': - dependencies: - '@eslint-react/eff': 1.52.6 - '@eslint-react/kit': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/shared': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@typescript-eslint/scope-manager': 8.40.0 - '@typescript-eslint/type-utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@typescript-eslint/types': 8.40.0 - '@typescript-eslint/utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - eslint: 9.33.0(jiti@2.5.1) - eslint-plugin-react-debug: 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - eslint-plugin-react-dom: 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - eslint-plugin-react-hooks-extra: 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - eslint-plugin-react-naming-convention: 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - eslint-plugin-react-web-api: 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - eslint-plugin-react-x: 1.52.6(eslint@9.33.0(jiti@2.5.1))(ts-api-utils@2.1.0(typescript@5.9.2))(typescript@5.9.2) - optionalDependencies: - typescript: 5.9.2 - transitivePeerDependencies: - - supports-color - - ts-api-utils - - '@eslint-react/kit@1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)': - dependencies: - '@eslint-react/eff': 1.52.6 - '@typescript-eslint/utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - ts-pattern: 5.8.0 - zod: 4.0.17 - transitivePeerDependencies: - - eslint - - supports-color - - typescript - - '@eslint-react/shared@1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)': - dependencies: - '@eslint-react/eff': 1.52.6 - '@eslint-react/kit': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@typescript-eslint/utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - ts-pattern: 5.8.0 - zod: 4.0.17 - transitivePeerDependencies: - - eslint - - supports-color - - typescript - - '@eslint-react/var@1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2)': - dependencies: - '@eslint-react/ast': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/eff': 1.52.6 - '@typescript-eslint/scope-manager': 8.40.0 - '@typescript-eslint/types': 8.40.0 - '@typescript-eslint/utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - string-ts: 2.2.1 - ts-pattern: 5.8.0 - transitivePeerDependencies: - - eslint - - supports-color - - typescript - '@eslint/config-array@0.21.0': dependencies: '@eslint/object-schema': 2.1.6 @@ -10050,11 +9154,6 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@pkgr/core@0.1.2': - optional: true - - '@pkgr/core@0.2.9': {} - '@playwright/test@1.55.0': dependencies: playwright: 1.55.0 @@ -12522,71 +11621,10 @@ snapshots: dependencies: dequal: 2.0.3 - aria-query@5.3.2: {} - - array-buffer-byte-length@1.0.2: - dependencies: - call-bound: 1.0.4 - is-array-buffer: 3.0.5 - - array-includes@3.1.9: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - is-string: 1.1.1 - math-intrinsics: 1.1.0 - array-union@2.1.0: {} - array.prototype.findlast@1.2.5: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - es-shim-unscopables: 1.1.0 - - array.prototype.flat@1.3.3: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-shim-unscopables: 1.1.0 - - array.prototype.flatmap@1.3.3: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-shim-unscopables: 1.1.0 - - array.prototype.tosorted@1.1.4: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-errors: 1.3.0 - es-shim-unscopables: 1.1.0 - - arraybuffer.prototype.slice@1.0.4: - dependencies: - array-buffer-byte-length: 1.0.2 - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - is-array-buffer: 3.0.5 - assertion-error@2.0.1: {} - ast-types-flow@0.0.8: {} - ast-v8-to-istanbul@0.3.4: dependencies: '@jridgewell/trace-mapping': 0.3.30 @@ -12595,8 +11633,6 @@ snapshots: astring@1.9.0: {} - async-function@1.0.0: {} - autoprefixer@10.4.21(postcss@8.5.6): dependencies: browserslist: 4.25.3 @@ -12607,14 +11643,6 @@ snapshots: postcss: 8.5.6 postcss-value-parser: 4.2.0 - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.1.0 - - axe-core@4.10.3: {} - - axobject-query@4.1.0: {} - b4a@1.6.7: {} babel-plugin-react-compiler@19.1.0-rc.2: @@ -12652,8 +11680,6 @@ snapshots: transitivePeerDependencies: - '@types/react' - birecord@0.1.1: {} - bl@4.1.0: dependencies: buffer: 5.7.1 @@ -12712,23 +11738,6 @@ snapshots: normalize-url: 8.0.2 responselike: 3.0.0 - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bind@1.0.8: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - get-intrinsic: 1.3.0 - set-function-length: 1.2.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - callsites@3.1.0: {} camelcase-css@2.0.1: {} @@ -12862,8 +11871,6 @@ snapshots: commander@9.5.0: {} - compare-versions@6.1.1: {} - compute-scroll-into-view@3.1.1: {} concat-map@0.0.1: {} @@ -12935,31 +11942,11 @@ snapshots: csstype@3.1.3: {} - damerau-levenshtein@1.0.8: {} - data-urls@5.0.0: dependencies: whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 - data-view-buffer@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-data-view: 1.0.2 - - data-view-byte-length@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-data-view: 1.0.2 - - data-view-byte-offset@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-data-view: 1.0.2 - debounce@2.2.0: {} debug@4.3.7: @@ -12994,18 +11981,6 @@ snapshots: defer-to-connect@2.0.1: {} - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.1 - es-errors: 1.3.0 - gopd: 1.2.0 - - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 - dequal@2.0.3: {} detect-libc@2.0.4: {} @@ -13024,10 +11999,6 @@ snapshots: dlv@1.1.3: {} - doctrine@2.1.0: - dependencies: - esutils: 2.0.3 - dom-accessibility-api@0.5.16: {} dom-serializer@2.0.0: @@ -13067,12 +12038,6 @@ snapshots: pg: 8.13.1 postgres: 3.4.7 - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - eastasianwidth@0.2.0: {} electron-to-chromium@1.5.207: {} @@ -13129,109 +12094,8 @@ snapshots: dependencies: stackframe: 1.3.4 - es-abstract@1.24.0: - dependencies: - array-buffer-byte-length: 1.0.2 - arraybuffer.prototype.slice: 1.0.4 - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.4 - data-view-buffer: 1.0.2 - data-view-byte-length: 1.0.2 - data-view-byte-offset: 1.0.1 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - es-set-tostringtag: 2.1.0 - es-to-primitive: 1.3.0 - function.prototype.name: 1.1.8 - get-intrinsic: 1.3.0 - get-proto: 1.0.1 - get-symbol-description: 1.1.0 - globalthis: 1.0.4 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - has-proto: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - internal-slot: 1.1.0 - is-array-buffer: 3.0.5 - is-callable: 1.2.7 - is-data-view: 1.0.2 - is-negative-zero: 2.0.3 - is-regex: 1.2.1 - is-set: 2.0.3 - is-shared-array-buffer: 1.0.4 - is-string: 1.1.1 - is-typed-array: 1.1.15 - is-weakref: 1.1.1 - math-intrinsics: 1.1.0 - object-inspect: 1.13.4 - object-keys: 1.1.1 - object.assign: 4.1.7 - own-keys: 1.0.1 - regexp.prototype.flags: 1.5.4 - safe-array-concat: 1.1.3 - safe-push-apply: 1.0.0 - safe-regex-test: 1.1.0 - set-proto: 1.0.0 - stop-iteration-iterator: 1.1.0 - string.prototype.trim: 1.2.10 - string.prototype.trimend: 1.0.9 - string.prototype.trimstart: 1.0.8 - typed-array-buffer: 1.0.3 - typed-array-byte-length: 1.0.3 - typed-array-byte-offset: 1.0.4 - typed-array-length: 1.0.7 - unbox-primitive: 1.1.0 - which-typed-array: 1.1.19 - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-iterator-helpers@1.2.1: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-errors: 1.3.0 - es-set-tostringtag: 2.1.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - globalthis: 1.0.4 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - has-proto: 1.2.0 - has-symbols: 1.1.0 - internal-slot: 1.1.0 - iterator.prototype: 1.1.5 - safe-array-concat: 1.1.3 - es-module-lexer@1.7.0: {} - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.1.0: - dependencies: - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - es-shim-unscopables@1.1.0: - dependencies: - hasown: 2.0.2 - - es-to-primitive@1.3.0: - dependencies: - is-callable: 1.2.7 - is-date-object: 1.1.0 - is-symbol: 1.1.1 - esast-util-from-estree@2.0.0: dependencies: '@types/estree-jsx': 1.0.5 @@ -13302,255 +12166,44 @@ snapshots: '@esbuild/openbsd-arm64': 0.25.0 '@esbuild/openbsd-x64': 0.25.0 '@esbuild/sunos-x64': 0.25.0 - '@esbuild/win32-arm64': 0.25.0 - '@esbuild/win32-ia32': 0.25.0 - '@esbuild/win32-x64': 0.25.0 - - esbuild@0.25.9: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.9 - '@esbuild/android-arm': 0.25.9 - '@esbuild/android-arm64': 0.25.9 - '@esbuild/android-x64': 0.25.9 - '@esbuild/darwin-arm64': 0.25.9 - '@esbuild/darwin-x64': 0.25.9 - '@esbuild/freebsd-arm64': 0.25.9 - '@esbuild/freebsd-x64': 0.25.9 - '@esbuild/linux-arm': 0.25.9 - '@esbuild/linux-arm64': 0.25.9 - '@esbuild/linux-ia32': 0.25.9 - '@esbuild/linux-loong64': 0.25.9 - '@esbuild/linux-mips64el': 0.25.9 - '@esbuild/linux-ppc64': 0.25.9 - '@esbuild/linux-riscv64': 0.25.9 - '@esbuild/linux-s390x': 0.25.9 - '@esbuild/linux-x64': 0.25.9 - '@esbuild/netbsd-arm64': 0.25.9 - '@esbuild/netbsd-x64': 0.25.9 - '@esbuild/openbsd-arm64': 0.25.9 - '@esbuild/openbsd-x64': 0.25.9 - '@esbuild/openharmony-arm64': 0.25.9 - '@esbuild/sunos-x64': 0.25.9 - '@esbuild/win32-arm64': 0.25.9 - '@esbuild/win32-ia32': 0.25.9 - '@esbuild/win32-x64': 0.25.9 - - escalade@3.2.0: {} - - escape-string-regexp@4.0.0: {} - - escape-string-regexp@5.0.0: {} - - eslint-config-prettier@10.1.8(eslint@9.33.0(jiti@2.5.1)): - dependencies: - eslint: 9.33.0(jiti@2.5.1) - - eslint-plugin-jsx-a11y@6.10.2(eslint@9.33.0(jiti@2.5.1)): - dependencies: - aria-query: 5.3.2 - array-includes: 3.1.9 - array.prototype.flatmap: 1.3.3 - ast-types-flow: 0.0.8 - axe-core: 4.10.3 - axobject-query: 4.1.0 - damerau-levenshtein: 1.0.8 - emoji-regex: 9.2.2 - eslint: 9.33.0(jiti@2.5.1) - hasown: 2.0.2 - jsx-ast-utils: 3.3.5 - language-tags: 1.0.9 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - safe-regex-test: 1.1.0 - string.prototype.includes: 2.0.1 - - eslint-plugin-perfectionist@4.15.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2): - dependencies: - '@typescript-eslint/types': 8.40.0 - '@typescript-eslint/utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - eslint: 9.33.0(jiti@2.5.1) - natural-orderby: 5.0.0 - transitivePeerDependencies: - - supports-color - - typescript - - eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@9.33.0(jiti@2.5.1)))(eslint@9.33.0(jiti@2.5.1))(prettier@3.6.2): - dependencies: - eslint: 9.33.0(jiti@2.5.1) - prettier: 3.6.2 - prettier-linter-helpers: 1.0.0 - synckit: 0.11.11 - optionalDependencies: - '@types/eslint': 9.6.1 - eslint-config-prettier: 10.1.8(eslint@9.33.0(jiti@2.5.1)) - - eslint-plugin-react-compiler@19.1.0-rc.2(eslint@9.33.0(jiti@2.5.1)): - dependencies: - '@babel/core': 7.28.3 - '@babel/parser': 7.28.3 - '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.28.3) - eslint: 9.33.0(jiti@2.5.1) - hermes-parser: 0.25.1 - zod: 3.25.76 - zod-validation-error: 3.5.3(zod@3.25.76) - transitivePeerDependencies: - - supports-color - - eslint-plugin-react-debug@1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2): - dependencies: - '@eslint-react/ast': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/core': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/eff': 1.52.6 - '@eslint-react/kit': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/shared': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/var': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@typescript-eslint/scope-manager': 8.40.0 - '@typescript-eslint/type-utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@typescript-eslint/types': 8.40.0 - '@typescript-eslint/utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - eslint: 9.33.0(jiti@2.5.1) - string-ts: 2.2.1 - ts-pattern: 5.8.0 - optionalDependencies: - typescript: 5.9.2 - transitivePeerDependencies: - - supports-color - - eslint-plugin-react-dom@1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2): - dependencies: - '@eslint-react/ast': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/core': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/eff': 1.52.6 - '@eslint-react/kit': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/shared': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/var': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@typescript-eslint/scope-manager': 8.40.0 - '@typescript-eslint/types': 8.40.0 - '@typescript-eslint/utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - compare-versions: 6.1.1 - eslint: 9.33.0(jiti@2.5.1) - string-ts: 2.2.1 - ts-pattern: 5.8.0 - optionalDependencies: - typescript: 5.9.2 - transitivePeerDependencies: - - supports-color - - eslint-plugin-react-hooks-extra@1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2): - dependencies: - '@eslint-react/ast': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/core': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/eff': 1.52.6 - '@eslint-react/kit': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/shared': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/var': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@typescript-eslint/scope-manager': 8.40.0 - '@typescript-eslint/type-utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@typescript-eslint/types': 8.40.0 - '@typescript-eslint/utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - eslint: 9.33.0(jiti@2.5.1) - string-ts: 2.2.1 - ts-pattern: 5.8.0 - optionalDependencies: - typescript: 5.9.2 - transitivePeerDependencies: - - supports-color - - eslint-plugin-react-hooks@6.0.0-rc1(eslint@9.33.0(jiti@2.5.1)): - dependencies: - '@babel/core': 7.28.3 - '@babel/parser': 7.28.3 - '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.28.3) - eslint: 9.33.0(jiti@2.5.1) - hermes-parser: 0.25.1 - zod: 3.25.76 - zod-validation-error: 3.5.3(zod@3.25.76) - transitivePeerDependencies: - - supports-color - - eslint-plugin-react-naming-convention@1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2): - dependencies: - '@eslint-react/ast': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/core': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/eff': 1.52.6 - '@eslint-react/kit': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/shared': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/var': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@typescript-eslint/scope-manager': 8.40.0 - '@typescript-eslint/type-utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@typescript-eslint/types': 8.40.0 - '@typescript-eslint/utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - eslint: 9.33.0(jiti@2.5.1) - string-ts: 2.2.1 - ts-pattern: 5.8.0 - optionalDependencies: - typescript: 5.9.2 - transitivePeerDependencies: - - supports-color + '@esbuild/win32-arm64': 0.25.0 + '@esbuild/win32-ia32': 0.25.0 + '@esbuild/win32-x64': 0.25.0 - eslint-plugin-react-web-api@1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2): - dependencies: - '@eslint-react/ast': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/core': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/eff': 1.52.6 - '@eslint-react/kit': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/shared': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/var': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@typescript-eslint/scope-manager': 8.40.0 - '@typescript-eslint/types': 8.40.0 - '@typescript-eslint/utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - eslint: 9.33.0(jiti@2.5.1) - string-ts: 2.2.1 - ts-pattern: 5.8.0 + esbuild@0.25.9: optionalDependencies: - typescript: 5.9.2 - transitivePeerDependencies: - - supports-color + '@esbuild/aix-ppc64': 0.25.9 + '@esbuild/android-arm': 0.25.9 + '@esbuild/android-arm64': 0.25.9 + '@esbuild/android-x64': 0.25.9 + '@esbuild/darwin-arm64': 0.25.9 + '@esbuild/darwin-x64': 0.25.9 + '@esbuild/freebsd-arm64': 0.25.9 + '@esbuild/freebsd-x64': 0.25.9 + '@esbuild/linux-arm': 0.25.9 + '@esbuild/linux-arm64': 0.25.9 + '@esbuild/linux-ia32': 0.25.9 + '@esbuild/linux-loong64': 0.25.9 + '@esbuild/linux-mips64el': 0.25.9 + '@esbuild/linux-ppc64': 0.25.9 + '@esbuild/linux-riscv64': 0.25.9 + '@esbuild/linux-s390x': 0.25.9 + '@esbuild/linux-x64': 0.25.9 + '@esbuild/netbsd-arm64': 0.25.9 + '@esbuild/netbsd-x64': 0.25.9 + '@esbuild/openbsd-arm64': 0.25.9 + '@esbuild/openbsd-x64': 0.25.9 + '@esbuild/openharmony-arm64': 0.25.9 + '@esbuild/sunos-x64': 0.25.9 + '@esbuild/win32-arm64': 0.25.9 + '@esbuild/win32-ia32': 0.25.9 + '@esbuild/win32-x64': 0.25.9 - eslint-plugin-react-x@1.52.6(eslint@9.33.0(jiti@2.5.1))(ts-api-utils@2.1.0(typescript@5.9.2))(typescript@5.9.2): - dependencies: - '@eslint-react/ast': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/core': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/eff': 1.52.6 - '@eslint-react/kit': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/shared': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@eslint-react/var': 1.52.6(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@typescript-eslint/scope-manager': 8.40.0 - '@typescript-eslint/type-utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - '@typescript-eslint/types': 8.40.0 - '@typescript-eslint/utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - compare-versions: 6.1.1 - eslint: 9.33.0(jiti@2.5.1) - is-immutable-type: 5.0.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - string-ts: 2.2.1 - ts-pattern: 5.8.0 - optionalDependencies: - ts-api-utils: 2.1.0(typescript@5.9.2) - typescript: 5.9.2 - transitivePeerDependencies: - - supports-color + escalade@3.2.0: {} - eslint-plugin-react@7.37.5(eslint@9.33.0(jiti@2.5.1)): - dependencies: - array-includes: 3.1.9 - array.prototype.findlast: 1.2.5 - array.prototype.flatmap: 1.3.3 - array.prototype.tosorted: 1.1.4 - doctrine: 2.1.0 - es-iterator-helpers: 1.2.1 - eslint: 9.33.0(jiti@2.5.1) - estraverse: 5.3.0 - hasown: 2.0.2 - jsx-ast-utils: 3.3.5 - minimatch: 3.1.2 - object.entries: 1.1.9 - object.fromentries: 2.0.8 - object.values: 1.2.1 - prop-types: 15.8.1 - resolve: 2.0.0-next.5 - semver: 6.3.1 - string.prototype.matchall: 4.0.12 - string.prototype.repeat: 1.0.0 + escape-string-regexp@4.0.0: {} + + escape-string-regexp@5.0.0: {} eslint-scope@5.1.1: dependencies: @@ -13700,8 +12353,6 @@ snapshots: fast-deep-equal@3.1.3: {} - fast-diff@1.3.0: {} - fast-fifo@1.3.2: {} fast-glob@3.3.3: @@ -13781,10 +12432,6 @@ snapshots: flatted@3.3.3: {} - for-each@0.3.5: - dependencies: - is-callable: 1.2.7 - foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -13904,17 +12551,6 @@ snapshots: function-bind@1.1.2: {} - function.prototype.name@1.1.8: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - functions-have-names: 1.2.3 - hasown: 2.0.2 - is-callable: 1.2.7 - - functions-have-names@1.2.3: {} - gel@2.1.0: dependencies: '@petamoriken/float16': 3.9.2 @@ -13933,34 +12569,10 @@ snapshots: get-east-asian-width@1.3.0: {} - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - math-intrinsics: 1.1.0 - get-nonce@1.0.1: {} - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - get-stream@6.0.1: {} - get-symbol-description@1.1.0: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - get-tsconfig@4.10.1: dependencies: resolve-pkg-maps: 1.0.0 @@ -13999,11 +12611,6 @@ snapshots: globals@14.0.0: {} - globalthis@1.0.4: - dependencies: - define-properties: 1.2.1 - gopd: 1.2.0 - globby@11.1.0: dependencies: array-union: 2.1.0 @@ -14015,8 +12622,6 @@ snapshots: globrex@0.1.2: {} - gopd@1.2.0: {} - got@13.0.0: dependencies: '@sindresorhus/is': 5.6.0 @@ -14035,24 +12640,8 @@ snapshots: graphemer@1.4.0: {} - has-bigints@1.1.0: {} - has-flag@4.0.0: {} - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.1 - - has-proto@1.2.0: - dependencies: - dunder-proto: 1.0.1 - - has-symbols@1.1.0: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.1.0 - hasown@2.0.2: dependencies: function-bind: 1.1.2 @@ -14122,12 +12711,6 @@ snapshots: he@1.2.0: {} - hermes-estree@0.25.1: {} - - hermes-parser@0.25.1: - dependencies: - hermes-estree: 0.25.1 - hono@4.9.2: {} html-encoding-sniffer@4.0.0: @@ -14214,12 +12797,6 @@ snapshots: dependencies: kind-of: 6.0.3 - internal-slot@1.1.0: - dependencies: - es-errors: 1.3.0 - hasown: 2.0.2 - side-channel: 1.1.0 - intl-messageformat@10.7.16: dependencies: '@formatjs/ecma402-abstract': 2.3.4 @@ -14234,98 +12811,32 @@ snapshots: is-alphabetical: 2.0.1 is-decimal: 2.0.1 - is-array-buffer@3.0.5: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - is-arrayish@0.3.2: {} - is-async-function@2.1.1: - dependencies: - async-function: 1.0.0 - call-bound: 1.0.4 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - - is-bigint@1.1.0: - dependencies: - has-bigints: 1.1.0 - is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 - is-boolean-object@1.2.2: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-callable@1.2.7: {} - is-core-module@2.16.1: dependencies: hasown: 2.0.2 - is-data-view@1.0.2: - dependencies: - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - is-typed-array: 1.1.15 - - is-date-object@1.1.0: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - is-decimal@2.0.1: {} is-extglob@2.1.1: {} - is-finalizationregistry@1.1.1: - dependencies: - call-bound: 1.0.4 - is-fullwidth-code-point@3.0.0: {} - is-generator-function@1.1.0: - dependencies: - call-bound: 1.0.4 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - is-glob@4.0.3: dependencies: is-extglob: 2.1.1 is-hexadecimal@2.0.1: {} - is-immutable-type@5.0.1(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2): - dependencies: - '@typescript-eslint/type-utils': 8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) - eslint: 9.33.0(jiti@2.5.1) - ts-api-utils: 2.1.0(typescript@5.9.2) - ts-declaration-location: 1.0.7(typescript@5.9.2) - typescript: 5.9.2 - transitivePeerDependencies: - - supports-color - is-interactive@1.0.0: {} is-interactive@2.0.0: {} - is-map@2.0.3: {} - - is-negative-zero@2.0.3: {} - - is-number-object@1.1.1: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - is-number@7.0.0: {} is-plain-obj@1.1.0: {} @@ -14334,55 +12845,14 @@ snapshots: is-potential-custom-element-name@1.0.1: {} - is-regex@1.2.1: - dependencies: - call-bound: 1.0.4 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - is-set@2.0.3: {} - - is-shared-array-buffer@1.0.4: - dependencies: - call-bound: 1.0.4 - is-stream@2.0.1: {} - is-string@1.1.1: - dependencies: - call-bound: 1.0.4 - has-tostringtag: 1.0.2 - - is-symbol@1.1.1: - dependencies: - call-bound: 1.0.4 - has-symbols: 1.1.0 - safe-regex-test: 1.1.0 - - is-typed-array@1.1.15: - dependencies: - which-typed-array: 1.1.19 - is-unicode-supported@0.1.0: {} is-unicode-supported@1.3.0: {} is-unicode-supported@2.1.0: {} - is-weakmap@2.0.2: {} - - is-weakref@1.1.1: - dependencies: - call-bound: 1.0.4 - - is-weakset@2.0.4: - dependencies: - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - - isarray@2.0.5: {} - isexe@2.0.0: {} isexe@3.1.1: @@ -14409,15 +12879,6 @@ snapshots: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 - iterator.prototype@1.1.5: - dependencies: - define-data-property: 1.1.4 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - get-proto: 1.0.1 - has-symbols: 1.1.0 - set-function-name: 2.0.2 - jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 @@ -14493,13 +12954,6 @@ snapshots: json5@2.2.3: {} - jsx-ast-utils@3.3.5: - dependencies: - array-includes: 3.1.9 - array.prototype.flat: 1.3.3 - object.assign: 4.1.7 - object.values: 1.2.1 - keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -14510,12 +12964,6 @@ snapshots: kleur@4.1.5: {} - language-subtag-registry@0.3.23: {} - - language-tags@1.0.9: - dependencies: - language-subtag-registry: 0.3.23 - leac@0.6.0: {} levn@0.4.1: @@ -14605,10 +13053,6 @@ snapshots: longest-streak@3.1.0: {} - loose-envify@1.4.0: - dependencies: - js-tokens: 4.0.0 - loupe@3.2.0: {} lowercase-keys@3.0.0: {} @@ -14647,8 +13091,6 @@ snapshots: marked@7.0.4: {} - math-intrinsics@1.1.0: {} - md-to-react-email@5.0.5(react@19.1.1): dependencies: marked: 7.0.4 @@ -15186,8 +13628,6 @@ snapshots: natural-compare@1.4.0: {} - natural-orderby@5.0.0: {} - negotiator@0.6.3: {} negotiator@1.0.0: {} @@ -15214,7 +13654,7 @@ snapshots: optionalDependencies: typescript: 5.9.2 - next-intl@4.3.4(next@15.5.0(@playwright/test@1.55.0)(babel-plugin-react-compiler@19.1.0-rc.2)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(typescript@5.9.2): + next-intl@4.3.4(next@15.5.0(@playwright/test@1.55.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(typescript@5.9.2): dependencies: '@formatjs/intl-localematcher': 0.5.10 negotiator: 1.0.0 @@ -15365,40 +13805,6 @@ snapshots: object-hash@3.0.0: {} - object-inspect@1.13.4: {} - - object-keys@1.1.1: {} - - object.assign@4.1.7: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - has-symbols: 1.1.0 - object-keys: 1.1.1 - - object.entries@1.1.9: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - - object.fromentries@2.0.8: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-object-atoms: 1.1.1 - - object.values@1.2.1: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - obuf@1.1.2: optional: true @@ -15455,12 +13861,6 @@ snapshots: string-width: 7.2.0 strip-ansi: 7.1.0 - own-keys@1.0.1: - dependencies: - get-intrinsic: 1.3.0 - object-keys: 1.1.1 - safe-push-apply: 1.0.0 - p-cancelable@3.0.0: {} p-limit@3.1.0: @@ -15616,8 +14016,6 @@ snapshots: dependencies: queue-lit: 1.5.2 - possible-typed-array-names@1.1.0: {} - postcss-import@15.1.0(postcss@8.5.6): dependencies: postcss: 8.5.6 @@ -15712,27 +14110,6 @@ snapshots: prelude-ls@1.2.1: {} - prettier-linter-helpers@1.0.0: - dependencies: - fast-diff: 1.3.0 - - prettier-plugin-astro@0.7.2: - dependencies: - '@astrojs/compiler': 0.31.4 - prettier: 2.8.8 - sass-formatter: 0.7.9 - synckit: 0.8.8 - optional: true - - prettier-plugin-tailwindcss@0.6.14(prettier-plugin-astro@0.7.2)(prettier@3.6.2): - dependencies: - prettier: 3.6.2 - optionalDependencies: - prettier-plugin-astro: 0.7.2 - - prettier@2.8.8: - optional: true - prettier@3.6.2: {} pretty-bytes@6.1.1: {} @@ -15756,12 +14133,6 @@ snapshots: kleur: 3.0.3 sisteransi: 1.0.5 - prop-types@15.8.1: - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 - property-information@7.1.0: {} punycode@2.3.1: {} @@ -15879,8 +14250,6 @@ snapshots: dependencies: react: 19.1.1 - react-is@16.13.1: {} - react-is@17.0.2: {} react-medium-image-zoom@5.3.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): @@ -16050,17 +14419,6 @@ snapshots: unified: 11.0.5 vfile: 6.0.3 - reflect.getprototypeof@1.0.10: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - get-proto: 1.0.1 - which-builtin-type: 1.2.1 - regex-recursion@6.0.2: dependencies: regex-utilities: 2.3.0 @@ -16071,15 +14429,6 @@ snapshots: dependencies: regex-utilities: 2.3.0 - regexp.prototype.flags@1.5.4: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-errors: 1.3.0 - get-proto: 1.0.1 - gopd: 1.2.0 - set-function-name: 2.0.2 - rehype-recma@1.0.0: dependencies: '@types/estree': 1.0.8 @@ -16162,12 +14511,6 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - resolve@2.0.0-next.5: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - responselike@3.0.0: dependencies: lowercase-keys: 3.0.0 @@ -16224,37 +14567,10 @@ snapshots: dependencies: tslib: 2.8.1 - s.color@0.0.15: - optional: true - - safe-array-concat@1.1.3: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - get-intrinsic: 1.3.0 - has-symbols: 1.1.0 - isarray: 2.0.5 - safe-buffer@5.2.1: {} - safe-push-apply@1.0.0: - dependencies: - es-errors: 1.3.0 - isarray: 2.0.5 - - safe-regex-test@1.1.0: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-regex: 1.2.1 - safer-buffer@2.1.2: {} - sass-formatter@0.7.9: - dependencies: - suf-log: 2.5.3 - optional: true - saxes@6.0.0: dependencies: xmlchars: 2.2.0 @@ -16298,30 +14614,8 @@ snapshots: dependencies: randombytes: 2.1.0 - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.3.0 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - - set-function-name@2.0.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - functions-have-names: 1.2.3 - has-property-descriptors: 1.0.2 - set-harmonic-interval@1.0.1: {} - set-proto@1.0.0: - dependencies: - dunder-proto: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - sharp@0.34.1: dependencies: color: 4.2.3 @@ -16398,34 +14692,6 @@ snapshots: '@shikijs/vscode-textmate': 10.0.2 '@types/hast': 3.0.4 - side-channel-list@1.0.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - - side-channel@1.1.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.0 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - siginfo@2.0.0: {} signal-exit@3.0.7: {} @@ -16550,11 +14816,6 @@ snapshots: stdin-discarder@0.2.2: {} - stop-iteration-iterator@1.1.0: - dependencies: - es-errors: 1.3.0 - internal-slot: 1.1.0 - streamx@2.22.1: dependencies: fast-fifo: 1.3.2 @@ -16562,8 +14823,6 @@ snapshots: optionalDependencies: bare-events: 2.6.1 - string-ts@2.2.1: {} - string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -16582,56 +14841,6 @@ snapshots: get-east-asian-width: 1.3.0 strip-ansi: 7.1.0 - string.prototype.includes@2.0.1: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.24.0 - - string.prototype.matchall@4.0.12: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - get-intrinsic: 1.3.0 - gopd: 1.2.0 - has-symbols: 1.1.0 - internal-slot: 1.1.0 - regexp.prototype.flags: 1.5.4 - set-function-name: 2.0.2 - side-channel: 1.1.0 - - string.prototype.repeat@1.0.0: - dependencies: - define-properties: 1.2.1 - es-abstract: 1.24.0 - - string.prototype.trim@1.2.10: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-data-property: 1.1.4 - define-properties: 1.2.1 - es-abstract: 1.24.0 - es-object-atoms: 1.1.1 - has-property-descriptors: 1.0.2 - - string.prototype.trimend@1.0.9: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.4 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - - string.prototype.trimstart@1.0.8: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-object-atoms: 1.1.1 - string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 @@ -16704,11 +14913,6 @@ snapshots: pirates: 4.0.7 ts-interface-checker: 0.1.13 - suf-log@2.5.3: - dependencies: - s.color: 0.0.15 - optional: true - supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -16721,16 +14925,6 @@ snapshots: symbol-tree@3.2.4: {} - synckit@0.11.11: - dependencies: - '@pkgr/core': 0.2.9 - - synckit@0.8.8: - dependencies: - '@pkgr/core': 0.1.2 - tslib: 2.8.1 - optional: true - tailwind-merge@3.2.0: {} tailwind-merge@3.3.1: {} @@ -16879,17 +15073,10 @@ snapshots: dependencies: typescript: 5.9.2 - ts-declaration-location@1.0.7(typescript@5.9.2): - dependencies: - picomatch: 4.0.3 - typescript: 5.9.2 - ts-easing@0.2.0: {} ts-interface-checker@0.1.13: {} - ts-pattern@5.8.0: {} - tsc-alias@1.8.16: dependencies: chokidar: 3.6.0 @@ -16985,39 +15172,6 @@ snapshots: type-fest@0.7.1: {} - typed-array-buffer@1.0.3: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - is-typed-array: 1.1.15 - - typed-array-byte-length@1.0.3: - dependencies: - call-bind: 1.0.8 - for-each: 0.3.5 - gopd: 1.2.0 - has-proto: 1.2.0 - is-typed-array: 1.1.15 - - typed-array-byte-offset@1.0.4: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - for-each: 0.3.5 - gopd: 1.2.0 - has-proto: 1.2.0 - is-typed-array: 1.1.15 - reflect.getprototypeof: 1.0.10 - - typed-array-length@1.0.7: - dependencies: - call-bind: 1.0.8 - for-each: 0.3.5 - gopd: 1.2.0 - is-typed-array: 1.1.15 - possible-typed-array-names: 1.1.0 - reflect.getprototypeof: 1.0.10 - typescript-eslint@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2): dependencies: '@typescript-eslint/eslint-plugin': 8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.33.0(jiti@2.5.1))(typescript@5.9.2) @@ -17035,13 +15189,6 @@ snapshots: uint8array-extras@1.4.1: {} - unbox-primitive@1.1.0: - dependencies: - call-bound: 1.0.4 - has-bigints: 1.1.0 - has-symbols: 1.1.0 - which-boxed-primitive: 1.1.1 - unbzip2-stream@1.4.3: dependencies: buffer: 5.7.1 @@ -17343,47 +15490,6 @@ snapshots: tr46: 1.0.1 webidl-conversions: 4.0.2 - which-boxed-primitive@1.1.1: - dependencies: - is-bigint: 1.1.0 - is-boolean-object: 1.2.2 - is-number-object: 1.1.1 - is-string: 1.1.1 - is-symbol: 1.1.1 - - which-builtin-type@1.2.1: - dependencies: - call-bound: 1.0.4 - function.prototype.name: 1.1.8 - has-tostringtag: 1.0.2 - is-async-function: 2.1.1 - is-date-object: 1.1.0 - is-finalizationregistry: 1.1.1 - is-generator-function: 1.1.0 - is-regex: 1.2.1 - is-weakref: 1.1.1 - isarray: 2.0.5 - which-boxed-primitive: 1.1.1 - which-collection: 1.0.2 - which-typed-array: 1.1.19 - - which-collection@1.0.2: - dependencies: - is-map: 2.0.3 - is-set: 2.0.3 - is-weakmap: 2.0.2 - is-weakset: 2.0.4 - - which-typed-array@1.1.19: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.4 - for-each: 0.3.5 - get-proto: 1.0.1 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - which@2.0.2: dependencies: isexe: 2.0.0 @@ -17462,14 +15568,8 @@ snapshots: yoctocolors@2.1.2: {} - zod-validation-error@3.5.3(zod@3.25.76): - dependencies: - zod: 3.25.76 - zod@3.24.3: {} - zod@3.25.76: {} - zod@4.0.17: {} zwitch@2.0.4: {} diff --git a/scripts/bump-version.ts b/scripts/bump-version.ts index e4a00d9fb..0c393c986 100644 --- a/scripts/bump-version.ts +++ b/scripts/bump-version.ts @@ -5,10 +5,11 @@ * It copies files and folders from the main web app to the create-vitnode-app template * and manages package version updates across the workspace. */ +/** biome-ignore-all lint/suspicious/noConsole: */ +import { Environment } from './environment.ts'; import { FileCopyManager } from './files/file-copy-manager.ts'; import { VersionManager } from './version-manager.ts'; -import { Environment } from './environment.ts'; // Main execution async function main(): Promise { diff --git a/scripts/environment.ts b/scripts/environment.ts index 12e9a2abe..cec5397a1 100644 --- a/scripts/environment.ts +++ b/scripts/environment.ts @@ -15,34 +15,32 @@ export interface EnvironmentConfig { }; } -export class Environment { - static validate(): EnvironmentConfig { - const required = { - WORKSPACE: process.env.GITHUB_WORKSPACE || process.cwd(), - EVENT_PATH: process.env.GITHUB_EVENT_PATH, - VERSION_TYPE: process.env.VERSION_TYPE, - GITHUB_TOKEN: process.env.GITHUB_TOKEN, - GITHUB_REPOSITORY: process.env.GITHUB_REPOSITORY, - }; +export function validateEnvironment(): EnvironmentConfig { + const required = { + WORKSPACE: process.env.GITHUB_WORKSPACE || process.cwd(), + EVENT_PATH: process.env.GITHUB_EVENT_PATH, + VERSION_TYPE: process.env.VERSION_TYPE, + GITHUB_TOKEN: process.env.GITHUB_TOKEN, + GITHUB_REPOSITORY: process.env.GITHUB_REPOSITORY, + }; - Object.entries(required).forEach(([key, value]) => { - if (!value) - throw new Error(`Missing required environment variable: ${key}`); - }); + Object.entries(required).forEach(([key, value]) => { + if (!value) + throw new Error(`Missing required environment variable: ${key}`); + }); - return { - ...required, - RELEASE_TYPE: process.env.RELEASE_TYPE, - GITHUB_HEAD_REF: process.env.GITHUB_HEAD_REF, - GITHUB_REF: process.env.GITHUB_REF, - GITHUB_ACTOR: process.env.GITHUB_ACTOR, - GITHUB_OPTION_MODE: process.env.GITHUB_OPTION_MODE, - GIT_USER: { - NAME: process.env.GITHUB_USER ?? 'Automated Version Bump', - EMAIL: - process.env.GITHUB_EMAIL ?? - 'gh-action-bump-version@users.noreply.github.com', - }, - } as EnvironmentConfig; - } + return { + ...required, + RELEASE_TYPE: process.env.RELEASE_TYPE, + GITHUB_HEAD_REF: process.env.GITHUB_HEAD_REF, + GITHUB_REF: process.env.GITHUB_REF, + GITHUB_ACTOR: process.env.GITHUB_ACTOR, + GITHUB_OPTION_MODE: process.env.GITHUB_OPTION_MODE, + GIT_USER: { + NAME: process.env.GITHUB_USER ?? 'Automated Version Bump', + EMAIL: + process.env.GITHUB_EMAIL ?? + 'gh-action-bump-version@users.noreply.github.com', + }, + } as EnvironmentConfig; } diff --git a/scripts/files/file-copy-manager.ts b/scripts/files/file-copy-manager.ts index f02e15005..313fe083b 100644 --- a/scripts/files/file-copy-manager.ts +++ b/scripts/files/file-copy-manager.ts @@ -1,7 +1,12 @@ -import { join } from 'path'; -import { FileSystem } from './file-system.ts'; -import { existsSync, statSync } from 'fs'; +/** biome-ignore-all lint/suspicious/noConsole: */ +import { existsSync, statSync } from 'node:fs'; +import { join } from 'node:path'; import type { EnvironmentConfig } from '../environment.ts'; +import { + copyDirectoryExcludingPlugins, + copyFile, + validatePath, +} from './file-system.ts'; export class FileCopyManager { constructor(private env: EnvironmentConfig) {} @@ -23,7 +28,7 @@ export class FileCopyManager { 'api-single-app', ); - if (!FileSystem.validatePath(sourcePath, 'web app directory')) { + if (!validatePath(sourcePath, 'web app directory')) { throw new Error('Required paths not found'); } @@ -59,11 +64,11 @@ export class FileCopyManager { ]); } - async copyFileOrDirectory( + copyFileOrDirectory( sourcePath: string, destPath: string, relativePath: string, - ): Promise { + ) { const from = join(sourcePath, relativePath); const to = join(destPath, relativePath); @@ -74,24 +79,20 @@ export class FileCopyManager { } if (stats.isDirectory()) { - FileSystem.copyDirectoryExcludingPlugins(from, to); + copyDirectoryExcludingPlugins(from, to); } else { - FileSystem.copyFile(from, to); + copyFile(from, to); } } - async copyFiles( - sourcePath: string, - destPath: string, - filesToCopy: string[], - ): Promise { + copyFiles(sourcePath: string, destPath: string, filesToCopy: string[]) { // Handle special files with different names const specialFiles = [ { source: '.gitignore', dest: '.gitignore_template' }, ]; for (const relativePath of filesToCopy) { - await this.copyFileOrDirectory(sourcePath, destPath, relativePath); + this.copyFileOrDirectory(sourcePath, destPath, relativePath); } // Handle special files with different destination names @@ -106,7 +107,7 @@ export class FileCopyManager { } if (stats.isFile()) { - FileSystem.copyFile(from, to); + copyFile(from, to); } } } diff --git a/scripts/files/file-system.ts b/scripts/files/file-system.ts index bbb25008a..c380b1efb 100644 --- a/scripts/files/file-system.ts +++ b/scripts/files/file-system.ts @@ -1,51 +1,54 @@ -import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync } from 'fs'; -import { dirname, join } from 'path'; +import { + copyFileSync, + existsSync, + mkdirSync, + readdirSync, + statSync, +} from 'node:fs'; +import { dirname, join } from 'node:path'; -export class FileSystem { - static copyFile(from: string, to: string): void { - try { - const destinationDir = dirname(to); - if (!existsSync(destinationDir)) { - mkdirSync(destinationDir, { recursive: true }); - } - copyFileSync(from, to); - } catch (error) { - throw new Error( - `Failed to copy file: ${from} → ${to}: ${(error as Error).message}`, - ); +export function copyFile(from: string, to: string): void { + try { + const destinationDir = dirname(to); + if (!existsSync(destinationDir)) { + mkdirSync(destinationDir, { recursive: true }); } + copyFileSync(from, to); + } catch (error) { + throw new Error( + `Failed to copy file: ${from} → ${to}: ${(error as Error).message}`, + ); } +} - static copyDirectoryExcludingPlugins(from: string, to: string): void { - try { - if (!existsSync(to)) { - mkdirSync(to, { recursive: true }); - } +export function copyDirectoryExcludingPlugins(from: string, to: string): void { + try { + if (!existsSync(to)) { + mkdirSync(to, { recursive: true }); + } - for (const item of readdirSync(from)) { - if (item === '(plugins)' || item === '.env') continue; + for (const item of readdirSync(from)) { + if (item === '(plugins)' || item === '.env') continue; - const sourcePath = join(from, item); - const destinationPath = join(to, item); - const stats = statSync(sourcePath); + const sourcePath = join(from, item); + const destinationPath = join(to, item); + const stats = statSync(sourcePath); - if (stats.isDirectory()) { - this.copyDirectoryExcludingPlugins(sourcePath, destinationPath); - } else { - copyFileSync(sourcePath, destinationPath); - } + if (stats.isDirectory()) { + copyDirectoryExcludingPlugins(sourcePath, destinationPath); + } else { + copyFileSync(sourcePath, destinationPath); } - } catch (error) { - throw new Error( - `Failed to copy directory: ${from} → ${to}: ${(error as Error).message}`, - ); } + } catch (error) { + throw new Error( + `Failed to copy directory: ${from} → ${to}: ${(error as Error).message}`, + ); } +} - static validatePath(filePath: string, description: string): boolean { - if (existsSync(filePath)) return true; +export function validatePath(filePath: string, description: string): boolean { + if (existsSync(filePath)) return true; - console.error(`✖ Missing ${description}: ${filePath}`); - return false; - } + throw new Error(`✖ Missing ${description}: ${filePath}`); } diff --git a/scripts/version-manager.ts b/scripts/version-manager.ts index f0da6c01c..b1de89432 100644 --- a/scripts/version-manager.ts +++ b/scripts/version-manager.ts @@ -1,7 +1,9 @@ -import { execSync, spawn } from 'child_process'; -import { readFileSync } from 'fs'; -import { join } from 'path'; -import { EOL } from 'os'; +/** biome-ignore-all lint/suspicious/noConsole: */ + +import { execSync, spawn } from 'node:child_process'; +import { readFileSync } from 'node:fs'; +import { EOL } from 'node:os'; +import { join } from 'node:path'; import type { EnvironmentConfig } from './environment.ts'; interface Config { @@ -33,9 +35,9 @@ export class VersionManager { return; } - await this.validateSetup(); + this.validateSetup(); const currentVersion = this.getCurrentVersion(); - const newVersion = await this.calculateNewVersion(currentVersion); + const newVersion = this.calculateNewVersion(currentVersion); await this.applyVersion(currentVersion, newVersion); await this.commitAndPush(newVersion); } @@ -60,7 +62,7 @@ export class VersionManager { return pkgJson.version.toString(); } - async calculateNewVersion(currentVersion: string): Promise { + calculateNewVersion(currentVersion: string) { const versionType = this.getVersionType(currentVersion); const npmOutput = execSync( `npm version --git-tag-version=false --commit-hooks=false --workspaces --workspaces-update=false ${versionType}`, @@ -105,7 +107,7 @@ export class VersionManager { await this.exposeNewVersion(newVersion); } - async runNpmVersion(version: string): Promise { + runNpmVersion(version: string) { return this.runInWorkspace('npm', [ 'version', '--allow-same-version=true', @@ -117,7 +119,7 @@ export class VersionManager { ]); } - async exposeNewVersion(version: string): Promise { + exposeNewVersion(version: string) { return this.runInWorkspace('sh', [ '-c', `echo "newTag=${version}" >> $GITHUB_OUTPUT`, @@ -125,13 +127,13 @@ export class VersionManager { } async commitAndPush(version: string): Promise { - const branch = await this.getCurrentBranch(); + const branch = this.getCurrentBranch(); await this.setupGit(); await this.createCommit(version); await this.pushChanges(branch); } - async getCurrentBranch(): Promise { + getCurrentBranch() { const { GITHUB_HEAD_REF, GITHUB_REF } = this.env; if (GITHUB_HEAD_REF) return GITHUB_HEAD_REF; if (!GITHUB_REF) throw new Error('No branch found'); @@ -159,14 +161,20 @@ export class VersionManager { async pushChanges(branch: string): Promise { const { GITHUB_ACTOR, GITHUB_TOKEN, GITHUB_REPOSITORY } = this.env; const remoteRepo = `https://${GITHUB_ACTOR}:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git`; - await this.runInWorkspace('git', ['push', remoteRepo]); + // push the branch and set upstream so future pushes can be done with just `git push` + await this.runInWorkspace('git', [ + 'push', + '--set-upstream', + remoteRepo, + branch, + ]); } /** * Runs a command in the workspace */ async runInWorkspace(command: string, args: string[]): Promise { - return new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { const child = spawn(command, args, { cwd: this.env.WORKSPACE }); const errorMessages: Buffer[] = []; @@ -186,7 +194,7 @@ export class VersionManager { }); } - async validateSetup(): Promise { + validateSetup() { // Validate version type if (!CONFIG.ALLOWED_VERSION_TYPES.includes(this.env.VERSION_TYPE)) { throw new Error( From d0a035cfcfec8e456e582afa78b6e277cb662c93 Mon Sep 17 00:00:00 2001 From: aXenDeveloper Date: Mon, 25 Aug 2025 18:59:29 +0200 Subject: [PATCH 2/4] chore: Update biome config --- apps/api/drizzle.config.ts | 8 +- apps/api/src/index.ts | 12 +- apps/api/src/vitnode.api.config.ts | 20 +- apps/docs/drizzle.config.ts | 8 +- apps/docs/e2e/auth.spec.ts | 18 +- apps/docs/e2e/homepage.spec.ts | 10 +- apps/docs/global.d.ts | 6 +- apps/docs/next.config.ts | 6 +- apps/docs/playwright.config.ts | 42 +-- apps/docs/postcss.config.mjs | 2 +- apps/docs/source.config.ts | 4 +- .../(docs)/docs/[[...slug]]/page.client.tsx | 34 +- .../[locale]/(docs)/docs/[[...slug]]/page.tsx | 24 +- .../src/app/[locale]/(docs)/docs/layout.tsx | 14 +- .../app/[locale]/(docs)/docs/not-found.tsx | 2 +- .../src/app/[locale]/(main)/(home)/page.tsx | 30 +- .../(main)/(home)/sections/admin/admin.tsx | 6 +- .../(main)/(home)/sections/call-to-action.tsx | 8 +- .../sections/powering-by/powering-by.tsx | 18 +- .../(plugins)/(vitnode-core)/login/page.tsx | 10 +- .../login/reset-password/page.tsx | 10 +- .../login/sso/[providerId]/page.tsx | 2 +- .../(vitnode-core)/register/page.tsx | 10 +- .../app/[locale]/(main)/[...rest]/page.tsx | 2 +- .../src/app/[locale]/(main)/layout.client.tsx | 6 +- .../src/app/[locale]/(main)/layout.config.tsx | 12 +- apps/docs/src/app/[locale]/(main)/layout.tsx | 6 +- .../src/app/[locale]/(main)/not-found.tsx | 2 +- .../(vitnode-blog)/blog/categories/page.tsx | 30 +- .../(vitnode-blog)/blog/posts/page.tsx | 30 +- .../(vitnode-core)/core/debug/page.tsx | 28 +- .../(plugins)/(vitnode-core)/core/page.tsx | 2 +- .../(vitnode-core)/core/test/page.tsx | 2 +- .../(vitnode-core)/core/users/page.tsx | 24 +- .../src/app/[locale]/admin/(auth)/layout.tsx | 4 +- apps/docs/src/app/[locale]/admin/page.tsx | 2 +- apps/docs/src/app/[locale]/layout.tsx | 24 +- apps/docs/src/app/api/[...route]/route.ts | 10 +- apps/docs/src/app/api/search/route.ts | 4 +- apps/docs/src/app/global-error.tsx | 16 +- apps/docs/src/app/layout.tsx | 2 +- .../animated-beam/animated-beam-home.tsx | 22 +- .../animated-beam/animated-beam.tsx | 44 +-- .../src/components/fumadocs/code-block.tsx | 8 +- apps/docs/src/components/fumadocs/img.tsx | 10 +- apps/docs/src/components/fumadocs/preview.tsx | 10 +- .../src/components/fumadocs/search-dialog.tsx | 10 +- apps/docs/src/components/infinite-slider.tsx | 28 +- apps/docs/src/components/logo-vitnode.tsx | 8 +- apps/docs/src/components/text-animate.tsx | 82 ++--- apps/docs/src/examples/accordion.tsx | 4 +- apps/docs/src/examples/auto-form.tsx | 40 +- apps/docs/src/examples/badge.tsx | 4 +- apps/docs/src/examples/button.tsx | 10 +- apps/docs/src/examples/card.tsx | 2 +- apps/docs/src/examples/checkbox.tsx | 12 +- apps/docs/src/examples/combobox.tsx | 20 +- .../examples/confirm-action-alert-dialog.tsx | 12 +- apps/docs/src/examples/data-table.tsx | 48 +-- apps/docs/src/examples/drawer.tsx | 6 +- apps/docs/src/examples/dropdown-menu.tsx | 6 +- apps/docs/src/examples/hover-card.tsx | 4 +- apps/docs/src/examples/input.tsx | 16 +- apps/docs/src/examples/popover.tsx | 6 +- apps/docs/src/examples/progress.tsx | 6 +- apps/docs/src/examples/radio-group.tsx | 24 +- apps/docs/src/examples/select.tsx | 24 +- apps/docs/src/examples/separator.tsx | 2 +- apps/docs/src/examples/sheet.tsx | 6 +- apps/docs/src/examples/skeleton.tsx | 2 +- apps/docs/src/examples/sonner.tsx | 14 +- apps/docs/src/examples/switch.tsx | 12 +- apps/docs/src/examples/textarea.tsx | 12 +- apps/docs/src/examples/toggle-group.tsx | 6 +- apps/docs/src/examples/toggle.tsx | 6 +- apps/docs/src/examples/tooltip.tsx | 6 +- apps/docs/src/lib/source.ts | 10 +- apps/docs/src/middleware.ts | 10 +- apps/docs/src/vitnode.api.config.ts | 28 +- apps/docs/src/vitnode.config.ts | 18 +- biome.json | 2 +- .../copy-of-vitnode-app/api-bun/src/index.ts | 8 +- .../api-single-app/src/vitnode.api.config.ts | 12 +- .../copy-of-vitnode-app/api/src/index.ts | 12 +- .../api/src/vitnode.api.config.ts | 14 +- .../copy-of-vitnode-app/root/global.d.ts | 4 +- .../copy-of-vitnode-app/root/next.config.ts | 4 +- .../root/src/app/[locale]/(main)/layout.tsx | 6 +- .../root/src/app/[locale]/(main)/page.tsx | 20 +- .../root/src/app/[locale]/layout.tsx | 18 +- .../root/src/middleware.ts | 10 +- .../root/src/vitnode.config.ts | 16 +- .../src/create/create-package-json.ts | 348 +++++++++--------- .../src/create/create-vitnode.ts | 152 ++++---- .../helpers/get-available-package-managers.ts | 12 +- .../src/helpers/get-package-json.ts | 6 +- .../src/helpers/init-vitnode.ts | 22 +- .../src/helpers/install-dependencies.ts | 62 ++-- .../src/helpers/is-folder-empty.ts | 50 +-- .../src/helpers/is-online.ts | 12 +- .../src/helpers/is-writeable.ts | 4 +- .../src/helpers/validate-pkg.ts | 2 +- packages/create-vitnode-app/src/index.ts | 66 ++-- .../create-vitnode-app/src/plugin/index.ts | 10 +- .../create-vitnode-app/src/prepare/prepare.ts | 10 +- packages/create-vitnode-app/src/questions.ts | 56 +-- packages/create-vitnode-app/src/validation.ts | 30 +- packages/vitnode/config/next.config.ts | 6 +- packages/vitnode/global.d.ts | 4 +- packages/vitnode/scripts/get-config.ts | 18 +- packages/vitnode/scripts/plugin.ts | 122 +++--- packages/vitnode/scripts/prepare-database.ts | 106 +++--- .../vitnode/scripts/prepare-plugins-files.ts | 118 +++--- .../scripts/run-interactive-shell-command.ts | 8 +- packages/vitnode/scripts/scripts.ts | 26 +- packages/vitnode/scripts/shared/file-utils.ts | 58 +-- .../src/api/adapters/email/nodemailer.ts | 14 +- .../vitnode/src/api/adapters/email/resend.ts | 6 +- .../vitnode/src/api/adapters/sso/discord.ts | 52 +-- .../vitnode/src/api/adapters/sso/facebook.ts | 54 +-- .../vitnode/src/api/adapters/sso/google.ts | 50 +-- packages/vitnode/src/api/config.ts | 42 +-- .../vitnode/src/api/lib/check-plugin-id.ts | 20 +- .../vitnode/src/api/lib/logger-middleware.ts | 22 +- packages/vitnode/src/api/lib/module.ts | 4 +- packages/vitnode/src/api/lib/plugin.ts | 6 +- packages/vitnode/src/api/lib/route.ts | 16 +- .../vitnode/src/api/lib/with-pagination.ts | 36 +- .../src/api/middlewares/captcha.middleware.ts | 40 +- .../src/api/middlewares/global.middleware.ts | 94 ++--- .../middlewares/rate-limiter.middleware.ts | 16 +- packages/vitnode/src/api/models/device.ts | 36 +- packages/vitnode/src/api/models/email.ts | 38 +- packages/vitnode/src/api/models/password.ts | 14 +- .../vitnode/src/api/models/session-admin.ts | 56 +-- packages/vitnode/src/api/models/session.ts | 50 +-- packages/vitnode/src/api/models/sso.ts | 50 +-- packages/vitnode/src/api/models/user.ts | 6 +- .../src/api/models/user/get-user-by-id.ts | 8 +- .../api/models/user/sign-in-with-passwords.ts | 12 +- .../vitnode/src/api/models/user/sign-up.ts | 36 +- .../src/api/modules/admin/admin.module.ts | 12 +- .../modules/admin/debug/debug.admin.module.ts | 8 +- .../modules/admin/debug/routes/logs.route.ts | 36 +- .../api/modules/admin/routes/session.route.ts | 22 +- .../modules/admin/users/routes/list.route.ts | 30 +- .../modules/admin/users/routes/users.route.ts | 32 +- .../modules/admin/users/users.admin.module.ts | 8 +- .../modules/middleware/middleware.module.ts | 8 +- .../src/api/modules/middleware/route.ts | 28 +- .../src/api/modules/users/avatar-color.ts | 2 +- .../users/routes/change-password.route.ts | 40 +- .../users/routes/reset-passowrd.route.ts | 52 +-- .../api/modules/users/routes/session.route.ts | 20 +- .../api/modules/users/routes/sign-in.route.ts | 32 +- .../modules/users/routes/sign-out.route.ts | 24 +- .../api/modules/users/routes/sign-up.route.ts | 40 +- .../api/modules/users/routes/test.route.ts | 24 +- .../users/sso/routes/callback.route.ts | 26 +- .../users/sso/routes/create-url.route.ts | 20 +- .../src/api/modules/users/sso/sso.module.ts | 10 +- .../src/api/modules/users/users.module.ts | 22 +- packages/vitnode/src/api/plugin.ts | 10 +- packages/vitnode/src/app/login/page.tsx | 10 +- .../src/app/login/reset-password/page.tsx | 10 +- .../src/app/login/sso/[providerId]/page.tsx | 2 +- packages/vitnode/src/app/register/page.tsx | 10 +- .../vitnode/src/app_admin/core/debug/page.tsx | 28 +- packages/vitnode/src/app_admin/core/page.tsx | 2 +- .../vitnode/src/app_admin/core/test/page.tsx | 2 +- .../vitnode/src/app_admin/core/users/page.tsx | 24 +- packages/vitnode/src/components/avatar.tsx | 8 +- .../confirm-action-alert-dialog.tsx | 20 +- .../src/components/confirm-action/content.tsx | 14 +- .../vitnode/src/components/date-format.tsx | 18 +- .../vitnode/src/components/form/auto-form.tsx | 50 +-- .../src/components/form/common/desc.tsx | 6 +- .../src/components/form/common/label.tsx | 6 +- .../src/components/form/fields/checkbox.tsx | 12 +- .../components/form/fields/combobox-async.tsx | 50 +-- .../src/components/form/fields/combobox.tsx | 40 +- .../src/components/form/fields/input.tsx | 16 +- .../components/form/fields/radio-group.tsx | 14 +- .../src/components/form/fields/select.tsx | 20 +- .../src/components/form/fields/switch.tsx | 12 +- .../src/components/form/fields/textarea.tsx | 16 +- .../vitnode/src/components/i18n-provider.tsx | 12 +- .../vitnode/src/components/logo-vitnode.tsx | 8 +- .../switchers/langs/language-swietcher.tsx | 20 +- .../switchers/themes/theme-switcher.tsx | 16 +- .../vitnode/src/components/table/content.tsx | 26 +- .../src/components/table/data-table.tsx | 20 +- .../src/components/table/order-table-head.tsx | 32 +- .../src/components/table/pagination.tsx | 52 +-- .../vitnode/src/components/ui/accordion.tsx | 16 +- .../src/components/ui/alert-dialog.tsx | 34 +- packages/vitnode/src/components/ui/alert.tsx | 24 +- packages/vitnode/src/components/ui/badge.tsx | 24 +- .../src/components/ui/button-client.tsx | 32 +- packages/vitnode/src/components/ui/button.tsx | 38 +- packages/vitnode/src/components/ui/card.tsx | 32 +- .../vitnode/src/components/ui/checkbox.tsx | 12 +- .../vitnode/src/components/ui/collapsible.tsx | 4 +- .../vitnode/src/components/ui/command.tsx | 32 +- .../src/components/ui/context-menu.tsx | 26 +- packages/vitnode/src/components/ui/dialog.tsx | 44 +-- packages/vitnode/src/components/ui/drawer.tsx | 32 +- .../src/components/ui/dropdown-menu.tsx | 28 +- packages/vitnode/src/components/ui/form.tsx | 56 +-- .../src/components/ui/header-content.tsx | 4 +- .../vitnode/src/components/ui/hover-card.tsx | 12 +- .../vitnode/src/components/ui/input-otp.tsx | 24 +- packages/vitnode/src/components/ui/input.tsx | 12 +- packages/vitnode/src/components/ui/label.tsx | 10 +- packages/vitnode/src/components/ui/loader.tsx | 10 +- .../vitnode/src/components/ui/menubar.tsx | 34 +- .../src/components/ui/navigation-menu.tsx | 32 +- .../vitnode/src/components/ui/popover.tsx | 12 +- .../vitnode/src/components/ui/progress.tsx | 10 +- .../vitnode/src/components/ui/radio-group.tsx | 14 +- packages/vitnode/src/components/ui/select.tsx | 36 +- .../vitnode/src/components/ui/separator.tsx | 12 +- packages/vitnode/src/components/ui/sheet.tsx | 46 +-- .../vitnode/src/components/ui/sidebar.tsx | 276 +++++++------- .../vitnode/src/components/ui/skeleton.tsx | 6 +- packages/vitnode/src/components/ui/slider.tsx | 14 +- packages/vitnode/src/components/ui/sonner.tsx | 22 +- packages/vitnode/src/components/ui/switch.tsx | 12 +- packages/vitnode/src/components/ui/table.tsx | 36 +- packages/vitnode/src/components/ui/tabs.tsx | 14 +- .../vitnode/src/components/ui/textarea.tsx | 8 +- .../src/components/ui/toggle-group.tsx | 20 +- packages/vitnode/src/components/ui/toggle.tsx | 24 +- .../vitnode/src/components/ui/tooltip.tsx | 10 +- packages/vitnode/src/config.ts | 4 +- packages/vitnode/src/database/admins.ts | 30 +- packages/vitnode/src/database/languages.ts | 18 +- packages/vitnode/src/database/logs.ts | 18 +- packages/vitnode/src/database/moderators.ts | 18 +- packages/vitnode/src/database/roles.ts | 4 +- packages/vitnode/src/database/sessions.ts | 18 +- packages/vitnode/src/database/users.ts | 34 +- packages/vitnode/src/drizzle.config.ts | 30 +- .../vitnode/src/emails/default-template.tsx | 98 ++--- .../vitnode/src/emails/reset-password.tsx | 68 ++-- packages/vitnode/src/emails/ui/button.tsx | 32 +- packages/vitnode/src/emails/ui/card.tsx | 16 +- .../vitnode/src/hooks/use-before-unload.ts | 8 +- packages/vitnode/src/hooks/use-captcha.ts | 66 ++-- packages/vitnode/src/hooks/use-mobile.ts | 6 +- .../vitnode/src/lib/api/get-middleware-api.ts | 12 +- .../src/lib/api/get-session-admin-api.ts | 16 +- .../vitnode/src/lib/api/get-session-api.ts | 12 +- packages/vitnode/src/lib/colors.test.ts | 126 +++---- packages/vitnode/src/lib/colors.ts | 20 +- packages/vitnode/src/lib/config.ts | 6 +- packages/vitnode/src/lib/fetcher-client.ts | 14 +- packages/vitnode/src/lib/fetcher.ts | 26 +- .../cookie-from-string-to-object.test.ts | 58 +-- .../fetcher/cookie-from-string-to-object.ts | 6 +- packages/vitnode/src/lib/fetcher/core.ts | 32 +- .../vitnode/src/lib/fetcher/helpers-server.ts | 8 +- .../vitnode/src/lib/fetcher/helpers.test.ts | 36 +- packages/vitnode/src/lib/fetcher/types.ts | 54 +-- .../vitnode/src/lib/helpers/auto-form.test.ts | 232 ++++++------ packages/vitnode/src/lib/helpers/auto-form.ts | 34 +- packages/vitnode/src/lib/navigation.ts | 8 +- packages/vitnode/src/lib/plugin.ts | 6 +- .../src/lib/special-characters.test.ts | 80 ++-- .../vitnode/src/lib/special-characters.ts | 16 +- packages/vitnode/src/lib/utils.ts | 4 +- packages/vitnode/src/tests/setup.ts | 2 +- .../src/views/admin/layouts/admin-layout.tsx | 28 +- .../views/admin/layouts/sidebar/nav/item.tsx | 40 +- .../views/admin/layouts/sidebar/nav/nav.tsx | 30 +- .../views/admin/layouts/sidebar/sidebar.tsx | 8 +- .../views/admin/layouts/sidebar/sign-out.tsx | 6 +- .../views/admin/layouts/user-bar/client.tsx | 20 +- .../views/admin/layouts/user-bar/user-bar.tsx | 14 +- .../admin/sign-in/sign-in-admin-view.tsx | 10 +- .../core/dashboard/dashboard-admin-view.tsx | 18 +- .../debug/actions/clear-cache/clear-cache.tsx | 32 +- .../debug/actions/clear-cache/mutation-api.ts | 6 +- .../system-logs/actions/more/content.tsx | 56 +-- .../debug/system-logs/actions/more/more.tsx | 30 +- .../debug/system-logs/badges/badge-status.tsx | 8 +- .../system-logs/badges/badge-type-log.tsx | 16 +- .../debug/system-logs/system-logs-view.tsx | 60 +-- .../src/views/admin/views/core/test.tsx | 90 ++--- .../views/core/users/users-admin-view.tsx | 40 +- .../change-password-form/form.tsx | 28 +- .../change-password-form/mutation-api.ts | 18 +- .../change-password-form/use-form.ts | 28 +- .../views/auth/password-reset/form/form.tsx | 52 +-- .../auth/password-reset/form/mutation-api.ts | 14 +- .../auth/password-reset/form/use-form.ts | 24 +- .../password-reset/password-reset-view.tsx | 16 +- .../src/views/auth/sign-in/form/form.tsx | 32 +- .../views/auth/sign-in/form/mutation-api.ts | 32 +- .../src/views/auth/sign-in/form/use-form.ts | 32 +- .../src/views/auth/sign-in/sign-in-view.tsx | 26 +- .../sign-up/components/password-input.tsx | 34 +- .../auth/sign-up/email-confirmation-view.tsx | 14 +- .../src/views/auth/sign-up/form/form.tsx | 58 +-- .../views/auth/sign-up/form/mutation-api.ts | 24 +- .../src/views/auth/sign-up/form/use-form.ts | 58 +-- .../src/views/auth/sign-up/sign-up-view.tsx | 28 +- .../src/views/auth/sign-up/wrapper.tsx | 8 +- .../src/views/auth/sso/buttons/client.tsx | 16 +- .../views/auth/sso/buttons/mutation-api.ts | 16 +- .../views/auth/sso/buttons/sso-buttons.tsx | 12 +- .../auth/sso/callback/callback-sso-view.tsx | 22 +- .../views/auth/sso/callback/client/client.tsx | 36 +- .../auth/sso/callback/client/mutation-api.ts | 20 +- .../vitnode/src/views/error/back-button.tsx | 6 +- .../vitnode/src/views/error/error-view.tsx | 20 +- .../src/views/error/global-error-view.tsx | 12 +- .../vitnode/src/views/layouts/provider.tsx | 22 +- .../vitnode/src/views/layouts/root-layout.tsx | 14 +- .../src/views/layouts/theme/header/header.tsx | 20 +- .../layouts/theme/header/user/auth/auth.tsx | 10 +- .../layouts/theme/header/user/auth/client.tsx | 22 +- .../header/user/auth/log-out-mutation-api.ts | 24 +- .../views/layouts/theme/header/user/user.tsx | 20 +- .../src/views/layouts/theme/layout.tsx | 6 +- packages/vitnode/src/vitnode.config.ts | 36 +- packages/vitnode/tsup.config.ts | 14 +- packages/vitnode/vitest.config.ts | 42 +-- plugins/blog/global.d.ts | 6 +- .../src/api/modules/admin/admin.module.ts | 10 +- .../categories/categories.admin.module.ts | 12 +- .../admin/categories/routes/create.route.ts | 34 +- .../admin/categories/routes/delete.route.ts | 24 +- .../admin/categories/routes/edit.route.ts | 42 +-- .../modules/admin/posts/posts.admin.module.ts | 12 +- .../admin/posts/routes/create.route.ts | 46 +-- .../admin/posts/routes/delete.route.ts | 24 +- .../modules/admin/posts/routes/edit.route.ts | 50 +-- .../modules/categories/categories.module.ts | 10 +- .../modules/categories/routes/get.route.ts | 30 +- .../src/api/modules/categories/test.route.ts | 34 +- .../src/api/modules/posts/posts.module.ts | 8 +- .../src/api/modules/posts/routes/get.route.ts | 32 +- .../src/app_admin/blog/categories/page.tsx | 30 +- .../blog/src/app_admin/blog/posts/page.tsx | 30 +- plugins/blog/src/config.api.ts | 10 +- plugins/blog/src/config.tsx | 14 +- plugins/blog/src/const.ts | 2 +- plugins/blog/src/database/categories.ts | 6 +- plugins/blog/src/database/index.ts | 6 +- plugins/blog/src/database/posts.ts | 6 +- plugins/blog/src/database/relations.ts | 6 +- plugins/blog/src/emails/test-template.tsx | 4 +- .../admin/categories/actions/actions.tsx | 26 +- .../actions/create-edit/create-edit.tsx | 44 +-- .../actions/create-edit/mutation-api.ts | 36 +- .../table/actions/delete/delete-action.tsx | 36 +- .../table/actions/delete/mutation-api.ts | 20 +- .../categories/table/actions/edit-action.tsx | 28 +- .../table/categories-admin-view.tsx | 52 +-- .../src/views/admin/posts/actions/actions.tsx | 26 +- .../posts/actions/create-edit/create-edit.tsx | 76 ++-- .../posts/actions/create-edit/mutation-api.ts | 36 +- .../table/actions/delete/delete-action.tsx | 36 +- .../table/actions/delete/mutation-api.ts | 20 +- .../admin/posts/table/actions/edit-action.tsx | 28 +- .../admin/posts/table/posts-admin-view.tsx | 58 +-- scripts/bump-version.ts | 12 +- scripts/environment.ts | 4 +- scripts/files/file-copy-manager.ts | 68 ++-- scripts/files/file-system.ts | 6 +- scripts/version-manager.ts | 104 +++--- 372 files changed, 4727 insertions(+), 4727 deletions(-) diff --git a/apps/api/drizzle.config.ts b/apps/api/drizzle.config.ts index 0d630172b..789f77082 100644 --- a/apps/api/drizzle.config.ts +++ b/apps/api/drizzle.config.ts @@ -1,11 +1,11 @@ -import { defineVitNodeDrizzleConfig } from '@vitnode/core/drizzle.config'; +import { defineVitNodeDrizzleConfig } from "@vitnode/core/drizzle.config"; -import { POSTGRES_URL, vitNodeApiConfig } from './src/vitnode.api.config'; +import { POSTGRES_URL, vitNodeApiConfig } from "./src/vitnode.api.config"; export default defineVitNodeDrizzleConfig({ vitNodeApiConfig, - out: './migrations/', - dialect: 'postgresql', + out: "./migrations/", + dialect: "postgresql", dbCredentials: { url: POSTGRES_URL, }, diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index fae3dfb34..27b8d96fd 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -1,10 +1,10 @@ -import { serve } from '@hono/node-server'; -import { OpenAPIHono } from '@hono/zod-openapi'; -import { VitNodeAPI } from '@vitnode/core/api/config'; +import { serve } from "@hono/node-server"; +import { OpenAPIHono } from "@hono/zod-openapi"; +import { VitNodeAPI } from "@vitnode/core/api/config"; -import { vitNodeApiConfig } from './vitnode.api.config.js'; +import { vitNodeApiConfig } from "./vitnode.api.config.js"; -const app = new OpenAPIHono().basePath('/api'); +const app = new OpenAPIHono().basePath("/api"); VitNodeAPI({ app, @@ -17,7 +17,7 @@ serve( port: 8080, }, info => { - const initMessage = '\x1b[34m[VitNode]\x1b[0m'; + const initMessage = "\x1b[34m[VitNode]\x1b[0m"; // biome-ignore lint/suspicious/noConsole: console.log( diff --git a/apps/api/src/vitnode.api.config.ts b/apps/api/src/vitnode.api.config.ts index c39543133..4e0d071bd 100644 --- a/apps/api/src/vitnode.api.config.ts +++ b/apps/api/src/vitnode.api.config.ts @@ -1,21 +1,21 @@ -import { NodemailerEmailAdapter } from '@vitnode/core/api/adapters/email/nodemailer'; -import { buildApiConfig } from '@vitnode/core/vitnode.config'; -import { config } from 'dotenv'; -import { drizzle } from 'drizzle-orm/postgres-js'; +import { NodemailerEmailAdapter } from "@vitnode/core/api/adapters/email/nodemailer"; +import { buildApiConfig } from "@vitnode/core/vitnode.config"; +import { config } from "dotenv"; +import { drizzle } from "drizzle-orm/postgres-js"; config({ quiet: true, }); export const POSTGRES_URL = - process.env.POSTGRES_URL || 'postgresql://root:root@localhost:5432/vitnode'; + process.env.POSTGRES_URL || "postgresql://root:root@localhost:5432/vitnode"; export const vitNodeApiConfig = buildApiConfig({ plugins: [], pathToMessages: async path => await import(`./locales/${path}`), dbProvider: drizzle({ connection: POSTGRES_URL, - casing: 'camelCase', + casing: "camelCase", }), email: { adapter: NodemailerEmailAdapter({ @@ -25,12 +25,12 @@ export const vitNodeApiConfig = buildApiConfig({ user: process.env.NOD_EMAILER_USER, }), logo: { - text: 'VitNode Email Test', - src: 'http://localhost:3000/logo_vitnode_dark.png', + text: "VitNode Email Test", + src: "http://localhost:3000/logo_vitnode_dark.png", }, }, metadata: { - title: 'VitNode API', - shortTitle: 'VitNode', + title: "VitNode API", + shortTitle: "VitNode", }, }); diff --git a/apps/docs/drizzle.config.ts b/apps/docs/drizzle.config.ts index 0d630172b..789f77082 100644 --- a/apps/docs/drizzle.config.ts +++ b/apps/docs/drizzle.config.ts @@ -1,11 +1,11 @@ -import { defineVitNodeDrizzleConfig } from '@vitnode/core/drizzle.config'; +import { defineVitNodeDrizzleConfig } from "@vitnode/core/drizzle.config"; -import { POSTGRES_URL, vitNodeApiConfig } from './src/vitnode.api.config'; +import { POSTGRES_URL, vitNodeApiConfig } from "./src/vitnode.api.config"; export default defineVitNodeDrizzleConfig({ vitNodeApiConfig, - out: './migrations/', - dialect: 'postgresql', + out: "./migrations/", + dialect: "postgresql", dbCredentials: { url: POSTGRES_URL, }, diff --git a/apps/docs/e2e/auth.spec.ts b/apps/docs/e2e/auth.spec.ts index 929f0e584..247facef2 100644 --- a/apps/docs/e2e/auth.spec.ts +++ b/apps/docs/e2e/auth.spec.ts @@ -1,4 +1,4 @@ -import { expect, test } from '@playwright/test'; +import { expect, test } from "@playwright/test"; // Generate random user data for test isolation function generateTestUser() { @@ -11,16 +11,16 @@ function generateTestUser() { }; } -test.describe('Authentication', () => { +test.describe("Authentication", () => { const testUser = generateTestUser(); - test('should allow user registration', async ({ page }) => { + test("should allow user registration", async ({ page }) => { // Navigate to registration page - await page.goto('/register'); + await page.goto("/register"); // Wait for the form to be visible await expect( - page.getByRole('heading', { name: /register/i }), + page.getByRole("heading", { name: /register/i }), ).toBeVisible(); // Fill out registration form @@ -36,12 +36,12 @@ test.describe('Authentication', () => { await expect(page).toHaveURL(/\/verify-email|\/dashboard/); }); - test('should allow user login', async ({ page }) => { + test("should allow user login", async ({ page }) => { // Navigate to login page - await page.goto('/login'); + await page.goto("/login"); // Wait for the form to be visible - await expect(page.getByRole('heading', { name: /login/i })).toBeVisible(); + await expect(page.getByRole("heading", { name: /login/i })).toBeVisible(); // Fill out login form with previously registered credentials await page.getByLabel(/email/i).fill(testUser.email); @@ -50,6 +50,6 @@ test.describe('Authentication', () => { // Submit form await page.locator('button[type="submit"]').click(); - await expect(page.getByText('Start Your Journey!')).toBeVisible(); + await expect(page.getByText("Start Your Journey!")).toBeVisible(); }); }); diff --git a/apps/docs/e2e/homepage.spec.ts b/apps/docs/e2e/homepage.spec.ts index 4538e2e5b..97da110b1 100644 --- a/apps/docs/e2e/homepage.spec.ts +++ b/apps/docs/e2e/homepage.spec.ts @@ -1,11 +1,11 @@ -import { expect, test } from '@playwright/test'; +import { expect, test } from "@playwright/test"; const vitNodeTitleRegex = /VitNode/; -test.describe('Homepage', () => { - test('should load successfully', async ({ page }) => { - await page.goto('/'); +test.describe("Homepage", () => { + test("should load successfully", async ({ page }) => { + await page.goto("/"); await expect(page).toHaveTitle(vitNodeTitleRegex); - await expect(page.getByText('Start Your Journey!')).toBeVisible(); + await expect(page.getByText("Start Your Journey!")).toBeVisible(); }); }); diff --git a/apps/docs/global.d.ts b/apps/docs/global.d.ts index 38b3f3b57..2b865e5a5 100644 --- a/apps/docs/global.d.ts +++ b/apps/docs/global.d.ts @@ -1,9 +1,9 @@ /// -import blog from './src/locales/@vitnode/blog/en.json' with { type: 'json' }; -import core from './src/locales/@vitnode/core/en.json' with { type: 'json' }; +import blog from "./src/locales/@vitnode/blog/en.json" with { type: "json" }; +import core from "./src/locales/@vitnode/core/en.json" with { type: "json" }; -declare module 'next-intl' { +declare module "next-intl" { interface AppConfig { Messages: typeof core & typeof blog; } diff --git a/apps/docs/next.config.ts b/apps/docs/next.config.ts index e5326f8eb..1e70b502f 100644 --- a/apps/docs/next.config.ts +++ b/apps/docs/next.config.ts @@ -1,6 +1,6 @@ -import { vitNodeNextConfig } from '@vitnode/core/config/next.config'; -import { createMDX } from 'fumadocs-mdx/next'; -import type { NextConfig } from 'next'; +import { vitNodeNextConfig } from "@vitnode/core/config/next.config"; +import { createMDX } from "fumadocs-mdx/next"; +import type { NextConfig } from "next"; const withMDX = createMDX(); diff --git a/apps/docs/playwright.config.ts b/apps/docs/playwright.config.ts index 81f4c6646..d4a4479a5 100644 --- a/apps/docs/playwright.config.ts +++ b/apps/docs/playwright.config.ts @@ -1,57 +1,57 @@ -import { defineConfig, devices } from '@playwright/test'; +import { defineConfig, devices } from "@playwright/test"; /** * See https://playwright.dev/docs/test-configuration */ export default defineConfig({ - testDir: './e2e', + testDir: "./e2e", fullyParallel: true, forbidOnly: !!process.env.CI, /* Maximum time one test can run for */ timeout: 30 * 1000, /* Fail the build on CI if test failures */ - reporter: process.env.CI ? 'github' : 'html', + reporter: process.env.CI ? "github" : "html", /* Shared settings for all projects */ use: { /* Base URL to use in actions like `await page.goto('/')` */ - baseURL: process.env.TEST_BASE_URL ?? 'http://localhost:3000', + baseURL: process.env.TEST_BASE_URL ?? "http://localhost:3000", /* Collect trace when retrying the failed test */ - trace: 'on-first-retry', + trace: "on-first-retry", /* Take screenshots on failure */ - screenshot: 'only-on-failure', + screenshot: "only-on-failure", /* Record video on failure */ - video: 'on-first-retry', + video: "on-first-retry", }, /* Configure projects for different browsers */ projects: [ { - name: 'chromium', - use: { ...devices['Desktop Chrome'] }, + name: "chromium", + use: { ...devices["Desktop Chrome"] }, }, { - name: 'firefox', - use: { ...devices['Desktop Firefox'] }, + name: "firefox", + use: { ...devices["Desktop Firefox"] }, }, { - name: 'webkit', - use: { ...devices['Desktop Safari'] }, + name: "webkit", + use: { ...devices["Desktop Safari"] }, }, /* Test against mobile viewports. */ { - name: 'mobile-chrome', - use: { ...devices['Pixel 7'] }, + name: "mobile-chrome", + use: { ...devices["Pixel 7"] }, }, { - name: 'mobile-safari', - use: { ...devices['iPhone 14'] }, + name: "mobile-safari", + use: { ...devices["iPhone 14"] }, }, ], /* Run web server for the tests */ webServer: { - command: 'pnpm dev', - url: 'http://localhost:3000', + command: "pnpm dev", + url: "http://localhost:3000", reuseExistingServer: !process.env.CI, - stdout: 'pipe', - stderr: 'pipe', + stdout: "pipe", + stderr: "pipe", }, }); diff --git a/apps/docs/postcss.config.mjs b/apps/docs/postcss.config.mjs index 5d6d8457f..79bcf135d 100644 --- a/apps/docs/postcss.config.mjs +++ b/apps/docs/postcss.config.mjs @@ -1,7 +1,7 @@ /** @type {import('postcss-load-config').Config} */ const config = { plugins: { - '@tailwindcss/postcss': {}, + "@tailwindcss/postcss": {}, }, }; diff --git a/apps/docs/source.config.ts b/apps/docs/source.config.ts index 47c48e97f..6b183f332 100644 --- a/apps/docs/source.config.ts +++ b/apps/docs/source.config.ts @@ -1,7 +1,7 @@ -import { defineConfig, defineDocs } from 'fumadocs-mdx/config'; +import { defineConfig, defineDocs } from "fumadocs-mdx/config"; export const docs = defineDocs({ - dir: 'content/docs', + dir: "content/docs", }); export default defineConfig({ diff --git a/apps/docs/src/app/[locale]/(docs)/docs/[[...slug]]/page.client.tsx b/apps/docs/src/app/[locale]/(docs)/docs/[[...slug]]/page.client.tsx index 02db80128..c33614755 100644 --- a/apps/docs/src/app/[locale]/(docs)/docs/[[...slug]]/page.client.tsx +++ b/apps/docs/src/app/[locale]/(docs)/docs/[[...slug]]/page.client.tsx @@ -1,31 +1,31 @@ -'use client'; +"use client"; // Source: https://github.com/fuma-nama/fumadocs/blob/dev/apps/docs/app/docs/%5B...slug%5D/page.client.tsx -import { cva } from 'class-variance-authority'; -import { buttonVariants } from 'fumadocs-ui/components/ui/button'; +import { cva } from "class-variance-authority"; +import { buttonVariants } from "fumadocs-ui/components/ui/button"; import { Popover, PopoverContent, PopoverTrigger, -} from 'fumadocs-ui/components/ui/popover'; -import { ChevronDown } from 'fumadocs-ui/internal/icons'; -import { cn } from 'fumadocs-ui/utils/cn'; -import { ExternalLinkIcon, MessageCircleIcon } from 'lucide-react'; +} from "fumadocs-ui/components/ui/popover"; +import { ChevronDown } from "fumadocs-ui/internal/icons"; +import { cn } from "fumadocs-ui/utils/cn"; +import { ExternalLinkIcon, MessageCircleIcon } from "lucide-react"; const optionVariants = cva( - 'text-sm p-2 rounded-lg inline-flex items-center gap-2 hover:text-fd-accent-foreground hover:bg-fd-accent [&_svg]:size-4', + "text-sm p-2 rounded-lg inline-flex items-center gap-2 hover:text-fd-accent-foreground hover:bg-fd-accent [&_svg]:size-4", ); export function ViewOptions(props: { githubUrl: string; markdownUrl: string }) { - const markdownUrl = new URL(props.markdownUrl, 'https://vitnode.com/'); + const markdownUrl = new URL(props.markdownUrl, "https://vitnode.com/"); const q = `Read ${markdownUrl}, I want to ask questions about it.`; const claude = `https://claude.ai/new?${new URLSearchParams({ q, })}`; const gpt = `https://chatgpt.com/?${new URLSearchParams({ - hints: 'search', + hints: "search", q, })}`; const t3 = `https://t3.chat/new?${new URLSearchParams({ @@ -37,9 +37,9 @@ export function ViewOptions(props: { githubUrl: string; markdownUrl: string }) { @@ -49,7 +49,7 @@ export function ViewOptions(props: { githubUrl: string; markdownUrl: string }) { {[ { - title: 'Open in GitHub', + title: "Open in GitHub", href: props.githubUrl, icon: ( @@ -59,7 +59,7 @@ export function ViewOptions(props: { githubUrl: string; markdownUrl: string }) { ), }, { - title: 'Open in ChatGPT', + title: "Open in ChatGPT", href: gpt, icon: ( , }, diff --git a/apps/docs/src/app/[locale]/(docs)/docs/[[...slug]]/page.tsx b/apps/docs/src/app/[locale]/(docs)/docs/[[...slug]]/page.tsx index 8184cbe18..52678a7c8 100644 --- a/apps/docs/src/app/[locale]/(docs)/docs/[[...slug]]/page.tsx +++ b/apps/docs/src/app/[locale]/(docs)/docs/[[...slug]]/page.tsx @@ -1,21 +1,21 @@ -import { redirect } from '@vitnode/core/lib/navigation'; -import { getBreadcrumbItems } from 'fumadocs-core/breadcrumb'; -import { Step, Steps } from 'fumadocs-ui/components/steps'; -import defaultMdxComponents from 'fumadocs-ui/mdx'; -import { DocsBody, DocsPage } from 'fumadocs-ui/page'; -import { notFound } from 'next/navigation'; +import { redirect } from "@vitnode/core/lib/navigation"; +import { getBreadcrumbItems } from "fumadocs-core/breadcrumb"; +import { Step, Steps } from "fumadocs-ui/components/steps"; +import defaultMdxComponents from "fumadocs-ui/mdx"; +import { DocsBody, DocsPage } from "fumadocs-ui/page"; +import { notFound } from "next/navigation"; -import { Preview } from '@/components/fumadocs/preview'; -import { source } from '@/lib/source'; +import { Preview } from "@/components/fumadocs/preview"; +import { source } from "@/lib/source"; -import { ViewOptions } from './page.client'; +import { ViewOptions } from "./page.client"; export default async function Page(props: { params: Promise<{ slug?: string[] }>; }) { const params = await props.params; if (!params.slug) { - await redirect('/docs/dev'); + await redirect("/docs/dev"); } const page = source.getPage(params.slug); if (!page) notFound(); @@ -25,7 +25,7 @@ export default async function Page(props: { item.name as string); return { - title: `${page.data.title}${lastItemsBreadcrumb.length > 0 ? ` - ${lastItemsBreadcrumb.join(' - ')}` : ''}`, + title: `${page.data.title}${lastItemsBreadcrumb.length > 0 ? ` - ${lastItemsBreadcrumb.join(" - ")}` : ""}`, description: page.data.description, }; } diff --git a/apps/docs/src/app/[locale]/(docs)/docs/layout.tsx b/apps/docs/src/app/[locale]/(docs)/docs/layout.tsx index 068598349..f85b9512f 100644 --- a/apps/docs/src/app/[locale]/(docs)/docs/layout.tsx +++ b/apps/docs/src/app/[locale]/(docs)/docs/layout.tsx @@ -1,21 +1,21 @@ -import { DocsLayout } from 'fumadocs-ui/layouts/notebook'; -import type { ReactNode } from 'react'; +import { DocsLayout } from "fumadocs-ui/layouts/notebook"; +import type { ReactNode } from "react"; -import { baseOptions } from '@/app/[locale]/(main)/layout.config'; -import { source } from '@/lib/source'; +import { baseOptions } from "@/app/[locale]/(main)/layout.config"; +import { source } from "@/lib/source"; export default function Layout({ children }: { children: ReactNode }) { return ( diff --git a/apps/docs/src/app/[locale]/(docs)/docs/not-found.tsx b/apps/docs/src/app/[locale]/(docs)/docs/not-found.tsx index 1c4db2aeb..5942f5eca 100644 --- a/apps/docs/src/app/[locale]/(docs)/docs/not-found.tsx +++ b/apps/docs/src/app/[locale]/(docs)/docs/not-found.tsx @@ -1,4 +1,4 @@ -import { ErrorView } from '@vitnode/core/views/error/error-view'; +import { ErrorView } from "@vitnode/core/views/error/error-view"; export default function NotFoundPage() { return ; diff --git a/apps/docs/src/app/[locale]/(main)/(home)/page.tsx b/apps/docs/src/app/[locale]/(main)/(home)/page.tsx index 07e93ec52..5102ea8d0 100644 --- a/apps/docs/src/app/[locale]/(main)/(home)/page.tsx +++ b/apps/docs/src/app/[locale]/(main)/(home)/page.tsx @@ -1,18 +1,18 @@ -import { buttonVariants } from '@vitnode/core/components/ui/button'; -import { cn } from '@vitnode/core/lib/utils'; -import Link from 'fumadocs-core/link'; -import { ChevronRight } from 'lucide-react'; -import type { Metadata } from 'next'; +import { buttonVariants } from "@vitnode/core/components/ui/button"; +import { cn } from "@vitnode/core/lib/utils"; +import Link from "fumadocs-core/link"; +import { ChevronRight } from "lucide-react"; +import type { Metadata } from "next"; -import { AnimatedBeamHome } from '../../../../components/animated-beam/animated-beam-home'; -import { AdminSection } from './sections/admin/admin'; -import { CallToActionSection } from './sections/call-to-action'; -import { PoweringBySection } from './sections/powering-by/powering-by'; +import { AnimatedBeamHome } from "../../../../components/animated-beam/animated-beam-home"; +import { AdminSection } from "./sections/admin/admin"; +import { CallToActionSection } from "./sections/call-to-action"; +import { PoweringBySection } from "./sections/powering-by/powering-by"; export const metadata: Metadata = { - title: 'VitNode: Extendable Framework for Building Apps', + title: "VitNode: Extendable Framework for Building Apps", description: - 'Build with Next.js and Hono.js. It provides a structured, plugin-based architecture with Admin Control Panel that makes development faster and less complex.', + "Build with Next.js and Hono.js. It provides a structured, plugin-based architecture with Admin Control Panel that makes development faster and less complex.", }; export default function HomePage() { @@ -34,7 +34,7 @@ export default function HomePage() { { return ( diff --git a/apps/docs/src/app/[locale]/(main)/(home)/sections/call-to-action.tsx b/apps/docs/src/app/[locale]/(main)/(home)/sections/call-to-action.tsx index 56dc35104..f7a4c6ae3 100644 --- a/apps/docs/src/app/[locale]/(main)/(home)/sections/call-to-action.tsx +++ b/apps/docs/src/app/[locale]/(main)/(home)/sections/call-to-action.tsx @@ -1,7 +1,7 @@ -import { Card } from '@vitnode/core/components/ui/card'; -import { cn } from '@vitnode/core/lib/utils'; +import { Card } from "@vitnode/core/components/ui/card"; +import { cn } from "@vitnode/core/lib/utils"; -import { CodeBlock } from '../../../../../components/fumadocs/code-block'; +import { CodeBlock } from "../../../../../components/fumadocs/code-block"; export const CallToActionSection = () => { return ( @@ -20,7 +20,7 @@ export const CallToActionSection = () => { lang="bash" wrapper={{ className: cn( - 'bg-background m-0 w-full sm:w-[calc(100%_-_10rem)]', + "bg-background m-0 w-full sm:w-[calc(100%_-_10rem)]", ), }} /> diff --git a/apps/docs/src/app/[locale]/(main)/(home)/sections/powering-by/powering-by.tsx b/apps/docs/src/app/[locale]/(main)/(home)/sections/powering-by/powering-by.tsx index 9d8c145a9..31647306f 100644 --- a/apps/docs/src/app/[locale]/(main)/(home)/sections/powering-by/powering-by.tsx +++ b/apps/docs/src/app/[locale]/(main)/(home)/sections/powering-by/powering-by.tsx @@ -1,14 +1,14 @@ -import { Link } from '@vitnode/core/lib/navigation'; +import { Link } from "@vitnode/core/lib/navigation"; -import { InfiniteSlider } from '@/components/infinite-slider'; +import { InfiniteSlider } from "@/components/infinite-slider"; -import { DrizzleORMLogo } from './logos/drizzleorm'; -import { HonoJSLogo } from './logos/honojs'; -import { NextIntlLogo } from './logos/next-intl'; -import { NextJSLogo } from './logos/nextjs'; -import { PostgreSQLLogo } from './logos/postgresql'; -import { TailwindCSSLogo } from './logos/tailwindcss'; -import { TurboRepoLogo } from './logos/turborepo'; +import { DrizzleORMLogo } from "./logos/drizzleorm"; +import { HonoJSLogo } from "./logos/honojs"; +import { NextIntlLogo } from "./logos/next-intl"; +import { NextJSLogo } from "./logos/nextjs"; +import { PostgreSQLLogo } from "./logos/postgresql"; +import { TailwindCSSLogo } from "./logos/tailwindcss"; +import { TurboRepoLogo } from "./logos/turborepo"; export const PoweringBySection = () => { return ( diff --git a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/page.tsx b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/page.tsx index 37a0b89c1..5c8eabfa7 100644 --- a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/page.tsx +++ b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/page.tsx @@ -1,6 +1,6 @@ -import { SignInView } from '@vitnode/core/views/auth/sign-in/sign-in-view'; -import type { Metadata } from 'next/dist/types'; -import { getTranslations } from 'next-intl/server'; +import { SignInView } from "@vitnode/core/views/auth/sign-in/sign-in-view"; +import type { Metadata } from "next/dist/types"; +import { getTranslations } from "next-intl/server"; export const generateMetadata = async ({ params, @@ -8,10 +8,10 @@ export const generateMetadata = async ({ params: Promise<{ locale: string }>; }): Promise => { const { locale } = await params; - const t = await getTranslations({ locale, namespace: 'core.global' }); + const t = await getTranslations({ locale, namespace: "core.global" }); return { - title: t('login'), + title: t("login"), }; }; diff --git a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/reset-password/page.tsx b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/reset-password/page.tsx index 6c8acefe7..c96f6fc61 100644 --- a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/reset-password/page.tsx +++ b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/reset-password/page.tsx @@ -1,6 +1,6 @@ -import { PasswordResetView } from '@vitnode/core/views/auth/password-reset/password-reset-view'; -import type { Metadata } from 'next/dist/types'; -import { getTranslations } from 'next-intl/server'; +import { PasswordResetView } from "@vitnode/core/views/auth/password-reset/password-reset-view"; +import type { Metadata } from "next/dist/types"; +import { getTranslations } from "next-intl/server"; export const generateMetadata = async ({ params, @@ -10,11 +10,11 @@ export const generateMetadata = async ({ const { locale } = await params; const t = await getTranslations({ locale, - namespace: 'core.auth.reset_password', + namespace: "core.auth.reset_password", }); return { - title: t('title'), + title: t("title"), }; }; diff --git a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/sso/[providerId]/page.tsx b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/sso/[providerId]/page.tsx index 31b3fd3f1..e52b56cbb 100644 --- a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/sso/[providerId]/page.tsx +++ b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/login/sso/[providerId]/page.tsx @@ -1,4 +1,4 @@ -import { CallbackSSOView } from '@vitnode/core/views/auth/sso/callback/callback-sso-view'; +import { CallbackSSOView } from "@vitnode/core/views/auth/sso/callback/callback-sso-view"; export default async function Page({ params, diff --git a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/register/page.tsx b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/register/page.tsx index f6cc0bfd7..5a3ad9525 100644 --- a/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/register/page.tsx +++ b/apps/docs/src/app/[locale]/(main)/(plugins)/(vitnode-core)/register/page.tsx @@ -1,6 +1,6 @@ -import { SignUpView } from '@vitnode/core/views/auth/sign-up/sign-up-view'; -import type { Metadata } from 'next/dist/types'; -import { getTranslations } from 'next-intl/server'; +import { SignUpView } from "@vitnode/core/views/auth/sign-up/sign-up-view"; +import type { Metadata } from "next/dist/types"; +import { getTranslations } from "next-intl/server"; export const generateMetadata = async ({ params, @@ -8,10 +8,10 @@ export const generateMetadata = async ({ params: Promise<{ locale: string }>; }): Promise => { const { locale } = await params; - const t = await getTranslations({ locale, namespace: 'core.global' }); + const t = await getTranslations({ locale, namespace: "core.global" }); return { - title: t('register'), + title: t("register"), }; }; diff --git a/apps/docs/src/app/[locale]/(main)/[...rest]/page.tsx b/apps/docs/src/app/[locale]/(main)/[...rest]/page.tsx index 560ede6f4..64011c694 100644 --- a/apps/docs/src/app/[locale]/(main)/[...rest]/page.tsx +++ b/apps/docs/src/app/[locale]/(main)/[...rest]/page.tsx @@ -1,4 +1,4 @@ -import { notFound } from 'next/navigation'; +import { notFound } from "next/navigation"; export default function RestPage() { notFound(); diff --git a/apps/docs/src/app/[locale]/(main)/layout.client.tsx b/apps/docs/src/app/[locale]/(main)/layout.client.tsx index 711ebecd9..7ff71dd9f 100644 --- a/apps/docs/src/app/[locale]/(main)/layout.client.tsx +++ b/apps/docs/src/app/[locale]/(main)/layout.client.tsx @@ -1,7 +1,7 @@ -'use client'; +"use client"; -import { cn } from 'fumadocs-ui/utils/cn'; -import { useParams } from 'next/navigation'; +import { cn } from "fumadocs-ui/utils/cn"; +import { useParams } from "next/navigation"; export function useMode(): string | undefined { const { slug } = useParams(); diff --git a/apps/docs/src/app/[locale]/(main)/layout.config.tsx b/apps/docs/src/app/[locale]/(main)/layout.config.tsx index ceda38f48..703b9cafc 100644 --- a/apps/docs/src/app/[locale]/(main)/layout.config.tsx +++ b/apps/docs/src/app/[locale]/(main)/layout.config.tsx @@ -1,6 +1,6 @@ -import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared'; +import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared"; -import { LogoVitNode } from '@/components/logo-vitnode'; +import { LogoVitNode } from "@/components/logo-vitnode"; /** * Shared layout configurations @@ -11,7 +11,7 @@ import { LogoVitNode } from '@/components/logo-vitnode'; */ // TODO: Remove this export const baseOptions: BaseLayoutProps = { - githubUrl: 'https://github.com/VitNode/vitnode', + githubUrl: "https://github.com/VitNode/vitnode", nav: { title: ( <> @@ -21,9 +21,9 @@ export const baseOptions: BaseLayoutProps = { }, links: [ { - text: 'Documentation', - url: '/docs', - active: 'nested-url', + text: "Documentation", + url: "/docs", + active: "nested-url", }, ], }; diff --git a/apps/docs/src/app/[locale]/(main)/layout.tsx b/apps/docs/src/app/[locale]/(main)/layout.tsx index c8fdf9759..d16c65026 100644 --- a/apps/docs/src/app/[locale]/(main)/layout.tsx +++ b/apps/docs/src/app/[locale]/(main)/layout.tsx @@ -8,10 +8,10 @@ // return {children}; // } -import { LogoVitNode } from '@vitnode/core/components/logo-vitnode'; -import { ThemeLayout } from '@vitnode/core/views/layouts/theme/layout'; +import { LogoVitNode } from "@vitnode/core/components/logo-vitnode"; +import { ThemeLayout } from "@vitnode/core/views/layouts/theme/layout"; -import { vitNodeConfig } from '../../../vitnode.config'; +import { vitNodeConfig } from "../../../vitnode.config"; export default function Layout({ children }: { children: React.ReactNode }) { return ( diff --git a/apps/docs/src/app/[locale]/(main)/not-found.tsx b/apps/docs/src/app/[locale]/(main)/not-found.tsx index 1c4db2aeb..5942f5eca 100644 --- a/apps/docs/src/app/[locale]/(main)/not-found.tsx +++ b/apps/docs/src/app/[locale]/(main)/not-found.tsx @@ -1,4 +1,4 @@ -import { ErrorView } from '@vitnode/core/views/error/error-view'; +import { ErrorView } from "@vitnode/core/views/error/error-view"; export default function NotFoundPage() { return ; diff --git a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-blog)/blog/categories/page.tsx b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-blog)/blog/categories/page.tsx index b14339bf9..2331c3599 100644 --- a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-blog)/blog/categories/page.tsx +++ b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-blog)/blog/categories/page.tsx @@ -1,26 +1,26 @@ -import { ActionsCategoriesAdmin } from '@vitnode/blog/views/admin/categories/actions/actions'; +import { ActionsCategoriesAdmin } from "@vitnode/blog/views/admin/categories/actions/actions"; -import { I18nProvider } from '@vitnode/core/components/i18n-provider'; -import { DataTableSkeleton } from '@vitnode/core/components/table/data-table'; -import { HeaderContent } from '@vitnode/core/components/ui/header-content'; -import type { Metadata } from 'next'; -import dynamic from 'next/dynamic'; -import { getTranslations } from 'next-intl/server'; -import React from 'react'; +import { I18nProvider } from "@vitnode/core/components/i18n-provider"; +import { DataTableSkeleton } from "@vitnode/core/components/table/data-table"; +import { HeaderContent } from "@vitnode/core/components/ui/header-content"; +import type { Metadata } from "next"; +import dynamic from "next/dynamic"; +import { getTranslations } from "next-intl/server"; +import React from "react"; const CategoriesAdminView = dynamic(async () => import( - '@vitnode/blog/views/admin/categories/table/categories-admin-view' + "@vitnode/blog/views/admin/categories/table/categories-admin-view" ).then(mod => ({ default: mod.CategoriesAdminView, })), ); export const generateMetadata = async (): Promise => { - const t = await getTranslations('@vitnode/blog.admin.nav'); + const t = await getTranslations("@vitnode/blog.admin.nav"); return { - title: t('categories'), + title: t("categories"), }; }; @@ -28,14 +28,14 @@ export default async function CategoriesPage( params: React.ComponentProps, ) { const [t, tNav] = await Promise.all([ - getTranslations('@vitnode/blog.admin.categories'), - getTranslations('@vitnode/blog.admin.nav'), + getTranslations("@vitnode/blog.admin.categories"), + getTranslations("@vitnode/blog.admin.nav"), ]); return ( - +
- + diff --git a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-blog)/blog/posts/page.tsx b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-blog)/blog/posts/page.tsx index 152bf2908..5dbf8a8b3 100644 --- a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-blog)/blog/posts/page.tsx +++ b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-blog)/blog/posts/page.tsx @@ -1,15 +1,15 @@ -import { ActionsPostsAdmin } from '@vitnode/blog/views/admin/posts/actions/actions'; +import { ActionsPostsAdmin } from "@vitnode/blog/views/admin/posts/actions/actions"; -import { I18nProvider } from '@vitnode/core/components/i18n-provider'; -import { DataTableSkeleton } from '@vitnode/core/components/table/data-table'; -import { HeaderContent } from '@vitnode/core/components/ui/header-content'; -import type { Metadata } from 'next'; -import dynamic from 'next/dynamic'; -import { getTranslations } from 'next-intl/server'; -import React from 'react'; +import { I18nProvider } from "@vitnode/core/components/i18n-provider"; +import { DataTableSkeleton } from "@vitnode/core/components/table/data-table"; +import { HeaderContent } from "@vitnode/core/components/ui/header-content"; +import type { Metadata } from "next"; +import dynamic from "next/dynamic"; +import { getTranslations } from "next-intl/server"; +import React from "react"; const PostsAdminView = dynamic(async () => - import('@vitnode/blog/views/admin/posts/table/posts-admin-view').then( + import("@vitnode/blog/views/admin/posts/table/posts-admin-view").then( mod => ({ default: mod.PostsAdminView, }), @@ -17,10 +17,10 @@ const PostsAdminView = dynamic(async () => ); export const generateMetadata = async (): Promise => { - const t = await getTranslations('@vitnode/blog.admin.nav'); + const t = await getTranslations("@vitnode/blog.admin.nav"); return { - title: t('posts'), + title: t("posts"), }; }; @@ -28,14 +28,14 @@ export default async function PostsPage( params: React.ComponentProps, ) { const [t, tNav] = await Promise.all([ - getTranslations('@vitnode/blog.admin.posts'), - getTranslations('@vitnode/blog.admin.nav'), + getTranslations("@vitnode/blog.admin.posts"), + getTranslations("@vitnode/blog.admin.nav"), ]); return ( - +
- + diff --git a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/debug/page.tsx b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/debug/page.tsx index edaac0aab..3dd64b364 100644 --- a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/debug/page.tsx +++ b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/debug/page.tsx @@ -1,41 +1,41 @@ -import { I18nProvider } from '@vitnode/core/components/i18n-provider'; -import { DataTableSkeleton } from '@vitnode/core/components/table/data-table'; -import { HeaderContent } from '@vitnode/core/components/ui/header-content'; -import { ClearCacheAction } from '@vitnode/core/views/admin/views/core/debug/actions/clear-cache/clear-cache'; -import dynamic from 'next/dynamic'; -import { getTranslations } from 'next-intl/server'; -import React from 'react'; +import { I18nProvider } from "@vitnode/core/components/i18n-provider"; +import { DataTableSkeleton } from "@vitnode/core/components/table/data-table"; +import { HeaderContent } from "@vitnode/core/components/ui/header-content"; +import { ClearCacheAction } from "@vitnode/core/views/admin/views/core/debug/actions/clear-cache/clear-cache"; +import dynamic from "next/dynamic"; +import { getTranslations } from "next-intl/server"; +import React from "react"; const SystemLogsView = dynamic(async () => import( - '@vitnode/core/views/admin/views/core/debug/system-logs/system-logs-view' + "@vitnode/core/views/admin/views/core/debug/system-logs/system-logs-view" ).then(module => ({ default: module.SystemLogsView, })), ); export const generateMetadata = async () => { - const t = await getTranslations('admin.debug'); + const t = await getTranslations("admin.debug"); return { - title: t('title'), - description: t('desc'), + title: t("title"), + description: t("desc"), }; }; export default async function Page( props: React.ComponentProps, ) { - const t = await getTranslations('admin.debug'); + const t = await getTranslations("admin.debug"); return (
- + - + }> diff --git a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/page.tsx b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/page.tsx index dd1ff7e3d..54dd37081 100644 --- a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/page.tsx +++ b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/page.tsx @@ -1,4 +1,4 @@ -import { DashboardAdminView } from '@vitnode/core/views/admin/views/core/dashboard/dashboard-admin-view'; +import { DashboardAdminView } from "@vitnode/core/views/admin/views/core/dashboard/dashboard-admin-view"; export default function Page() { return ; diff --git a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/test/page.tsx b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/test/page.tsx index f0f142aca..00abbaee8 100644 --- a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/test/page.tsx +++ b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/test/page.tsx @@ -1,4 +1,4 @@ -import { TestView } from '@vitnode/core/views/admin/views/core/test'; +import { TestView } from "@vitnode/core/views/admin/views/core/test"; export default function Page() { return ; diff --git a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/users/page.tsx b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/users/page.tsx index 9008d1034..f8d0a61c3 100644 --- a/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/users/page.tsx +++ b/apps/docs/src/app/[locale]/admin/(auth)/(plugins)/(vitnode-core)/core/users/page.tsx @@ -1,12 +1,12 @@ -import { DataTableSkeleton } from '@vitnode/core/components/table/data-table'; -import { HeaderContent } from '@vitnode/core/components/ui/header-content'; -import type { Metadata } from 'next/dist/types'; -import dynamic from 'next/dynamic'; -import { getTranslations } from 'next-intl/server'; -import React from 'react'; +import { DataTableSkeleton } from "@vitnode/core/components/table/data-table"; +import { HeaderContent } from "@vitnode/core/components/ui/header-content"; +import type { Metadata } from "next/dist/types"; +import dynamic from "next/dynamic"; +import { getTranslations } from "next-intl/server"; +import React from "react"; const UsersAdminView = dynamic(async () => - import('@vitnode/core/views/admin/views/core/users/users-admin-view').then( + import("@vitnode/core/views/admin/views/core/users/users-admin-view").then( module => ({ default: module.UsersAdminView, }), @@ -14,10 +14,10 @@ const UsersAdminView = dynamic(async () => ); export const generateMetadata = async (): Promise => { - const t = await getTranslations('admin.global.nav.users'); + const t = await getTranslations("admin.global.nav.users"); return { - title: t('list'), + title: t("list"), }; }; @@ -25,13 +25,13 @@ export default async function Page( props: React.ComponentProps, ) { const [t, tNav] = await Promise.all([ - getTranslations('admin.user.list'), - getTranslations('admin.global.nav.users'), + getTranslations("admin.user.list"), + getTranslations("admin.global.nav.users"), ]); return (
- + }> diff --git a/apps/docs/src/app/[locale]/admin/(auth)/layout.tsx b/apps/docs/src/app/[locale]/admin/(auth)/layout.tsx index 01f8ac1c5..83191e110 100644 --- a/apps/docs/src/app/[locale]/admin/(auth)/layout.tsx +++ b/apps/docs/src/app/[locale]/admin/(auth)/layout.tsx @@ -1,9 +1,9 @@ import { AdminLayout, type AdminLayoutProps, -} from '@vitnode/core/views/admin/layouts/admin-layout'; +} from "@vitnode/core/views/admin/layouts/admin-layout"; -import { vitNodeConfig } from '@/vitnode.config'; +import { vitNodeConfig } from "@/vitnode.config"; export default function Layout(props: AdminLayoutProps) { return ; diff --git a/apps/docs/src/app/[locale]/admin/page.tsx b/apps/docs/src/app/[locale]/admin/page.tsx index 9238ffa31..5e82810dd 100644 --- a/apps/docs/src/app/[locale]/admin/page.tsx +++ b/apps/docs/src/app/[locale]/admin/page.tsx @@ -1,4 +1,4 @@ -import { SignInAdminView } from '@vitnode/core/views/admin/sign-in/sign-in-admin-view'; +import { SignInAdminView } from "@vitnode/core/views/admin/sign-in/sign-in-admin-view"; export default function Page() { return ; diff --git a/apps/docs/src/app/[locale]/layout.tsx b/apps/docs/src/app/[locale]/layout.tsx index 548c95479..f55a6a02d 100644 --- a/apps/docs/src/app/[locale]/layout.tsx +++ b/apps/docs/src/app/[locale]/layout.tsx @@ -1,25 +1,25 @@ -import type { RootLayoutProps } from '@vitnode/core/views/layouts/root-layout'; +import type { RootLayoutProps } from "@vitnode/core/views/layouts/root-layout"; import { generateMetadataRootLayout, RootLayout, -} from '@vitnode/core/views/layouts/root-layout'; -import { RootProvider } from 'fumadocs-ui/provider'; -import type { Metadata } from 'next'; -import { Geist, Geist_Mono } from 'next/font/google'; +} from "@vitnode/core/views/layouts/root-layout"; +import { RootProvider } from "fumadocs-ui/provider"; +import type { Metadata } from "next"; +import { Geist, Geist_Mono } from "next/font/google"; -import { vitNodeConfig } from '@/vitnode.config'; +import { vitNodeConfig } from "@/vitnode.config"; -import SearchDialogFumadocs from '../../components/fumadocs/search-dialog'; -import { Body } from './(main)/layout.client'; +import SearchDialogFumadocs from "../../components/fumadocs/search-dialog"; +import { Body } from "./(main)/layout.client"; const geistSans = Geist({ - variable: '--font-geist-sans', - subsets: ['latin'], + variable: "--font-geist-sans", + subsets: ["latin"], }); const geistMono = Geist_Mono({ - variable: '--font-geist-mono', - subsets: ['latin'], + variable: "--font-geist-mono", + subsets: ["latin"], }); export const generateMetadata = (): Metadata => diff --git a/apps/docs/src/app/api/[...route]/route.ts b/apps/docs/src/app/api/[...route]/route.ts index 76af11c6d..c71850481 100644 --- a/apps/docs/src/app/api/[...route]/route.ts +++ b/apps/docs/src/app/api/[...route]/route.ts @@ -1,10 +1,10 @@ -import { OpenAPIHono } from '@hono/zod-openapi'; -import { VitNodeAPI } from '@vitnode/core/api/config'; -import { handle } from 'hono/vercel'; +import { OpenAPIHono } from "@hono/zod-openapi"; +import { VitNodeAPI } from "@vitnode/core/api/config"; +import { handle } from "hono/vercel"; -import { vitNodeApiConfig } from '@/vitnode.api.config'; +import { vitNodeApiConfig } from "@/vitnode.api.config"; -const app = new OpenAPIHono().basePath('/api'); +const app = new OpenAPIHono().basePath("/api"); VitNodeAPI({ app, vitNodeApiConfig, diff --git a/apps/docs/src/app/api/search/route.ts b/apps/docs/src/app/api/search/route.ts index 9e0c44333..84ede8477 100644 --- a/apps/docs/src/app/api/search/route.ts +++ b/apps/docs/src/app/api/search/route.ts @@ -1,5 +1,5 @@ -import { createFromSource } from 'fumadocs-core/search/server'; +import { createFromSource } from "fumadocs-core/search/server"; -import { source } from '@/lib/source'; +import { source } from "@/lib/source"; export const { GET } = createFromSource(source); diff --git a/apps/docs/src/app/global-error.tsx b/apps/docs/src/app/global-error.tsx index b78f0b2c0..ef398858a 100644 --- a/apps/docs/src/app/global-error.tsx +++ b/apps/docs/src/app/global-error.tsx @@ -1,18 +1,18 @@ -'use client'; +"use client"; -import { GlobalErrorView } from '@vitnode/core/views/error/global-error-view'; -import { Geist, Geist_Mono } from 'next/font/google'; +import { GlobalErrorView } from "@vitnode/core/views/error/global-error-view"; +import { Geist, Geist_Mono } from "next/font/google"; -import './global.css'; +import "./global.css"; const geistSans = Geist({ - variable: '--font-geist-sans', - subsets: ['latin'], + variable: "--font-geist-sans", + subsets: ["latin"], }); const geistMono = Geist_Mono({ - variable: '--font-geist-mono', - subsets: ['latin'], + variable: "--font-geist-mono", + subsets: ["latin"], }); export default function GlobalError() { diff --git a/apps/docs/src/app/layout.tsx b/apps/docs/src/app/layout.tsx index ca8ce53fb..918bea878 100644 --- a/apps/docs/src/app/layout.tsx +++ b/apps/docs/src/app/layout.tsx @@ -1,4 +1,4 @@ -import './global.css'; +import "./global.css"; export default function RootLayout({ children, diff --git a/apps/docs/src/components/animated-beam/animated-beam-home.tsx b/apps/docs/src/components/animated-beam/animated-beam-home.tsx index a0bb65aa0..7463ae8a3 100644 --- a/apps/docs/src/components/animated-beam/animated-beam-home.tsx +++ b/apps/docs/src/components/animated-beam/animated-beam-home.tsx @@ -1,13 +1,13 @@ -'use client'; +"use client"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, -} from '@vitnode/core/components/ui/tooltip'; -import { Link } from '@vitnode/core/lib/navigation'; -import { cn } from '@vitnode/core/lib/utils'; +} from "@vitnode/core/components/ui/tooltip"; +import { Link } from "@vitnode/core/lib/navigation"; +import { cn } from "@vitnode/core/lib/utils"; import { AtSign, Database, @@ -17,12 +17,12 @@ import { ShieldCheck, Sparkle, Users, -} from 'lucide-react'; -import type React from 'react'; -import { useRef } from 'react'; +} from "lucide-react"; +import type React from "react"; +import { useRef } from "react"; -import { LogoVitNode } from '../logo-vitnode'; -import { AnimatedBeam } from './animated-beam'; +import { LogoVitNode } from "../logo-vitnode"; +import { AnimatedBeam } from "./animated-beam"; const Circle = ({ className, @@ -32,7 +32,7 @@ const Circle = ({ tooltip?: string; }) => { const classNameLink = cn( - 'bg-card hover:bg-accent hover:text-accent-foreground focus-visible:border-ring focus-visible:ring-ring/50 z-10 flex size-12 items-center justify-center rounded-md border p-3 transition-all focus-visible:ring-[3px]', + "bg-card hover:bg-accent hover:text-accent-foreground focus-visible:border-ring focus-visible:ring-ring/50 z-10 flex size-12 items-center justify-center rounded-md border p-3 transition-all focus-visible:ring-[3px]", className, ); @@ -53,7 +53,7 @@ const Circle = ({ ); }; -Circle.displayName = 'Circle'; +Circle.displayName = "Circle"; export function AnimatedBeamHome() { const containerRef = useRef(null); diff --git a/apps/docs/src/components/animated-beam/animated-beam.tsx b/apps/docs/src/components/animated-beam/animated-beam.tsx index bca0dc7de..0eb84fad9 100644 --- a/apps/docs/src/components/animated-beam/animated-beam.tsx +++ b/apps/docs/src/components/animated-beam/animated-beam.tsx @@ -1,8 +1,8 @@ -'use client'; +"use client"; -import { cn } from '@vitnode/core/lib/utils'; -import { motion } from 'motion/react'; -import { type RefObject, useEffect, useId, useState } from 'react'; +import { cn } from "@vitnode/core/lib/utils"; +import { motion } from "motion/react"; +import { type RefObject, useEffect, useId, useState } from "react"; export const AnimatedBeam = ({ className, @@ -13,11 +13,11 @@ export const AnimatedBeam = ({ reverse = false, // Include the reverse prop duration = Math.random() * 3 + 4, delay = 0, - pathColor = 'gray', + pathColor = "gray", pathWidth = 2, pathOpacity = 0.2, - gradientStartColor = '#325fbd', - gradientStopColor = '#363895', + gradientStartColor = "#325fbd", + gradientStopColor = "#363895", startXOffset = 0, startYOffset = 0, endXOffset = 0, @@ -42,22 +42,22 @@ export const AnimatedBeam = ({ toRef: RefObject; }) => { const id = useId(); - const [pathD, setPathD] = useState(''); + const [pathD, setPathD] = useState(""); const [svgDimensions, setSvgDimensions] = useState({ width: 0, height: 0 }); // Calculate the gradient coordinates based on the reverse prop const gradientCoordinates = reverse ? { - x1: ['90%', '-10%'], - x2: ['100%', '0%'], - y1: ['0%', '0%'], - y2: ['0%', '0%'], + x1: ["90%", "-10%"], + x2: ["100%", "0%"], + y1: ["0%", "0%"], + y2: ["0%", "0%"], } : { - x1: ['10%', '110%'], - x2: ['0%', '100%'], - y1: ['0%', '0%'], - y2: ['0%', '0%'], + x1: ["10%", "110%"], + x2: ["0%", "100%"], + y1: ["0%", "0%"], + y2: ["0%", "0%"], }; useEffect(() => { @@ -122,7 +122,7 @@ export const AnimatedBeam = ({ return ( - +
); }; diff --git a/apps/docs/src/components/fumadocs/preview.tsx b/apps/docs/src/components/fumadocs/preview.tsx index 6f46e121d..bc2b3a462 100644 --- a/apps/docs/src/components/fumadocs/preview.tsx +++ b/apps/docs/src/components/fumadocs/preview.tsx @@ -1,7 +1,7 @@ -import { Loader } from '@vitnode/core/components/ui/loader'; -import { cn } from '@vitnode/core/lib/utils'; -import dynamic from 'next/dynamic'; -import React from 'react'; +import { Loader } from "@vitnode/core/components/ui/loader"; +import { cn } from "@vitnode/core/lib/utils"; +import dynamic from "next/dynamic"; +import React from "react"; export const Preview = ({ name, @@ -27,7 +27,7 @@ export const Preview = ({ return (
diff --git a/apps/docs/src/components/fumadocs/search-dialog.tsx b/apps/docs/src/components/fumadocs/search-dialog.tsx index 6186fd789..6978afadc 100644 --- a/apps/docs/src/components/fumadocs/search-dialog.tsx +++ b/apps/docs/src/components/fumadocs/search-dialog.tsx @@ -1,6 +1,6 @@ -'use client'; +"use client"; -import { useDocsSearch } from 'fumadocs-core/search/client'; +import { useDocsSearch } from "fumadocs-core/search/client"; import { SearchDialog, SearchDialogClose, @@ -11,11 +11,11 @@ import { SearchDialogList, SearchDialogOverlay, type SharedProps, -} from 'fumadocs-ui/components/dialog/search'; +} from "fumadocs-ui/components/dialog/search"; export default function SearchDialogFumadocs(props: SharedProps) { const { search, setSearch, query } = useDocsSearch({ - type: 'fetch', + type: "fetch", }); return ( @@ -32,7 +32,7 @@ export default function SearchDialogFumadocs(props: SharedProps) { - + ); diff --git a/apps/docs/src/components/infinite-slider.tsx b/apps/docs/src/components/infinite-slider.tsx index 7ac54177e..7b3ced940 100644 --- a/apps/docs/src/components/infinite-slider.tsx +++ b/apps/docs/src/components/infinite-slider.tsx @@ -1,19 +1,19 @@ -'use client'; +"use client"; -import { cn } from '@vitnode/core/lib/utils'; +import { cn } from "@vitnode/core/lib/utils"; import { type AnimationPlaybackControlsWithThen, animate, motion, useMotionValue, -} from 'motion/react'; -import React from 'react'; -import { useMeasure } from 'react-use'; +} from "motion/react"; +import React from "react"; +import { useMeasure } from "react-use"; export interface InfiniteSliderProps { children: React.ReactNode; className?: string; - direction?: 'horizontal' | 'vertical'; + direction?: "horizontal" | "vertical"; gap?: number; reverse?: boolean; speed?: number; @@ -25,7 +25,7 @@ export function InfiniteSlider({ gap = 16, speed = 100, speedOnHover, - direction = 'horizontal', + direction = "horizontal", reverse = false, className, }: InfiniteSliderProps) { @@ -38,7 +38,7 @@ export function InfiniteSlider({ // biome-ignore lint/correctness/useExhaustiveDependencies: React.useEffect(() => { let controls: AnimationPlaybackControlsWithThen | undefined; - const size = direction === 'horizontal' ? width : height; + const size = direction === "horizontal" ? width : height; const contentSize = size + gap; const from = reverse ? -contentSize / 2 : 0; const to = reverse ? 0 : -contentSize / 2; @@ -51,7 +51,7 @@ export function InfiniteSlider({ const transitionDuration = remainingDistance / currentSpeed; controls = animate(translation, [translation.get(), to], { - ease: 'linear', + ease: "linear", duration: transitionDuration, onComplete: () => { setIsTransitioning(false); @@ -60,10 +60,10 @@ export function InfiniteSlider({ }); } else { controls = animate(translation, [from, to], { - ease: 'linear', + ease: "linear", duration: duration, repeat: Infinity, - repeatType: 'loop', + repeatType: "loop", repeatDelay: 0, onRepeat: () => { translation.set(from); @@ -98,16 +98,16 @@ export function InfiniteSlider({ : {}; return ( -
+
diff --git a/apps/docs/src/components/logo-vitnode.tsx b/apps/docs/src/components/logo-vitnode.tsx index 74de91b45..39a1f0e20 100644 --- a/apps/docs/src/components/logo-vitnode.tsx +++ b/apps/docs/src/components/logo-vitnode.tsx @@ -1,17 +1,17 @@ -import { cn } from '@vitnode/core/lib/utils'; +import { cn } from "@vitnode/core/lib/utils"; export const LogoVitNode = ({ className, small, ...props -}: React.ComponentProps<'svg'> & { +}: React.ComponentProps<"svg"> & { small?: boolean; }) => { if (small) return ( { const MotionComponent = motion.create(Component); let segments: string[] = []; switch (by) { - case 'character': - segments = children.split(''); + case "character": + segments = children.split(""); break; - case 'line': - segments = children.split('\n'); + case "line": + segments = children.split("\n"); break; - case 'word': + case "word": segments = children.split(/(\s+)/); break; - case 'text': + case "text": segments = [children]; break; default: @@ -388,20 +388,20 @@ const TextAnimateBase = ({ return ( {segments.map((segment, i) => ( val, { - message: 'You must accept the terms and conditions', + message: "You must accept the terms and conditions", }), description: z .string() - .min(10, 'Description must be at least 10 characters'), + .min(10, "Description must be at least 10 characters"), }); return ( ( ( ), }, { - id: 'user_type', + id: "user_type", component: props => ( ), }, { - id: 'accept_terms', + id: "accept_terms", component: props => ( ( val, { - message: 'You must accept the terms and conditions', + message: "You must accept the terms and conditions", }), }); @@ -15,7 +15,7 @@ export default function SwitchExample() { ( ( { - toast.success('Category deleted successfully!', { - description: 'The category has been removed from your list.', + toast.success("Category deleted successfully!", { + description: "The category has been removed from your list.", }); onClose(); }} diff --git a/apps/docs/src/examples/data-table.tsx b/apps/docs/src/examples/data-table.tsx index c47e476ed..35e2b98a1 100644 --- a/apps/docs/src/examples/data-table.tsx +++ b/apps/docs/src/examples/data-table.tsx @@ -1,49 +1,49 @@ -import { DataTable } from '@vitnode/core/components/table/data-table'; +import { DataTable } from "@vitnode/core/components/table/data-table"; export default function DataTableExample() { return ( ( ( ( ( - toast('Event has been created', { - description: 'Sunday, December 03, 2023 at 9:00 AM', + toast("Event has been created", { + description: "Sunday, December 03, 2023 at 9:00 AM", action: { - label: 'Undo', + label: "Undo", // biome-ignore lint/suspicious/noConsole: - onClick: () => console.log('Undo'), + onClick: () => console.log("Undo"), }, }) } diff --git a/apps/docs/src/examples/switch.tsx b/apps/docs/src/examples/switch.tsx index 8106b8b97..2ad21e550 100644 --- a/apps/docs/src/examples/switch.tsx +++ b/apps/docs/src/examples/switch.tsx @@ -1,13 +1,13 @@ -'use client'; +"use client"; -import { AutoForm } from '@vitnode/core/components/form/auto-form'; -import { AutoFormSwitch } from '@vitnode/core/components/form/fields/switch'; -import { z } from 'zod'; +import { AutoForm } from "@vitnode/core/components/form/auto-form"; +import { AutoFormSwitch } from "@vitnode/core/components/form/fields/switch"; +import { z } from "zod"; export default function SwitchExample() { const formSchema = z.object({ acceptTerms: z.boolean().refine(val => val, { - message: 'You must accept the terms and conditions', + message: "You must accept the terms and conditions", }), }); @@ -15,7 +15,7 @@ export default function SwitchExample() { ( ( locale.code), @@ -11,14 +11,14 @@ export default createMiddleware({ export const config = { matcher: [ // Enable a redirect to a matching locale at the root - '/', + "/", // Set a cookie to remember the previous locale for // all requests that have a locale prefix - '/(en)/:path*', + "/(en)/:path*", // Enable redirects that add missing locales // (e.g. `/pathnames` -> `/en/pathnames`) - '/((?!_next|_vercel|api|.*\\..*).*)', + "/((?!_next|_vercel|api|.*\\..*).*)", ], }; diff --git a/apps/docs/src/vitnode.api.config.ts b/apps/docs/src/vitnode.api.config.ts index e0be6e05b..0ad864805 100644 --- a/apps/docs/src/vitnode.api.config.ts +++ b/apps/docs/src/vitnode.api.config.ts @@ -1,29 +1,29 @@ -import { blogApiPlugin } from '@vitnode/blog/config.api'; -import { NodemailerEmailAdapter } from '@vitnode/core/api/adapters/email/nodemailer'; -import { DiscordSSOApiPlugin } from '@vitnode/core/api/adapters/sso/discord'; -import { FacebookSSOApiPlugin } from '@vitnode/core/api/adapters/sso/facebook'; -import { GoogleSSOApiPlugin } from '@vitnode/core/api/adapters/sso/google'; -import { buildApiConfig } from '@vitnode/core/vitnode.config'; -import { drizzle } from 'drizzle-orm/postgres-js'; +import { blogApiPlugin } from "@vitnode/blog/config.api"; +import { NodemailerEmailAdapter } from "@vitnode/core/api/adapters/email/nodemailer"; +import { DiscordSSOApiPlugin } from "@vitnode/core/api/adapters/sso/discord"; +import { FacebookSSOApiPlugin } from "@vitnode/core/api/adapters/sso/facebook"; +import { GoogleSSOApiPlugin } from "@vitnode/core/api/adapters/sso/google"; +import { buildApiConfig } from "@vitnode/core/vitnode.config"; +import { drizzle } from "drizzle-orm/postgres-js"; export const POSTGRES_URL = - process.env.POSTGRES_URL || 'postgresql://root:root@localhost:5432/vitnode'; + process.env.POSTGRES_URL || "postgresql://root:root@localhost:5432/vitnode"; export const vitNodeApiConfig = buildApiConfig({ pathToMessages: async path => await import(`./locales/${path}`), captcha: { - type: 'cloudflare_turnstile', + type: "cloudflare_turnstile", siteKey: process.env.CLOUDFLARE_TURNSTILE_SITE_KEY, secretKey: process.env.CLOUDFLARE_TURNSTILE_SECRET_KEY, }, metadata: { - title: 'VitNode API', - shortTitle: 'VitNode', + title: "VitNode API", + shortTitle: "VitNode", }, plugins: [blogApiPlugin()], dbProvider: drizzle({ connection: POSTGRES_URL, - casing: 'camelCase', + casing: "camelCase", }), rateLimiter: { points: 20, // 20 requests @@ -37,8 +37,8 @@ export const vitNodeApiConfig = buildApiConfig({ user: process.env.NOD_EMAILER_USER, }), logo: { - text: 'VitNode Email Test', - src: 'http://localhost:3000/logo_vitnode_dark.png', + text: "VitNode Email Test", + src: "http://localhost:3000/logo_vitnode_dark.png", }, }, authorization: { diff --git a/apps/docs/src/vitnode.config.ts b/apps/docs/src/vitnode.config.ts index 4dcda6d6d..ca8d7836d 100644 --- a/apps/docs/src/vitnode.config.ts +++ b/apps/docs/src/vitnode.config.ts @@ -1,25 +1,25 @@ -import { blogPlugin } from '@vitnode/blog/config'; -import { buildConfig, handleRequestConfig } from '@vitnode/core/vitnode.config'; -import { getRequestConfig } from 'next-intl/server'; +import { blogPlugin } from "@vitnode/blog/config"; +import { buildConfig, handleRequestConfig } from "@vitnode/core/vitnode.config"; +import { getRequestConfig } from "next-intl/server"; export const vitNodeConfig = buildConfig({ metadata: { - title: 'VitNode', - shortTitle: 'VitNode', + title: "VitNode", + shortTitle: "VitNode", }, plugins: [blogPlugin()], debug: true, i18n: { locales: [ { - code: 'en', - name: 'English', + code: "en", + name: "English", }, ], - defaultLocale: 'en', + defaultLocale: "en", }, theme: { - defaultTheme: 'light', + defaultTheme: "light", }, }); diff --git a/biome.json b/biome.json index 3951392b8..a535ccc4a 100644 --- a/biome.json +++ b/biome.json @@ -26,7 +26,7 @@ }, "javascript": { "formatter": { - "quoteStyle": "single", + "quoteStyle": "double", "jsxQuoteStyle": "double", "arrowParentheses": "asNeeded" } diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/api-bun/src/index.ts b/packages/create-vitnode-app/copy-of-vitnode-app/api-bun/src/index.ts index 284aa1840..19cb3e2a3 100644 --- a/packages/create-vitnode-app/copy-of-vitnode-app/api-bun/src/index.ts +++ b/packages/create-vitnode-app/copy-of-vitnode-app/api-bun/src/index.ts @@ -1,9 +1,9 @@ -import { OpenAPIHono } from '@hono/zod-openapi'; -import { VitNodeAPI } from '@vitnode/core/api/config'; +import { OpenAPIHono } from "@hono/zod-openapi"; +import { VitNodeAPI } from "@vitnode/core/api/config"; -import { vitNodeApiConfig } from './vitnode.api.config.js'; +import { vitNodeApiConfig } from "./vitnode.api.config.js"; -const app = new OpenAPIHono().basePath('/api'); +const app = new OpenAPIHono().basePath("/api"); VitNodeAPI({ app, diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/api-single-app/src/vitnode.api.config.ts b/packages/create-vitnode-app/copy-of-vitnode-app/api-single-app/src/vitnode.api.config.ts index cbe624760..316a9eb9c 100644 --- a/packages/create-vitnode-app/copy-of-vitnode-app/api-single-app/src/vitnode.api.config.ts +++ b/packages/create-vitnode-app/copy-of-vitnode-app/api-single-app/src/vitnode.api.config.ts @@ -1,18 +1,18 @@ -import { buildApiConfig } from '@vitnode/core/vitnode.config'; -import { drizzle } from 'drizzle-orm/postgres-js'; +import { buildApiConfig } from "@vitnode/core/vitnode.config"; +import { drizzle } from "drizzle-orm/postgres-js"; export const POSTGRES_URL = - process.env.POSTGRES_URL || 'postgresql://root:root@localhost:5432/vitnode'; + process.env.POSTGRES_URL || "postgresql://root:root@localhost:5432/vitnode"; export const vitNodeApiConfig = buildApiConfig({ pathToMessages: async path => await import(`./locales/${path}`), metadata: { - title: 'VitNode', - shortTitle: 'VitNode', + title: "VitNode", + shortTitle: "VitNode", }, plugins: [], dbProvider: drizzle({ connection: POSTGRES_URL, - casing: 'camelCase', + casing: "camelCase", }), }); diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/api/src/index.ts b/packages/create-vitnode-app/copy-of-vitnode-app/api/src/index.ts index 55319d430..204f0e9b3 100644 --- a/packages/create-vitnode-app/copy-of-vitnode-app/api/src/index.ts +++ b/packages/create-vitnode-app/copy-of-vitnode-app/api/src/index.ts @@ -1,11 +1,11 @@ /** biome-ignore-all lint/suspicious/noConsole: */ -import { serve } from '@hono/node-server'; -import { OpenAPIHono } from '@hono/zod-openapi'; -import { VitNodeAPI } from '@vitnode/core/api/config'; +import { serve } from "@hono/node-server"; +import { OpenAPIHono } from "@hono/zod-openapi"; +import { VitNodeAPI } from "@vitnode/core/api/config"; -import { vitNodeApiConfig } from './vitnode.api.config.js'; +import { vitNodeApiConfig } from "./vitnode.api.config.js"; -const app = new OpenAPIHono().basePath('/api'); +const app = new OpenAPIHono().basePath("/api"); VitNodeAPI({ app, @@ -18,7 +18,7 @@ serve( port: 8080, }, info => { - const initMessage = '\x1b[34m[VitNode]\x1b[0m'; + const initMessage = "\x1b[34m[VitNode]\x1b[0m"; console.log( `${initMessage} API server is running on http://localhost:${info.port}`, diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/api/src/vitnode.api.config.ts b/packages/create-vitnode-app/copy-of-vitnode-app/api/src/vitnode.api.config.ts index d61dfc4b3..7cc1e1355 100644 --- a/packages/create-vitnode-app/copy-of-vitnode-app/api/src/vitnode.api.config.ts +++ b/packages/create-vitnode-app/copy-of-vitnode-app/api/src/vitnode.api.config.ts @@ -1,23 +1,23 @@ -import { buildApiConfig } from '@vitnode/core/vitnode.config'; -import { config } from 'dotenv'; -import { drizzle } from 'drizzle-orm/postgres-js'; +import { buildApiConfig } from "@vitnode/core/vitnode.config"; +import { config } from "dotenv"; +import { drizzle } from "drizzle-orm/postgres-js"; config({ quiet: true, }); export const POSTGRES_URL = - process.env.POSTGRES_URL || 'postgresql://root:root@localhost:5432/vitnode'; + process.env.POSTGRES_URL || "postgresql://root:root@localhost:5432/vitnode"; export const vitNodeApiConfig = buildApiConfig({ plugins: [], pathToMessages: async path => await import(`./locales/${path}`), dbProvider: drizzle({ connection: POSTGRES_URL, - casing: 'camelCase', + casing: "camelCase", }), metadata: { - title: 'VitNode API', - shortTitle: 'VitNode', + title: "VitNode API", + shortTitle: "VitNode", }, }); diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/root/global.d.ts b/packages/create-vitnode-app/copy-of-vitnode-app/root/global.d.ts index f33ce4917..ca6f61f83 100644 --- a/packages/create-vitnode-app/copy-of-vitnode-app/root/global.d.ts +++ b/packages/create-vitnode-app/copy-of-vitnode-app/root/global.d.ts @@ -1,8 +1,8 @@ /// -import core from './src/locales/@vitnode/core/en.json' with { type: 'json' }; +import core from "./src/locales/@vitnode/core/en.json" with { type: "json" }; -declare module 'next-intl' { +declare module "next-intl" { interface AppConfig { Messages: typeof core; } diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/root/next.config.ts b/packages/create-vitnode-app/copy-of-vitnode-app/root/next.config.ts index b7aef51e2..7a7db9334 100644 --- a/packages/create-vitnode-app/copy-of-vitnode-app/root/next.config.ts +++ b/packages/create-vitnode-app/copy-of-vitnode-app/root/next.config.ts @@ -1,5 +1,5 @@ -import { vitNodeNextConfig } from '@vitnode/core/config/next.config'; -import type { NextConfig } from 'next'; +import { vitNodeNextConfig } from "@vitnode/core/config/next.config"; +import type { NextConfig } from "next"; const nextConfig: NextConfig = { experimental: { diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/(main)/layout.tsx b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/(main)/layout.tsx index 086bc3368..85b599a1d 100644 --- a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/(main)/layout.tsx +++ b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/(main)/layout.tsx @@ -1,7 +1,7 @@ -import { LogoVitNode } from '@vitnode/core/components/logo-vitnode'; -import { ThemeLayout } from '@vitnode/core/views/layouts/theme/layout'; +import { LogoVitNode } from "@vitnode/core/components/logo-vitnode"; +import { ThemeLayout } from "@vitnode/core/views/layouts/theme/layout"; -import { vitNodeConfig } from '../../../vitnode.config'; +import { vitNodeConfig } from "../../../vitnode.config"; export default function Layout({ children }: { children: React.ReactNode }) { return ( diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/(main)/page.tsx b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/(main)/page.tsx index 753c497aa..ad15f195f 100644 --- a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/(main)/page.tsx +++ b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/app/[locale]/(main)/page.tsx @@ -1,8 +1,8 @@ -import { LogoVitNode } from '@vitnode/core/components/logo-vitnode'; -import { buttonVariants } from '@vitnode/core/components/ui/button'; -import { Link } from '@vitnode/core/lib/navigation'; -import { cn } from '@vitnode/core/lib/utils'; -import { ArrowRight, Book, Terminal } from 'lucide-react'; +import { LogoVitNode } from "@vitnode/core/components/logo-vitnode"; +import { buttonVariants } from "@vitnode/core/components/ui/button"; +import { Link } from "@vitnode/core/lib/navigation"; +import { cn } from "@vitnode/core/lib/utils"; +import { ArrowRight, Book, Terminal } from "lucide-react"; export default function Page() { return ( @@ -25,7 +25,7 @@ export default function Page() { diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/middleware.ts b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/middleware.ts index 99e9d7051..dbc211b22 100644 --- a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/middleware.ts +++ b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/middleware.ts @@ -1,6 +1,6 @@ -import createMiddleware from 'next-intl/middleware'; +import createMiddleware from "next-intl/middleware"; -import { vitNodeConfig } from './vitnode.config'; +import { vitNodeConfig } from "./vitnode.config"; export default createMiddleware({ locales: vitNodeConfig.i18n.locales.map(locale => locale.code), @@ -11,14 +11,14 @@ export default createMiddleware({ export const config = { matcher: [ // Enable a redirect to a matching locale at the root - '/', + "/", // Set a cookie to remember the previous locale for // all requests that have a locale prefix - '/(en)/:path*', + "/(en)/:path*", // Enable redirects that add missing locales // (e.g. `/pathnames` -> `/en/pathnames`) - '/((?!_next|_vercel|api|.*\\..*).*)', + "/((?!_next|_vercel|api|.*\\..*).*)", ], }; diff --git a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/vitnode.config.ts b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/vitnode.config.ts index 4f20e8a11..ef9d4743c 100644 --- a/packages/create-vitnode-app/copy-of-vitnode-app/root/src/vitnode.config.ts +++ b/packages/create-vitnode-app/copy-of-vitnode-app/root/src/vitnode.config.ts @@ -1,23 +1,23 @@ -import { buildConfig, handleRequestConfig } from '@vitnode/core/vitnode.config'; -import { getRequestConfig } from 'next-intl/server'; +import { buildConfig, handleRequestConfig } from "@vitnode/core/vitnode.config"; +import { getRequestConfig } from "next-intl/server"; export const vitNodeConfig = buildConfig({ metadata: { - title: 'VitNode', - shortTitle: 'VitNode', + title: "VitNode", + shortTitle: "VitNode", }, plugins: [], i18n: { locales: [ { - code: 'en', - name: 'English', + code: "en", + name: "English", }, ], - defaultLocale: 'en', + defaultLocale: "en", }, theme: { - defaultTheme: 'light', + defaultTheme: "light", }, }); diff --git a/packages/create-vitnode-app/src/create/create-package-json.ts b/packages/create-vitnode-app/src/create/create-package-json.ts index 9411f56b2..e21109c28 100644 --- a/packages/create-vitnode-app/src/create/create-package-json.ts +++ b/packages/create-vitnode-app/src/create/create-package-json.ts @@ -1,80 +1,80 @@ -import { readFile, writeFile } from 'node:fs/promises'; -import { dirname, join } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { getAvailablePackageManagers } from '../helpers/get-available-package-managers.js'; -import type { PackageJSON } from '../helpers/packages-json.js'; -import type { CreateCliReturn } from '../questions.js'; +import { readFile, writeFile } from "node:fs/promises"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { getAvailablePackageManagers } from "../helpers/get-available-package-managers.js"; +import type { PackageJSON } from "../helpers/packages-json.js"; +import type { CreateCliReturn } from "../questions.js"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); -type Mode = CreateCliReturn['mode']; +type Mode = CreateCliReturn["mode"]; const writeJson = async (path: string, data: unknown) => writeFile(path, JSON.stringify(data, null, 2)); const paths = (root: string) => ({ root, - api: join(root, 'apps', 'api'), - web: join(root, 'apps', 'web'), + api: join(root, "apps", "api"), + web: join(root, "apps", "web"), }); const withIf = >(cond: boolean, obj: T) => (cond ? obj : {}) as Partial; const versions = { - typesNode: '^24', - typesReact: '^19.1', - typesReactDom: '^19.1', - typesMdx: '^2.0.13', - typesBun: 'latest', - - turbo: '^2.5.6', - typescript: '^5.9.2', - tsx: '^4.20.4', - tscAlias: '^1.8.16', - eslint: '^9.33.0', - prettier: '^3.6.2', - prettierTailwind: '^0.6.14', - tailwind: '^4.1.12', - tailwindPostcss: '^4.1.12', - postcss: '^8.5.6', - twAnimateCssWeb: '^1.3.7', - twAnimateCssSingle: '^1.3.6', - - react: '^19.1', - reactDom: '^19.1', - nextSingle: '^15.5.0', - nextWebInMonorepo: '^15.4.6', - nextIntl: '^4.3.4', - useIntl: '^4.3.4', - rhf: '^7.62.0', - rhfResolvers: '^5.1.1', - lucide: '^0.540.0', - sonner: '^2.0.7', - dotenv: '^17.2.1', - - drizzleKitSingle: '^0.31.4', - drizzleKitApi: '^0.31.3', - drizzleOrm: '^0.44.4', - - hono: '^4.9.2', - honoZodOpenapi: '^1.1.0', - honoZodValidator: '^0.7.2', - reactEmail: '^4.2.8', - reactEmailComponents: '^0.5.1', - zod: '^4.0.17', - - babelReactCompiler: '19.1.0-rc.2', - cva: '^0.7.1', + typesNode: "^24", + typesReact: "^19.1", + typesReactDom: "^19.1", + typesMdx: "^2.0.13", + typesBun: "latest", + + turbo: "^2.5.6", + typescript: "^5.9.2", + tsx: "^4.20.4", + tscAlias: "^1.8.16", + eslint: "^9.33.0", + prettier: "^3.6.2", + prettierTailwind: "^0.6.14", + tailwind: "^4.1.12", + tailwindPostcss: "^4.1.12", + postcss: "^8.5.6", + twAnimateCssWeb: "^1.3.7", + twAnimateCssSingle: "^1.3.6", + + react: "^19.1", + reactDom: "^19.1", + nextSingle: "^15.5.0", + nextWebInMonorepo: "^15.4.6", + nextIntl: "^4.3.4", + useIntl: "^4.3.4", + rhf: "^7.62.0", + rhfResolvers: "^5.1.1", + lucide: "^0.540.0", + sonner: "^2.0.7", + dotenv: "^17.2.1", + + drizzleKitSingle: "^0.31.4", + drizzleKitApi: "^0.31.3", + drizzleOrm: "^0.44.4", + + hono: "^4.9.2", + honoZodOpenapi: "^1.1.0", + honoZodValidator: "^0.7.2", + reactEmail: "^4.2.8", + reactEmailComponents: "^0.5.1", + zod: "^4.0.17", + + babelReactCompiler: "19.1.0-rc.2", + cva: "^0.7.1", }; /** * Shared blocks */ const eslintScripts = { - lint: 'eslint .', - 'lint:fix': 'eslint . --fix', + lint: "eslint .", + "lint:fix": "eslint . --fix", }; const dockerDevScript = (appName: string) => @@ -85,14 +85,14 @@ const rootScripts = ( enableDocker: boolean, appName: string, ) => ({ - 'db:migrate': 'turbo db:migrate', - 'db:push': 'turbo db:push', - init: 'turbo init', - dev: 'turbo dev', - build: 'turbo build', - start: 'turbo start', + "db:migrate": "turbo db:migrate", + "db:push": "turbo db:push", + init: "turbo init", + dev: "turbo dev", + build: "turbo build", + start: "turbo start", ...withIf(enableEslint, eslintScripts), - ...withIf(enableDocker, { 'docker:dev': dockerDevScript(appName) }), + ...withIf(enableDocker, { "docker:dev": dockerDevScript(appName) }), }); const apiScripts = ( @@ -102,23 +102,23 @@ const apiScripts = ( onlyApi: boolean, appName: string, ) => ({ - 'db:push': 'vitnode push', - 'db:migrate': 'vitnode migrate', - init: 'vitnode init --api', - ...(pm === 'bun' + "db:push": "vitnode push", + "db:migrate": "vitnode migrate", + init: "vitnode init --api", + ...(pm === "bun" ? { - dev: 'vitnode init --api && bun run --hot src/index.ts', - start: 'NODE_ENV=production bun run src/index.ts', + dev: "vitnode init --api && bun run --hot src/index.ts", + start: "NODE_ENV=production bun run src/index.ts", } : { - dev: 'vitnode init --api && tsx watch src/index.ts', - build: 'tsc && tsc-alias -p tsconfig.json', - start: 'node dist/index.js', + dev: "vitnode init --api && tsx watch src/index.ts", + build: "tsc && tsc-alias -p tsconfig.json", + start: "node dist/index.js", }), - 'dev:email': 'email dev --dir src/emails', + "dev:email": "email dev --dir src/emails", ...withIf(eslint, eslintScripts), - ...withIf(docker && onlyApi, { 'docker:dev': dockerDevScript(appName) }), - 'drizzle-kit': 'drizzle-kit', + ...withIf(docker && onlyApi, { "docker:dev": dockerDevScript(appName) }), + "drizzle-kit": "drizzle-kit", }); const singleAppScripts = ( @@ -126,23 +126,23 @@ const singleAppScripts = ( docker: boolean, appName: string, ) => ({ - 'db:push': 'vitnode push', - 'db:migrate': 'vitnode migrate', - init: 'vitnode init', - dev: 'vitnode init && next dev --turbopack', - 'dev:email': 'email dev --dir src/emails', - build: 'next build', - start: 'next start', + "db:push": "vitnode push", + "db:migrate": "vitnode migrate", + init: "vitnode init", + dev: "vitnode init && next dev --turbopack", + "dev:email": "email dev --dir src/emails", + build: "next build", + start: "next start", ...withIf(eslint, eslintScripts), - ...withIf(docker, { 'docker:dev': dockerDevScript(appName) }), - 'drizzle-kit': 'drizzle-kit', + ...withIf(docker, { "docker:dev": dockerDevScript(appName) }), + "drizzle-kit": "drizzle-kit", }); const webScripts = (eslint: boolean) => ({ - init: 'vitnode init --web', - dev: 'vitnode init --web && next dev --turbopack', - build: 'next build', - start: 'next start', + init: "vitnode init --web", + dev: "vitnode init --web && next dev --turbopack", + build: "next build", + start: "next start", ...withIf(eslint, eslintScripts), }); @@ -150,13 +150,13 @@ const webScripts = (eslint: boolean) => ({ * Dependency builders */ const baseDevDeps = (eslint: boolean, includePrettier: boolean) => ({ - '@types/node': versions.typesNode, - '@vitnode/eslint-config': '', // filled with local version dynamically + "@types/node": versions.typesNode, + "@vitnode/eslint-config": "", // filled with local version dynamically ...withIf(eslint, { eslint: versions.eslint, ...withIf(includePrettier, { prettier: versions.prettier, - 'prettier-plugin-tailwindcss': versions.prettierTailwind, + "prettier-plugin-tailwindcss": versions.prettierTailwind, }), }), }); @@ -169,102 +169,102 @@ const rootDevDeps = (eslint: boolean) => ({ }); const apiDeps = { - '@hono/zod-openapi': versions.honoZodOpenapi, - '@hono/zod-validator': versions.honoZodValidator, - '@react-email/components': versions.reactEmailComponents, - '@vitnode/core': '', // filled dynamically - 'drizzle-kit': versions.drizzleKitApi, - 'drizzle-orm': versions.drizzleOrm, + "@hono/zod-openapi": versions.honoZodOpenapi, + "@hono/zod-validator": versions.honoZodValidator, + "@react-email/components": versions.reactEmailComponents, + "@vitnode/core": "", // filled dynamically + "drizzle-kit": versions.drizzleKitApi, + "drizzle-orm": versions.drizzleOrm, hono: versions.hono, - 'next-intl': versions.nextIntl, + "next-intl": versions.nextIntl, react: versions.react, - 'react-dom': versions.reactDom, - 'use-intl': versions.useIntl, + "react-dom": versions.reactDom, + "use-intl": versions.useIntl, zod: versions.zod, }; const apiDevDeps = (pm: string, eslint: boolean) => ({ - '@hono/node-server': '^1.19.0', - ...(pm === 'bun' ? { '@types/bun': versions.typesBun } : {}), - '@types/node': versions.typesNode, - '@types/react': versions.typesReact, - '@types/react-dom': versions.typesReactDom, - '@vitnode/eslint-config': '', + "@hono/node-server": "^1.19.0", + ...(pm === "bun" ? { "@types/bun": versions.typesBun } : {}), + "@types/node": versions.typesNode, + "@types/react": versions.typesReact, + "@types/react-dom": versions.typesReactDom, + "@vitnode/eslint-config": "", dotenv: versions.dotenv, ...withIf(eslint, { eslint: versions.eslint, // Prettier in API only when onlyApi + eslint in original code – we'll preserve by passing include later if needed }), - 'react-email': versions.reactEmail, - 'tsc-alias': versions.tscAlias, + "react-email": versions.reactEmail, + "tsc-alias": versions.tscAlias, tsx: versions.tsx, typescript: versions.typescript, }); const singleAppDeps = { - '@hono/zod-openapi': versions.honoZodOpenapi, - '@hono/zod-validator': versions.honoZodValidator, - '@hookform/resolvers': versions.rhfResolvers, - '@react-email/components': versions.reactEmailComponents, - '@vitnode/core': '', - 'babel-plugin-react-compiler': versions.babelReactCompiler, - 'drizzle-kit': versions.drizzleKitSingle, - 'drizzle-orm': versions.drizzleOrm, + "@hono/zod-openapi": versions.honoZodOpenapi, + "@hono/zod-validator": versions.honoZodValidator, + "@hookform/resolvers": versions.rhfResolvers, + "@react-email/components": versions.reactEmailComponents, + "@vitnode/core": "", + "babel-plugin-react-compiler": versions.babelReactCompiler, + "drizzle-kit": versions.drizzleKitSingle, + "drizzle-orm": versions.drizzleOrm, hono: versions.hono, - 'lucide-react': versions.lucide, + "lucide-react": versions.lucide, next: versions.nextSingle, - 'next-intl': versions.nextIntl, + "next-intl": versions.nextIntl, react: versions.react, - 'react-dom': versions.reactDom, - 'react-hook-form': versions.rhf, + "react-dom": versions.reactDom, + "react-hook-form": versions.rhf, sonner: versions.sonner, - 'use-intl': versions.useIntl, + "use-intl": versions.useIntl, zod: versions.zod, }; const singleAppDevDeps = (eslint: boolean) => ({ - '@tailwindcss/postcss': versions.tailwindPostcss, - '@types/node': versions.typesNode, - '@types/react': versions.typesReact, - '@types/react-dom': versions.typesReactDom, - '@vitnode/eslint-config': '', + "@tailwindcss/postcss": versions.tailwindPostcss, + "@types/node": versions.typesNode, + "@types/react": versions.typesReact, + "@types/react-dom": versions.typesReactDom, + "@vitnode/eslint-config": "", ...withIf(eslint, { eslint: versions.eslint, prettier: versions.prettier, - 'prettier-plugin-tailwindcss': versions.prettierTailwind, + "prettier-plugin-tailwindcss": versions.prettierTailwind, }), - 'react-email': versions.reactEmail, + "react-email": versions.reactEmail, turbo: versions.turbo, tailwindcss: versions.tailwind, - 'tw-animate-css': versions.twAnimateCssSingle, + "tw-animate-css": versions.twAnimateCssSingle, typescript: versions.typescript, }); const webDeps = { - '@vitnode/core': '', - 'babel-plugin-react-compiler': versions.babelReactCompiler, - 'lucide-react': versions.lucide, + "@vitnode/core": "", + "babel-plugin-react-compiler": versions.babelReactCompiler, + "lucide-react": versions.lucide, next: versions.nextWebInMonorepo, - 'next-intl': versions.nextIntl, + "next-intl": versions.nextIntl, react: versions.react, - 'react-dom': versions.reactDom, - 'react-hook-form': versions.rhf, + "react-dom": versions.reactDom, + "react-hook-form": versions.rhf, sonner: versions.sonner, }; const webDevDeps = (eslint: boolean) => ({ - '@hookform/resolvers': versions.rhfResolvers, - '@tailwindcss/postcss': versions.tailwindPostcss, - '@types/mdx': versions.typesMdx, - '@types/node': versions.typesNode, - '@types/react': versions.typesReact, - '@types/react-dom': versions.typesReactDom, - '@vitnode/eslint-config': '', - 'class-variance-authority': versions.cva, + "@hookform/resolvers": versions.rhfResolvers, + "@tailwindcss/postcss": versions.tailwindPostcss, + "@types/mdx": versions.typesMdx, + "@types/node": versions.typesNode, + "@types/react": versions.typesReact, + "@types/react-dom": versions.typesReactDom, + "@vitnode/eslint-config": "", + "class-variance-authority": versions.cva, ...withIf(eslint, { eslint: versions.eslint }), postcss: versions.postcss, tailwindcss: versions.tailwind, - 'tw-animate-css': versions.twAnimateCssWeb, + "tw-animate-css": versions.twAnimateCssWeb, typescript: versions.typescript, zod: versions.zod, }); @@ -291,7 +291,7 @@ export const createPackageJSON = async ({ }) => { // Resolve local version of @vitnode/* based on this CLI's package.json const cliPkg: PackageJSON = JSON.parse( - await readFile(join(__dirname, '..', '..', '..', 'package.json'), 'utf-8'), + await readFile(join(__dirname, "..", "..", "..", "package.json"), "utf-8"), ); const vitnodeVersionRange = `^${cliPkg.version}`; @@ -299,9 +299,9 @@ export const createPackageJSON = async ({ const pmSpec = `${packageManager}@${pmVersions[packageManager]}`; const p = paths(root); - const isApiMonorepo = mode === 'apiMonorepo' || !!monorepo; - const isOnlyApi = mode === 'onlyApi'; - const isSingleApp = mode === 'singleApp'; + const isApiMonorepo = mode === "apiMonorepo" || !!monorepo; + const isOnlyApi = mode === "onlyApi"; + const isSingleApp = mode === "singleApp"; // 1) Root package.json (for monorepo/apiMonorepo) if (isApiMonorepo) { @@ -311,93 +311,93 @@ export const createPackageJSON = async ({ scripts: rootScripts(eslint, !!docker, appName), devDependencies: { ...rootDevDeps(eslint), - '@vitnode/eslint-config': vitnodeVersionRange, + "@vitnode/eslint-config": vitnodeVersionRange, }, packageManager: pmSpec, - workspaces: ['apps/*', 'plugins/*'], + workspaces: ["apps/*", "plugins/*"], }; - await writeJson(join(p.root, 'package.json'), rootPkg); + await writeJson(join(p.root, "package.json"), rootPkg); } // 2) API package.json (shared by onlyApi and apiMonorepo) const apiPkg: PackageJSON = { - name: isApiMonorepo ? 'api' : appName, - version: '0.1.0', + name: isApiMonorepo ? "api" : appName, + version: "0.1.0", private: true, - type: 'module', + type: "module", scripts: apiScripts( packageManager, eslint, !!docker, - mode === 'onlyApi', + mode === "onlyApi", appName, ), dependencies: { ...apiDeps, - '@vitnode/core': vitnodeVersionRange, + "@vitnode/core": vitnodeVersionRange, }, devDependencies: { ...apiDevDeps(packageManager, eslint), - '@vitnode/eslint-config': vitnodeVersionRange, - ...(eslint && mode === 'onlyApi' + "@vitnode/eslint-config": vitnodeVersionRange, + ...(eslint && mode === "onlyApi" ? { prettier: versions.prettier, - 'prettier-plugin-tailwindcss': versions.prettierTailwind, + "prettier-plugin-tailwindcss": versions.prettierTailwind, } : {}), // TS pipeline pieces when not using Bun for dev - ...(packageManager === 'bun' ? {} : {}), + ...(packageManager === "bun" ? {} : {}), }, }; // 3) Single app (Next.js + API inside one app) if (isSingleApp) { const singlePkg: PackageJSON = { - name: monorepo ? 'web' : appName, - version: '0.1.0', + name: monorepo ? "web" : appName, + version: "0.1.0", private: true, - type: 'module', + type: "module", scripts: singleAppScripts(eslint, !!docker, appName), dependencies: { ...singleAppDeps, - '@vitnode/core': vitnodeVersionRange, + "@vitnode/core": vitnodeVersionRange, }, devDependencies: { ...singleAppDevDeps(eslint), - '@vitnode/eslint-config': vitnodeVersionRange, + "@vitnode/eslint-config": vitnodeVersionRange, }, packageManager: pmSpec, }; - await writeJson(join(monorepo ? p.web : p.root, 'package.json'), singlePkg); + await writeJson(join(monorepo ? p.web : p.root, "package.json"), singlePkg); } // 4) apiMonorepo: write API + WEB - if (mode === 'apiMonorepo') { - await writeJson(join(p.api, 'package.json'), apiPkg); + if (mode === "apiMonorepo") { + await writeJson(join(p.api, "package.json"), apiPkg); const webPkg: PackageJSON = { - name: 'web', - version: '0.1.0', + name: "web", + version: "0.1.0", private: true, - type: 'module', + type: "module", scripts: webScripts(eslint), dependencies: { ...webDeps, - '@vitnode/core': vitnodeVersionRange, + "@vitnode/core": vitnodeVersionRange, }, devDependencies: { ...webDevDeps(eslint), - '@vitnode/eslint-config': vitnodeVersionRange, + "@vitnode/eslint-config": vitnodeVersionRange, }, }; - await writeJson(join(p.web, 'package.json'), webPkg); + await writeJson(join(p.web, "package.json"), webPkg); } // 5) onlyApi: write API (in root or in monorepo structure if requested) if (isOnlyApi) { - await writeJson(join(monorepo ? p.api : p.root, 'package.json'), apiPkg); + await writeJson(join(monorepo ? p.api : p.root, "package.json"), apiPkg); } }; diff --git a/packages/create-vitnode-app/src/create/create-vitnode.ts b/packages/create-vitnode-app/src/create/create-vitnode.ts index 72d89f585..596fd3382 100644 --- a/packages/create-vitnode-app/src/create/create-vitnode.ts +++ b/packages/create-vitnode-app/src/create/create-vitnode.ts @@ -1,4 +1,4 @@ -import { existsSync } from 'node:fs'; +import { existsSync } from "node:fs"; import { copyFile, cp, @@ -6,19 +6,19 @@ import { readFile, rename, writeFile, -} from 'node:fs/promises'; -import { dirname, join } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import ora from 'ora'; -import color from 'picocolors'; +} from "node:fs/promises"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import ora from "ora"; +import color from "picocolors"; import { generateMigrationsVitnode, initFilesVitnode, -} from '../helpers/init-vitnode.js'; -import { installDependencies } from '../helpers/install-dependencies.js'; -import { isFolderEmpty } from '../helpers/is-folder-empty.js'; -import type { CreateCliReturn } from '../questions.js'; -import { createPackageJSON } from './create-package-json.js'; +} from "../helpers/init-vitnode.js"; +import { installDependencies } from "../helpers/install-dependencies.js"; +import { isFolderEmpty } from "../helpers/is-folder-empty.js"; +import type { CreateCliReturn } from "../questions.js"; +import { createPackageJSON } from "./create-package-json.js"; export const createVitNode = async ({ root, @@ -40,10 +40,10 @@ export const createVitNode = async ({ const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); - const templatePath = join(__dirname, '..', '..', '..', 'copy-of-vitnode-app'); + const templatePath = join(__dirname, "..", "..", "..", "copy-of-vitnode-app"); if (!existsSync(templatePath)) { spinner.fail( - `\n${color.red('Error!')} Template path ${color.cyan(templatePath)} does not exist.`, + `\n${color.red("Error!")} Template path ${color.cyan(templatePath)} does not exist.`, ); process.exit(1); } @@ -54,17 +54,17 @@ export const createVitNode = async ({ process.exit(1); } const monorepoStructure = { - api: join(root, 'apps', 'api'), - web: join(root, 'apps', 'web'), + api: join(root, "apps", "api"), + web: join(root, "apps", "web"), }; - spinner.text = 'Preparing the project structure...'; - if (monorepo || mode === 'apiMonorepo') { + spinner.text = "Preparing the project structure..."; + if (monorepo || mode === "apiMonorepo") { const dirsToCreate: string[] = []; - if (mode === 'apiMonorepo' || (monorepo && mode === 'onlyApi')) { + if (mode === "apiMonorepo" || (monorepo && mode === "onlyApi")) { dirsToCreate.push(monorepoStructure.api); } - if (mode === 'apiMonorepo' || (monorepo && mode === 'singleApp')) { + if (mode === "apiMonorepo" || (monorepo && mode === "singleApp")) { dirsToCreate.push(monorepoStructure.web); } await Promise.all( @@ -72,50 +72,50 @@ export const createVitNode = async ({ ); } - spinner.text = 'Copying files...'; - await cp(join(templatePath, '.vscode'), join(root, '.vscode'), { + spinner.text = "Copying files..."; + await cp(join(templatePath, ".vscode"), join(root, ".vscode"), { recursive: true, }); - if (mode === 'singleApp') { + if (mode === "singleApp") { await Promise.all([ - cp(join(templatePath, 'root'), monorepo ? monorepoStructure.web : root, { + cp(join(templatePath, "root"), monorepo ? monorepoStructure.web : root, { recursive: true, }), cp( - join(templatePath, 'api-single-app'), + join(templatePath, "api-single-app"), monorepo ? monorepoStructure.web : root, { recursive: true, }, ), ]); - } else if (mode === 'apiMonorepo') { + } else if (mode === "apiMonorepo") { await Promise.all([ - cp(join(templatePath, 'root'), monorepoStructure.web, { + cp(join(templatePath, "root"), monorepoStructure.web, { recursive: true, }), - cp(join(templatePath, 'api'), monorepoStructure.api, { + cp(join(templatePath, "api"), monorepoStructure.api, { recursive: true, }), ]); - if (packageManager === 'bun') { - await cp(join(templatePath, 'api-bun'), monorepoStructure.api, { + if (packageManager === "bun") { + await cp(join(templatePath, "api-bun"), monorepoStructure.api, { recursive: true, }); } - } else if (mode === 'onlyApi') { + } else if (mode === "onlyApi") { await cp( - join(templatePath, 'api'), + join(templatePath, "api"), monorepo ? monorepoStructure.api : root, { recursive: true, }, ); - if (packageManager === 'bun') { + if (packageManager === "bun") { await cp( - join(templatePath, 'api-bun'), + join(templatePath, "api-bun"), monorepo ? monorepoStructure.api : root, { recursive: true, @@ -124,37 +124,37 @@ export const createVitNode = async ({ } } - if (mode === 'apiMonorepo' || monorepo) { - await cp(join(templatePath, 'monorepo'), root, { + if (mode === "apiMonorepo" || monorepo) { + await cp(join(templatePath, "monorepo"), root, { recursive: true, }); } if (biome) { - spinner.text = 'Copying Biome files...'; - await cp(join(templatePath, 'biome'), root, { + spinner.text = "Copying Biome files..."; + await cp(join(templatePath, "biome"), root, { recursive: true, }); } - spinner.text = 'Renaming special files...'; - await rename(join(root, '.gitignore_template'), join(root, '.gitignore')); - if (mode === 'apiMonorepo' || monorepo) { - if (existsSync(join(monorepoStructure.api, '.gitignore_template'))) { + spinner.text = "Renaming special files..."; + await rename(join(root, ".gitignore_template"), join(root, ".gitignore")); + if (mode === "apiMonorepo" || monorepo) { + if (existsSync(join(monorepoStructure.api, ".gitignore_template"))) { await rename( - join(monorepoStructure.api, '.gitignore_template'), - join(monorepoStructure.api, '.gitignore'), + join(monorepoStructure.api, ".gitignore_template"), + join(monorepoStructure.api, ".gitignore"), ); } - if (existsSync(join(monorepoStructure.web, '.gitignore_template'))) { + if (existsSync(join(monorepoStructure.web, ".gitignore_template"))) { await rename( - join(monorepoStructure.web, '.gitignore_template'), - join(monorepoStructure.web, '.gitignore'), + join(monorepoStructure.web, ".gitignore_template"), + join(monorepoStructure.web, ".gitignore"), ); } } - spinner.text = 'Creating package.json...'; + spinner.text = "Creating package.json..."; await createPackageJSON({ root, appName, @@ -165,22 +165,22 @@ export const createVitNode = async ({ monorepo, }); - if ((mode === 'apiMonorepo' || monorepo) && packageManager === 'pnpm') { - spinner.text = 'Creating pnpm-workspace.yaml...'; + if ((mode === "apiMonorepo" || monorepo) && packageManager === "pnpm") { + spinner.text = "Creating pnpm-workspace.yaml..."; const pnpmWorkspaceContent = `packages:\n - 'apps/*'\n - 'plugins/*'\n`; - await writeFile(join(root, 'pnpm-workspace.yaml'), pnpmWorkspaceContent); + await writeFile(join(root, "pnpm-workspace.yaml"), pnpmWorkspaceContent); } if (docker) { - spinner.text = 'Copying docker files...'; + spinner.text = "Copying docker files..."; await copyFile( - join(templatePath, 'docker', 'docker-compose.yml'), - join(root, 'docker-compose.yml'), + join(templatePath, "docker", "docker-compose.yml"), + join(root, "docker-compose.yml"), ); // Update docker-compose.yml with app name - const dockerComposePath = join(root, 'docker-compose.yml'); - const dockerComposeContent = await readFile(dockerComposePath, 'utf-8'); + const dockerComposePath = join(root, "docker-compose.yml"); + const dockerComposeContent = await readFile(dockerComposePath, "utf-8"); const updatedContent = dockerComposeContent.replace( /vitnode_postgres_dev/g, `${appName}_vitnode_postgres_dev`, @@ -188,19 +188,19 @@ export const createVitNode = async ({ await writeFile(dockerComposePath, updatedContent); } - spinner.text = 'Updating VitNode paths...'; + spinner.text = "Updating VitNode paths..."; if ( - (mode === 'apiMonorepo' || monorepo) && - packageManager !== 'pnpm' && - mode !== 'onlyApi' + (mode === "apiMonorepo" || monorepo) && + packageManager !== "pnpm" && + mode !== "onlyApi" ) { const globalCssPath = join( monorepoStructure.web, - 'src', - 'app', - 'global.css', + "src", + "app", + "global.css", ); - const globalCssContent = await readFile(globalCssPath, 'utf-8'); + const globalCssContent = await readFile(globalCssPath, "utf-8"); const updatedGlobalCssContent = globalCssContent.replaceAll( '@source "../../node_modules/@vitnode/', '@source "../../../../node_modules/@vitnode/', @@ -208,33 +208,33 @@ export const createVitNode = async ({ await writeFile(globalCssPath, updatedGlobalCssContent); } - if (mode === 'apiMonorepo') { - spinner.text = 'Setting up environment variables...'; - const envExamplePath = join(monorepoStructure.web, '.env.example'); + if (mode === "apiMonorepo") { + spinner.text = "Setting up environment variables..."; + const envExamplePath = join(monorepoStructure.web, ".env.example"); if (existsSync(envExamplePath)) { - await rename(envExamplePath, join(monorepoStructure.web, '.env')); + await rename(envExamplePath, join(monorepoStructure.web, ".env")); } } if (install) { - spinner.text = 'Installing dependencies...'; + spinner.text = "Installing dependencies..."; await installDependencies({ packageManager, cwd: root, }); - spinner.text = 'Initializing VitNode files...'; - if (mode === 'apiMonorepo') { + spinner.text = "Initializing VitNode files..."; + if (mode === "apiMonorepo") { await Promise.all([ initFilesVitnode({ packageManager, cwd: monorepoStructure.web, - flag: 'web', + flag: "web", }), initFilesVitnode({ packageManager, cwd: monorepoStructure.api, - flag: 'api', + flag: "api", }), initFilesVitnode({ packageManager, @@ -248,11 +248,11 @@ export const createVitNode = async ({ }); } - spinner.text = 'Generating migrations...'; + spinner.text = "Generating migrations..."; let migrationsCwd: string; - if (mode === 'apiMonorepo' || (monorepo && mode !== 'singleApp')) { + if (mode === "apiMonorepo" || (monorepo && mode !== "singleApp")) { migrationsCwd = monorepoStructure.api; - } else if (mode === 'singleApp' && monorepo) { + } else if (mode === "singleApp" && monorepo) { migrationsCwd = monorepoStructure.web; } else { migrationsCwd = root; @@ -264,6 +264,6 @@ export const createVitNode = async ({ } spinner.succeed( - `${color.green('Success!')} Created ${color.cyan(appName)} at ${color.cyan(root)}`, + `${color.green("Success!")} Created ${color.cyan(appName)} at ${color.cyan(root)}`, ); }; diff --git a/packages/create-vitnode-app/src/helpers/get-available-package-managers.ts b/packages/create-vitnode-app/src/helpers/get-available-package-managers.ts index 416cc0a13..89d22a82e 100644 --- a/packages/create-vitnode-app/src/helpers/get-available-package-managers.ts +++ b/packages/create-vitnode-app/src/helpers/get-available-package-managers.ts @@ -1,6 +1,6 @@ -import { exec } from 'node:child_process'; +import { exec } from "node:child_process"; -export type PackageManager = 'bun' | 'npm' | 'pnpm'; +export type PackageManager = "bun" | "npm" | "pnpm"; export const execShellCommand = async ( cmd: string, @@ -12,7 +12,7 @@ export const execShellCommand = async ( } const result = stdout ? stdout : stderr; - resolve(result.replace(/\s+/g, '')); + resolve(result.replace(/\s+/g, "")); }); }); }; @@ -21,9 +21,9 @@ export const getAvailablePackageManagers = async (): Promise< Record > => { const [npm, pnpm, bun] = await Promise.all([ - execShellCommand('npm --version'), - execShellCommand('pnpm --version'), - execShellCommand('bun --version'), + execShellCommand("npm --version"), + execShellCommand("pnpm --version"), + execShellCommand("bun --version"), ]); return { diff --git a/packages/create-vitnode-app/src/helpers/get-package-json.ts b/packages/create-vitnode-app/src/helpers/get-package-json.ts index 2797efe43..78ea81a40 100644 --- a/packages/create-vitnode-app/src/helpers/get-package-json.ts +++ b/packages/create-vitnode-app/src/helpers/get-package-json.ts @@ -1,7 +1,7 @@ -import { readFileSync } from 'node:fs'; +import { readFileSync } from "node:fs"; -import type { PackageJSON } from './packages-json.js'; +import type { PackageJSON } from "./packages-json.js"; export const packageJson: PackageJSON = JSON.parse( - readFileSync(new URL('../../../package.json', import.meta.url), 'utf-8'), + readFileSync(new URL("../../../package.json", import.meta.url), "utf-8"), ); diff --git a/packages/create-vitnode-app/src/helpers/init-vitnode.ts b/packages/create-vitnode-app/src/helpers/init-vitnode.ts index ae6f48bae..a0844e7a0 100644 --- a/packages/create-vitnode-app/src/helpers/init-vitnode.ts +++ b/packages/create-vitnode-app/src/helpers/init-vitnode.ts @@ -1,20 +1,20 @@ -import { spawn } from 'node:child_process'; +import { spawn } from "node:child_process"; -import type { CreateCliReturn } from '../questions.js'; +import type { CreateCliReturn } from "../questions.js"; export const initFilesVitnode = ({ packageManager: pm, cwd, flag, -}: Pick & { +}: Pick & { cwd?: string; - flag?: 'api' | 'web'; + flag?: "api" | "web"; }) => { - const packageManager = pm.split('@')[0]; + const packageManager = pm.split("@")[0]; const args: string[] = [ - 'vitnode', - 'prepare-plugins', - flag ? `--${flag}` : '', + "vitnode", + "prepare-plugins", + flag ? `--${flag}` : "", ]; spawn(packageManager, args, { @@ -26,9 +26,9 @@ export const initFilesVitnode = ({ export const generateMigrationsVitnode = ({ packageManager: pm, cwd, -}: Pick & { cwd?: string }) => { - const packageManager = pm.split('@')[0]; - const args: string[] = ['vitnode', 'migrate', '--generate']; +}: Pick & { cwd?: string }) => { + const packageManager = pm.split("@")[0]; + const args: string[] = ["vitnode", "migrate", "--generate"]; spawn(packageManager, args, { cwd, diff --git a/packages/create-vitnode-app/src/helpers/install-dependencies.ts b/packages/create-vitnode-app/src/helpers/install-dependencies.ts index 7304f68fd..1e03da45f 100644 --- a/packages/create-vitnode-app/src/helpers/install-dependencies.ts +++ b/packages/create-vitnode-app/src/helpers/install-dependencies.ts @@ -1,40 +1,40 @@ /** biome-ignore-all lint/suspicious/noConsole: */ -import { spawn } from 'node:child_process'; -import color from 'picocolors'; +import { spawn } from "node:child_process"; +import color from "picocolors"; -import type { CreateCliReturn } from '../questions.js'; +import type { CreateCliReturn } from "../questions.js"; -import { getOnline } from './is-online.js'; +import { getOnline } from "./is-online.js"; function printInstallErrorSuggestions( stderr: string, - color: typeof import('picocolors'), + color: typeof import("picocolors"), ) { - if (stderr.includes('ENOTFOUND') || stderr.includes('network')) { + if (stderr.includes("ENOTFOUND") || stderr.includes("network")) { console.error( color.yellow( - '💡 Network error detected. Please check your internet connection.', + "💡 Network error detected. Please check your internet connection.", ), ); } else if ( - stderr.includes('EACCES') || - stderr.includes('permission denied') + stderr.includes("EACCES") || + stderr.includes("permission denied") ) { console.error( color.yellow( - '💡 Permission error detected. Try running with elevated privileges or check file permissions.', + "💡 Permission error detected. Try running with elevated privileges or check file permissions.", ), ); - } else if (stderr.includes('ENOSPC')) { + } else if (stderr.includes("ENOSPC")) { console.error( color.yellow( - '💡 Disk space error detected. Please free up some disk space.', + "💡 Disk space error detected. Please free up some disk space.", ), ); - } else if (stderr.includes('ERR_PNPM_PEER_DEP_ISSUES')) { + } else if (stderr.includes("ERR_PNPM_PEER_DEP_ISSUES")) { console.error( color.yellow( - '💡 Peer dependency issues detected. Consider using --force flag or resolve conflicts manually.', + "💡 Peer dependency issues detected. Consider using --force flag or resolve conflicts manually.", ), ); } @@ -43,69 +43,69 @@ function printInstallErrorSuggestions( export const installDependencies = async ({ packageManager: pm, cwd, -}: Pick & { cwd?: string }) => { - const packageManager = pm.split('@')[0]; +}: Pick & { cwd?: string }) => { + const packageManager = pm.split("@")[0]; const isOnline = await getOnline(); - const args: string[] = ['install']; + const args: string[] = ["install"]; if (!isOnline) { console.log( color.yellow( - 'You appear to be offline.\nFalling back to the local cache.', + "You appear to be offline.\nFalling back to the local cache.", ), ); - args.push('--offline'); + args.push("--offline"); } /** * Return a Promise that resolves once the installation is finished. */ return new Promise((resolve, reject) => { - let stdout = ''; - let stderr = ''; + let stdout = ""; + let stderr = ""; /** * Spawn the installation process. */ const child = spawn(packageManager, args, { - stdio: 'pipe', // Change to 'pipe' to capture output + stdio: "pipe", // Change to 'pipe' to capture output cwd, // Set the working directory shell: true, // Use shell to properly handle Windows batch files env: { ...process.env, - ADBLOCK: '1', + ADBLOCK: "1", // we set NODE_ENV to development as pnpm skips dev // dependencies when production - NODE_ENV: 'development', - DISABLE_OPENCOLLECTIVE: '1', + NODE_ENV: "development", + DISABLE_OPENCOLLECTIVE: "1", }, }); // Capture stdout - child.stdout?.on('data', (data: Buffer) => { + child.stdout?.on("data", (data: Buffer) => { const output = data.toString(); stdout += output; }); // Capture stderr - child.stderr?.on('data', (data: Buffer) => { + child.stderr?.on("data", (data: Buffer) => { const output = data.toString(); stderr += output; }); - child.on('close', code => { + child.on("close", code => { if (code !== 0) { console.error( color.red(`\n❌ Installation failed with exit code: ${code}`), ); if (stderr) { - console.error(color.red('Error output:')); + console.error(color.red("Error output:")); console.error(stderr); } if (stdout) { - console.log(color.yellow('Standard output:')); + console.log(color.yellow("Standard output:")); console.log(stdout); } @@ -125,7 +125,7 @@ export const installDependencies = async ({ }); // Handle process errors - child.on('error', error => { + child.on("error", error => { console.error( color.red(`❌ Failed to start ${packageManager}:`), error.message, diff --git a/packages/create-vitnode-app/src/helpers/is-folder-empty.ts b/packages/create-vitnode-app/src/helpers/is-folder-empty.ts index c7a4ecd63..d86210167 100644 --- a/packages/create-vitnode-app/src/helpers/is-folder-empty.ts +++ b/packages/create-vitnode-app/src/helpers/is-folder-empty.ts @@ -1,34 +1,34 @@ /** biome-ignore-all lint/suspicious/noConsole: */ -import { lstatSync, readdirSync } from 'node:fs'; -import { join } from 'node:path'; -import colors from 'picocolors'; +import { lstatSync, readdirSync } from "node:fs"; +import { join } from "node:path"; +import colors from "picocolors"; export function isFolderEmpty(root: string, name: string): boolean { const validFiles = [ - '.DS_Store', - '.git', - '.gitattributes', - '.gitignore', - '.gitlab-ci.yml', - '.hg', - '.hgcheck', - '.hgignore', - '.idea', - '.npmignore', - '.travis.yml', - 'LICENSE', - 'Thumbs.db', - 'docs', - 'mkdocs.yml', - 'npm-debug.log', - 'yarn-debug.log', - 'yarn-error.log', - 'yarnrc.yml', - '.yarn', + ".DS_Store", + ".git", + ".gitattributes", + ".gitignore", + ".gitlab-ci.yml", + ".hg", + ".hgcheck", + ".hgignore", + ".idea", + ".npmignore", + ".travis.yml", + "LICENSE", + "Thumbs.db", + "docs", + "mkdocs.yml", + "npm-debug.log", + "yarn-debug.log", + "yarn-error.log", + "yarnrc.yml", + ".yarn", ]; const conflicts = readdirSync(root).filter( - file => !(validFiles.includes(file) || file.endsWith('.iml')), + file => !(validFiles.includes(file) || file.endsWith(".iml")), ); if (conflicts.length > 0) { @@ -49,7 +49,7 @@ export function isFolderEmpty(root: string, name: string): boolean { } console.log( - '\nEither try using a new directory name, or remove the files listed above.\n', + "\nEither try using a new directory name, or remove the files listed above.\n", ); return false; diff --git a/packages/create-vitnode-app/src/helpers/is-online.ts b/packages/create-vitnode-app/src/helpers/is-online.ts index d73c56139..2a667c30f 100644 --- a/packages/create-vitnode-app/src/helpers/is-online.ts +++ b/packages/create-vitnode-app/src/helpers/is-online.ts @@ -1,6 +1,6 @@ -import { execSync } from 'node:child_process'; -import { lookup } from 'node:dns/promises'; -import { URL } from 'node:url'; +import { execSync } from "node:child_process"; +import { lookup } from "node:dns/promises"; +import { URL } from "node:url"; function getProxy(): string | undefined { if (process.env.https_proxy) { @@ -8,9 +8,9 @@ function getProxy(): string | undefined { } try { - const httpsProxy = execSync('npm config get https-proxy').toString().trim(); + const httpsProxy = execSync("npm config get https-proxy").toString().trim(); - return httpsProxy !== 'null' ? httpsProxy : undefined; + return httpsProxy !== "null" ? httpsProxy : undefined; } catch { return; } @@ -18,7 +18,7 @@ function getProxy(): string | undefined { export async function getOnline(): Promise { try { - await lookup('registry.yarnpkg.com'); + await lookup("registry.yarnpkg.com"); // If DNS lookup succeeds, we are online return true; diff --git a/packages/create-vitnode-app/src/helpers/is-writeable.ts b/packages/create-vitnode-app/src/helpers/is-writeable.ts index d587454c5..ed5b517ca 100644 --- a/packages/create-vitnode-app/src/helpers/is-writeable.ts +++ b/packages/create-vitnode-app/src/helpers/is-writeable.ts @@ -1,5 +1,5 @@ -import { W_OK } from 'node:constants'; -import { access } from 'node:fs/promises'; +import { W_OK } from "node:constants"; +import { access } from "node:fs/promises"; export async function isWriteable(directory: string): Promise { try { diff --git a/packages/create-vitnode-app/src/helpers/validate-pkg.ts b/packages/create-vitnode-app/src/helpers/validate-pkg.ts index 75e958484..b9e8f34c1 100644 --- a/packages/create-vitnode-app/src/helpers/validate-pkg.ts +++ b/packages/create-vitnode-app/src/helpers/validate-pkg.ts @@ -1,4 +1,4 @@ -import validateProjectName from 'validate-npm-package-name'; +import validateProjectName from "validate-npm-package-name"; type ValidateNpmNameResult = | { diff --git a/packages/create-vitnode-app/src/index.ts b/packages/create-vitnode-app/src/index.ts index 43a571d6a..9e636bc41 100644 --- a/packages/create-vitnode-app/src/index.ts +++ b/packages/create-vitnode-app/src/index.ts @@ -1,18 +1,18 @@ #!/usr/bin/env node /** biome-ignore-all lint/suspicious/noConsole: */ -import { basename, resolve } from 'node:path'; -import { input } from '@inquirer/prompts'; -import { Command, Option } from 'commander'; -import color from 'picocolors'; +import { basename, resolve } from "node:path"; +import { input } from "@inquirer/prompts"; +import { Command, Option } from "commander"; +import color from "picocolors"; -import { createVitNode } from './create/create-vitnode.js'; -import { packageJson } from './helpers/get-package-json.js'; -import { validateNpmName } from './helpers/validate-pkg.js'; -import { createPlugin } from './plugin/index.js'; -import { createQuestionsCli } from './questions.js'; -import { validationProject } from './validation.js'; +import { createVitNode } from "./create/create-vitnode.js"; +import { packageJson } from "./helpers/get-package-json.js"; +import { validateNpmName } from "./helpers/validate-pkg.js"; +import { createPlugin } from "./plugin/index.js"; +import { createQuestionsCli } from "./questions.js"; +import { validationProject } from "./validation.js"; -const [major] = process.versions.node.split('.').map(Number); +const [major] = process.versions.node.split(".").map(Number); if (major < 20) { console.error( color.red( @@ -22,48 +22,48 @@ if (major < 20) { process.exit(1); } -process.on('uncaughtException', (error: Error) => { - if (error.name === 'ExitPromptError') { - console.log(color.dim('👋 VitNode setup cancelled - see you next time!')); +process.on("uncaughtException", (error: Error) => { + if (error.name === "ExitPromptError") { + console.log(color.dim("👋 VitNode setup cancelled - see you next time!")); process.exit(0); } - console.error(color.red('An unexpected error occurred:')); + console.error(color.red("An unexpected error occurred:")); console.error(color.red(error.stack ?? error.message)); process.exit(1); }); const init = async () => { - let projectPath = ''; + let projectPath = ""; const program = new Command() - .version(packageJson.version ?? '0.1.0') - .argument('[project-directory]') - .usage(`${color.green('[project-directory]')} [options]`) + .version(packageJson.version ?? "0.1.0") + .argument("[project-directory]") + .usage(`${color.green("[project-directory]")} [options]`) .action(name => { projectPath = name; }); program.addOption( new Option( - '--package-manager ', - 'Specify the package manager to use', - ).choices(['npm', 'pnpm', 'bun']), + "--package-manager ", + "Specify the package manager to use", + ).choices(["npm", "pnpm", "bun"]), ); - program.option('--biome', 'Initialize with Biome config.'); + program.option("--biome", "Initialize with Biome config."); program.option( - '--skip-install', - 'Skip installing packages after initializing the project.', + "--skip-install", + "Skip installing packages after initializing the project.", ); - program.option('--plugin', 'Enable plugin mode.'); + program.option("--plugin", "Enable plugin mode."); program.addOption( new Option( - '--mode ', - 'What type of app do you want to create?', - ).choices(['singleApp', 'apiMonorepo', 'onlyApi']), + "--mode ", + "What type of app do you want to create?", + ).choices(["singleApp", "apiMonorepo", "onlyApi"]), ); - program.option('--monorepo', 'Create project with monorepo structure.'); - program.option('--docker', 'Initialize with Docker support.'); + program.option("--monorepo", "Create project with monorepo structure."); + program.option("--docker", "Initialize with Docker support."); program.parse(process.argv); @@ -76,8 +76,8 @@ const init = async () => { if (!projectPath) { projectPath = await input({ - message: 'What is your project named?', - default: 'my-vitnode', + message: "What is your project named?", + default: "my-vitnode", validate: (name: string) => { const validation = validateNpmName({ name: basename(resolve(name)) }); if (validation.valid) return true; diff --git a/packages/create-vitnode-app/src/plugin/index.ts b/packages/create-vitnode-app/src/plugin/index.ts index 5a9bc73ea..4d4349e0b 100644 --- a/packages/create-vitnode-app/src/plugin/index.ts +++ b/packages/create-vitnode-app/src/plugin/index.ts @@ -1,14 +1,14 @@ -import { basename, resolve } from 'node:path'; -import { input } from '@inquirer/prompts'; +import { basename, resolve } from "node:path"; +import { input } from "@inquirer/prompts"; -import { validateNpmName } from '../helpers/validate-pkg.js'; +import { validateNpmName } from "../helpers/validate-pkg.js"; export const createPlugin = async (projectPath: string) => { let name = projectPath; if (!name) { name = await input({ - message: 'What is your plugin named?', - default: 'my-vitnode-plugin', + message: "What is your plugin named?", + default: "my-vitnode-plugin", validate: (name: string) => { const validation = validateNpmName({ name: basename(resolve(name)) }); if (validation.valid) return true; diff --git a/packages/create-vitnode-app/src/prepare/prepare.ts b/packages/create-vitnode-app/src/prepare/prepare.ts index 46b1e05c9..198e55317 100644 --- a/packages/create-vitnode-app/src/prepare/prepare.ts +++ b/packages/create-vitnode-app/src/prepare/prepare.ts @@ -1,14 +1,14 @@ /** biome-ignore-all lint/suspicious/noConsole: */ -import { existsSync } from 'node:fs'; -import { mkdir } from 'node:fs/promises'; -import { join } from 'node:path'; +import { existsSync } from "node:fs"; +import { mkdir } from "node:fs/promises"; +import { join } from "node:path"; const prepare = async () => { - const toRootPath = join(process.cwd(), 'copy-of-vitnode-app'); + const toRootPath = join(process.cwd(), "copy-of-vitnode-app"); if (!existsSync(toRootPath)) { await mkdir(toRootPath); } - const fromRootPath = join(process.cwd(), '..', '..', 'apps', 'docs'); + const fromRootPath = join(process.cwd(), "..", "..", "apps", "docs"); if (!existsSync(fromRootPath)) { console.error( `\x1b[31mThe path ${fromRootPath} does not exist. Please check the directory structure.\x1b[0m`, diff --git a/packages/create-vitnode-app/src/questions.ts b/packages/create-vitnode-app/src/questions.ts index 6e874cde8..f4a566fc4 100644 --- a/packages/create-vitnode-app/src/questions.ts +++ b/packages/create-vitnode-app/src/questions.ts @@ -1,14 +1,14 @@ -import { confirm, select } from '@inquirer/prompts'; -import type { Command } from 'commander'; -import color from 'picocolors'; +import { confirm, select } from "@inquirer/prompts"; +import type { Command } from "commander"; +import color from "picocolors"; -import { getAvailablePackageManagers } from './helpers/get-available-package-managers.js'; +import { getAvailablePackageManagers } from "./helpers/get-available-package-managers.js"; export interface CreateCliReturn { docker?: boolean; biome: boolean; install: boolean; - mode: 'apiMonorepo' | 'onlyApi' | 'singleApp'; + mode: "apiMonorepo" | "onlyApi" | "singleApp"; monorepo?: boolean; packageManager: string; } @@ -29,21 +29,21 @@ export const createQuestionsCli = async ( if (!optionsFromProgram.packageManager) { const availablePackageManagers = await getAvailablePackageManagers(); options.packageManager = await select({ - message: `Which ${color.blue('package manager')} do you want to use?`, + message: `Which ${color.blue("package manager")} do you want to use?`, choices: [ { - name: `bun${availablePackageManagers.bun ? `@${availablePackageManagers.bun}` : ''}`, - value: 'bun', + name: `bun${availablePackageManagers.bun ? `@${availablePackageManagers.bun}` : ""}`, + value: "bun", disabled: !availablePackageManagers.bun, }, { - name: `pnpm${availablePackageManagers.pnpm ? `@${availablePackageManagers.pnpm}` : ''}`, - value: 'pnpm', + name: `pnpm${availablePackageManagers.pnpm ? `@${availablePackageManagers.pnpm}` : ""}`, + value: "pnpm", disabled: !availablePackageManagers.pnpm, }, { - name: `npm${availablePackageManagers.npm ? `@${availablePackageManagers.npm}` : ''}`, - value: 'npm', + name: `npm${availablePackageManagers.npm ? `@${availablePackageManagers.npm}` : ""}`, + value: "npm", disabled: !availablePackageManagers.npm, }, ], @@ -52,55 +52,55 @@ export const createQuestionsCli = async ( if (optionsFromProgram.mode === undefined) { options.mode = await select({ - message: `What type of ${color.blue('app')} do you want to create?`, + message: `What type of ${color.blue("app")} do you want to create?`, choices: [ { - name: `Single App - ${color.blue('Next.js')} & ${color.blue('Hono.js')}`, + name: `Single App - ${color.blue("Next.js")} & ${color.blue("Hono.js")}`, description: - 'Create a single app with Next.js and Hono.js in the same project.', - value: 'singleApp', + "Create a single app with Next.js and Hono.js in the same project.", + value: "singleApp", }, { - name: `Monorepo App - ${color.blue('Next.js')} & ${color.blue('Hono.js')}`, + name: `Monorepo App - ${color.blue("Next.js")} & ${color.blue("Hono.js")}`, description: - 'Create a monorepo with both Next.js and Hono.js apps separately.', - value: 'apiMonorepo', + "Create a monorepo with both Next.js and Hono.js apps separately.", + value: "apiMonorepo", }, { - name: `Only API - ${color.blue('Hono.js')}`, - description: 'Create only an API app using Hono.js without Next.js.', - value: 'onlyApi', + name: `Only API - ${color.blue("Hono.js")}`, + description: "Create only an API app using Hono.js without Next.js.", + value: "onlyApi", }, ], - default: 'singleApp', + default: "singleApp", }); } if ( optionsFromProgram.monorepo === undefined && - options.mode !== 'apiMonorepo' + options.mode !== "apiMonorepo" ) { options.monorepo = await confirm({ - message: `Would you like to use ${color.blue('TurboRepo')} for monorepo management? ${color.red('(Required for plugins development)')}`, + message: `Would you like to use ${color.blue("TurboRepo")} for monorepo management? ${color.red("(Required for plugins development)")}`, default: false, }); } if (optionsFromProgram.biome === undefined) { options.biome = await confirm({ - message: `Would you like to use ${color.blue('Biome')}?`, + message: `Would you like to use ${color.blue("Biome")}?`, }); } if (optionsFromProgram.docker === undefined) { options.docker = await confirm({ - message: `Would you like to use ${color.blue('Docker Container')}?`, + message: `Would you like to use ${color.blue("Docker Container")}?`, }); } if (optionsFromProgram.skipInstall === undefined) { options.install = await confirm({ - message: `Would you like to ${color.blue('Install dependencies')}?`, + message: `Would you like to ${color.blue("Install dependencies")}?`, }); } diff --git a/packages/create-vitnode-app/src/validation.ts b/packages/create-vitnode-app/src/validation.ts index 9410bff7c..dc18256d0 100644 --- a/packages/create-vitnode-app/src/validation.ts +++ b/packages/create-vitnode-app/src/validation.ts @@ -1,21 +1,21 @@ /** biome-ignore-all lint/suspicious/noConsole: */ -import { existsSync } from 'node:fs'; -import { basename, dirname, resolve } from 'node:path'; -import { program } from 'commander'; -import color from 'picocolors'; +import { existsSync } from "node:fs"; +import { basename, dirname, resolve } from "node:path"; +import { program } from "commander"; +import color from "picocolors"; -import { isFolderEmpty } from './helpers/is-folder-empty.js'; -import { isWriteable } from './helpers/is-writeable.js'; -import { validateNpmName } from './helpers/validate-pkg.js'; +import { isFolderEmpty } from "./helpers/is-folder-empty.js"; +import { isWriteable } from "./helpers/is-writeable.js"; +import { validateNpmName } from "./helpers/validate-pkg.js"; export const validationProject = async (projectPath: string) => { // Verify the project path is provided if (!projectPath) { console.log( - '\nPlease specify the project directory:\n' + - ` ${color.cyan(program.name())} ${color.green('')}\n` + - 'For example:\n' + - ` ${color.cyan(program.name())} ${color.green('my-vitnode-app')}\n\n` + + "\nPlease specify the project directory:\n" + + ` ${color.cyan(program.name())} ${color.green("")}\n` + + "For example:\n" + + ` ${color.cyan(program.name())} ${color.green("my-vitnode-app")}\n\n` + `Run ${color.cyan(`${program.name()} --help`)} to see all options.`, ); process.exit(1); @@ -33,7 +33,7 @@ export const validationProject = async (projectPath: string) => { ); validation.problems.forEach(p => { - console.error(`${color.red(color.bold('*'))} ${p}`); + console.error(`${color.red(color.bold("*"))} ${p}`); }); process.exit(1); } @@ -43,17 +43,17 @@ export const validationProject = async (projectPath: string) => { const appName = basename(root); const folderExists = existsSync(root); if (folderExists && !isFolderEmpty(root, appName)) { - console.error('The specified directory is not empty.'); + console.error("The specified directory is not empty."); process.exit(1); } // Verify the project dir is writeable if (!(await isWriteable(dirname(root)))) { console.error( - 'The application path is not writable, please check folder permissions and try again.', + "The application path is not writable, please check folder permissions and try again.", ); console.error( - 'It is likely you do not have write permissions for this folder.', + "It is likely you do not have write permissions for this folder.", ); process.exit(1); } diff --git a/packages/vitnode/config/next.config.ts b/packages/vitnode/config/next.config.ts index 5514ca669..a2be43106 100644 --- a/packages/vitnode/config/next.config.ts +++ b/packages/vitnode/config/next.config.ts @@ -1,7 +1,7 @@ -import type { NextConfig } from 'next'; -import createNextIntlPlugin from 'next-intl/plugin'; +import type { NextConfig } from "next"; +import createNextIntlPlugin from "next-intl/plugin"; -const withNextIntl = createNextIntlPlugin('./src/vitnode.config.ts'); +const withNextIntl = createNextIntlPlugin("./src/vitnode.config.ts"); export const vitNodeNextConfig = (config: NextConfig): NextConfig => withNextIntl({ diff --git a/packages/vitnode/global.d.ts b/packages/vitnode/global.d.ts index d7f578cf3..d829e1bdb 100644 --- a/packages/vitnode/global.d.ts +++ b/packages/vitnode/global.d.ts @@ -1,8 +1,8 @@ /// -import plugin from './src/locales/en.json' with { type: 'json' }; +import plugin from "./src/locales/en.json" with { type: "json" }; -declare module 'next-intl' { +declare module "next-intl" { interface AppConfig { Messages: typeof plugin; } diff --git a/packages/vitnode/scripts/get-config.ts b/packages/vitnode/scripts/get-config.ts index 48bd0fb0b..04fa005f5 100644 --- a/packages/vitnode/scripts/get-config.ts +++ b/packages/vitnode/scripts/get-config.ts @@ -1,28 +1,28 @@ -import { join } from 'node:path'; -import { pathToFileURL } from 'node:url'; +import { join } from "node:path"; +import { pathToFileURL } from "node:url"; -import type { VitNodeApiConfig, VitNodeConfig } from '../src/vitnode.config'; +import type { VitNodeApiConfig, VitNodeConfig } from "../src/vitnode.config"; -type ConfigType = T extends 'config' +type ConfigType = T extends "config" ? VitNodeConfig : VitNodeApiConfig; -export const getConfig = async ({ - type = 'config' as T, +export const getConfig = async ({ + type = "config" as T, }: { type?: T; }): Promise> => { - const configPath = join(process.cwd(), 'src', `vitnode.${type}.ts`); + const configPath = join(process.cwd(), "src", `vitnode.${type}.ts`); try { const configUrl = pathToFileURL(configPath).href; const loaded = await import(configUrl); const config = - type === 'config' ? loaded.vitNodeConfig : loaded.vitNodeApiConfig; + type === "config" ? loaded.vitNodeConfig : loaded.vitNodeApiConfig; return config as ConfigType; } catch (error) { // biome-ignore lint/suspicious/noConsole: - console.error('Failed to load config:', error); + console.error("Failed to load config:", error); process.exit(1); } }; diff --git a/packages/vitnode/scripts/plugin.ts b/packages/vitnode/scripts/plugin.ts index 463a82077..bf7c88662 100644 --- a/packages/vitnode/scripts/plugin.ts +++ b/packages/vitnode/scripts/plugin.ts @@ -5,9 +5,9 @@ import { readdirSync, readFileSync, unlinkSync, -} from 'node:fs'; -import { basename, join, relative } from 'node:path'; -import chokidar from 'chokidar'; +} from "node:fs"; +import { basename, join, relative } from "node:path"; +import chokidar from "chokidar"; import { buildInitialRouteMap, @@ -18,19 +18,19 @@ import { isDirectoryEmpty, routeKey, type SourceConfig, -} from './shared/file-utils'; +} from "./shared/file-utils"; /** * Helper: detect if an app path is web, api, or null */ const detectAppType = (appPath: string) => { - const hasWebConfig = existsSync(join(appPath, 'src', 'vitnode.config.ts')); + const hasWebConfig = existsSync(join(appPath, "src", "vitnode.config.ts")); const hasApiConfig = existsSync( - join(appPath, 'src', 'vitnode.api.config.ts'), + join(appPath, "src", "vitnode.api.config.ts"), ); - if (hasApiConfig && !hasWebConfig) return 'api'; - if (hasWebConfig) return 'web'; + if (hasApiConfig && !hasWebConfig) return "api"; + if (hasWebConfig) return "web"; return null; }; @@ -45,7 +45,7 @@ const collectSources = ( // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: ): SourceConfig[] => { const sources: SourceConfig[] = []; - const appsDir = join(repoRoot, 'apps'); + const appsDir = join(repoRoot, "apps"); const isMonorepo = existsSync(appsDir); if (isMonorepo) { @@ -56,43 +56,43 @@ const collectSources = ( : []; for (const appName of appDirs) { - const appPath = join(repoRoot, 'apps', appName); + const appPath = join(repoRoot, "apps", appName); const appType = detectAppType(appPath); - if (appType === 'web') { + if (appType === "web") { sources.push( { - sourceDir: join(pluginDir, 'src', 'app_admin'), + sourceDir: join(pluginDir, "src", "app_admin"), destinationDir: join( appPath, - 'src', - 'app', - '[locale]', - 'admin', - '(auth)', - join('(plugins)', `(${pluginPathName})`), + "src", + "app", + "[locale]", + "admin", + "(auth)", + join("(plugins)", `(${pluginPathName})`), ), }, { - sourceDir: join(pluginDir, 'src', 'app'), + sourceDir: join(pluginDir, "src", "app"), destinationDir: join( appPath, - 'src', - 'app', - '[locale]', - '(main)', - join('(plugins)', `(${pluginPathName})`), + "src", + "app", + "[locale]", + "(main)", + join("(plugins)", `(${pluginPathName})`), ), }, { - sourceDir: join(pluginDir, 'src', 'locales'), - destinationDir: join(appPath, 'src', 'locales', pluginName), + sourceDir: join(pluginDir, "src", "locales"), + destinationDir: join(appPath, "src", "locales", pluginName), }, ); - } else if (appType === 'api') { + } else if (appType === "api") { sources.push({ - sourceDir: join(pluginDir, 'src', 'locales'), - destinationDir: join(appPath, 'src', 'locales', pluginName), + sourceDir: join(pluginDir, "src", "locales"), + destinationDir: join(appPath, "src", "locales", pluginName), }); } } @@ -100,40 +100,40 @@ const collectSources = ( const cwd = process.cwd(); const projectType = detectAppType(cwd); - if (projectType === 'web') { + if (projectType === "web") { sources.push( { - sourceDir: join(pluginDir, 'src', 'app_admin'), + sourceDir: join(pluginDir, "src", "app_admin"), destinationDir: join( cwd, - 'src', - 'app', - '[locale]', - 'admin', - '(auth)', - join('(plugins)', `(${pluginPathName})`), + "src", + "app", + "[locale]", + "admin", + "(auth)", + join("(plugins)", `(${pluginPathName})`), ), }, { - sourceDir: join(pluginDir, 'src', 'app'), + sourceDir: join(pluginDir, "src", "app"), destinationDir: join( cwd, - 'src', - 'app', - '[locale]', - '(main)', - join('(plugins)', `(${pluginPathName})`), + "src", + "app", + "[locale]", + "(main)", + join("(plugins)", `(${pluginPathName})`), ), }, { - sourceDir: join(pluginDir, 'src', 'locales'), - destinationDir: join(cwd, 'src', 'locales', pluginName), + sourceDir: join(pluginDir, "src", "locales"), + destinationDir: join(cwd, "src", "locales", pluginName), }, ); - } else if (projectType === 'api') { + } else if (projectType === "api") { sources.push({ - sourceDir: join(pluginDir, 'src', 'locales'), - destinationDir: join(cwd, 'src', 'locales', pluginName), + sourceDir: join(pluginDir, "src", "locales"), + destinationDir: join(cwd, "src", "locales", pluginName), }); } } @@ -200,7 +200,7 @@ const cleanupDeletedFiles = ( ) => { if (!existsSync(destinationDir)) return; - const isLocaleDir = destinationDir.includes(join('src', 'locales')); + const isLocaleDir = destinationDir.includes(join("src", "locales")); if (isLocaleDir) return; const destFiles = getAllFiles(destinationDir); @@ -220,8 +220,8 @@ const cleanupDeletedFiles = ( const makeGetDestinationPaths = (sources: SourceConfig[]) => { return (srcPath: string): string[] => { const candidates = sources.filter(({ sourceDir }) => { - const normalizedSrcPath = srcPath.replace(/\\/g, '/'); - const normalizedSourceDir = sourceDir.replace(/\\/g, '/'); + const normalizedSrcPath = srcPath.replace(/\\/g, "/"); + const normalizedSourceDir = sourceDir.replace(/\\/g, "/"); return ( normalizedSrcPath === normalizedSourceDir || @@ -255,20 +255,20 @@ const setupWatcher = ( }); watcher - .on('add', filePath => { + .on("add", filePath => { const destPaths = getDestinationPaths(filePath); destPaths.forEach(destPath => copyFileWrapper(filePath, destPath)); }) - .on('change', filePath => { + .on("change", filePath => { const destPaths = getDestinationPaths(filePath); destPaths.forEach(destPath => copyFileWrapper(filePath, destPath)); }) - .on('unlink', filePath => { + .on("unlink", filePath => { const destPaths = getDestinationPaths(filePath); destPaths.forEach(destPath => removeFile(destPath)); }) - .on('error', error => { - console.error('\x1b[31mWatcher error:\x1b[0m', error); + .on("error", error => { + console.error("\x1b[31mWatcher error:\x1b[0m", error); }); return watcher; @@ -285,17 +285,17 @@ export const processPlugin = ({ initMessage }: { initMessage: string }) => { let pluginName = basename(pluginDir); try { - const packageJsonPath = join(pluginDir, 'package.json'); + const packageJsonPath = join(pluginDir, "package.json"); if (existsSync(packageJsonPath)) { - const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')); - pluginName = packageJson.name ?? ''; + const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8")); + pluginName = packageJson.name ?? ""; } } catch (error) { - console.error('\x1b[31mError reading package.json:\x1b[0m', error); + console.error("\x1b[31mError reading package.json:\x1b[0m", error); return; } - const pluginPathName = pluginName.replace(/\//g, '-').replace(/@/g, ''); + const pluginPathName = pluginName.replace(/\//g, "-").replace(/@/g, ""); const sources = collectSources( pluginDir, diff --git a/packages/vitnode/scripts/prepare-database.ts b/packages/vitnode/scripts/prepare-database.ts index ffbe8195d..721c119d8 100644 --- a/packages/vitnode/scripts/prepare-database.ts +++ b/packages/vitnode/scripts/prepare-database.ts @@ -1,45 +1,45 @@ /** biome-ignore-all lint/suspicious/noConsole: */ -import { count } from 'drizzle-orm'; +import { count } from "drizzle-orm"; -import { core_admin_permissions } from '@/database/admins.js'; -import { core_languages, core_languages_words } from '@/database/languages.js'; -import { core_moderators_permissions } from '@/database/moderators.js'; -import { core_roles } from '@/database/roles.js'; +import { core_admin_permissions } from "@/database/admins.js"; +import { core_languages, core_languages_words } from "@/database/languages.js"; +import { core_moderators_permissions } from "@/database/moderators.js"; +import { core_roles } from "@/database/roles.js"; -import { getConfig } from './get-config.js'; -import { preparePluginsFiles } from './prepare-plugins-files.js'; -import { runInteractiveShellCommand } from './run-interactive-shell-command.js'; +import { getConfig } from "./get-config.js"; +import { preparePluginsFiles } from "./prepare-plugins-files.js"; +import { runInteractiveShellCommand } from "./run-interactive-shell-command.js"; export const generateDatabaseMigrations = async () => { try { - await runInteractiveShellCommand('npm', ['run', 'drizzle-kit', 'up']); - await runInteractiveShellCommand('npm', ['run', 'drizzle-kit', 'generate']); + await runInteractiveShellCommand("npm", ["run", "drizzle-kit", "up"]); + await runInteractiveShellCommand("npm", ["run", "drizzle-kit", "generate"]); } catch (err) { - console.error('\x1b[31m%s\x1b[0m', err); + console.error("\x1b[31m%s\x1b[0m", err); process.exit(1); } }; export const runMigrations = async () => { try { - await runInteractiveShellCommand('npm', ['run', 'drizzle-kit', 'migrate']); + await runInteractiveShellCommand("npm", ["run", "drizzle-kit", "migrate"]); } catch (err) { - console.error('\x1b[31m%s\x1b[0m', err); + console.error("\x1b[31m%s\x1b[0m", err); process.exit(1); } }; export const runPush = async () => { try { - await runInteractiveShellCommand('npm', ['run', 'drizzle-kit', 'push']); + await runInteractiveShellCommand("npm", ["run", "drizzle-kit", "push"]); } catch (err) { - console.error('\x1b[31m%s\x1b[0m', err); + console.error("\x1b[31m%s\x1b[0m", err); process.exit(1); } }; export const initialDataForDatabase = async () => { - const config = await getConfig({ type: 'api.config' }); + const config = await getConfig({ type: "api.config" }); const dbClient = config.dbProvider; const [roleCount] = await dbClient @@ -57,11 +57,11 @@ export const initialDataForDatabase = async () => { if (languageCount.count === 0) { await dbClient.insert(core_languages).values([ { - code: 'en', - name: 'English (USA)', + code: "en", + name: "English (USA)", default: true, protected: true, - timezone: 'America/New_York', + timezone: "America/New_York", }, ]); } @@ -83,13 +83,13 @@ export const initialDataForDatabase = async () => { { // Moderator role protected: true, - color: 'hsl(122, 80%, 45%)', + color: "hsl(122, 80%, 45%)", }, { // Administrator role protected: true, root: true, - color: 'hsl(0, 100%, 50%)', + color: "hsl(0, 100%, 50%)", }, ]) .returning({ id: core_roles.id }); @@ -97,39 +97,39 @@ export const initialDataForDatabase = async () => { await dbClient.insert(core_languages_words).values([ { // Guest role - languageCode: 'en', - pluginCode: 'core', + languageCode: "en", + pluginCode: "core", itemId: roles[0].id, - value: 'Guest', - tableName: 'core_roles', - variable: 'name', + value: "Guest", + tableName: "core_roles", + variable: "name", }, { // Member role - languageCode: 'en', - pluginCode: 'core', + languageCode: "en", + pluginCode: "core", itemId: roles[1].id, - value: 'Member', - tableName: 'core_roles', - variable: 'name', + value: "Member", + tableName: "core_roles", + variable: "name", }, { // Moderator role - languageCode: 'en', - pluginCode: 'core', + languageCode: "en", + pluginCode: "core", itemId: roles[2].id, - value: 'Moderator', - tableName: 'core_roles', - variable: 'name', + value: "Moderator", + tableName: "core_roles", + variable: "name", }, { // Administrator role - languageCode: 'en', - pluginCode: 'core', + languageCode: "en", + pluginCode: "core", itemId: roles[3].id, - value: 'Administrator', - tableName: 'core_roles', - variable: 'name', + value: "Administrator", + tableName: "core_roles", + variable: "name", }, ]); @@ -156,46 +156,46 @@ export const prepareDatabase = async ({ }) => { const steps: { action: () => Promise; label: string }[] = []; - if (flag === '--web') { + if (flag === "--web") { steps.push({ - label: 'Prepare plugins files...', + label: "Prepare plugins files...", action: async () => await preparePluginsFiles(flag), }); - } else if (flag === '--api') { + } else if (flag === "--api") { steps.push( { - label: 'Prepare plugins files...', + label: "Prepare plugins files...", action: async () => await preparePluginsFiles(flag), }, { - label: 'Generate migrations...', + label: "Generate migrations...", action: generateDatabaseMigrations, }, { - label: 'Run migrations...', + label: "Run migrations...", action: runMigrations, }, { - label: 'Insert initial data...', + label: "Insert initial data...", action: initialDataForDatabase, }, ); } else { steps.push( { - label: 'Prepare plugins files...', + label: "Prepare plugins files...", action: async () => await preparePluginsFiles(flag), }, { - label: 'Generate migrations...', + label: "Generate migrations...", action: generateDatabaseMigrations, }, { - label: 'Run migrations...', + label: "Run migrations...", action: runMigrations, }, { - label: 'Insert initial data...', + label: "Insert initial data...", action: initialDataForDatabase, }, ); @@ -204,7 +204,7 @@ export const prepareDatabase = async ({ await Promise.all( steps.map((step, i) => { const stepNum = `[${i + 1}/${steps.length}]`; - if (step.label === 'Insert initial data...') { + if (step.label === "Insert initial data...") { console.log(`\n${initMessage} ${stepNum} ${step.label}`); } else { console.log(`${initMessage} ${stepNum} ${step.label}`); diff --git a/packages/vitnode/scripts/prepare-plugins-files.ts b/packages/vitnode/scripts/prepare-plugins-files.ts index 48457749e..8d53d6368 100644 --- a/packages/vitnode/scripts/prepare-plugins-files.ts +++ b/packages/vitnode/scripts/prepare-plugins-files.ts @@ -1,9 +1,9 @@ /** biome-ignore-all lint/suspicious/noConsole: */ -import { existsSync, readdirSync } from 'node:fs'; -import { readFile } from 'node:fs/promises'; -import { join, relative } from 'node:path'; +import { existsSync, readdirSync } from "node:fs"; +import { readFile } from "node:fs/promises"; +import { join, relative } from "node:path"; -import { getConfig } from './get-config'; +import { getConfig } from "./get-config"; import { buildInitialRouteMap, copyDirectoryRecursive, @@ -11,25 +11,25 @@ import { findRepoRoot, isDirectoryEmpty, type SourceConfig, -} from './shared/file-utils'; +} from "./shared/file-utils"; export const preparePluginsFiles = async (flag?: string) => { // Detect which config file to load based on flag or auto-detection const cwd = process.cwd(); - const hasWebConfig = existsSync(join(cwd, 'src', 'vitnode.config.ts')); - const hasApiConfig = existsSync(join(cwd, 'src', 'vitnode.api.config.ts')); + const hasWebConfig = existsSync(join(cwd, "src", "vitnode.config.ts")); + const hasApiConfig = existsSync(join(cwd, "src", "vitnode.api.config.ts")); let config: { plugins: { pluginId: string }[] }; - if (flag === '--api' && hasApiConfig) { + if (flag === "--api" && hasApiConfig) { // Force API config when --api flag is used - config = await getConfig({ type: 'api.config' }); - } else if (flag === '--web' && hasWebConfig) { + config = await getConfig({ type: "api.config" }); + } else if (flag === "--web" && hasWebConfig) { // Force web config when --web flag is used config = await getConfig({}); } else if (hasApiConfig && !hasWebConfig) { // API config only (auto-detect) - config = await getConfig({ type: 'api.config' }); + config = await getConfig({ type: "api.config" }); } else if (hasWebConfig) { // Web config (may also have API config but web takes precedence in auto-detect) config = await getConfig({}); @@ -40,7 +40,7 @@ export const preparePluginsFiles = async (flag?: string) => { const plugins: string[] = [ ...config.plugins.map(plugin => plugin.pluginId), - '@vitnode/core', + "@vitnode/core", ]; const repoRoot = findRepoRoot(process.cwd()); @@ -54,14 +54,14 @@ export const preparePluginsFiles = async (flag?: string) => { const cwd = process.cwd(); // Check in current working directory first - const cwdPluginPath = join(cwd, 'node_modules', pluginName); + const cwdPluginPath = join(cwd, "node_modules", pluginName); if (existsSync(cwdPluginPath)) { return cwdPluginPath; } // Check in monorepo root if it exists and is different from cwd if (repoRoot && repoRoot !== cwd) { - const rootPluginPath = join(repoRoot, 'node_modules', pluginName); + const rootPluginPath = join(repoRoot, "node_modules", pluginName); if (existsSync(rootPluginPath)) { return rootPluginPath; } @@ -84,14 +84,14 @@ export const preparePluginsFiles = async (flag?: string) => { } // Get the package name from package.json for imports - let packageName = ''; + let packageName = ""; try { - const packageJsonPath = join(pluginPath, 'package.json'); + const packageJsonPath = join(pluginPath, "package.json"); if (existsSync(packageJsonPath)) { const packageJson = JSON.parse( - await readFile(packageJsonPath, 'utf-8'), + await readFile(packageJsonPath, "utf-8"), ); - packageName = packageJson.name ?? ''; + packageName = packageJson.name ?? ""; } } catch (error) { console.error( @@ -103,25 +103,25 @@ export const preparePluginsFiles = async (flag?: string) => { } // Transform plugin name for path usage - const pluginPathName = pluginName.replace(/\//g, '-').replace(/@/g, ''); + const pluginPathName = pluginName.replace(/\//g, "-").replace(/@/g, ""); // Detect app types by checking for config files const detectAppType = (appPath: string) => { const hasWebConfig = existsSync( - join(appPath, 'src', 'vitnode.config.ts'), + join(appPath, "src", "vitnode.config.ts"), ); const hasApiConfig = existsSync( - join(appPath, 'src', 'vitnode.api.config.ts'), + join(appPath, "src", "vitnode.api.config.ts"), ); - if (hasApiConfig && !hasWebConfig) return 'api'; - if (hasWebConfig) return 'web'; + if (hasApiConfig && !hasWebConfig) return "api"; + if (hasWebConfig) return "web"; return null; }; // Check if we're in a monorepo by looking for apps directories - const appsDir = join(repoRoot, 'apps'); + const appsDir = join(repoRoot, "apps"); const isMonorepo = existsSync(appsDir); // Define source configurations for this plugin @@ -136,50 +136,50 @@ export const preparePluginsFiles = async (flag?: string) => { : []; for (const appName of appDirs) { - const appPath = join(repoRoot, 'apps', appName); + const appPath = join(repoRoot, "apps", appName); const appType = detectAppType(appPath); - if (appType === 'web') { + if (appType === "web") { // Web app: copy app, app_admin, and locales const mainDest = join( appPath, - 'src', - 'app', - '[locale]', - '(main)', - join('(plugins)', `(${pluginPathName})`), + "src", + "app", + "[locale]", + "(main)", + join("(plugins)", `(${pluginPathName})`), ); const adminDest = join( appPath, - 'src', - 'app', - '[locale]', - 'admin', - '(auth)', - join('(plugins)', `(${pluginPathName})`), + "src", + "app", + "[locale]", + "admin", + "(auth)", + join("(plugins)", `(${pluginPathName})`), ); - const langDest = join(appPath, 'src', 'locales', pluginName); + const langDest = join(appPath, "src", "locales", pluginName); sources.push( { - sourceDir: join(pluginPath, 'src', 'app_admin'), + sourceDir: join(pluginPath, "src", "app_admin"), destinationDir: adminDest, }, { - sourceDir: join(pluginPath, 'src', 'app'), + sourceDir: join(pluginPath, "src", "app"), destinationDir: mainDest, }, { - sourceDir: join(pluginPath, 'src', 'locales'), + sourceDir: join(pluginPath, "src", "locales"), destinationDir: langDest, }, ); - } else if (appType === 'api') { + } else if (appType === "api") { // API app: copy only locales - const apiLangDest = join(appPath, 'src', 'locales', pluginName); + const apiLangDest = join(appPath, "src", "locales", pluginName); sources.push({ - sourceDir: join(pluginPath, 'src', 'locales'), + sourceDir: join(pluginPath, "src", "locales"), destinationDir: apiLangDest, }); } @@ -188,34 +188,34 @@ export const preparePluginsFiles = async (flag?: string) => { // Standalone project: use current directory as base const mainDest = join( baseDir, - 'src', - 'app', - '[locale]', - '(main)', - join('(plugins)', `(${pluginPathName})`), + "src", + "app", + "[locale]", + "(main)", + join("(plugins)", `(${pluginPathName})`), ); const adminDest = join( baseDir, - 'src', - 'app', - '[locale]', - 'admin', - '(auth)', - join('(plugins)', `(${pluginPathName})`), + "src", + "app", + "[locale]", + "admin", + "(auth)", + join("(plugins)", `(${pluginPathName})`), ); - const langDest = join(baseDir, 'src', 'locales', pluginName); + const langDest = join(baseDir, "src", "locales", pluginName); sources.push( { - sourceDir: join(pluginPath, 'src', 'app_admin'), + sourceDir: join(pluginPath, "src", "app_admin"), destinationDir: adminDest, }, { - sourceDir: join(pluginPath, 'src', 'app'), + sourceDir: join(pluginPath, "src", "app"), destinationDir: mainDest, }, { - sourceDir: join(pluginPath, 'src', 'locales'), + sourceDir: join(pluginPath, "src", "locales"), destinationDir: langDest, }, ); diff --git a/packages/vitnode/scripts/run-interactive-shell-command.ts b/packages/vitnode/scripts/run-interactive-shell-command.ts index 5c36f5672..185bf07dc 100644 --- a/packages/vitnode/scripts/run-interactive-shell-command.ts +++ b/packages/vitnode/scripts/run-interactive-shell-command.ts @@ -1,4 +1,4 @@ -import { spawn } from 'node:child_process'; +import { spawn } from "node:child_process"; export const runInteractiveShellCommand = async ( cmd: string, @@ -6,16 +6,16 @@ export const runInteractiveShellCommand = async ( ) => { return await new Promise((resolve, reject) => { const child = spawn(cmd, args, { - stdio: 'inherit', + stdio: "inherit", shell: true, env: process.env, }); - child.on('error', error => { + child.on("error", error => { reject(error); }); - child.on('close', code => { + child.on("close", code => { if (code !== 0) { reject(new Error(`Command failed with exit code ${code}`)); } else { diff --git a/packages/vitnode/scripts/scripts.ts b/packages/vitnode/scripts/scripts.ts index cea35f2e1..78e6537b8 100644 --- a/packages/vitnode/scripts/scripts.ts +++ b/packages/vitnode/scripts/scripts.ts @@ -1,35 +1,35 @@ #!/usr/bin/env node /** biome-ignore-all lint/suspicious/noConsole: */ -import { config } from 'dotenv'; +import { config } from "dotenv"; -import { processPlugin } from './plugin.js'; +import { processPlugin } from "./plugin.js"; import { generateDatabaseMigrations, initialDataForDatabase, prepareDatabase, runMigrations, runPush, -} from './prepare-database.js'; -import { preparePluginsFiles } from './prepare-plugins-files.js'; +} from "./prepare-database.js"; +import { preparePluginsFiles } from "./prepare-plugins-files.js"; config({ quiet: true, }); -const initMessage = '\x1b[34m[VitNode]\x1b[0m'; +const initMessage = "\x1b[34m[VitNode]\x1b[0m"; const command = process.argv[2]; const flag = process.argv[3]; switch (command) { - case 'init': + case "init": void prepareDatabase({ initMessage, flag }); break; - case 'migrate': + case "migrate": await generateDatabaseMigrations(); - if (flag === '--generate') { + if (flag === "--generate") { console.log( `${initMessage} \x1b[32mDatabase migrations generated successfully.\x1b[0m`, ); @@ -44,20 +44,20 @@ switch (command) { process.exit(0); break; - case 'plugin': - if (flag === '--w' || flag === '--watch') { + case "plugin": + if (flag === "--w" || flag === "--watch") { processPlugin({ initMessage }); } break; - case 'prepare-plugins': + case "prepare-plugins": await preparePluginsFiles(flag); console.log(`${initMessage} \x1b[32mPlugins prepared successfully.\x1b[0m`); process.exit(0); break; - case 'push': + case "push": await runPush(); await initialDataForDatabase(); @@ -67,7 +67,7 @@ switch (command) { default: console.log( - `${initMessage} \x1b[31mCommand not found: "${command ?? ''}"\x1b[0m`, + `${initMessage} \x1b[31mCommand not found: "${command ?? ""}"\x1b[0m`, ); process.exit(1); } diff --git a/packages/vitnode/scripts/shared/file-utils.ts b/packages/vitnode/scripts/shared/file-utils.ts index 9503406c5..8b4a7b414 100644 --- a/packages/vitnode/scripts/shared/file-utils.ts +++ b/packages/vitnode/scripts/shared/file-utils.ts @@ -6,7 +6,7 @@ import { readdirSync, readFileSync, writeFileSync, -} from 'node:fs'; +} from "node:fs"; import { basename, dirname, @@ -16,7 +16,7 @@ import { relative, resolve, sep, -} from 'node:path'; +} from "node:path"; // Regex patterns for import statements const relativeImportRegex = @@ -35,10 +35,10 @@ export const routeKey = (filePath: string, localeRoot: string): string => { parts.pop(); // drop any group folders - const filtered = parts.filter(p => !p.startsWith('(')); + const filtered = parts.filter(p => !p.startsWith("(")); // '' represents the root route - return normalize(filtered.join('/')); + return normalize(filtered.join("/")); }; export const buildInitialRouteMap = ( @@ -79,11 +79,11 @@ export const transformFileImports = ( relativeImportRegex, (match, importPath: string) => { // Only transform relative imports (starting with ./ or ../) - if (importPath.startsWith('.')) { + if (importPath.startsWith(".")) { // Remove any file extensions from the import path - const cleanPath = importPath.replace(jsExtensionRegex, ''); + const cleanPath = importPath.replace(jsExtensionRegex, ""); // Extract the path after removing leading '../' sequences - const normalizedPath = cleanPath.replace(/^(?:\.\.\/)+/, ''); + const normalizedPath = cleanPath.replace(/^(?:\.\.\/)+/, ""); // Return the package import format return match.replace(importPath, `${pluginName}/${normalizedPath}`); @@ -99,8 +99,8 @@ export const transformFileImports = ( (match, importPath: string) => { // Remove '@/' prefix and any file extensions const cleanPath = importPath - .replace(/^@\//, '') - .replace(jsExtensionRegex, ''); + .replace(/^@\//, "") + .replace(jsExtensionRegex, ""); // Return the package import format return match.replace(importPath, `${pluginName}/${cleanPath}`); @@ -113,8 +113,8 @@ export const transformFileImports = ( (match, importPath: string) => { // Remove '@/' prefix and any file extensions const cleanPath = importPath - .replace(/^@\//, '') - .replace(jsExtensionRegex, ''); + .replace(/^@\//, "") + .replace(jsExtensionRegex, ""); // Return the package import format return match.replace(importPath, `${pluginName}/${cleanPath}`); @@ -127,8 +127,8 @@ export const transformFileImports = ( /dynamic\s*\(\s*\(\s*=>\s*import\s*\(\s*['"](@\/[^'"]*)['"]\s*\)\s*\)\s*\)/g, (match, importPath: string) => { const cleanPath = importPath - .replace(/^@\//, '') - .replace(jsExtensionRegex, ''); + .replace(/^@\//, "") + .replace(jsExtensionRegex, ""); return match.replace(importPath, `${pluginName}/${cleanPath}`); }, @@ -139,26 +139,26 @@ export const transformFileImports = ( export function findRepoRoot(startPath: string): string { let currentPath = startPath; - while (currentPath !== resolve(currentPath, '..')) { - const turboConfigPath = join(currentPath, 'turbo.json'); + while (currentPath !== resolve(currentPath, "..")) { + const turboConfigPath = join(currentPath, "turbo.json"); if (existsSync(turboConfigPath)) { return currentPath; } - currentPath = resolve(currentPath, '..'); + currentPath = resolve(currentPath, ".."); } - const packagePath = join(startPath, 'package.json'); + const packagePath = join(startPath, "package.json"); if (existsSync(packagePath)) { return startPath; } - throw new Error('❌ Could not locate project root'); + throw new Error("❌ Could not locate project root"); } export function findLocaleRoot(repoRoot: string): string { // Check for standalone structure (src/app/[locale]) - const standalonePath = join(repoRoot, 'src', 'app', '[locale]'); + const standalonePath = join(repoRoot, "src", "app", "[locale]"); if (existsSync(standalonePath)) { return standalonePath; } @@ -182,10 +182,10 @@ export function findLocaleRoot(repoRoot: string): string { const fullPath = join(currentDir, entry.name); // Check if this is a [locale] directory with app structure - if (entry.name === '[locale]') { + if (entry.name === "[locale]") { // Verify it's inside an app directory structure const parentPath = dirname(fullPath); - if (parentPath.endsWith(join('src', 'app'))) { + if (parentPath.endsWith(join("src", "app"))) { localeDirectories.push(fullPath); continue; } @@ -215,11 +215,11 @@ export function findLocaleRoot(repoRoot: string): string { // Default to apps/docs structure if nothing is found (for new projects) const defaultAppDir = join( repoRoot, - 'apps', - 'docs', - 'src', - 'app', - '[locale]', + "apps", + "docs", + "src", + "app", + "[locale]", ); return defaultAppDir; @@ -294,9 +294,9 @@ export const copyFile = ( // Check if file should have imports processed (like .js, .jsx, .ts, .tsx files) const ext = extname(srcPath); - if (pluginName && ['.js', '.jsx', '.ts', '.tsx'].includes(ext)) { + if (pluginName && [".js", ".jsx", ".ts", ".tsx"].includes(ext)) { // Read file content - const content = readFileSync(srcPath, 'utf-8'); + const content = readFileSync(srcPath, "utf-8"); // Transform imports const transformedContent = transformFileImports(content, pluginName); // Write transformed content @@ -309,7 +309,7 @@ export const copyFile = ( if (verbose) { // Show even shorter, project-rooted paths for clarity // Remove everything before '/src/app' in the source path if present - const srcAppIdx = srcPath.indexOf(join('src', 'app')); + const srcAppIdx = srcPath.indexOf(join("src", "app")); let shortSrc: string; if (srcAppIdx !== -1) { shortSrc = srcPath.substring(srcAppIdx); diff --git a/packages/vitnode/src/api/adapters/email/nodemailer.ts b/packages/vitnode/src/api/adapters/email/nodemailer.ts index 02dbb45d7..7f00ecfec 100644 --- a/packages/vitnode/src/api/adapters/email/nodemailer.ts +++ b/packages/vitnode/src/api/adapters/email/nodemailer.ts @@ -1,14 +1,14 @@ -import { createTransport } from 'nodemailer'; +import { createTransport } from "nodemailer"; -import type { EmailApiPlugin } from '@/api/models/email'; +import type { EmailApiPlugin } from "@/api/models/email"; export const NodemailerEmailAdapter = ({ - host = '', + host = "", port = 587, secure = false, - user = '', - password = '', - from = '', + user = "", + password = "", + from = "", }: { from: string | undefined; host: string | undefined; @@ -20,7 +20,7 @@ export const NodemailerEmailAdapter = ({ return { sendEmail: async ({ metadata, to, subject, html, replyTo }) => { if (!(host && user && password && from)) { - throw new Error('Missing nodemailer configuration'); + throw new Error("Missing nodemailer configuration"); } const transporter = createTransport( diff --git a/packages/vitnode/src/api/adapters/email/resend.ts b/packages/vitnode/src/api/adapters/email/resend.ts index 4f4c82a16..8c2f3cf3a 100644 --- a/packages/vitnode/src/api/adapters/email/resend.ts +++ b/packages/vitnode/src/api/adapters/email/resend.ts @@ -1,6 +1,6 @@ -import { Resend } from 'resend'; +import { Resend } from "resend"; -import type { EmailApiPlugin } from '@/api/models/email'; +import type { EmailApiPlugin } from "@/api/models/email"; export const ResendEmailAdapter = ({ apiKey, @@ -12,7 +12,7 @@ export const ResendEmailAdapter = ({ return { sendEmail: async ({ to, subject, replyTo, metadata, html }) => { if (!(apiKey && from)) { - throw new Error('Missing Resend configuration'); + throw new Error("Missing Resend configuration"); } const resend = new Resend(apiKey); diff --git a/packages/vitnode/src/api/adapters/sso/discord.ts b/packages/vitnode/src/api/adapters/sso/discord.ts index 0119e947f..a9ef621df 100644 --- a/packages/vitnode/src/api/adapters/sso/discord.ts +++ b/packages/vitnode/src/api/adapters/sso/discord.ts @@ -1,19 +1,19 @@ -import { HTTPException } from 'hono/http-exception'; -import type { ContentfulStatusCode } from 'hono/utils/http-status'; -import { z } from 'zod'; +import { HTTPException } from "hono/http-exception"; +import type { ContentfulStatusCode } from "hono/utils/http-status"; +import { z } from "zod"; -import type { SSOApiPlugin } from '@/api/models/sso'; +import type { SSOApiPlugin } from "@/api/models/sso"; -import { getRedirectUri } from '@/api/models/sso'; +import { getRedirectUri } from "@/api/models/sso"; export const DiscordSSOApiPlugin = ({ - clientId = '', - clientSecret = '', + clientId = "", + clientSecret = "", }: { clientId: string | undefined; clientSecret: string | undefined; }): SSOApiPlugin => { - const id = 'discord'; + const id = "discord"; const redirectUri = getRedirectUri(id); const userSchema = z.object({ id: z.string(), @@ -28,19 +28,19 @@ export const DiscordSSOApiPlugin = ({ return { fetchToken: async code => { if (!(clientId && clientSecret)) { - throw new Error('Missing Discord client ID or secret'); + throw new Error("Missing Discord client ID or secret"); } - const res = await fetch('https://discord.com/api/oauth2/token', { - method: 'POST', + const res = await fetch("https://discord.com/api/oauth2/token", { + method: "POST", headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - Accept: 'application/json', + "Content-Type": "application/x-www-form-urlencoded", + Accept: "application/json", }, body: new URLSearchParams({ code, redirect_uri: redirectUri, - grant_type: 'authorization_code', + grant_type: "authorization_code", client_id: clientId, client_secret: clientSecret, }), @@ -50,7 +50,7 @@ export const DiscordSSOApiPlugin = ({ throw new HTTPException( +res.status.toString() as ContentfulStatusCode, { - message: 'Internal error requesting token', + message: "Internal error requesting token", }, ); } @@ -58,14 +58,14 @@ export const DiscordSSOApiPlugin = ({ const { data, error } = tokenSchema.safeParse(await res.json()); if (error || !data) { throw new HTTPException(400, { - message: 'Invalid token response', + message: "Invalid token response", }); } return data; }, fetchUser: async ({ token_type, access_token }) => { - const res = await fetch('https://discord.com/api/users/@me', { + const res = await fetch("https://discord.com/api/users/@me", { headers: { Authorization: `${token_type} ${access_token}`, }, @@ -73,7 +73,7 @@ export const DiscordSSOApiPlugin = ({ const { data, error } = userSchema.safeParse(await res.json()); if (error || !data) { throw new HTTPException(400, { - message: 'Invalid user response', + message: "Invalid user response", }); } @@ -81,19 +81,19 @@ export const DiscordSSOApiPlugin = ({ }, getUrl: ({ state }) => { if (!clientId) { - throw new Error('Missing Discord client ID'); + throw new Error("Missing Discord client ID"); } - const url = new URL('https://discord.com/oauth2/authorize'); - url.searchParams.set('client_id', clientId); - url.searchParams.set('redirect_uri', redirectUri); - url.searchParams.set('response_type', 'code'); - url.searchParams.set('scope', 'identify email'); - url.searchParams.set('state', state); + const url = new URL("https://discord.com/oauth2/authorize"); + url.searchParams.set("client_id", clientId); + url.searchParams.set("redirect_uri", redirectUri); + url.searchParams.set("response_type", "code"); + url.searchParams.set("scope", "identify email"); + url.searchParams.set("state", state); return url.toString(); }, id, - name: 'Discord', + name: "Discord", }; }; diff --git a/packages/vitnode/src/api/adapters/sso/facebook.ts b/packages/vitnode/src/api/adapters/sso/facebook.ts index aa921e7e8..d9942fa37 100644 --- a/packages/vitnode/src/api/adapters/sso/facebook.ts +++ b/packages/vitnode/src/api/adapters/sso/facebook.ts @@ -1,10 +1,10 @@ -import { HTTPException } from 'hono/http-exception'; -import type { ContentfulStatusCode } from 'hono/utils/http-status'; -import { z } from 'zod'; +import { HTTPException } from "hono/http-exception"; +import type { ContentfulStatusCode } from "hono/utils/http-status"; +import { z } from "zod"; -import type { SSOApiPlugin } from '@/api/models/sso'; +import type { SSOApiPlugin } from "@/api/models/sso"; -import { getRedirectUri } from '@/api/models/sso'; +import { getRedirectUri } from "@/api/models/sso"; export const FacebookSSOApiPlugin = ({ clientId, @@ -13,7 +13,7 @@ export const FacebookSSOApiPlugin = ({ clientId: string | undefined; clientSecret: string | undefined; }): SSOApiPlugin => { - const id = 'facebook'; + const id = "facebook"; const redirectUri = getRedirectUri(id); const tokenSchema = z.object({ access_token: z.string(), @@ -27,25 +27,25 @@ export const FacebookSSOApiPlugin = ({ return { id, - name: 'Facebook', + name: "Facebook", fetchToken: async code => { if (!(clientId && clientSecret)) { - throw new Error('Missing Facebook client ID or secret'); + throw new Error("Missing Facebook client ID or secret"); } const url = new URL( - 'https://graph.facebook.com/v22.0/oauth/access_token', + "https://graph.facebook.com/v22.0/oauth/access_token", ); - url.searchParams.set('code', code); - url.searchParams.set('redirect_uri', redirectUri); - url.searchParams.set('client_id', clientId); - url.searchParams.set('client_secret', clientSecret); + url.searchParams.set("code", code); + url.searchParams.set("redirect_uri", redirectUri); + url.searchParams.set("client_id", clientId); + url.searchParams.set("client_secret", clientSecret); const res = await fetch(url.toString()); if (!res.ok) { throw new HTTPException( +res.status.toString() as ContentfulStatusCode, { - message: 'Internal error requesting token', + message: "Internal error requesting token", }, ); } @@ -53,7 +53,7 @@ export const FacebookSSOApiPlugin = ({ const { data, error } = tokenSchema.safeParse(await res.json()); if (error || !data) { throw new HTTPException(400, { - message: 'Invalid token response', + message: "Invalid token response", }); } @@ -61,15 +61,15 @@ export const FacebookSSOApiPlugin = ({ }, fetchUser: async ({ access_token }) => { - const url = new URL('https://graph.facebook.com/v22.0/me'); - url.searchParams.set('fields', 'id,name,email'); - url.searchParams.set('access_token', access_token); + const url = new URL("https://graph.facebook.com/v22.0/me"); + url.searchParams.set("fields", "id,name,email"); + url.searchParams.set("access_token", access_token); const res = await fetch(url.toString()); if (!res.ok) { throw new HTTPException( +res.status.toString() as ContentfulStatusCode, { - message: 'Internal error requesting user', + message: "Internal error requesting user", }, ); } @@ -78,7 +78,7 @@ export const FacebookSSOApiPlugin = ({ ); if (userError || !userData) { throw new HTTPException(400, { - message: 'Invalid user response', + message: "Invalid user response", }); } @@ -87,15 +87,15 @@ export const FacebookSSOApiPlugin = ({ getUrl: ({ state }) => { if (!clientId) { - throw new Error('Missing Facebook client ID'); + throw new Error("Missing Facebook client ID"); } - const url = new URL('https://www.facebook.com/v22.0/dialog/oauth'); - url.searchParams.set('client_id', clientId); - url.searchParams.set('redirect_uri', redirectUri); - url.searchParams.set('scope', 'public_profile,email'); - url.searchParams.set('response_type', 'code'); - url.searchParams.set('state', state); + const url = new URL("https://www.facebook.com/v22.0/dialog/oauth"); + url.searchParams.set("client_id", clientId); + url.searchParams.set("redirect_uri", redirectUri); + url.searchParams.set("scope", "public_profile,email"); + url.searchParams.set("response_type", "code"); + url.searchParams.set("state", state); return url.toString(); }, diff --git a/packages/vitnode/src/api/adapters/sso/google.ts b/packages/vitnode/src/api/adapters/sso/google.ts index 6260ab89e..c4602e0fb 100644 --- a/packages/vitnode/src/api/adapters/sso/google.ts +++ b/packages/vitnode/src/api/adapters/sso/google.ts @@ -1,10 +1,10 @@ -import { HTTPException } from 'hono/http-exception'; -import type { ContentfulStatusCode } from 'hono/utils/http-status'; -import { z } from 'zod'; +import { HTTPException } from "hono/http-exception"; +import type { ContentfulStatusCode } from "hono/utils/http-status"; +import { z } from "zod"; -import type { SSOApiPlugin } from '@/api/models/sso'; +import type { SSOApiPlugin } from "@/api/models/sso"; -import { getRedirectUri } from '@/api/models/sso'; +import { getRedirectUri } from "@/api/models/sso"; export const GoogleSSOApiPlugin = ({ clientId, @@ -13,7 +13,7 @@ export const GoogleSSOApiPlugin = ({ clientId: string | undefined; clientSecret: string | undefined; }): SSOApiPlugin => { - const id = 'google'; + const id = "google"; const redirectUri = getRedirectUri(id); const tokenSchema = z.object({ access_token: z.string(), @@ -28,22 +28,22 @@ export const GoogleSSOApiPlugin = ({ return { id, - name: 'Google', + name: "Google", fetchToken: async code => { if (!(clientId && clientSecret)) { - throw new Error('Missing Google client ID or secret'); + throw new Error("Missing Google client ID or secret"); } - const res = await fetch('https://oauth2.googleapis.com/token', { - method: 'POST', + const res = await fetch("https://oauth2.googleapis.com/token", { + method: "POST", headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - Accept: 'application/json', + "Content-Type": "application/x-www-form-urlencoded", + Accept: "application/json", }, body: new URLSearchParams({ code, redirect_uri: redirectUri, - grant_type: 'authorization_code', + grant_type: "authorization_code", client_id: clientId, client_secret: clientSecret, }), @@ -53,7 +53,7 @@ export const GoogleSSOApiPlugin = ({ throw new HTTPException( +res.status.toString() as ContentfulStatusCode, { - message: 'Internal error requesting token', + message: "Internal error requesting token", }, ); } @@ -61,14 +61,14 @@ export const GoogleSSOApiPlugin = ({ const { data, error } = tokenSchema.safeParse(await res.json()); if (error || !data) { throw new HTTPException(400, { - message: 'Invalid token response', + message: "Invalid token response", }); } return data; }, fetchUser: async ({ token_type, access_token }) => { - const res = await fetch('https://www.googleapis.com/oauth2/v1/userinfo', { + const res = await fetch("https://www.googleapis.com/oauth2/v1/userinfo", { headers: { Authorization: `${token_type} ${access_token}`, }, @@ -77,13 +77,13 @@ export const GoogleSSOApiPlugin = ({ const { data, error } = userSchema.safeParse(dataFromRes); if (error || !data) { throw new HTTPException(400, { - message: 'Invalid user response', + message: "Invalid user response", }); } if (!data.verified_email) { throw new HTTPException(400, { - message: 'Email not verified', + message: "Email not verified", }); } @@ -94,15 +94,15 @@ export const GoogleSSOApiPlugin = ({ }, getUrl: ({ state }) => { if (!clientId) { - throw new Error('Missing Google client ID'); + throw new Error("Missing Google client ID"); } - const url = new URL('https://accounts.google.com/o/oauth2/auth'); - url.searchParams.set('client_id', clientId); - url.searchParams.set('redirect_uri', redirectUri); - url.searchParams.set('response_type', 'code'); - url.searchParams.set('scope', 'openid profile email'); - url.searchParams.set('state', state); + const url = new URL("https://accounts.google.com/o/oauth2/auth"); + url.searchParams.set("client_id", clientId); + url.searchParams.set("redirect_uri", redirectUri); + url.searchParams.set("response_type", "code"); + url.searchParams.set("scope", "openid profile email"); + url.searchParams.set("state", state); return url.toString(); }, diff --git a/packages/vitnode/src/api/config.ts b/packages/vitnode/src/api/config.ts index 26f18cbce..feecd8e8a 100644 --- a/packages/vitnode/src/api/config.ts +++ b/packages/vitnode/src/api/config.ts @@ -1,18 +1,18 @@ -import { swaggerUI } from '@hono/swagger-ui'; -import type { OpenAPIHono } from '@hono/zod-openapi'; -import type { Context, Env, Schema } from 'hono'; -import { cors } from 'hono/cors'; -import { csrf } from 'hono/csrf'; -import { HTTPException } from 'hono/http-exception'; -import { newBuildPluginApiCore } from '@/api/plugin'; -import { CONFIG_PLUGIN } from '@/config'; -import type { VitNodeApiConfig } from '@/vitnode.config'; +import { swaggerUI } from "@hono/swagger-ui"; +import type { OpenAPIHono } from "@hono/zod-openapi"; +import type { Context, Env, Schema } from "hono"; +import { cors } from "hono/cors"; +import { csrf } from "hono/csrf"; +import { HTTPException } from "hono/http-exception"; +import { newBuildPluginApiCore } from "@/api/plugin"; +import { CONFIG_PLUGIN } from "@/config"; +import type { VitNodeApiConfig } from "@/vitnode.config"; import { globalAdminMiddleware, globalMiddleware, -} from './middlewares/global.middleware'; -import { rateLimiterMiddleware } from './middlewares/rate-limiter.middleware'; +} from "./middlewares/global.middleware"; +import { rateLimiterMiddleware } from "./middlewares/rate-limiter.middleware"; interface CORSOptions { allowHeaders?: string[]; @@ -42,19 +42,19 @@ export function VitNodeAPI({ csrf?: CSRFOptions; vitNodeApiConfig: VitNodeApiConfig; }) { - app.doc('/swagger/doc', { - openapi: '3.0.0', + app.doc("/swagger/doc", { + openapi: "3.0.0", info: { version: CONFIG_PLUGIN.version, - title: 'VitNode API', + title: "VitNode API", }, }); app.use(cors(corsOptions)); app.use(csrf(csrfOptions)); - app.use('*', rateLimiterMiddleware(vitNodeApiConfig.rateLimiter)); - app.get('/swagger', swaggerUI({ url: '/api/swagger/doc' })); + app.use("*", rateLimiterMiddleware(vitNodeApiConfig.rateLimiter)); + app.get("/swagger", swaggerUI({ url: "/api/swagger/doc" })); app.use( - '*', + "*", globalMiddleware({ pathToMessages: vitNodeApiConfig.pathToMessages, email: vitNodeApiConfig.email, @@ -66,7 +66,7 @@ export function VitNodeAPI({ }), ); app.use(async (c, next) => { - if (c.req.path.includes('/admin/')) { + if (c.req.path.includes("/admin/")) { return await globalAdminMiddleware()(c, next); } @@ -78,12 +78,12 @@ export function VitNodeAPI({ return error.getResponse(); } - await c.get('log').error(`Unhandled error: ${error.message}`); + await c.get("log").error(`Unhandled error: ${error.message}`); return new Response( - process.env.NODE_ENV === 'development' + process.env.NODE_ENV === "development" ? error.message - : 'Internal Server Error', + : "Internal Server Error", { status: 500, }, diff --git a/packages/vitnode/src/api/lib/check-plugin-id.ts b/packages/vitnode/src/api/lib/check-plugin-id.ts index 3159b95a9..9b2835d90 100644 --- a/packages/vitnode/src/api/lib/check-plugin-id.ts +++ b/packages/vitnode/src/api/lib/check-plugin-id.ts @@ -1,7 +1,7 @@ -import { existsSync, readFileSync } from 'node:fs'; -import { join, resolve } from 'node:path'; +import { existsSync, readFileSync } from "node:fs"; +import { join, resolve } from "node:path"; -import { CONFIG } from '../../lib/config'; +import { CONFIG } from "../../lib/config"; export interface PackageJSON { dependencies?: Record; @@ -22,13 +22,13 @@ export const checkPluginId = (pluginName: string): null | PackageJSON => { const findMonorepoRoot = (startPath: string): null | string => { let currentPath = startPath; - while (currentPath !== resolve(currentPath, '..')) { - const turboConfigPath = join(currentPath, 'turbo.json'); + while (currentPath !== resolve(currentPath, "..")) { + const turboConfigPath = join(currentPath, "turbo.json"); if (existsSync(turboConfigPath)) { return currentPath; } - currentPath = resolve(currentPath, '..'); + currentPath = resolve(currentPath, ".."); } return null; @@ -39,7 +39,7 @@ export const checkPluginId = (pluginName: string): null | PackageJSON => { const monorepoRoot = findMonorepoRoot(cwd); // Check in current working directory first - const cwdPluginPath = join(cwd, 'node_modules', pluginName, 'package.json'); + const cwdPluginPath = join(cwd, "node_modules", pluginName, "package.json"); if (existsSync(cwdPluginPath)) { return cwdPluginPath; } @@ -48,9 +48,9 @@ export const checkPluginId = (pluginName: string): null | PackageJSON => { if (monorepoRoot && monorepoRoot !== cwd) { const rootPluginPath = join( monorepoRoot, - 'node_modules', + "node_modules", pluginName, - 'package.json', + "package.json", ); if (existsSync(rootPluginPath)) { return rootPluginPath; @@ -68,7 +68,7 @@ export const checkPluginId = (pluginName: string): null | PackageJSON => { ); } - const content: PackageJSON = JSON.parse(readFileSync(path, 'utf-8')); + const content: PackageJSON = JSON.parse(readFileSync(path, "utf-8")); if (content.name !== pluginName) { throw new Error( `The plugin name in package.json (${content.name}) does not match the requested plugin name (${pluginName}). Please check the plugin name.`, diff --git a/packages/vitnode/src/api/lib/logger-middleware.ts b/packages/vitnode/src/api/lib/logger-middleware.ts index d67d76fec..bb4921b8c 100644 --- a/packages/vitnode/src/api/lib/logger-middleware.ts +++ b/packages/vitnode/src/api/lib/logger-middleware.ts @@ -1,9 +1,9 @@ /** biome-ignore-all lint/suspicious/noConsole: */ -import type { Context } from 'hono'; +import type { Context } from "hono"; -import { core_logs } from '@/database/logs'; +import { core_logs } from "@/database/logs"; -type CoreLogsType = 'debug' | 'error' | 'warn'; +type CoreLogsType = "debug" | "error" | "warn"; export interface LoggerMiddlewareType { debug: (content: string) => Promise; @@ -13,11 +13,11 @@ export interface LoggerMiddlewareType { export const loggerMiddleware = (c: Context): LoggerMiddlewareType => { const logToDbAndConsole = async (content: string, type: CoreLogsType) => { - const pluginId = c.get('plugin')?.id ?? 'core'; - const ipAddress = c.get('ipAddress'); + const pluginId = c.get("plugin")?.id ?? "core"; + const ipAddress = c.get("ipAddress"); await c - .get('db') + .get("db") .insert(core_logs) .values({ pluginId, @@ -26,9 +26,9 @@ export const loggerMiddleware = (c: Context): LoggerMiddlewareType => { ipAddress, method: c.req.method.toUpperCase(), path: c.req.path, - userAgent: c.req.header('User-Agent'), + userAgent: c.req.header("User-Agent"), statusCode: c.res.status, - userId: c.get('user')?.id, + userId: c.get("user")?.id, }); const loggers: Record void> = { @@ -50,13 +50,13 @@ export const loggerMiddleware = (c: Context): LoggerMiddlewareType => { return { debug: async (content: string) => { - await logToDbAndConsole(content, 'debug'); + await logToDbAndConsole(content, "debug"); }, error: async (content: string) => { - await logToDbAndConsole(content, 'error'); + await logToDbAndConsole(content, "error"); }, warn: async (content: string) => { - await logToDbAndConsole(content, 'warn'); + await logToDbAndConsole(content, "warn"); }, }; }; diff --git a/packages/vitnode/src/api/lib/module.ts b/packages/vitnode/src/api/lib/module.ts index 9f9297ffc..de0d2c5a8 100644 --- a/packages/vitnode/src/api/lib/module.ts +++ b/packages/vitnode/src/api/lib/module.ts @@ -1,6 +1,6 @@ -import { OpenAPIHono } from '@hono/zod-openapi'; +import { OpenAPIHono } from "@hono/zod-openapi"; -import type { Route } from './route'; +import type { Route } from "./route"; export interface BuildModuleType { plugin: Plugin; diff --git a/packages/vitnode/src/api/lib/plugin.ts b/packages/vitnode/src/api/lib/plugin.ts index feff79633..f4829a34d 100644 --- a/packages/vitnode/src/api/lib/plugin.ts +++ b/packages/vitnode/src/api/lib/plugin.ts @@ -1,6 +1,6 @@ -import { OpenAPIHono } from '@hono/zod-openapi'; -import { checkPluginId } from './check-plugin-id'; -import type { BuildModuleReturn } from './module'; +import { OpenAPIHono } from "@hono/zod-openapi"; +import { checkPluginId } from "./check-plugin-id"; +import type { BuildModuleReturn } from "./module"; export interface BuildPluginApiReturn { hono: OpenAPIHono; diff --git a/packages/vitnode/src/api/lib/route.ts b/packages/vitnode/src/api/lib/route.ts index 50670873d..33170e1f4 100644 --- a/packages/vitnode/src/api/lib/route.ts +++ b/packages/vitnode/src/api/lib/route.ts @@ -1,12 +1,12 @@ -import type { RouteConfig, RouteHandler } from '@hono/zod-openapi'; +import type { RouteConfig, RouteHandler } from "@hono/zod-openapi"; -import { createRoute as createRouteHono } from '@hono/zod-openapi'; +import { createRoute as createRouteHono } from "@hono/zod-openapi"; -import { captchaMiddleware } from '../middlewares/captcha.middleware'; +import { captchaMiddleware } from "../middlewares/captcha.middleware"; import { type EnvVitNode, pluginMiddleware, -} from '../middlewares/global.middleware'; +} from "../middlewares/global.middleware"; type RoutingPath

= P extends `${infer Head}/{${infer Param}}${infer Tail}` @@ -20,7 +20,7 @@ type ValidHandler = ( export const buildRoute = < Plugin extends string, P extends string, - R extends Omit & { + R extends Omit & { path: P; withCaptcha?: boolean; }, @@ -37,13 +37,13 @@ export const buildRoute = < handler: H; pluginId: Plugin; route: R & { - getRoutingPath: () => RoutingPath; + getRoutingPath: () => RoutingPath; }; } => { const pluginTag = pluginId .split(/[-_]/) .map(word => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' '); + .join(" "); const tags = [pluginTag, ...(route.tags ?? [])]; @@ -62,7 +62,7 @@ export const buildRoute = < ], ...route, }) as R & { - getRoutingPath: () => RoutingPath; + getRoutingPath: () => RoutingPath; }, handler, pluginId, diff --git a/packages/vitnode/src/api/lib/with-pagination.ts b/packages/vitnode/src/api/lib/with-pagination.ts index ecdecb2d2..0ecfc4cef 100644 --- a/packages/vitnode/src/api/lib/with-pagination.ts +++ b/packages/vitnode/src/api/lib/with-pagination.ts @@ -1,13 +1,13 @@ -import { z } from '@hono/zod-openapi'; -import type { ColumnBaseConfig, Placeholder, SQL } from 'drizzle-orm'; -import { and, asc, count, desc, gt, lt } from 'drizzle-orm'; +import { z } from "@hono/zod-openapi"; +import type { ColumnBaseConfig, Placeholder, SQL } from "drizzle-orm"; +import { and, asc, count, desc, gt, lt } from "drizzle-orm"; import type { PgColumn, PgTable, PgTableWithColumns, TableConfig, -} from 'drizzle-orm/pg-core'; -import type { Context } from 'hono'; +} from "drizzle-orm/pg-core"; +import type { Context } from "hono"; function parsePaginationParams(params: { query: { cursor?: string; first?: string; last?: string }; @@ -23,13 +23,13 @@ function parsePaginationParams(params: { : undefined; if (first !== undefined && last !== undefined) { - throw new Error('Cannot specify both first and last'); + throw new Error("Cannot specify both first and last"); } if (first !== undefined && first < 0) { - throw new Error('first must be positive'); + throw new Error("first must be positive"); } if (last !== undefined && last < 0) { - throw new Error('last must be positive'); + throw new Error("last must be positive"); } return { cursor, first, last }; @@ -37,28 +37,28 @@ function parsePaginationParams(params: { function getOrderFn( isForward: boolean, - order: 'asc' | 'desc', + order: "asc" | "desc", ): typeof asc | typeof desc { if (isForward) { - return order === 'asc' ? asc : desc; + return order === "asc" ? asc : desc; } - return order === 'asc' ? desc : asc; + return order === "asc" ? desc : asc; } function buildWhereWithCursor< - Primary extends ColumnBaseConfig<'number', string>, + Primary extends ColumnBaseConfig<"number", string>, >( baseWhere: SQL | undefined, cursor: number | undefined, isForward: boolean, - order: 'asc' | 'desc', + order: "asc" | "desc", table: PgTable, primaryCursor: PgColumn, ): SQL | undefined { if (!cursor) return baseWhere; const cursorFilter = - (isForward && order === 'asc') || (!isForward && order === 'desc') + (isForward && order === "asc") || (!isForward && order === "desc") ? gt : lt; @@ -72,7 +72,7 @@ async function fetchTotalCount( where: SQL | undefined, ): Promise { const [{ count: totalCount }] = await c - .get('db') + .get("db") .select({ count: count() }) .from(table) .where(where); @@ -82,7 +82,7 @@ async function fetchTotalCount( export async function withPagination< QueryMin extends Record, T extends TableConfig, - Primary extends ColumnBaseConfig<'number', string>, + Primary extends ColumnBaseConfig<"number", string>, >({ query, table, @@ -95,7 +95,7 @@ export async function withPagination< c: Context; orderBy: { column: PgColumn; - order: 'asc' | 'desc'; + order: "asc" | "desc"; }; params: { query: { @@ -110,7 +110,7 @@ export async function withPagination< orderBy: SQL; where: SQL | undefined; }) => Promise; - table: Omit, 'enableRLS'>; + table: Omit, "enableRLS">; where?: SQL; }): Promise<{ edges: QueryMin[]; diff --git a/packages/vitnode/src/api/middlewares/captcha.middleware.ts b/packages/vitnode/src/api/middlewares/captcha.middleware.ts index 6dc9deba3..09628d4c5 100644 --- a/packages/vitnode/src/api/middlewares/captcha.middleware.ts +++ b/packages/vitnode/src/api/middlewares/captcha.middleware.ts @@ -1,55 +1,55 @@ -import type { Context, Next } from 'hono'; +import type { Context, Next } from "hono"; -import { HTTPException } from 'hono/http-exception'; +import { HTTPException } from "hono/http-exception"; -import type { VitNodeApiConfig } from '../../vitnode.config'; +import type { VitNodeApiConfig } from "../../vitnode.config"; const getResFromReCaptcha = async ({ token, userIp, captchaConfig, }: { - captchaConfig: NonNullable['captcha']>; + captchaConfig: NonNullable["captcha"]>; token: string; userIp: string; -}): Promise<{ 'error-codes'?: string[]; score: number; success: boolean }> => { - if (captchaConfig.type === 'cloudflare_turnstile') { +}): Promise<{ "error-codes"?: string[]; score: number; success: boolean }> => { + if (captchaConfig.type === "cloudflare_turnstile") { const res = await fetch( - 'https://challenges.cloudflare.com/turnstile/v0/siteverify', + "https://challenges.cloudflare.com/turnstile/v0/siteverify", { - method: 'POST', + method: "POST", body: JSON.stringify({ secret: captchaConfig.secretKey, response: token, remoteip: userIp, }), headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, }, ); const data: { - 'error-codes'?: string[]; + "error-codes"?: string[]; success: boolean; } = await res.json(); return { success: data.success, score: data.success ? 1 : 0, - 'error-codes': data['error-codes'], + "error-codes": data["error-codes"], }; } - if (captchaConfig.type === 'recaptcha_v3') { + if (captchaConfig.type === "recaptcha_v3") { const res = await fetch( `https://www.google.com/recaptcha/api/siteverify?secret=${captchaConfig.secretKey}&response=${token}&remoteip=${userIp}`, { - method: 'POST', + method: "POST", }, ); const data: { - 'error-codes'?: string[]; + "error-codes"?: string[]; score: number; success: boolean; } = await res.json(); @@ -57,7 +57,7 @@ const getResFromReCaptcha = async ({ return { success: data.success, score: data.score ?? 0, - 'error-codes': data['error-codes'], + "error-codes": data["error-codes"], }; } @@ -69,8 +69,8 @@ const getResFromReCaptcha = async ({ export const captchaMiddleware = () => { return async (c: Context, next: Next) => { - const token = c.req.header('x-vitnode-captcha-token'); - const captchaConfig = c.get('core').captcha; + const token = c.req.header("x-vitnode-captcha-token"); + const captchaConfig = c.get("core").captcha; if (!captchaConfig) { await next(); @@ -79,19 +79,19 @@ export const captchaMiddleware = () => { if (!token) { throw new HTTPException(400, { - message: 'Captcha token is required', + message: "Captcha token is required", }); } const res = await getResFromReCaptcha({ token, - userIp: c.get('ipAddress'), + userIp: c.get("ipAddress"), captchaConfig, }); if (!res.success || res.score < 0.5) { throw new HTTPException(400, { - message: 'Captcha validation failed', + message: "Captcha validation failed", }); } diff --git a/packages/vitnode/src/api/middlewares/global.middleware.ts b/packages/vitnode/src/api/middlewares/global.middleware.ts index d1cb7112e..ec8bf40a1 100644 --- a/packages/vitnode/src/api/middlewares/global.middleware.ts +++ b/packages/vitnode/src/api/middlewares/global.middleware.ts @@ -1,17 +1,17 @@ -import type { Context, Env, Next } from 'hono'; +import type { Context, Env, Next } from "hono"; -import { HTTPException } from 'hono/http-exception'; -import { EmailModel, type EmailModelSendArgs } from '@/api/models/email'; -import { SessionModel } from '@/api/models/session'; -import { SessionAdminModel } from '@/api/models/session-admin'; -import type { VitNodeApiConfig, VitNodeConfig } from '@/vitnode.config'; +import { HTTPException } from "hono/http-exception"; +import { EmailModel, type EmailModelSendArgs } from "@/api/models/email"; +import { SessionModel } from "@/api/models/session"; +import { SessionAdminModel } from "@/api/models/session-admin"; +import type { VitNodeApiConfig, VitNodeConfig } from "@/vitnode.config"; import { type LoggerMiddlewareType, loggerMiddleware, -} from '../lib/logger-middleware'; -import type { SSOApiPlugin } from '../models/sso'; +} from "../lib/logger-middleware"; +import type { SSOApiPlugin } from "../models/sso"; -declare module 'hono' { +declare module "hono" { interface ContextVariableMap extends EnvVariablesVitNode {} } @@ -45,8 +45,8 @@ export interface EnvVariablesVitNode { deviceCookieName: string; ssoAdapters: SSOApiPlugin[]; }; - captcha?: Pick['captcha']; - email?: VitNodeApiConfig['email']; + captcha?: Pick["captcha"]; + email?: VitNodeApiConfig["email"]; metadata: { shortTitle?: string; title: string; @@ -54,7 +54,7 @@ export interface EnvVariablesVitNode { pathToMessages: (path: string) => Promise<{ default: object }>; plugins: { id: string }[]; }; - db: Pick['dbProvider']; + db: Pick["dbProvider"]; email: { send: (args: EmailModelSendArgs) => Promise; }; @@ -87,34 +87,34 @@ export const globalMiddleware = ({ pathToMessages, }: Pick< VitNodeApiConfig, - | 'authorization' - | 'captcha' - | 'dbProvider' - | 'email' - | 'pathToMessages' - | 'plugins' + | "authorization" + | "captcha" + | "dbProvider" + | "email" + | "pathToMessages" + | "plugins" > & - Pick) => { + Pick) => { // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: return async (c: Context, next: Next) => { // Collect possible IP header keys in order of trust/preference const ipHeaderKeys = [ - 'x-forwarded-for', - 'x-real-ip', - 'cf-connecting-ip', - 'x-client-ip', - 'x-forwarded', - 'x-cluster-client-ip', - 'forwarded-for', - 'forwarded', - 'via', - 'remote-addr', - 'client-ip', - 'ip', - 'x-ip', - 'true-client-ip', - 'fastly-client-ip', - 'x-fastly-client-ip', + "x-forwarded-for", + "x-real-ip", + "cf-connecting-ip", + "x-client-ip", + "x-forwarded", + "x-cluster-client-ip", + "forwarded-for", + "forwarded", + "via", + "remote-addr", + "client-ip", + "ip", + "x-ip", + "true-client-ip", + "fastly-client-ip", + "x-fastly-client-ip", ]; let ipAddress: string | undefined; @@ -140,23 +140,23 @@ export const globalMiddleware = ({ } // Fallback to localhost if nothing found - c.set('ipAddress', ipAddress ?? '127.0.0.1'); - c.set('db', dbProvider); - c.set('email', new EmailModel(c)); + c.set("ipAddress", ipAddress ?? "127.0.0.1"); + c.set("db", dbProvider); + c.set("email", new EmailModel(c)); - c.set('core', { + c.set("core", { pathToMessages, metadata, email, authorization: { - cookieName: authorization?.cookieName ?? 'vitnode_auth', + cookieName: authorization?.cookieName ?? "vitnode_auth", cookie_expires: authorization?.cookieExpires ?? 1000 * 60 * 60 * 24 * 90, // 90 days ssoAdapters: authorization?.ssoAdapters ?? [], - deviceCookieName: authorization?.deviceCookieName ?? 'vitnode_device', + deviceCookieName: authorization?.deviceCookieName ?? "vitnode_device", deviceCookieExpires: authorization?.deviceCookieExpires ?? 1000 * 60 * 60 * 24 * 365, // 1 year, - adminCookieName: authorization?.adminCookieName ?? 'vitnode_auth_admin', + adminCookieName: authorization?.adminCookieName ?? "vitnode_auth_admin", adminCookieExpires: authorization?.adminCookieExpires ?? 1000 * 60 * 60 * 24 * 1, // 1 day cookieSecure: authorization?.cookieSecure ?? true, @@ -168,9 +168,9 @@ export const globalMiddleware = ({ }); const user = await new SessionModel(c).getUser(); - c.set('user', user); - c.set('admin', null); - c.set('log', loggerMiddleware(c)); + c.set("user", user); + c.set("admin", null); + c.set("log", loggerMiddleware(c)); await next(); }; @@ -178,7 +178,7 @@ export const globalMiddleware = ({ export const pluginMiddleware = (pluginId: string) => { return async (c: Context, next: Next) => { - c.set('plugin', { + c.set("plugin", { id: pluginId, }); await next(); @@ -189,7 +189,7 @@ export const globalAdminMiddleware = () => { return async (c: Context, next: Next) => { const user = await new SessionAdminModel(c).getUser(); if (!user) throw new HTTPException(403); - c.set('admin', { + c.set("admin", { user, }); diff --git a/packages/vitnode/src/api/middlewares/rate-limiter.middleware.ts b/packages/vitnode/src/api/middlewares/rate-limiter.middleware.ts index 37a38c175..1c4e54055 100644 --- a/packages/vitnode/src/api/middlewares/rate-limiter.middleware.ts +++ b/packages/vitnode/src/api/middlewares/rate-limiter.middleware.ts @@ -1,17 +1,17 @@ -import type { Context, Next } from 'hono'; +import type { Context, Next } from "hono"; import { type IRateLimiterOptions, type RateLimiterAbstract, RateLimiterMemory, -} from 'rate-limiter-flexible'; +} from "rate-limiter-flexible"; -import { CONFIG } from '../../lib/config'; +import { CONFIG } from "../../lib/config"; const createRateLimiter = ({ keyPrefix, ...options -}: Omit & { +}: Omit & { keyPrefix: string; }): RateLimiterAbstract => { // TODO: Add support for Redis or other storage options @@ -25,7 +25,7 @@ const createRateLimiter = ({ }; export const rateLimiterMiddleware = ( - options?: Omit, + options?: Omit, ) => { if (CONFIG.node_development) { // In development, we disable the rate limiter for easier testing @@ -36,16 +36,16 @@ export const rateLimiterMiddleware = ( const rateLimiter = createRateLimiter({ ...options, - keyPrefix: 'vitnode-api-rate-limiter', + keyPrefix: "vitnode-api-rate-limiter", }); return async (c: Context, next: Next) => { - const key = c.get('ipAddress'); + const key = c.get("ipAddress"); try { await rateLimiter.consume(key); } catch { - return c.text('Too Many Requests', 429); + return c.text("Too Many Requests", 429); } await next(); diff --git a/packages/vitnode/src/api/models/device.ts b/packages/vitnode/src/api/models/device.ts index 20fae1887..8907d6340 100644 --- a/packages/vitnode/src/api/models/device.ts +++ b/packages/vitnode/src/api/models/device.ts @@ -1,10 +1,10 @@ -import { randomBytes } from 'node:crypto'; -import { eq } from 'drizzle-orm'; -import type { Context } from 'hono'; -import { getCookie, setCookie } from 'hono/cookie'; +import { randomBytes } from "node:crypto"; +import { eq } from "drizzle-orm"; +import type { Context } from "hono"; +import { getCookie, setCookie } from "hono/cookie"; -import { core_sessions_known_devices } from '@/database/sessions'; -import { CONFIG } from '@/lib/config'; +import { core_sessions_known_devices } from "@/database/sessions"; +import { CONFIG } from "@/lib/config"; export class DeviceModel { constructor(c: Context) { @@ -13,14 +13,14 @@ export class DeviceModel { protected readonly c: Context; private async createDevice() { - const publicId = randomBytes(16).toString('hex'); + const publicId = randomBytes(16).toString("hex"); const [device] = await this.c - .get('db') + .get("db") .insert(core_sessions_known_devices) .values({ publicId, - ipAddress: this.c.get('ipAddress'), + ipAddress: this.c.get("ipAddress"), userAgent: this.getUserAgent(), }) .returning({ id: core_sessions_known_devices.id }); @@ -31,21 +31,21 @@ export class DeviceModel { } private getUserAgent() { - return this.c.req.header('User-Agent') ?? 'node'; + return this.c.req.header("User-Agent") ?? "node"; } private setCookieDevice(publicDeviceId: string) { setCookie( this.c, - this.c.get('core').authorization.deviceCookieName, + this.c.get("core").authorization.deviceCookieName, publicDeviceId, { httpOnly: true, - secure: this.c.get('core').authorization.cookieSecure, - path: '/', + secure: this.c.get("core").authorization.cookieSecure, + path: "/", domain: CONFIG.web.hostname, expires: new Date( - Date.now() + this.c.get('core').authorization.deviceCookieExpires, + Date.now() + this.c.get("core").authorization.deviceCookieExpires, ), }, ); @@ -54,13 +54,13 @@ export class DeviceModel { async getDeviceId() { const deviceIdFromCookie = getCookie( this.c, - this.c.get('core').authorization.deviceCookieName, + this.c.get("core").authorization.deviceCookieName, ); try { if (deviceIdFromCookie) { const [device] = await this.c - .get('db') + .get("db") .select({ id: core_sessions_known_devices.id, }) @@ -72,10 +72,10 @@ export class DeviceModel { } await this.c - .get('db') + .get("db") .update(core_sessions_known_devices) .set({ - ipAddress: this.c.get('ipAddress'), + ipAddress: this.c.get("ipAddress"), userAgent: this.getUserAgent(), }) .where(eq(core_sessions_known_devices.publicId, deviceIdFromCookie)); diff --git a/packages/vitnode/src/api/models/email.ts b/packages/vitnode/src/api/models/email.ts index 6df130d9a..80c3b8eb7 100644 --- a/packages/vitnode/src/api/models/email.ts +++ b/packages/vitnode/src/api/models/email.ts @@ -1,10 +1,10 @@ -import { render } from '@react-email/components'; -import type { Context, ContextVariableMap } from 'hono'; -import { HTTPException } from 'hono/http-exception'; -import type React from 'react'; +import { render } from "@react-email/components"; +import type { Context, ContextVariableMap } from "hono"; +import { HTTPException } from "hono/http-exception"; +import type React from "react"; -import type { DefaultTemplateEmailProps } from '../../emails/default-template'; -import { CONFIG } from '../../lib/config'; +import type { DefaultTemplateEmailProps } from "../../emails/default-template"; +import { CONFIG } from "../../lib/config"; interface EmailModelSendArgsWithUser { locale?: never; @@ -27,7 +27,7 @@ interface EmailModelSendArgsWithEmail { export interface EmailApiPlugin { sendEmail: (args: { html: string; - metadata: ContextVariableMap['core']['metadata']; + metadata: ContextVariableMap["core"]["metadata"]; replyTo?: string; subject: string; text: string; @@ -37,14 +37,14 @@ export interface EmailApiPlugin { export type EmailModelSendArgs = { content: ( - props: Omit & - Pick, + props: Omit & + Pick, ) => React.ReactNode; html?: string; locale?: string; replyTo?: string; subject: - | ((props: Pick) => string) + | ((props: Pick) => string) | string; } & (EmailModelSendArgsWithEmail | EmailModelSendArgsWithUser); @@ -64,18 +64,18 @@ export class EmailModel { content, locale: localeFromArgs, }: EmailModelSendArgs) { - const core = this.c.get('core'); + const core = this.c.get("core"); const provider = core.email?.adapter; if (!provider) { throw new HTTPException(500, { - message: 'Email provider not found', + message: "Email provider not found", }); } - const locale = localeFromArgs ?? user?.language ?? 'en'; + const locale = localeFromArgs ?? user?.language ?? "en"; const pluginIds: string[] = [ - '@vitnode/core', - ...this.c.get('core').plugins.map(plugin => plugin.id), + "@vitnode/core", + ...this.c.get("core").plugins.map(plugin => plugin.id), ]; const messagesPromises = pluginIds.map(async pluginId => { @@ -116,7 +116,7 @@ export class EmailModel { const emailTo = user?.email ?? to; if (!emailTo) { throw new HTTPException(400, { - message: 'Email address is required', + message: "Email address is required", }); } @@ -125,7 +125,7 @@ export class EmailModel { html: await render(htmlContent), to: emailTo, subject: - typeof subject === 'function' + typeof subject === "function" ? subject({ i18n: { locale, messages } }) : subject, replyTo, @@ -138,9 +138,9 @@ export class EmailModel { const error = err instanceof Error ? err - : new Error('Unknown error from email provider'); + : new Error("Unknown error from email provider"); - await this.c.get('log').error(`Failed to send email: ${error.message}`); + await this.c.get("log").error(`Failed to send email: ${error.message}`); } } } diff --git a/packages/vitnode/src/api/models/password.ts b/packages/vitnode/src/api/models/password.ts index 82c0fb9b6..099fa1761 100644 --- a/packages/vitnode/src/api/models/password.ts +++ b/packages/vitnode/src/api/models/password.ts @@ -1,22 +1,22 @@ -import crypto from 'node:crypto'; +import crypto from "node:crypto"; export class PasswordModel { async encryptPassword(password: string): Promise { return await new Promise((resolve, reject) => { - const salt = crypto.randomBytes(8).toString('hex'); + const salt = crypto.randomBytes(8).toString("hex"); crypto.scrypt(password, salt, 64, (err, derivedKey) => { if (err) reject(err); - resolve(`${salt}:${derivedKey.toString('hex')}`); + resolve(`${salt}:${derivedKey.toString("hex")}`); }); }); } async verifyPassword(password: string, hash: string): Promise { return await new Promise((resolve, reject) => { - const [salt, key] = hash.split(':'); - const keyBuffer = Buffer.from(key, 'hex'); + const [salt, key] = hash.split(":"); + const keyBuffer = Buffer.from(key, "hex"); crypto.scrypt(password, salt, 64, (err, derivedKey) => { if (err) reject(err); @@ -35,10 +35,10 @@ export class PasswordModel { export class ForgotPasswordTokenModel { generateResetToken() { - return crypto.randomBytes(32).toString('base64url'); + return crypto.randomBytes(32).toString("base64url"); } hashResetToken(token: string) { - return crypto.createHash('sha256').update(token).digest('hex'); + return crypto.createHash("sha256").update(token).digest("hex"); } } diff --git a/packages/vitnode/src/api/models/session-admin.ts b/packages/vitnode/src/api/models/session-admin.ts index b0f1303d4..0814867f4 100644 --- a/packages/vitnode/src/api/models/session-admin.ts +++ b/packages/vitnode/src/api/models/session-admin.ts @@ -1,13 +1,13 @@ -import { and, eq, gt, or } from 'drizzle-orm'; // Removed 'or' as it's safer not to use it here -import type { Context } from 'hono'; -import { deleteCookie, getCookie, setCookie } from 'hono/cookie'; -import { HTTPException } from 'hono/http-exception'; +import { and, eq, gt, or } from "drizzle-orm"; // Removed 'or' as it's safer not to use it here +import type { Context } from "hono"; +import { deleteCookie, getCookie, setCookie } from "hono/cookie"; +import { HTTPException } from "hono/http-exception"; -import { core_admin_permissions, core_admin_sessions } from '@/database/admins'; -import { CONFIG } from '@/lib/config'; +import { core_admin_permissions, core_admin_sessions } from "@/database/admins"; +import { CONFIG } from "@/lib/config"; -import { DeviceModel } from './device'; -import { UserModel } from './user'; +import { DeviceModel } from "./device"; +import { UserModel } from "./user"; export class SessionAdminModel { constructor(c: Context) { @@ -18,10 +18,10 @@ export class SessionAdminModel { private async hashToken(token: string): Promise { const encoder = new TextEncoder(); const data = encoder.encode(token); - const hashBuffer = await crypto.subtle.digest('SHA-256', data); + const hashBuffer = await crypto.subtle.digest("SHA-256", data); const hashArray = Array.from(new Uint8Array(hashBuffer)); - return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); + return hashArray.map(b => b.toString(16).padStart(2, "0")).join(""); } async checkIfUserIsAdmin(userId: number) { @@ -29,7 +29,7 @@ export class SessionAdminModel { if (!user) return false; const [permission] = await this.c - .get('db') + .get("db") .select() .from(core_admin_permissions) .where( @@ -46,36 +46,36 @@ export class SessionAdminModel { async createSessionByUserId(userId: number) { const isAdmin = await this.checkIfUserIsAdmin(userId); if (!isAdmin) { - throw new HTTPException(403, { message: 'Forbidden' }); + throw new HTTPException(403, { message: "Forbidden" }); } const randomBytes = new Uint8Array(64); crypto.getRandomValues(randomBytes); const token = Array.from(randomBytes) - .map(b => b.toString(16).padStart(2, '0')) - .join(''); + .map(b => b.toString(16).padStart(2, "0")) + .join(""); const hashedToken = await this.hashToken(token); const device = await new DeviceModel(this.c).getDeviceId(); await this.c - .get('db') + .get("db") .insert(core_admin_sessions) .values({ token: hashedToken, userId, expiresAt: new Date( - Date.now() + this.c.get('core').authorization.adminCookieExpires, + Date.now() + this.c.get("core").authorization.adminCookieExpires, ), deviceId: device.id, }); - setCookie(this.c, this.c.get('core').authorization.adminCookieName, token, { + setCookie(this.c, this.c.get("core").authorization.adminCookieName, token, { httpOnly: true, - secure: this.c.get('core').authorization.cookieSecure, - path: '/', + secure: this.c.get("core").authorization.cookieSecure, + path: "/", expires: new Date( - Date.now() + this.c.get('core').authorization.adminCookieExpires, + Date.now() + this.c.get("core").authorization.adminCookieExpires, ), domain: CONFIG.web.hostname, }); @@ -86,25 +86,25 @@ export class SessionAdminModel { async deleteSession() { const token = getCookie( this.c, - this.c.get('core').authorization.adminCookieName, + this.c.get("core").authorization.adminCookieName, ); if (!token) return; const hashedToken = await this.hashToken(token); await this.c - .get('db') + .get("db") .delete(core_admin_sessions) .where(eq(core_admin_sessions.token, hashedToken)); - deleteCookie(this.c, this.c.get('core').authorization.adminCookieName, { - path: '/', + deleteCookie(this.c, this.c.get("core").authorization.adminCookieName, { + path: "/", domain: CONFIG.web.hostname, }); } async getUser() { - const { authorization } = this.c.get('core'); + const { authorization } = this.c.get("core"); const token = getCookie(this.c, authorization.adminCookieName); if (!token) return null; @@ -114,7 +114,7 @@ export class SessionAdminModel { const hashedToken = await this.hashToken(token); const [session] = await this.c - .get('db') + .get("db") .select({ userId: core_admin_sessions.userId, }) @@ -129,8 +129,8 @@ export class SessionAdminModel { .limit(1); if (!session) { - deleteCookie(this.c, this.c.get('core').authorization.adminCookieName, { - path: '/', + deleteCookie(this.c, this.c.get("core").authorization.adminCookieName, { + path: "/", domain: CONFIG.web.hostname, }); diff --git a/packages/vitnode/src/api/models/session.ts b/packages/vitnode/src/api/models/session.ts index fdd2364b8..251a10a2f 100644 --- a/packages/vitnode/src/api/models/session.ts +++ b/packages/vitnode/src/api/models/session.ts @@ -1,12 +1,12 @@ -import { and, eq, gt } from 'drizzle-orm'; -import type { Context } from 'hono'; -import { deleteCookie, getCookie, setCookie } from 'hono/cookie'; +import { and, eq, gt } from "drizzle-orm"; +import type { Context } from "hono"; +import { deleteCookie, getCookie, setCookie } from "hono/cookie"; -import { core_sessions } from '@/database/sessions'; -import { CONFIG } from '@/lib/config'; +import { core_sessions } from "@/database/sessions"; +import { CONFIG } from "@/lib/config"; -import { DeviceModel } from './device'; -import { UserModel } from './user'; +import { DeviceModel } from "./device"; +import { UserModel } from "./user"; export class SessionModel { constructor(c: Context) { @@ -17,10 +17,10 @@ export class SessionModel { private async hashToken(token: string): Promise { const encoder = new TextEncoder(); const data = encoder.encode(token); - const hashBuffer = await crypto.subtle.digest('SHA-256', data); + const hashBuffer = await crypto.subtle.digest("SHA-256", data); const hashArray = Array.from(new Uint8Array(hashBuffer)); - return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); + return hashArray.map(b => b.toString(16).padStart(2, "0")).join(""); } async createSessionByUserId(userId: number) { @@ -28,31 +28,31 @@ export class SessionModel { const randomBytes = new Uint8Array(64); crypto.getRandomValues(randomBytes); const token = Array.from(randomBytes) - .map(b => b.toString(16).padStart(2, '0')) - .join(''); + .map(b => b.toString(16).padStart(2, "0")) + .join(""); const device = await new DeviceModel(this.c).getDeviceId(); const hashedToken = await this.hashToken(token); await this.c - .get('db') + .get("db") .insert(core_sessions) .values({ token: hashedToken, userId, expiresAt: new Date( - Date.now() + this.c.get('core').authorization.cookie_expires, + Date.now() + this.c.get("core").authorization.cookie_expires, ), deviceId: device.id, }); - setCookie(this.c, this.c.get('core').authorization.cookieName, token, { + setCookie(this.c, this.c.get("core").authorization.cookieName, token, { httpOnly: true, - secure: this.c.get('core').authorization.cookieSecure, - path: '/', + secure: this.c.get("core").authorization.cookieSecure, + path: "/", expires: - this.c.get('core').authorization.cookie_expires > 0 + this.c.get("core").authorization.cookie_expires > 0 ? new Date( - Date.now() + this.c.get('core').authorization.cookie_expires, + Date.now() + this.c.get("core").authorization.cookie_expires, ) : undefined, domain: CONFIG.web.hostname, @@ -64,13 +64,13 @@ export class SessionModel { async deleteSession() { const token = getCookie( this.c, - this.c.get('core').authorization.cookieName, + this.c.get("core").authorization.cookieName, ); const device = await new DeviceModel(this.c).getDeviceId(); // Ensure both token and deviceId exist before proceeding if (!(token && device.id)) { - deleteCookie(this.c, this.c.get('core').authorization.cookieName); + deleteCookie(this.c, this.c.get("core").authorization.cookieName); return; } @@ -78,7 +78,7 @@ export class SessionModel { const hashedToken = await this.hashToken(token); await this.c - .get('db') + .get("db") .delete(core_sessions) // Harden the query to ensure a user can only delete their own device's session .where( @@ -88,13 +88,13 @@ export class SessionModel { ), ); - deleteCookie(this.c, this.c.get('core').authorization.cookieName); + deleteCookie(this.c, this.c.get("core").authorization.cookieName); } async getUser() { const token = getCookie( this.c, - this.c.get('core').authorization.cookieName, + this.c.get("core").authorization.cookieName, ); if (!token) return null; @@ -104,7 +104,7 @@ export class SessionModel { const hashedToken = await this.hashToken(token); const [session] = await this.c - .get('db') + .get("db") .select({ userId: core_sessions.userId, }) @@ -119,7 +119,7 @@ export class SessionModel { .limit(1); if (!session) { - deleteCookie(this.c, this.c.get('core').authorization.cookieName); + deleteCookie(this.c, this.c.get("core").authorization.cookieName); return null; } diff --git a/packages/vitnode/src/api/models/sso.ts b/packages/vitnode/src/api/models/sso.ts index 8dbb095ff..09b618096 100644 --- a/packages/vitnode/src/api/models/sso.ts +++ b/packages/vitnode/src/api/models/sso.ts @@ -1,14 +1,14 @@ -import crypto from 'node:crypto'; -import { and, eq } from 'drizzle-orm'; -import type { Context } from 'hono'; -import { deleteCookie, getCookie, setCookie } from 'hono/cookie'; -import { HTTPException } from 'hono/http-exception'; +import crypto from "node:crypto"; +import { and, eq } from "drizzle-orm"; +import type { Context } from "hono"; +import { deleteCookie, getCookie, setCookie } from "hono/cookie"; +import { HTTPException } from "hono/http-exception"; -import { core_users, core_users_sso } from '@/database/users'; -import { CONFIG } from '@/lib/config'; -import { removeSpecialCharacters } from '@/lib/special-characters'; +import { core_users, core_users_sso } from "@/database/users"; +import { CONFIG } from "@/lib/config"; +import { removeSpecialCharacters } from "@/lib/special-characters"; -import { UserModel } from './user'; +import { UserModel } from "./user"; export interface SSOApiPlugin { fetchToken: ( @@ -29,7 +29,7 @@ export const getRedirectUri = (code: string) => export class SSOModel { constructor(c: Context) { this.c = c; - this.plugins = c.get('core').authorization.ssoAdapters; + this.plugins = c.get("core").authorization.ssoAdapters; } private readonly c: Context; @@ -57,7 +57,7 @@ export class SSOModel { }, c, ); - await c.get('db').insert(core_users_sso).values({ + await c.get("db").insert(core_users_sso).values({ userId: data.id, providerId: providerId, providerAccountId: user.id, @@ -86,7 +86,7 @@ export class SSOModel { const ssoToken = await provider.fetchToken(code); const userFromSSO = await provider.fetchUser(ssoToken); - return await this.c.get('db').transaction(async tx => { + return await this.c.get("db").transaction(async tx => { const [dataSSOFromDb] = await tx .select({ userId: core_users_sso.userId, @@ -123,7 +123,7 @@ export class SSOModel { // If email exists, throw an error throw new HTTPException(409, { - message: 'Email already exists', + message: "Email already exists", }); // await tx.insert(core_users_sso).values({ // providerId: providerId, @@ -139,25 +139,25 @@ export class SSOModel { } async encryptState() { - const state = crypto.randomBytes(8).toString('hex'); + const state = crypto.randomBytes(8).toString("hex"); const encryptedState = await new Promise((resolve, reject) => { - const salt = crypto.randomBytes(4).toString('hex'); + const salt = crypto.randomBytes(4).toString("hex"); crypto.scrypt(state, salt, 16, (err, derivedKey) => { if (err) reject(err); - resolve(`${salt}:${derivedKey.toString('hex')}`); + resolve(`${salt}:${derivedKey.toString("hex")}`); }); }); setCookie( this.c, - `${this.c.get('core').authorization.cookieName}--state-sso`, + `${this.c.get("core").authorization.cookieName}--state-sso`, encryptedState, { httpOnly: true, - secure: this.c.get('core').authorization.cookieSecure, - path: '/', + secure: this.c.get("core").authorization.cookieSecure, + path: "/", domain: CONFIG.web.hostname, }, ); @@ -177,32 +177,32 @@ export class SSOModel { async verifyState(state: string) { const storedState = getCookie( this.c, - `${this.c.get('core').authorization.cookieName}--state-sso`, + `${this.c.get("core").authorization.cookieName}--state-sso`, ); if (!storedState) { throw new HTTPException(400, { - message: 'Invalid state', + message: "Invalid state", }); } const isValid = await new Promise((resolve, reject) => { - const [salt, storedHash] = storedState.split(':'); + const [salt, storedHash] = storedState.split(":"); crypto.scrypt(state, salt, 16, (err, derivedKey) => { if (err) reject(err); - resolve(storedHash === derivedKey.toString('hex')); + resolve(storedHash === derivedKey.toString("hex")); }); }); if (!isValid) { throw new HTTPException(400, { - message: 'Invalid state', + message: "Invalid state", }); } deleteCookie( this.c, - `${this.c.get('core').authorization.cookieName}--state-sso`, + `${this.c.get("core").authorization.cookieName}--state-sso`, ); } } diff --git a/packages/vitnode/src/api/models/user.ts b/packages/vitnode/src/api/models/user.ts index 06b59190b..e2b8608ff 100644 --- a/packages/vitnode/src/api/models/user.ts +++ b/packages/vitnode/src/api/models/user.ts @@ -1,6 +1,6 @@ -import { getUserById } from './user/get-user-by-id'; -import { signInWithPassword } from './user/sign-in-with-passwords'; -import { signUp } from './user/sign-up'; +import { getUserById } from "./user/get-user-by-id"; +import { signInWithPassword } from "./user/sign-in-with-passwords"; +import { signUp } from "./user/sign-up"; export class UserModel { getUserById = getUserById; diff --git a/packages/vitnode/src/api/models/user/get-user-by-id.ts b/packages/vitnode/src/api/models/user/get-user-by-id.ts index c84840801..457a0cef1 100644 --- a/packages/vitnode/src/api/models/user/get-user-by-id.ts +++ b/packages/vitnode/src/api/models/user/get-user-by-id.ts @@ -1,11 +1,11 @@ -import { eq } from 'drizzle-orm'; -import type { Context } from 'hono'; +import { eq } from "drizzle-orm"; +import type { Context } from "hono"; -import { core_users } from '@/database/users'; +import { core_users } from "@/database/users"; export const getUserById = async ({ id, c }: { c: Context; id: number }) => { const [user] = await c - .get('db') + .get("db") .select({ id: core_users.id, email: core_users.email, diff --git a/packages/vitnode/src/api/models/user/sign-in-with-passwords.ts b/packages/vitnode/src/api/models/user/sign-in-with-passwords.ts index 3ace4c6f4..a0d3fb549 100644 --- a/packages/vitnode/src/api/models/user/sign-in-with-passwords.ts +++ b/packages/vitnode/src/api/models/user/sign-in-with-passwords.ts @@ -1,10 +1,10 @@ -import { eq } from 'drizzle-orm'; -import type { Context } from 'hono'; -import { HTTPException } from 'hono/http-exception'; +import { eq } from "drizzle-orm"; +import type { Context } from "hono"; +import { HTTPException } from "hono/http-exception"; -import { core_users } from '@/database/users'; +import { core_users } from "@/database/users"; -import { PasswordModel } from '../password'; +import { PasswordModel } from "../password"; export const signInWithPassword = async ({ email, @@ -16,7 +16,7 @@ export const signInWithPassword = async ({ password: string; }) => { const [user] = await c - .get('db') + .get("db") .select({ id: core_users.id, email: core_users.email, diff --git a/packages/vitnode/src/api/models/user/sign-up.ts b/packages/vitnode/src/api/models/user/sign-up.ts index f3fa19a51..a67a0eb95 100644 --- a/packages/vitnode/src/api/models/user/sign-up.ts +++ b/packages/vitnode/src/api/models/user/sign-up.ts @@ -1,11 +1,11 @@ -import { and, count, eq, or } from 'drizzle-orm'; -import type { Context } from 'hono'; -import { HTTPException } from 'hono/http-exception'; +import { and, count, eq, or } from "drizzle-orm"; +import type { Context } from "hono"; +import { HTTPException } from "hono/http-exception"; -import { generateAvatarColor } from '@/api/modules/users/avatar-color'; -import { core_roles } from '@/database/roles'; -import { core_users } from '@/database/users'; -import { removeSpecialCharacters } from '@/lib/special-characters'; +import { generateAvatarColor } from "@/api/modules/users/avatar-color"; +import { core_roles } from "@/database/roles"; +import { core_users } from "@/database/users"; +import { removeSpecialCharacters } from "@/lib/special-characters"; const getDefaultData = async ( c: Context, @@ -14,14 +14,14 @@ const getDefaultData = async ( roleId: number; }> => { const [countUsers] = await c - .get('db') + .get("db") .select({ count: count() }) .from(core_users); // If no users, return root group if (countUsers.count === 0) { const [defaultRole] = await c - .get('db') + .get("db") .select({ id: core_roles.id, }) @@ -31,7 +31,7 @@ const getDefaultData = async ( if (!defaultRole) { throw new HTTPException(400, { - message: 'Default group not found.', + message: "Default group not found.", }); } @@ -42,7 +42,7 @@ const getDefaultData = async ( } const [defaultRole] = await c - .get('db') + .get("db") .select({ id: core_roles.id, }) @@ -52,13 +52,13 @@ const getDefaultData = async ( if (!defaultRole) { throw new HTTPException(400, { - message: 'Default role not found.', + message: "Default role not found.", }); } return { roleId: defaultRole.id, - emailVerified: !c.get('core').email?.adapter, + emailVerified: !c.get("core").email?.adapter, }; }; @@ -78,7 +78,7 @@ export const signUp = async ( ) => { const convertToNameSEO = removeSpecialCharacters(name); const checkIfUserExist = await c - .get('db') + .get("db") .select({ email: core_users.email, name_code: core_users.nameCode, @@ -94,7 +94,7 @@ export const signUp = async ( const findEmail = checkIfUserExist.find(user => user.email === email); if (findEmail) { throw new HTTPException(409, { - message: 'Email already exists', + message: "Email already exists", }); } const findName = checkIfUserExist.find( @@ -102,13 +102,13 @@ export const signUp = async ( ); if (findName) { throw new HTTPException(409, { - message: 'Name already exists', + message: "Name already exists", }); } const { roleId, emailVerified } = await getDefaultData(c); const [data] = await c - .get('db') + .get("db") .insert(core_users) .values({ email, @@ -120,7 +120,7 @@ export const signUp = async ( avatarColor: generateAvatarColor(name), roleId, emailVerified, - ipAddress: c.get('ipAddress'), + ipAddress: c.get("ipAddress"), // TODO: Handle language // language: await this.getLanguage(req), }) diff --git a/packages/vitnode/src/api/modules/admin/admin.module.ts b/packages/vitnode/src/api/modules/admin/admin.module.ts index 81cc2e8df..1593e9822 100644 --- a/packages/vitnode/src/api/modules/admin/admin.module.ts +++ b/packages/vitnode/src/api/modules/admin/admin.module.ts @@ -1,13 +1,13 @@ -import { buildModule } from '@/api/lib/module'; -import { CONFIG_PLUGIN } from '@/config'; +import { buildModule } from "@/api/lib/module"; +import { CONFIG_PLUGIN } from "@/config"; -import { debugAdminModule } from './debug/debug.admin.module'; -import { sessionAdminRoute } from './routes/session.route'; -import { usersAdminModule } from './users/users.admin.module'; +import { debugAdminModule } from "./debug/debug.admin.module"; +import { sessionAdminRoute } from "./routes/session.route"; +import { usersAdminModule } from "./users/users.admin.module"; export const adminModule = buildModule({ ...CONFIG_PLUGIN, - name: 'admin', + name: "admin", routes: [sessionAdminRoute], modules: [usersAdminModule, debugAdminModule], }); diff --git a/packages/vitnode/src/api/modules/admin/debug/debug.admin.module.ts b/packages/vitnode/src/api/modules/admin/debug/debug.admin.module.ts index 316227e1c..8df6dbfdb 100644 --- a/packages/vitnode/src/api/modules/admin/debug/debug.admin.module.ts +++ b/packages/vitnode/src/api/modules/admin/debug/debug.admin.module.ts @@ -1,9 +1,9 @@ -import { CONFIG_PLUGIN } from '../../../../config'; -import { buildModule } from '../../../lib/module'; -import { logsDebugAdminRoute } from './routes/logs.route'; +import { CONFIG_PLUGIN } from "../../../../config"; +import { buildModule } from "../../../lib/module"; +import { logsDebugAdminRoute } from "./routes/logs.route"; export const debugAdminModule = buildModule({ ...CONFIG_PLUGIN, - name: 'debug', + name: "debug", routes: [logsDebugAdminRoute], }); diff --git a/packages/vitnode/src/api/modules/admin/debug/routes/logs.route.ts b/packages/vitnode/src/api/modules/admin/debug/routes/logs.route.ts index eb3048106..a97843e18 100644 --- a/packages/vitnode/src/api/modules/admin/debug/routes/logs.route.ts +++ b/packages/vitnode/src/api/modules/admin/debug/routes/logs.route.ts @@ -1,39 +1,39 @@ -import { eq } from 'drizzle-orm'; -import { z } from 'zod'; +import { eq } from "drizzle-orm"; +import { z } from "zod"; -import { CONFIG_PLUGIN } from '@/config'; -import { core_logs } from '@/database/logs'; +import { CONFIG_PLUGIN } from "@/config"; +import { core_logs } from "@/database/logs"; -import { core_users } from '../../../../../database/users'; -import { buildRoute } from '../../../../lib/route'; +import { core_users } from "../../../../../database/users"; +import { buildRoute } from "../../../../lib/route"; import { withPagination, zodPaginationPageInfo, zodPaginationQuery, -} from '../../../../lib/with-pagination'; +} from "../../../../lib/with-pagination"; export const logsDebugAdminRoute = buildRoute({ ...CONFIG_PLUGIN, route: { - method: 'get', - description: 'Get Admin Debug Logs', - path: '/logs', + method: "get", + description: "Get Admin Debug Logs", + path: "/logs", request: { query: zodPaginationQuery.extend({ - order: z.enum(['asc', 'desc']).optional(), - orderBy: z.enum(['type', 'createdAt', 'pluginId']).optional(), + order: z.enum(["asc", "desc"]).optional(), + orderBy: z.enum(["type", "createdAt", "pluginId"]).optional(), }), }, responses: { 200: { content: { - 'application/json': { + "application/json": { schema: z.object({ edges: z.array( z.object({ id: z.number(), pluginId: z.string(), - type: z.enum(['warn', 'error', 'debug']), + type: z.enum(["warn", "error", "debug"]), content: z.string(), createdAt: z.date(), ipAddress: z.string(), @@ -54,12 +54,12 @@ export const logsDebugAdminRoute = buildRoute({ }), }, }, - description: 'List of users', + description: "List of users", }, }, }, handler: async c => { - const query = c.req.valid('query'); + const query = c.req.valid("query"); const data = await withPagination({ params: { query, @@ -67,7 +67,7 @@ export const logsDebugAdminRoute = buildRoute({ primaryCursor: core_logs.id, query: async ({ limit, where, orderBy }) => await c - .get('db') + .get("db") .select({ id: core_logs.id, pluginId: core_logs.pluginId, @@ -93,7 +93,7 @@ export const logsDebugAdminRoute = buildRoute({ table: core_logs, orderBy: { column: query.orderBy ? core_logs[query.orderBy] : core_logs.createdAt, - order: query.order ?? 'desc', + order: query.order ?? "desc", }, c, }); diff --git a/packages/vitnode/src/api/modules/admin/routes/session.route.ts b/packages/vitnode/src/api/modules/admin/routes/session.route.ts index 4de23c1eb..a6d861f26 100644 --- a/packages/vitnode/src/api/modules/admin/routes/session.route.ts +++ b/packages/vitnode/src/api/modules/admin/routes/session.route.ts @@ -1,19 +1,19 @@ -import { HTTPException } from 'hono/http-exception'; -import { z } from 'zod'; +import { HTTPException } from "hono/http-exception"; +import { z } from "zod"; -import { buildRoute } from '@/api/lib/route'; -import { CONFIG_PLUGIN } from '@/config'; +import { buildRoute } from "@/api/lib/route"; +import { CONFIG_PLUGIN } from "@/config"; export const sessionAdminRoute = buildRoute({ ...CONFIG_PLUGIN, route: { - method: 'get', - description: 'Verify admin session', - path: '/session', + method: "get", + description: "Verify admin session", + path: "/session", responses: { 200: { content: { - 'application/json': { + "application/json": { schema: z.object({ user: z.object({ id: z.number(), @@ -31,15 +31,15 @@ export const sessionAdminRoute = buildRoute({ }), }, }, - description: 'User', + description: "User", }, 403: { - description: 'Access Denied', + description: "Access Denied", }, }, }, handler: c => { - const user = c.get('admin')?.user; + const user = c.get("admin")?.user; if (!user) throw new HTTPException(403); return c.json({ diff --git a/packages/vitnode/src/api/modules/admin/users/routes/list.route.ts b/packages/vitnode/src/api/modules/admin/users/routes/list.route.ts index c509de84c..64af77bce 100644 --- a/packages/vitnode/src/api/modules/admin/users/routes/list.route.ts +++ b/packages/vitnode/src/api/modules/admin/users/routes/list.route.ts @@ -1,30 +1,30 @@ -import { z } from '@hono/zod-openapi'; +import { z } from "@hono/zod-openapi"; -import { buildRoute } from '@/api/lib/route'; +import { buildRoute } from "@/api/lib/route"; import { withPagination, zodPaginationPageInfo, zodPaginationQuery, -} from '@/api/lib/with-pagination'; -import { CONFIG_PLUGIN } from '@/config'; -import { core_users } from '@/database/users'; +} from "@/api/lib/with-pagination"; +import { CONFIG_PLUGIN } from "@/config"; +import { core_users } from "@/database/users"; export const listUsersAdminRoute = buildRoute({ ...CONFIG_PLUGIN, route: { - method: 'get', - description: 'Get list of all users', - path: '/list', + method: "get", + description: "Get list of all users", + path: "/list", request: { query: zodPaginationQuery.extend({ - order: z.enum(['asc', 'desc']).optional(), - orderBy: z.enum(['name', 'createdAt']).optional(), + order: z.enum(["asc", "desc"]).optional(), + orderBy: z.enum(["name", "createdAt"]).optional(), }), }, responses: { 200: { content: { - 'application/json': { + "application/json": { schema: z.object({ edges: z.array( z.object({ @@ -45,12 +45,12 @@ export const listUsersAdminRoute = buildRoute({ }), }, }, - description: 'List of users', + description: "List of users", }, }, }, handler: async c => { - const query = c.req.valid('query'); + const query = c.req.valid("query"); const data = await withPagination({ params: { query, @@ -58,7 +58,7 @@ export const listUsersAdminRoute = buildRoute({ primaryCursor: core_users.id, query: async ({ limit, where, orderBy }) => await c - .get('db') + .get("db") .select({ id: core_users.id, name: core_users.name, @@ -81,7 +81,7 @@ export const listUsersAdminRoute = buildRoute({ column: query.orderBy ? core_users[query.orderBy] : core_users.createdAt, - order: query.order ?? 'desc', + order: query.order ?? "desc", }, c, }); diff --git a/packages/vitnode/src/api/modules/admin/users/routes/users.route.ts b/packages/vitnode/src/api/modules/admin/users/routes/users.route.ts index d88239110..43472246a 100644 --- a/packages/vitnode/src/api/modules/admin/users/routes/users.route.ts +++ b/packages/vitnode/src/api/modules/admin/users/routes/users.route.ts @@ -1,30 +1,30 @@ -import { z } from '@hono/zod-openapi'; +import { z } from "@hono/zod-openapi"; -import { buildRoute } from '@/api/lib/route'; +import { buildRoute } from "@/api/lib/route"; import { withPagination, zodPaginationPageInfo, zodPaginationQuery, -} from '@/api/lib/with-pagination'; -import { CONFIG_PLUGIN } from '@/config'; -import { core_users } from '@/database/users'; +} from "@/api/lib/with-pagination"; +import { CONFIG_PLUGIN } from "@/config"; +import { core_users } from "@/database/users"; export const usersAdminRoute = buildRoute({ ...CONFIG_PLUGIN, route: { - method: 'get', - description: 'Get list of all users (Admin only)', - path: '/list', + method: "get", + description: "Get list of all users (Admin only)", + path: "/list", request: { query: zodPaginationQuery.extend({ - order: z.enum(['asc', 'desc']).optional(), - orderBy: z.enum(['id', 'name', 'email', 'createdAt']).optional(), + order: z.enum(["asc", "desc"]).optional(), + orderBy: z.enum(["id", "name", "email", "createdAt"]).optional(), }), }, responses: { 200: { content: { - 'application/json': { + "application/json": { schema: z.object({ edges: z.array( z.object({ @@ -45,15 +45,15 @@ export const usersAdminRoute = buildRoute({ }), }, }, - description: 'List of users', + description: "List of users", }, 403: { - description: 'Access Denied', + description: "Access Denied", }, }, }, handler: async c => { - const query = c.req.valid('query'); + const query = c.req.valid("query"); const data = await withPagination({ params: { query, @@ -61,7 +61,7 @@ export const usersAdminRoute = buildRoute({ primaryCursor: core_users.id, query: async ({ limit, where, orderBy }) => await c - .get('db') + .get("db") .select({ id: core_users.id, name: core_users.name, @@ -84,7 +84,7 @@ export const usersAdminRoute = buildRoute({ column: query.orderBy ? core_users[query.orderBy] : core_users.createdAt, - order: query.order ?? 'desc', + order: query.order ?? "desc", }, c, }); diff --git a/packages/vitnode/src/api/modules/admin/users/users.admin.module.ts b/packages/vitnode/src/api/modules/admin/users/users.admin.module.ts index 6584983a7..3b31ce8db 100644 --- a/packages/vitnode/src/api/modules/admin/users/users.admin.module.ts +++ b/packages/vitnode/src/api/modules/admin/users/users.admin.module.ts @@ -1,10 +1,10 @@ -import { buildModule } from '@/api/lib/module'; -import { CONFIG_PLUGIN } from '@/config'; +import { buildModule } from "@/api/lib/module"; +import { CONFIG_PLUGIN } from "@/config"; -import { listUsersAdminRoute } from './routes/list.route'; +import { listUsersAdminRoute } from "./routes/list.route"; export const usersAdminModule = buildModule({ ...CONFIG_PLUGIN, - name: 'users', + name: "users", routes: [listUsersAdminRoute], }); diff --git a/packages/vitnode/src/api/modules/middleware/middleware.module.ts b/packages/vitnode/src/api/modules/middleware/middleware.module.ts index 9b229b429..0158f4472 100644 --- a/packages/vitnode/src/api/modules/middleware/middleware.module.ts +++ b/packages/vitnode/src/api/modules/middleware/middleware.module.ts @@ -1,10 +1,10 @@ -import { buildModule } from '@/api/lib/module'; -import { CONFIG_PLUGIN } from '@/config'; +import { buildModule } from "@/api/lib/module"; +import { CONFIG_PLUGIN } from "@/config"; -import { routeMiddleware } from './route'; +import { routeMiddleware } from "./route"; export const middlewareModule = buildModule({ ...CONFIG_PLUGIN, - name: 'middleware', + name: "middleware", routes: [routeMiddleware], }); diff --git a/packages/vitnode/src/api/modules/middleware/route.ts b/packages/vitnode/src/api/modules/middleware/route.ts index 2f25bd22b..202054dd2 100644 --- a/packages/vitnode/src/api/modules/middleware/route.ts +++ b/packages/vitnode/src/api/modules/middleware/route.ts @@ -1,7 +1,7 @@ -import { z } from 'zod'; +import { z } from "zod"; -import { buildRoute } from '@/api/lib/route'; -import { CONFIG_PLUGIN } from '@/config'; +import { buildRoute } from "@/api/lib/route"; +import { CONFIG_PLUGIN } from "@/config"; export const routeMiddlewareSchema = z.object({ sso: z.array(z.object({ id: z.string(), name: z.string() })), @@ -9,7 +9,7 @@ export const routeMiddlewareSchema = z.object({ captcha: z .object({ siteKey: z.string(), - type: z.enum(['cloudflare_turnstile', 'recaptcha_v3']), + type: z.enum(["cloudflare_turnstile", "recaptcha_v3"]), }) .optional(), }); @@ -17,31 +17,31 @@ export const routeMiddlewareSchema = z.object({ export const routeMiddleware = buildRoute({ ...CONFIG_PLUGIN, route: { - path: '/', - method: 'get', - description: 'Middleware route with user authentication', + path: "/", + method: "get", + description: "Middleware route with user authentication", responses: { 200: { content: { - 'application/json': { + "application/json": { schema: routeMiddlewareSchema, }, }, - description: 'Middleware route', + description: "Middleware route", }, }, }, handler: c => { - const sso = c.get('core').authorization.ssoAdapters; + const sso = c.get("core").authorization.ssoAdapters; return c.json( { - isEmail: !!c.get('core').email?.adapter, + isEmail: !!c.get("core").email?.adapter, sso: sso.map(s => ({ id: s.id, name: s.name })), - captcha: c.get('core').captcha + captcha: c.get("core").captcha ? { - siteKey: c.get('core').captcha?.siteKey ?? '', - type: c.get('core').captcha?.type ?? 'cloudflare_turnstile', + siteKey: c.get("core").captcha?.siteKey ?? "", + type: c.get("core").captcha?.type ?? "cloudflare_turnstile", } : undefined, }, diff --git a/packages/vitnode/src/api/modules/users/avatar-color.ts b/packages/vitnode/src/api/modules/users/avatar-color.ts index 0c715993d..99b355957 100644 --- a/packages/vitnode/src/api/modules/users/avatar-color.ts +++ b/packages/vitnode/src/api/modules/users/avatar-color.ts @@ -1,4 +1,4 @@ -import { convertColor } from '@/lib/colors'; +import { convertColor } from "@/lib/colors"; const getHashOfString = (str: string) => { let hash = 0; diff --git a/packages/vitnode/src/api/modules/users/routes/change-password.route.ts b/packages/vitnode/src/api/modules/users/routes/change-password.route.ts index a42d0a0d1..532cd0747 100644 --- a/packages/vitnode/src/api/modules/users/routes/change-password.route.ts +++ b/packages/vitnode/src/api/modules/users/routes/change-password.route.ts @@ -1,31 +1,31 @@ -import { and, eq, gt } from 'drizzle-orm'; -import { HTTPException } from 'hono/http-exception'; -import { z } from 'zod'; +import { and, eq, gt } from "drizzle-orm"; +import { HTTPException } from "hono/http-exception"; +import { z } from "zod"; -import { buildRoute } from '@/api/lib/route'; -import { PasswordModel } from '@/api/models/password'; -import { CONFIG_PLUGIN } from '@/config'; -import { core_users, core_users_forgot_password } from '@/database/users'; +import { buildRoute } from "@/api/lib/route"; +import { PasswordModel } from "@/api/models/password"; +import { CONFIG_PLUGIN } from "@/config"; +import { core_users, core_users_forgot_password } from "@/database/users"; export const zodChangePasswordSchema = z.object({ password: z.string().min(8).openapi({ - example: 'Test123!', + example: "Test123!", }), userId: z.number().openapi({ example: 123456 }), - token: z.string().openapi({ example: 'abcdefg12345' }), + token: z.string().openapi({ example: "abcdefg12345" }), }); export const changePasswordRoute = buildRoute({ ...CONFIG_PLUGIN, route: { - method: 'post', - description: 'Change user password', - path: '/change-password', + method: "post", + description: "Change user password", + path: "/change-password", request: { body: { required: true, content: { - 'application/json': { + "application/json": { schema: zodChangePasswordSchema, }, }, @@ -33,15 +33,15 @@ export const changePasswordRoute = buildRoute({ }, responses: { 201: { - description: 'Password changed', + description: "Password changed", }, }, }, handler: async c => { - const { password, userId, token } = c.req.valid('json'); + const { password, userId, token } = c.req.valid("json"); const [user] = await c - .get('db') + .get("db") .select() .from(core_users_forgot_password) .where( @@ -54,22 +54,22 @@ export const changePasswordRoute = buildRoute({ .limit(1); if (!user) { - throw new HTTPException(400, { message: 'Invalid token' }); + throw new HTTPException(400, { message: "Invalid token" }); } const hashPassword = await new PasswordModel().encryptPassword(password); await Promise.all([ c - .get('db') + .get("db") .update(core_users) .set({ password: hashPassword }) .where(eq(core_users.id, userId)), c - .get('db') + .get("db") .delete(core_users_forgot_password) .where(eq(core_users_forgot_password.id, user.id)), ]); - return c.text('Password changed', 201); + return c.text("Password changed", 201); }, }); diff --git a/packages/vitnode/src/api/modules/users/routes/reset-passowrd.route.ts b/packages/vitnode/src/api/modules/users/routes/reset-passowrd.route.ts index 001a10169..108b4f7ce 100644 --- a/packages/vitnode/src/api/modules/users/routes/reset-passowrd.route.ts +++ b/packages/vitnode/src/api/modules/users/routes/reset-passowrd.route.ts @@ -1,29 +1,29 @@ -import { eq } from 'drizzle-orm'; -import { createTranslator } from 'use-intl'; -import { z } from 'zod'; +import { eq } from "drizzle-orm"; +import { createTranslator } from "use-intl"; +import { z } from "zod"; -import { buildRoute } from '@/api/lib/route'; -import { ForgotPasswordTokenModel } from '@/api/models/password'; -import { CONFIG_PLUGIN } from '@/config'; -import { core_users, core_users_forgot_password } from '@/database/users'; -import ResetPasswordEmailTemplate from '@/emails/reset-password'; -import { CONFIG } from '@/lib/config'; +import { buildRoute } from "@/api/lib/route"; +import { ForgotPasswordTokenModel } from "@/api/models/password"; +import { CONFIG_PLUGIN } from "@/config"; +import { core_users, core_users_forgot_password } from "@/database/users"; +import ResetPasswordEmailTemplate from "@/emails/reset-password"; +import { CONFIG } from "@/lib/config"; export const resetPasswordRoute = buildRoute({ ...CONFIG_PLUGIN, route: { - method: 'post', - description: 'Request a password reset', - path: '/reset-password', + method: "post", + description: "Request a password reset", + path: "/reset-password", withCaptcha: true, request: { body: { required: true, content: { - 'application/json': { + "application/json": { schema: z.object({ email: z.email().toLowerCase().openapi({ - example: 'test@test.com', + example: "test@test.com", }), }), }, @@ -32,15 +32,15 @@ export const resetPasswordRoute = buildRoute({ }, responses: { 201: { - description: 'Email sent', + description: "Email sent", }, }, }, handler: async c => { - const RESPONSE_TEXT = c.text('Email sent', 201); - const { email } = c.req.valid('json'); + const RESPONSE_TEXT = c.text("Email sent", 201); + const { email } = c.req.valid("json"); const [findUser] = await c - .get('db') + .get("db") .select({ email: core_users.email, id: core_users.id, @@ -57,7 +57,7 @@ export const resetPasswordRoute = buildRoute({ const hashToken = new ForgotPasswordTokenModel().generateResetToken(); const [findLastRecord] = await c - .get('db') + .get("db") .select() .from(core_users_forgot_password) .where(eq(core_users_forgot_password.userId, findUser.id)) @@ -72,22 +72,22 @@ export const resetPasswordRoute = buildRoute({ if (findLastRecord) { await c - .get('db') + .get("db") .update(core_users_forgot_password) .set({ createdAt: new Date(), expiresAt: EXPIRES_AT, token: hashToken, - ipAddress: c.get('ipAddress'), + ipAddress: c.get("ipAddress"), }) .where(eq(core_users_forgot_password.id, findLastRecord.id)); } else { await c - .get('db') + .get("db") .insert(core_users_forgot_password) .values({ token: hashToken, - ipAddress: c.get('ipAddress'), + ipAddress: c.get("ipAddress"), userId: findUser.id, expiresAt: EXPIRES_AT, }); @@ -99,7 +99,7 @@ export const resetPasswordRoute = buildRoute({ CONFIG.web.href, ); - await c.get('email').send({ + await c.get("email").send({ user: { id: findUser.id, email: findUser.email, @@ -110,12 +110,12 @@ export const resetPasswordRoute = buildRoute({ ...props, resetUrl: resetUrlNative.href, expiryDate: EXPIRES_AT, - userIpAddress: c.get('ipAddress'), + userIpAddress: c.get("ipAddress"), }), subject: ({ i18n }) => { const t = createTranslator(i18n); - return t('core.auth.reset_password.email.subject'); + return t("core.auth.reset_password.email.subject"); }, }); diff --git a/packages/vitnode/src/api/modules/users/routes/session.route.ts b/packages/vitnode/src/api/modules/users/routes/session.route.ts index e48b5bbe3..e9d0066e2 100644 --- a/packages/vitnode/src/api/modules/users/routes/session.route.ts +++ b/packages/vitnode/src/api/modules/users/routes/session.route.ts @@ -1,19 +1,19 @@ -import { z } from 'zod'; +import { z } from "zod"; -import { buildRoute } from '@/api/lib/route'; -import { SessionAdminModel } from '@/api/models/session-admin'; -import { CONFIG_PLUGIN } from '@/config'; +import { buildRoute } from "@/api/lib/route"; +import { SessionAdminModel } from "@/api/models/session-admin"; +import { CONFIG_PLUGIN } from "@/config"; export const sessionRoute = buildRoute({ ...CONFIG_PLUGIN, route: { - method: 'get', - description: 'Verify session', - path: '/session', + method: "get", + description: "Verify session", + path: "/session", responses: { 200: { content: { - 'application/json': { + "application/json": { schema: z.object({ user: z .object({ @@ -33,12 +33,12 @@ export const sessionRoute = buildRoute({ }), }, }, - description: 'User', + description: "User", }, }, }, handler: async c => { - const user = c.get('user'); + const user = c.get("user"); const admin = new SessionAdminModel(c); return c.json({ diff --git a/packages/vitnode/src/api/modules/users/routes/sign-in.route.ts b/packages/vitnode/src/api/modules/users/routes/sign-in.route.ts index 8fad0b330..6d3c2b681 100644 --- a/packages/vitnode/src/api/modules/users/routes/sign-in.route.ts +++ b/packages/vitnode/src/api/modules/users/routes/sign-in.route.ts @@ -1,17 +1,17 @@ -import { z } from 'zod'; +import { z } from "zod"; -import { buildRoute } from '@/api/lib/route'; -import { SessionModel } from '@/api/models/session'; -import { SessionAdminModel } from '@/api/models/session-admin'; -import { UserModel } from '@/api/models/user'; -import { CONFIG_PLUGIN } from '@/config'; +import { buildRoute } from "@/api/lib/route"; +import { SessionModel } from "@/api/models/session"; +import { SessionAdminModel } from "@/api/models/session-admin"; +import { UserModel } from "@/api/models/user"; +import { CONFIG_PLUGIN } from "@/config"; export const zodSignInSchema = z.object({ email: z.email().toLowerCase().openapi({ - example: 'test@test.com', + example: "test@test.com", }), password: z.string().openapi({ - example: 'Test123!', + example: "Test123!", }), isAdmin: z.boolean().optional().openapi({ example: false, @@ -21,14 +21,14 @@ export const zodSignInSchema = z.object({ export const signInRoute = buildRoute({ ...CONFIG_PLUGIN, route: { - method: 'post', - description: 'Sign in with email and password', - path: '/sign_in', + method: "post", + description: "Sign in with email and password", + path: "/sign_in", request: { body: { required: true, content: { - 'application/json': { + "application/json": { schema: zodSignInSchema, }, }, @@ -36,23 +36,23 @@ export const signInRoute = buildRoute({ }, responses: { 403: { - description: 'Access Denied', + description: "Access Denied", }, 201: { content: { - 'application/json': { + "application/json": { schema: z.object({ id: z.number(), token: z.string(), }), }, }, - description: 'User signed in', + description: "User signed in", }, }, }, handler: async c => { - const { password, isAdmin, email } = c.req.valid('json'); + const { password, isAdmin, email } = c.req.valid("json"); const data = await new UserModel().signInWithPassword({ password, email, diff --git a/packages/vitnode/src/api/modules/users/routes/sign-out.route.ts b/packages/vitnode/src/api/modules/users/routes/sign-out.route.ts index f26e76912..ef2b46ce4 100644 --- a/packages/vitnode/src/api/modules/users/routes/sign-out.route.ts +++ b/packages/vitnode/src/api/modules/users/routes/sign-out.route.ts @@ -1,20 +1,20 @@ -import { z } from '@hono/zod-openapi'; +import { z } from "@hono/zod-openapi"; -import { buildRoute } from '@/api/lib/route'; -import { SessionModel } from '@/api/models/session'; -import { SessionAdminModel } from '@/api/models/session-admin'; -import { CONFIG_PLUGIN } from '@/config'; +import { buildRoute } from "@/api/lib/route"; +import { SessionModel } from "@/api/models/session"; +import { SessionAdminModel } from "@/api/models/session-admin"; +import { CONFIG_PLUGIN } from "@/config"; export const signOutRoute = buildRoute({ ...CONFIG_PLUGIN, route: { - method: 'delete', - description: 'Sign out the current admin', - path: '/sign_out', + method: "delete", + description: "Sign out the current admin", + path: "/sign_out", request: { body: { content: { - 'application/json': { + "application/json": { schema: z.object({ isAdmin: z.boolean().optional().openapi({ example: false, @@ -26,15 +26,15 @@ export const signOutRoute = buildRoute({ }, responses: { 200: { - description: 'User signed out', + description: "User signed out", }, 403: { - description: 'Access Denied', + description: "Access Denied", }, }, }, handler: async c => { - const { isAdmin } = c.req.valid('json'); + const { isAdmin } = c.req.valid("json"); if (isAdmin) { await new SessionAdminModel(c).deleteSession(); diff --git a/packages/vitnode/src/api/modules/users/routes/sign-up.route.ts b/packages/vitnode/src/api/modules/users/routes/sign-up.route.ts index 57c7a1f12..a1b2f9f7e 100644 --- a/packages/vitnode/src/api/modules/users/routes/sign-up.route.ts +++ b/packages/vitnode/src/api/modules/users/routes/sign-up.route.ts @@ -1,27 +1,27 @@ -import { z } from 'zod'; +import { z } from "zod"; -import { buildRoute } from '@/api/lib/route'; -import { PasswordModel } from '@/api/models/password'; -import { UserModel } from '@/api/models/user'; -import { CONFIG_PLUGIN } from '@/config'; +import { buildRoute } from "@/api/lib/route"; +import { PasswordModel } from "@/api/models/password"; +import { UserModel } from "@/api/models/user"; +import { CONFIG_PLUGIN } from "@/config"; -import { SessionModel } from '../../../models/session'; +import { SessionModel } from "../../../models/session"; const nameRegex = /^(?!.* {2})[\p{L}\p{N}._@ -]*$/u; export const zodSignUpSchema = z.object({ email: z.email().toLowerCase().openapi({ - example: 'test@test.com', + example: "test@test.com", }), name: z .string() - .openapi({ example: 'test' }) + .openapi({ example: "test" }) .min(3) .refine(val => nameRegex.test(val), { - message: 'Invalid name', + message: "Invalid name", }), password: z.string().min(8).openapi({ - example: 'Test123!', + example: "Test123!", }), newsletter: z.boolean().default(false).optional(), }); @@ -29,15 +29,15 @@ export const zodSignUpSchema = z.object({ export const signUpRoute = buildRoute({ ...CONFIG_PLUGIN, route: { - method: 'post', - description: 'Create a new user', - path: '/sign_up', + method: "post", + description: "Create a new user", + path: "/sign_up", withCaptcha: true, request: { body: { required: true, content: { - 'application/json': { + "application/json": { schema: zodSignUpSchema, }, }, @@ -46,7 +46,7 @@ export const signUpRoute = buildRoute({ responses: { 201: { content: { - 'application/json': { + "application/json": { schema: z.object({ id: z.number(), emailVerified: z.boolean(), @@ -54,22 +54,22 @@ export const signUpRoute = buildRoute({ }), }, }, - description: 'User created', + description: "User created", }, 400: { - description: 'Bad Request', + description: "Bad Request", }, 409: { - description: 'Email or name already exists', + description: "Email or name already exists", }, }, }, handler: async c => { const hashedPassword = await new PasswordModel().encryptPassword( - c.req.valid('json').password, + c.req.valid("json").password, ); const data = await new UserModel().signUp( - { ...c.req.valid('json'), hashedPassword }, + { ...c.req.valid("json"), hashedPassword }, c, ); diff --git a/packages/vitnode/src/api/modules/users/routes/test.route.ts b/packages/vitnode/src/api/modules/users/routes/test.route.ts index 6bc597294..489c74bb3 100644 --- a/packages/vitnode/src/api/modules/users/routes/test.route.ts +++ b/packages/vitnode/src/api/modules/users/routes/test.route.ts @@ -1,36 +1,36 @@ -import { z } from 'zod'; +import { z } from "zod"; -import { buildRoute } from '@/api/lib/route'; -import { CONFIG_PLUGIN } from '@/config'; +import { buildRoute } from "@/api/lib/route"; +import { CONFIG_PLUGIN } from "@/config"; export const testRoute = buildRoute({ ...CONFIG_PLUGIN, route: { - method: 'post', - description: 'Test route', - path: '/test', + method: "post", + description: "Test route", + path: "/test", responses: { 200: { content: { - 'text/plain': { + "text/plain": { schema: z.string(), }, }, - description: 'User', + description: "User", }, 201: { content: { - 'text/plain': { + "text/plain": { schema: z.string(), }, }, - description: 'User', + description: "User", }, }, }, handler: async c => { - await c.get('log').warn('This is a test warn log'); + await c.get("log").warn("This is a test warn log"); - return c.text('test'); + return c.text("test"); }, }); diff --git a/packages/vitnode/src/api/modules/users/sso/routes/callback.route.ts b/packages/vitnode/src/api/modules/users/sso/routes/callback.route.ts index de9d91fff..ab928594f 100644 --- a/packages/vitnode/src/api/modules/users/sso/routes/callback.route.ts +++ b/packages/vitnode/src/api/modules/users/sso/routes/callback.route.ts @@ -1,16 +1,16 @@ -import { z } from 'zod'; +import { z } from "zod"; -import { buildRoute } from '@/api/lib/route'; -import { SessionModel } from '@/api/models/session'; -import { SSOModel } from '@/api/models/sso'; -import { CONFIG_PLUGIN } from '@/config'; +import { buildRoute } from "@/api/lib/route"; +import { SessionModel } from "@/api/models/session"; +import { SSOModel } from "@/api/models/sso"; +import { CONFIG_PLUGIN } from "@/config"; export const callbackRoute = buildRoute({ ...CONFIG_PLUGIN, route: { - method: 'get', - description: 'SSO Callback', - path: '/{providerId}/callback', + method: "get", + description: "SSO Callback", + path: "/{providerId}/callback", request: { params: z.object({ providerId: z.string(), @@ -23,23 +23,23 @@ export const callbackRoute = buildRoute({ responses: { 200: { content: { - 'application/json': { + "application/json": { schema: z.object({ id: z.number(), token: z.string(), }), }, }, - description: 'URL', + description: "URL", }, 409: { - description: 'Email already exists', + description: "Email already exists", }, }, }, handler: async c => { - const { providerId } = c.req.valid('param'); - const { code, state } = c.req.valid('query'); + const { providerId } = c.req.valid("param"); + const { code, state } = c.req.valid("query"); const sso = await new SSOModel(c).callback({ providerId, code, state }); const { token } = await new SessionModel(c).createSessionByUserId( sso.userId, diff --git a/packages/vitnode/src/api/modules/users/sso/routes/create-url.route.ts b/packages/vitnode/src/api/modules/users/sso/routes/create-url.route.ts index 4c4ea3b67..32deb959a 100644 --- a/packages/vitnode/src/api/modules/users/sso/routes/create-url.route.ts +++ b/packages/vitnode/src/api/modules/users/sso/routes/create-url.route.ts @@ -1,15 +1,15 @@ -import { z } from 'zod'; +import { z } from "zod"; -import { buildRoute } from '@/api/lib/route'; -import { SSOModel } from '@/api/models/sso'; -import { CONFIG_PLUGIN } from '@/config'; +import { buildRoute } from "@/api/lib/route"; +import { SSOModel } from "@/api/models/sso"; +import { CONFIG_PLUGIN } from "@/config"; export const createUrlRoute = buildRoute({ ...CONFIG_PLUGIN, route: { - method: 'post', - description: 'Generate SSO URL', - path: '/{providerId}', + method: "post", + description: "Generate SSO URL", + path: "/{providerId}", request: { params: z.object({ providerId: z.string(), @@ -18,16 +18,16 @@ export const createUrlRoute = buildRoute({ responses: { 200: { content: { - 'application/json': { + "application/json": { schema: z.object({ url: z.string() }), }, }, - description: 'URL', + description: "URL", }, }, }, handler: async c => { - const { providerId } = c.req.valid('param'); + const { providerId } = c.req.valid("param"); const url = await new SSOModel(c).getUrl(providerId); return c.json({ diff --git a/packages/vitnode/src/api/modules/users/sso/sso.module.ts b/packages/vitnode/src/api/modules/users/sso/sso.module.ts index 0d4196ed7..dd9df7e54 100644 --- a/packages/vitnode/src/api/modules/users/sso/sso.module.ts +++ b/packages/vitnode/src/api/modules/users/sso/sso.module.ts @@ -1,11 +1,11 @@ -import { buildModule } from '@/api/lib/module'; -import { CONFIG_PLUGIN } from '@/config'; +import { buildModule } from "@/api/lib/module"; +import { CONFIG_PLUGIN } from "@/config"; -import { callbackRoute } from './routes/callback.route'; -import { createUrlRoute } from './routes/create-url.route'; +import { callbackRoute } from "./routes/callback.route"; +import { createUrlRoute } from "./routes/create-url.route"; export const ssoUserModule = buildModule({ ...CONFIG_PLUGIN, - name: 'sso', + name: "sso", routes: [callbackRoute, createUrlRoute], }); diff --git a/packages/vitnode/src/api/modules/users/users.module.ts b/packages/vitnode/src/api/modules/users/users.module.ts index c6f1293f9..b3e4e0223 100644 --- a/packages/vitnode/src/api/modules/users/users.module.ts +++ b/packages/vitnode/src/api/modules/users/users.module.ts @@ -1,18 +1,18 @@ -import { buildModule } from '@/api/lib/module'; -import { CONFIG_PLUGIN } from '@/config'; +import { buildModule } from "@/api/lib/module"; +import { CONFIG_PLUGIN } from "@/config"; -import { changePasswordRoute } from './routes/change-password.route'; -import { resetPasswordRoute } from './routes/reset-passowrd.route'; -import { sessionRoute } from './routes/session.route'; -import { signInRoute } from './routes/sign-in.route'; -import { signOutRoute } from './routes/sign-out.route'; -import { signUpRoute } from './routes/sign-up.route'; -import { testRoute } from './routes/test.route'; -import { ssoUserModule } from './sso/sso.module'; +import { changePasswordRoute } from "./routes/change-password.route"; +import { resetPasswordRoute } from "./routes/reset-passowrd.route"; +import { sessionRoute } from "./routes/session.route"; +import { signInRoute } from "./routes/sign-in.route"; +import { signOutRoute } from "./routes/sign-out.route"; +import { signUpRoute } from "./routes/sign-up.route"; +import { testRoute } from "./routes/test.route"; +import { ssoUserModule } from "./sso/sso.module"; export const usersModule = buildModule({ ...CONFIG_PLUGIN, - name: 'users', + name: "users", routes: [ sessionRoute, signInRoute, diff --git a/packages/vitnode/src/api/plugin.ts b/packages/vitnode/src/api/plugin.ts index 54e6bb0fd..70f15100a 100644 --- a/packages/vitnode/src/api/plugin.ts +++ b/packages/vitnode/src/api/plugin.ts @@ -1,9 +1,9 @@ -import { CONFIG_PLUGIN } from '@/config'; +import { CONFIG_PLUGIN } from "@/config"; -import { buildApiPlugin } from './lib/plugin'; -import { adminModule } from './modules/admin/admin.module'; -import { middlewareModule } from './modules/middleware/middleware.module'; -import { usersModule } from './modules/users/users.module'; +import { buildApiPlugin } from "./lib/plugin"; +import { adminModule } from "./modules/admin/admin.module"; +import { middlewareModule } from "./modules/middleware/middleware.module"; +import { usersModule } from "./modules/users/users.module"; export const newBuildPluginApiCore = buildApiPlugin({ ...CONFIG_PLUGIN, diff --git a/packages/vitnode/src/app/login/page.tsx b/packages/vitnode/src/app/login/page.tsx index 3493152a8..66b7cf78a 100644 --- a/packages/vitnode/src/app/login/page.tsx +++ b/packages/vitnode/src/app/login/page.tsx @@ -1,8 +1,8 @@ -import type { Metadata } from 'next/dist/types'; +import type { Metadata } from "next/dist/types"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; -import { SignInView } from '../../views/auth/sign-in/sign-in-view'; +import { SignInView } from "../../views/auth/sign-in/sign-in-view"; export const generateMetadata = async ({ params, @@ -10,10 +10,10 @@ export const generateMetadata = async ({ params: Promise<{ locale: string }>; }): Promise => { const { locale } = await params; - const t = await getTranslations({ locale, namespace: 'core.global' }); + const t = await getTranslations({ locale, namespace: "core.global" }); return { - title: t('login'), + title: t("login"), }; }; diff --git a/packages/vitnode/src/app/login/reset-password/page.tsx b/packages/vitnode/src/app/login/reset-password/page.tsx index e363ee2a5..e9f49711e 100644 --- a/packages/vitnode/src/app/login/reset-password/page.tsx +++ b/packages/vitnode/src/app/login/reset-password/page.tsx @@ -1,8 +1,8 @@ -import type { Metadata } from 'next/dist/types'; +import type { Metadata } from "next/dist/types"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; -import { PasswordResetView } from '@/views/auth/password-reset/password-reset-view'; +import { PasswordResetView } from "@/views/auth/password-reset/password-reset-view"; export const generateMetadata = async ({ params, @@ -12,11 +12,11 @@ export const generateMetadata = async ({ const { locale } = await params; const t = await getTranslations({ locale, - namespace: 'core.auth.reset_password', + namespace: "core.auth.reset_password", }); return { - title: t('title'), + title: t("title"), }; }; diff --git a/packages/vitnode/src/app/login/sso/[providerId]/page.tsx b/packages/vitnode/src/app/login/sso/[providerId]/page.tsx index 9e8b6aa07..b9d1b7895 100644 --- a/packages/vitnode/src/app/login/sso/[providerId]/page.tsx +++ b/packages/vitnode/src/app/login/sso/[providerId]/page.tsx @@ -1,4 +1,4 @@ -import { CallbackSSOView } from '@/views/auth/sso/callback/callback-sso-view'; +import { CallbackSSOView } from "@/views/auth/sso/callback/callback-sso-view"; export default async function Page({ params, diff --git a/packages/vitnode/src/app/register/page.tsx b/packages/vitnode/src/app/register/page.tsx index 8ba66aec9..004422387 100644 --- a/packages/vitnode/src/app/register/page.tsx +++ b/packages/vitnode/src/app/register/page.tsx @@ -1,8 +1,8 @@ -import type { Metadata } from 'next/dist/types'; +import type { Metadata } from "next/dist/types"; -import { getTranslations } from 'next-intl/server'; +import { getTranslations } from "next-intl/server"; -import { SignUpView } from '../../views/auth/sign-up/sign-up-view'; +import { SignUpView } from "../../views/auth/sign-up/sign-up-view"; export const generateMetadata = async ({ params, @@ -10,10 +10,10 @@ export const generateMetadata = async ({ params: Promise<{ locale: string }>; }): Promise => { const { locale } = await params; - const t = await getTranslations({ locale, namespace: 'core.global' }); + const t = await getTranslations({ locale, namespace: "core.global" }); return { - title: t('register'), + title: t("register"), }; }; diff --git a/packages/vitnode/src/app_admin/core/debug/page.tsx b/packages/vitnode/src/app_admin/core/debug/page.tsx index 6ecb54b24..fa46ae5fe 100644 --- a/packages/vitnode/src/app_admin/core/debug/page.tsx +++ b/packages/vitnode/src/app_admin/core/debug/page.tsx @@ -1,14 +1,14 @@ -import dynamic from 'next/dynamic'; -import { getTranslations } from 'next-intl/server'; -import React from 'react'; +import dynamic from "next/dynamic"; +import { getTranslations } from "next-intl/server"; +import React from "react"; -import { I18nProvider } from '@/components/i18n-provider'; -import { DataTableSkeleton } from '@/components/table/data-table'; -import { HeaderContent } from '@/components/ui/header-content'; -import { ClearCacheAction } from '@/views/admin/views/core/debug/actions/clear-cache/clear-cache'; +import { I18nProvider } from "@/components/i18n-provider"; +import { DataTableSkeleton } from "@/components/table/data-table"; +import { HeaderContent } from "@/components/ui/header-content"; +import { ClearCacheAction } from "@/views/admin/views/core/debug/actions/clear-cache/clear-cache"; const SystemLogsView = dynamic(async () => - import('@/views/admin/views/core/debug/system-logs/system-logs-view').then( + import("@/views/admin/views/core/debug/system-logs/system-logs-view").then( module => ({ default: module.SystemLogsView, }), @@ -16,27 +16,27 @@ const SystemLogsView = dynamic(async () => ); export const generateMetadata = async () => { - const t = await getTranslations('admin.debug'); + const t = await getTranslations("admin.debug"); return { - title: t('title'), - description: t('desc'), + title: t("title"), + description: t("desc"), }; }; export default async function Page( props: React.ComponentProps, ) { - const t = await getTranslations('admin.debug'); + const t = await getTranslations("admin.debug"); return (

- + - + }> diff --git a/packages/vitnode/src/app_admin/core/page.tsx b/packages/vitnode/src/app_admin/core/page.tsx index 331370fe7..5a81f0e88 100644 --- a/packages/vitnode/src/app_admin/core/page.tsx +++ b/packages/vitnode/src/app_admin/core/page.tsx @@ -1,4 +1,4 @@ -import { DashboardAdminView } from '../../views/admin/views/core/dashboard/dashboard-admin-view'; +import { DashboardAdminView } from "../../views/admin/views/core/dashboard/dashboard-admin-view"; export default function Page() { return ; diff --git a/packages/vitnode/src/app_admin/core/test/page.tsx b/packages/vitnode/src/app_admin/core/test/page.tsx index 0f9a918b8..b34e7eeae 100644 --- a/packages/vitnode/src/app_admin/core/test/page.tsx +++ b/packages/vitnode/src/app_admin/core/test/page.tsx @@ -1,4 +1,4 @@ -import { TestView } from '../../../views/admin/views/core/test'; +import { TestView } from "../../../views/admin/views/core/test"; export default function Page() { return ; diff --git a/packages/vitnode/src/app_admin/core/users/page.tsx b/packages/vitnode/src/app_admin/core/users/page.tsx index d8639cf32..504c12bb2 100644 --- a/packages/vitnode/src/app_admin/core/users/page.tsx +++ b/packages/vitnode/src/app_admin/core/users/page.tsx @@ -1,22 +1,22 @@ -import type { Metadata } from 'next/dist/types'; -import dynamic from 'next/dynamic'; -import { getTranslations } from 'next-intl/server'; -import React from 'react'; +import type { Metadata } from "next/dist/types"; +import dynamic from "next/dynamic"; +import { getTranslations } from "next-intl/server"; +import React from "react"; -import { DataTableSkeleton } from '@/components/table/data-table'; -import { HeaderContent } from '@/components/ui/header-content'; +import { DataTableSkeleton } from "@/components/table/data-table"; +import { HeaderContent } from "@/components/ui/header-content"; const UsersAdminView = dynamic(async () => - import('@/views/admin/views/core/users/users-admin-view').then(module => ({ + import("@/views/admin/views/core/users/users-admin-view").then(module => ({ default: module.UsersAdminView, })), ); export const generateMetadata = async (): Promise => { - const t = await getTranslations('admin.global.nav.users'); + const t = await getTranslations("admin.global.nav.users"); return { - title: t('list'), + title: t("list"), }; }; @@ -24,13 +24,13 @@ export default async function Page( props: React.ComponentProps, ) { const [t, tNav] = await Promise.all([ - getTranslations('admin.user.list'), - getTranslations('admin.global.nav.users'), + getTranslations("admin.user.list"), + getTranslations("admin.global.nav.users"), ]); return (
- + }> diff --git a/packages/vitnode/src/components/avatar.tsx b/packages/vitnode/src/components/avatar.tsx index 684b69b66..4d0e3dceb 100644 --- a/packages/vitnode/src/components/avatar.tsx +++ b/packages/vitnode/src/components/avatar.tsx @@ -1,6 +1,6 @@ -import Image from 'next/image'; +import Image from "next/image"; -import { cn } from '@/lib/utils'; +import { cn } from "@/lib/utils"; const generateLetterPhoto = (letter: string, color: string) => `data:image/svg+xml,${encodeURIComponent( @@ -14,7 +14,7 @@ export const Avatar = ({ ...props }: Omit< React.ComponentProps, - 'alt' | 'height' | 'src' | 'width' + "alt" | "height" | "src" | "width" > & { size: number; user: { avatarColor: string; name: string; nameCode: string }; @@ -22,7 +22,7 @@ export const Avatar = ({ return ( {name} - import('./content').then(module => ({ + import("./content").then(module => ({ default: module.ContentConfirmAction, })), ); @@ -27,13 +27,13 @@ export const ConfirmActionAlertDialog = ({ textSubmit, onSubmit, ...props -}: Omit, 'children'> & +}: Omit, "children"> & React.ComponentProps & { children: React.ReactNode; description?: React.ReactNode; title?: React.ReactNode; }) => { - const t = useTranslations('core.global.confirm_action'); + const t = useTranslations("core.global.confirm_action"); return ( @@ -41,9 +41,9 @@ export const ConfirmActionAlertDialog = ({ - {title ?? t('title')} + {title ?? t("title")} - {description ?? t('desc')} + {description ?? t("desc")} diff --git a/packages/vitnode/src/components/confirm-action/content.tsx b/packages/vitnode/src/components/confirm-action/content.tsx index f09325979..882df8ca3 100644 --- a/packages/vitnode/src/components/confirm-action/content.tsx +++ b/packages/vitnode/src/components/confirm-action/content.tsx @@ -1,12 +1,12 @@ -import { useTranslations } from 'next-intl'; -import React from 'react'; +import { useTranslations } from "next-intl"; +import React from "react"; import { AlertDialogCancel, AlertDialogFooter, useAlertDialog, -} from '../ui/alert-dialog'; -import { Button } from '../ui/button'; +} from "../ui/alert-dialog"; +import { Button } from "../ui/button"; export const ContentConfirmAction = ({ onSubmit, @@ -15,7 +15,7 @@ export const ContentConfirmAction = ({ onSubmit: (props: { onClose: () => void }) => Promise | void; textSubmit?: string; }) => { - const t = useTranslations('core.global.confirm_action'); + const t = useTranslations("core.global.confirm_action"); const { setOpen } = useAlertDialog(); const [_, formAction, isLoading] = React.useActionState(async () => { @@ -25,9 +25,9 @@ export const ContentConfirmAction = ({ return (
- {t('cancel')} + {t("cancel")}
diff --git a/packages/vitnode/src/components/date-format.tsx b/packages/vitnode/src/components/date-format.tsx index 6ecabd830..0e8431d1b 100644 --- a/packages/vitnode/src/components/date-format.tsx +++ b/packages/vitnode/src/components/date-format.tsx @@ -1,13 +1,13 @@ -'use client'; +"use client"; -import { useFormatter, useNow } from 'next-intl'; +import { useFormatter, useNow } from "next-intl"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, -} from './ui/tooltip'; +} from "./ui/tooltip"; export const DateFormat = ({ date, @@ -18,7 +18,7 @@ export const DateFormat = ({ showFullDate?: boolean; updateInterval?: number; }) => { - const dateToFormat = typeof date === 'string' ? new Date(date) : date; + const dateToFormat = typeof date === "string" ? new Date(date) : date; const format = useFormatter(); const now = useNow({ updateInterval: @@ -29,11 +29,11 @@ export const DateFormat = ({ const fullDate = format.dateTime(dateToFormat, { year: - dateToFormat.getFullYear() === now.getFullYear() ? undefined : 'numeric', - month: 'short', - day: 'numeric', - hour: 'numeric', - minute: 'numeric', + dateToFormat.getFullYear() === now.getFullYear() ? undefined : "numeric", + month: "short", + day: "numeric", + hour: "numeric", + minute: "numeric", }); if (showFullDate) { diff --git a/packages/vitnode/src/components/form/auto-form.tsx b/packages/vitnode/src/components/form/auto-form.tsx index 23e0e23c8..cd3e0a1a5 100644 --- a/packages/vitnode/src/components/form/auto-form.tsx +++ b/packages/vitnode/src/components/form/auto-form.tsx @@ -1,7 +1,7 @@ -'use client'; +"use client"; -import { zodResolver } from '@hookform/resolvers/zod'; -import { useTranslations } from 'next-intl'; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useTranslations } from "next-intl"; import { type ControllerRenderProps, type FieldPath, @@ -9,18 +9,18 @@ import { type Mode, type UseFormReturn, useForm, -} from 'react-hook-form'; -import z from 'zod'; -import type { routeMiddlewareSchema } from '../../api/modules/middleware/route'; -import { useCaptcha } from '../../hooks/use-captcha'; +} from "react-hook-form"; +import z from "zod"; +import type { routeMiddlewareSchema } from "../../api/modules/middleware/route"; +import { useCaptcha } from "../../hooks/use-captcha"; import { getDefaults, getNestedParam, getZodInputParams, -} from '../../lib/helpers/auto-form'; -import { Button } from '../ui/button'; -import { DialogClose, DialogFooter, useDialog } from '../ui/dialog'; -import { Form, FormField } from '../ui/form'; +} from "../../lib/helpers/auto-form"; +import { Button } from "../ui/button"; +import { DialogClose, DialogFooter, useDialog } from "../ui/dialog"; +import { Form, FormField } from "../ui/form"; type ItemAutoFormProps< T extends z.ZodObject = z.ZodObject, @@ -75,15 +75,15 @@ export function AutoForm< submitButtonProps, children, ...props -}: Omit, 'onSubmit'> & { - captcha?: z.infer['captcha']; +}: Omit, "onSubmit"> & { + captcha?: z.infer["captcha"]; fields: ItemAutoFormProps[]; formSchema: T; mode?: Mode; onSubmit?: AutoFormOnSubmit; submitButtonProps?: Omit< React.ComponentProps, - 'isLoading' | 'type' + "isLoading" | "type" >; }) { const { @@ -92,7 +92,7 @@ export function AutoForm< onReset: onResetCaptcha, } = useCaptcha(captcha); const { setIsDirty } = useDialog(); - const t = useTranslations('core.global'); + const t = useTranslations("core.global"); const jsonSchema: z.core.JSONSchema.JSONSchema = z.toJSONSchema(formSchema); const inputParams = getZodInputParams(jsonSchema); const form = useForm, TContext, z.core.output>({ @@ -105,7 +105,7 @@ export function AutoForm< const parsedValues = formSchema.safeParse(values); if (parsedValues.success) { await onSubmitProp?.(parsedValues.data, form, { - captchaToken: captcha ? await getTokenCaptcha() : '', + captchaToken: captcha ? await getTokenCaptcha() : "", }); if (captcha) { @@ -123,10 +123,10 @@ export function AutoForm< } isLoading={form.formState.isSubmitting} {...submitButtonProps} - aria-label={submitButtonProps?.['aria-label'] ?? t('submit')} + aria-label={submitButtonProps?.["aria-label"] ?? t("submit")} type="submit" > - {submitButtonProps?.children ?? t('submit')} + {submitButtonProps?.children ?? t("submit")} ); @@ -166,28 +166,28 @@ export function AutoForm< {item.component({ field, description: - typeof params.description === 'string' + typeof params.description === "string" ? params.description - : '', + : "", otherProps: { isOptional: !params.required, enum: Array.isArray(params.enum) ? params.enum : undefined, maxLength: - typeof params.maxLength === 'number' + typeof params.maxLength === "number" ? params.maxLength : undefined, minLength: - typeof params.minLength === 'number' + typeof params.minLength === "number" ? params.minLength : undefined, pattern: - typeof params.pattern === 'string' + typeof params.pattern === "string" ? params.pattern : undefined, type: - typeof params.type === 'string' + typeof params.type === "string" ? params.type : undefined, }, @@ -205,7 +205,7 @@ export function AutoForm< {setIsDirty ? ( - + {submitButton} diff --git a/packages/vitnode/src/components/form/common/desc.tsx b/packages/vitnode/src/components/form/common/desc.tsx index af6c19cce..167a48fed 100644 --- a/packages/vitnode/src/components/form/common/desc.tsx +++ b/packages/vitnode/src/components/form/common/desc.tsx @@ -1,12 +1,12 @@ -import { cn } from '@/lib/utils'; +import { cn } from "@/lib/utils"; export const AutoFormDesc = ({ children, className, ...props -}: React.ComponentProps<'p'>) => { +}: React.ComponentProps<"p">) => { return ( -

+

{children}

); diff --git a/packages/vitnode/src/components/form/common/label.tsx b/packages/vitnode/src/components/form/common/label.tsx index 01257defd..7a37e5d6e 100644 --- a/packages/vitnode/src/components/form/common/label.tsx +++ b/packages/vitnode/src/components/form/common/label.tsx @@ -1,5 +1,5 @@ -import { FormLabel } from '@/components/ui/form'; -import { cn } from '@/lib/utils'; +import { FormLabel } from "@/components/ui/form"; +import { cn } from "@/lib/utils"; export const AutoFormLabel = ({ children, @@ -13,7 +13,7 @@ export const AutoFormLabel = ({ , 'checked'>) => { + Omit, "checked">) => { return ( diff --git a/packages/vitnode/src/components/form/fields/combobox-async.tsx b/packages/vitnode/src/components/form/fields/combobox-async.tsx index 428460d38..3ea0e0033 100644 --- a/packages/vitnode/src/components/form/fields/combobox-async.tsx +++ b/packages/vitnode/src/components/form/fields/combobox-async.tsx @@ -1,10 +1,10 @@ -import { useQuery } from '@tanstack/react-query'; -import { Check, ChevronsUpDown } from 'lucide-react'; -import { useTranslations } from 'next-intl'; -import React from 'react'; -import { useDebouncedCallback } from 'use-debounce'; +import { useQuery } from "@tanstack/react-query"; +import { Check, ChevronsUpDown } from "lucide-react"; +import { useTranslations } from "next-intl"; +import React from "react"; +import { useDebouncedCallback } from "use-debounce"; -import { Button } from '@/components/ui/button'; +import { Button } from "@/components/ui/button"; import { Command, CommandEmpty, @@ -12,18 +12,18 @@ import { CommandInput, CommandItem, CommandList, -} from '@/components/ui/command'; -import { FormControl, FormItem, FormMessage } from '@/components/ui/form'; +} from "@/components/ui/command"; +import { FormControl, FormItem, FormMessage } from "@/components/ui/form"; import { Popover, PopoverContent, PopoverTrigger, -} from '@/components/ui/popover'; -import { cn } from '@/lib/utils'; -import { Skeleton } from '../../ui/skeleton'; -import type { ItemAutoFormComponentProps } from '../auto-form'; -import { AutoFormDesc } from '../common/desc'; -import { AutoFormLabel } from '../common/label'; +} from "@/components/ui/popover"; +import { cn } from "@/lib/utils"; +import { Skeleton } from "../../ui/skeleton"; +import type { ItemAutoFormComponentProps } from "../auto-form"; +import { AutoFormDesc } from "../common/desc"; +import { AutoFormLabel } from "../common/label"; export const AutoFormComboboxAsync = ({ label, @@ -38,7 +38,7 @@ export const AutoFormComboboxAsync = ({ fetchData, ...props }: ItemAutoFormComponentProps & - Omit, 'role' | 'variant'> & { + Omit, "role" | "variant"> & { fetchData: (params: { search: string }) => | Promise< { @@ -54,8 +54,8 @@ export const AutoFormComboboxAsync = ({ placeholder?: string; searchPlaceholder?: string; }) => { - const t = useTranslations('core.global'); - const [search, setSearch] = React.useState(''); + const t = useTranslations("core.global"); + const [search, setSearch] = React.useState(""); const { data, isLoading } = useQuery({ queryKey: [id, { search }], queryFn: async () => { @@ -81,15 +81,15 @@ export const AutoFormComboboxAsync = ({ @@ -101,7 +101,7 @@ export const AutoFormComboboxAsync = ({ onChangeCapture={e => { handleChangeSearch(e.currentTarget.value); }} - placeholder={searchPlaceholder ?? t('search_placeholder')} + placeholder={searchPlaceholder ?? t("search_placeholder")} /> @@ -116,7 +116,7 @@ export const AutoFormComboboxAsync = ({ } if ((data ?? []).length === 0) { - return {t('results_not_found')}; + return {t("results_not_found")}; } return ( @@ -135,10 +135,10 @@ export const AutoFormComboboxAsync = ({ {label} diff --git a/packages/vitnode/src/components/form/fields/combobox.tsx b/packages/vitnode/src/components/form/fields/combobox.tsx index 5bd6317d7..5b1705365 100644 --- a/packages/vitnode/src/components/form/fields/combobox.tsx +++ b/packages/vitnode/src/components/form/fields/combobox.tsx @@ -1,8 +1,8 @@ -import { Check, ChevronsUpDown } from 'lucide-react'; -import { useTranslations } from 'next-intl'; -import type React from 'react'; +import { Check, ChevronsUpDown } from "lucide-react"; +import { useTranslations } from "next-intl"; +import type React from "react"; -import { Button } from '@/components/ui/button'; +import { Button } from "@/components/ui/button"; import { Command, CommandEmpty, @@ -10,19 +10,19 @@ import { CommandInput, CommandItem, CommandList, -} from '@/components/ui/command'; -import { FormControl, FormItem, FormMessage } from '@/components/ui/form'; +} from "@/components/ui/command"; +import { FormControl, FormItem, FormMessage } from "@/components/ui/form"; import { Popover, PopoverContent, PopoverTrigger, -} from '@/components/ui/popover'; -import { cn } from '@/lib/utils'; +} from "@/components/ui/popover"; +import { cn } from "@/lib/utils"; -import type { ItemAutoFormComponentProps } from '../auto-form'; +import type { ItemAutoFormComponentProps } from "../auto-form"; -import { AutoFormDesc } from '../common/desc'; -import { AutoFormLabel } from '../common/label'; +import { AutoFormDesc } from "../common/desc"; +import { AutoFormLabel } from "../common/label"; export const AutoFormCombobox = ({ label, @@ -36,12 +36,12 @@ export const AutoFormCombobox = ({ searchPlaceholder, ...props }: ItemAutoFormComponentProps & - Omit, 'role' | 'variant'> & { + Omit, "role" | "variant"> & { labels?: { label: string; value: string }[]; placeholder?: string; searchPlaceholder?: string; }) => { - const t = useTranslations('core.global'); + const t = useTranslations("core.global"); const values: { label: string; value: string }[] = enumValues.map(value => { const label = labels.find(l => l.value === value)?.label; @@ -66,8 +66,8 @@ export const AutoFormCombobox = ({ @@ -85,10 +85,10 @@ export const AutoFormCombobox = ({ - {t('results_not_found')} + {t("results_not_found")} {values.map(({ label, value }) => ( diff --git a/packages/vitnode/src/components/form/fields/input.tsx b/packages/vitnode/src/components/form/fields/input.tsx index 6dc8671d7..e194a385d 100644 --- a/packages/vitnode/src/components/form/fields/input.tsx +++ b/packages/vitnode/src/components/form/fields/input.tsx @@ -1,8 +1,8 @@ -import { FormControl, FormItem, FormMessage } from '../../ui/form'; -import { Input } from '../../ui/input'; -import type { ItemAutoFormComponentProps } from '../auto-form'; -import { AutoFormDesc } from '../common/desc'; -import { AutoFormLabel } from '../common/label'; +import { FormControl, FormItem, FormMessage } from "../../ui/form"; +import { Input } from "../../ui/input"; +import type { ItemAutoFormComponentProps } from "../auto-form"; +import { AutoFormDesc } from "../common/desc"; +import { AutoFormLabel } from "../common/label"; export const AutoFormInput = ({ label, @@ -12,7 +12,7 @@ export const AutoFormInput = ({ field, ...props }: ItemAutoFormComponentProps & - Omit, 'value'>) => { + Omit, "value">) => { return ( {label && ( @@ -34,8 +34,8 @@ export const AutoFormInput = ({ props.onChange?.(e); }} pattern={pattern} - type={type ?? 'text'} - value={field.value ?? ''} + type={type ?? "text"} + value={field.value ?? ""} {...props} /> diff --git a/packages/vitnode/src/components/form/fields/radio-group.tsx b/packages/vitnode/src/components/form/fields/radio-group.tsx index 04e3afddd..7301dbaba 100644 --- a/packages/vitnode/src/components/form/fields/radio-group.tsx +++ b/packages/vitnode/src/components/form/fields/radio-group.tsx @@ -1,17 +1,17 @@ -import type React from 'react'; +import type React from "react"; import { FormControl, FormItem, FormLabel, FormMessage, -} from '@/components/ui/form'; -import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; +} from "@/components/ui/form"; +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; -import type { ItemAutoFormComponentProps } from '../auto-form'; +import type { ItemAutoFormComponentProps } from "../auto-form"; -import { AutoFormDesc } from '../common/desc'; -import { AutoFormLabel } from '../common/label'; +import { AutoFormDesc } from "../common/desc"; +import { AutoFormLabel } from "../common/label"; export const AutoFormRadioGroup = ({ label, @@ -22,7 +22,7 @@ export const AutoFormRadioGroup = ({ labels = [], ...props }: ItemAutoFormComponentProps & - Omit, 'value'> & { + Omit, "value"> & { labels?: { label: string; value: string }[]; }) => { const values: { label: string; value: string }[] = enumValues.map(value => { diff --git a/packages/vitnode/src/components/form/fields/select.tsx b/packages/vitnode/src/components/form/fields/select.tsx index cfa13ff11..fb0f28cd4 100644 --- a/packages/vitnode/src/components/form/fields/select.tsx +++ b/packages/vitnode/src/components/form/fields/select.tsx @@ -1,19 +1,19 @@ -import { useTranslations } from 'next-intl'; -import type React from 'react'; +import { useTranslations } from "next-intl"; +import type React from "react"; -import { FormControl, FormItem, FormMessage } from '@/components/ui/form'; +import { FormControl, FormItem, FormMessage } from "@/components/ui/form"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, -} from '@/components/ui/select'; +} from "@/components/ui/select"; -import type { ItemAutoFormComponentProps } from '../auto-form'; +import type { ItemAutoFormComponentProps } from "../auto-form"; -import { AutoFormDesc } from '../common/desc'; -import { AutoFormLabel } from '../common/label'; +import { AutoFormDesc } from "../common/desc"; +import { AutoFormLabel } from "../common/label"; export const AutoFormSelect = ({ label, @@ -25,11 +25,11 @@ export const AutoFormSelect = ({ labels = [], ...props }: ItemAutoFormComponentProps & - Omit, 'value'> & { + Omit, "value"> & { labels?: { label: string; value: string }[]; placeholder?: string; }) => { - const t = useTranslations('core.global'); + const t = useTranslations("core.global"); const values: { label: string; value: string }[] = enumValues.map(value => { const label = labels.find(l => l.value === value)?.label; @@ -41,7 +41,7 @@ export const AutoFormSelect = ({ const currentPlaceholder = (values ?? labels).find(l => l.value === field.value)?.label ?? - t('select_option'); + t("select_option"); return ( diff --git a/packages/vitnode/src/components/form/fields/switch.tsx b/packages/vitnode/src/components/form/fields/switch.tsx index c592a78d1..05de62c25 100644 --- a/packages/vitnode/src/components/form/fields/switch.tsx +++ b/packages/vitnode/src/components/form/fields/switch.tsx @@ -1,10 +1,10 @@ -import { FormControl, FormItem } from '@/components/ui/form'; -import { Switch } from '@/components/ui/switch'; +import { FormControl, FormItem } from "@/components/ui/form"; +import { Switch } from "@/components/ui/switch"; -import type { ItemAutoFormComponentProps } from '../auto-form'; +import type { ItemAutoFormComponentProps } from "../auto-form"; -import { AutoFormDesc } from '../common/desc'; -import { AutoFormLabel } from '../common/label'; +import { AutoFormDesc } from "../common/desc"; +import { AutoFormLabel } from "../common/label"; export const AutoFormSwitch = ({ label, @@ -14,7 +14,7 @@ export const AutoFormSwitch = ({ description, ...props }: ItemAutoFormComponentProps & - Omit, 'checked'>) => { + Omit, "checked">) => { return ( {(label || description) && ( diff --git a/packages/vitnode/src/components/form/fields/textarea.tsx b/packages/vitnode/src/components/form/fields/textarea.tsx index 833edac9d..8bfb8df63 100644 --- a/packages/vitnode/src/components/form/fields/textarea.tsx +++ b/packages/vitnode/src/components/form/fields/textarea.tsx @@ -1,12 +1,12 @@ -import type React from 'react'; +import type React from "react"; -import { FormControl, FormItem, FormMessage } from '@/components/ui/form'; -import { Textarea } from '@/components/ui/textarea'; +import { FormControl, FormItem, FormMessage } from "@/components/ui/form"; +import { Textarea } from "@/components/ui/textarea"; -import type { ItemAutoFormComponentProps } from '../auto-form'; +import type { ItemAutoFormComponentProps } from "../auto-form"; -import { AutoFormDesc } from '../common/desc'; -import { AutoFormLabel } from '../common/label'; +import { AutoFormDesc } from "../common/desc"; +import { AutoFormLabel } from "../common/label"; export const AutoFormTextarea = ({ label, @@ -16,7 +16,7 @@ export const AutoFormTextarea = ({ field, ...props }: ItemAutoFormComponentProps & - Omit, 'value'> & { + Omit, "value"> & { description?: React.ReactNode; label?: React.ReactNode; }) => { @@ -40,7 +40,7 @@ export const AutoFormTextarea = ({ field.onChange(e); props.onChange?.(e); }} - value={field.value ?? ''} + value={field.value ?? ""} {...props} /> diff --git a/packages/vitnode/src/components/i18n-provider.tsx b/packages/vitnode/src/components/i18n-provider.tsx index 893cdf66a..a049d3b61 100644 --- a/packages/vitnode/src/components/i18n-provider.tsx +++ b/packages/vitnode/src/components/i18n-provider.tsx @@ -1,13 +1,13 @@ -import type { Messages, NamespaceKeys, NestedKeyOf } from 'next-intl'; +import type { Messages, NamespaceKeys, NestedKeyOf } from "next-intl"; -import { NextIntlClientProvider } from 'next-intl'; -import { getLocale, getMessages } from 'next-intl/server'; -import 'server-only'; +import { NextIntlClientProvider } from "next-intl"; +import { getLocale, getMessages } from "next-intl/server"; +import "server-only"; const pick = (obj: object, paths: string[]) => { const result = {}; for (const path of paths) { - const keys = path.split('.'); + const keys = path.split("."); let src: object | undefined = obj; let dest = result; for (let i = 0; i < keys.length; i++) { @@ -41,7 +41,7 @@ export async function I18nProvider< const locale = await getLocale(); const messagesInit: object = await getMessages({ locale }); const messages = pick(messagesInit, [ - 'core.global', + "core.global", ...(Array.isArray(namespaces) ? namespaces : [namespaces]), ]); diff --git a/packages/vitnode/src/components/logo-vitnode.tsx b/packages/vitnode/src/components/logo-vitnode.tsx index 94549c568..0c8d12afd 100644 --- a/packages/vitnode/src/components/logo-vitnode.tsx +++ b/packages/vitnode/src/components/logo-vitnode.tsx @@ -1,17 +1,17 @@ -import { cn } from '@/lib/utils'; +import { cn } from "@/lib/utils"; export const LogoVitNode = ({ className, small, ...props -}: React.ComponentProps<'svg'> & { +}: React.ComponentProps<"svg"> & { small?: boolean; }) => { if (small) return ( { const currentLocale = useLocale(); const [isPending, startTransition] = React.useTransition(); const { replace } = useRouter(); - const t = useTranslations('core.global'); + const t = useTranslations("core.global"); const pathname = usePathname(); return (
@@ -56,13 +56,13 @@ export function ContentDataTable({ allData: edges, row, }) ?? - (column.id === 'actions' ? '' : String(row[column.id])); + (column.id === "actions" ? "" : String(row[column.id])); return ( @@ -84,10 +84,10 @@ export function ContentDataTable({

- {t('no_results.title')} + {t("no_results.title")}

- {t('no_results.desc')} + {t("no_results.desc")}

diff --git a/packages/vitnode/src/components/table/data-table.tsx b/packages/vitnode/src/components/table/data-table.tsx index c89d92803..050be810d 100644 --- a/packages/vitnode/src/components/table/data-table.tsx +++ b/packages/vitnode/src/components/table/data-table.tsx @@ -1,15 +1,15 @@ -import React from 'react'; -import { ErrorView } from '../../views/error/error-view'; -import { Skeleton } from '../ui/skeleton'; +import React from "react"; +import { ErrorView } from "../../views/error/error-view"; +import { Skeleton } from "../ui/skeleton"; import { Table, TableBody, TableHead, TableHeader, TableRow, -} from '../ui/table'; -import { ContentDataTable } from './content'; -import type { PaginationDataTable } from './pagination'; +} from "../ui/table"; +import { ContentDataTable } from "./content"; +import type { PaginationDataTable } from "./pagination"; export interface DataTableTMin { id: number; @@ -19,7 +19,7 @@ export interface SearchParamsDataTable { cursor?: string; first?: string; last?: string; - order?: 'asc' | 'desc'; + order?: "asc" | "desc"; orderBy?: keyof T; } @@ -78,12 +78,12 @@ export const DataTableSkeleton = ({ columns }: { columns: number }) => { }; export function DataTable( - props: Omit, 'columns'> & + props: Omit, "columns"> & React.ComponentProps & { columns: { cell?: (data: { allData: T[]; row: T }) => React.ReactNode; className?: string; - id: 'actions' | keyof T; + id: "actions" | keyof T; label: string; }[]; customNotFoundComponent?: React.ReactNode; @@ -92,7 +92,7 @@ export function DataTable( columns?: (keyof T)[]; defaultOrder: { column: keyof T; - order: 'asc' | 'desc'; + order: "asc" | "desc"; }; }; }, diff --git a/packages/vitnode/src/components/table/order-table-head.tsx b/packages/vitnode/src/components/table/order-table-head.tsx index 7a38fc273..fa69aba78 100644 --- a/packages/vitnode/src/components/table/order-table-head.tsx +++ b/packages/vitnode/src/components/table/order-table-head.tsx @@ -1,21 +1,21 @@ -'use client'; +"use client"; -import { ArrowDown, ArrowUp, ChevronsUpDown } from 'lucide-react'; -import { useSearchParams } from 'next/navigation'; -import React from 'react'; +import { ArrowDown, ArrowUp, ChevronsUpDown } from "lucide-react"; +import { useSearchParams } from "next/navigation"; +import React from "react"; -import { usePathname, useRouter } from '@/lib/navigation'; -import { Button } from '../ui/button'; -import { Loader } from '../ui/loader'; -import type { DataTable, DataTableTMin } from './data-table'; +import { usePathname, useRouter } from "@/lib/navigation"; +import { Button } from "../ui/button"; +import { Loader } from "../ui/loader"; +import type { DataTable, DataTableTMin } from "./data-table"; export function OrderTableHeadDataTable({ id, children, order: { defaultOrder }, -}: Pick>, 'order'> & { +}: Pick>, "order"> & { children: React.ReactNode; - id: React.ComponentProps>['columns'][0]['id']; + id: React.ComponentProps>["columns"][0]["id"]; }) { const [isPending, startTransition] = React.useTransition(); const searchParams = useSearchParams(); @@ -23,11 +23,11 @@ export function OrderTableHeadDataTable({ const { push } = useRouter(); const currentOrderBy = - searchParams.get('orderBy') ?? defaultOrder.column.toString(); - const currentOrder = searchParams.get('order') ?? defaultOrder.order; + searchParams.get("orderBy") ?? defaultOrder.column.toString(); + const currentOrder = searchParams.get("order") ?? defaultOrder.order; const isActive = currentOrderBy === id.toString(); - const nextOrder = isActive && currentOrder === 'asc' ? 'desc' : 'asc'; + const nextOrder = isActive && currentOrder === "asc" ? "desc" : "asc"; return (