diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index ba422b66..3c88d2ca 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -25,9 +25,10 @@ jobs: - name: Install dependencies uses: "./.github/actions/install-dependencies" - - name: Run lint - shell: bash - run: bun run lint:ci + - uses: oxc-project/oxlint-action@latest + with: + config: .oxlintrc.json + deny-warnings: true - name: Run type checks shell: bash diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 00000000..98859c34 --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,5 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "printWidth": 120, + "useTabs": true +} diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 00000000..634c4b7c --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,150 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "plugins": ["unicorn", "typescript", "oxc"], + "categories": {}, + "rules": { + "constructor-super": "warn", + "for-direction": "warn", + "no-async-promise-executor": "warn", + "no-caller": "warn", + "no-class-assign": "warn", + "no-compare-neg-zero": "warn", + "no-cond-assign": "warn", + "no-const-assign": "warn", + "no-constant-binary-expression": "warn", + "no-constant-condition": "warn", + "no-control-regex": "warn", + "no-debugger": "warn", + "no-delete-var": "warn", + "no-dupe-class-members": "warn", + "no-dupe-else-if": "warn", + "no-dupe-keys": "warn", + "no-duplicate-case": "warn", + "no-empty-character-class": "warn", + "no-empty-pattern": "warn", + "no-empty-static-block": "warn", + "no-eval": "warn", + "no-ex-assign": "warn", + "no-extra-boolean-cast": "warn", + "no-func-assign": "warn", + "no-global-assign": "warn", + "no-import-assign": "warn", + "no-invalid-regexp": "warn", + "no-irregular-whitespace": "warn", + "no-loss-of-precision": "warn", + "no-new-native-nonconstructor": "warn", + "no-nonoctal-decimal-escape": "warn", + "no-obj-calls": "warn", + "no-self-assign": "warn", + "no-setter-return": "warn", + "no-shadow-restricted-names": "warn", + "no-sparse-arrays": "warn", + "no-this-before-super": "warn", + "no-unassigned-vars": "warn", + "no-unsafe-finally": "warn", + "no-unsafe-negation": "warn", + "no-unsafe-optional-chaining": "warn", + "no-unused-expressions": "warn", + "no-unused-labels": "warn", + "no-unused-private-class-members": "warn", + "no-unused-vars": [ + "warn", + { + "caughtErrorsIgnorePattern": "^_", + "argsIgnorePattern": "^_" + } + ], + "no-useless-backreference": "warn", + "no-useless-catch": "warn", + "no-useless-escape": "warn", + "no-useless-rename": "warn", + "no-with": "warn", + "require-yield": "warn", + "use-isnan": "warn", + "valid-typeof": "warn", + "oxc/bad-array-method-on-arguments": "warn", + "oxc/bad-char-at-comparison": "warn", + "oxc/bad-comparison-sequence": "warn", + "oxc/bad-min-max-func": "warn", + "oxc/bad-object-literal-comparison": "warn", + "oxc/bad-replace-all-arg": "warn", + "oxc/const-comparisons": "warn", + "oxc/double-comparisons": "warn", + "oxc/erasing-op": "warn", + "oxc/missing-throw": "warn", + "oxc/number-arg-out-of-range": "warn", + "oxc/only-used-in-recursion": "warn", + "oxc/uninvoked-array-callback": "warn", + "typescript/await-thenable": "warn", + "typescript/no-array-delete": "warn", + "typescript/no-base-to-string": "warn", + "typescript/no-duplicate-enum-values": "warn", + "typescript/no-duplicate-type-constituents": "warn", + "typescript/no-extra-non-null-assertion": "warn", + "typescript/no-floating-promises": "warn", + "typescript/no-for-in-array": "warn", + "typescript/no-implied-eval": "warn", + "typescript/no-meaningless-void-operator": "warn", + "typescript/no-misused-new": "warn", + "typescript/no-misused-spread": "warn", + "typescript/no-non-null-asserted-optional-chain": "warn", + "typescript/no-redundant-type-constituents": "warn", + "typescript/no-this-alias": "warn", + "typescript/no-unnecessary-parameter-property-assignment": "warn", + "typescript/no-unsafe-declaration-merging": "warn", + "typescript/no-unsafe-unary-minus": "warn", + "typescript/no-useless-empty-export": "warn", + "typescript/no-wrapper-object-types": "warn", + "typescript/prefer-as-const": "warn", + "typescript/require-array-sort-compare": "warn", + "typescript/restrict-template-expressions": "warn", + "typescript/triple-slash-reference": "warn", + "typescript/unbound-method": "warn", + "unicorn/no-await-in-promise-methods": "warn", + "unicorn/no-empty-file": "warn", + "unicorn/no-invalid-fetch-options": "warn", + "unicorn/no-invalid-remove-event-listener": "warn", + "unicorn/no-new-array": "warn", + "unicorn/no-single-promise-in-promise-methods": "warn", + "unicorn/no-thenable": "warn", + "unicorn/no-unnecessary-await": "warn", + "unicorn/no-useless-fallback-in-spread": "warn", + "unicorn/no-useless-length-check": "warn", + "unicorn/no-useless-spread": "warn", + "unicorn/prefer-set-size": "warn", + "unicorn/prefer-string-starts-ends-with": "warn" + }, + "settings": { + "jsx-a11y": { + "polymorphicPropName": null, + "components": {}, + "attributes": {} + }, + "next": { + "rootDir": [] + }, + "react": { + "formComponents": [], + "linkComponents": [], + "version": null + }, + "jsdoc": { + "ignorePrivate": false, + "ignoreInternal": false, + "ignoreReplacesDocs": true, + "overrideReplacesDocs": true, + "augmentsExtendsReplacesDocs": false, + "implementsReplacesDocs": false, + "exemptDestructuredRootsFromChecks": false, + "tagNamePreference": {} + }, + "vitest": { + "typecheck": false + } + }, + "env": { + "builtin": true + }, + "globals": {}, + "ignorePatterns": ["**/api-client/**"] +} diff --git a/app/client/components/directory-browser.tsx b/app/client/components/directory-browser.tsx index 002b8a47..bfea00b3 100644 --- a/app/client/components/directory-browser.tsx +++ b/app/client/components/directory-browser.tsx @@ -23,7 +23,7 @@ export const DirectoryBrowser = ({ onSelectPath, selectedPath }: Props) => { return await queryClient.ensureQueryData(browseFilesystemOptions({ query: { path } })); }, prefetchFolder: (path) => { - queryClient.prefetchQuery(browseFilesystemOptions({ query: { path } })); + void queryClient.prefetchQuery(browseFilesystemOptions({ query: { path } })); }, }); diff --git a/app/client/components/layout.tsx b/app/client/components/layout.tsx index 6030391d..3868bd9e 100644 --- a/app/client/components/layout.tsx +++ b/app/client/components/layout.tsx @@ -30,7 +30,7 @@ export default function Layout({ loaderData }: Route.ComponentProps) { const logout = useMutation({ ...logoutMutation(), onSuccess: async () => { - navigate("/login", { replace: true }); + void navigate("/login", { replace: true }); }, onError: (error) => { console.error(error); diff --git a/app/client/components/restore-form.tsx b/app/client/components/restore-form.tsx index 55a6d0ce..37d6d55e 100644 --- a/app/client/components/restore-form.tsx +++ b/app/client/components/restore-form.tsx @@ -83,7 +83,7 @@ export function RestoreForm({ snapshot, repository, snapshotId, returnPath }: Re ); }, prefetchFolder: (path) => { - queryClient.prefetchQuery( + void queryClient.prefetchQuery( listSnapshotFilesOptions({ path: { id: repository.id, snapshotId }, query: { path }, @@ -102,7 +102,7 @@ export function RestoreForm({ snapshot, repository, snapshotId, returnPath }: Re toast.success("Restore completed", { description: `Successfully restored ${data.filesRestored} file(s). ${data.filesSkipped} file(s) skipped.`, }); - navigate(returnPath); + void navigate(returnPath); }, onError: (error) => { toast.error("Restore failed", { description: error.message || "Failed to restore snapshot" }); diff --git a/app/client/components/snapshots-table.tsx b/app/client/components/snapshots-table.tsx index 0f8574bb..2e3b7f79 100644 --- a/app/client/components/snapshots-table.tsx +++ b/app/client/components/snapshots-table.tsx @@ -50,7 +50,7 @@ export const SnapshotsTable = ({ snapshots, repositoryId, backups }: Props) => { const deleteSnapshots = useMutation({ ...deleteSnapshotsMutation(), onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ["listSnapshots"] }); + void queryClient.invalidateQueries({ queryKey: ["listSnapshots"] }); setShowBulkDeleteConfirm(false); setSelectedIds(new Set()); }, @@ -62,7 +62,7 @@ export const SnapshotsTable = ({ snapshots, repositoryId, backups }: Props) => { setShowReTagDialog(false); }, onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ["listSnapshots"] }); + void queryClient.invalidateQueries({ queryKey: ["listSnapshots"] }); setShowReTagDialog(false); setSelectedIds(new Set()); setTargetScheduleId(""); @@ -70,7 +70,7 @@ export const SnapshotsTable = ({ snapshots, repositoryId, backups }: Props) => { }); const handleRowClick = (snapshotId: string) => { - navigate(`/repositories/${repositoryId}/${snapshotId}`); + void navigate(`/repositories/${repositoryId}/${snapshotId}`); }; const toggleSelectAll = () => { diff --git a/app/client/components/volume-file-browser.tsx b/app/client/components/volume-file-browser.tsx index 6e9cd53e..78327270 100644 --- a/app/client/components/volume-file-browser.tsx +++ b/app/client/components/volume-file-browser.tsx @@ -46,7 +46,7 @@ export const VolumeFileBrowser = ({ ); }, prefetchFolder: (path) => { - queryClient.prefetchQuery( + void queryClient.prefetchQuery( listFilesOptions({ path: { name: volumeName }, query: { path }, diff --git a/app/client/hooks/use-server-events.ts b/app/client/hooks/use-server-events.ts index d7c03777..211d33dc 100644 --- a/app/client/hooks/use-server-events.ts +++ b/app/client/hooks/use-server-events.ts @@ -87,8 +87,8 @@ export function useServerEvents() { const data = JSON.parse(e.data) as BackupEvent; console.log("[SSE] Backup completed:", data); - queryClient.invalidateQueries(); - queryClient.refetchQueries(); + void queryClient.invalidateQueries(); + void queryClient.refetchQueries(); handlersRef.current.get("backup:completed")?.forEach((handler) => { handler(data); @@ -117,7 +117,7 @@ export function useServerEvents() { const data = JSON.parse(e.data) as VolumeEvent; console.log("[SSE] Volume updated:", data); - queryClient.invalidateQueries(); + void queryClient.invalidateQueries(); handlersRef.current.get("volume:updated")?.forEach((handler) => { handler(data); @@ -128,7 +128,7 @@ export function useServerEvents() { const data = JSON.parse(e.data) as VolumeEvent; console.log("[SSE] Volume status updated:", data); - queryClient.invalidateQueries(); + void queryClient.invalidateQueries(); handlersRef.current.get("volume:updated")?.forEach((handler) => { handler(data); @@ -149,7 +149,7 @@ export function useServerEvents() { console.log("[SSE] Mirror copy completed:", data); // Invalidate queries to refresh mirror status in the UI - queryClient.invalidateQueries(); + void queryClient.invalidateQueries(); handlersRef.current.get("mirror:completed")?.forEach((handler) => { handler(data); diff --git a/app/client/modules/auth/routes/download-recovery-key.tsx b/app/client/modules/auth/routes/download-recovery-key.tsx index 5235702a..af29dd05 100644 --- a/app/client/modules/auth/routes/download-recovery-key.tsx +++ b/app/client/modules/auth/routes/download-recovery-key.tsx @@ -42,7 +42,7 @@ export default function DownloadRecoveryKeyPage() { window.URL.revokeObjectURL(url); toast.success("Recovery key downloaded successfully!"); - navigate("/volumes", { replace: true }); + void navigate("/volumes", { replace: true }); }, onError: (error) => { toast.error("Failed to download recovery key", { description: error.message }); diff --git a/app/client/modules/auth/routes/login.tsx b/app/client/modules/auth/routes/login.tsx index b62e0cca..984d8130 100644 --- a/app/client/modules/auth/routes/login.tsx +++ b/app/client/modules/auth/routes/login.tsx @@ -49,9 +49,9 @@ export default function LoginPage() { ...loginMutation(), onSuccess: async (data) => { if (data.user && !data.user.hasDownloadedResticPassword) { - navigate("/download-recovery-key"); + void navigate("/download-recovery-key"); } else { - navigate("/volumes"); + void navigate("/volumes"); } }, onError: (error) => { diff --git a/app/client/modules/auth/routes/onboarding.tsx b/app/client/modules/auth/routes/onboarding.tsx index cb72ed60..f95356f9 100644 --- a/app/client/modules/auth/routes/onboarding.tsx +++ b/app/client/modules/auth/routes/onboarding.tsx @@ -56,7 +56,7 @@ export default function OnboardingPage() { ...registerMutation(), onSuccess: async () => { toast.success("Admin user created successfully!"); - navigate("/download-recovery-key"); + void navigate("/download-recovery-key"); }, onError: (error) => { console.error(error); diff --git a/app/client/modules/backups/components/create-schedule-form.tsx b/app/client/modules/backups/components/create-schedule-form.tsx index 63d3a8e0..8e2a088b 100644 --- a/app/client/modules/backups/components/create-schedule-form.tsx +++ b/app/client/modules/backups/components/create-schedule-form.tsx @@ -728,7 +728,7 @@ export const CreateScheduleForm = ({ initialValues, formId, onSubmit, volume }: .filter(([key, value]) => key.startsWith("keep") && Boolean(value)) .map(([key, value]) => { const label = key.replace("keep", "").toLowerCase(); - return `${value} ${label}`; + return `${value.toString()} ${label}`; }) .join(", ") || "-"}

diff --git a/app/client/modules/backups/components/snapshot-file-browser.tsx b/app/client/modules/backups/components/snapshot-file-browser.tsx index 6935c8e6..185b0ed3 100644 --- a/app/client/modules/backups/components/snapshot-file-browser.tsx +++ b/app/client/modules/backups/components/snapshot-file-browser.tsx @@ -68,7 +68,7 @@ export const SnapshotFileBrowser = (props: Props) => { ); }, prefetchFolder: (path) => { - queryClient.prefetchQuery( + void queryClient.prefetchQuery( listSnapshotFilesOptions({ path: { id: repositoryId, snapshotId: snapshot.short_id }, query: { path }, diff --git a/app/client/modules/backups/routes/backup-details.tsx b/app/client/modules/backups/routes/backup-details.tsx index d7b6b841..1e218979 100644 --- a/app/client/modules/backups/routes/backup-details.tsx +++ b/app/client/modules/backups/routes/backup-details.tsx @@ -120,7 +120,7 @@ export default function ScheduleDetailsPage({ params, loaderData }: Route.Compon ...deleteBackupScheduleMutation(), onSuccess: () => { toast.success("Backup schedule deleted successfully"); - navigate("/backups"); + void navigate("/backups"); }, onError: (error) => { toast.error("Failed to delete backup schedule", { description: parseError(error)?.message }); diff --git a/app/client/modules/backups/routes/create-backup.tsx b/app/client/modules/backups/routes/create-backup.tsx index a7a12e1d..9fc686bb 100644 --- a/app/client/modules/backups/routes/create-backup.tsx +++ b/app/client/modules/backups/routes/create-backup.tsx @@ -59,7 +59,7 @@ export default function CreateBackup({ loaderData }: Route.ComponentProps) { ...createBackupScheduleMutation(), onSuccess: (data) => { toast.success("Backup job created successfully"); - navigate(`/backups/${data.id}`); + void navigate(`/backups/${data.id}`); }, onError: (error) => { toast.error("Failed to create backup job", { diff --git a/app/client/modules/notifications/routes/create-notification.tsx b/app/client/modules/notifications/routes/create-notification.tsx index 72268a6b..c6db625c 100644 --- a/app/client/modules/notifications/routes/create-notification.tsx +++ b/app/client/modules/notifications/routes/create-notification.tsx @@ -33,7 +33,7 @@ export default function CreateNotification() { ...createNotificationDestinationMutation(), onSuccess: () => { toast.success("Notification destination created successfully"); - navigate(`/notifications`); + void navigate(`/notifications`); }, }); diff --git a/app/client/modules/notifications/routes/notification-details.tsx b/app/client/modules/notifications/routes/notification-details.tsx index ba6ea340..3aae456b 100644 --- a/app/client/modules/notifications/routes/notification-details.tsx +++ b/app/client/modules/notifications/routes/notification-details.tsx @@ -24,7 +24,7 @@ import { getNotificationDestination } from "~/client/api-client/sdk.gen"; import type { Route } from "./+types/notification-details"; import { cn } from "~/client/lib/utils"; import { Card, CardContent, CardHeader, CardTitle } from "~/client/components/ui/card"; -import { Bell, Save, TestTube2, Trash2, X } from "lucide-react"; +import { Bell, Save, TestTube2, Trash2 } from "lucide-react"; import { Alert, AlertDescription } from "~/client/components/ui/alert"; import { CreateNotificationForm, type NotificationFormValues } from "../components/create-notification-form"; @@ -66,7 +66,7 @@ export default function NotificationDetailsPage({ loaderData }: Route.ComponentP ...deleteNotificationDestinationMutation(), onSuccess: () => { toast.success("Notification destination deleted successfully"); - navigate("/notifications"); + void navigate("/notifications"); }, onError: (error) => { toast.error("Failed to delete notification destination", { diff --git a/app/client/modules/repositories/routes/create-repository.tsx b/app/client/modules/repositories/routes/create-repository.tsx index 9d65b084..cad73dc4 100644 --- a/app/client/modules/repositories/routes/create-repository.tsx +++ b/app/client/modules/repositories/routes/create-repository.tsx @@ -36,7 +36,7 @@ export default function CreateRepository() { ...createRepositoryMutation(), onSuccess: (data) => { toast.success("Repository created successfully"); - navigate(`/repositories/${data.repository.shortId}`); + void navigate(`/repositories/${data.repository.shortId}`); }, }); diff --git a/app/client/modules/repositories/routes/repository-details.tsx b/app/client/modules/repositories/routes/repository-details.tsx index cfd52963..53f61a77 100644 --- a/app/client/modules/repositories/routes/repository-details.tsx +++ b/app/client/modules/repositories/routes/repository-details.tsx @@ -67,14 +67,14 @@ export default function RepositoryDetailsPage({ loaderData }: Route.ComponentPro }); useEffect(() => { - queryClient.prefetchQuery(listSnapshotsOptions({ path: { id: data.id } })); + void queryClient.prefetchQuery(listSnapshotsOptions({ path: { id: data.id } })); }, [queryClient, data.id]); const deleteRepo = useMutation({ ...deleteRepositoryMutation(), onSuccess: () => { toast.success("Repository deleted successfully"); - navigate("/repositories"); + void navigate("/repositories"); }, onError: (error) => { toast.error("Failed to delete repository", { diff --git a/app/client/modules/settings/routes/settings.tsx b/app/client/modules/settings/routes/settings.tsx index aca29aaf..19b1f022 100644 --- a/app/client/modules/settings/routes/settings.tsx +++ b/app/client/modules/settings/routes/settings.tsx @@ -54,7 +54,7 @@ export default function Settings({ loaderData }: Route.ComponentProps) { const logout = useMutation({ ...logoutMutation(), onSuccess: () => { - navigate("/login", { replace: true }); + void navigate("/login", { replace: true }); }, }); diff --git a/app/client/modules/volumes/routes/create-volume.tsx b/app/client/modules/volumes/routes/create-volume.tsx index e2faf6a6..46bbec06 100644 --- a/app/client/modules/volumes/routes/create-volume.tsx +++ b/app/client/modules/volumes/routes/create-volume.tsx @@ -33,7 +33,7 @@ export default function CreateVolume() { ...createVolumeMutation(), onSuccess: (data) => { toast.success("Volume created successfully"); - navigate(`/volumes/${data.name}`); + void navigate(`/volumes/${data.name}`); }, }); diff --git a/app/client/modules/volumes/routes/volume-details.tsx b/app/client/modules/volumes/routes/volume-details.tsx index 61131a9f..f2b3da4a 100644 --- a/app/client/modules/volumes/routes/volume-details.tsx +++ b/app/client/modules/volumes/routes/volume-details.tsx @@ -75,7 +75,7 @@ export default function VolumeDetails({ loaderData }: Route.ComponentProps) { ...deleteVolumeMutation(), onSuccess: () => { toast.success("Volume deleted successfully"); - navigate("/volumes"); + void navigate("/volumes"); }, onError: (error) => { toast.error("Failed to delete volume", { diff --git a/app/client/modules/volumes/tabs/info.tsx b/app/client/modules/volumes/tabs/info.tsx index 79a9c200..c960ba6c 100644 --- a/app/client/modules/volumes/tabs/info.tsx +++ b/app/client/modules/volumes/tabs/info.tsx @@ -37,7 +37,7 @@ export const VolumeInfoTabContent = ({ volume, statfs }: Props) => { setPendingValues(null); if (data.name !== volume.name) { - navigate(`/volumes/${data.name}`); + void navigate(`/volumes/${data.name}`); } }, onError: (error) => { diff --git a/app/root.tsx b/app/root.tsx index 85ac9b95..2bb3ac81 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -27,11 +27,11 @@ export const links: Route.LinksFunction = () => [ const queryClient = new QueryClient({ mutationCache: new MutationCache({ onSuccess: () => { - queryClient.invalidateQueries(); + void queryClient.invalidateQueries(); }, onError: (error) => { console.error("Mutation error:", error); - queryClient.invalidateQueries(); + void queryClient.invalidateQueries(); }, }), }); diff --git a/app/server/core/scheduler.ts b/app/server/core/scheduler.ts index bb066c51..d115fbeb 100644 --- a/app/server/core/scheduler.ts +++ b/app/server/core/scheduler.ts @@ -34,7 +34,7 @@ class SchedulerClass { async stop() { for (const task of this.tasks) { - task.stop(); + await task.stop(); } this.tasks = []; logger.info("Scheduler stopped"); @@ -42,7 +42,7 @@ class SchedulerClass { async clear() { for (const task of this.tasks) { - task.destroy(); + await task.destroy(); } this.tasks = []; logger.info("Scheduler cleared all tasks"); diff --git a/app/server/index.ts b/app/server/index.ts index 205180e0..52beea7c 100644 --- a/app/server/index.ts +++ b/app/server/index.ts @@ -22,9 +22,7 @@ runDbMigrations(); await retagSnapshots(); await validateRequiredMigrations(REQUIRED_MIGRATIONS); -startup(); - -logger.info(`Server is running at http://localhost:${config.port}`); +await startup(); export type AppType = typeof app; diff --git a/app/server/jobs/cleanup-sessions.ts b/app/server/jobs/cleanup-sessions.ts index 09ec34c9..e457f1ce 100644 --- a/app/server/jobs/cleanup-sessions.ts +++ b/app/server/jobs/cleanup-sessions.ts @@ -3,7 +3,7 @@ import { authService } from "../modules/auth/auth.service"; export class CleanupSessionsJob extends Job { async run() { - authService.cleanupExpiredSessions(); + await authService.cleanupExpiredSessions(); return { done: true, timestamp: new Date() }; } diff --git a/app/server/modules/backups/__tests__/backups.service.test.ts b/app/server/modules/backups/__tests__/backups.service.test.ts index 75933fc1..2c70f82e 100644 --- a/app/server/modules/backups/__tests__/backups.service.test.ts +++ b/app/server/modules/backups/__tests__/backups.service.test.ts @@ -101,7 +101,7 @@ describe("execute backup", () => { }); // act - backupsService.executeBackup(schedule.id); + void backupsService.executeBackup(schedule.id); await new Promise((resolve) => setTimeout(resolve, 10)); await backupsService.executeBackup(schedule.id); diff --git a/app/server/modules/backups/backups.service.ts b/app/server/modules/backups/backups.service.ts index 6c29f739..d18e507b 100644 --- a/app/server/modules/backups/backups.service.ts +++ b/app/server/modules/backups/backups.service.ts @@ -28,7 +28,7 @@ const calculateNextRun = (cronExpression: string): number => { return interval.next().getTime(); } catch (error) { - logger.error(`Failed to parse cron expression "${cronExpression}": ${error}`); + logger.error(`Failed to parse cron expression "${cronExpression}": ${toMessage(error)}`); const fallback = new Date(); fallback.setMinutes(fallback.getMinutes() + 1); return fallback.getTime(); diff --git a/app/server/modules/events/events.controller.ts b/app/server/modules/events/events.controller.ts index 4661e133..f7991735 100644 --- a/app/server/modules/events/events.controller.ts +++ b/app/server/modules/events/events.controller.ts @@ -13,14 +13,14 @@ export const eventsController = new Hono().use(requireAuth).get("/", (c) => { event: "connected", }); - const onBackupStarted = (data: { scheduleId: number; volumeName: string; repositoryName: string }) => { - stream.writeSSE({ + const onBackupStarted = async (data: { scheduleId: number; volumeName: string; repositoryName: string }) => { + await stream.writeSSE({ data: JSON.stringify(data), event: "backup:started", }); }; - const onBackupProgress = (data: { + const onBackupProgress = async (data: { scheduleId: number; volumeName: string; repositoryName: string; @@ -32,60 +32,60 @@ export const eventsController = new Hono().use(requireAuth).get("/", (c) => { bytes_done: number; current_files: string[]; }) => { - stream.writeSSE({ + await stream.writeSSE({ data: JSON.stringify(data), event: "backup:progress", }); }; - const onBackupCompleted = (data: { + const onBackupCompleted = async (data: { scheduleId: number; volumeName: string; repositoryName: string; status: "success" | "error" | "stopped" | "warning"; }) => { - stream.writeSSE({ + await stream.writeSSE({ data: JSON.stringify(data), event: "backup:completed", }); }; - const onVolumeMounted = (data: { volumeName: string }) => { - stream.writeSSE({ + const onVolumeMounted = async (data: { volumeName: string }) => { + await stream.writeSSE({ data: JSON.stringify(data), event: "volume:mounted", }); }; - const onVolumeUnmounted = (data: { volumeName: string }) => { - stream.writeSSE({ + const onVolumeUnmounted = async (data: { volumeName: string }) => { + await stream.writeSSE({ data: JSON.stringify(data), event: "volume:unmounted", }); }; - const onVolumeUpdated = (data: { volumeName: string }) => { - stream.writeSSE({ + const onVolumeUpdated = async (data: { volumeName: string }) => { + await stream.writeSSE({ data: JSON.stringify(data), event: "volume:updated", }); }; - const onMirrorStarted = (data: { scheduleId: number; repositoryId: string; repositoryName: string }) => { - stream.writeSSE({ + const onMirrorStarted = async (data: { scheduleId: number; repositoryId: string; repositoryName: string }) => { + await stream.writeSSE({ data: JSON.stringify(data), event: "mirror:started", }); }; - const onMirrorCompleted = (data: { + const onMirrorCompleted = async (data: { scheduleId: number; repositoryId: string; repositoryName: string; status: "success" | "error"; error?: string; }) => { - stream.writeSSE({ + await stream.writeSSE({ data: JSON.stringify(data), event: "mirror:completed", }); diff --git a/app/server/utils/logger.ts b/app/server/utils/logger.ts index ecc323b2..6d106f60 100644 --- a/app/server/utils/logger.ts +++ b/app/server/utils/logger.ts @@ -3,7 +3,7 @@ import { sanitizeSensitiveData } from "./sanitize"; const { printf, combine, colorize } = format; -const printConsole = printf((info) => `${info.level} > ${info.message}`); +const printConsole = printf((info) => `${info.level} > ${String(info.message)}`); const consoleFormat = combine(colorize(), printConsole); const getDefaultLevel = () => { @@ -27,7 +27,7 @@ const log = (level: "info" | "warn" | "error" | "debug", messages: unknown[]) => return sanitizeSensitiveData(JSON.stringify(m, null, 2)); } - return sanitizeSensitiveData(String(m)); + return sanitizeSensitiveData(String(JSON.stringify(m))); }); winstonLogger.log(level, stringMessages.join(" ")); diff --git a/app/server/utils/rclone.ts b/app/server/utils/rclone.ts index 92beeccc..4e6470d0 100644 --- a/app/server/utils/rclone.ts +++ b/app/server/utils/rclone.ts @@ -1,5 +1,6 @@ import { $ } from "bun"; import { logger } from "./logger"; +import { toMessage } from "./errors"; /** * List all configured rclone remotes @@ -9,7 +10,7 @@ export async function listRcloneRemotes(): Promise { const result = await $`rclone listremotes`.nothrow(); if (result.exitCode !== 0) { - logger.error(`Failed to list rclone remotes: ${result.stderr}`); + logger.error(`Failed to list rclone remotes: ${result.stderr.toString()}`); return []; } @@ -36,7 +37,7 @@ export async function getRcloneRemoteInfo( const result = await $`rclone config show ${remote}`.quiet(); if (result.exitCode !== 0) { - logger.error(`Failed to get info for remote ${remote}: ${result.stderr}`); + logger.error(`Failed to get info for remote ${remote}: ${result.stderr.toString()}`); return null; } @@ -70,7 +71,7 @@ export async function getRcloneRemoteInfo( return { type, config }; } catch (error) { - logger.error(`Error getting remote info for ${remote}: ${error}`); + logger.error(`Error getting remote info for ${remote}: ${toMessage(error)}`); return null; } } diff --git a/app/server/utils/restic.ts b/app/server/utils/restic.ts index 17d05310..1e2051ab 100644 --- a/app/server/utils/restic.ts +++ b/app/server/utils/restic.ts @@ -358,8 +358,8 @@ const backup = async ( } }, finally: async () => { - includeFile && (await fs.unlink(includeFile).catch(() => {})); - excludeFile && (await fs.unlink(excludeFile).catch(() => {})); + if (includeFile) await fs.unlink(includeFile).catch(() => {}); + if (excludeFile) await fs.unlink(excludeFile).catch(() => {}); await cleanupTemporaryKeys(env); }, }); @@ -393,7 +393,7 @@ const backup = async ( const result = backupOutputSchema(summaryLine); if (result instanceof type.errors) { - logger.error(`Restic backup output validation failed: ${result}`); + logger.error(`Restic backup output validation failed: ${result.summary}`); return { result: null, exitCode: res.exitCode }; } @@ -484,7 +484,7 @@ const restore = async ( const result = restoreOutputSchema(resSummary); if (result instanceof type.errors) { - logger.warn(`Restic restore output validation failed: ${result}`); + logger.warn(`Restic restore output validation failed: ${result.summary}`); logger.info(`Restic restore completed for snapshot ${snapshotId} to target ${target}`); return { message_type: "summary" as const, @@ -529,8 +529,8 @@ const snapshots = async (config: RepositoryConfig, options: { tags?: string[] } const result = snapshotInfoSchema.array()(JSON.parse(res.stdout)); if (result instanceof type.errors) { - logger.error(`Restic snapshots output validation failed: ${result}`); - throw new Error(`Restic snapshots output validation failed: ${result}`); + logger.error(`Restic snapshots output validation failed: ${result.summary}`); + throw new Error(`Restic snapshots output validation failed: ${result.summary}`); } return result; @@ -712,8 +712,8 @@ const ls = async (config: RepositoryConfig, snapshotId: string, path?: string) = const snapshot = lsSnapshotInfoSchema(snapshotLine); if (snapshot instanceof type.errors) { - logger.error(`Restic ls snapshot info validation failed: ${snapshot}`); - throw new Error(`Restic ls snapshot info validation failed: ${snapshot}`); + logger.error(`Restic ls snapshot info validation failed: ${snapshot.summary}`); + throw new Error(`Restic ls snapshot info validation failed: ${snapshot.summary}`); } const nodes: Array = []; @@ -722,7 +722,7 @@ const ls = async (config: RepositoryConfig, snapshotId: string, path?: string) = const nodeValidation = lsNodeSchema(nodeLine); if (nodeValidation instanceof type.errors) { - logger.warn(`Skipping invalid node: ${nodeValidation}`); + logger.warn(`Skipping invalid node: ${nodeValidation.summary}`); continue; } diff --git a/app/test/setup.ts b/app/test/setup.ts index 8791d42c..43360902 100644 --- a/app/test/setup.ts +++ b/app/test/setup.ts @@ -4,7 +4,7 @@ import path from "node:path"; import { cwd } from "node:process"; import { db } from "~/server/db/db"; -mock.module("~/server/utils/logger", () => ({ +void mock.module("~/server/utils/logger", () => ({ logger: { debug: () => {}, info: () => {}, diff --git a/biome.json b/biome.json deleted file mode 100644 index 928a49a7..00000000 --- a/biome.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", - "vcs": { - "enabled": true, - "clientKind": "git", - "defaultBranch": "origin/main", - "useIgnoreFile": true - }, - "files": { - "includes": ["**/*.{ts,tsx,json}", "!**/api-client", "!**/components/ui"], - "ignoreUnknown": false - }, - "formatter": { - "enabled": true, - "indentStyle": "tab", - "lineWidth": 120 - }, - "linter": { - "enabled": true, - "rules": { - "recommended": true - } - }, - "javascript": { - "formatter": { - "quoteStyle": "double" - } - }, - "assist": { - "enabled": true, - "actions": { - "source": { - "organizeImports": "off" - } - } - }, - "css": { - "parser": { - "tailwindDirectives": true - } - } -} diff --git a/bun.lock b/bun.lock index d69df99c..b7fb5e1b 100644 --- a/bun.lock +++ b/bun.lock @@ -64,7 +64,6 @@ "yaml": "^2.8.2", }, "devDependencies": { - "@biomejs/biome": "^2.3.8", "@faker-js/faker": "^10.1.0", "@happy-dom/global-registrator": "^20.0.11", "@hey-api/openapi-ts": "^0.88.0", @@ -82,6 +81,9 @@ "dotenv-cli": "^11.0.0", "drizzle-kit": "^0.31.7", "lightningcss": "^1.30.2", + "oxfmt": "^0.22.0", + "oxlint": "^1.36.0", + "oxlint-tsgolint": "^0.10.1", "tailwindcss": "^4.1.17", "tinyglobby": "^0.2.15", "tw-animate-css": "^1.4.0", @@ -158,24 +160,6 @@ "@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="], - "@biomejs/biome": ["@biomejs/biome@2.3.10", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.10", "@biomejs/cli-darwin-x64": "2.3.10", "@biomejs/cli-linux-arm64": "2.3.10", "@biomejs/cli-linux-arm64-musl": "2.3.10", "@biomejs/cli-linux-x64": "2.3.10", "@biomejs/cli-linux-x64-musl": "2.3.10", "@biomejs/cli-win32-arm64": "2.3.10", "@biomejs/cli-win32-x64": "2.3.10" }, "bin": { "biome": "bin/biome" } }, "sha512-/uWSUd1MHX2fjqNLHNL6zLYWBbrJeG412/8H7ESuK8ewoRoMPUgHDebqKrPTx/5n6f17Xzqc9hdg3MEqA5hXnQ=="], - - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-M6xUjtCVnNGFfK7HMNKa593nb7fwNm43fq1Mt71kpLpb+4mE7odO8W/oWVDyBVO4ackhresy1ZYO7OJcVo/B7w=="], - - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-Vae7+V6t/Avr8tVbFNjnFSTKZogZHFYl7MMH62P/J1kZtr0tyRQ9Fe0onjqjS2Ek9lmNLmZc/VR5uSekh+p1fg=="], - - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-hhPw2V3/EpHKsileVOFynuWiKRgFEV48cLe0eA+G2wO4SzlwEhLEB9LhlSrVeu2mtSn205W283LkX7Fh48CaxA=="], - - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-B9DszIHkuKtOH2IFeeVkQmSMVUjss9KtHaNXquYYWCjH8IstNgXgx5B0aSBQNr6mn4RcKKRQZXn9Zu1rM3O0/A=="], - - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.10", "", { "os": "linux", "cpu": "x64" }, "sha512-wwAkWD1MR95u+J4LkWP74/vGz+tRrIQvr8kfMMJY8KOQ8+HMVleREOcPYsQX82S7uueco60L58Wc6M1I9WA9Dw=="], - - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.10", "", { "os": "linux", "cpu": "x64" }, "sha512-QTfHZQh62SDFdYc2nfmZFuTm5yYb4eO1zwfB+90YxUumRCR171tS1GoTX5OD0wrv4UsziMPmrePMtkTnNyYG3g=="], - - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-o7lYc9n+CfRbHvkjPhm8s9FgbKdYZu5HCcGVMItLjz93EhgJ8AM44W+QckDqLA9MKDNFrR8nPbO4b73VC5kGGQ=="], - - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.10", "", { "os": "win32", "cpu": "x64" }, "sha512-pHEFgq7dUEsKnqG9mx9bXihxGI49X+ar+UBrEIj3Wqj3UCZp1rNgV+OoyjFgcXsjCWpuEAF4VJdkZr3TrWdCbQ=="], - "@colors/colors": ["@colors/colors@1.6.0", "", {}, "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA=="], "@dabh/diagnostics": ["@dabh/diagnostics@2.0.8", "", { "dependencies": { "@so-ric/colorspace": "^1.1.6", "enabled": "2.0.x", "kuler": "^2.0.0" } }, "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q=="], @@ -320,6 +304,50 @@ "@mjackson/node-fetch-server": ["@mjackson/node-fetch-server@0.2.0", "", {}, "sha512-EMlH1e30yzmTpGLQjlFmaDAjyOeZhng1/XCd7DExR8PNAnG/G1tyruZxEoUe11ClnwGhGrtsdnyyUx1frSzjng=="], + "@oxfmt/darwin-arm64": ["@oxfmt/darwin-arm64@0.22.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-dhz2m2uLrHT3MwM+LAdvr97EojJZTwaZ6BuMTRftJzqa9dHYDG/MtSBuDD2DpGpZ0SM2iVwni2wCzCYGKTojbA=="], + + "@oxfmt/darwin-x64": ["@oxfmt/darwin-x64@0.22.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-VykUbibvqSOG5YIFUMpHtZVrY1YKDl9Il2SvFemUfR5Ac1t1BFZOnazYe98jtZGFY4sEdEORs0ImBARnyMX/hw=="], + + "@oxfmt/linux-arm64-gnu": ["@oxfmt/linux-arm64-gnu@0.22.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-y0MBha/K34TztYAZUn6KQE9xLPLNHqRpOdzRp96fhkbrQTeEXo+jF+8+aV8VnqjG0y7p+IQN4ATxNSstSPO9sA=="], + + "@oxfmt/linux-arm64-musl": ["@oxfmt/linux-arm64-musl@0.22.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-8a0p2UEmavB+moQ7ID17i+dE7N2xng6lPU8vrrNnnwKde0YpGHdW6hmuH4mS+rrltvs0fjyGRSvCnD2Qm9IAcA=="], + + "@oxfmt/linux-x64-gnu": ["@oxfmt/linux-x64-gnu@0.22.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ZA1lS6MLvtGfD9AaDylCSTTiOWVQs1eIl9uqsGYs+Zr8p0mI7QRIRA6juWk9FXn1hHfmYBdBgWu2GdIW0YFCFA=="], + + "@oxfmt/linux-x64-musl": ["@oxfmt/linux-x64-musl@0.22.0", "", { "os": "linux", "cpu": "x64" }, "sha512-J5zFB8T5yk6Jx63rdKuXfcPqR1cAp12nO5/NJfGITH00AML2Yj9JM4dRnmssJomYHKa8dNSr40l6OdxRZN88CQ=="], + + "@oxfmt/win32-arm64": ["@oxfmt/win32-arm64@0.22.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-XYxyIiOf3HqlfETLFKqCHYL88mhw+Ka25vDVgmlcghbJv9BPoVzquZW7P4i0T3D5GWp4LHhZHmMo8BuK8PP5BA=="], + + "@oxfmt/win32-x64": ["@oxfmt/win32-x64@0.22.0", "", { "os": "win32", "cpu": "x64" }, "sha512-/shfU+wwlXcKP2NkZt+kYCSVom2EEu8MwbENlYCak6LtPPrN5xAQhHuOSFByjDzTBApdQugch0j0ZB/4Wyaljg=="], + + "@oxlint-tsgolint/darwin-arm64": ["@oxlint-tsgolint/darwin-arm64@0.10.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-KGC4++BeEqrIcmDHiJt/e6/860PWJmUJjjp0mE+smpBmRXMjmOFFjrPmN+ZyCyVgf1WdmhPkQXsRSPeTR+2omw=="], + + "@oxlint-tsgolint/darwin-x64": ["@oxlint-tsgolint/darwin-x64@0.10.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-tvmrDgj3Q0tdc+zMWfCVLVq8EQDEUqasm1zaWgSMYIszpID6qdgqbT+OpWWXV9fLZgtvrkoXGwxkHAUJzdVZXQ=="], + + "@oxlint-tsgolint/linux-arm64": ["@oxlint-tsgolint/linux-arm64@0.10.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-7kD28z6/ykGx8WetKTPRZt30pd+ziassxg/8cM24lhjUI+hNXyRHVtHes73dh9D6glJKno+1ut+3amUdZBZcpQ=="], + + "@oxlint-tsgolint/linux-x64": ["@oxlint-tsgolint/linux-x64@0.10.1", "", { "os": "linux", "cpu": "x64" }, "sha512-NmJmiqdzYUTHIxteSTyX6IFFgnIsOAjRWXfrS6Jbo5xlB3g39WHniSF3asB/khLJNtwSg4InUS34NprYM7zrEw=="], + + "@oxlint-tsgolint/win32-arm64": ["@oxlint-tsgolint/win32-arm64@0.10.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-3KrT80vl3nXUkjuJI/z8dF6xWsKx0t9Tz4ZQHgQw3fYw+CoihBRWGklrdlmCz+EGfMyVaQLqBV9PZckhSqLe2A=="], + + "@oxlint-tsgolint/win32-x64": ["@oxlint-tsgolint/win32-x64@0.10.1", "", { "os": "win32", "cpu": "x64" }, "sha512-hW1fSJZVxG51sLdGq1sQjOzb1tsQ23z/BquJfUwL7CqBobxr7TJvGmoINL+9KryOJt0jCoaiMfWe4yoYw5XfIA=="], + + "@oxlint/darwin-arm64": ["@oxlint/darwin-arm64@1.36.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-MJkj82GH+nhvWKJhSIM6KlZ8tyGKdogSQXtNdpIyP02r/tTayFJQaAEWayG2Jhsn93kske+nimg5MYFhwO/rlg=="], + + "@oxlint/darwin-x64": ["@oxlint/darwin-x64@1.36.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-VvEhfkqj/99dCTqOcfkyFXOSbx4lIy5u2m2GHbK4WCMDySokOcMTNRHGw8fH/WgQ5cDrDMSTYIGQTmnBGi9tiQ=="], + + "@oxlint/linux-arm64-gnu": ["@oxlint/linux-arm64-gnu@1.36.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-EMx92X5q+hHc3olTuj/kgkx9+yP0p/AVs4yvHbUfzZhBekXNpUWxWvg4hIKmQWn+Ee2j4o80/0ACGO0hDYJ9mg=="], + + "@oxlint/linux-arm64-musl": ["@oxlint/linux-arm64-musl@1.36.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-7YCxtrPIctVYLqWrWkk8pahdCxch6PtsaucfMLC7TOlDt4nODhnQd4yzEscKqJ8Gjrw1bF4g+Ngob1gB+Qr9Fw=="], + + "@oxlint/linux-x64-gnu": ["@oxlint/linux-x64-gnu@1.36.0", "", { "os": "linux", "cpu": "x64" }, "sha512-lnaJVlx5r3NWmoOMesfQXJSf78jHTn8Z+sdAf795Kgteo72+qGC1Uax2SToCJVN2J8PNG3oRV5bLriiCNR2i6Q=="], + + "@oxlint/linux-x64-musl": ["@oxlint/linux-x64-musl@1.36.0", "", { "os": "linux", "cpu": "x64" }, "sha512-AhuEU2Qdl66lSfTGu/Htirq8r/8q2YnZoG3yEXLMQWnPMn7efy8spD/N1NA7kH0Hll+cdfwgQkQqC2G4MS2lPQ=="], + + "@oxlint/win32-arm64": ["@oxlint/win32-arm64@1.36.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-GlWCBjUJY2QgvBFuNRkiRJu7K/djLmM0UQKfZV8IN+UXbP/JbjZHWKRdd4LXlQmzoz7M5Hd6p+ElCej8/90FCg=="], + + "@oxlint/win32-x64": ["@oxlint/win32-x64@1.36.0", "", { "os": "win32", "cpu": "x64" }, "sha512-J+Vc00Utcf8p77lZPruQgb0QnQXuKnFogN88kCnOqs2a83I+vTBB8ILr0+L9sTwVRvIDMSC0pLdeQH4svWGFZg=="], + "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], @@ -1112,6 +1140,12 @@ "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], + "oxfmt": ["oxfmt@0.22.0", "", { "dependencies": { "tinypool": "2.0.0" }, "optionalDependencies": { "@oxfmt/darwin-arm64": "0.22.0", "@oxfmt/darwin-x64": "0.22.0", "@oxfmt/linux-arm64-gnu": "0.22.0", "@oxfmt/linux-arm64-musl": "0.22.0", "@oxfmt/linux-x64-gnu": "0.22.0", "@oxfmt/linux-x64-musl": "0.22.0", "@oxfmt/win32-arm64": "0.22.0", "@oxfmt/win32-x64": "0.22.0" }, "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-Z7JM5yv4KaDz5kT21MxRAvtYo5Eu9Ti/XY1JYShSOlaH859XSn4UaS3wlZKyz4Mpbo8ISkxhU75UY5yp+OQUyA=="], + + "oxlint": ["oxlint@1.36.0", "", { "optionalDependencies": { "@oxlint/darwin-arm64": "1.36.0", "@oxlint/darwin-x64": "1.36.0", "@oxlint/linux-arm64-gnu": "1.36.0", "@oxlint/linux-arm64-musl": "1.36.0", "@oxlint/linux-x64-gnu": "1.36.0", "@oxlint/linux-x64-musl": "1.36.0", "@oxlint/win32-arm64": "1.36.0", "@oxlint/win32-x64": "1.36.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.10.0" }, "optionalPeers": ["oxlint-tsgolint"], "bin": { "oxc_language_server": "bin/oxc_language_server", "oxlint": "bin/oxlint" } }, "sha512-IicUdXfXgI8OKrDPnoSjvBfeEF8PkKtm+CoLlg4LYe4ypc8U+T4r7730XYshdBGZdelg+JRw8GtCb2w/KaaZvw=="], + + "oxlint-tsgolint": ["oxlint-tsgolint@0.10.1", "", { "optionalDependencies": { "@oxlint-tsgolint/darwin-arm64": "0.10.1", "@oxlint-tsgolint/darwin-x64": "0.10.1", "@oxlint-tsgolint/linux-arm64": "0.10.1", "@oxlint-tsgolint/linux-x64": "0.10.1", "@oxlint-tsgolint/win32-arm64": "0.10.1", "@oxlint-tsgolint/win32-x64": "0.10.1" }, "bin": { "tsgolint": "bin/tsgolint.js" } }, "sha512-EEHNdo5cW2w1xwYdBQ7d3IXDqWAtMkfVFrh+9gQ4kYbYJwygY4QXSh1eH80/xVipZdVKujAwBgg/nNNHk56kxQ=="], + "p-map": ["p-map@7.0.4", "", {}, "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ=="], "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], @@ -1286,6 +1320,8 @@ "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + "tinypool": ["tinypool@2.0.0", "", {}, "sha512-/RX9RzeH2xU5ADE7n2Ykvmi9ED3FBGPAjw9u3zucrNNaEBIO0HPSYgL0NT7+3p147ojeSdaVu08F6hjpv31HJg=="], + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], diff --git a/components.json b/components.json index e74bbe43..94cd1628 100644 --- a/components.json +++ b/components.json @@ -11,11 +11,11 @@ "prefix": "" }, "aliases": { - "components": "~/components", - "utils": "~/lib/utils", - "ui": "~/components/ui", - "lib": "~/lib", - "hooks": "~/hooks" + "components": "~/client/components", + "utils": "~/client/lib/utils", + "ui": "~/client/components/ui", + "lib": "~/client/lib", + "hooks": "~/client/hooks" }, "iconLibrary": "lucide" } diff --git a/package.json b/package.json index fb4586b3..f13d45de 100644 --- a/package.json +++ b/package.json @@ -4,14 +4,13 @@ "type": "module", "packageManager": "bun@1.3.5", "scripts": { + "lint": "oxlint --type-aware", "build": "react-router build", "dev": "bunx --bun vite", "start": "bun ./dist/server/index.js", "cli:dev": "bun run app/server/cli/main.ts", "cli": "ZEROBYTE_CLI=1 bun ./dist/server/index.js", "tsc": "react-router typegen && tsc", - "lint": "biome check .", - "lint:ci": "biome ci . --changed --error-on-warnings --no-errors-on-unmatched", "start:dev": "docker compose down && docker compose up --build zerobyte-dev", "start:prod": "docker compose down && docker compose up --build zerobyte-prod", "gen:api-client": "openapi-ts", @@ -84,7 +83,6 @@ "yaml": "^2.8.2" }, "devDependencies": { - "@biomejs/biome": "^2.3.8", "@faker-js/faker": "^10.1.0", "@happy-dom/global-registrator": "^20.0.11", "@hey-api/openapi-ts": "^0.88.0", @@ -102,6 +100,9 @@ "dotenv-cli": "^11.0.0", "drizzle-kit": "^0.31.7", "lightningcss": "^1.30.2", + "oxfmt": "^0.22.0", + "oxlint": "^1.36.0", + "oxlint-tsgolint": "^0.10.1", "tailwindcss": "^4.1.17", "tinyglobby": "^0.2.15", "tw-animate-css": "^1.4.0", diff --git a/tsconfig.json b/tsconfig.json index 49baa089..4967a03c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,6 @@ "moduleResolution": "bundler", "jsx": "react-jsx", "rootDirs": [".", "./.react-router/types"], - "baseUrl": ".", "paths": { "~/*": ["./app/*"] },