From 9247d36d776e25e85c53829b95e871ce263a791d Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 19:59:59 +0000 Subject: [PATCH 1/2] Move the packages into apps/ and packages/ (renames only) backend/ -> apps/server/, frontend/ -> apps/web/, shared/ -> packages/shared/src/, dev/setup.sh -> scripts/dev/setup.sh, start.sh -> scripts/start.sh. Nothing but the renames, so the history follows them; the paths are fixed in the next commit. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Y3rdfbWb1xDUdPRng7TX9Z --- {backend => apps/server}/bun.lock | 0 {backend => apps/server}/package.json | 0 {backend => apps/server}/schedules.json | 0 {backend => apps/server}/scripts/seed-demo.ts | 0 .../server}/src/__tests__/assert-error.ts | 0 .../server}/src/__tests__/atomic-write.test.ts | 0 {backend => apps/server}/src/__tests__/auth.test.ts | 0 .../server}/src/__tests__/backup.test.ts | 0 .../server}/src/__tests__/config.test.ts | 0 .../server}/src/__tests__/daemon-engine.test.ts | 0 {backend => apps/server}/src/__tests__/demo.test.ts | 0 .../server}/src/__tests__/disk-check.test.ts | 0 .../server}/src/__tests__/disk-management.test.ts | 0 .../server}/src/__tests__/disk-power.test.ts | 0 .../server}/src/__tests__/disk-removal.test.ts | 0 .../server}/src/__tests__/disk-replacement.test.ts | 0 .../server}/src/__tests__/docker.test.ts | 0 .../server}/src/__tests__/duplicates.test.ts | 0 .../server}/src/__tests__/engine-contract.test.ts | 0 .../server}/src/__tests__/engine-contract.ts | 0 .../server}/src/__tests__/engine-settings.test.ts | 0 .../server}/src/__tests__/fake-engine.ts | 0 .../server}/src/__tests__/force-option.test.ts | 0 .../server}/src/__tests__/frontend.test.ts | 0 {backend => apps/server}/src/__tests__/heal.test.ts | 0 .../server}/src/__tests__/home-assistant.test.ts | 0 .../server}/src/__tests__/log-manager.test.ts | 0 .../src/__tests__/maintenance-settings.test.ts | 0 {backend => apps/server}/src/__tests__/mcp.test.ts | 0 .../server}/src/__tests__/metrics.test.ts | 0 .../server}/src/__tests__/notifications.test.ts | 0 .../server}/src/__tests__/recovery.test.ts | 0 .../server}/src/__tests__/restore-ownership.test.ts | 0 .../server}/src/__tests__/run-result.test.ts | 0 .../server}/src/__tests__/scheduler.test.ts | 0 .../server}/src/__tests__/setup.test.ts | 0 .../server}/src/__tests__/smart-baseline.test.ts | 0 .../server}/src/__tests__/smart-history.test.ts | 0 .../server}/src/__tests__/smart-ignore.test.ts | 0 .../server}/src/__tests__/smart-selftest.test.ts | 0 .../server}/src/__tests__/spindown.test.ts | 0 .../server}/src/__tests__/usage-history.test.ts | 0 .../server}/src/__tests__/version.test.ts | 0 {backend => apps/server}/src/app-files.ts | 0 {backend => apps/server}/src/array-snapshot.ts | 0 {backend => apps/server}/src/atomic-write.ts | 0 {backend => apps/server}/src/auth.ts | 0 {backend => apps/server}/src/backup.ts | 0 {backend => apps/server}/src/bin-cli.ts | 0 {backend => apps/server}/src/bin.ts | 0 {backend => apps/server}/src/command.ts | 0 {backend => apps/server}/src/config-parser.ts | 0 {backend => apps/server}/src/config.ts | 0 {backend => apps/server}/src/container-pause.ts | 0 {backend => apps/server}/src/credentials.ts | 0 {backend => apps/server}/src/demo.ts | 0 {backend => apps/server}/src/disk-check.ts | 0 {backend => apps/server}/src/disk-power.ts | 0 {backend => apps/server}/src/disk-removal.ts | 0 {backend => apps/server}/src/disk-replacement.ts | 0 {backend => apps/server}/src/docker.ts | 0 {backend => apps/server}/src/duplicates.ts | 0 .../server}/src/engine/REVIEWED_DAEMON_VERSION | 0 {backend => apps/server}/src/engine/cli-engine.ts | 0 .../server}/src/engine/daemon-engine.ts | 0 .../server}/src/engine/engine-settings.ts | 0 {backend => apps/server}/src/engine/engine.ts | 0 .../server}/src/executors/command-executor.ts | 0 {backend => apps/server}/src/executors/log-tail.ts | 0 {backend => apps/server}/src/frontend.ts | 0 {backend => apps/server}/src/fs.ts | 0 .../server}/src/home-assistant-service.ts | 0 {backend => apps/server}/src/home-assistant.ts | 0 {backend => apps/server}/src/log-manager.ts | 0 {backend => apps/server}/src/main.ts | 0 .../server}/src/maintenance-settings.ts | 0 {backend => apps/server}/src/mcp.ts | 0 {backend => apps/server}/src/metrics.ts | 0 {backend => apps/server}/src/notification-events.ts | 0 {backend => apps/server}/src/notifications.ts | 0 {backend => apps/server}/src/parity-usage.ts | 0 .../src/parsers/__tests__/fixtures/check-ok.log | 0 .../src/parsers/__tests__/fixtures/check.log | 0 .../src/parsers/__tests__/fixtures/diff-remove.log | 0 .../server}/src/parsers/__tests__/fixtures/diff.log | 0 .../server}/src/parsers/__tests__/fixtures/dup.log | 0 .../server}/src/parsers/__tests__/fixtures/list.log | 0 .../src/parsers/__tests__/fixtures/probe.log | 0 .../src/parsers/__tests__/fixtures/smart.log | 0 .../src/parsers/__tests__/fixtures/status-bad.log | 0 .../src/parsers/__tests__/fixtures/status.log | 0 .../src/parsers/__tests__/fixtures/touch.log | 0 .../server}/src/parsers/__tests__/parsers.test.ts | 0 .../src/parsers/__tests__/progress-parser.test.ts | 0 .../server}/src/parsers/check-parser.ts | 0 .../server}/src/parsers/devices-parser.ts | 0 {backend => apps/server}/src/parsers/df-parser.ts | 0 {backend => apps/server}/src/parsers/diff-parser.ts | 0 {backend => apps/server}/src/parsers/dup-parser.ts | 0 {backend => apps/server}/src/parsers/list-parser.ts | 0 .../server}/src/parsers/probe-parser.ts | 0 .../server}/src/parsers/progress-parser.ts | 0 .../server}/src/parsers/smart-parser.ts | 0 .../server}/src/parsers/status-parser.ts | 0 .../server}/src/parsers/structured-log.ts | 0 {backend => apps/server}/src/parsers/utils.ts | 0 {backend => apps/server}/src/passkeys.ts | 0 {backend => apps/server}/src/recovery.ts | 0 {backend => apps/server}/src/restore-ownership.ts | 0 .../server}/src/routes/config-operations.ts | 0 {backend => apps/server}/src/routes/config.ts | 0 .../server}/src/routes/disk-management.ts | 0 .../server}/src/routes/disk-replacement.ts | 0 {backend => apps/server}/src/routes/engine.ts | 0 {backend => apps/server}/src/routes/filesystem.ts | 0 {backend => apps/server}/src/routes/hardware.ts | 0 .../server}/src/routes/home-assistant.ts | 0 {backend => apps/server}/src/routes/logs.ts | 0 {backend => apps/server}/src/routes/maintenance.ts | 0 {backend => apps/server}/src/routes/mcp.ts | 0 {backend => apps/server}/src/routes/metrics.ts | 0 .../server}/src/routes/notifications.ts | 0 {backend => apps/server}/src/routes/reports.ts | 0 {backend => apps/server}/src/routes/schedules.ts | 0 {backend => apps/server}/src/routes/setup.ts | 0 {backend => apps/server}/src/routes/snapraid.ts | 0 {backend => apps/server}/src/run-report.ts | 0 {backend => apps/server}/src/scheduler.ts | 0 {backend => apps/server}/src/setup.ts | 0 {backend => apps/server}/src/smart-baseline.ts | 0 {backend => apps/server}/src/smart-history.ts | 0 {backend => apps/server}/src/smart-ignore.ts | 0 {backend => apps/server}/src/smart-selftest.ts | 0 {backend => apps/server}/src/snapraid-runner.ts | 0 {backend => apps/server}/src/spindown.ts | 0 {backend => apps/server}/src/status-cache.ts | 0 {backend => apps/server}/src/usage-history.ts | 0 {backend => apps/server}/src/version.ts | 0 {backend => apps/server}/src/websocket.ts | 0 {backend => apps/server}/tsconfig.json | 0 {frontend => apps/web}/.cta.json | 0 {frontend => apps/web}/.gitignore | 0 {frontend => apps/web}/README.md | 0 {frontend => apps/web}/biome.json | 0 {frontend => apps/web}/bun.lock | 0 {frontend => apps/web}/components.json | 0 {frontend => apps/web}/messages/de.json | 0 {frontend => apps/web}/messages/en.json | 0 {frontend => apps/web}/messages/it.json | 0 {frontend => apps/web}/package.json | 0 {frontend => apps/web}/project.inlang/project_id | 0 {frontend => apps/web}/project.inlang/settings.json | 0 .../web}/public/apple-touch-icon-180x180.png | Bin {frontend => apps/web}/public/favicon.ico | Bin {frontend => apps/web}/public/favicon.svg | 0 .../web}/public/maskable-icon-512x512.png | Bin {frontend => apps/web}/public/pwa-192x192.png | Bin {frontend => apps/web}/public/pwa-512x512.png | Bin {frontend => apps/web}/public/pwa-64x64.png | Bin {frontend => apps/web}/public/robots.txt | 0 {frontend => apps/web}/pwa-assets.config.ts | 0 {frontend => apps/web}/scripts/add-messages.py | 0 {frontend => apps/web}/scripts/release.sh | 0 .../web}/src/components/AccountDialog.tsx | 0 .../web}/src/components/AppSidebar.tsx | 0 .../web}/src/components/ArrayHealthPanel.tsx | 0 {frontend => apps/web}/src/components/AuthGate.tsx | 0 .../web}/src/components/BackupDialog.tsx | 0 {frontend => apps/web}/src/components/Button.tsx | 0 .../web}/src/components/CheckDialog.tsx | 0 .../web}/src/components/CheckViewer.tsx | 0 .../web}/src/components/CommandMenu.tsx | 0 .../web}/src/components/CommandPalette.tsx | 0 .../web}/src/components/ConfigAddForm.tsx | 0 {frontend => apps/web}/src/components/ConfigBar.tsx | 0 .../web}/src/components/ConfigEditor.tsx | 0 .../web}/src/components/ConfigList.tsx | 0 .../web}/src/components/ConfigListItem.tsx | 0 .../web}/src/components/ConfigManager.tsx | 0 .../web}/src/components/ConfigTextEditor.tsx | 0 .../web}/src/components/ContentFileSection.tsx | 0 .../web}/src/components/DataDiskSection.tsx | 0 .../web}/src/components/DirectoryBrowser.tsx | 0 .../web}/src/components/DiskManager.tsx | 0 .../web}/src/components/DisksPanel.tsx | 0 .../web}/src/components/ErrorAlert.tsx | 0 .../web}/src/components/ExcludePatternSection.tsx | 0 {frontend => apps/web}/src/components/Feedback.tsx | 0 .../web}/src/components/FileBrowser.tsx | 0 .../web}/src/components/ForceRetryBox.tsx | 0 {frontend => apps/web}/src/components/Header.tsx | 0 .../web}/src/components/IntegrityReport.tsx | 0 .../web}/src/components/LogFilters.tsx | 0 {frontend => apps/web}/src/components/LogList.tsx | 0 .../web}/src/components/LogListItem.tsx | 0 .../web}/src/components/LogRawView.tsx | 0 {frontend => apps/web}/src/components/LogViewer.tsx | 0 {frontend => apps/web}/src/components/LoginPage.tsx | 0 .../web}/src/components/MountSuggestions.tsx | 0 .../web}/src/components/OptionsSection.tsx | 0 .../web}/src/components/OutputConsole.tsx | 0 .../web}/src/components/PageLayout.tsx | 0 .../web}/src/components/ParityDiskSection.tsx | 0 .../web}/src/components/PoolSection.tsx | 0 .../web}/src/components/RemoveDataDiskWizard.tsx | 0 .../web}/src/components/ReplaceDiskWizard.tsx | 0 .../web}/src/components/RunningJobBox.tsx | 0 {frontend => apps/web}/src/components/SaveBar.tsx | 0 .../web}/src/components/ScrubDialog.tsx | 0 .../web}/src/components/ScrubPlanPicker.tsx | 0 .../web}/src/components/SegmentedControl.tsx | 0 {frontend => apps/web}/src/components/Select.tsx | 0 {frontend => apps/web}/src/components/Skeleton.tsx | 0 .../web}/src/components/SmartHistoryCharts.tsx | 0 .../web}/src/components/SmartMonitor.tsx | 0 .../web}/src/components/SmartSelfTest.tsx | 0 .../web}/src/components/SyncPreviewDialog.tsx | 0 .../web}/src/components/UsageHistoryChart.tsx | 0 .../web}/src/components/ValidationResultAlert.tsx | 0 .../web}/src/components/ViewModeToggle.tsx | 0 .../src/components/__tests__/array-health.test.ts | 0 .../src/components/__tests__/schedule-text.test.ts | 0 .../components/__tests__/temperature-trend.test.ts | 0 .../web}/src/components/schedules/ScheduleForm.tsx | 0 .../web}/src/components/schedules/ScheduleRow.tsx | 0 .../web}/src/components/schedules/ScheduleWeek.tsx | 0 .../web}/src/components/schedules/scheduleText.ts | 0 .../web}/src/components/smartHistory.ts | 0 .../web}/src/components/temperatureTrend.ts | 0 .../web}/src/components/ui/alert-dialog.tsx | 0 {frontend => apps/web}/src/components/ui/alert.tsx | 0 {frontend => apps/web}/src/components/ui/badge.tsx | 0 {frontend => apps/web}/src/components/ui/button.tsx | 0 {frontend => apps/web}/src/components/ui/card.tsx | 0 .../web}/src/components/ui/checkbox.tsx | 0 .../web}/src/components/ui/command.tsx | 0 {frontend => apps/web}/src/components/ui/dialog.tsx | 0 .../web}/src/components/ui/dropdown-menu.tsx | 0 {frontend => apps/web}/src/components/ui/input.tsx | 0 {frontend => apps/web}/src/components/ui/kbd.tsx | 0 {frontend => apps/web}/src/components/ui/label.tsx | 0 .../web}/src/components/ui/progress.tsx | 0 .../web}/src/components/ui/radio-group.tsx | 0 {frontend => apps/web}/src/components/ui/select.tsx | 0 .../web}/src/components/ui/separator.tsx | 0 {frontend => apps/web}/src/components/ui/sheet.tsx | 0 {frontend => apps/web}/src/components/ui/sonner.tsx | 0 {frontend => apps/web}/src/components/ui/switch.tsx | 0 {frontend => apps/web}/src/components/ui/table.tsx | 0 {frontend => apps/web}/src/components/ui/tabs.tsx | 0 .../web}/src/components/ui/textarea.tsx | 0 .../web}/src/components/ui/toggle-group.tsx | 0 .../web}/src/components/ui/tooltip.tsx | 0 .../web}/src/components/usageHistory.ts | 0 {frontend => apps/web}/src/hooks/queries.ts | 0 {frontend => apps/web}/src/hooks/useAppShell.tsx | 0 {frontend => apps/web}/src/hooks/useJob.tsx | 0 .../web}/src/hooks/useSelectedConfig.tsx | 0 .../web}/src/lib/__tests__/duplicates.test.ts | 0 .../web}/src/lib/__tests__/i18n.test.ts | 0 .../web}/src/lib/__tests__/job-tracker.test.ts | 0 .../web}/src/lib/__tests__/log-parse.test.ts | 0 .../web}/src/lib/__tests__/progress.test.ts | 0 .../web}/src/lib/__tests__/protected-files.test.ts | 0 .../web}/src/lib/__tests__/recovery.test.ts | 0 .../web}/src/lib/__tests__/run-result.test.ts | 0 .../web}/src/lib/__tests__/setup-wizard.test.ts | 0 .../web}/src/lib/__tests__/smart-attributes.test.ts | 0 {frontend => apps/web}/src/lib/api/auth.ts | 0 {frontend => apps/web}/src/lib/api/config.ts | 0 {frontend => apps/web}/src/lib/api/constants.ts | 0 {frontend => apps/web}/src/lib/api/engine.ts | 0 {frontend => apps/web}/src/lib/api/errors.ts | 0 {frontend => apps/web}/src/lib/api/filesystem.ts | 0 .../web}/src/lib/api/home-assistant.ts | 0 {frontend => apps/web}/src/lib/api/logs.ts | 0 {frontend => apps/web}/src/lib/api/maintenance.ts | 0 {frontend => apps/web}/src/lib/api/notifications.ts | 0 {frontend => apps/web}/src/lib/api/schedules.ts | 0 {frontend => apps/web}/src/lib/api/setup.ts | 0 {frontend => apps/web}/src/lib/api/snapraid.ts | 0 {frontend => apps/web}/src/lib/api/websocket.ts | 0 {frontend => apps/web}/src/lib/commands.ts | 0 {frontend => apps/web}/src/lib/duplicates.ts | 0 {frontend => apps/web}/src/lib/i18n.ts | 0 {frontend => apps/web}/src/lib/job-tracker.ts | 0 {frontend => apps/web}/src/lib/log-parse.ts | 0 {frontend => apps/web}/src/lib/nav.ts | 0 {frontend => apps/web}/src/lib/progress.ts | 0 {frontend => apps/web}/src/lib/protected-files.ts | 0 {frontend => apps/web}/src/lib/recovery.ts | 0 {frontend => apps/web}/src/lib/run-result.ts | 0 {frontend => apps/web}/src/lib/setup-wizard.ts | 0 {frontend => apps/web}/src/lib/smart-attributes.ts | 0 {frontend => apps/web}/src/lib/theme.ts | 0 {frontend => apps/web}/src/lib/utils.ts | 0 {frontend => apps/web}/src/lib/version.ts | 0 {frontend => apps/web}/src/routeTree.gen.ts | 0 {frontend => apps/web}/src/router.tsx | 0 {frontend => apps/web}/src/routes/__root.tsx | 0 {frontend => apps/web}/src/routes/array.tsx | 0 {frontend => apps/web}/src/routes/automation.tsx | 0 {frontend => apps/web}/src/routes/changes.tsx | 0 {frontend => apps/web}/src/routes/duplicates.tsx | 0 {frontend => apps/web}/src/routes/files.tsx | 0 {frontend => apps/web}/src/routes/index.tsx | 0 {frontend => apps/web}/src/routes/integrity.tsx | 0 {frontend => apps/web}/src/routes/logs.tsx | 0 {frontend => apps/web}/src/routes/notifications.tsx | 0 {frontend => apps/web}/src/routes/recovery.tsx | 0 {frontend => apps/web}/src/routes/schedules.tsx | 0 {frontend => apps/web}/src/routes/setup.tsx | 0 {frontend => apps/web}/src/routes/smart.tsx | 0 {frontend => apps/web}/src/server.ts | 0 {frontend => apps/web}/src/styles.css | 0 {frontend => apps/web}/tsconfig.json | 0 {frontend => apps/web}/vite.config.ts | 0 {shared => packages/shared/src}/array-setup.ts | 0 {shared => packages/shared/src}/force-option.ts | 0 {shared => packages/shared/src}/i18n.ts | 0 {shared => packages/shared/src}/run-result.ts | 0 {shared => packages/shared/src}/smart-attributes.ts | 0 {shared => packages/shared/src}/smart-health.ts | 0 {shared => packages/shared/src}/types.ts | 0 {shared => packages/shared/src}/usage-forecast.ts | 0 {dev => scripts/dev}/setup.sh | 0 start.sh => scripts/start.sh | 0 327 files changed, 0 insertions(+), 0 deletions(-) rename {backend => apps/server}/bun.lock (100%) rename {backend => apps/server}/package.json (100%) rename {backend => apps/server}/schedules.json (100%) rename {backend => apps/server}/scripts/seed-demo.ts (100%) rename {backend => apps/server}/src/__tests__/assert-error.ts (100%) rename {backend => apps/server}/src/__tests__/atomic-write.test.ts (100%) rename {backend => apps/server}/src/__tests__/auth.test.ts (100%) rename {backend => apps/server}/src/__tests__/backup.test.ts (100%) rename {backend => apps/server}/src/__tests__/config.test.ts (100%) rename {backend => apps/server}/src/__tests__/daemon-engine.test.ts (100%) rename {backend => apps/server}/src/__tests__/demo.test.ts (100%) rename {backend => apps/server}/src/__tests__/disk-check.test.ts (100%) rename {backend => apps/server}/src/__tests__/disk-management.test.ts (100%) rename {backend => apps/server}/src/__tests__/disk-power.test.ts (100%) rename {backend => apps/server}/src/__tests__/disk-removal.test.ts (100%) rename {backend => apps/server}/src/__tests__/disk-replacement.test.ts (100%) rename {backend => apps/server}/src/__tests__/docker.test.ts (100%) rename {backend => apps/server}/src/__tests__/duplicates.test.ts (100%) rename {backend => apps/server}/src/__tests__/engine-contract.test.ts (100%) rename {backend => apps/server}/src/__tests__/engine-contract.ts (100%) rename {backend => apps/server}/src/__tests__/engine-settings.test.ts (100%) rename {backend => apps/server}/src/__tests__/fake-engine.ts (100%) rename {backend => apps/server}/src/__tests__/force-option.test.ts (100%) rename {backend => apps/server}/src/__tests__/frontend.test.ts (100%) rename {backend => apps/server}/src/__tests__/heal.test.ts (100%) rename {backend => apps/server}/src/__tests__/home-assistant.test.ts (100%) rename {backend => apps/server}/src/__tests__/log-manager.test.ts (100%) rename {backend => apps/server}/src/__tests__/maintenance-settings.test.ts (100%) rename {backend => apps/server}/src/__tests__/mcp.test.ts (100%) rename {backend => apps/server}/src/__tests__/metrics.test.ts (100%) rename {backend => apps/server}/src/__tests__/notifications.test.ts (100%) rename {backend => apps/server}/src/__tests__/recovery.test.ts (100%) rename {backend => apps/server}/src/__tests__/restore-ownership.test.ts (100%) rename {backend => apps/server}/src/__tests__/run-result.test.ts (100%) rename {backend => apps/server}/src/__tests__/scheduler.test.ts (100%) rename {backend => apps/server}/src/__tests__/setup.test.ts (100%) rename {backend => apps/server}/src/__tests__/smart-baseline.test.ts (100%) rename {backend => apps/server}/src/__tests__/smart-history.test.ts (100%) rename {backend => apps/server}/src/__tests__/smart-ignore.test.ts (100%) rename {backend => apps/server}/src/__tests__/smart-selftest.test.ts (100%) rename {backend => apps/server}/src/__tests__/spindown.test.ts (100%) rename {backend => apps/server}/src/__tests__/usage-history.test.ts (100%) rename {backend => apps/server}/src/__tests__/version.test.ts (100%) rename {backend => apps/server}/src/app-files.ts (100%) rename {backend => apps/server}/src/array-snapshot.ts (100%) rename {backend => apps/server}/src/atomic-write.ts (100%) rename {backend => apps/server}/src/auth.ts (100%) rename {backend => apps/server}/src/backup.ts (100%) rename {backend => apps/server}/src/bin-cli.ts (100%) rename {backend => apps/server}/src/bin.ts (100%) rename {backend => apps/server}/src/command.ts (100%) rename {backend => apps/server}/src/config-parser.ts (100%) rename {backend => apps/server}/src/config.ts (100%) rename {backend => apps/server}/src/container-pause.ts (100%) rename {backend => apps/server}/src/credentials.ts (100%) rename {backend => apps/server}/src/demo.ts (100%) rename {backend => apps/server}/src/disk-check.ts (100%) rename {backend => apps/server}/src/disk-power.ts (100%) rename {backend => apps/server}/src/disk-removal.ts (100%) rename {backend => apps/server}/src/disk-replacement.ts (100%) rename {backend => apps/server}/src/docker.ts (100%) rename {backend => apps/server}/src/duplicates.ts (100%) rename {backend => apps/server}/src/engine/REVIEWED_DAEMON_VERSION (100%) rename {backend => apps/server}/src/engine/cli-engine.ts (100%) rename {backend => apps/server}/src/engine/daemon-engine.ts (100%) rename {backend => apps/server}/src/engine/engine-settings.ts (100%) rename {backend => apps/server}/src/engine/engine.ts (100%) rename {backend => apps/server}/src/executors/command-executor.ts (100%) rename {backend => apps/server}/src/executors/log-tail.ts (100%) rename {backend => apps/server}/src/frontend.ts (100%) rename {backend => apps/server}/src/fs.ts (100%) rename {backend => apps/server}/src/home-assistant-service.ts (100%) rename {backend => apps/server}/src/home-assistant.ts (100%) rename {backend => apps/server}/src/log-manager.ts (100%) rename {backend => apps/server}/src/main.ts (100%) rename {backend => apps/server}/src/maintenance-settings.ts (100%) rename {backend => apps/server}/src/mcp.ts (100%) rename {backend => apps/server}/src/metrics.ts (100%) rename {backend => apps/server}/src/notification-events.ts (100%) rename {backend => apps/server}/src/notifications.ts (100%) rename {backend => apps/server}/src/parity-usage.ts (100%) rename {backend => apps/server}/src/parsers/__tests__/fixtures/check-ok.log (100%) rename {backend => apps/server}/src/parsers/__tests__/fixtures/check.log (100%) rename {backend => apps/server}/src/parsers/__tests__/fixtures/diff-remove.log (100%) rename {backend => apps/server}/src/parsers/__tests__/fixtures/diff.log (100%) rename {backend => apps/server}/src/parsers/__tests__/fixtures/dup.log (100%) rename {backend => apps/server}/src/parsers/__tests__/fixtures/list.log (100%) rename {backend => apps/server}/src/parsers/__tests__/fixtures/probe.log (100%) rename {backend => apps/server}/src/parsers/__tests__/fixtures/smart.log (100%) rename {backend => apps/server}/src/parsers/__tests__/fixtures/status-bad.log (100%) rename {backend => apps/server}/src/parsers/__tests__/fixtures/status.log (100%) rename {backend => apps/server}/src/parsers/__tests__/fixtures/touch.log (100%) rename {backend => apps/server}/src/parsers/__tests__/parsers.test.ts (100%) rename {backend => apps/server}/src/parsers/__tests__/progress-parser.test.ts (100%) rename {backend => apps/server}/src/parsers/check-parser.ts (100%) rename {backend => apps/server}/src/parsers/devices-parser.ts (100%) rename {backend => apps/server}/src/parsers/df-parser.ts (100%) rename {backend => apps/server}/src/parsers/diff-parser.ts (100%) rename {backend => apps/server}/src/parsers/dup-parser.ts (100%) rename {backend => apps/server}/src/parsers/list-parser.ts (100%) rename {backend => apps/server}/src/parsers/probe-parser.ts (100%) rename {backend => apps/server}/src/parsers/progress-parser.ts (100%) rename {backend => apps/server}/src/parsers/smart-parser.ts (100%) rename {backend => apps/server}/src/parsers/status-parser.ts (100%) rename {backend => apps/server}/src/parsers/structured-log.ts (100%) rename {backend => apps/server}/src/parsers/utils.ts (100%) rename {backend => apps/server}/src/passkeys.ts (100%) rename {backend => apps/server}/src/recovery.ts (100%) rename {backend => apps/server}/src/restore-ownership.ts (100%) rename {backend => apps/server}/src/routes/config-operations.ts (100%) rename {backend => apps/server}/src/routes/config.ts (100%) rename {backend => apps/server}/src/routes/disk-management.ts (100%) rename {backend => apps/server}/src/routes/disk-replacement.ts (100%) rename {backend => apps/server}/src/routes/engine.ts (100%) rename {backend => apps/server}/src/routes/filesystem.ts (100%) rename {backend => apps/server}/src/routes/hardware.ts (100%) rename {backend => apps/server}/src/routes/home-assistant.ts (100%) rename {backend => apps/server}/src/routes/logs.ts (100%) rename {backend => apps/server}/src/routes/maintenance.ts (100%) rename {backend => apps/server}/src/routes/mcp.ts (100%) rename {backend => apps/server}/src/routes/metrics.ts (100%) rename {backend => apps/server}/src/routes/notifications.ts (100%) rename {backend => apps/server}/src/routes/reports.ts (100%) rename {backend => apps/server}/src/routes/schedules.ts (100%) rename {backend => apps/server}/src/routes/setup.ts (100%) rename {backend => apps/server}/src/routes/snapraid.ts (100%) rename {backend => apps/server}/src/run-report.ts (100%) rename {backend => apps/server}/src/scheduler.ts (100%) rename {backend => apps/server}/src/setup.ts (100%) rename {backend => apps/server}/src/smart-baseline.ts (100%) rename {backend => apps/server}/src/smart-history.ts (100%) rename {backend => apps/server}/src/smart-ignore.ts (100%) rename {backend => apps/server}/src/smart-selftest.ts (100%) rename {backend => apps/server}/src/snapraid-runner.ts (100%) rename {backend => apps/server}/src/spindown.ts (100%) rename {backend => apps/server}/src/status-cache.ts (100%) rename {backend => apps/server}/src/usage-history.ts (100%) rename {backend => apps/server}/src/version.ts (100%) rename {backend => apps/server}/src/websocket.ts (100%) rename {backend => apps/server}/tsconfig.json (100%) rename {frontend => apps/web}/.cta.json (100%) rename {frontend => apps/web}/.gitignore (100%) rename {frontend => apps/web}/README.md (100%) rename {frontend => apps/web}/biome.json (100%) rename {frontend => apps/web}/bun.lock (100%) rename {frontend => apps/web}/components.json (100%) rename {frontend => apps/web}/messages/de.json (100%) rename {frontend => apps/web}/messages/en.json (100%) rename {frontend => apps/web}/messages/it.json (100%) rename {frontend => apps/web}/package.json (100%) rename {frontend => apps/web}/project.inlang/project_id (100%) rename {frontend => apps/web}/project.inlang/settings.json (100%) rename {frontend => apps/web}/public/apple-touch-icon-180x180.png (100%) rename {frontend => apps/web}/public/favicon.ico (100%) rename {frontend => apps/web}/public/favicon.svg (100%) rename {frontend => apps/web}/public/maskable-icon-512x512.png (100%) rename {frontend => apps/web}/public/pwa-192x192.png (100%) rename {frontend => apps/web}/public/pwa-512x512.png (100%) rename {frontend => apps/web}/public/pwa-64x64.png (100%) rename {frontend => apps/web}/public/robots.txt (100%) rename {frontend => apps/web}/pwa-assets.config.ts (100%) rename {frontend => apps/web}/scripts/add-messages.py (100%) rename {frontend => apps/web}/scripts/release.sh (100%) rename {frontend => apps/web}/src/components/AccountDialog.tsx (100%) rename {frontend => apps/web}/src/components/AppSidebar.tsx (100%) rename {frontend => apps/web}/src/components/ArrayHealthPanel.tsx (100%) rename {frontend => apps/web}/src/components/AuthGate.tsx (100%) rename {frontend => apps/web}/src/components/BackupDialog.tsx (100%) rename {frontend => apps/web}/src/components/Button.tsx (100%) rename {frontend => apps/web}/src/components/CheckDialog.tsx (100%) rename {frontend => apps/web}/src/components/CheckViewer.tsx (100%) rename {frontend => apps/web}/src/components/CommandMenu.tsx (100%) rename {frontend => apps/web}/src/components/CommandPalette.tsx (100%) rename {frontend => apps/web}/src/components/ConfigAddForm.tsx (100%) rename {frontend => apps/web}/src/components/ConfigBar.tsx (100%) rename {frontend => apps/web}/src/components/ConfigEditor.tsx (100%) rename {frontend => apps/web}/src/components/ConfigList.tsx (100%) rename {frontend => apps/web}/src/components/ConfigListItem.tsx (100%) rename {frontend => apps/web}/src/components/ConfigManager.tsx (100%) rename {frontend => apps/web}/src/components/ConfigTextEditor.tsx (100%) rename {frontend => apps/web}/src/components/ContentFileSection.tsx (100%) rename {frontend => apps/web}/src/components/DataDiskSection.tsx (100%) rename {frontend => apps/web}/src/components/DirectoryBrowser.tsx (100%) rename {frontend => apps/web}/src/components/DiskManager.tsx (100%) rename {frontend => apps/web}/src/components/DisksPanel.tsx (100%) rename {frontend => apps/web}/src/components/ErrorAlert.tsx (100%) rename {frontend => apps/web}/src/components/ExcludePatternSection.tsx (100%) rename {frontend => apps/web}/src/components/Feedback.tsx (100%) rename {frontend => apps/web}/src/components/FileBrowser.tsx (100%) rename {frontend => apps/web}/src/components/ForceRetryBox.tsx (100%) rename {frontend => apps/web}/src/components/Header.tsx (100%) rename {frontend => apps/web}/src/components/IntegrityReport.tsx (100%) rename {frontend => apps/web}/src/components/LogFilters.tsx (100%) rename {frontend => apps/web}/src/components/LogList.tsx (100%) rename {frontend => apps/web}/src/components/LogListItem.tsx (100%) rename {frontend => apps/web}/src/components/LogRawView.tsx (100%) rename {frontend => apps/web}/src/components/LogViewer.tsx (100%) rename {frontend => apps/web}/src/components/LoginPage.tsx (100%) rename {frontend => apps/web}/src/components/MountSuggestions.tsx (100%) rename {frontend => apps/web}/src/components/OptionsSection.tsx (100%) rename {frontend => apps/web}/src/components/OutputConsole.tsx (100%) rename {frontend => apps/web}/src/components/PageLayout.tsx (100%) rename {frontend => apps/web}/src/components/ParityDiskSection.tsx (100%) rename {frontend => apps/web}/src/components/PoolSection.tsx (100%) rename {frontend => apps/web}/src/components/RemoveDataDiskWizard.tsx (100%) rename {frontend => apps/web}/src/components/ReplaceDiskWizard.tsx (100%) rename {frontend => apps/web}/src/components/RunningJobBox.tsx (100%) rename {frontend => apps/web}/src/components/SaveBar.tsx (100%) rename {frontend => apps/web}/src/components/ScrubDialog.tsx (100%) rename {frontend => apps/web}/src/components/ScrubPlanPicker.tsx (100%) rename {frontend => apps/web}/src/components/SegmentedControl.tsx (100%) rename {frontend => apps/web}/src/components/Select.tsx (100%) rename {frontend => apps/web}/src/components/Skeleton.tsx (100%) rename {frontend => apps/web}/src/components/SmartHistoryCharts.tsx (100%) rename {frontend => apps/web}/src/components/SmartMonitor.tsx (100%) rename {frontend => apps/web}/src/components/SmartSelfTest.tsx (100%) rename {frontend => apps/web}/src/components/SyncPreviewDialog.tsx (100%) rename {frontend => apps/web}/src/components/UsageHistoryChart.tsx (100%) rename {frontend => apps/web}/src/components/ValidationResultAlert.tsx (100%) rename {frontend => apps/web}/src/components/ViewModeToggle.tsx (100%) rename {frontend => apps/web}/src/components/__tests__/array-health.test.ts (100%) rename {frontend => apps/web}/src/components/__tests__/schedule-text.test.ts (100%) rename {frontend => apps/web}/src/components/__tests__/temperature-trend.test.ts (100%) rename {frontend => apps/web}/src/components/schedules/ScheduleForm.tsx (100%) rename {frontend => apps/web}/src/components/schedules/ScheduleRow.tsx (100%) rename {frontend => apps/web}/src/components/schedules/ScheduleWeek.tsx (100%) rename {frontend => apps/web}/src/components/schedules/scheduleText.ts (100%) rename {frontend => apps/web}/src/components/smartHistory.ts (100%) rename {frontend => apps/web}/src/components/temperatureTrend.ts (100%) rename {frontend => apps/web}/src/components/ui/alert-dialog.tsx (100%) rename {frontend => apps/web}/src/components/ui/alert.tsx (100%) rename {frontend => apps/web}/src/components/ui/badge.tsx (100%) rename {frontend => apps/web}/src/components/ui/button.tsx (100%) rename {frontend => apps/web}/src/components/ui/card.tsx (100%) rename {frontend => apps/web}/src/components/ui/checkbox.tsx (100%) rename {frontend => apps/web}/src/components/ui/command.tsx (100%) rename {frontend => apps/web}/src/components/ui/dialog.tsx (100%) rename {frontend => apps/web}/src/components/ui/dropdown-menu.tsx (100%) rename {frontend => apps/web}/src/components/ui/input.tsx (100%) rename {frontend => apps/web}/src/components/ui/kbd.tsx (100%) rename {frontend => apps/web}/src/components/ui/label.tsx (100%) rename {frontend => apps/web}/src/components/ui/progress.tsx (100%) rename {frontend => apps/web}/src/components/ui/radio-group.tsx (100%) rename {frontend => apps/web}/src/components/ui/select.tsx (100%) rename {frontend => apps/web}/src/components/ui/separator.tsx (100%) rename {frontend => apps/web}/src/components/ui/sheet.tsx (100%) rename {frontend => apps/web}/src/components/ui/sonner.tsx (100%) rename {frontend => apps/web}/src/components/ui/switch.tsx (100%) rename {frontend => apps/web}/src/components/ui/table.tsx (100%) rename {frontend => apps/web}/src/components/ui/tabs.tsx (100%) rename {frontend => apps/web}/src/components/ui/textarea.tsx (100%) rename {frontend => apps/web}/src/components/ui/toggle-group.tsx (100%) rename {frontend => apps/web}/src/components/ui/tooltip.tsx (100%) rename {frontend => apps/web}/src/components/usageHistory.ts (100%) rename {frontend => apps/web}/src/hooks/queries.ts (100%) rename {frontend => apps/web}/src/hooks/useAppShell.tsx (100%) rename {frontend => apps/web}/src/hooks/useJob.tsx (100%) rename {frontend => apps/web}/src/hooks/useSelectedConfig.tsx (100%) rename {frontend => apps/web}/src/lib/__tests__/duplicates.test.ts (100%) rename {frontend => apps/web}/src/lib/__tests__/i18n.test.ts (100%) rename {frontend => apps/web}/src/lib/__tests__/job-tracker.test.ts (100%) rename {frontend => apps/web}/src/lib/__tests__/log-parse.test.ts (100%) rename {frontend => apps/web}/src/lib/__tests__/progress.test.ts (100%) rename {frontend => apps/web}/src/lib/__tests__/protected-files.test.ts (100%) rename {frontend => apps/web}/src/lib/__tests__/recovery.test.ts (100%) rename {frontend => apps/web}/src/lib/__tests__/run-result.test.ts (100%) rename {frontend => apps/web}/src/lib/__tests__/setup-wizard.test.ts (100%) rename {frontend => apps/web}/src/lib/__tests__/smart-attributes.test.ts (100%) rename {frontend => apps/web}/src/lib/api/auth.ts (100%) rename {frontend => apps/web}/src/lib/api/config.ts (100%) rename {frontend => apps/web}/src/lib/api/constants.ts (100%) rename {frontend => apps/web}/src/lib/api/engine.ts (100%) rename {frontend => apps/web}/src/lib/api/errors.ts (100%) rename {frontend => apps/web}/src/lib/api/filesystem.ts (100%) rename {frontend => apps/web}/src/lib/api/home-assistant.ts (100%) rename {frontend => apps/web}/src/lib/api/logs.ts (100%) rename {frontend => apps/web}/src/lib/api/maintenance.ts (100%) rename {frontend => apps/web}/src/lib/api/notifications.ts (100%) rename {frontend => apps/web}/src/lib/api/schedules.ts (100%) rename {frontend => apps/web}/src/lib/api/setup.ts (100%) rename {frontend => apps/web}/src/lib/api/snapraid.ts (100%) rename {frontend => apps/web}/src/lib/api/websocket.ts (100%) rename {frontend => apps/web}/src/lib/commands.ts (100%) rename {frontend => apps/web}/src/lib/duplicates.ts (100%) rename {frontend => apps/web}/src/lib/i18n.ts (100%) rename {frontend => apps/web}/src/lib/job-tracker.ts (100%) rename {frontend => apps/web}/src/lib/log-parse.ts (100%) rename {frontend => apps/web}/src/lib/nav.ts (100%) rename {frontend => apps/web}/src/lib/progress.ts (100%) rename {frontend => apps/web}/src/lib/protected-files.ts (100%) rename {frontend => apps/web}/src/lib/recovery.ts (100%) rename {frontend => apps/web}/src/lib/run-result.ts (100%) rename {frontend => apps/web}/src/lib/setup-wizard.ts (100%) rename {frontend => apps/web}/src/lib/smart-attributes.ts (100%) rename {frontend => apps/web}/src/lib/theme.ts (100%) rename {frontend => apps/web}/src/lib/utils.ts (100%) rename {frontend => apps/web}/src/lib/version.ts (100%) rename {frontend => apps/web}/src/routeTree.gen.ts (100%) rename {frontend => apps/web}/src/router.tsx (100%) rename {frontend => apps/web}/src/routes/__root.tsx (100%) rename {frontend => apps/web}/src/routes/array.tsx (100%) rename {frontend => apps/web}/src/routes/automation.tsx (100%) rename {frontend => apps/web}/src/routes/changes.tsx (100%) rename {frontend => apps/web}/src/routes/duplicates.tsx (100%) rename {frontend => apps/web}/src/routes/files.tsx (100%) rename {frontend => apps/web}/src/routes/index.tsx (100%) rename {frontend => apps/web}/src/routes/integrity.tsx (100%) rename {frontend => apps/web}/src/routes/logs.tsx (100%) rename {frontend => apps/web}/src/routes/notifications.tsx (100%) rename {frontend => apps/web}/src/routes/recovery.tsx (100%) rename {frontend => apps/web}/src/routes/schedules.tsx (100%) rename {frontend => apps/web}/src/routes/setup.tsx (100%) rename {frontend => apps/web}/src/routes/smart.tsx (100%) rename {frontend => apps/web}/src/server.ts (100%) rename {frontend => apps/web}/src/styles.css (100%) rename {frontend => apps/web}/tsconfig.json (100%) rename {frontend => apps/web}/vite.config.ts (100%) rename {shared => packages/shared/src}/array-setup.ts (100%) rename {shared => packages/shared/src}/force-option.ts (100%) rename {shared => packages/shared/src}/i18n.ts (100%) rename {shared => packages/shared/src}/run-result.ts (100%) rename {shared => packages/shared/src}/smart-attributes.ts (100%) rename {shared => packages/shared/src}/smart-health.ts (100%) rename {shared => packages/shared/src}/types.ts (100%) rename {shared => packages/shared/src}/usage-forecast.ts (100%) rename {dev => scripts/dev}/setup.sh (100%) rename start.sh => scripts/start.sh (100%) diff --git a/backend/bun.lock b/apps/server/bun.lock similarity index 100% rename from backend/bun.lock rename to apps/server/bun.lock diff --git a/backend/package.json b/apps/server/package.json similarity index 100% rename from backend/package.json rename to apps/server/package.json diff --git a/backend/schedules.json b/apps/server/schedules.json similarity index 100% rename from backend/schedules.json rename to apps/server/schedules.json diff --git a/backend/scripts/seed-demo.ts b/apps/server/scripts/seed-demo.ts similarity index 100% rename from backend/scripts/seed-demo.ts rename to apps/server/scripts/seed-demo.ts diff --git a/backend/src/__tests__/assert-error.ts b/apps/server/src/__tests__/assert-error.ts similarity index 100% rename from backend/src/__tests__/assert-error.ts rename to apps/server/src/__tests__/assert-error.ts diff --git a/backend/src/__tests__/atomic-write.test.ts b/apps/server/src/__tests__/atomic-write.test.ts similarity index 100% rename from backend/src/__tests__/atomic-write.test.ts rename to apps/server/src/__tests__/atomic-write.test.ts diff --git a/backend/src/__tests__/auth.test.ts b/apps/server/src/__tests__/auth.test.ts similarity index 100% rename from backend/src/__tests__/auth.test.ts rename to apps/server/src/__tests__/auth.test.ts diff --git a/backend/src/__tests__/backup.test.ts b/apps/server/src/__tests__/backup.test.ts similarity index 100% rename from backend/src/__tests__/backup.test.ts rename to apps/server/src/__tests__/backup.test.ts diff --git a/backend/src/__tests__/config.test.ts b/apps/server/src/__tests__/config.test.ts similarity index 100% rename from backend/src/__tests__/config.test.ts rename to apps/server/src/__tests__/config.test.ts diff --git a/backend/src/__tests__/daemon-engine.test.ts b/apps/server/src/__tests__/daemon-engine.test.ts similarity index 100% rename from backend/src/__tests__/daemon-engine.test.ts rename to apps/server/src/__tests__/daemon-engine.test.ts diff --git a/backend/src/__tests__/demo.test.ts b/apps/server/src/__tests__/demo.test.ts similarity index 100% rename from backend/src/__tests__/demo.test.ts rename to apps/server/src/__tests__/demo.test.ts diff --git a/backend/src/__tests__/disk-check.test.ts b/apps/server/src/__tests__/disk-check.test.ts similarity index 100% rename from backend/src/__tests__/disk-check.test.ts rename to apps/server/src/__tests__/disk-check.test.ts diff --git a/backend/src/__tests__/disk-management.test.ts b/apps/server/src/__tests__/disk-management.test.ts similarity index 100% rename from backend/src/__tests__/disk-management.test.ts rename to apps/server/src/__tests__/disk-management.test.ts diff --git a/backend/src/__tests__/disk-power.test.ts b/apps/server/src/__tests__/disk-power.test.ts similarity index 100% rename from backend/src/__tests__/disk-power.test.ts rename to apps/server/src/__tests__/disk-power.test.ts diff --git a/backend/src/__tests__/disk-removal.test.ts b/apps/server/src/__tests__/disk-removal.test.ts similarity index 100% rename from backend/src/__tests__/disk-removal.test.ts rename to apps/server/src/__tests__/disk-removal.test.ts diff --git a/backend/src/__tests__/disk-replacement.test.ts b/apps/server/src/__tests__/disk-replacement.test.ts similarity index 100% rename from backend/src/__tests__/disk-replacement.test.ts rename to apps/server/src/__tests__/disk-replacement.test.ts diff --git a/backend/src/__tests__/docker.test.ts b/apps/server/src/__tests__/docker.test.ts similarity index 100% rename from backend/src/__tests__/docker.test.ts rename to apps/server/src/__tests__/docker.test.ts diff --git a/backend/src/__tests__/duplicates.test.ts b/apps/server/src/__tests__/duplicates.test.ts similarity index 100% rename from backend/src/__tests__/duplicates.test.ts rename to apps/server/src/__tests__/duplicates.test.ts diff --git a/backend/src/__tests__/engine-contract.test.ts b/apps/server/src/__tests__/engine-contract.test.ts similarity index 100% rename from backend/src/__tests__/engine-contract.test.ts rename to apps/server/src/__tests__/engine-contract.test.ts diff --git a/backend/src/__tests__/engine-contract.ts b/apps/server/src/__tests__/engine-contract.ts similarity index 100% rename from backend/src/__tests__/engine-contract.ts rename to apps/server/src/__tests__/engine-contract.ts diff --git a/backend/src/__tests__/engine-settings.test.ts b/apps/server/src/__tests__/engine-settings.test.ts similarity index 100% rename from backend/src/__tests__/engine-settings.test.ts rename to apps/server/src/__tests__/engine-settings.test.ts diff --git a/backend/src/__tests__/fake-engine.ts b/apps/server/src/__tests__/fake-engine.ts similarity index 100% rename from backend/src/__tests__/fake-engine.ts rename to apps/server/src/__tests__/fake-engine.ts diff --git a/backend/src/__tests__/force-option.test.ts b/apps/server/src/__tests__/force-option.test.ts similarity index 100% rename from backend/src/__tests__/force-option.test.ts rename to apps/server/src/__tests__/force-option.test.ts diff --git a/backend/src/__tests__/frontend.test.ts b/apps/server/src/__tests__/frontend.test.ts similarity index 100% rename from backend/src/__tests__/frontend.test.ts rename to apps/server/src/__tests__/frontend.test.ts diff --git a/backend/src/__tests__/heal.test.ts b/apps/server/src/__tests__/heal.test.ts similarity index 100% rename from backend/src/__tests__/heal.test.ts rename to apps/server/src/__tests__/heal.test.ts diff --git a/backend/src/__tests__/home-assistant.test.ts b/apps/server/src/__tests__/home-assistant.test.ts similarity index 100% rename from backend/src/__tests__/home-assistant.test.ts rename to apps/server/src/__tests__/home-assistant.test.ts diff --git a/backend/src/__tests__/log-manager.test.ts b/apps/server/src/__tests__/log-manager.test.ts similarity index 100% rename from backend/src/__tests__/log-manager.test.ts rename to apps/server/src/__tests__/log-manager.test.ts diff --git a/backend/src/__tests__/maintenance-settings.test.ts b/apps/server/src/__tests__/maintenance-settings.test.ts similarity index 100% rename from backend/src/__tests__/maintenance-settings.test.ts rename to apps/server/src/__tests__/maintenance-settings.test.ts diff --git a/backend/src/__tests__/mcp.test.ts b/apps/server/src/__tests__/mcp.test.ts similarity index 100% rename from backend/src/__tests__/mcp.test.ts rename to apps/server/src/__tests__/mcp.test.ts diff --git a/backend/src/__tests__/metrics.test.ts b/apps/server/src/__tests__/metrics.test.ts similarity index 100% rename from backend/src/__tests__/metrics.test.ts rename to apps/server/src/__tests__/metrics.test.ts diff --git a/backend/src/__tests__/notifications.test.ts b/apps/server/src/__tests__/notifications.test.ts similarity index 100% rename from backend/src/__tests__/notifications.test.ts rename to apps/server/src/__tests__/notifications.test.ts diff --git a/backend/src/__tests__/recovery.test.ts b/apps/server/src/__tests__/recovery.test.ts similarity index 100% rename from backend/src/__tests__/recovery.test.ts rename to apps/server/src/__tests__/recovery.test.ts diff --git a/backend/src/__tests__/restore-ownership.test.ts b/apps/server/src/__tests__/restore-ownership.test.ts similarity index 100% rename from backend/src/__tests__/restore-ownership.test.ts rename to apps/server/src/__tests__/restore-ownership.test.ts diff --git a/backend/src/__tests__/run-result.test.ts b/apps/server/src/__tests__/run-result.test.ts similarity index 100% rename from backend/src/__tests__/run-result.test.ts rename to apps/server/src/__tests__/run-result.test.ts diff --git a/backend/src/__tests__/scheduler.test.ts b/apps/server/src/__tests__/scheduler.test.ts similarity index 100% rename from backend/src/__tests__/scheduler.test.ts rename to apps/server/src/__tests__/scheduler.test.ts diff --git a/backend/src/__tests__/setup.test.ts b/apps/server/src/__tests__/setup.test.ts similarity index 100% rename from backend/src/__tests__/setup.test.ts rename to apps/server/src/__tests__/setup.test.ts diff --git a/backend/src/__tests__/smart-baseline.test.ts b/apps/server/src/__tests__/smart-baseline.test.ts similarity index 100% rename from backend/src/__tests__/smart-baseline.test.ts rename to apps/server/src/__tests__/smart-baseline.test.ts diff --git a/backend/src/__tests__/smart-history.test.ts b/apps/server/src/__tests__/smart-history.test.ts similarity index 100% rename from backend/src/__tests__/smart-history.test.ts rename to apps/server/src/__tests__/smart-history.test.ts diff --git a/backend/src/__tests__/smart-ignore.test.ts b/apps/server/src/__tests__/smart-ignore.test.ts similarity index 100% rename from backend/src/__tests__/smart-ignore.test.ts rename to apps/server/src/__tests__/smart-ignore.test.ts diff --git a/backend/src/__tests__/smart-selftest.test.ts b/apps/server/src/__tests__/smart-selftest.test.ts similarity index 100% rename from backend/src/__tests__/smart-selftest.test.ts rename to apps/server/src/__tests__/smart-selftest.test.ts diff --git a/backend/src/__tests__/spindown.test.ts b/apps/server/src/__tests__/spindown.test.ts similarity index 100% rename from backend/src/__tests__/spindown.test.ts rename to apps/server/src/__tests__/spindown.test.ts diff --git a/backend/src/__tests__/usage-history.test.ts b/apps/server/src/__tests__/usage-history.test.ts similarity index 100% rename from backend/src/__tests__/usage-history.test.ts rename to apps/server/src/__tests__/usage-history.test.ts diff --git a/backend/src/__tests__/version.test.ts b/apps/server/src/__tests__/version.test.ts similarity index 100% rename from backend/src/__tests__/version.test.ts rename to apps/server/src/__tests__/version.test.ts diff --git a/backend/src/app-files.ts b/apps/server/src/app-files.ts similarity index 100% rename from backend/src/app-files.ts rename to apps/server/src/app-files.ts diff --git a/backend/src/array-snapshot.ts b/apps/server/src/array-snapshot.ts similarity index 100% rename from backend/src/array-snapshot.ts rename to apps/server/src/array-snapshot.ts diff --git a/backend/src/atomic-write.ts b/apps/server/src/atomic-write.ts similarity index 100% rename from backend/src/atomic-write.ts rename to apps/server/src/atomic-write.ts diff --git a/backend/src/auth.ts b/apps/server/src/auth.ts similarity index 100% rename from backend/src/auth.ts rename to apps/server/src/auth.ts diff --git a/backend/src/backup.ts b/apps/server/src/backup.ts similarity index 100% rename from backend/src/backup.ts rename to apps/server/src/backup.ts diff --git a/backend/src/bin-cli.ts b/apps/server/src/bin-cli.ts similarity index 100% rename from backend/src/bin-cli.ts rename to apps/server/src/bin-cli.ts diff --git a/backend/src/bin.ts b/apps/server/src/bin.ts similarity index 100% rename from backend/src/bin.ts rename to apps/server/src/bin.ts diff --git a/backend/src/command.ts b/apps/server/src/command.ts similarity index 100% rename from backend/src/command.ts rename to apps/server/src/command.ts diff --git a/backend/src/config-parser.ts b/apps/server/src/config-parser.ts similarity index 100% rename from backend/src/config-parser.ts rename to apps/server/src/config-parser.ts diff --git a/backend/src/config.ts b/apps/server/src/config.ts similarity index 100% rename from backend/src/config.ts rename to apps/server/src/config.ts diff --git a/backend/src/container-pause.ts b/apps/server/src/container-pause.ts similarity index 100% rename from backend/src/container-pause.ts rename to apps/server/src/container-pause.ts diff --git a/backend/src/credentials.ts b/apps/server/src/credentials.ts similarity index 100% rename from backend/src/credentials.ts rename to apps/server/src/credentials.ts diff --git a/backend/src/demo.ts b/apps/server/src/demo.ts similarity index 100% rename from backend/src/demo.ts rename to apps/server/src/demo.ts diff --git a/backend/src/disk-check.ts b/apps/server/src/disk-check.ts similarity index 100% rename from backend/src/disk-check.ts rename to apps/server/src/disk-check.ts diff --git a/backend/src/disk-power.ts b/apps/server/src/disk-power.ts similarity index 100% rename from backend/src/disk-power.ts rename to apps/server/src/disk-power.ts diff --git a/backend/src/disk-removal.ts b/apps/server/src/disk-removal.ts similarity index 100% rename from backend/src/disk-removal.ts rename to apps/server/src/disk-removal.ts diff --git a/backend/src/disk-replacement.ts b/apps/server/src/disk-replacement.ts similarity index 100% rename from backend/src/disk-replacement.ts rename to apps/server/src/disk-replacement.ts diff --git a/backend/src/docker.ts b/apps/server/src/docker.ts similarity index 100% rename from backend/src/docker.ts rename to apps/server/src/docker.ts diff --git a/backend/src/duplicates.ts b/apps/server/src/duplicates.ts similarity index 100% rename from backend/src/duplicates.ts rename to apps/server/src/duplicates.ts diff --git a/backend/src/engine/REVIEWED_DAEMON_VERSION b/apps/server/src/engine/REVIEWED_DAEMON_VERSION similarity index 100% rename from backend/src/engine/REVIEWED_DAEMON_VERSION rename to apps/server/src/engine/REVIEWED_DAEMON_VERSION diff --git a/backend/src/engine/cli-engine.ts b/apps/server/src/engine/cli-engine.ts similarity index 100% rename from backend/src/engine/cli-engine.ts rename to apps/server/src/engine/cli-engine.ts diff --git a/backend/src/engine/daemon-engine.ts b/apps/server/src/engine/daemon-engine.ts similarity index 100% rename from backend/src/engine/daemon-engine.ts rename to apps/server/src/engine/daemon-engine.ts diff --git a/backend/src/engine/engine-settings.ts b/apps/server/src/engine/engine-settings.ts similarity index 100% rename from backend/src/engine/engine-settings.ts rename to apps/server/src/engine/engine-settings.ts diff --git a/backend/src/engine/engine.ts b/apps/server/src/engine/engine.ts similarity index 100% rename from backend/src/engine/engine.ts rename to apps/server/src/engine/engine.ts diff --git a/backend/src/executors/command-executor.ts b/apps/server/src/executors/command-executor.ts similarity index 100% rename from backend/src/executors/command-executor.ts rename to apps/server/src/executors/command-executor.ts diff --git a/backend/src/executors/log-tail.ts b/apps/server/src/executors/log-tail.ts similarity index 100% rename from backend/src/executors/log-tail.ts rename to apps/server/src/executors/log-tail.ts diff --git a/backend/src/frontend.ts b/apps/server/src/frontend.ts similarity index 100% rename from backend/src/frontend.ts rename to apps/server/src/frontend.ts diff --git a/backend/src/fs.ts b/apps/server/src/fs.ts similarity index 100% rename from backend/src/fs.ts rename to apps/server/src/fs.ts diff --git a/backend/src/home-assistant-service.ts b/apps/server/src/home-assistant-service.ts similarity index 100% rename from backend/src/home-assistant-service.ts rename to apps/server/src/home-assistant-service.ts diff --git a/backend/src/home-assistant.ts b/apps/server/src/home-assistant.ts similarity index 100% rename from backend/src/home-assistant.ts rename to apps/server/src/home-assistant.ts diff --git a/backend/src/log-manager.ts b/apps/server/src/log-manager.ts similarity index 100% rename from backend/src/log-manager.ts rename to apps/server/src/log-manager.ts diff --git a/backend/src/main.ts b/apps/server/src/main.ts similarity index 100% rename from backend/src/main.ts rename to apps/server/src/main.ts diff --git a/backend/src/maintenance-settings.ts b/apps/server/src/maintenance-settings.ts similarity index 100% rename from backend/src/maintenance-settings.ts rename to apps/server/src/maintenance-settings.ts diff --git a/backend/src/mcp.ts b/apps/server/src/mcp.ts similarity index 100% rename from backend/src/mcp.ts rename to apps/server/src/mcp.ts diff --git a/backend/src/metrics.ts b/apps/server/src/metrics.ts similarity index 100% rename from backend/src/metrics.ts rename to apps/server/src/metrics.ts diff --git a/backend/src/notification-events.ts b/apps/server/src/notification-events.ts similarity index 100% rename from backend/src/notification-events.ts rename to apps/server/src/notification-events.ts diff --git a/backend/src/notifications.ts b/apps/server/src/notifications.ts similarity index 100% rename from backend/src/notifications.ts rename to apps/server/src/notifications.ts diff --git a/backend/src/parity-usage.ts b/apps/server/src/parity-usage.ts similarity index 100% rename from backend/src/parity-usage.ts rename to apps/server/src/parity-usage.ts diff --git a/backend/src/parsers/__tests__/fixtures/check-ok.log b/apps/server/src/parsers/__tests__/fixtures/check-ok.log similarity index 100% rename from backend/src/parsers/__tests__/fixtures/check-ok.log rename to apps/server/src/parsers/__tests__/fixtures/check-ok.log diff --git a/backend/src/parsers/__tests__/fixtures/check.log b/apps/server/src/parsers/__tests__/fixtures/check.log similarity index 100% rename from backend/src/parsers/__tests__/fixtures/check.log rename to apps/server/src/parsers/__tests__/fixtures/check.log diff --git a/backend/src/parsers/__tests__/fixtures/diff-remove.log b/apps/server/src/parsers/__tests__/fixtures/diff-remove.log similarity index 100% rename from backend/src/parsers/__tests__/fixtures/diff-remove.log rename to apps/server/src/parsers/__tests__/fixtures/diff-remove.log diff --git a/backend/src/parsers/__tests__/fixtures/diff.log b/apps/server/src/parsers/__tests__/fixtures/diff.log similarity index 100% rename from backend/src/parsers/__tests__/fixtures/diff.log rename to apps/server/src/parsers/__tests__/fixtures/diff.log diff --git a/backend/src/parsers/__tests__/fixtures/dup.log b/apps/server/src/parsers/__tests__/fixtures/dup.log similarity index 100% rename from backend/src/parsers/__tests__/fixtures/dup.log rename to apps/server/src/parsers/__tests__/fixtures/dup.log diff --git a/backend/src/parsers/__tests__/fixtures/list.log b/apps/server/src/parsers/__tests__/fixtures/list.log similarity index 100% rename from backend/src/parsers/__tests__/fixtures/list.log rename to apps/server/src/parsers/__tests__/fixtures/list.log diff --git a/backend/src/parsers/__tests__/fixtures/probe.log b/apps/server/src/parsers/__tests__/fixtures/probe.log similarity index 100% rename from backend/src/parsers/__tests__/fixtures/probe.log rename to apps/server/src/parsers/__tests__/fixtures/probe.log diff --git a/backend/src/parsers/__tests__/fixtures/smart.log b/apps/server/src/parsers/__tests__/fixtures/smart.log similarity index 100% rename from backend/src/parsers/__tests__/fixtures/smart.log rename to apps/server/src/parsers/__tests__/fixtures/smart.log diff --git a/backend/src/parsers/__tests__/fixtures/status-bad.log b/apps/server/src/parsers/__tests__/fixtures/status-bad.log similarity index 100% rename from backend/src/parsers/__tests__/fixtures/status-bad.log rename to apps/server/src/parsers/__tests__/fixtures/status-bad.log diff --git a/backend/src/parsers/__tests__/fixtures/status.log b/apps/server/src/parsers/__tests__/fixtures/status.log similarity index 100% rename from backend/src/parsers/__tests__/fixtures/status.log rename to apps/server/src/parsers/__tests__/fixtures/status.log diff --git a/backend/src/parsers/__tests__/fixtures/touch.log b/apps/server/src/parsers/__tests__/fixtures/touch.log similarity index 100% rename from backend/src/parsers/__tests__/fixtures/touch.log rename to apps/server/src/parsers/__tests__/fixtures/touch.log diff --git a/backend/src/parsers/__tests__/parsers.test.ts b/apps/server/src/parsers/__tests__/parsers.test.ts similarity index 100% rename from backend/src/parsers/__tests__/parsers.test.ts rename to apps/server/src/parsers/__tests__/parsers.test.ts diff --git a/backend/src/parsers/__tests__/progress-parser.test.ts b/apps/server/src/parsers/__tests__/progress-parser.test.ts similarity index 100% rename from backend/src/parsers/__tests__/progress-parser.test.ts rename to apps/server/src/parsers/__tests__/progress-parser.test.ts diff --git a/backend/src/parsers/check-parser.ts b/apps/server/src/parsers/check-parser.ts similarity index 100% rename from backend/src/parsers/check-parser.ts rename to apps/server/src/parsers/check-parser.ts diff --git a/backend/src/parsers/devices-parser.ts b/apps/server/src/parsers/devices-parser.ts similarity index 100% rename from backend/src/parsers/devices-parser.ts rename to apps/server/src/parsers/devices-parser.ts diff --git a/backend/src/parsers/df-parser.ts b/apps/server/src/parsers/df-parser.ts similarity index 100% rename from backend/src/parsers/df-parser.ts rename to apps/server/src/parsers/df-parser.ts diff --git a/backend/src/parsers/diff-parser.ts b/apps/server/src/parsers/diff-parser.ts similarity index 100% rename from backend/src/parsers/diff-parser.ts rename to apps/server/src/parsers/diff-parser.ts diff --git a/backend/src/parsers/dup-parser.ts b/apps/server/src/parsers/dup-parser.ts similarity index 100% rename from backend/src/parsers/dup-parser.ts rename to apps/server/src/parsers/dup-parser.ts diff --git a/backend/src/parsers/list-parser.ts b/apps/server/src/parsers/list-parser.ts similarity index 100% rename from backend/src/parsers/list-parser.ts rename to apps/server/src/parsers/list-parser.ts diff --git a/backend/src/parsers/probe-parser.ts b/apps/server/src/parsers/probe-parser.ts similarity index 100% rename from backend/src/parsers/probe-parser.ts rename to apps/server/src/parsers/probe-parser.ts diff --git a/backend/src/parsers/progress-parser.ts b/apps/server/src/parsers/progress-parser.ts similarity index 100% rename from backend/src/parsers/progress-parser.ts rename to apps/server/src/parsers/progress-parser.ts diff --git a/backend/src/parsers/smart-parser.ts b/apps/server/src/parsers/smart-parser.ts similarity index 100% rename from backend/src/parsers/smart-parser.ts rename to apps/server/src/parsers/smart-parser.ts diff --git a/backend/src/parsers/status-parser.ts b/apps/server/src/parsers/status-parser.ts similarity index 100% rename from backend/src/parsers/status-parser.ts rename to apps/server/src/parsers/status-parser.ts diff --git a/backend/src/parsers/structured-log.ts b/apps/server/src/parsers/structured-log.ts similarity index 100% rename from backend/src/parsers/structured-log.ts rename to apps/server/src/parsers/structured-log.ts diff --git a/backend/src/parsers/utils.ts b/apps/server/src/parsers/utils.ts similarity index 100% rename from backend/src/parsers/utils.ts rename to apps/server/src/parsers/utils.ts diff --git a/backend/src/passkeys.ts b/apps/server/src/passkeys.ts similarity index 100% rename from backend/src/passkeys.ts rename to apps/server/src/passkeys.ts diff --git a/backend/src/recovery.ts b/apps/server/src/recovery.ts similarity index 100% rename from backend/src/recovery.ts rename to apps/server/src/recovery.ts diff --git a/backend/src/restore-ownership.ts b/apps/server/src/restore-ownership.ts similarity index 100% rename from backend/src/restore-ownership.ts rename to apps/server/src/restore-ownership.ts diff --git a/backend/src/routes/config-operations.ts b/apps/server/src/routes/config-operations.ts similarity index 100% rename from backend/src/routes/config-operations.ts rename to apps/server/src/routes/config-operations.ts diff --git a/backend/src/routes/config.ts b/apps/server/src/routes/config.ts similarity index 100% rename from backend/src/routes/config.ts rename to apps/server/src/routes/config.ts diff --git a/backend/src/routes/disk-management.ts b/apps/server/src/routes/disk-management.ts similarity index 100% rename from backend/src/routes/disk-management.ts rename to apps/server/src/routes/disk-management.ts diff --git a/backend/src/routes/disk-replacement.ts b/apps/server/src/routes/disk-replacement.ts similarity index 100% rename from backend/src/routes/disk-replacement.ts rename to apps/server/src/routes/disk-replacement.ts diff --git a/backend/src/routes/engine.ts b/apps/server/src/routes/engine.ts similarity index 100% rename from backend/src/routes/engine.ts rename to apps/server/src/routes/engine.ts diff --git a/backend/src/routes/filesystem.ts b/apps/server/src/routes/filesystem.ts similarity index 100% rename from backend/src/routes/filesystem.ts rename to apps/server/src/routes/filesystem.ts diff --git a/backend/src/routes/hardware.ts b/apps/server/src/routes/hardware.ts similarity index 100% rename from backend/src/routes/hardware.ts rename to apps/server/src/routes/hardware.ts diff --git a/backend/src/routes/home-assistant.ts b/apps/server/src/routes/home-assistant.ts similarity index 100% rename from backend/src/routes/home-assistant.ts rename to apps/server/src/routes/home-assistant.ts diff --git a/backend/src/routes/logs.ts b/apps/server/src/routes/logs.ts similarity index 100% rename from backend/src/routes/logs.ts rename to apps/server/src/routes/logs.ts diff --git a/backend/src/routes/maintenance.ts b/apps/server/src/routes/maintenance.ts similarity index 100% rename from backend/src/routes/maintenance.ts rename to apps/server/src/routes/maintenance.ts diff --git a/backend/src/routes/mcp.ts b/apps/server/src/routes/mcp.ts similarity index 100% rename from backend/src/routes/mcp.ts rename to apps/server/src/routes/mcp.ts diff --git a/backend/src/routes/metrics.ts b/apps/server/src/routes/metrics.ts similarity index 100% rename from backend/src/routes/metrics.ts rename to apps/server/src/routes/metrics.ts diff --git a/backend/src/routes/notifications.ts b/apps/server/src/routes/notifications.ts similarity index 100% rename from backend/src/routes/notifications.ts rename to apps/server/src/routes/notifications.ts diff --git a/backend/src/routes/reports.ts b/apps/server/src/routes/reports.ts similarity index 100% rename from backend/src/routes/reports.ts rename to apps/server/src/routes/reports.ts diff --git a/backend/src/routes/schedules.ts b/apps/server/src/routes/schedules.ts similarity index 100% rename from backend/src/routes/schedules.ts rename to apps/server/src/routes/schedules.ts diff --git a/backend/src/routes/setup.ts b/apps/server/src/routes/setup.ts similarity index 100% rename from backend/src/routes/setup.ts rename to apps/server/src/routes/setup.ts diff --git a/backend/src/routes/snapraid.ts b/apps/server/src/routes/snapraid.ts similarity index 100% rename from backend/src/routes/snapraid.ts rename to apps/server/src/routes/snapraid.ts diff --git a/backend/src/run-report.ts b/apps/server/src/run-report.ts similarity index 100% rename from backend/src/run-report.ts rename to apps/server/src/run-report.ts diff --git a/backend/src/scheduler.ts b/apps/server/src/scheduler.ts similarity index 100% rename from backend/src/scheduler.ts rename to apps/server/src/scheduler.ts diff --git a/backend/src/setup.ts b/apps/server/src/setup.ts similarity index 100% rename from backend/src/setup.ts rename to apps/server/src/setup.ts diff --git a/backend/src/smart-baseline.ts b/apps/server/src/smart-baseline.ts similarity index 100% rename from backend/src/smart-baseline.ts rename to apps/server/src/smart-baseline.ts diff --git a/backend/src/smart-history.ts b/apps/server/src/smart-history.ts similarity index 100% rename from backend/src/smart-history.ts rename to apps/server/src/smart-history.ts diff --git a/backend/src/smart-ignore.ts b/apps/server/src/smart-ignore.ts similarity index 100% rename from backend/src/smart-ignore.ts rename to apps/server/src/smart-ignore.ts diff --git a/backend/src/smart-selftest.ts b/apps/server/src/smart-selftest.ts similarity index 100% rename from backend/src/smart-selftest.ts rename to apps/server/src/smart-selftest.ts diff --git a/backend/src/snapraid-runner.ts b/apps/server/src/snapraid-runner.ts similarity index 100% rename from backend/src/snapraid-runner.ts rename to apps/server/src/snapraid-runner.ts diff --git a/backend/src/spindown.ts b/apps/server/src/spindown.ts similarity index 100% rename from backend/src/spindown.ts rename to apps/server/src/spindown.ts diff --git a/backend/src/status-cache.ts b/apps/server/src/status-cache.ts similarity index 100% rename from backend/src/status-cache.ts rename to apps/server/src/status-cache.ts diff --git a/backend/src/usage-history.ts b/apps/server/src/usage-history.ts similarity index 100% rename from backend/src/usage-history.ts rename to apps/server/src/usage-history.ts diff --git a/backend/src/version.ts b/apps/server/src/version.ts similarity index 100% rename from backend/src/version.ts rename to apps/server/src/version.ts diff --git a/backend/src/websocket.ts b/apps/server/src/websocket.ts similarity index 100% rename from backend/src/websocket.ts rename to apps/server/src/websocket.ts diff --git a/backend/tsconfig.json b/apps/server/tsconfig.json similarity index 100% rename from backend/tsconfig.json rename to apps/server/tsconfig.json diff --git a/frontend/.cta.json b/apps/web/.cta.json similarity index 100% rename from frontend/.cta.json rename to apps/web/.cta.json diff --git a/frontend/.gitignore b/apps/web/.gitignore similarity index 100% rename from frontend/.gitignore rename to apps/web/.gitignore diff --git a/frontend/README.md b/apps/web/README.md similarity index 100% rename from frontend/README.md rename to apps/web/README.md diff --git a/frontend/biome.json b/apps/web/biome.json similarity index 100% rename from frontend/biome.json rename to apps/web/biome.json diff --git a/frontend/bun.lock b/apps/web/bun.lock similarity index 100% rename from frontend/bun.lock rename to apps/web/bun.lock diff --git a/frontend/components.json b/apps/web/components.json similarity index 100% rename from frontend/components.json rename to apps/web/components.json diff --git a/frontend/messages/de.json b/apps/web/messages/de.json similarity index 100% rename from frontend/messages/de.json rename to apps/web/messages/de.json diff --git a/frontend/messages/en.json b/apps/web/messages/en.json similarity index 100% rename from frontend/messages/en.json rename to apps/web/messages/en.json diff --git a/frontend/messages/it.json b/apps/web/messages/it.json similarity index 100% rename from frontend/messages/it.json rename to apps/web/messages/it.json diff --git a/frontend/package.json b/apps/web/package.json similarity index 100% rename from frontend/package.json rename to apps/web/package.json diff --git a/frontend/project.inlang/project_id b/apps/web/project.inlang/project_id similarity index 100% rename from frontend/project.inlang/project_id rename to apps/web/project.inlang/project_id diff --git a/frontend/project.inlang/settings.json b/apps/web/project.inlang/settings.json similarity index 100% rename from frontend/project.inlang/settings.json rename to apps/web/project.inlang/settings.json diff --git a/frontend/public/apple-touch-icon-180x180.png b/apps/web/public/apple-touch-icon-180x180.png similarity index 100% rename from frontend/public/apple-touch-icon-180x180.png rename to apps/web/public/apple-touch-icon-180x180.png diff --git a/frontend/public/favicon.ico b/apps/web/public/favicon.ico similarity index 100% rename from frontend/public/favicon.ico rename to apps/web/public/favicon.ico diff --git a/frontend/public/favicon.svg b/apps/web/public/favicon.svg similarity index 100% rename from frontend/public/favicon.svg rename to apps/web/public/favicon.svg diff --git a/frontend/public/maskable-icon-512x512.png b/apps/web/public/maskable-icon-512x512.png similarity index 100% rename from frontend/public/maskable-icon-512x512.png rename to apps/web/public/maskable-icon-512x512.png diff --git a/frontend/public/pwa-192x192.png b/apps/web/public/pwa-192x192.png similarity index 100% rename from frontend/public/pwa-192x192.png rename to apps/web/public/pwa-192x192.png diff --git a/frontend/public/pwa-512x512.png b/apps/web/public/pwa-512x512.png similarity index 100% rename from frontend/public/pwa-512x512.png rename to apps/web/public/pwa-512x512.png diff --git a/frontend/public/pwa-64x64.png b/apps/web/public/pwa-64x64.png similarity index 100% rename from frontend/public/pwa-64x64.png rename to apps/web/public/pwa-64x64.png diff --git a/frontend/public/robots.txt b/apps/web/public/robots.txt similarity index 100% rename from frontend/public/robots.txt rename to apps/web/public/robots.txt diff --git a/frontend/pwa-assets.config.ts b/apps/web/pwa-assets.config.ts similarity index 100% rename from frontend/pwa-assets.config.ts rename to apps/web/pwa-assets.config.ts diff --git a/frontend/scripts/add-messages.py b/apps/web/scripts/add-messages.py similarity index 100% rename from frontend/scripts/add-messages.py rename to apps/web/scripts/add-messages.py diff --git a/frontend/scripts/release.sh b/apps/web/scripts/release.sh similarity index 100% rename from frontend/scripts/release.sh rename to apps/web/scripts/release.sh diff --git a/frontend/src/components/AccountDialog.tsx b/apps/web/src/components/AccountDialog.tsx similarity index 100% rename from frontend/src/components/AccountDialog.tsx rename to apps/web/src/components/AccountDialog.tsx diff --git a/frontend/src/components/AppSidebar.tsx b/apps/web/src/components/AppSidebar.tsx similarity index 100% rename from frontend/src/components/AppSidebar.tsx rename to apps/web/src/components/AppSidebar.tsx diff --git a/frontend/src/components/ArrayHealthPanel.tsx b/apps/web/src/components/ArrayHealthPanel.tsx similarity index 100% rename from frontend/src/components/ArrayHealthPanel.tsx rename to apps/web/src/components/ArrayHealthPanel.tsx diff --git a/frontend/src/components/AuthGate.tsx b/apps/web/src/components/AuthGate.tsx similarity index 100% rename from frontend/src/components/AuthGate.tsx rename to apps/web/src/components/AuthGate.tsx diff --git a/frontend/src/components/BackupDialog.tsx b/apps/web/src/components/BackupDialog.tsx similarity index 100% rename from frontend/src/components/BackupDialog.tsx rename to apps/web/src/components/BackupDialog.tsx diff --git a/frontend/src/components/Button.tsx b/apps/web/src/components/Button.tsx similarity index 100% rename from frontend/src/components/Button.tsx rename to apps/web/src/components/Button.tsx diff --git a/frontend/src/components/CheckDialog.tsx b/apps/web/src/components/CheckDialog.tsx similarity index 100% rename from frontend/src/components/CheckDialog.tsx rename to apps/web/src/components/CheckDialog.tsx diff --git a/frontend/src/components/CheckViewer.tsx b/apps/web/src/components/CheckViewer.tsx similarity index 100% rename from frontend/src/components/CheckViewer.tsx rename to apps/web/src/components/CheckViewer.tsx diff --git a/frontend/src/components/CommandMenu.tsx b/apps/web/src/components/CommandMenu.tsx similarity index 100% rename from frontend/src/components/CommandMenu.tsx rename to apps/web/src/components/CommandMenu.tsx diff --git a/frontend/src/components/CommandPalette.tsx b/apps/web/src/components/CommandPalette.tsx similarity index 100% rename from frontend/src/components/CommandPalette.tsx rename to apps/web/src/components/CommandPalette.tsx diff --git a/frontend/src/components/ConfigAddForm.tsx b/apps/web/src/components/ConfigAddForm.tsx similarity index 100% rename from frontend/src/components/ConfigAddForm.tsx rename to apps/web/src/components/ConfigAddForm.tsx diff --git a/frontend/src/components/ConfigBar.tsx b/apps/web/src/components/ConfigBar.tsx similarity index 100% rename from frontend/src/components/ConfigBar.tsx rename to apps/web/src/components/ConfigBar.tsx diff --git a/frontend/src/components/ConfigEditor.tsx b/apps/web/src/components/ConfigEditor.tsx similarity index 100% rename from frontend/src/components/ConfigEditor.tsx rename to apps/web/src/components/ConfigEditor.tsx diff --git a/frontend/src/components/ConfigList.tsx b/apps/web/src/components/ConfigList.tsx similarity index 100% rename from frontend/src/components/ConfigList.tsx rename to apps/web/src/components/ConfigList.tsx diff --git a/frontend/src/components/ConfigListItem.tsx b/apps/web/src/components/ConfigListItem.tsx similarity index 100% rename from frontend/src/components/ConfigListItem.tsx rename to apps/web/src/components/ConfigListItem.tsx diff --git a/frontend/src/components/ConfigManager.tsx b/apps/web/src/components/ConfigManager.tsx similarity index 100% rename from frontend/src/components/ConfigManager.tsx rename to apps/web/src/components/ConfigManager.tsx diff --git a/frontend/src/components/ConfigTextEditor.tsx b/apps/web/src/components/ConfigTextEditor.tsx similarity index 100% rename from frontend/src/components/ConfigTextEditor.tsx rename to apps/web/src/components/ConfigTextEditor.tsx diff --git a/frontend/src/components/ContentFileSection.tsx b/apps/web/src/components/ContentFileSection.tsx similarity index 100% rename from frontend/src/components/ContentFileSection.tsx rename to apps/web/src/components/ContentFileSection.tsx diff --git a/frontend/src/components/DataDiskSection.tsx b/apps/web/src/components/DataDiskSection.tsx similarity index 100% rename from frontend/src/components/DataDiskSection.tsx rename to apps/web/src/components/DataDiskSection.tsx diff --git a/frontend/src/components/DirectoryBrowser.tsx b/apps/web/src/components/DirectoryBrowser.tsx similarity index 100% rename from frontend/src/components/DirectoryBrowser.tsx rename to apps/web/src/components/DirectoryBrowser.tsx diff --git a/frontend/src/components/DiskManager.tsx b/apps/web/src/components/DiskManager.tsx similarity index 100% rename from frontend/src/components/DiskManager.tsx rename to apps/web/src/components/DiskManager.tsx diff --git a/frontend/src/components/DisksPanel.tsx b/apps/web/src/components/DisksPanel.tsx similarity index 100% rename from frontend/src/components/DisksPanel.tsx rename to apps/web/src/components/DisksPanel.tsx diff --git a/frontend/src/components/ErrorAlert.tsx b/apps/web/src/components/ErrorAlert.tsx similarity index 100% rename from frontend/src/components/ErrorAlert.tsx rename to apps/web/src/components/ErrorAlert.tsx diff --git a/frontend/src/components/ExcludePatternSection.tsx b/apps/web/src/components/ExcludePatternSection.tsx similarity index 100% rename from frontend/src/components/ExcludePatternSection.tsx rename to apps/web/src/components/ExcludePatternSection.tsx diff --git a/frontend/src/components/Feedback.tsx b/apps/web/src/components/Feedback.tsx similarity index 100% rename from frontend/src/components/Feedback.tsx rename to apps/web/src/components/Feedback.tsx diff --git a/frontend/src/components/FileBrowser.tsx b/apps/web/src/components/FileBrowser.tsx similarity index 100% rename from frontend/src/components/FileBrowser.tsx rename to apps/web/src/components/FileBrowser.tsx diff --git a/frontend/src/components/ForceRetryBox.tsx b/apps/web/src/components/ForceRetryBox.tsx similarity index 100% rename from frontend/src/components/ForceRetryBox.tsx rename to apps/web/src/components/ForceRetryBox.tsx diff --git a/frontend/src/components/Header.tsx b/apps/web/src/components/Header.tsx similarity index 100% rename from frontend/src/components/Header.tsx rename to apps/web/src/components/Header.tsx diff --git a/frontend/src/components/IntegrityReport.tsx b/apps/web/src/components/IntegrityReport.tsx similarity index 100% rename from frontend/src/components/IntegrityReport.tsx rename to apps/web/src/components/IntegrityReport.tsx diff --git a/frontend/src/components/LogFilters.tsx b/apps/web/src/components/LogFilters.tsx similarity index 100% rename from frontend/src/components/LogFilters.tsx rename to apps/web/src/components/LogFilters.tsx diff --git a/frontend/src/components/LogList.tsx b/apps/web/src/components/LogList.tsx similarity index 100% rename from frontend/src/components/LogList.tsx rename to apps/web/src/components/LogList.tsx diff --git a/frontend/src/components/LogListItem.tsx b/apps/web/src/components/LogListItem.tsx similarity index 100% rename from frontend/src/components/LogListItem.tsx rename to apps/web/src/components/LogListItem.tsx diff --git a/frontend/src/components/LogRawView.tsx b/apps/web/src/components/LogRawView.tsx similarity index 100% rename from frontend/src/components/LogRawView.tsx rename to apps/web/src/components/LogRawView.tsx diff --git a/frontend/src/components/LogViewer.tsx b/apps/web/src/components/LogViewer.tsx similarity index 100% rename from frontend/src/components/LogViewer.tsx rename to apps/web/src/components/LogViewer.tsx diff --git a/frontend/src/components/LoginPage.tsx b/apps/web/src/components/LoginPage.tsx similarity index 100% rename from frontend/src/components/LoginPage.tsx rename to apps/web/src/components/LoginPage.tsx diff --git a/frontend/src/components/MountSuggestions.tsx b/apps/web/src/components/MountSuggestions.tsx similarity index 100% rename from frontend/src/components/MountSuggestions.tsx rename to apps/web/src/components/MountSuggestions.tsx diff --git a/frontend/src/components/OptionsSection.tsx b/apps/web/src/components/OptionsSection.tsx similarity index 100% rename from frontend/src/components/OptionsSection.tsx rename to apps/web/src/components/OptionsSection.tsx diff --git a/frontend/src/components/OutputConsole.tsx b/apps/web/src/components/OutputConsole.tsx similarity index 100% rename from frontend/src/components/OutputConsole.tsx rename to apps/web/src/components/OutputConsole.tsx diff --git a/frontend/src/components/PageLayout.tsx b/apps/web/src/components/PageLayout.tsx similarity index 100% rename from frontend/src/components/PageLayout.tsx rename to apps/web/src/components/PageLayout.tsx diff --git a/frontend/src/components/ParityDiskSection.tsx b/apps/web/src/components/ParityDiskSection.tsx similarity index 100% rename from frontend/src/components/ParityDiskSection.tsx rename to apps/web/src/components/ParityDiskSection.tsx diff --git a/frontend/src/components/PoolSection.tsx b/apps/web/src/components/PoolSection.tsx similarity index 100% rename from frontend/src/components/PoolSection.tsx rename to apps/web/src/components/PoolSection.tsx diff --git a/frontend/src/components/RemoveDataDiskWizard.tsx b/apps/web/src/components/RemoveDataDiskWizard.tsx similarity index 100% rename from frontend/src/components/RemoveDataDiskWizard.tsx rename to apps/web/src/components/RemoveDataDiskWizard.tsx diff --git a/frontend/src/components/ReplaceDiskWizard.tsx b/apps/web/src/components/ReplaceDiskWizard.tsx similarity index 100% rename from frontend/src/components/ReplaceDiskWizard.tsx rename to apps/web/src/components/ReplaceDiskWizard.tsx diff --git a/frontend/src/components/RunningJobBox.tsx b/apps/web/src/components/RunningJobBox.tsx similarity index 100% rename from frontend/src/components/RunningJobBox.tsx rename to apps/web/src/components/RunningJobBox.tsx diff --git a/frontend/src/components/SaveBar.tsx b/apps/web/src/components/SaveBar.tsx similarity index 100% rename from frontend/src/components/SaveBar.tsx rename to apps/web/src/components/SaveBar.tsx diff --git a/frontend/src/components/ScrubDialog.tsx b/apps/web/src/components/ScrubDialog.tsx similarity index 100% rename from frontend/src/components/ScrubDialog.tsx rename to apps/web/src/components/ScrubDialog.tsx diff --git a/frontend/src/components/ScrubPlanPicker.tsx b/apps/web/src/components/ScrubPlanPicker.tsx similarity index 100% rename from frontend/src/components/ScrubPlanPicker.tsx rename to apps/web/src/components/ScrubPlanPicker.tsx diff --git a/frontend/src/components/SegmentedControl.tsx b/apps/web/src/components/SegmentedControl.tsx similarity index 100% rename from frontend/src/components/SegmentedControl.tsx rename to apps/web/src/components/SegmentedControl.tsx diff --git a/frontend/src/components/Select.tsx b/apps/web/src/components/Select.tsx similarity index 100% rename from frontend/src/components/Select.tsx rename to apps/web/src/components/Select.tsx diff --git a/frontend/src/components/Skeleton.tsx b/apps/web/src/components/Skeleton.tsx similarity index 100% rename from frontend/src/components/Skeleton.tsx rename to apps/web/src/components/Skeleton.tsx diff --git a/frontend/src/components/SmartHistoryCharts.tsx b/apps/web/src/components/SmartHistoryCharts.tsx similarity index 100% rename from frontend/src/components/SmartHistoryCharts.tsx rename to apps/web/src/components/SmartHistoryCharts.tsx diff --git a/frontend/src/components/SmartMonitor.tsx b/apps/web/src/components/SmartMonitor.tsx similarity index 100% rename from frontend/src/components/SmartMonitor.tsx rename to apps/web/src/components/SmartMonitor.tsx diff --git a/frontend/src/components/SmartSelfTest.tsx b/apps/web/src/components/SmartSelfTest.tsx similarity index 100% rename from frontend/src/components/SmartSelfTest.tsx rename to apps/web/src/components/SmartSelfTest.tsx diff --git a/frontend/src/components/SyncPreviewDialog.tsx b/apps/web/src/components/SyncPreviewDialog.tsx similarity index 100% rename from frontend/src/components/SyncPreviewDialog.tsx rename to apps/web/src/components/SyncPreviewDialog.tsx diff --git a/frontend/src/components/UsageHistoryChart.tsx b/apps/web/src/components/UsageHistoryChart.tsx similarity index 100% rename from frontend/src/components/UsageHistoryChart.tsx rename to apps/web/src/components/UsageHistoryChart.tsx diff --git a/frontend/src/components/ValidationResultAlert.tsx b/apps/web/src/components/ValidationResultAlert.tsx similarity index 100% rename from frontend/src/components/ValidationResultAlert.tsx rename to apps/web/src/components/ValidationResultAlert.tsx diff --git a/frontend/src/components/ViewModeToggle.tsx b/apps/web/src/components/ViewModeToggle.tsx similarity index 100% rename from frontend/src/components/ViewModeToggle.tsx rename to apps/web/src/components/ViewModeToggle.tsx diff --git a/frontend/src/components/__tests__/array-health.test.ts b/apps/web/src/components/__tests__/array-health.test.ts similarity index 100% rename from frontend/src/components/__tests__/array-health.test.ts rename to apps/web/src/components/__tests__/array-health.test.ts diff --git a/frontend/src/components/__tests__/schedule-text.test.ts b/apps/web/src/components/__tests__/schedule-text.test.ts similarity index 100% rename from frontend/src/components/__tests__/schedule-text.test.ts rename to apps/web/src/components/__tests__/schedule-text.test.ts diff --git a/frontend/src/components/__tests__/temperature-trend.test.ts b/apps/web/src/components/__tests__/temperature-trend.test.ts similarity index 100% rename from frontend/src/components/__tests__/temperature-trend.test.ts rename to apps/web/src/components/__tests__/temperature-trend.test.ts diff --git a/frontend/src/components/schedules/ScheduleForm.tsx b/apps/web/src/components/schedules/ScheduleForm.tsx similarity index 100% rename from frontend/src/components/schedules/ScheduleForm.tsx rename to apps/web/src/components/schedules/ScheduleForm.tsx diff --git a/frontend/src/components/schedules/ScheduleRow.tsx b/apps/web/src/components/schedules/ScheduleRow.tsx similarity index 100% rename from frontend/src/components/schedules/ScheduleRow.tsx rename to apps/web/src/components/schedules/ScheduleRow.tsx diff --git a/frontend/src/components/schedules/ScheduleWeek.tsx b/apps/web/src/components/schedules/ScheduleWeek.tsx similarity index 100% rename from frontend/src/components/schedules/ScheduleWeek.tsx rename to apps/web/src/components/schedules/ScheduleWeek.tsx diff --git a/frontend/src/components/schedules/scheduleText.ts b/apps/web/src/components/schedules/scheduleText.ts similarity index 100% rename from frontend/src/components/schedules/scheduleText.ts rename to apps/web/src/components/schedules/scheduleText.ts diff --git a/frontend/src/components/smartHistory.ts b/apps/web/src/components/smartHistory.ts similarity index 100% rename from frontend/src/components/smartHistory.ts rename to apps/web/src/components/smartHistory.ts diff --git a/frontend/src/components/temperatureTrend.ts b/apps/web/src/components/temperatureTrend.ts similarity index 100% rename from frontend/src/components/temperatureTrend.ts rename to apps/web/src/components/temperatureTrend.ts diff --git a/frontend/src/components/ui/alert-dialog.tsx b/apps/web/src/components/ui/alert-dialog.tsx similarity index 100% rename from frontend/src/components/ui/alert-dialog.tsx rename to apps/web/src/components/ui/alert-dialog.tsx diff --git a/frontend/src/components/ui/alert.tsx b/apps/web/src/components/ui/alert.tsx similarity index 100% rename from frontend/src/components/ui/alert.tsx rename to apps/web/src/components/ui/alert.tsx diff --git a/frontend/src/components/ui/badge.tsx b/apps/web/src/components/ui/badge.tsx similarity index 100% rename from frontend/src/components/ui/badge.tsx rename to apps/web/src/components/ui/badge.tsx diff --git a/frontend/src/components/ui/button.tsx b/apps/web/src/components/ui/button.tsx similarity index 100% rename from frontend/src/components/ui/button.tsx rename to apps/web/src/components/ui/button.tsx diff --git a/frontend/src/components/ui/card.tsx b/apps/web/src/components/ui/card.tsx similarity index 100% rename from frontend/src/components/ui/card.tsx rename to apps/web/src/components/ui/card.tsx diff --git a/frontend/src/components/ui/checkbox.tsx b/apps/web/src/components/ui/checkbox.tsx similarity index 100% rename from frontend/src/components/ui/checkbox.tsx rename to apps/web/src/components/ui/checkbox.tsx diff --git a/frontend/src/components/ui/command.tsx b/apps/web/src/components/ui/command.tsx similarity index 100% rename from frontend/src/components/ui/command.tsx rename to apps/web/src/components/ui/command.tsx diff --git a/frontend/src/components/ui/dialog.tsx b/apps/web/src/components/ui/dialog.tsx similarity index 100% rename from frontend/src/components/ui/dialog.tsx rename to apps/web/src/components/ui/dialog.tsx diff --git a/frontend/src/components/ui/dropdown-menu.tsx b/apps/web/src/components/ui/dropdown-menu.tsx similarity index 100% rename from frontend/src/components/ui/dropdown-menu.tsx rename to apps/web/src/components/ui/dropdown-menu.tsx diff --git a/frontend/src/components/ui/input.tsx b/apps/web/src/components/ui/input.tsx similarity index 100% rename from frontend/src/components/ui/input.tsx rename to apps/web/src/components/ui/input.tsx diff --git a/frontend/src/components/ui/kbd.tsx b/apps/web/src/components/ui/kbd.tsx similarity index 100% rename from frontend/src/components/ui/kbd.tsx rename to apps/web/src/components/ui/kbd.tsx diff --git a/frontend/src/components/ui/label.tsx b/apps/web/src/components/ui/label.tsx similarity index 100% rename from frontend/src/components/ui/label.tsx rename to apps/web/src/components/ui/label.tsx diff --git a/frontend/src/components/ui/progress.tsx b/apps/web/src/components/ui/progress.tsx similarity index 100% rename from frontend/src/components/ui/progress.tsx rename to apps/web/src/components/ui/progress.tsx diff --git a/frontend/src/components/ui/radio-group.tsx b/apps/web/src/components/ui/radio-group.tsx similarity index 100% rename from frontend/src/components/ui/radio-group.tsx rename to apps/web/src/components/ui/radio-group.tsx diff --git a/frontend/src/components/ui/select.tsx b/apps/web/src/components/ui/select.tsx similarity index 100% rename from frontend/src/components/ui/select.tsx rename to apps/web/src/components/ui/select.tsx diff --git a/frontend/src/components/ui/separator.tsx b/apps/web/src/components/ui/separator.tsx similarity index 100% rename from frontend/src/components/ui/separator.tsx rename to apps/web/src/components/ui/separator.tsx diff --git a/frontend/src/components/ui/sheet.tsx b/apps/web/src/components/ui/sheet.tsx similarity index 100% rename from frontend/src/components/ui/sheet.tsx rename to apps/web/src/components/ui/sheet.tsx diff --git a/frontend/src/components/ui/sonner.tsx b/apps/web/src/components/ui/sonner.tsx similarity index 100% rename from frontend/src/components/ui/sonner.tsx rename to apps/web/src/components/ui/sonner.tsx diff --git a/frontend/src/components/ui/switch.tsx b/apps/web/src/components/ui/switch.tsx similarity index 100% rename from frontend/src/components/ui/switch.tsx rename to apps/web/src/components/ui/switch.tsx diff --git a/frontend/src/components/ui/table.tsx b/apps/web/src/components/ui/table.tsx similarity index 100% rename from frontend/src/components/ui/table.tsx rename to apps/web/src/components/ui/table.tsx diff --git a/frontend/src/components/ui/tabs.tsx b/apps/web/src/components/ui/tabs.tsx similarity index 100% rename from frontend/src/components/ui/tabs.tsx rename to apps/web/src/components/ui/tabs.tsx diff --git a/frontend/src/components/ui/textarea.tsx b/apps/web/src/components/ui/textarea.tsx similarity index 100% rename from frontend/src/components/ui/textarea.tsx rename to apps/web/src/components/ui/textarea.tsx diff --git a/frontend/src/components/ui/toggle-group.tsx b/apps/web/src/components/ui/toggle-group.tsx similarity index 100% rename from frontend/src/components/ui/toggle-group.tsx rename to apps/web/src/components/ui/toggle-group.tsx diff --git a/frontend/src/components/ui/tooltip.tsx b/apps/web/src/components/ui/tooltip.tsx similarity index 100% rename from frontend/src/components/ui/tooltip.tsx rename to apps/web/src/components/ui/tooltip.tsx diff --git a/frontend/src/components/usageHistory.ts b/apps/web/src/components/usageHistory.ts similarity index 100% rename from frontend/src/components/usageHistory.ts rename to apps/web/src/components/usageHistory.ts diff --git a/frontend/src/hooks/queries.ts b/apps/web/src/hooks/queries.ts similarity index 100% rename from frontend/src/hooks/queries.ts rename to apps/web/src/hooks/queries.ts diff --git a/frontend/src/hooks/useAppShell.tsx b/apps/web/src/hooks/useAppShell.tsx similarity index 100% rename from frontend/src/hooks/useAppShell.tsx rename to apps/web/src/hooks/useAppShell.tsx diff --git a/frontend/src/hooks/useJob.tsx b/apps/web/src/hooks/useJob.tsx similarity index 100% rename from frontend/src/hooks/useJob.tsx rename to apps/web/src/hooks/useJob.tsx diff --git a/frontend/src/hooks/useSelectedConfig.tsx b/apps/web/src/hooks/useSelectedConfig.tsx similarity index 100% rename from frontend/src/hooks/useSelectedConfig.tsx rename to apps/web/src/hooks/useSelectedConfig.tsx diff --git a/frontend/src/lib/__tests__/duplicates.test.ts b/apps/web/src/lib/__tests__/duplicates.test.ts similarity index 100% rename from frontend/src/lib/__tests__/duplicates.test.ts rename to apps/web/src/lib/__tests__/duplicates.test.ts diff --git a/frontend/src/lib/__tests__/i18n.test.ts b/apps/web/src/lib/__tests__/i18n.test.ts similarity index 100% rename from frontend/src/lib/__tests__/i18n.test.ts rename to apps/web/src/lib/__tests__/i18n.test.ts diff --git a/frontend/src/lib/__tests__/job-tracker.test.ts b/apps/web/src/lib/__tests__/job-tracker.test.ts similarity index 100% rename from frontend/src/lib/__tests__/job-tracker.test.ts rename to apps/web/src/lib/__tests__/job-tracker.test.ts diff --git a/frontend/src/lib/__tests__/log-parse.test.ts b/apps/web/src/lib/__tests__/log-parse.test.ts similarity index 100% rename from frontend/src/lib/__tests__/log-parse.test.ts rename to apps/web/src/lib/__tests__/log-parse.test.ts diff --git a/frontend/src/lib/__tests__/progress.test.ts b/apps/web/src/lib/__tests__/progress.test.ts similarity index 100% rename from frontend/src/lib/__tests__/progress.test.ts rename to apps/web/src/lib/__tests__/progress.test.ts diff --git a/frontend/src/lib/__tests__/protected-files.test.ts b/apps/web/src/lib/__tests__/protected-files.test.ts similarity index 100% rename from frontend/src/lib/__tests__/protected-files.test.ts rename to apps/web/src/lib/__tests__/protected-files.test.ts diff --git a/frontend/src/lib/__tests__/recovery.test.ts b/apps/web/src/lib/__tests__/recovery.test.ts similarity index 100% rename from frontend/src/lib/__tests__/recovery.test.ts rename to apps/web/src/lib/__tests__/recovery.test.ts diff --git a/frontend/src/lib/__tests__/run-result.test.ts b/apps/web/src/lib/__tests__/run-result.test.ts similarity index 100% rename from frontend/src/lib/__tests__/run-result.test.ts rename to apps/web/src/lib/__tests__/run-result.test.ts diff --git a/frontend/src/lib/__tests__/setup-wizard.test.ts b/apps/web/src/lib/__tests__/setup-wizard.test.ts similarity index 100% rename from frontend/src/lib/__tests__/setup-wizard.test.ts rename to apps/web/src/lib/__tests__/setup-wizard.test.ts diff --git a/frontend/src/lib/__tests__/smart-attributes.test.ts b/apps/web/src/lib/__tests__/smart-attributes.test.ts similarity index 100% rename from frontend/src/lib/__tests__/smart-attributes.test.ts rename to apps/web/src/lib/__tests__/smart-attributes.test.ts diff --git a/frontend/src/lib/api/auth.ts b/apps/web/src/lib/api/auth.ts similarity index 100% rename from frontend/src/lib/api/auth.ts rename to apps/web/src/lib/api/auth.ts diff --git a/frontend/src/lib/api/config.ts b/apps/web/src/lib/api/config.ts similarity index 100% rename from frontend/src/lib/api/config.ts rename to apps/web/src/lib/api/config.ts diff --git a/frontend/src/lib/api/constants.ts b/apps/web/src/lib/api/constants.ts similarity index 100% rename from frontend/src/lib/api/constants.ts rename to apps/web/src/lib/api/constants.ts diff --git a/frontend/src/lib/api/engine.ts b/apps/web/src/lib/api/engine.ts similarity index 100% rename from frontend/src/lib/api/engine.ts rename to apps/web/src/lib/api/engine.ts diff --git a/frontend/src/lib/api/errors.ts b/apps/web/src/lib/api/errors.ts similarity index 100% rename from frontend/src/lib/api/errors.ts rename to apps/web/src/lib/api/errors.ts diff --git a/frontend/src/lib/api/filesystem.ts b/apps/web/src/lib/api/filesystem.ts similarity index 100% rename from frontend/src/lib/api/filesystem.ts rename to apps/web/src/lib/api/filesystem.ts diff --git a/frontend/src/lib/api/home-assistant.ts b/apps/web/src/lib/api/home-assistant.ts similarity index 100% rename from frontend/src/lib/api/home-assistant.ts rename to apps/web/src/lib/api/home-assistant.ts diff --git a/frontend/src/lib/api/logs.ts b/apps/web/src/lib/api/logs.ts similarity index 100% rename from frontend/src/lib/api/logs.ts rename to apps/web/src/lib/api/logs.ts diff --git a/frontend/src/lib/api/maintenance.ts b/apps/web/src/lib/api/maintenance.ts similarity index 100% rename from frontend/src/lib/api/maintenance.ts rename to apps/web/src/lib/api/maintenance.ts diff --git a/frontend/src/lib/api/notifications.ts b/apps/web/src/lib/api/notifications.ts similarity index 100% rename from frontend/src/lib/api/notifications.ts rename to apps/web/src/lib/api/notifications.ts diff --git a/frontend/src/lib/api/schedules.ts b/apps/web/src/lib/api/schedules.ts similarity index 100% rename from frontend/src/lib/api/schedules.ts rename to apps/web/src/lib/api/schedules.ts diff --git a/frontend/src/lib/api/setup.ts b/apps/web/src/lib/api/setup.ts similarity index 100% rename from frontend/src/lib/api/setup.ts rename to apps/web/src/lib/api/setup.ts diff --git a/frontend/src/lib/api/snapraid.ts b/apps/web/src/lib/api/snapraid.ts similarity index 100% rename from frontend/src/lib/api/snapraid.ts rename to apps/web/src/lib/api/snapraid.ts diff --git a/frontend/src/lib/api/websocket.ts b/apps/web/src/lib/api/websocket.ts similarity index 100% rename from frontend/src/lib/api/websocket.ts rename to apps/web/src/lib/api/websocket.ts diff --git a/frontend/src/lib/commands.ts b/apps/web/src/lib/commands.ts similarity index 100% rename from frontend/src/lib/commands.ts rename to apps/web/src/lib/commands.ts diff --git a/frontend/src/lib/duplicates.ts b/apps/web/src/lib/duplicates.ts similarity index 100% rename from frontend/src/lib/duplicates.ts rename to apps/web/src/lib/duplicates.ts diff --git a/frontend/src/lib/i18n.ts b/apps/web/src/lib/i18n.ts similarity index 100% rename from frontend/src/lib/i18n.ts rename to apps/web/src/lib/i18n.ts diff --git a/frontend/src/lib/job-tracker.ts b/apps/web/src/lib/job-tracker.ts similarity index 100% rename from frontend/src/lib/job-tracker.ts rename to apps/web/src/lib/job-tracker.ts diff --git a/frontend/src/lib/log-parse.ts b/apps/web/src/lib/log-parse.ts similarity index 100% rename from frontend/src/lib/log-parse.ts rename to apps/web/src/lib/log-parse.ts diff --git a/frontend/src/lib/nav.ts b/apps/web/src/lib/nav.ts similarity index 100% rename from frontend/src/lib/nav.ts rename to apps/web/src/lib/nav.ts diff --git a/frontend/src/lib/progress.ts b/apps/web/src/lib/progress.ts similarity index 100% rename from frontend/src/lib/progress.ts rename to apps/web/src/lib/progress.ts diff --git a/frontend/src/lib/protected-files.ts b/apps/web/src/lib/protected-files.ts similarity index 100% rename from frontend/src/lib/protected-files.ts rename to apps/web/src/lib/protected-files.ts diff --git a/frontend/src/lib/recovery.ts b/apps/web/src/lib/recovery.ts similarity index 100% rename from frontend/src/lib/recovery.ts rename to apps/web/src/lib/recovery.ts diff --git a/frontend/src/lib/run-result.ts b/apps/web/src/lib/run-result.ts similarity index 100% rename from frontend/src/lib/run-result.ts rename to apps/web/src/lib/run-result.ts diff --git a/frontend/src/lib/setup-wizard.ts b/apps/web/src/lib/setup-wizard.ts similarity index 100% rename from frontend/src/lib/setup-wizard.ts rename to apps/web/src/lib/setup-wizard.ts diff --git a/frontend/src/lib/smart-attributes.ts b/apps/web/src/lib/smart-attributes.ts similarity index 100% rename from frontend/src/lib/smart-attributes.ts rename to apps/web/src/lib/smart-attributes.ts diff --git a/frontend/src/lib/theme.ts b/apps/web/src/lib/theme.ts similarity index 100% rename from frontend/src/lib/theme.ts rename to apps/web/src/lib/theme.ts diff --git a/frontend/src/lib/utils.ts b/apps/web/src/lib/utils.ts similarity index 100% rename from frontend/src/lib/utils.ts rename to apps/web/src/lib/utils.ts diff --git a/frontend/src/lib/version.ts b/apps/web/src/lib/version.ts similarity index 100% rename from frontend/src/lib/version.ts rename to apps/web/src/lib/version.ts diff --git a/frontend/src/routeTree.gen.ts b/apps/web/src/routeTree.gen.ts similarity index 100% rename from frontend/src/routeTree.gen.ts rename to apps/web/src/routeTree.gen.ts diff --git a/frontend/src/router.tsx b/apps/web/src/router.tsx similarity index 100% rename from frontend/src/router.tsx rename to apps/web/src/router.tsx diff --git a/frontend/src/routes/__root.tsx b/apps/web/src/routes/__root.tsx similarity index 100% rename from frontend/src/routes/__root.tsx rename to apps/web/src/routes/__root.tsx diff --git a/frontend/src/routes/array.tsx b/apps/web/src/routes/array.tsx similarity index 100% rename from frontend/src/routes/array.tsx rename to apps/web/src/routes/array.tsx diff --git a/frontend/src/routes/automation.tsx b/apps/web/src/routes/automation.tsx similarity index 100% rename from frontend/src/routes/automation.tsx rename to apps/web/src/routes/automation.tsx diff --git a/frontend/src/routes/changes.tsx b/apps/web/src/routes/changes.tsx similarity index 100% rename from frontend/src/routes/changes.tsx rename to apps/web/src/routes/changes.tsx diff --git a/frontend/src/routes/duplicates.tsx b/apps/web/src/routes/duplicates.tsx similarity index 100% rename from frontend/src/routes/duplicates.tsx rename to apps/web/src/routes/duplicates.tsx diff --git a/frontend/src/routes/files.tsx b/apps/web/src/routes/files.tsx similarity index 100% rename from frontend/src/routes/files.tsx rename to apps/web/src/routes/files.tsx diff --git a/frontend/src/routes/index.tsx b/apps/web/src/routes/index.tsx similarity index 100% rename from frontend/src/routes/index.tsx rename to apps/web/src/routes/index.tsx diff --git a/frontend/src/routes/integrity.tsx b/apps/web/src/routes/integrity.tsx similarity index 100% rename from frontend/src/routes/integrity.tsx rename to apps/web/src/routes/integrity.tsx diff --git a/frontend/src/routes/logs.tsx b/apps/web/src/routes/logs.tsx similarity index 100% rename from frontend/src/routes/logs.tsx rename to apps/web/src/routes/logs.tsx diff --git a/frontend/src/routes/notifications.tsx b/apps/web/src/routes/notifications.tsx similarity index 100% rename from frontend/src/routes/notifications.tsx rename to apps/web/src/routes/notifications.tsx diff --git a/frontend/src/routes/recovery.tsx b/apps/web/src/routes/recovery.tsx similarity index 100% rename from frontend/src/routes/recovery.tsx rename to apps/web/src/routes/recovery.tsx diff --git a/frontend/src/routes/schedules.tsx b/apps/web/src/routes/schedules.tsx similarity index 100% rename from frontend/src/routes/schedules.tsx rename to apps/web/src/routes/schedules.tsx diff --git a/frontend/src/routes/setup.tsx b/apps/web/src/routes/setup.tsx similarity index 100% rename from frontend/src/routes/setup.tsx rename to apps/web/src/routes/setup.tsx diff --git a/frontend/src/routes/smart.tsx b/apps/web/src/routes/smart.tsx similarity index 100% rename from frontend/src/routes/smart.tsx rename to apps/web/src/routes/smart.tsx diff --git a/frontend/src/server.ts b/apps/web/src/server.ts similarity index 100% rename from frontend/src/server.ts rename to apps/web/src/server.ts diff --git a/frontend/src/styles.css b/apps/web/src/styles.css similarity index 100% rename from frontend/src/styles.css rename to apps/web/src/styles.css diff --git a/frontend/tsconfig.json b/apps/web/tsconfig.json similarity index 100% rename from frontend/tsconfig.json rename to apps/web/tsconfig.json diff --git a/frontend/vite.config.ts b/apps/web/vite.config.ts similarity index 100% rename from frontend/vite.config.ts rename to apps/web/vite.config.ts diff --git a/shared/array-setup.ts b/packages/shared/src/array-setup.ts similarity index 100% rename from shared/array-setup.ts rename to packages/shared/src/array-setup.ts diff --git a/shared/force-option.ts b/packages/shared/src/force-option.ts similarity index 100% rename from shared/force-option.ts rename to packages/shared/src/force-option.ts diff --git a/shared/i18n.ts b/packages/shared/src/i18n.ts similarity index 100% rename from shared/i18n.ts rename to packages/shared/src/i18n.ts diff --git a/shared/run-result.ts b/packages/shared/src/run-result.ts similarity index 100% rename from shared/run-result.ts rename to packages/shared/src/run-result.ts diff --git a/shared/smart-attributes.ts b/packages/shared/src/smart-attributes.ts similarity index 100% rename from shared/smart-attributes.ts rename to packages/shared/src/smart-attributes.ts diff --git a/shared/smart-health.ts b/packages/shared/src/smart-health.ts similarity index 100% rename from shared/smart-health.ts rename to packages/shared/src/smart-health.ts diff --git a/shared/types.ts b/packages/shared/src/types.ts similarity index 100% rename from shared/types.ts rename to packages/shared/src/types.ts diff --git a/shared/usage-forecast.ts b/packages/shared/src/usage-forecast.ts similarity index 100% rename from shared/usage-forecast.ts rename to packages/shared/src/usage-forecast.ts diff --git a/dev/setup.sh b/scripts/dev/setup.sh similarity index 100% rename from dev/setup.sh rename to scripts/dev/setup.sh diff --git a/start.sh b/scripts/start.sh similarity index 100% rename from start.sh rename to scripts/start.sh From 41b2e8aa96ac8791ced4658f36a730612dcad1c9 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 20:33:48 +0000 Subject: [PATCH 2/2] Make the repository one Bun workspace, as haben and namarr The paths the moves changed, and the workspace around them: - Root package.json with workspaces (apps/*, packages/*, e2e) and packageManager, one bun.lock instead of three (every resolved version as before), bunfig.toml with the isolated linker, so apps/web/node_modules/@inlang/... is still where project.inlang/settings.json loads its plugin from, and tsconfig.base.json with the options all three tsconfigs share (tsc --showConfig: the same options). - packages/shared is the workspace package @snapraid-ui/shared. The 192 imports in 152 files keep their @shared/ specifier; the tsconfig paths now point at the linked package in node_modules instead of ../shared. - The backend's default data directory stays snapraid/ in the repository's root (../../snapraid from apps/server); the server finds the built frontend, its package.json and docs/ at apps/web/... from the root, in the binary too. - scripts/start.sh and scripts/dev/setup.sh, with bun run dev and bun run demo in the root; the old install.sh (bun install in two folders) is replaced by bun install in the root. The installer's URL (the release asset from scripts/install.sh) does not change. - Dockerfile, .dockerignore, .gitignore, CI, release, bump, screenshots, upstream-release and Renovate, the e2e harness, build-binary.sh, docs, README. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Y3rdfbWb1xDUdPRng7TX9Z --- .dockerignore | 15 +- .github/renovate.json5 | 12 +- .github/workflows/bump.yml | 4 +- .github/workflows/ci.yml | 31 +- .github/workflows/release.yml | 9 +- .github/workflows/screenshots.yml | 18 +- .github/workflows/upstream-release.yml | 10 +- .gitignore | 40 +- README.md | 8 +- apps/server/bun.lock | 394 ------------------ apps/server/package.json | 2 +- apps/server/scripts/seed-demo.ts | 6 +- .../src/__tests__/engine-contract.test.ts | 2 +- apps/server/src/__tests__/version.test.ts | 2 +- apps/server/src/app-files.ts | 8 +- apps/server/src/bin-cli.ts | 2 +- apps/server/src/config.ts | 3 +- apps/server/src/demo.ts | 2 +- apps/server/src/frontend.ts | 4 +- apps/server/src/mcp.ts | 2 +- apps/server/src/version.ts | 4 +- apps/server/tsconfig.json | 11 +- apps/web/package.json | 2 +- apps/web/src/components/AccountDialog.tsx | 2 +- .../src/components/RemoveDataDiskWizard.tsx | 2 +- apps/web/src/lib/__tests__/i18n.test.ts | 4 +- apps/web/src/lib/__tests__/log-parse.test.ts | 2 +- apps/web/src/lib/__tests__/run-result.test.ts | 2 +- apps/web/src/lib/i18n.ts | 4 +- apps/web/tsconfig.json | 7 +- apps/web/bun.lock => bun.lock | 316 ++++++++++++++ bunfig.toml | 4 + docker/Dockerfile | 52 ++- docs/architecture.md | 53 +-- docs/assets/stylesheets/extra.css | 2 +- docs/configuration.md | 2 +- docs/development.md | 145 +++---- e2e/bun.lock | 169 -------- e2e/harness/demo-setup.ts | 12 +- e2e/harness/global-setup.ts | 4 +- e2e/package.json | 1 - e2e/scripts/setup-tools.sh | 4 +- e2e/tests/binary.spec.ts | 2 +- e2e/tests/daemon.spec.ts | 2 +- e2e/tsconfig.json | 5 +- install.sh | 14 - package.json | 16 + packages/shared/package.json | 8 + packages/shared/src/i18n.ts | 6 +- scripts/build-binary.sh | 20 +- scripts/dev/setup.sh | 21 +- scripts/social-preview/social-preview.html | 2 +- scripts/start.sh | 17 +- tsconfig.base.json | 8 + 54 files changed, 638 insertions(+), 861 deletions(-) delete mode 100644 apps/server/bun.lock rename apps/web/bun.lock => bun.lock (88%) create mode 100644 bunfig.toml delete mode 100644 e2e/bun.lock delete mode 100755 install.sh create mode 100644 package.json create mode 100644 packages/shared/package.json create mode 100644 tsconfig.base.json diff --git a/.dockerignore b/.dockerignore index 7b1bd05..6344198 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,4 @@ -node_modules +**/node_modules dist .git .vscode @@ -7,9 +7,7 @@ npm-debug.log* .DS_Store # Development files -frontend/node_modules -backend/node_modules -backend/.cache +apps/server/.cache # Test files **/*.test.ts @@ -19,9 +17,10 @@ backend/.cache # Local data and dev sandbox snapraid/ -dev/ -frontend/.output -frontend/.vinxi +scripts/dev/ +apps/web/.output +apps/web/.vinxi -# End-to-end tests: built tools, test data +# End-to-end tests: built tools, test data (only e2e/package.json goes in, for the workspace) e2e/ +!e2e/package.json diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 527d9c9..d12e167 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -17,7 +17,7 @@ customType: "regex", managerFilePatterns: [ "/^docker/(Dockerfile|README\\.md)$/", - "/^dev/setup\\.sh$/", + "/^scripts/dev/setup\\.sh$/", "/^e2e/scripts/setup-tools\\.sh$/", "/^scripts/install\\.sh$/", "/^\\.github/workflows/ci\\.yml$/", @@ -40,10 +40,10 @@ versioningTemplate: "regex:^(?\\d+)\\.(?\\d+)$", }, { - // "packageManager": "bun@x.y.z" in backend/, frontend/ and e2e/: the Bun of every CI job + // "packageManager": "bun@x.y.z" in the root package.json: the Bun of every CI job // (bun-version-file, and the npm-installed Bun of the screenshots job in the Playwright image) customType: "regex", - managerFilePatterns: ["/^(backend|frontend|e2e)/package\\.json$/"], + managerFilePatterns: ["/^package\\.json$/"], matchStrings: ['"packageManager": "bun@(?\\d+\\.\\d+\\.\\d+)"'], depNameTemplate: "oven-sh/bun", datasourceTemplate: "github-releases", @@ -62,20 +62,20 @@ sourceUrl: "https://github.com/amadvance/snapraid", commitMessageTopic: "SnapRAID", prBodyNotes: [ - "The parsers are tested against this one release. Before merging: read SnapRAID's [HISTORY](https://github.com/amadvance/snapraid/blob/master/HISTORY) for changes to the structured log (`--log`); the end-to-end tests of this PR build and run the new release. Locally: `SNAPRAID_VERSION= dev/setup.sh --reset`, then status, diff, sync, scrub, check, list, dup, touch and smart in the UI. A major version: compare the log tags with `backend/src/parsers/__tests__/fixtures/`, and the text \"SnapRAID 15\" in docs/installation.md. Check whether snapraid-daemon (`REVIEWED_DAEMON_VERSION`) needs this release; docs/automation.md names the versions the daemon mode was tested with.", + "The parsers are tested against this one release. Before merging: read SnapRAID's [HISTORY](https://github.com/amadvance/snapraid/blob/master/HISTORY) for changes to the structured log (`--log`); the end-to-end tests of this PR build and run the new release. Locally: `SNAPRAID_VERSION= scripts/dev/setup.sh --reset`, then status, diff, sync, scrub, check, list, dup, touch and smart in the UI. A major version: compare the log tags with `apps/server/src/parsers/__tests__/fixtures/`, and the text \"SnapRAID 15\" in docs/installation.md. Check whether snapraid-daemon (`REVIEWED_DAEMON_VERSION`) needs this release; docs/automation.md names the versions the daemon mode was tested with.", ], schedule: ["at any time"], automerge: false, dependencyDashboardApproval: false, }, { - // Bun of the image (oven/bun) and packageManager in backend/, frontend/ and e2e/ in one PR, + // Bun of the image (oven/bun) and packageManager in package.json in one PR, // so CI tests the Bun the image runs on matchPackageNames: ["docker.io/oven/bun", "oven/bun", "oven-sh/bun"], groupName: "Bun", sourceUrl: "https://github.com/oven-sh/bun", prBodyNotes: [ - "The image (`oven/bun:-slim` in docker/Dockerfile, the backend runs on it) and `packageManager` in `backend/package.json`, `frontend/package.json` and `e2e/package.json` (CI's `bun-version-file`, which also builds the standalone binary, and the screenshots job's `npm install bun@…`) move together. Bun runs the backend and its tests, installs, runs Vite and Vitest (`bun --bun`) and compiles the binary, and runs Playwright's test runner (`bun --bun playwright test`). Check the backend and frontend jobs and the end-to-end tests against the local build, the image and the binary.", + "The image (`oven/bun:-slim` in docker/Dockerfile, the backend runs on it) and `packageManager` in the root `package.json` (CI's `bun-version-file`, which also builds the standalone binary, and the screenshots job's `npm install bun@…`) move together. Bun runs the backend and its tests, installs, runs Vite and Vitest (`bun --bun`) and compiles the binary, and runs Playwright's test runner (`bun --bun playwright test`). Check the backend and frontend jobs and the end-to-end tests against the local build, the image and the binary.", ], schedule: ["at any time"], automerge: false, diff --git a/.github/workflows/bump.yml b/.github/workflows/bump.yml index ebf727d..ed4e3e0 100644 --- a/.github/workflows/bump.yml +++ b/.github/workflows/bump.yml @@ -1,6 +1,6 @@ name: Bump version -# Raises the version in frontend/package.json, commits it, tags the commit vX.Y.Z and starts release.yml on it. +# Raises the version in apps/web/package.json, commits it, tags the commit vX.Y.Z and starts release.yml on it. # Same scheme as in every project, see https://github.com/firsttris/workflows#bump-version on: workflow_dispatch: @@ -15,7 +15,7 @@ jobs: uses: firsttris/workflows/.github/workflows/bump-version.yml@v1 with: bump: ${{ inputs.bump }} - version-file: frontend/package.json + version-file: apps/web/package.json permissions: contents: write actions: write diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 591d0ce..519498d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,13 +29,15 @@ jobs: runs-on: ubuntu-latest defaults: run: - working-directory: backend + working-directory: apps/server steps: - uses: actions/checkout@v7 - uses: oven-sh/setup-bun@v2 with: - bun-version-file: backend/package.json + bun-version-file: package.json + # The workspace's one bun.lock, installed from the root - run: bun install --frozen-lockfile + working-directory: . - run: bun run typecheck - run: bun test @@ -44,13 +46,14 @@ jobs: runs-on: ubuntu-latest defaults: run: - working-directory: frontend + working-directory: apps/web steps: - uses: actions/checkout@v7 - uses: oven-sh/setup-bun@v2 with: - bun-version-file: frontend/package.json + bun-version-file: package.json - run: bun install --frozen-lockfile + working-directory: . - run: bunx biome check - run: bun run typecheck - run: bun run test @@ -67,11 +70,8 @@ jobs: - run: shellcheck -s sh scripts/install.sh - uses: oven-sh/setup-bun@v2 with: - bun-version-file: frontend/package.json + bun-version-file: package.json - run: bun install --frozen-lockfile - working-directory: frontend - - run: bun install --frozen-lockfile - working-directory: backend - run: scripts/build-binary.sh - name: Serve the binary like a release working-directory: dist @@ -141,12 +141,12 @@ jobs: # Bun installs, builds and runs the backend, and runs Playwright's test runner (bun --bun) - uses: oven-sh/setup-bun@v2 with: - bun-version-file: e2e/package.json + bun-version-file: package.json - name: Cache SnapRAID and snapraid-daemon builds uses: actions/cache@v6 with: path: e2e/.tools - key: e2e-tools-${{ runner.os }}-${{ hashFiles('e2e/scripts/setup-tools.sh', 'backend/src/engine/REVIEWED_DAEMON_VERSION') }} + key: e2e-tools-${{ runner.os }}-${{ hashFiles('e2e/scripts/setup-tools.sh', 'apps/server/src/engine/REVIEWED_DAEMON_VERSION') }} # smartmontools: SnapRAID lists the disks on the SMART page with it, self-tests use a stand-in - uses: firsttris/workflows/actions/apt-install@v1 with: @@ -154,17 +154,12 @@ jobs: recommends: "false" - name: Build SnapRAID and snapraid-daemon run: bun run setup + # All packages of the workspace: the tests', and the backend and frontend the tests run or + # the binary build is made of - run: bun install --frozen-lockfile + working-directory: . - run: bun run typecheck - run: bun --bun playwright install --with-deps chromium - - name: Install the frontend, the tests or the binary build it - if: matrix.target != 'image' - working-directory: frontend - run: bun install --frozen-lockfile - - name: Install the backend, the tests run it or the binary build it - if: matrix.target != 'image' - working-directory: backend - run: bun install --frozen-lockfile # The tests run the x86_64 one; arm64 is built too, so the release does not find a problem first - name: Build the binaries if: matrix.target == 'binary' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b991b3e..fd7af85 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,7 +4,7 @@ name: Release # the code, see https://github.com/firsttris/workflows#tested-on-main), then the shared Docker release from # firsttris/workflows, which pushes the image as :x.y.z, :x.y and :latest, updates the Docker Hub # description and creates the GitHub release; the standalone binaries are attached to it after. -# The tag must match the version in frontend/package.json. +# The tag must match the version in apps/web/package.json. # Run by hand on master: the same checks, then the image as :edge, nothing is released. on: push: @@ -34,7 +34,7 @@ jobs: with: image: tristanteu/snapraid-ui description: Web UI for SnapRAID — run sync/scrub, schedule jobs, monitor disks and SMART health - version-file: frontend/package.json + version-file: apps/web/package.json secrets: inherit permissions: contents: write @@ -53,11 +53,8 @@ jobs: - uses: actions/checkout@v7 - uses: oven-sh/setup-bun@v2 with: - bun-version-file: frontend/package.json + bun-version-file: package.json - run: bun install --frozen-lockfile - working-directory: frontend - - run: bun install --frozen-lockfile - working-directory: backend - run: scripts/build-binary.sh bun-linux-x64 bun-linux-arm64 - name: Checksums working-directory: dist diff --git a/.github/workflows/screenshots.yml b/.github/workflows/screenshots.yml index 5785c54..602fb7d 100644 --- a/.github/workflows/screenshots.yml +++ b/.github/workflows/screenshots.yml @@ -18,7 +18,7 @@ jobs: - id: playwright uses: firsttris/workflows/actions/playwright-image@v1 with: - lockfile: e2e/bun.lock + lockfile: bun.lock screenshots: needs: image @@ -31,24 +31,22 @@ jobs: contents: write steps: - uses: actions/checkout@v7 - # dev/setup.sh builds SnapRAID for the sandbox + # scripts/dev/setup.sh builds SnapRAID for the sandbox - name: Build tools run: apt-get update && apt-get install -y --no-install-recommends build-essential curl - name: Cache the SnapRAID build uses: actions/cache@v6 with: path: | - dev/bin - dev/.cache - key: demo-snapraid-${{ hashFiles('dev/setup.sh') }} + scripts/dev/bin + scripts/dev/.cache + key: demo-snapraid-${{ hashFiles('scripts/dev/setup.sh') }} # The npm package brings Bun without curl or unzip; the version is packageManager in - # e2e/package.json, as setup-bun takes it in the other workflows + # package.json, as setup-bun takes it in the other workflows - name: Install run: | - npm install --global "bun@$(node -p "require('./e2e/package.json').packageManager.split('@')[1]")" - bun install --frozen-lockfile --cwd backend - bun install --frozen-lockfile --cwd frontend - bun install --frozen-lockfile --cwd e2e + npm install --global "bun@$(node -p "require('./package.json').packageManager.split('@')[1]")" + bun install --frozen-lockfile - name: Take the screenshots working-directory: e2e # The container runs as root: the disks go to /mnt, as on a server diff --git a/.github/workflows/upstream-release.yml b/.github/workflows/upstream-release.yml index e64dee8..4b2321e 100644 --- a/.github/workflows/upstream-release.yml +++ b/.github/workflows/upstream-release.yml @@ -1,9 +1,9 @@ name: Upstream Release Check # Opens an issue when snapraid-daemon has a newer version, so it is looked at on purpose: jobs, -# status, diff, SMART and the power state go through the SnapRAID engine (backend/src/engine/engine.ts), +# status, diff, SMART and the power state go through the SnapRAID engine (apps/server/src/engine/engine.ts), # so they could also run on snapraid-daemon's REST API. Its API was last compared with -# backend/src/engine/REVIEWED_DAEMON_VERSION. A new SnapRAID release comes as a Renovate pull +# apps/server/src/engine/REVIEWED_DAEMON_VERSION. A new SnapRAID release comes as a Renovate pull # request instead (.github/renovate.json5). on: @@ -26,7 +26,7 @@ jobs: env: GH_TOKEN: ${{ github.token }} run: | - reviewed=$(tr -d '[:space:]' < backend/src/engine/REVIEWED_DAEMON_VERSION) + reviewed=$(tr -d '[:space:]' < apps/server/src/engine/REVIEWED_DAEMON_VERSION) # Tags like v1.15, v2.0rc2 and v2.0. Release candidates count, the API changes in them. # Sort key: 2.0rc2 -> 2.0.2, 2.0 -> 2.0.999, so a release sorts after its candidates newest=$(git ls-remote --tags https://github.com/amadvance/snapraid-daemon.git \ @@ -48,9 +48,9 @@ jobs: snapraid-daemon [v$newest](https://github.com/amadvance/snapraid-daemon/tree/v$newest) is out, the SnapRAID engine was last compared with v$reviewed. - [ ] Compare the API: [snapraidd.yaml v$reviewed...v$newest](https://github.com/amadvance/snapraid-daemon/compare/v$reviewed...v$newest) and the [HISTORY](https://github.com/amadvance/snapraid-daemon/blob/v$newest/HISTORY) - - [ ] An endpoint for something CLI-only today (\`dup\`, \`list\`, \`devices\`, config, ...)? Add a method to \`SnapRaidEngine\` in \`backend/src/engine/engine.ts\`, implement it in \`cli-engine.ts\`, move the callers and extend \`backend/src/__tests__/engine-contract.ts\` + - [ ] An endpoint for something CLI-only today (\`dup\`, \`list\`, \`devices\`, config, ...)? Add a method to \`SnapRaidEngine\` in \`apps/server/src/engine/engine.ts\`, implement it in \`cli-engine.ts\`, move the callers and extend \`apps/server/src/__tests__/engine-contract.ts\` - [ ] Update the tables in [docs/architecture.md](https://github.com/${{ github.repository }}/blob/master/docs/architecture.md#snapraid-engine) - - [ ] Put $newest into \`backend/src/engine/REVIEWED_DAEMON_VERSION\` + - [ ] Put $newest into \`apps/server/src/engine/REVIEWED_DAEMON_VERSION\` - [ ] The end-to-end tests build and run against that version; if it needs a newer SnapRAID, raise \`SNAPRAID15_REF\` in \`e2e/scripts/setup-tools.sh\` BODY )" diff --git a/.gitignore b/.gitignore index b1d3247..f149d61 100644 --- a/.gitignore +++ b/.gitignore @@ -1,27 +1,27 @@ -node_modules/ -dist/ -.env -.env.local -*.log -.DS_Store -.vscode/ -.idea/ -config.json -frontend/.output/ -frontend/.vinxi/ +node_modules/ +dist/ +.env +.env.local +*.log +.DS_Store +.vscode/ +.idea/ +config.json +apps/web/.output/ +apps/web/.vinxi/ -# Dev sandbox (dev/setup.sh) -dev/bin/ -dev/.cache/ -dev/sandbox/ +# Dev sandbox (scripts/dev/setup.sh) +scripts/dev/bin/ +scripts/dev/.cache/ +scripts/dev/sandbox/ -# Local data of ./start.sh (configs, schedules, logs) +# Local data of scripts/start.sh (configs, schedules, logs) /snapraid/ -!backend/src/parsers/__tests__/fixtures/*.log +!apps/server/src/parsers/__tests__/fixtures/*.log -# Paraglide (generated from frontend/messages/*.json) and the inlang cache -frontend/src/paraglide/ -frontend/project.inlang/cache/ +# Paraglide (generated from apps/web/messages/*.json) and the inlang cache +apps/web/src/paraglide/ +apps/web/project.inlang/cache/ # MkDocs build (mkdocs build) site/ diff --git a/README.md b/README.md index 26fa237..51ef7f7 100644 --- a/README.md +++ b/README.md @@ -244,13 +244,13 @@ December 23. ## 🛠️ Development -Requires Bun (the version in `frontend/package.json`) and the fish shell for `start.sh`; +Requires Bun (the version in `package.json`) and the fish shell for `scripts/start.sh`; the end-to-end tests run on Bun too. ```bash git clone https://github.com/firsttris/snapraid-ui && cd snapraid-ui -./install.sh -./start.sh --demo # sandbox with fake disks, no real array needed +bun install +bun run demo # sandbox with fake disks, no real array needed ``` The frontend runs on http://localhost:3000, the API on http://localhost:8080. Built with Bun + Hono, @@ -260,7 +260,7 @@ project layout, tests and translations. ## 🤝 Contributing Issues and pull requests are welcome. Please run `bunx biome check`, `bun run typecheck` and the tests -(`bun test` in `backend/`, `bun run test` in `frontend/`) before opening a pull request. +(`bun test` in `apps/server/`, `bun run test` in `apps/web/`) before opening a pull request. Changes to jobs, schedules or the Docker image are also covered by the end-to-end tests in `e2e/`, which CI runs against the local build and the image; see [Development](https://firsttris.github.io/snapraid-ui/development.html#end-to-end-tests) to run them locally. diff --git a/apps/server/bun.lock b/apps/server/bun.lock deleted file mode 100644 index 0449c51..0000000 --- a/apps/server/bun.lock +++ /dev/null @@ -1,394 +0,0 @@ -{ - "lockfileVersion": 2, - "configVersion": 1, - "workspaces": { - "": { - "name": "snapraid-ui-backend", - "dependencies": { - "@modelcontextprotocol/sdk": "^1.32.1", - "@simplewebauthn/server": "^14.0.3", - "croner": "^11.0.0", - "hono": "^4.13.9", - "mqtt": "^5.14.1", - "nodemailer": "^10.0.13", - "zod": "^4.1.12", - }, - "devDependencies": { - "@types/bun": "^1.4.4", - "@types/nodemailer": "^8.0.2", - "typescript": "^7.0.2", - }, - }, - }, - "packages": { - "@babel/runtime": ["@babel/runtime@7.29.10", "", {}, "sha512-NpugTzbKgGrNe45Ui32cN8vCFFMxVtP/9rtfwv0nP5FRDAjvYBxJcLVpYA9mr1jxP6cnIT7x9LUzuDTbWYYNRA=="], - - "@hexagon/base64": ["@hexagon/base64@1.1.28", "", {}, "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw=="], - - "@hono/node-server": ["@hono/node-server@2.1.6", "", { "peerDependencies": { "hono": "^4 || ^5.0.0-0" } }, "sha512-p/KH9MnfNcsm3sR6x4lNgm5j+QN4Et8srKredl9DZfXiyZ1UQ3aQ2YVpFLbqktYN5JI+DB+VZFKM2PqE5hLKjQ=="], - - "@levischuck/tiny-cbor": ["@levischuck/tiny-cbor@0.2.11", "", {}, "sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow=="], - - "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.32.2", "", { "dependencies": { "@hono/node-server": "^1.19.9 || ^2.0.5", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-SWsVtH/0vULSQStoZ6xxH2K09Li8fXYTMC64ZeogCdpZ3XSi1eCPOByV1OpuJxuI3OcmHhosDWYyii07tpPsLw=="], - - "@peculiar/asn1-android": ["@peculiar/asn1-android@2.11.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-AuUBRDNdWrgtnuJGXARa0Xdtq6OAD8GqezDQz6Qka7kyynMo9fzkcrtpBEHuX984uEB570BrhMZfJfkUDfpQfw=="], - - "@peculiar/asn1-asym-key": ["@peculiar/asn1-asym-key@2.11.1", "", { "dependencies": { "@peculiar/asn1-pkcs8": "^2.11.1", "@peculiar/asn1-schema": "^2.11.1", "@peculiar/asn1-x509": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-YtXR0FbDtjHuNGvWXD4HFE7th5xYwG67O+ApDH/FZsjj5MOrWHzDvqwZ1vm6uFUJtMAkUDcF6VMPGOJ5gnLyUA=="], - - "@peculiar/asn1-cms": ["@peculiar/asn1-cms@2.11.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.11.1", "@peculiar/asn1-x509": "^2.11.1", "@peculiar/asn1-x509-attr": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-Bk+oatE62dW0Tzzpfk6vdQxfGGEfvuGdWgmijnKjcv7HqZCJISs85z4zw2uDArrk+R1Oel1wts/BBl7HHrtAcA=="], - - "@peculiar/asn1-csr": ["@peculiar/asn1-csr@2.11.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.11.1", "@peculiar/asn1-x509": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-KVfbXaKng8886/tOPG55t5K1XkTkGbR4amjIJzytXWobFU5nZgw/sKnSpa1hxP17nUSsrOF/7Mt9WUYeyLsi6Q=="], - - "@peculiar/asn1-ecc": ["@peculiar/asn1-ecc@2.11.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.11.1", "@peculiar/asn1-x509": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-7MSNji77fQa/oN3WAfwagMy8EoJFg50sW4hZ1dtr0zU1BSugit0I2DGpIdPhvkNplopQhz5bogxpn1077eaamA=="], - - "@peculiar/asn1-pfx": ["@peculiar/asn1-pfx@2.11.1", "", { "dependencies": { "@peculiar/asn1-cms": "^2.11.1", "@peculiar/asn1-pkcs8": "^2.11.1", "@peculiar/asn1-rsa": "^2.11.1", "@peculiar/asn1-schema": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-2SN2z8qkc2hk/GgTYA8RN54wtsCDZShg9gI2Mq1vLhdj1adcv5fhTbiEmExZUaAMqlizxiEpULQDfHM5iYtn1g=="], - - "@peculiar/asn1-pkcs8": ["@peculiar/asn1-pkcs8@2.11.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.11.1", "@peculiar/asn1-x509": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-HqsSrnpQghRp9SM9QSmR6fa4z/BNktnKq7928+oq4qF0VAnuHZ4cyePKvcxh9fP4taBCP/WHNItlVNsYaQm2qw=="], - - "@peculiar/asn1-pkcs9": ["@peculiar/asn1-pkcs9@2.11.1", "", { "dependencies": { "@peculiar/asn1-cms": "^2.11.1", "@peculiar/asn1-pfx": "^2.11.1", "@peculiar/asn1-pkcs8": "^2.11.1", "@peculiar/asn1-schema": "^2.11.1", "@peculiar/asn1-x509": "^2.11.1", "@peculiar/asn1-x509-attr": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-OHFkTg8D2zYafILqFE4KPbqX8srpvd3diGDc2h2cduumawc765mOHGcuFMGvLWlL2bbCMWMmMSCtlXptsOuiPA=="], - - "@peculiar/asn1-rsa": ["@peculiar/asn1-rsa@2.11.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.11.1", "@peculiar/asn1-x509": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-6KwKGjulF489CpidZqkogNgLj6oGzgC4BL2FClM8PuJQ4t5NqT4q4a6A4vrSxBIBhMnL8hGYkLOpbVw0Vq38Tw=="], - - "@peculiar/asn1-schema": ["@peculiar/asn1-schema@2.11.1", "", { "dependencies": { "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-jaU9OaCcbZUdyXx9hMETZB9xoNmxTGjOGFqd3cOwKFkpKnyKd9WxF3uqj4PvX4cyOMP9bSdHg5YyML2qtx/d/g=="], - - "@peculiar/asn1-x509": ["@peculiar/asn1-x509@2.11.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.11.1", "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-SXU4/X6B3FquqQAN6ARI0bFDUV7+L1WlvPEPMKzEMkstd2D7UyM0r93fv241+OjdhqAKYGx5MvV3gzZMAp0APA=="], - - "@peculiar/asn1-x509-attr": ["@peculiar/asn1-x509-attr@2.11.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.11.1", "@peculiar/asn1-x509": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-n7RKFrbTzt8pGBlZ90kmKEn1qsLTTyw7lrvHMK0xsA42kQugPk/GXYpNbEcl7DkkdVsU/one5UX7KQPfxLj8vQ=="], - - "@peculiar/asn1-x509-post-quantum": ["@peculiar/asn1-x509-post-quantum@2.11.1", "", { "dependencies": { "@peculiar/asn1-asym-key": "^2.11.1", "@peculiar/asn1-schema": "^2.11.1", "@peculiar/asn1-x509": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-yD/pxCQ97nzwF7MGAW4kRd1WVLiMPhD39HxOnAr38DfGFwbpVhPnMbq+r6WX0XUrxn4w8ulsTdUafwVl46igLA=="], - - "@peculiar/utils": ["@peculiar/utils@2.0.3", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ=="], - - "@peculiar/x509": ["@peculiar/x509@2.2.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.11.0", "@peculiar/asn1-csr": "^2.11.0", "@peculiar/asn1-ecc": "^2.11.0", "@peculiar/asn1-pkcs9": "^2.11.0", "@peculiar/asn1-rsa": "^2.11.0", "@peculiar/asn1-schema": "^2.11.0", "@peculiar/asn1-x509": "^2.11.0", "@peculiar/asn1-x509-post-quantum": "^2.11.0", "pvtsutils": "^1.3.6", "tslib": "^2.8.1", "tsyringe": "^4.10.0" } }, "sha512-adpW58ySPQMOUPv8pTKT3RIyCFqfUFt8X05ukPdtkQToXDVr8dMtxuenik1yx8tNFTbpApWsM/Hi5SzlIx48+Q=="], - - "@simplewebauthn/server": ["@simplewebauthn/server@14.0.3", "", { "dependencies": { "@hexagon/base64": "^1.1.27", "@levischuck/tiny-cbor": "^0.2.2", "@peculiar/asn1-android": "^2.6.0", "@peculiar/asn1-ecc": "^2.6.1", "@peculiar/asn1-rsa": "^2.6.1", "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.1", "@peculiar/asn1-x509-post-quantum": "^2.9.4", "@peculiar/x509": "^2.1.0", "reflect-metadata": "^0.2.2" } }, "sha512-D39/0fiDiYmGV+nraY9s63D1Ho2AX/waMLy22/9gUadTUuvbAnstfb4bpbHYbwZuV5ZLDYN1SmzDKuiHzKAA4Q=="], - - "@types/bun": ["@types/bun@1.4.4", "", { "dependencies": { "bun-types": "1.4.4" } }, "sha512-9wkRDXUXK124pEnAutE6n/l18j+i9GC5IAHK8TmX/2nlySETmky0OU6yL5U8pW1AoKh++AN9auDCr647BfurjQ=="], - - "@types/node": ["@types/node@26.6.6", "", { "dependencies": { "undici-types": "~8.9.0" } }, "sha512-MzQj+8Kx3XI40OmUAE6Qoh58ZMNstNNMwn2nfd3qWsfe7nDNSqJNzes4njhz6AHwrSHOffSLwXU6yb474Q4aog=="], - - "@types/nodemailer": ["@types/nodemailer@8.0.2", "", { "dependencies": { "@types/node": "*" } }, "sha512-c7M5ox8p0nEOfbJ2E9Qcmt8/QCu/VzsiAhzBiZbvky2PhKZDHpKB3sQHBM5MEZNkCfBOaS6S9//AGPRTNB7IaA=="], - - "@types/readable-stream": ["@types/readable-stream@4.0.25", "", { "dependencies": { "@types/node": "*" } }, "sha512-7GsxL8Wd5X3jh73/qgW7WCnbBvGSdD8U3E4Jiuus3/8I+PN20DtlkJtwFhr/sImXfDDQ2Z03i/9wNiIF3TVSsw=="], - - "@types/ws": ["@types/ws@8.18.2", "", { "dependencies": { "@types/node": "*" } }, "sha512-67MQl+fpWKVTT1NYdnmo3U4sc/xPo/zQBncVnI74qmQa0z/b+1g6iYqNmGCPbxO+zz2aklb08a0oHfegiVd0/w=="], - - "@typescript/typescript-aix-ppc64": ["@typescript/typescript-aix-ppc64@7.0.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ=="], - - "@typescript/typescript-darwin-arm64": ["@typescript/typescript-darwin-arm64@7.0.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA=="], - - "@typescript/typescript-darwin-x64": ["@typescript/typescript-darwin-x64@7.0.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA=="], - - "@typescript/typescript-freebsd-arm64": ["@typescript/typescript-freebsd-arm64@7.0.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ=="], - - "@typescript/typescript-freebsd-x64": ["@typescript/typescript-freebsd-x64@7.0.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw=="], - - "@typescript/typescript-linux-arm": ["@typescript/typescript-linux-arm@7.0.2", "", { "os": "linux", "cpu": "arm" }, "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ=="], - - "@typescript/typescript-linux-arm64": ["@typescript/typescript-linux-arm64@7.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ=="], - - "@typescript/typescript-linux-loong64": ["@typescript/typescript-linux-loong64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ=="], - - "@typescript/typescript-linux-mips64el": ["@typescript/typescript-linux-mips64el@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA=="], - - "@typescript/typescript-linux-ppc64": ["@typescript/typescript-linux-ppc64@7.0.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA=="], - - "@typescript/typescript-linux-riscv64": ["@typescript/typescript-linux-riscv64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ=="], - - "@typescript/typescript-linux-s390x": ["@typescript/typescript-linux-s390x@7.0.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw=="], - - "@typescript/typescript-linux-x64": ["@typescript/typescript-linux-x64@7.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A=="], - - "@typescript/typescript-netbsd-arm64": ["@typescript/typescript-netbsd-arm64@7.0.2", "", { "os": "none", "cpu": "arm64" }, "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA=="], - - "@typescript/typescript-netbsd-x64": ["@typescript/typescript-netbsd-x64@7.0.2", "", { "os": "none", "cpu": "x64" }, "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA=="], - - "@typescript/typescript-openbsd-arm64": ["@typescript/typescript-openbsd-arm64@7.0.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ=="], - - "@typescript/typescript-openbsd-x64": ["@typescript/typescript-openbsd-x64@7.0.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg=="], - - "@typescript/typescript-sunos-x64": ["@typescript/typescript-sunos-x64@7.0.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g=="], - - "@typescript/typescript-win32-arm64": ["@typescript/typescript-win32-arm64@7.0.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ=="], - - "@typescript/typescript-win32-x64": ["@typescript/typescript-win32-x64@7.0.2", "", { "os": "win32", "cpu": "x64" }, "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g=="], - - "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], - - "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], - - "ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], - - "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], - - "asn1js": ["asn1js@3.1.0", "", { "dependencies": { "pvtsutils": "^1.3.6", "pvutils": "^1.2.0", "tslib": "^2.8.1" } }, "sha512-HWYi1THkOqguCiO+vwj09ZkQFD2kKEsejYHAYCtX0D80WVdoDnvRc9IukXX/jeb0T9NRWJnRtqwdbHltnFjwXA=="], - - "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], - - "bl": ["bl@6.1.6", "", { "dependencies": { "@types/readable-stream": "^4.0.0", "buffer": "^6.0.3", "inherits": "^2.0.4", "readable-stream": "^4.2.0" } }, "sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg=="], - - "body-parser": ["body-parser@2.4.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^2.1.0", "debug": "^4.4.3", "http-errors": "^2.0.1", "iconv-lite": "^0.7.3", "on-finished": "^2.4.1", "qs": "^6.16.1", "raw-body": "^3.0.2", "type-is": "^2.1.0" } }, "sha512-Ifk1KcLrxaGt1occQagQNFNNOqRt1LFuehEnHHzB8Z6cRgIxRU1TBaiIsUiCdA+gy5PL7Y1IX31DNKkRmxtXjQ=="], - - "broker-factory": ["broker-factory@3.1.15", "", { "dependencies": { "@babel/runtime": "^7.29.7", "fast-unique-numbers": "^9.0.27", "tslib": "^2.8.1", "worker-factory": "^7.0.50" } }, "sha512-ko+aWvgNuP49meGrdjUu7rC+Y+Wai3cCPxP3xWwHsHfehFjOh5ZQM2yC4gEB2UddeZ/YXhm0K1eG/L6fxym2Og=="], - - "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], - - "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], - - "bun-types": ["bun-types@1.4.4", "", { "dependencies": { "@types/node": "*", "undici-types": "*" } }, "sha512-yjrJh5oGX7mMPKGd19YXyIjmEuj4G4zNC30bs7PM9Az7NQ3ndF3j6hbeRdvJTRQEhJ/FYQwW0a2moweEi2hMxQ=="], - - "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], - - "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], - - "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], - - "commist": ["commist@3.2.0", "", {}, "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw=="], - - "concat-stream": ["concat-stream@2.0.0", "", { "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.0.2", "typedarray": "^0.0.6" } }, "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A=="], - - "content-disposition": ["content-disposition@2.0.1", "", {}, "sha512-e+H0ZXHSWYrENhQzw1LPuP4oF5MzVKmDU6d3hxlvaPEYLLg62MxtQNPRx4SYSuYJSBUgnQIG4HIN2tEtNv7Dog=="], - - "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], - - "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], - - "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], - - "cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], - - "croner": ["croner@11.0.0", "", {}, "sha512-T17ADjdttrVToyzLpLq9Xb9xzcMwpQ/M3Qs9szhh0MVNTbv6NWQ9OFYa2qlzPYXFMn+uoDlnW1IpIv6cAvFtyA=="], - - "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - - "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - - "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], - - "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], - - "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], - - "encodeurl": ["encodeurl@2.0.1", "", {}, "sha512-Ev/hIyePLZtpL98x562lvPtenrUfGr0lfaf44HFZ+hDEwso7ccV9QAZrEVLg0hxNAzygaAFxy2dYaAKKM0G06A=="], - - "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], - - "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], - - "es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="], - - "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], - - "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], - - "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], - - "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], - - "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], - - "eventsource-parser": ["eventsource-parser@3.1.1", "", {}, "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ=="], - - "express": ["express@5.3.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.3.0", "content-disposition": "^2.0.1", "content-type": "^2.0.0", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.8", "qs": "^6.16.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.1.0", "vary": "^1.1.2" } }, "sha512-qCqy1xSoaugxOD4IVHb1ZWdkvCF2W3aE2okKouWj8VwJ+ztskSiXoBnCSJqfKT+cVPJhuJqe9YLbY425+Zh00g=="], - - "express-rate-limit": ["express-rate-limit@8.7.3", "", { "dependencies": { "debug": "^4.4.3", "ip-address": "^10.2.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-zPLqDI2f0gV2WmGnacLf9XSSqn4VldRyP5LCzOwx0BKH40mO6+2PCFkoSsRO2jePx87/Kf6NUF6P2QOTKXHbQA=="], - - "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], - - "fast-unique-numbers": ["fast-unique-numbers@9.0.27", "", { "dependencies": { "@babel/runtime": "^7.29.2", "tslib": "^2.8.1" } }, "sha512-nDA9ADeINN8SA2u2wCtU+siWFTTDqQR37XvgPIDDmboWQeExz7X0mImxuaN+kJddliIqy2FpVRmnvRZ+j8i1/A=="], - - "fast-uri": ["fast-uri@3.1.9", "", {}, "sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ=="], - - "finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], - - "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], - - "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], - - "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], - - "get-intrinsic": ["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" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], - - "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], - - "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], - - "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], - - "hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="], - - "help-me": ["help-me@5.0.0", "", {}, "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg=="], - - "hono": ["hono@4.13.16", "", {}, "sha512-AOLOEhvrV+iH92xXCnFr/5+3KjPKcI3xP63XJtX5spAnkznPdHijlIsUTpi7/9EhWqrTCekw5c5zh1K+YsVw7A=="], - - "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], - - "iconv-lite": ["iconv-lite@0.7.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ=="], - - "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], - - "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], - - "ip-address": ["ip-address@10.7.6", "", {}, "sha512-00LYkM6byILa9eQIx6qoaXCM40xTLd56AZ4vyaPW3YTyb49VZYPo5xsiAq4tuQ54GH4R7gVdYScJ1B7B77PuTg=="], - - "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], - - "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], - - "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - - "jose": ["jose@6.2.15", "", {}, "sha512-qdF6ewcZARtef63fJ9AKKZPgxw+t7USH6HLXYcD4CrdmSvDp79rpJSPFEKc90QBeGS63u1KQtN2MZQHC0yDIzA=="], - - "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], - - "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], - - "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], - - "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], - - "media-typer": ["media-typer@1.1.1", "", {}, "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ=="], - - "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], - - "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], - - "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], - - "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], - - "mqtt": ["mqtt@5.16.1", "", { "dependencies": { "@types/readable-stream": "^4.0.21", "@types/ws": "^8.18.1", "commist": "^3.2.0", "concat-stream": "^2.0.0", "debug": "^4.4.1", "help-me": "^5.0.0", "lru-cache": "^10.4.3", "minimist": "^1.2.8", "mqtt-packet": "^9.0.2", "readable-stream": "^4.7.0", "rfdc": "^1.4.1", "socks": "^2.8.6", "split2": "^4.2.0", "worker-timers": "^8.0.23", "ws": "^8.18.3" }, "bin": { "mqtt": "build/bin/mqtt.js", "mqtt_pub": "build/bin/pub.js", "mqtt_sub": "build/bin/sub.js" } }, "sha512-9aEaiR/mLblsMAiynD8quhcGTaINdta2bdyknAtfVzESfoe6Ncdh/uEs5p+3zqcTEtgYErwL4pNk0QkAynXxlg=="], - - "mqtt-packet": ["mqtt-packet@9.0.2", "", { "dependencies": { "bl": "^6.0.8", "debug": "^4.3.4", "process-nextick-args": "^2.0.1" } }, "sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA=="], - - "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - - "negotiator": ["negotiator@1.1.0", "", { "dependencies": { "content-type": "^2.1.0" } }, "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg=="], - - "nodemailer": ["nodemailer@10.3.1", "", {}, "sha512-uF0AFsJC07Y1vdW7p580DKAnh7rAO+4UPSSB3mDCPNDufoiaOebV6JmrzpxB14OjEEEiBsNdYoM2Y+fN8M7BBA=="], - - "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], - - "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], - - "on-finished": ["on-finished@2.4.2", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-ATWUv387i+nLD49EiDoOft5scf32Vsdz3ktrQJG9bi7jRFmaXTssvyvj1EmeKVVgvesbqUtvQQvaS2TpTukAZQ=="], - - "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], - - "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], - - "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - - "path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], - - "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], - - "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="], - - "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], - - "proxy-addr": ["proxy-addr@2.0.8", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ=="], - - "pvtsutils": ["pvtsutils@1.3.6", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg=="], - - "pvutils": ["pvutils@1.2.0", "", {}, "sha512-BbubeCEyTuQjVMakvJQ/Sxbc93F2pwmbsxONT/ZRrwU7Ua38d8unYTwXpTVLAKJ4BDuH9IGztCjQcd/N/39Dvg=="], - - "qs": ["qs@6.17.1", "", { "dependencies": { "es-define-property": "^1.0.1", "side-channel": "^1.1.1" } }, "sha512-jd3COkaKVgGUW+ZHyEHCG9bH7RoaaiWuMgzsIBjUMVjLAaWGWpoUAVwljVvBHxRu/2qEHnadLGZrkutYUBPQJA=="], - - "range-parser": ["range-parser@1.3.0", "", {}, "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw=="], - - "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], - - "readable-stream": ["readable-stream@4.8.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-2jhTTU3Eilp/2DMT71gbcoqC35eQHasMJH0RHkYxwlxIv3+FR6Y5xAFZIwEGLxKkjZYEFlsiMNIdwUqjK2lLHQ=="], - - "reflect-metadata": ["reflect-metadata@0.2.2", "", {}, "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q=="], - - "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], - - "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], - - "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], - - "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - - "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], - - "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], - - "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], - - "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], - - "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - - "side-channel": ["side-channel@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4", "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ=="], - - "side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="], - - "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], - - "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], - - "smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="], - - "socks": ["socks@2.8.10", "", { "dependencies": { "ip-address": "^10.1.1", "smart-buffer": "^4.2.0" } }, "sha512-e0VyvkVTwVYViNovRkZ9aodhxVlyoMn7eJhVUPxZ+eK9P/7CBkxvvsBOHqFPEH416726W8tLXXXjKwqgTErrCQ=="], - - "split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="], - - "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], - - "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], - - "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], - - "tslib": ["tslib@2.9.1", "", {}, "sha512-a4Ux9VKuIMHsOS7ENbEq6hK2ZGYIT27Rtl0E4eD9KqwxhKiQeMZE/9xb3IdYwWaSOyIBQLExhQgXDTYoiCxnWA=="], - - "tsyringe": ["tsyringe@4.10.0", "", { "dependencies": { "tslib": "^1.9.3" } }, "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw=="], - - "type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="], - - "typedarray": ["typedarray@0.0.6", "", {}, "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="], - - "typescript": ["typescript@7.0.2", "", { "optionalDependencies": { "@typescript/typescript-aix-ppc64": "7.0.2", "@typescript/typescript-darwin-arm64": "7.0.2", "@typescript/typescript-darwin-x64": "7.0.2", "@typescript/typescript-freebsd-arm64": "7.0.2", "@typescript/typescript-freebsd-x64": "7.0.2", "@typescript/typescript-linux-arm": "7.0.2", "@typescript/typescript-linux-arm64": "7.0.2", "@typescript/typescript-linux-loong64": "7.0.2", "@typescript/typescript-linux-mips64el": "7.0.2", "@typescript/typescript-linux-ppc64": "7.0.2", "@typescript/typescript-linux-riscv64": "7.0.2", "@typescript/typescript-linux-s390x": "7.0.2", "@typescript/typescript-linux-x64": "7.0.2", "@typescript/typescript-netbsd-arm64": "7.0.2", "@typescript/typescript-netbsd-x64": "7.0.2", "@typescript/typescript-openbsd-arm64": "7.0.2", "@typescript/typescript-openbsd-x64": "7.0.2", "@typescript/typescript-sunos-x64": "7.0.2", "@typescript/typescript-win32-arm64": "7.0.2", "@typescript/typescript-win32-x64": "7.0.2" }, "bin": { "tsc": "bin/tsc" } }, "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA=="], - - "undici-types": ["undici-types@8.9.0", "", {}, "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg=="], - - "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], - - "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], - - "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], - - "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - - "worker-factory": ["worker-factory@7.0.50", "", { "dependencies": { "@babel/runtime": "^7.29.7", "fast-unique-numbers": "^9.0.27", "tslib": "^2.8.1" } }, "sha512-hhwc0G+sFwM4qBuhJIUBn2p1Jf8v/FwmLUANBf/Q+Lt2uI8mfIZQhXaZQACodQD4R7Zp6cn/6702bIvNn2puJQ=="], - - "worker-timers": ["worker-timers@8.0.34", "", { "dependencies": { "@babel/runtime": "^7.29.7", "tslib": "^2.8.1", "worker-timers-broker": "^8.0.18", "worker-timers-worker": "^9.0.15" } }, "sha512-WXL+Dqsm0G6dnC66rQsvM3tPT2adhbqSirWUCZGglkALOr8ocS0KlpU0iuKbjflJOlu3ydZikMEVrYnHOM9suw=="], - - "worker-timers-broker": ["worker-timers-broker@8.0.18", "", { "dependencies": { "@babel/runtime": "^7.29.7", "broker-factory": "^3.1.15", "fast-unique-numbers": "^9.0.27", "tslib": "^2.8.1", "worker-timers-worker": "^9.0.15" } }, "sha512-FrjzDVX1wKfZN0gRbCFqv8VHuTncG4sbI/WGEg4tSSQeIsnwqg4YBYWMAHYLJtUDEYYmiK65UKFrVMVk2irDSg=="], - - "worker-timers-worker": ["worker-timers-worker@9.0.15", "", { "dependencies": { "@babel/runtime": "^7.29.7", "tslib": "^2.8.1", "worker-factory": "^7.0.50" } }, "sha512-KKUe7lZ/Aignr51H6hOUik8LwTnIgojH/1lwhli8A8qIEIyewogZTpNpMW5B6BF7nmwBOkUoTYgf1H3QShcjSA=="], - - "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], - - "ws": ["ws@8.22.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-oyL6OPYme/CmiFvNuGzeoxCJ4nOrZE2O1FwM9MWM1AsGri6nLXB2pg+lNA8aLeXMSav0J6eIVS1j6vUPzUtK9g=="], - - "zod": ["zod@4.6.7", "", {}, "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg=="], - - "zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], - - "body-parser/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], - - "concat-stream/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], - - "express/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], - - "negotiator/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], - - "tsyringe/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], - - "type-is/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], - } -} diff --git a/apps/server/package.json b/apps/server/package.json index cf94265..7eb757c 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -1,7 +1,6 @@ { "name": "snapraid-ui-backend", "private": true, - "packageManager": "bun@1.4.4", "type": "module", "scripts": { "dev": "bun --watch src/main.ts", @@ -12,6 +11,7 @@ "dependencies": { "@modelcontextprotocol/sdk": "^1.32.1", "@simplewebauthn/server": "^14.0.3", + "@snapraid-ui/shared": "workspace:*", "croner": "^11.0.0", "hono": "^4.13.9", "mqtt": "^5.14.1", diff --git a/apps/server/scripts/seed-demo.ts b/apps/server/scripts/seed-demo.ts index 2229134..56c54ff 100644 --- a/apps/server/scripts/seed-demo.ts +++ b/apps/server/scripts/seed-demo.ts @@ -1,11 +1,11 @@ -// Gives the demo sandbox a past, run by dev/setup.sh after its real SnapRAID jobs: +// Gives the demo sandbox a past, run by scripts/dev/setup.sh after its real SnapRAID jobs: // - the job logs (logs/run--.log, in the order they ran) move back in time, so // the dashboard shows the last sync and scrub, and the Logs page a history // - usage-history.json: two months of disk usage, one disk filling up // - schedules.json: a nightly sync with touch and scrub, a weekly scrub, a SMART check // - maintenance.json: Docker pause and spindown, on the demo's own containers and disks // -// bun scripts/seed-demo.ts (in backend/, whose packages it uses) +// bun scripts/seed-demo.ts (in apps/server/, whose packages it uses) import { readdir, readFile, rm, utimes, writeFile } from "node:fs/promises"; import { join } from "node:path"; import { Cron } from "croner"; @@ -123,7 +123,7 @@ const schedules: Schedule[] = [ ]; await writeFile(join(sandbox, "schedules.json"), JSON.stringify({ schedules }, null, 2)); -// The demo answers for Docker itself (backend/src/demo.ts), spindown watches the demo disks +// The demo answers for Docker itself (apps/server/src/demo.ts), spindown watches the demo disks // The rest (metrics, MCP) takes its defaults when the backend loads the file const maintenance: Pick = { dockerPause: { diff --git a/apps/server/src/__tests__/engine-contract.test.ts b/apps/server/src/__tests__/engine-contract.test.ts index 7a94736..1543a5c 100644 --- a/apps/server/src/__tests__/engine-contract.test.ts +++ b/apps/server/src/__tests__/engine-contract.test.ts @@ -21,7 +21,7 @@ engineContract("fake", () => { }); // The test disks are directories on one filesystem, SnapRAID only accepts that with --test-skip-device. -// Run it with: SNAPRAID_BIN=../dev/bin/snapraid SNAPRAID_EXTRA_ARGS=--test-skip-device bun test +// Run it with: SNAPRAID_BIN=../../scripts/dev/bin/snapraid SNAPRAID_EXTRA_ARGS=--test-skip-device bun test const cliAvailable = await (async () => { if (!SNAPRAID_EXTRA_ARGS.includes("--test-skip-device")) return false; try { diff --git a/apps/server/src/__tests__/version.test.ts b/apps/server/src/__tests__/version.test.ts index 5232ceb..e3cf9a6 100644 --- a/apps/server/src/__tests__/version.test.ts +++ b/apps/server/src/__tests__/version.test.ts @@ -17,7 +17,7 @@ const withEnv = async (value: string | undefined, run: () => Promise) => { } }; -test("readVersion - the version of frontend/package.json", async () => { +test("readVersion - the version of apps/web/package.json", async () => { const { version } = JSON.parse(await readFile(PACKAGE_JSON, "utf8")); await withEnv(undefined, async () => expect(await readVersion()).toEqual(version)); }); diff --git a/apps/server/src/app-files.ts b/apps/server/src/app-files.ts index ea1c79e..0176d12 100644 --- a/apps/server/src/app-files.ts +++ b/apps/server/src/app-files.ts @@ -1,11 +1,11 @@ -// Files of SnapRAID UI outside backend/: the built frontend, its package.json (the version) and the -// documentation. In the repository and the image they sit next to backend/; the standalone binary +// Files of SnapRAID UI outside apps/server/: the built frontend, its package.json (the version) and +// the documentation. In the repository and the image they sit at their paths from the root; the standalone binary // (scripts/build-binary.sh) embeds them at the same paths in its own file system, /$bunfs/root/ const COMPILED = import.meta.url.startsWith("file:///$bunfs/"); -const ROOT = COMPILED ? new URL("file:///$bunfs/root/") : new URL("../../", import.meta.url); +const ROOT = COMPILED ? new URL("file:///$bunfs/root/") : new URL("../../../", import.meta.url); /** - * A file of the app by its path from the repository's root, e.g. "frontend/package.json" + * A file of the app by its path from the repository's root, e.g. "apps/web/package.json" */ export const appFile = (path: string): URL => new URL(path, ROOT); diff --git a/apps/server/src/bin-cli.ts b/apps/server/src/bin-cli.ts index d89bf20..491e777 100644 --- a/apps/server/src/bin-cli.ts +++ b/apps/server/src/bin-cli.ts @@ -30,5 +30,5 @@ if (arg !== undefined) { process.exit(2); } -// The repository's default (../snapraid next to backend/) means nothing for a binary +// The repository's default (snapraid/ in the repository's root) means nothing for a binary if (!process.env.SNAPRAID_BASE_PATH) process.env.SNAPRAID_BASE_PATH = join(process.cwd(), "snapraid"); diff --git a/apps/server/src/config.ts b/apps/server/src/config.ts index c37aed6..aba95ca 100644 --- a/apps/server/src/config.ts +++ b/apps/server/src/config.ts @@ -1,6 +1,7 @@ import { isAbsolute, join, relative, resolve } from "node:path"; -export const BASE_PATH = process.env.SNAPRAID_BASE_PATH || join(process.cwd(), "..", "snapraid"); +// Without the variable: snapraid/ in the repository's root, two levels up from apps/server/ +export const BASE_PATH = process.env.SNAPRAID_BASE_PATH || join(process.cwd(), "..", "..", "snapraid"); /** * Resolve a path against BASE_PATH; absolute paths (e.g. /mnt/parity/snapraid.conf) are kept as is diff --git a/apps/server/src/demo.ts b/apps/server/src/demo.ts index e0388e0..4cfc11d 100644 --- a/apps/server/src/demo.ts +++ b/apps/server/src/demo.ts @@ -1,4 +1,4 @@ -// Stand-ins for `snapraid smart` and `probe` in the demo sandbox (./start.sh --demo). +// Stand-ins for `snapraid smart` and `probe` in the demo sandbox (scripts/start.sh --demo). // Its disks are directories, so SnapRAID finds no device to query. The output below has // the structured log format of the real commands and goes through the same parsers. // The sandbox disks also hold only a few KiB on the host's filesystem: `status` and the diff --git a/apps/server/src/frontend.ts b/apps/server/src/frontend.ts index a1e5a5f..b17f194 100644 --- a/apps/server/src/frontend.ts +++ b/apps/server/src/frontend.ts @@ -7,8 +7,8 @@ import { appFile } from "./app-files.ts"; export type FetchHandler = (request: Request) => Response | Promise; -// Next to the backend as in the repository and the Docker image: frontend/.output/server/index.mjs -export const FRONTEND_ENTRY = appFile("frontend/.output/server/index.mjs"); +// Next to the backend as in the repository and the Docker image: apps/web/.output/server/index.mjs +export const FRONTEND_ENTRY = appFile("apps/web/.output/server/index.mjs"); // The backend's own paths, never passed on to the frontend export const isBackendPath = (path: string): boolean => /^\/(api|ws)(\/|$)/.test(path); diff --git a/apps/server/src/mcp.ts b/apps/server/src/mcp.ts index 028e300..ca17b38 100644 --- a/apps/server/src/mcp.ts +++ b/apps/server/src/mcp.ts @@ -76,7 +76,7 @@ const ok = (data: Json | string): CallToolResult => ({ const fail = (message: string): CallToolResult => ({ content: [{ type: "text", text: message }], isError: true }); -// Messages meant for the web UI arrive as keys, see shared/i18n.ts; an agent gets the key and inputs +// Messages meant for the web UI arrive as keys, see packages/shared/src/i18n.ts; an agent gets the key and inputs const plain = (text: string) => localize(text, (key, inputs) => Object.keys(inputs).length ? `${key} (${JSON.stringify(inputs)})` : key); diff --git a/apps/server/src/version.ts b/apps/server/src/version.ts index d9a7b2a..44e9c82 100644 --- a/apps/server/src/version.ts +++ b/apps/server/src/version.ts @@ -1,9 +1,9 @@ -// The version of SnapRAID UI, the one in frontend/package.json that the release raises. The image +// The version of SnapRAID UI, the one in apps/web/package.json that the release raises. The image // and the binary carry that file next to the frontend's build; SNAPRAID_UI_VERSION overrides it import { readFile } from "node:fs/promises"; import { appFile } from "./app-files.ts"; -export const PACKAGE_JSON = appFile("frontend/package.json"); +export const PACKAGE_JSON = appFile("apps/web/package.json"); export const readVersion = async (packageJson: URL = PACKAGE_JSON): Promise => { const override = process.env.SNAPRAID_UI_VERSION; diff --git a/apps/server/tsconfig.json b/apps/server/tsconfig.json index 908f039..84f3c7b 100644 --- a/apps/server/tsconfig.json +++ b/apps/server/tsconfig.json @@ -1,19 +1,18 @@ { + "extends": "../../tsconfig.base.json", "compilerOptions": { "target": "ESNext", "module": "Preserve", - "moduleResolution": "bundler", "moduleDetection": "force", "lib": ["ESNext"], "types": ["bun"], "allowImportingTsExtensions": true, "verbatimModuleSyntax": false, - "noEmit": true, - "strict": true, - "skipLibCheck": true, + // @shared/… is the workspace package @snapraid-ui/shared (packages/shared/src); Bun + // resolves these paths at runtime too "paths": { - "@shared/*": ["../shared/*"] + "@shared/*": ["./node_modules/@snapraid-ui/shared/src/*"] } }, - "include": ["src", "scripts", "../shared"] + "include": ["src", "scripts", "../../packages/shared/src"] } diff --git a/apps/web/package.json b/apps/web/package.json index 415ab77..2c3d147 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -3,7 +3,6 @@ "version": "1.3.3", "license": "AGPL-3.0-only", "private": true, - "packageManager": "bun@1.4.4", "type": "module", "scripts": { "dev": "bun --bun vite dev --port 3000", @@ -22,6 +21,7 @@ "@fontsource-variable/geist": "^5.3.0", "@fontsource-variable/geist-mono": "^5.3.0", "@simplewebauthn/browser": "^14.0.0", + "@snapraid-ui/shared": "workspace:*", "@tailwindcss/vite": "^4.3.3", "@tanstack/query-sync-storage-persister": "^5.104.0", "@tanstack/react-devtools": "^0.10.13", diff --git a/apps/web/src/components/AccountDialog.tsx b/apps/web/src/components/AccountDialog.tsx index 2b1ed88..9ccf010 100644 --- a/apps/web/src/components/AccountDialog.tsx +++ b/apps/web/src/components/AccountDialog.tsx @@ -31,7 +31,7 @@ import { import { Input } from './ui/input' import { Label } from './ui/label' -// The backend's minimum (MIN_PASSWORD_LENGTH in backend/src/auth.ts) +// The backend's minimum (MIN_PASSWORD_LENGTH in apps/server/src/auth.ts) const MIN_PASSWORD_LENGTH = 12 const MAX_NAME_LENGTH = 64 diff --git a/apps/web/src/components/RemoveDataDiskWizard.tsx b/apps/web/src/components/RemoveDataDiskWizard.tsx index 72e1b17..ce47ef3 100644 --- a/apps/web/src/components/RemoveDataDiskWizard.tsx +++ b/apps/web/src/components/RemoveDataDiskWizard.tsx @@ -26,7 +26,7 @@ import { DialogTitle, } from './ui/dialog' -// Must match REMOVAL_DIR in backend/src/config-parser.ts +// Must match REMOVAL_DIR in apps/server/src/config-parser.ts const REMOVAL_DIR = '.snapraid-removal' const JOB_POLL_MS = 2000 diff --git a/apps/web/src/lib/__tests__/i18n.test.ts b/apps/web/src/lib/__tests__/i18n.test.ts index dcfb934..eb21fb6 100644 --- a/apps/web/src/lib/__tests__/i18n.test.ts +++ b/apps/web/src/lib/__tests__/i18n.test.ts @@ -50,7 +50,7 @@ describe('messages/*.json', () => { it('every message is used, every used key exists', () => { const frontend = files('src').map((f) => readFileSync(f, 'utf8')) - const backend = files('../backend/src').map((f) => readFileSync(f, 'utf8')) + const backend = files('../server/src').map((f) => readFileSync(f, 'utf8')) const code = [...frontend, ...backend].join('\n') // m.key() only counts in files that import the messages as m (elsewhere m is often a regex) const mCode = frontend @@ -75,7 +75,7 @@ describe('messages/*.json', () => { }) it('backend messages are server_ keys, the only ones the UI can render', () => { - const backend = files('../backend/src') + const backend = files('../server/src') .map((f) => readFileSync(f, 'utf8')) .join('\n') const keys = [...backend.matchAll(/\bmsg\(\s*["'](\w+)["']/g)].map( diff --git a/apps/web/src/lib/__tests__/log-parse.test.ts b/apps/web/src/lib/__tests__/log-parse.test.ts index a7930a6..1513bdd 100644 --- a/apps/web/src/lib/__tests__/log-parse.test.ts +++ b/apps/web/src/lib/__tests__/log-parse.test.ts @@ -5,7 +5,7 @@ import { classifyLogLine, parseLog } from '../log-parse' const fixture = (name: string) => readFileSync( new URL( - `../../../../backend/src/parsers/__tests__/fixtures/${name}`, + `../../../../server/src/parsers/__tests__/fixtures/${name}`, import.meta.url, ), 'utf8', diff --git a/apps/web/src/lib/__tests__/run-result.test.ts b/apps/web/src/lib/__tests__/run-result.test.ts index 0f7396e..6070650 100644 --- a/apps/web/src/lib/__tests__/run-result.test.ts +++ b/apps/web/src/lib/__tests__/run-result.test.ts @@ -5,7 +5,7 @@ import { describe, expect, it } from 'vitest' const fixture = (name: string) => readFileSync( new URL( - `../../../../backend/src/parsers/__tests__/fixtures/${name}`, + `../../../../server/src/parsers/__tests__/fixtures/${name}`, import.meta.url, ), 'utf8', diff --git a/apps/web/src/lib/i18n.ts b/apps/web/src/lib/i18n.ts index 30a79be..92ef42b 100644 --- a/apps/web/src/lib/i18n.ts +++ b/apps/web/src/lib/i18n.ts @@ -2,7 +2,7 @@ import { localize, type MsgInputs } from '@shared/i18n' type Message = (inputs: MsgInputs) => string -// The backend only sends server_* keys (msg() in shared/i18n.ts). Importing +// The backend only sends server_* keys (msg() in @shared/i18n.ts). Importing // just those message modules, instead of looking keys up on the whole // `import * as m` namespace, keeps the other messages tree-shakable: a lookup // by a runtime key pulled every message in all locales into the entry bundle. @@ -17,7 +17,7 @@ const messages: Record = Object.assign( ) /** - * Renders the messages the backend sends as keys (msg() in shared/i18n.ts) + * Renders the messages the backend sends as keys (msg() in @shared/i18n.ts) * in the language of the UI. Plain text passes through unchanged. */ export const localizeServer = (text: string): string => diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index b38408c..6437b68 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -1,4 +1,5 @@ { + "extends": "../../tsconfig.base.json", "include": ["**/*.ts", "**/*.tsx", "**/*.js"], "compilerOptions": { "target": "ES2022", @@ -8,22 +9,18 @@ "types": ["vite/client"], /* Bundler mode */ - "moduleResolution": "bundler", "allowImportingTsExtensions": true, "allowJs": true, "verbatimModuleSyntax": false, - "noEmit": true, /* Linting */ - "skipLibCheck": true, - "strict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, "noUncheckedSideEffectImports": true, "paths": { "@/*": ["./src/*"], - "@shared/*": ["../shared/*"] + "@shared/*": ["./node_modules/@snapraid-ui/shared/src/*"] } } } diff --git a/apps/web/bun.lock b/bun.lock similarity index 88% rename from apps/web/bun.lock rename to bun.lock index 7ec3c82..a5720d5 100644 --- a/apps/web/bun.lock +++ b/bun.lock @@ -3,11 +3,34 @@ "configVersion": 1, "workspaces": { "": { + "name": "snapraid-ui-workspace", + }, + "apps/server": { + "name": "snapraid-ui-backend", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.32.1", + "@simplewebauthn/server": "^14.0.3", + "@snapraid-ui/shared": "workspace:*", + "croner": "^11.0.0", + "hono": "^4.13.9", + "mqtt": "^5.14.1", + "nodemailer": "^10.0.13", + "zod": "^4.1.12", + }, + "devDependencies": { + "@types/bun": "^1.4.4", + "@types/nodemailer": "^8.0.2", + "typescript": "^7.0.2", + }, + }, + "apps/web": { "name": "snapraid-ui", + "version": "1.3.3", "dependencies": { "@fontsource-variable/geist": "^5.3.0", "@fontsource-variable/geist-mono": "^5.3.0", "@simplewebauthn/browser": "^14.0.0", + "@snapraid-ui/shared": "workspace:*", "@tailwindcss/vite": "^4.3.3", "@tanstack/query-sync-storage-persister": "^5.104.0", "@tanstack/react-devtools": "^0.10.13", @@ -52,6 +75,19 @@ "web-vitals": "^6.2.2", }, }, + "e2e": { + "name": "snapraid-ui-e2e", + "devDependencies": { + "@playwright/test": "1.64.1", + "@types/node": "^26.0.0", + "aedes": "^1.0.0", + "mqtt": "^5.14.1", + "typescript": "^7.0.0", + }, + }, + "packages/shared": { + "name": "@snapraid-ui/shared", + }, }, "packages": { "@apideck/better-ajv-errors": ["@apideck/better-ajv-errors@0.3.7", "", { "dependencies": { "jsonpointer": "^5.0.1", "leven": "^3.1.0" }, "peerDependencies": { "ajv": ">=8" } }, "sha512-TajUJwGWbDwkCx/CZi7tRE8PVB7simCvKJfHUsSdvps+aTM/PDPP4gkLmKnc+x3CE//y9i/nj74GqdL/hwk7Iw=="], @@ -324,6 +360,10 @@ "@fontsource-variable/geist-mono": ["@fontsource-variable/geist-mono@5.3.0", "", {}, "sha512-vBbuwDEo9AkrqADMXOrlAR3DFcJi4/JxeuU43FoiQERnNwsfXNnvxvReZG02cQKmyk4DZkZdBZX3oTDvy2zBAw=="], + "@hexagon/base64": ["@hexagon/base64@1.1.28", "", {}, "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw=="], + + "@hono/node-server": ["@hono/node-server@2.1.6", "", { "peerDependencies": { "hono": "^4 || ^5.0.0-0" } }, "sha512-p/KH9MnfNcsm3sR6x4lNgm5j+QN4Et8srKredl9DZfXiyZ1UQ3aQ2YVpFLbqktYN5JI+DB+VZFKM2PqE5hLKjQ=="], + "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.35.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.3.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg=="], @@ -402,8 +442,12 @@ "@kurkle/color": ["@kurkle/color@0.3.4", "", {}, "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w=="], + "@levischuck/tiny-cbor": ["@levischuck/tiny-cbor@0.2.11", "", {}, "sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow=="], + "@lix-js/sdk": ["@lix-js/sdk@0.20.1", "", { "dependencies": { "@bytecodealliance/jco-transpile": "0.13.0", "binaryen": "130.0.0", "fflate": "^0.8.3" } }, "sha512-oscOFP8Cr1My6Wh2KVYC9+aJ1mHs+d7vnD+7PPJaA4L+Y1FR37Sm99SrFGfvUizaj/XtRi2GlDF2IOFt9iXMNQ=="], + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.32.2", "", { "dependencies": { "@hono/node-server": "^1.19.9 || ^2.0.5", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-SWsVtH/0vULSQStoZ6xxH2K09Li8fXYTMC64ZeogCdpZ3XSi1eCPOByV1OpuJxuI3OcmHhosDWYyii07tpPsLw=="], + "@napi-rs/lzma-linux-x64-gnu": ["@napi-rs/lzma-linux-x64-gnu@1.5.1", "", { "os": "linux", "cpu": "x64", "libc": "glibc" }, "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ=="], "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.2.7", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1 || 2.0.0-beta.2 || 2.0.0-beta.3", "@emnapi/runtime": "^1.7.1 || 2.0.0-beta.2 || 2.0.0-beta.3" } }, "sha512-zJccwplOAbgfZju95IUHazLnx9RAPtPN2YGJXI6UbrgeaR9QfPjGrQL6ERkSAtnVCA4Q+83ApGyaX5zdpCaKkQ=="], @@ -502,6 +546,38 @@ "@oxc-project/types": ["@oxc-project/types@0.156.0", "", {}, "sha512-1Duyg48TdJNdyhEB8Xn1L36bT9Pv6GUtlKjHxOys8xkaF/rQq3XJE+bAurSK/AgIz5mGFsb77cn9Pb20+WCScw=="], + "@peculiar/asn1-android": ["@peculiar/asn1-android@2.11.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-AuUBRDNdWrgtnuJGXARa0Xdtq6OAD8GqezDQz6Qka7kyynMo9fzkcrtpBEHuX984uEB570BrhMZfJfkUDfpQfw=="], + + "@peculiar/asn1-asym-key": ["@peculiar/asn1-asym-key@2.11.1", "", { "dependencies": { "@peculiar/asn1-pkcs8": "^2.11.1", "@peculiar/asn1-schema": "^2.11.1", "@peculiar/asn1-x509": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-YtXR0FbDtjHuNGvWXD4HFE7th5xYwG67O+ApDH/FZsjj5MOrWHzDvqwZ1vm6uFUJtMAkUDcF6VMPGOJ5gnLyUA=="], + + "@peculiar/asn1-cms": ["@peculiar/asn1-cms@2.11.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.11.1", "@peculiar/asn1-x509": "^2.11.1", "@peculiar/asn1-x509-attr": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-Bk+oatE62dW0Tzzpfk6vdQxfGGEfvuGdWgmijnKjcv7HqZCJISs85z4zw2uDArrk+R1Oel1wts/BBl7HHrtAcA=="], + + "@peculiar/asn1-csr": ["@peculiar/asn1-csr@2.11.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.11.1", "@peculiar/asn1-x509": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-KVfbXaKng8886/tOPG55t5K1XkTkGbR4amjIJzytXWobFU5nZgw/sKnSpa1hxP17nUSsrOF/7Mt9WUYeyLsi6Q=="], + + "@peculiar/asn1-ecc": ["@peculiar/asn1-ecc@2.11.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.11.1", "@peculiar/asn1-x509": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-7MSNji77fQa/oN3WAfwagMy8EoJFg50sW4hZ1dtr0zU1BSugit0I2DGpIdPhvkNplopQhz5bogxpn1077eaamA=="], + + "@peculiar/asn1-pfx": ["@peculiar/asn1-pfx@2.11.1", "", { "dependencies": { "@peculiar/asn1-cms": "^2.11.1", "@peculiar/asn1-pkcs8": "^2.11.1", "@peculiar/asn1-rsa": "^2.11.1", "@peculiar/asn1-schema": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-2SN2z8qkc2hk/GgTYA8RN54wtsCDZShg9gI2Mq1vLhdj1adcv5fhTbiEmExZUaAMqlizxiEpULQDfHM5iYtn1g=="], + + "@peculiar/asn1-pkcs8": ["@peculiar/asn1-pkcs8@2.11.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.11.1", "@peculiar/asn1-x509": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-HqsSrnpQghRp9SM9QSmR6fa4z/BNktnKq7928+oq4qF0VAnuHZ4cyePKvcxh9fP4taBCP/WHNItlVNsYaQm2qw=="], + + "@peculiar/asn1-pkcs9": ["@peculiar/asn1-pkcs9@2.11.1", "", { "dependencies": { "@peculiar/asn1-cms": "^2.11.1", "@peculiar/asn1-pfx": "^2.11.1", "@peculiar/asn1-pkcs8": "^2.11.1", "@peculiar/asn1-schema": "^2.11.1", "@peculiar/asn1-x509": "^2.11.1", "@peculiar/asn1-x509-attr": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-OHFkTg8D2zYafILqFE4KPbqX8srpvd3diGDc2h2cduumawc765mOHGcuFMGvLWlL2bbCMWMmMSCtlXptsOuiPA=="], + + "@peculiar/asn1-rsa": ["@peculiar/asn1-rsa@2.11.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.11.1", "@peculiar/asn1-x509": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-6KwKGjulF489CpidZqkogNgLj6oGzgC4BL2FClM8PuJQ4t5NqT4q4a6A4vrSxBIBhMnL8hGYkLOpbVw0Vq38Tw=="], + + "@peculiar/asn1-schema": ["@peculiar/asn1-schema@2.11.1", "", { "dependencies": { "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-jaU9OaCcbZUdyXx9hMETZB9xoNmxTGjOGFqd3cOwKFkpKnyKd9WxF3uqj4PvX4cyOMP9bSdHg5YyML2qtx/d/g=="], + + "@peculiar/asn1-x509": ["@peculiar/asn1-x509@2.11.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.11.1", "@peculiar/utils": "^2.0.2", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-SXU4/X6B3FquqQAN6ARI0bFDUV7+L1WlvPEPMKzEMkstd2D7UyM0r93fv241+OjdhqAKYGx5MvV3gzZMAp0APA=="], + + "@peculiar/asn1-x509-attr": ["@peculiar/asn1-x509-attr@2.11.1", "", { "dependencies": { "@peculiar/asn1-schema": "^2.11.1", "@peculiar/asn1-x509": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-n7RKFrbTzt8pGBlZ90kmKEn1qsLTTyw7lrvHMK0xsA42kQugPk/GXYpNbEcl7DkkdVsU/one5UX7KQPfxLj8vQ=="], + + "@peculiar/asn1-x509-post-quantum": ["@peculiar/asn1-x509-post-quantum@2.11.1", "", { "dependencies": { "@peculiar/asn1-asym-key": "^2.11.1", "@peculiar/asn1-schema": "^2.11.1", "@peculiar/asn1-x509": "^2.11.1", "asn1js": "^3.0.10", "tslib": "^2.8.1" } }, "sha512-yD/pxCQ97nzwF7MGAW4kRd1WVLiMPhD39HxOnAr38DfGFwbpVhPnMbq+r6WX0XUrxn4w8ulsTdUafwVl46igLA=="], + + "@peculiar/utils": ["@peculiar/utils@2.0.3", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ=="], + + "@peculiar/x509": ["@peculiar/x509@2.2.0", "", { "dependencies": { "@peculiar/asn1-cms": "^2.11.0", "@peculiar/asn1-csr": "^2.11.0", "@peculiar/asn1-ecc": "^2.11.0", "@peculiar/asn1-pkcs9": "^2.11.0", "@peculiar/asn1-rsa": "^2.11.0", "@peculiar/asn1-schema": "^2.11.0", "@peculiar/asn1-x509": "^2.11.0", "@peculiar/asn1-x509-post-quantum": "^2.11.0", "pvtsutils": "^1.3.6", "tslib": "^2.8.1", "tsyringe": "^4.10.0" } }, "sha512-adpW58ySPQMOUPv8pTKT3RIyCFqfUFt8X05ukPdtkQToXDVr8dMtxuenik1yx8tNFTbpApWsM/Hi5SzlIx48+Q=="], + + "@playwright/test": ["@playwright/test@1.64.1", "", { "dependencies": { "playwright": "1.64.1" }, "bin": { "playwright": "cli.js" } }, "sha512-IaYGml/nSOiZWROfPtuIL7neuu9qDOZ3TNvNljUIrhDMxiGTgPjzj/g9p9RxpS2tSepPDRY+UwAaEeiKtUvIAg=="], + "@quansync/fs": ["@quansync/fs@1.1.0", "", { "dependencies": { "quansync": "^1.0.0" } }, "sha512-qAPG/t3HqML1TlN7sY/pTbEjzFVAKsMjNNMGheyDosro+kT4iw2KCUoHcVdmliwWjorm4elZbgNQyU2eD97eDg=="], "@radix-ui/number": ["@radix-ui/number@1.1.3", "", {}, "sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA=="], @@ -720,8 +796,12 @@ "@simplewebauthn/browser": ["@simplewebauthn/browser@14.0.0", "", {}, "sha512-1odWVqeEBTl7lJ9zMKLEsmTlnyrDO5iRcTvfMKKk1WThUnp/i8JJdffdj2icP+tty159s4PgwE3BiMoEW9NFow=="], + "@simplewebauthn/server": ["@simplewebauthn/server@14.0.3", "", { "dependencies": { "@hexagon/base64": "^1.1.27", "@levischuck/tiny-cbor": "^0.2.2", "@peculiar/asn1-android": "^2.6.0", "@peculiar/asn1-ecc": "^2.6.1", "@peculiar/asn1-rsa": "^2.6.1", "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.1", "@peculiar/asn1-x509-post-quantum": "^2.9.4", "@peculiar/x509": "^2.1.0", "reflect-metadata": "^0.2.2" } }, "sha512-D39/0fiDiYmGV+nraY9s63D1Ho2AX/waMLy22/9gUadTUuvbAnstfb4bpbHYbwZuV5ZLDYN1SmzDKuiHzKAA4Q=="], + "@sinclair/typebox": ["@sinclair/typebox@0.31.30", "", {}, "sha512-MGsM7bVmHg3sUKCphlu3SGQ+T+5JTbygZWYArfKQCW5anJeACHeqLhcnf+R7XlCWZ+QR3C8JTBx3kCKJTN69ow=="], + "@snapraid-ui/shared": ["@snapraid-ui/shared@workspace:packages/shared"], + "@solid-primitives/event-listener": ["@solid-primitives/event-listener@2.4.6", "", { "dependencies": { "@solid-primitives/utils": "^6.4.1" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-5I0YJcTVYIWoMmgBSROBZGcz+ymhew/pGTg2dHW74BUjFKsV8Li4bOZYl0YAGP4mHw5o4UBd9/BEesqBci3wxw=="], "@solid-primitives/keyboard": ["@solid-primitives/keyboard@1.3.7", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.6", "@solid-primitives/rootless": "^1.5.4", "@solid-primitives/utils": "^6.4.1" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-558RPNYnXx4nGh537DSqAn4xMrC8iFipl/5+xzgzWoTNFst4RnUN3BOLmtDjJ0UGGoQXVMALYR3bNOHM0xnt1Q=="], @@ -838,6 +918,8 @@ "@tybys/wasm-util": ["@tybys/wasm-util@0.10.4", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A=="], + "@types/bun": ["@types/bun@1.4.4", "", { "dependencies": { "bun-types": "1.4.4" } }, "sha512-9wkRDXUXK124pEnAutE6n/l18j+i9GC5IAHK8TmX/2nlySETmky0OU6yL5U8pW1AoKh++AN9auDCr647BfurjQ=="], + "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], @@ -846,14 +928,20 @@ "@types/node": ["@types/node@26.6.6", "", { "dependencies": { "undici-types": "~8.9.0" } }, "sha512-MzQj+8Kx3XI40OmUAE6Qoh58ZMNstNNMwn2nfd3qWsfe7nDNSqJNzes4njhz6AHwrSHOffSLwXU6yb474Q4aog=="], + "@types/nodemailer": ["@types/nodemailer@8.0.2", "", { "dependencies": { "@types/node": "*" } }, "sha512-c7M5ox8p0nEOfbJ2E9Qcmt8/QCu/VzsiAhzBiZbvky2PhKZDHpKB3sQHBM5MEZNkCfBOaS6S9//AGPRTNB7IaA=="], + "@types/react": ["@types/react@19.3.0", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg=="], "@types/react-dom": ["@types/react-dom@19.3.0", "", { "peerDependencies": { "@types/react": "^19.3.0" } }, "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q=="], + "@types/readable-stream": ["@types/readable-stream@4.0.25", "", { "dependencies": { "@types/node": "*" } }, "sha512-7GsxL8Wd5X3jh73/qgW7WCnbBvGSdD8U3E4Jiuus3/8I+PN20DtlkJtwFhr/sImXfDDQ2Z03i/9wNiIF3TVSsw=="], + "@types/resolve": ["@types/resolve@1.20.2", "", {}, "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="], "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], + "@types/ws": ["@types/ws@8.18.2", "", { "dependencies": { "@types/node": "*" } }, "sha512-67MQl+fpWKVTT1NYdnmo3U4sc/xPo/zQBncVnI74qmQa0z/b+1g6iYqNmGCPbxO+zz2aklb08a0oHfegiVd0/w=="], + "@typescript/typescript-aix-ppc64": ["@typescript/typescript-aix-ppc64@7.0.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ=="], "@typescript/typescript-darwin-arm64": ["@typescript/typescript-darwin-arm64@7.0.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA=="], @@ -902,10 +990,22 @@ "@vitest/spy": ["@vitest/spy@5.1.0", "", {}, "sha512-2DPO5OMHWKrBngC+fCmTQqemNF0gidRhp3lxbBmRhW+M1XJrfgPpqNqWVevXtn2f2ufD5ZaR1B6B641aAlcX5A=="], + "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], + + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], + "acorn": ["acorn@8.19.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw=="], + "aedes": ["aedes@1.2.0", "", { "dependencies": { "aedes-packet": "^4.0.1", "aedes-persistence": "^11.0.0", "mqemitter": "^7.1.0", "mqtt-packet": "^9.0.2" }, "peerDependencies": { "aedes-persistence-level": "^9.1.2", "aedes-persistence-mongodb": "^9.3.1", "aedes-persistence-redis": "^11.2.1" }, "optionalPeers": ["aedes-persistence-level", "aedes-persistence-mongodb", "aedes-persistence-redis"] }, "sha512-fCgK7TzmP2zGYS2iI/RE1tjHAIIqq5MjeOx3bicd89aTWTac+wiaywwIWhQQK6CXS+ml7ssGz8NntKYL5jPaZA=="], + + "aedes-packet": ["aedes-packet@4.0.1", "", { "dependencies": { "mqtt-packet": "^9.0.0" } }, "sha512-PLjKJElXEYB66RzHhJCw0SrRLfSmGRbbg2mtO5t/6FAbFp8vC0cG5ErhvPvfa9iKBrKZwoluWLgn8doBU8sRAw=="], + + "aedes-persistence": ["aedes-persistence@11.0.0", "", { "dependencies": { "aedes-packet": "^4.0.1", "qlobber": "^8.0.1" } }, "sha512-6ufVYbC9qrYC9te/jdVX1nXKSRJpUOi3XGLYWfmHo3ijrieZOX6k2/35LFZaX1E2VL2lV9xtq2NLNalPciuxeg=="], + "ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], + "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], + "ansis": ["ansis@4.5.0", "", {}, "sha512-KwIVkfFfJSSzBmZhDGuTmkhWqTdyB5oNVzzznBJbBZ5bZtHiqSrcugRF0RKJKT4Ss3d7ZrcjnlFVDlQNmtWfWw=="], "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], @@ -918,6 +1018,8 @@ "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="], + "asn1js": ["asn1js@3.1.0", "", { "dependencies": { "pvtsutils": "^1.3.6", "pvutils": "^1.2.0", "tslib": "^2.8.1" } }, "sha512-HWYi1THkOqguCiO+vwj09ZkQFD2kKEsejYHAYCtX0D80WVdoDnvRc9IukXX/jeb0T9NRWJnRtqwdbHltnFjwXA=="], + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="], @@ -938,18 +1040,32 @@ "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.11.35", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-GNk8KX52CHqeNKGk8+7tZnQab/2k1t/BgrUHPKNdijzpEhF1saPQZHVKb6m5loSAxB39gyrTYw34Qf9CQ3u1qQ=="], "bidi-js": ["bidi-js@1.1.0", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg=="], "binaryen": ["binaryen@130.0.0", "", { "bin": { "wasm-as": "bin/wasm-as", "wasm2js": "bin/wasm2js", "wasm-dis": "bin/wasm-dis", "wasm-opt": "bin/wasm-opt", "wasm-merge": "bin/wasm-merge", "wasm-shell": "bin/wasm-shell", "wasm-reduce": "bin/wasm-reduce", "wasm-metadce": "bin/wasm-metadce", "wasm-ctor-eval": "bin/wasm-ctor-eval" } }, "sha512-XDrb+zql0RbFPKgj7MuH9zOc78R3Fa/P/VSGnnpdwYsvNZPWjcMYMdAkKCOQEL2A7yqgjSMTDRFp6gfSDW+/QQ=="], + "bl": ["bl@6.1.6", "", { "dependencies": { "@types/readable-stream": "^4.0.0", "buffer": "^6.0.3", "inherits": "^2.0.4", "readable-stream": "^4.2.0" } }, "sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg=="], + + "body-parser": ["body-parser@2.4.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^2.1.0", "debug": "^4.4.3", "http-errors": "^2.0.1", "iconv-lite": "^0.7.3", "on-finished": "^2.4.1", "qs": "^6.16.1", "raw-body": "^3.0.2", "type-is": "^2.1.0" } }, "sha512-Ifk1KcLrxaGt1occQagQNFNNOqRt1LFuehEnHHzB8Z6cRgIxRU1TBaiIsUiCdA+gy5PL7Y1IX31DNKkRmxtXjQ=="], + "brace-expansion": ["brace-expansion@5.0.12", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ=="], + "broker-factory": ["broker-factory@3.1.15", "", { "dependencies": { "@babel/runtime": "^7.29.7", "fast-unique-numbers": "^9.0.27", "tslib": "^2.8.1", "worker-factory": "^7.0.50" } }, "sha512-ko+aWvgNuP49meGrdjUu7rC+Y+Wai3cCPxP3xWwHsHfehFjOh5ZQM2yC4gEB2UddeZ/YXhm0K1eG/L6fxym2Og=="], + "browserslist": ["browserslist@4.29.5", "", { "dependencies": { "baseline-browser-mapping": "^2.11.33", "caniuse-lite": "^1.0.30001819", "electron-to-chromium": "^1.5.464", "node-releases": "^2.0.60", "update-browserslist-db": "^1.3.4" }, "bin": { "browserslist": "cli.js" } }, "sha512-khTmKu5NGYTotqcYo8Bew2k4RAE9PuXRSFRXG2lQBrCEQHyeQ/vX6gKMxfokwywBsLiXZMnHsmWMG1pnO8J7uQ=="], + "buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], + "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], + "bun-types": ["bun-types@1.4.4", "", { "dependencies": { "@types/node": "*", "undici-types": "*" } }, "sha512-yjrJh5oGX7mMPKGd19YXyIjmEuj4G4zNC30bs7PM9Az7NQ3ndF3j6hbeRdvJTRQEhJ/FYQwW0a2moweEi2hMxQ=="], + + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + "cac": ["cac@7.1.0", "", {}, "sha512-LMDU/U2yA4IPIY8KT0NsNPWEYZvAMkbgyspxJ6btJaYV+WMDOHAZCmB0lsTObiHaRS1V/ztKAl7+TSkTqXyeoA=="], "call-bind": ["call-bind@1.0.9", "", { "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" } }, "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ=="], @@ -978,16 +1094,30 @@ "comment-json": ["comment-json@4.6.2", "", { "dependencies": { "array-timsort": "^1.0.3", "esprima": "^4.0.1" } }, "sha512-R2rze/hDX30uul4NZoIZ76ImSJLFxn/1/ZxtKC1L77y2X1k+yYu1joKbAtMA2Fg3hZrTOiw0I5mwVMo0cf250w=="], + "commist": ["commist@3.2.0", "", {}, "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw=="], + "common-tags": ["common-tags@1.8.2", "", {}, "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA=="], + "concat-stream": ["concat-stream@2.0.0", "", { "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.0.2", "typedarray": "^0.0.6" } }, "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A=="], + "consola": ["consola@3.4.0", "", {}, "sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA=="], + "content-disposition": ["content-disposition@2.0.1", "", {}, "sha512-e+H0ZXHSWYrENhQzw1LPuP4oF5MzVKmDU6d3hxlvaPEYLLg62MxtQNPRx4SYSuYJSBUgnQIG4HIN2tEtNv7Dog=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], + "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + "cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="], + "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], + "core-js-compat": ["core-js-compat@3.51.0", "", { "dependencies": { "browserslist": "^4.29.3" } }, "sha512-52r6fkXgxv4GLMjmFcMWRx7X9bw071xTTdrDcdyPGRqU2oE+9pCG0bAOMifSBdLw0JRBZKEF0q3B55qPlLuE8g=="], + "cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], + "croner": ["croner@11.0.0", "", {}, "sha512-T17ADjdttrVToyzLpLq9Xb9xzcMwpQ/M3Qs9szhh0MVNTbv6NWQ9OFYa2qlzPYXFMn+uoDlnW1IpIv6cAvFtyA=="], "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], @@ -1028,6 +1158,8 @@ "defu": ["defu@6.1.7", "", {}, "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ=="], + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], @@ -1036,10 +1168,14 @@ "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + "ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="], "electron-to-chromium": ["electron-to-chromium@1.5.474", "", {}, "sha512-t1DE2RM6wtco58TQ47NcKw0CXw4fT3gWQvj49kkNBzMrUX3m9Sq/SoKcjEfwa9vvQ6sKYlqi/g05vG2tKLWH4w=="], + "encodeurl": ["encodeurl@2.0.1", "", {}, "sha512-Ev/hIyePLZtpL98x562lvPtenrUfGr0lfaf44HFZ+hDEwso7ccV9QAZrEVLg0hxNAzygaAFxy2dYaAKKM0G06A=="], + "enhanced-resolve": ["enhanced-resolve@5.26.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-PPnnSJq3azKOIewXR/uppBV/6h98dprBJ/WaQ9Qgj10xX1lJLNfg+XShpbsrSqcDSy2yYYyhfXM1wWhftqgyzw=="], "entities": ["entities@9.0.0", "", {}, "sha512-oJFNDJg3kTPwAD1cNn2+WmUq4wFoo09npc3Ydf1D7ED4smRiWrsKnaLxykrTWgyGEcusc87P5dxlAleol9kpkg=="], @@ -1064,6 +1200,8 @@ "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], @@ -1072,16 +1210,34 @@ "eta": ["eta@4.6.0", "", {}, "sha512-lW6is4T1NFOYnmqGZIfvixqj7A7sSvScF+DN8EK6K58xI5MZ5UvYe0GjopxOXQtZvUn4eDdVuZ8XSoYWTMEKwA=="], + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + + "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], + + "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], + + "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], + + "eventsource-parser": ["eventsource-parser@3.1.1", "", {}, "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ=="], + "expect-type": ["expect-type@1.4.0", "", {}, "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA=="], + "express": ["express@5.3.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.3.0", "content-disposition": "^2.0.1", "content-type": "^2.0.0", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.8", "qs": "^6.16.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.1.0", "vary": "^1.1.2" } }, "sha512-qCqy1xSoaugxOD4IVHb1ZWdkvCF2W3aE2okKouWj8VwJ+ztskSiXoBnCSJqfKT+cVPJhuJqe9YLbY425+Zh00g=="], + + "express-rate-limit": ["express-rate-limit@8.7.3", "", { "dependencies": { "debug": "^4.4.3", "ip-address": "^10.2.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-zPLqDI2f0gV2WmGnacLf9XSSqn4VldRyP5LCzOwx0BKH40mO6+2PCFkoSsRO2jePx87/Kf6NUF6P2QOTKXHbQA=="], + "exsolve": ["exsolve@1.2.0", "", {}, "sha512-h1pQPR9XoeSMdntRNCjyTznaume4UpfXrULBnlkZ2CUM/G5eSa+pBByGldO5uR8dxbPC+OuJXsPPNf+JZ81ADg=="], "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="], + "fast-unique-numbers": ["fast-unique-numbers@9.0.27", "", { "dependencies": { "@babel/runtime": "^7.29.2", "tslib": "^2.8.1" } }, "sha512-nDA9ADeINN8SA2u2wCtU+siWFTTDqQR37XvgPIDDmboWQeExz7X0mImxuaN+kJddliIqy2FpVRmnvRZ+j8i1/A=="], + "fast-uri": ["fast-uri@3.1.9", "", {}, "sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ=="], + "fastparallel": ["fastparallel@2.4.1", "", { "dependencies": { "reusify": "^1.0.4", "xtend": "^4.0.2" } }, "sha512-qUmhxPgNHmvRjZKBFUNI0oZuuH9OlSIOXmJ98lhKPxMZZ7zS/Fi0wRHOihDSz0R1YiIOjxzOY4bq65YTcdBi2Q=="], + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], "fetchdts": ["fetchdts@0.2.0", "", {}, "sha512-PJb4ZTGT2/wK78RmVrxZITStWabhZlKBsL52VOgq+c7clEkdHdEB+0rtzrXCbZJqeYJj+IwNWYqB5/G6vouA6A=="], @@ -1090,10 +1246,16 @@ "filelist": ["filelist@1.0.6", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA=="], + "finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], + "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + + "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], + "fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin", "hasInstallScript": true }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], @@ -1142,18 +1304,34 @@ "hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="], + "help-me": ["help-me@5.0.0", "", {}, "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg=="], + + "hono": ["hono@4.13.16", "", {}, "sha512-AOLOEhvrV+iH92xXCnFr/5+3KjPKcI3xP63XJtX5spAnkznPdHijlIsUTpi7/9EhWqrTCekw5c5zh1K+YsVw7A=="], + "hookable": ["hookable@6.1.2", "", {}, "sha512-+abwxtiEA52GCVIsQqut3S/uKTbUwYIp4Pe/vv+6py5XiXBCqMZHg6pA6Y5qhgLSEys0/cYuPbO0z1QFj5ZCmg=="], "html-encoding-sniffer": ["html-encoding-sniffer@7.0.0", "", { "dependencies": { "@exodus/bytes": "^1.15.1" } }, "sha512-UikN5yr7xsCDAq87Or5or0PAlD3HJJOKVzM05az588WnpDJ4Ux7a2A53Qi6gofGg2/EtvF/H4hCi/TXfCW4Y6w=="], + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + "httpxy": ["httpxy@0.5.6", "", {}, "sha512-iqc3nOxvNb5FxbhJozt8wNmHgjlmk4zzba512jMEXVwHCEBCbGAj49eGfy5PI8ts3/LFSFq8siiSNqwfY1AlfQ=="], "ico-endec": ["ico-endec@0.1.6", "", {}, "sha512-ZdLU38ZoED3g1j3iEyzcQj+wAkY2xfWNkymszfJPoxucIUhK7NayQ+/C4Kv0nDFMIsbtbEHldv3V8PU494/ueQ=="], + "iconv-lite": ["iconv-lite@0.7.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ=="], + "idb": ["idb@7.1.1", "", {}, "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ=="], + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], + "ip-address": ["ip-address@10.7.6", "", {}, "sha512-00LYkM6byILa9eQIx6qoaXCM40xTLd56AZ4vyaPW3YTyb49VZYPo5xsiAq4tuQ54GH4R7gVdYScJ1B7B77PuTg=="], + + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="], "is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="], @@ -1188,6 +1366,8 @@ "is-potential-custom-element-name": ["is-potential-custom-element-name@1.0.1", "", {}, "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="], + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], + "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], "is-regexp": ["is-regexp@1.0.0", "", {}, "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA=="], @@ -1222,6 +1402,8 @@ "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], + "jose": ["jose@6.2.15", "", {}, "sha512-qdF6ewcZARtef63fJ9AKKZPgxw+t7USH6HLXYcD4CrdmSvDp79rpJSPFEKc90QBeGS63u1KQtN2MZQHC0yDIzA=="], + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], "js-yaml": ["js-yaml@4.3.2", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA=="], @@ -1232,6 +1414,8 @@ "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], "jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="], @@ -1282,20 +1466,42 @@ "mdn-data": ["mdn-data@2.27.1", "", {}, "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ=="], + "media-typer": ["media-typer@1.1.1", "", {}, "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ=="], + + "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], + + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], + + "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + "minimatch": ["minimatch@10.2.6", "", { "dependencies": { "brace-expansion": "^5.0.8" } }, "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A=="], + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + "minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], + "mqemitter": ["mqemitter@7.1.0", "", { "dependencies": { "fastparallel": "^2.4.1", "qlobber": "^8.0.1" } }, "sha512-GnBDNz3lxmllW201ne0mrmdy5tPOTnc79jjVcsfUa2LG2pUGeyGWVeiae6ZysfC/64XrYOqCKRAQYrB7pGyBVQ=="], + + "mqtt": ["mqtt@5.16.1", "", { "dependencies": { "@types/readable-stream": "^4.0.21", "@types/ws": "^8.18.1", "commist": "^3.2.0", "concat-stream": "^2.0.0", "debug": "^4.4.1", "help-me": "^5.0.0", "lru-cache": "^10.4.3", "minimist": "^1.2.8", "mqtt-packet": "^9.0.2", "readable-stream": "^4.7.0", "rfdc": "^1.4.1", "socks": "^2.8.6", "split2": "^4.2.0", "worker-timers": "^8.0.23", "ws": "^8.18.3" }, "bin": { "mqtt": "build/bin/mqtt.js", "mqtt_pub": "build/bin/pub.js", "mqtt_sub": "build/bin/sub.js" } }, "sha512-9aEaiR/mLblsMAiynD8quhcGTaINdta2bdyknAtfVzESfoe6Ncdh/uEs5p+3zqcTEtgYErwL4pNk0QkAynXxlg=="], + + "mqtt-packet": ["mqtt-packet@9.0.2", "", { "dependencies": { "bl": "^6.0.8", "debug": "^4.3.4", "process-nextick-args": "^2.0.1" } }, "sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "nanoid": ["nanoid@3.3.20", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ=="], + "negotiator": ["negotiator@1.1.0", "", { "dependencies": { "content-type": "^2.1.0" } }, "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg=="], + "nf3": ["nf3@0.3.24", "", {}, "sha512-HxLK4bo+5jNsEETZp4w3tJblHOA9MCBY14IN9nZJJV8JDxt9yNIYxuBuLMjTAR5GFa3HL61+8VQDUrXv3/C8fw=="], "nitro": ["nitro@3.0.260903-beta", "", { "dependencies": { "consola": "^3.4.2", "crossws": "^0.4.12", "db0": "^0.4.1", "env-runner": "^0.2.1", "h3": "^2.0.1-rc.31", "hookable": "^6.1.1", "nf3": "^0.3.24", "ocache": "^0.3.0", "rolldown": "^1.2.7", "rou3": "^0.9.2", "srvx": "^1.0.3", "unenv": "^2.0.0-rc.24", "unstorage": "^2.0.0-alpha.10" }, "bin": { "nitro": "dist/cli/index.mjs" } }, "sha512-54gANPi62O8rfMvepiJUVuIzEIinYfpeHbebywlLxvVTgIYXDwSvpaU9Id+0sJOBjBx0wC1/CVYXJkH7SY+l0g=="], "node-releases": ["node-releases@2.0.63", "", {}, "sha512-6li2QanLsAXpIH2R6j77MzRyvOyrvdTqlKtO4CgBdHGNvN99ZZfyxszHc5o5+IZBWD5u6Hzwg83JoGXmqZNG9Q=="], + "nodemailer": ["nodemailer@10.3.1", "", {}, "sha512-uF0AFsJC07Y1vdW7p580DKAnh7rAO+4UPSSB3mDCPNDufoiaOebV6JmrzpxB14OjEEEiBsNdYoM2Y+fN8M7BBA=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], @@ -1306,6 +1512,10 @@ "ocache": ["ocache@0.3.1", "", {}, "sha512-qeBrhQ0ly6tgXsakgNwLx6diRUppJZIcV5OjVGa5yp9HzztutkaGDDXJqVMo1fKWz5fNDtTp3iPU2fuhHf0+Lg=="], + "on-finished": ["on-finished@2.4.2", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-ATWUv387i+nLD49EiDoOft5scf32Vsdz3ktrQJG9bi7jRFmaXTssvyvj1EmeKVVgvesbqUtvQQvaS2TpTukAZQ=="], + + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + "own-keys": ["own-keys@1.0.2", "", { "dependencies": { "call-bound": "^1.0.4", "get-intrinsic": "^1.3.0", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg=="], "oxc-minify": ["oxc-minify@0.136.0", "", { "optionalDependencies": { "@oxc-minify/binding-android-arm-eabi": "0.136.0", "@oxc-minify/binding-android-arm64": "0.136.0", "@oxc-minify/binding-darwin-arm64": "0.136.0", "@oxc-minify/binding-darwin-x64": "0.136.0", "@oxc-minify/binding-freebsd-x64": "0.136.0", "@oxc-minify/binding-linux-arm-gnueabihf": "0.136.0", "@oxc-minify/binding-linux-arm-musleabihf": "0.136.0", "@oxc-minify/binding-linux-arm64-gnu": "0.136.0", "@oxc-minify/binding-linux-arm64-musl": "0.136.0", "@oxc-minify/binding-linux-ppc64-gnu": "0.136.0", "@oxc-minify/binding-linux-riscv64-gnu": "0.136.0", "@oxc-minify/binding-linux-riscv64-musl": "0.136.0", "@oxc-minify/binding-linux-s390x-gnu": "0.136.0", "@oxc-minify/binding-linux-x64-gnu": "0.136.0", "@oxc-minify/binding-linux-x64-musl": "0.136.0", "@oxc-minify/binding-openharmony-arm64": "0.136.0", "@oxc-minify/binding-wasm32-wasi": "0.136.0", "@oxc-minify/binding-win32-arm64-msvc": "0.136.0", "@oxc-minify/binding-win32-ia32-msvc": "0.136.0", "@oxc-minify/binding-win32-x64-msvc": "0.136.0" } }, "sha512-u9UvPFTYVwQ/i1kpp/c20S/SbvLaLV0Ngzyhgi0YV4B1SlxM7RgnnePl4vY9N3Z9sTRcozL1MRStI3PlUN9RFA=="], @@ -1316,18 +1526,28 @@ "parse5": ["parse5@8.1.0", "", { "dependencies": { "entities": "^9.0.0" } }, "sha512-sq3qBFpee5jeocbFbO48VHlGr8sT7mbXS0vxlKN6sottBtJzrOG3998sr0llSZcQAGgfpdmEYAIZE0P9z0dE8w=="], + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], "path-scurry": ["path-scurry@2.0.2", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg=="], + "path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@4.0.7", "", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="], + "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], + + "playwright": ["playwright@1.64.1", "", { "dependencies": { "playwright-core": "1.64.1" }, "bin": { "playwright": "cli.js" } }, "sha512-HCrVhHr5QQuwYoswEUGTb7gGp522yflbv/ST8nF4sFWGuLjQCt17Q/EubpGGSNKYn1tsTCPZxqHhHCfVURu7UA=="], + + "playwright-core": ["playwright-core@1.64.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-BC4s8y6SEVA6Q1rSxZTto8hI1KUI3xXlHGZgvGqP4IqPdI/a6FX9H29IDNz7vKo9l21b6LfYosScPL6M3xxyDQ=="], + "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], "postcss": ["postcss@8.5.32", "", { "dependencies": { "nanoid": "^3.3.20", "picocolors": "^1.1.1", "source-map-js": "^1.2.2" } }, "sha512-LJlKCCAUFKOuZxipUdYw1NzIITEsPcgyOG9LkvRJvuKj/bRzbuRY0ARXvL0pWGpsZvn8jku+LMep2eU0seHp8g=="], @@ -1336,12 +1556,30 @@ "pretty-bytes": ["pretty-bytes@6.1.1", "", {}, "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ=="], + "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="], + + "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], + + "proxy-addr": ["proxy-addr@2.0.8", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ=="], + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + "pvtsutils": ["pvtsutils@1.3.6", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg=="], + + "pvutils": ["pvutils@1.2.0", "", {}, "sha512-BbubeCEyTuQjVMakvJQ/Sxbc93F2pwmbsxONT/ZRrwU7Ua38d8unYTwXpTVLAKJ4BDuH9IGztCjQcd/N/39Dvg=="], + + "qlobber": ["qlobber@8.0.1", "", {}, "sha512-O+Wd1chXj5YE1DwmD+ae0bXiSLehmnS3czlC1R9FL/Nt/3q8uMS1bIHmg2lJfCoiimCxClWM8AAuJrF0EvNiog=="], + + "qs": ["qs@6.17.1", "", { "dependencies": { "es-define-property": "^1.0.1", "side-channel": "^1.1.1" } }, "sha512-jd3COkaKVgGUW+ZHyEHCG9bH7RoaaiWuMgzsIBjUMVjLAaWGWpoUAVwljVvBHxRu/2qEHnadLGZrkutYUBPQJA=="], + "quansync": ["quansync@1.0.0", "", {}, "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA=="], "radix-ui": ["radix-ui@1.8.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-accessible-icon": "1.2.0", "@radix-ui/react-accordion": "1.3.1", "@radix-ui/react-alert-dialog": "1.2.1", "@radix-ui/react-arrow": "1.1.17", "@radix-ui/react-aspect-ratio": "1.2.0", "@radix-ui/react-avatar": "1.3.1", "@radix-ui/react-checkbox": "1.4.1", "@radix-ui/react-collapsible": "1.2.1", "@radix-ui/react-collection": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-context-menu": "2.4.1", "@radix-ui/react-dialog": "1.3.1", "@radix-ui/react-direction": "1.2.0", "@radix-ui/react-dismissable-layer": "1.2.1", "@radix-ui/react-dropdown-menu": "2.2.1", "@radix-ui/react-focus-guards": "1.2.0", "@radix-ui/react-focus-scope": "1.3.1", "@radix-ui/react-form": "0.3.0", "@radix-ui/react-hover-card": "1.2.1", "@radix-ui/react-label": "2.1.17", "@radix-ui/react-menu": "2.2.1", "@radix-ui/react-menubar": "1.2.1", "@radix-ui/react-navigation-menu": "1.4.1", "@radix-ui/react-one-time-password-field": "0.2.1", "@radix-ui/react-password-toggle-field": "0.2.1", "@radix-ui/react-popover": "1.3.1", "@radix-ui/react-popper": "1.4.0", "@radix-ui/react-portal": "1.2.0", "@radix-ui/react-presence": "1.1.11", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-progress": "1.2.1", "@radix-ui/react-radio-group": "1.5.1", "@radix-ui/react-roving-focus": "1.2.1", "@radix-ui/react-scroll-area": "1.4.0", "@radix-ui/react-select": "2.4.1", "@radix-ui/react-separator": "1.2.0", "@radix-ui/react-slider": "1.6.1", "@radix-ui/react-slot": "1.5.0", "@radix-ui/react-switch": "1.4.1", "@radix-ui/react-tabs": "1.2.1", "@radix-ui/react-toast": "1.3.1", "@radix-ui/react-toggle": "1.2.1", "@radix-ui/react-toggle-group": "1.2.1", "@radix-ui/react-toolbar": "1.2.1", "@radix-ui/react-tooltip": "1.4.1", "@radix-ui/react-use-callback-ref": "1.1.5", "@radix-ui/react-use-controllable-state": "1.3.0", "@radix-ui/react-use-effect-event": "0.0.5", "@radix-ui/react-use-escape-keydown": "1.1.6", "@radix-ui/react-use-is-hydrated": "0.1.3", "@radix-ui/react-use-layout-effect": "1.1.4", "@radix-ui/react-use-size": "1.1.5", "@radix-ui/react-visually-hidden": "1.2.13" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-HPfpVGizO9xANlsFSauruLhRe/3+iOt3vPX0MXYle/UQi3gorQrSYtBcdiv0BZYewcl2rvL59qrIuLFMGAmUrw=="], + "range-parser": ["range-parser@1.3.0", "", {}, "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw=="], + + "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], + "react": ["react@19.3.1", "", {}, "sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw=="], "react-chartjs-2": ["react-chartjs-2@5.3.1", "", { "peerDependencies": { "chart.js": "^4.1.1", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-h5IPXKg9EXpjoBzUfyWJvllMjG2mQ4EiuHQFhms/AjUm0XSZHhyRy2xVmLXHKrtcdrPO4mnGqRtYoD0vp95A0A=="], @@ -1354,8 +1592,12 @@ "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], + "readable-stream": ["readable-stream@4.8.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-2jhTTU3Eilp/2DMT71gbcoqC35eQHasMJH0RHkYxwlxIv3+FR6Y5xAFZIwEGLxKkjZYEFlsiMNIdwUqjK2lLHQ=="], + "readdirp": ["readdirp@5.1.1", "", {}, "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA=="], + "reflect-metadata": ["reflect-metadata@0.2.2", "", {}, "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q=="], + "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="], "regenerate": ["regenerate@1.5.0", "", {}, "sha512-1s+CxXjJ71i7LNVy8nYwg47XV4y6lAnOEhsMtSKq3WpmPfhl9LuKlTqKIn/3yUpE8lBz+W+TT88Bxyhnr0U4yw=="], @@ -1374,36 +1616,52 @@ "resolve": ["resolve@1.22.14", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.18.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-1ZjYvR9sbkiwC5+9QHetqW5HmA2pHRMaoupD01t2HlYnV/vydC2zPwLixhsMieSWVZt0JJRg5LVIk0Ow1D/TrA=="], + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + + "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], + "rolldown": ["rolldown@1.2.16", "", { "dependencies": { "@oxc-project/types": "=0.156.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm-eabi": "1.2.16", "@rolldown/binding-android-arm64": "1.2.16", "@rolldown/binding-darwin-arm64": "1.2.16", "@rolldown/binding-darwin-x64": "1.2.16", "@rolldown/binding-freebsd-x64": "1.2.16", "@rolldown/binding-linux-arm-gnueabihf": "1.2.16", "@rolldown/binding-linux-arm64-gnu": "1.2.16", "@rolldown/binding-linux-arm64-musl": "1.2.16", "@rolldown/binding-linux-ppc64-gnu": "1.2.16", "@rolldown/binding-linux-s390x-gnu": "1.2.16", "@rolldown/binding-linux-x64-gnu": "1.2.16", "@rolldown/binding-linux-x64-musl": "1.2.16", "@rolldown/binding-openharmony-arm64": "1.2.16", "@rolldown/binding-win32-arm64-msvc": "1.2.16", "@rolldown/binding-win32-x64-msvc": "1.2.16" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-bWvlMpFKiTfDKfnK7FC2MPgPeWCf/Kpwsq49ApdW+FQaBZb6O6dZ4MAPbPUjp7hYQrw/RobqCzn64BRrQecGHw=="], "rollup": ["rollup@4.67.4", "", { "dependencies": { "@types/estree": "^1.0.10" }, "optionalDependencies": { "@napi-rs/lzma-linux-x64-gnu": "1.5.1", "@rollup/rollup-android-arm-eabi": "4.67.4", "@rollup/rollup-android-arm64": "4.67.4", "@rollup/rollup-android-x64": "4.67.4", "@rollup/rollup-darwin-arm64": "4.67.4", "@rollup/rollup-darwin-x64": "4.67.4", "@rollup/rollup-freebsd-arm64": "4.67.4", "@rollup/rollup-freebsd-x64": "4.67.4", "@rollup/rollup-linux-arm-gnueabihf": "4.67.4", "@rollup/rollup-linux-arm-musleabihf": "4.67.4", "@rollup/rollup-linux-arm64-gnu": "4.67.4", "@rollup/rollup-linux-arm64-musl": "4.67.4", "@rollup/rollup-linux-loong64-gnu": "4.67.4", "@rollup/rollup-linux-loong64-musl": "4.67.4", "@rollup/rollup-linux-ppc64-gnu": "4.67.4", "@rollup/rollup-linux-ppc64-musl": "4.67.4", "@rollup/rollup-linux-riscv64-gnu": "4.67.4", "@rollup/rollup-linux-riscv64-musl": "4.67.4", "@rollup/rollup-linux-s390x-gnu": "4.67.4", "@rollup/rollup-linux-x64-gnu": "4.67.4", "@rollup/rollup-linux-x64-musl": "4.67.4", "@rollup/rollup-openbsd-x64": "4.67.4", "@rollup/rollup-openharmony-arm64": "4.67.4", "@rollup/rollup-win32-arm64-msvc": "4.67.4", "@rollup/rollup-win32-ia32-msvc": "4.67.4", "@rollup/rollup-win32-x64-gnu": "4.67.4", "@rollup/rollup-win32-x64-msvc": "4.67.4", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-Lsv3oKcz5jgOQJej/p7nfwbVEdAbDT+z6OPf/SCdI+nF+q2ENp0pTKT35ZwXAmH8g45mfrIiJeEyAqKYSYoL9Q=="], "rou3": ["rou3@0.9.2", "", {}, "sha512-3SOzvaAg8rkHrXtRjpCvCvbyO5to9oOO27Z/XqHEYXfMRVSw/qMIVdmaOk9W2lcRLtR6dlqTjo9hDeJk70QBYQ=="], + "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], + "safe-array-concat": ["safe-array-concat@1.1.4", "", { "dependencies": { "call-bind": "^1.0.9", "call-bound": "^1.0.4", "get-intrinsic": "^1.3.0", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg=="], + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + "safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="], "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + "saxes": ["saxes@6.0.0", "", { "dependencies": { "xmlchars": "^2.2.0" } }, "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="], "scheduler": ["scheduler@0.28.0", "", {}, "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw=="], "semver": ["semver@7.8.6", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-9lpSSU4Mly3mrtruwLs1eLK6+gzbzqeBFwRNjIjE+9LuDpWdYctrQlQ3ej1Ilgi7gknpsTRnPoLP8DpCHqHIJw=="], + "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], + "serialize-javascript": ["serialize-javascript@7.1.4", "", {}, "sha512-FqlEuoTq/zRmA4Lg6aOqyH3lCHbUtCB0bmHCeFniX2GBdDqMJM3FwdvEhlE5Pim1dlt2FfBdVPmUNNiGhhKqpw=="], "seroval": ["seroval@1.6.12", "", {}, "sha512-CqbIVfk/7GL3ITwZI55lcPbpQbjHZ2DD5cUj4iH7E9YsCfHNJV1mx7yNp86Z84w5IpmkZZS/2s5vPOWmVWSdQg=="], "seroval-plugins": ["seroval-plugins@1.6.12", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-+reiwp/lYxiyctSrdvX7OQ1YLrcbWYgBiZrhSFL+V43b0/DGRLNVSDyZINbQe+tWplPOI2fw2dPrdv6hcc09kQ=="], + "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], + "set-function-length": ["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.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], "set-function-name": ["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" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="], "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="], + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + "sharp": ["sharp@0.35.5", "", { "dependencies": { "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", "semver": "^7.8.5" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.35.5", "@img/sharp-darwin-x64": "0.35.5", "@img/sharp-freebsd-wasm32": "0.35.5", "@img/sharp-libvips-darwin-arm64": "1.3.4", "@img/sharp-libvips-darwin-x64": "1.3.4", "@img/sharp-libvips-linux-arm": "1.3.4", "@img/sharp-libvips-linux-arm64": "1.3.4", "@img/sharp-libvips-linux-ppc64": "1.3.4", "@img/sharp-libvips-linux-riscv64": "1.3.4", "@img/sharp-libvips-linux-s390x": "1.3.4", "@img/sharp-libvips-linux-x64": "1.3.4", "@img/sharp-libvips-linuxmusl-arm64": "1.3.4", "@img/sharp-libvips-linuxmusl-x64": "1.3.4", "@img/sharp-linux-arm": "0.35.5", "@img/sharp-linux-arm64": "0.35.5", "@img/sharp-linux-ppc64": "0.35.5", "@img/sharp-linux-riscv64": "0.35.5", "@img/sharp-linux-s390x": "0.35.5", "@img/sharp-linux-x64": "0.35.5", "@img/sharp-linuxmusl-arm64": "0.35.5", "@img/sharp-linuxmusl-x64": "0.35.5", "@img/sharp-webcontainers-wasm32": "0.35.5", "@img/sharp-win32-arm64": "0.35.5", "@img/sharp-win32-ia32": "0.35.5", "@img/sharp-win32-x64": "0.35.5" }, "peerDependencies": { "@types/node": "*" }, "optionalPeers": ["@types/node"] }, "sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g=="], "sharp-ico": ["sharp-ico@0.1.5", "", { "dependencies": { "decode-ico": "*", "ico-endec": "*", "sharp": "*" } }, "sha512-a3jODQl82NPp1d5OYb0wY+oFaPk7AvyxipIowCHk7pBsZCWgbe0yAkU2OOXdoH0ENyANhyOQbs9xkAiRHcF02Q=="], @@ -1424,8 +1682,18 @@ "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + "smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="], + "smob": ["smob@1.6.2", "", {}, "sha512-RQsvleCbF8cVHEv+xuDGaA4pOizFqJ0GgjtMSRo6oP8pnN7WsigHgVGey6aILRBKv4W2YOMHLqbKdnB6hpB9fw=="], + "snapraid-ui": ["snapraid-ui@workspace:apps/web"], + + "snapraid-ui-backend": ["snapraid-ui-backend@workspace:apps/server"], + + "snapraid-ui-e2e": ["snapraid-ui-e2e@workspace:e2e"], + + "socks": ["socks@2.8.10", "", { "dependencies": { "ip-address": "^10.1.1", "smart-buffer": "^4.2.0" } }, "sha512-e0VyvkVTwVYViNovRkZ9aodhxVlyoMn7eJhVUPxZ+eK9P/7CBkxvvsBOHqFPEH416726W8tLXXXjKwqgTErrCQ=="], + "solid-js": ["solid-js@1.9.18", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.6.11", "seroval-plugins": "~1.6.8" } }, "sha512-CHGO5YFk6Y9m/1YSxVtKnXfrW7JD3Tmof8HmR4z6TmCPsS8efo8LnzIR8CQh3cUvffqYm6DWyBFfk8dQw1CNQQ=="], "sonner": ["sonner@2.0.8", "", { "peerDependencies": { "@types/react": "^18.0.0 || ^19.0.0", "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-UM/ByIoFra8yzV75n1o0Puu0bw5U/9UNnDacrJNspekBewIfsQ3D6ez1nvlWpt7aTsO6rujQtifBpycwIivqlg=="], @@ -1436,8 +1704,12 @@ "source-map-support": ["source-map-support@0.5.21", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="], + "split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="], + "srvx": ["srvx@1.0.8", "", { "bin": { "srvx": "./bin/srvx.mjs" } }, "sha512-ngFS/J6St4PJfHRHGjr78xkPPtbyXtdMOLNE4x/XZunEK3OhV7aGUWMHf9WTUEy6wm1U0W4okkd82ZFFWRVQTw=="], + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + "std-env": ["std-env@4.3.0", "", {}, "sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag=="], "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], @@ -1450,6 +1722,8 @@ "string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="], + "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], + "stringify-object": ["stringify-object@3.3.0", "", { "dependencies": { "get-own-enumerable-property-symbols": "^3.0.0", "is-obj": "^1.0.1", "is-regexp": "^1.0.0" } }, "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw=="], "strip-comments": ["strip-comments@2.0.1", "", {}, "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw=="], @@ -1480,16 +1754,22 @@ "to-data-view": ["to-data-view@1.1.0", "", {}, "sha512-1eAdufMg6mwgmlojAx3QeMnzB/BTVp7Tbndi3U7ftcT2zCZadjxkkmLmd97zmaxWi+sgGcgWrokmpEoy0Dn0vQ=="], + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + "tough-cookie": ["tough-cookie@6.0.2", "", { "dependencies": { "tldts": "^7.0.5" } }, "sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA=="], "tr46": ["tr46@7.0.0", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-BcDSDW+SIGWVOns1Zm6Yg91q9AEj7eaID0QDY9oIqJD1A8dscNwMcl9Sms2DeuWCNov1X0/oXanHGYQBSLwZsg=="], "tslib": ["tslib@2.9.1", "", {}, "sha512-a4Ux9VKuIMHsOS7ENbEq6hK2ZGYIT27Rtl0E4eD9KqwxhKiQeMZE/9xb3IdYwWaSOyIBQLExhQgXDTYoiCxnWA=="], + "tsyringe": ["tsyringe@4.10.0", "", { "dependencies": { "tslib": "^1.9.3" } }, "sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw=="], + "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], "type-fest": ["type-fest@0.16.0", "", {}, "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg=="], + "type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="], + "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="], @@ -1498,6 +1778,8 @@ "typed-array-length": ["typed-array-length@1.0.8", "", { "dependencies": { "call-bind": "^1.0.9", "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" } }, "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g=="], + "typedarray": ["typedarray@0.0.6", "", {}, "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="], + "typescript": ["typescript@7.0.2", "", { "optionalDependencies": { "@typescript/typescript-aix-ppc64": "7.0.2", "@typescript/typescript-darwin-arm64": "7.0.2", "@typescript/typescript-darwin-x64": "7.0.2", "@typescript/typescript-freebsd-arm64": "7.0.2", "@typescript/typescript-freebsd-x64": "7.0.2", "@typescript/typescript-linux-arm": "7.0.2", "@typescript/typescript-linux-arm64": "7.0.2", "@typescript/typescript-linux-loong64": "7.0.2", "@typescript/typescript-linux-mips64el": "7.0.2", "@typescript/typescript-linux-ppc64": "7.0.2", "@typescript/typescript-linux-riscv64": "7.0.2", "@typescript/typescript-linux-s390x": "7.0.2", "@typescript/typescript-linux-x64": "7.0.2", "@typescript/typescript-netbsd-arm64": "7.0.2", "@typescript/typescript-netbsd-x64": "7.0.2", "@typescript/typescript-openbsd-arm64": "7.0.2", "@typescript/typescript-openbsd-x64": "7.0.2", "@typescript/typescript-sunos-x64": "7.0.2", "@typescript/typescript-win32-arm64": "7.0.2", "@typescript/typescript-win32-x64": "7.0.2" }, "bin": { "tsc": "bin/tsc" } }, "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA=="], "ufo": ["ufo@1.6.4", "", {}, "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA=="], @@ -1526,6 +1808,8 @@ "universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="], + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + "unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="], "unstorage": ["unstorage@2.0.0-beta.0", "", {}, "sha512-VeTY+zQ3ozrNtIFBRQRuSwanHdpuRH+4t/ZzchZIVQO8/oKq2xl7fJ9c+z76tWJDW/BEXSEuzpHOxqoAl6B64A=="], @@ -1542,10 +1826,14 @@ "use-sync-external-store": ["use-sync-external-store@1.7.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-6L+EeigHMQhdaIPNIFUKwfWJSwWFQ8gJbJ2DLOs5sDIegTwR9fRxvnM3uciHKjIZhFz+KAv2emhWMRvDmMcY8A=="], + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + "uuid": ["uuid@14.0.3", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-Sc5u1VVln93y1Xnz4R+CunOZc7R9LUcTmhj7Rc7Eg3HVcf7rVn2FOoa812fe0W8p3jD7xeoGSYbfqCGGH2B6xw=="], "valibot": ["valibot@1.5.0", "", { "peerDependencies": { "typescript": ">=5" }, "optionalPeers": ["typescript"] }, "sha512-nil6AkP2TChWL43Z5uJ6GTxX01CUA+g8LWUM+N/rB9NBbkUMaUsi9PUNzlUPgoKASgmx9f7eGOYpJ04/fSa6FQ=="], + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + "vite": ["vite@8.3.5", "", { "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.7", "postcss": "^8.5.30", "rolldown": "~1.2.14", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.7.1 || ^0.8.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-z1YpjieJ9g0ebTmQpLBv/oq9E3F9EIlOe+7LahW8SrE3LTiG3udBRUXQOlYXP9b+IC2q2phHeVxj9xGlw34OEQ=="], "vite-plugin-pwa": ["vite-plugin-pwa@2.0.0", "", { "dependencies": { "debug": "^4.3.6", "pretty-bytes": "^6.1.1", "tinyglobby": "^0.2.10", "workbox-build": "^7.4.1", "workbox-window": "^7.4.1" }, "peerDependencies": { "@vite-pwa/assets-generator": "^1.0.0 || ^2.0.0", "vite": "^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["@vite-pwa/assets-generator"] }, "sha512-6G9dsPKT9QKYpeTuTLJM28zZL3HsXUTUM/QUZBEgQk2jbfrPJDq2i8gTewAKqi1c9Mt6MiidrL4SFI+HthiQMg=="], @@ -1610,6 +1898,16 @@ "workbox-window": ["workbox-window@7.4.1", "", { "dependencies": { "@types/trusted-types": "^2.0.2", "workbox-core": "7.4.1" } }, "sha512-notZDH2u8VXaqyuD7xaqIfEFi6SRM4SUSd7ewe9PDsVqADuepxX2ZMY3uvuZGxzY5ZOsGC/vD3A/3smFtJt4/A=="], + "worker-factory": ["worker-factory@7.0.50", "", { "dependencies": { "@babel/runtime": "^7.29.7", "fast-unique-numbers": "^9.0.27", "tslib": "^2.8.1" } }, "sha512-hhwc0G+sFwM4qBuhJIUBn2p1Jf8v/FwmLUANBf/Q+Lt2uI8mfIZQhXaZQACodQD4R7Zp6cn/6702bIvNn2puJQ=="], + + "worker-timers": ["worker-timers@8.0.34", "", { "dependencies": { "@babel/runtime": "^7.29.7", "tslib": "^2.8.1", "worker-timers-broker": "^8.0.18", "worker-timers-worker": "^9.0.15" } }, "sha512-WXL+Dqsm0G6dnC66rQsvM3tPT2adhbqSirWUCZGglkALOr8ocS0KlpU0iuKbjflJOlu3ydZikMEVrYnHOM9suw=="], + + "worker-timers-broker": ["worker-timers-broker@8.0.18", "", { "dependencies": { "@babel/runtime": "^7.29.7", "broker-factory": "^3.1.15", "fast-unique-numbers": "^9.0.27", "tslib": "^2.8.1", "worker-timers-worker": "^9.0.15" } }, "sha512-FrjzDVX1wKfZN0gRbCFqv8VHuTncG4sbI/WGEg4tSSQeIsnwqg4YBYWMAHYLJtUDEYYmiK65UKFrVMVk2irDSg=="], + + "worker-timers-worker": ["worker-timers-worker@9.0.15", "", { "dependencies": { "@babel/runtime": "^7.29.7", "tslib": "^2.8.1", "worker-factory": "^7.0.50" } }, "sha512-KKUe7lZ/Aignr51H6hOUik8LwTnIgojH/1lwhli8A8qIEIyewogZTpNpMW5B6BF7nmwBOkUoTYgf1H3QShcjSA=="], + + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + "ws": ["ws@8.22.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-oyL6OPYme/CmiFvNuGzeoxCJ4nOrZE2O1FwM9MWM1AsGri6nLXB2pg+lNA8aLeXMSav0J6eIVS1j6vUPzUtK9g=="], "xml-name-validator": ["xml-name-validator@5.0.0", "", {}, "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg=="], @@ -1618,10 +1916,14 @@ "xmlchars": ["xmlchars@2.2.0", "", {}, "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="], + "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="], + "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], "zod": ["zod@4.6.7", "", {}, "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg=="], + "zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], + "@babel/core/semver": ["semver@6.3.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-GYrCADwpu6yV5OAvchkQ27Z5wwMxWAEMg5NNWjyHh/5DboFFqbdbvYNIvZouQpxXyeMPoTOh0vdPYKlf0ZswdQ=="], "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], @@ -1670,10 +1972,20 @@ "babel-plugin-polyfill-corejs2/semver": ["semver@6.3.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-GYrCADwpu6yV5OAvchkQ27Z5wwMxWAEMg5NNWjyHh/5DboFFqbdbvYNIvZouQpxXyeMPoTOh0vdPYKlf0ZswdQ=="], + "body-parser/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], + + "concat-stream/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + + "express/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], + "filelist/minimatch": ["minimatch@5.1.9", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw=="], "h3/rou3": ["rou3@1.0.0", "", {}, "sha512-ySeaQkSrua+ut9g+65I7FHqOFBaY8tV0FmccXItoq0q21se32Q3u4YiW7/RqXSosWgYJaaKwRHXy3BST9PeCNQ=="], + "mqtt/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "negotiator/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], + "nitro/consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], "oxc-parser/@oxc-project/types": ["@oxc-project/types@0.120.0", "", {}, "sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg=="], @@ -1682,6 +1994,10 @@ "terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + "tsyringe/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], + + "type-is/content-type": ["content-type@2.1.0", "", {}, "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag=="], + "workbox-build/pretty-bytes": ["pretty-bytes@5.6.0", "", {}, "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg=="], "@oxc-minify/binding-wasm32-wasi/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="], diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000..9ac4935 --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,4 @@ +[install] +# Each package sees only its own dependencies (node_modules/.bun), not whatever another +# workspace brings: the frontend's inlang settings load ./node_modules/@inlang/... from apps/web +linker = "isolated" diff --git a/docker/Dockerfile b/docker/Dockerfile index 0ab297e..e9a6e9b 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -10,26 +10,36 @@ RUN apt-get update \ && ./configure && make -j"$(nproc)" \ && install -m 0755 snapraid /usr/local/bin/snapraid -# The Bun of packageManager in backend/ and frontend/package.json (Renovate moves them together) -FROM docker.io/oven/bun:1.4.4-slim AS backend-builder +# The Bun of packageManager in package.json (Renovate moves them together) +FROM docker.io/oven/bun:1.4.4-slim AS workspace -WORKDIR /app/backend +WORKDIR /app -# The backend's packages without the tools for types and tests; tsconfig.json maps @shared/ -COPY backend/package.json backend/bun.lock backend/tsconfig.json ./ -RUN bun install --frozen-lockfile --production +# The workspace's manifests: one bun.lock for all packages, installed per package below; the +# compiler options the packages' tsconfig.json extend (Vite and Bun read them for @shared/) +COPY package.json bun.lock bunfig.toml tsconfig.base.json ./ +COPY apps/server/package.json apps/server/ +COPY apps/web/package.json apps/web/ +COPY packages/shared/package.json packages/shared/ +COPY e2e/package.json e2e/ -COPY backend/src/ ./src/ +FROM workspace AS backend-builder -FROM docker.io/oven/bun:1.4.4-slim AS frontend-builder +# The backend's packages without the tools for types and tests (the isolated linker keeps them +# in /app/node_modules/.bun, linked from apps/server/node_modules); tsconfig.json maps @shared/ +# to the workspace package @snapraid-ui/shared +RUN bun install --frozen-lockfile --production --filter ./apps/server -WORKDIR /app/frontend +COPY apps/server/tsconfig.json apps/server/ +COPY apps/server/src/ apps/server/src/ -COPY frontend/package.json frontend/bun.lock ./ -RUN bun install --frozen-lockfile +FROM workspace AS frontend-builder -COPY shared/ ../shared/ -COPY frontend/ ./ +RUN bun install --frozen-lockfile --filter ./apps/web + +COPY packages/shared/ packages/shared/ +COPY apps/web/ apps/web/ +WORKDIR /app/apps/web RUN bun run build # Final image: one Bun process serves the UI, the API and the WebSocket on port 80 @@ -44,12 +54,14 @@ RUN apt-get update \ COPY --from=snapraid-builder /usr/local/bin/snapraid /usr/local/bin/snapraid -COPY --from=backend-builder /app/backend /app/backend -COPY shared/ /app/shared/ -# The backend loads the frontend's server from here (backend/src/frontend.ts) -COPY --from=frontend-builder /app/frontend/.output /app/frontend/.output -# The version, which the backend reads from it (backend/src/version.ts) -COPY frontend/package.json /app/frontend/package.json +COPY --from=backend-builder /app/tsconfig.base.json /app/tsconfig.base.json +COPY --from=backend-builder /app/node_modules /app/node_modules +COPY --from=backend-builder /app/apps/server /app/apps/server +COPY packages/shared/ /app/packages/shared/ +# The backend loads the frontend's server from here (apps/server/src/frontend.ts) +COPY --from=frontend-builder /app/apps/web/.output /app/apps/web/.output +# The version, which the backend reads from it (apps/server/src/version.ts) +COPY apps/web/package.json /app/apps/web/package.json # The documentation, which the MCP endpoint offers to AI agents COPY docs/*.md /app/docs/ @@ -63,7 +75,7 @@ EXPOSE 80 HEALTHCHECK --interval=30s --timeout=10s --start-period=15s --retries=3 \ CMD curl -f http://localhost/api/health || exit 1 -WORKDIR /app/backend +WORKDIR /app/apps/server # tini as PID 1 passes docker stop's SIGTERM on to Bun and reaps the processes it starts ENTRYPOINT ["/usr/bin/tini", "--"] CMD ["bun", "src/main.ts"] diff --git a/docs/architecture.md b/docs/architecture.md index d38619b..e27a64c 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -23,9 +23,9 @@ SnapRAID UI has three parts: | Part | Technology | Location | |---|---|---| -| Backend | [Bun](https://bun.sh/) + [Hono](https://hono.dev/), REST API and WebSocket; serves the built frontend on the same port | `backend/src/` | -| Frontend | React 19, TanStack Start / Router / Query, Tailwind CSS, shadcn/ui, Paraglide JS; server-rendered by Nitro, which builds one fetch handler that the backend runs | `frontend/src/` | -| Shared code | Types and logic used by both sides (types, i18n markers, SMART assessment, fill-up forecast, `--force-*` detection) | `shared/` | +| Backend | [Bun](https://bun.sh/) + [Hono](https://hono.dev/), REST API and WebSocket; serves the built frontend on the same port | `apps/server/src/` | +| Frontend | React 19, TanStack Start / Router / Query, Tailwind CSS, shadcn/ui, Paraglide JS; server-rendered by Nitro, which builds one fetch handler that the backend runs | `apps/web/src/` | +| Shared code | Types and logic used by both sides (types, i18n markers, SMART assessment, fill-up forecast, `--force-*` detection), the workspace package `@snapraid-ui/shared` | `packages/shared/src/` | There is no database. All state lives in JSON files and log files in the data directory (`SNAPRAID_BASE_PATH`), next to your `snapraid.conf` files. The backend runs the `snapraid` binary as a child process and reads its **structured log** (available since SnapRAID 14.0) instead of scraping the human-readable console output. @@ -34,17 +34,18 @@ There is no database. All state lives in JSON files and log files in the data di The Docker image (`docker/Dockerfile`) is a multi-stage build: 1. **snapraid-builder** (Debian bookworm-slim) downloads the pinned SnapRAID release (`ARG SNAPRAID_VERSION=15.1`) and compiles it. -2. **backend-builder** (`oven/bun:1.4.4-slim`) installs the backend's packages with `bun install --frozen-lockfile --production`. -3. **frontend-builder** (`oven/bun:1.4.4-slim`) runs `bun install --frozen-lockfile` and `bun run build`, which produces `frontend/.output/`. -4. The **final image** is based on `oven/bun:1.4.4-slim`, adds `curl`, `smartmontools` (SnapRAID calls `smartctl` for `smart` and `probe`) and `tini`, and copies in the SnapRAID binary, the backend with its packages, `shared/` and the built frontend. +2. **workspace** (`oven/bun:1.4.4-slim`) holds the workspace's `package.json` files, `bun.lock` and `bunfig.toml`, which the next two stages install from. +3. **backend-builder** installs the backend's packages with `bun install --frozen-lockfile --production --filter ./apps/server`. +4. **frontend-builder** runs `bun install --frozen-lockfile --filter ./apps/web` and `bun run build` in `apps/web/`, which produces `apps/web/.output/`. +5. The **final image** is based on `oven/bun:1.4.4-slim`, adds `curl`, `smartmontools` (SnapRAID calls `smartctl` for `smart` and `probe`) and `tini`, and copies in the SnapRAID binary, the backend with its packages (`/app/apps/server`, `/app/node_modules`), `packages/shared/` and the built frontend, at their paths in the repository under `/app`. -The container runs a single process, the backend, under [tini](https://github.com/krallin/tini) as PID 1, which passes `docker stop`'s `SIGTERM` on and reaps the processes the backend starts: +The container runs a single process, the backend (in `/app/apps/server`), under [tini](https://github.com/krallin/tini) as PID 1, which passes `docker stop`'s `SIGTERM` on and reaps the processes the backend starts: ``` bun src/main.ts ``` -It listens on port 80 (`PORT=80`) and answers `/api` and `/ws` itself. Every other path goes to the frontend: Nitro builds the server-side rendering and the static files (`frontend/.output/`) with its `standard` preset into one fetch handler, which `backend/src/frontend.ts` loads from `../frontend/.output/server/index.mjs`. +It listens on port 80 (`PORT=80`) and answers `/api` and `/ws` itself. Every other path goes to the frontend: Nitro builds the server-side rendering and the static files (`apps/web/.output/`) with its `standard` preset into one fetch handler, which `apps/server/src/frontend.ts` loads from `apps/web/.output/server/index.mjs`. ```mermaid flowchart LR @@ -62,19 +63,19 @@ The image sets `SNAPRAID_BASE_PATH=/app/snapraid`, `SNAPRAID_BIN=/usr/local/bin/ ## Standalone binary -`scripts/build-binary.sh` compiles `backend/src/bin.ts` with `bun build --compile` into one executable per architecture (`snapraid-ui-linux-x86_64`, `snapraid-ui-linux-aarch64`), which the release attaches to the GitHub release. The binary embeds the Bun runtime, the backend bundled with its packages, and with `--asset` the built frontend (`frontend/.output/`), `frontend/package.json` and `docs/*.md`. Inside the binary these files lie under `/$bunfs/root/` at their paths in the repository (`frontend/…`, `docs/…`); `backend/src/app-files.ts` finds them there, and next to `backend/` in the repository and the image: +`scripts/build-binary.sh` compiles `apps/server/src/bin.ts` with `bun build --compile` into one executable per architecture (`snapraid-ui-linux-x86_64`, `snapraid-ui-linux-aarch64`), which the release attaches to the GitHub release. The binary embeds the Bun runtime, the backend bundled with its packages, and with `--asset` the built frontend (`apps/web/.output/`), `apps/web/package.json` and `docs/*.md`. Inside the binary these files lie under `/$bunfs/root/` at their paths in the repository (`apps/web/…`, `docs/…`); `apps/server/src/app-files.ts` finds them there, and at the same paths from the root in the repository and the image: | File | Read by | For | |---|---|---| -| `frontend/.output/server/index.mjs` | `backend/src/frontend.ts` | The UI | -| `frontend/package.json` | `backend/src/version.ts` | The version, for `--version`, Home Assistant and the MCP endpoint; `SNAPRAID_UI_VERSION` overrides it | -| `docs/*.md` | `backend/src/mcp.ts` | The documentation the MCP endpoint offers | +| `apps/web/.output/server/index.mjs` | `apps/server/src/frontend.ts` | The UI | +| `apps/web/package.json` | `apps/server/src/version.ts` | The version, for `--version`, Home Assistant and the MCP endpoint; `SNAPRAID_UI_VERSION` overrides it | +| `docs/*.md` | `apps/server/src/mcp.ts` | The documentation the MCP endpoint offers | `bin.ts` first imports `bin-cli.ts`, which answers `--version` and `--help` and sets the data directory to `./snapraid` when `SNAPRAID_BASE_PATH` is not set, and then `main.ts`. SnapRAID and `smartctl` are not inside: the binary runs the ones installed on the host. ## Backend -`backend/src/main.ts` builds the Hono app, wires the modules together and starts `Bun.serve` (with its `websocket` handler for `/ws`) on `SNAPRAID_UI_HOST` (`0.0.0.0` when unset) at `PORT` (8080 when unset, 80 in the image). On start it loads `config.json` (creating a default one if it is missing), creates the log directory, loads the schedules and runs one log rotation. +`apps/server/src/main.ts` builds the Hono app, wires the modules together and starts `Bun.serve` (with its `websocket` handler for `/ws`) on `SNAPRAID_UI_HOST` (`0.0.0.0` when unset) at `PORT` (8080 when unset, 80 in the image). On start it loads `config.json` (creating a default one if it is missing), creates the log directory, loads the schedules and runs one log rotation. Middleware, in order: @@ -125,11 +126,11 @@ Every SnapRAID call goes through `snapraidCommand()` in `config.ts`: it runs `SN Aborting sends `SIGINT`, which SnapRAID treats like Ctrl+C: it stops at the next block and saves its state. The job stays current until the process has exited. -When a job fails, the executor checks its output for SnapRAID's suggestion `'snapraid --force-zero|empty|uuid …'` (`shared/force-option.ts`) and reports it as `forceOption`, so the UI can offer a confirmed retry. See [Usage](usage.md) for the user side. +When a job fails, the executor checks its output for SnapRAID's suggestion `'snapraid --force-zero|empty|uuid …'` (`packages/shared/src/force-option.ts`) and reports it as `forceOption`, so the UI can offer a confirmed retry. See [Usage](usage.md) for the user side. ## SnapRAID engine -Everything the UI could get from [snapraid-daemon](https://github.com/amadvance/snapraid-daemon)'s REST API instead of calling the CLI itself sits behind one interface, `SnapRaidEngine` in `backend/src/engine/engine.ts`. Routes, WebSocket and `main.ts` call `getEngine()`; scheduler and spindown keep `activeEngine`, which always passes on to the engine in place, so the engine can be switched while the backend runs. +Everything the UI could get from [snapraid-daemon](https://github.com/amadvance/snapraid-daemon)'s REST API instead of calling the CLI itself sits behind one interface, `SnapRaidEngine` in `apps/server/src/engine/engine.ts`. Routes, WebSocket and `main.ts` call `getEngine()`; scheduler and spindown keep `activeEngine`, which always passes on to the engine in place, so the engine can be switched while the backend runs. There are two implementations: @@ -164,7 +165,7 @@ Known gaps of the daemon engine: ### Keeping the engine in line with snapraid-daemon -The API was last compared with snapraid-daemon **`v2.1`**; the version is in `backend/src/engine/REVIEWED_DAEMON_VERSION`. A weekly workflow (job `snapraid-daemon` in `upstream-release.yml`) opens an issue when a newer tag is out. For each new version: +The API was last compared with snapraid-daemon **`v2.1`**; the version is in `apps/server/src/engine/REVIEWED_DAEMON_VERSION`. A weekly workflow (job `snapraid-daemon` in `upstream-release.yml`) opens an issue when a newer tag is out. For each new version: 1. Compare `snapraidd.yaml` (the OpenAPI spec) between the reviewed and the new tag. 2. If the daemon gained an endpoint for something in the "stays with the UI" list (for example `dup` or `list`), add a method to `SnapRaidEngine`, implement it in the CLI engine, move the callers to it and add a step to the contract tests. @@ -173,7 +174,7 @@ The API was last compared with snapraid-daemon **`v2.1`**; the version is in `ba ### Tests -`backend/src/__tests__/engine-contract.ts` holds what every engine has to do: a sync succeeds, the status lists the disks, the diff finds a new file, `afterRun` runs while the job is still current, and so on. `engine-contract.test.ts` runs it against the fake engine (`__tests__/fake-engine.ts`) always, against the CLI engine with a real SnapRAID binary, and against the daemon engine with a running snapraid-daemon (see [Development](development.md#tests)). +`apps/server/src/__tests__/engine-contract.ts` holds what every engine has to do: a sync succeeds, the status lists the disks, the diff finds a new file, `afterRun` runs while the job is still current, and so on. `engine-contract.test.ts` runs it against the fake engine (`__tests__/fake-engine.ts`) always, against the CLI engine with a real SnapRAID binary, and against the daemon engine with a running snapraid-daemon (see [Development](development.md#tests)). Without a daemon, `daemon-engine.test.ts` runs the daemon engine against a stand-in that answers like snapraid-daemon 2.0: queuing and following a task, progress, abort, the fallback, auth and errors, and the mapping of its JSON to the UI's types. `engine-settings.test.ts` covers the settings and which engine runs which config, `scheduler.test.ts` the scheduler against the fake engine. @@ -189,7 +190,7 @@ The outcome of a run (`RunResult`: `ok`, `warning`, `error`, `aborted`, `incompl - `summary:exit:ok` / `warning` map directly; `equal`, `diff`, `nodup`, `dup` and `recovered` count as `ok`; `recoverable` as `warning`; anything else as `error`; - no `summary:exit` at all means `error` if there is a `msg:fatal:` line, otherwise `incomplete` (still running or killed). -Each log's `conf:file` tag links it to a config, which is how the dashboard finds the last sync and scrub of the selected config. The frontend has its own reader for the log viewer (`frontend/src/lib/log-parse.ts`). Parser tests use real SnapRAID output in `backend/src/parsers/__tests__/fixtures/`. +Each log's `conf:file` tag links it to a config, which is how the dashboard finds the last sync and scrub of the selected config. The frontend has its own reader for the log viewer (`apps/web/src/lib/log-parse.ts`). Parser tests use real SnapRAID output in `apps/server/src/parsers/__tests__/fixtures/`. ## Job runner and live output @@ -217,7 +218,7 @@ sequenceDiagram - A job can carry an `afterRun` step that runs before the job is released, e.g. removing the data disk from the config after a successful `sync -E`, or recording a replacement step. Nothing else can start in between. - After a manual `sync`, `scrub`, `fix` or `check`, a notification is sent if the settings ask for manual jobs. After `status` or `diff` via `/execute`, a `status` message with the parsed status is broadcast. -The frontend (`frontend/src/hooks/useJob.tsx`) combines the WebSocket with polling `GET /api/snapraid/current-job` every 5 seconds, so it picks up jobs started elsewhere (another tab, a schedule) and notices jobs that ended while it was disconnected. `frontend/src/lib/job-tracker.ts` makes sure the end of a job is handled exactly once. The WebSocket client reconnects 3 seconds after the connection drops. +The frontend (`apps/web/src/hooks/useJob.tsx`) combines the WebSocket with polling `GET /api/snapraid/current-job` every 5 seconds, so it picks up jobs started elsewhere (another tab, a schedule) and notices jobs that ended while it was disconnected. `apps/web/src/lib/job-tracker.ts` makes sure the end of a job is handled exactly once. The WebSocket client reconnects 3 seconds after the connection drops. ## Scheduler @@ -232,7 +233,7 @@ Scheduled jobs run through the same executor as manual ones, so their output, pr ## Storage -Everything lives in the data directory, `SNAPRAID_BASE_PATH`. In the image that is `/app/snapraid`; without the variable the backend uses `../snapraid` relative to its working directory. Relative paths in API requests (e.g. `configPath: "snapraid.conf"`) are resolved against it; absolute paths are used as they are. +Everything lives in the data directory, `SNAPRAID_BASE_PATH`. In the image that is `/app/snapraid`; without the variable the backend uses `../../snapraid` relative to its working directory (`snapraid/` in the repository's root). Relative paths in API requests (e.g. `configPath: "snapraid.conf"`) are resolved against it; absolute paths are used as they are. | File | Written by | |---|---| @@ -249,11 +250,11 @@ Everything lives in the data directory, `SNAPRAID_BASE_PATH`. In the image that | `auth.json` | Password changed in the UI (hash) and passkeys (only with login enabled) | | `logs/*.log` | One structured log per job (directory set in `config.json`) | -Writes go through `node:fs`: the JSON state files through `writeTextFileAtomic` (`backend/src/atomic-write.ts`, a temporary file renamed over the old one), other writes (the SnapRAID configs among them) are plain `writeFile` calls. Most files are read again whenever they are needed. Two things are read only at startup: the log directory and retention settings from `config.json`, and the cron timers, which are rebuilt from `schedules.json` on start, on changes through the API and after a restore. See [Configuration](configuration.md) for details on each file. +Writes go through `node:fs`: the JSON state files through `writeTextFileAtomic` (`apps/server/src/atomic-write.ts`, a temporary file renamed over the old one), other writes (the SnapRAID configs among them) are plain `writeFile` calls. Most files are read again whenever they are needed. Two things are read only at startup: the log directory and retention settings from `config.json`, and the cron timers, which are rebuilt from `schedules.json` on start, on changes through the API and after a restore. See [Configuration](configuration.md) for details on each file. ## Frontend -The frontend is a [TanStack Start](https://tanstack.com/start) app with file-based routing. Nitro builds it into `frontend/.output/`, one fetch handler that the backend loads to render pages on the server and serve the client bundle (see [Container layout](#container-layout)). +The frontend is a [TanStack Start](https://tanstack.com/start) app with file-based routing. Nitro builds it into `apps/web/.output/`, one fetch handler that the backend loads to render pages on the server and serve the client bundle (see [Container layout](#container-layout)). | Path | Content | |---|---| @@ -270,11 +271,11 @@ Server state is managed with TanStack Query. Only the array status is persisted ## Internationalization -UI texts live in `frontend/messages/{en,de,it}.json` and are compiled by [Paraglide JS](https://inlang.com/m/gerre34r/library-inlang-paraglideJs) into `frontend/src/paraglide/` (generated, not committed). Components call `m.key()`. The language is resolved in this order: an explicit choice (cookie), the browser language, English. +UI texts live in `apps/web/messages/{en,de,it}.json` and are compiled by [Paraglide JS](https://inlang.com/m/gerre34r/library-inlang-paraglideJs) into `apps/web/src/paraglide/` (generated, not committed). Components call `m.key()`. The language is resolved in this order: an explicit choice (cookie), the browser language, English. -The backend does not know the viewer's language. For errors a user can run into, it sends a marked key instead of text: `msg("server_error_job_running")` from `shared/i18n.ts` produces `\u0002["server_error_job_running",{}]\u0003`. The frontend (`frontend/src/lib/i18n.ts`, `localizeServer()`) replaces such markers with the translated message; plain text, such as SnapRAID's own output, passes through unchanged. Some validation errors for malformed requests are plain English strings. +The backend does not know the viewer's language. For errors a user can run into, it sends a marked key instead of text: `msg("server_error_job_running")` from `packages/shared/src/i18n.ts` produces `\u0002["server_error_job_running",{}]\u0003`. The frontend (`apps/web/src/lib/i18n.ts`, `localizeServer()`) replaces such markers with the translated message; plain text, such as SnapRAID's own output, passes through unchanged. Some validation errors for malformed requests are plain English strings. -Notifications are the exception: they leave the server by e-mail, ntfy or webhook in the language set in the notification settings (`backend/src/notification-events.ts`). +Notifications are the exception: they leave the server by e-mail, ntfy or webhook in the language set in the notification settings (`apps/server/src/notification-events.ts`). See [Development](development.md#translations) for how to add or change texts. @@ -282,7 +283,7 @@ See [Development](development.md#translations) for how to add or change texts. All endpoints are under `/api` and exchange JSON unless noted otherwise. With the login enabled, every endpoint except `/api/auth/*` requires the session cookie. Errors come back as `{"error": "…"}` with a 4xx or 5xx status; the message may be a marked i18n key (see [above](#internationalization)). -Most endpoints that work on a SnapRAID config take its path: as the `path` query parameter for `GET`/`DELETE`, or as `configPath` in the JSON body for `POST`. The path is the one stored in `config.json` (relative to the data directory, or absolute). Response types refer to the interfaces in `shared/types.ts`. +Most endpoints that work on a SnapRAID config take its path: as the `path` query parameter for `GET`/`DELETE`, or as `configPath` in the JSON body for `POST`. The path is the one stored in `config.json` (relative to the data directory, or absolute). Response types refer to the interfaces in `packages/shared/src/types.ts`. > [!NOTE] > The API is the internal interface between the frontend and the backend, not a versioned public API. It can change between releases. diff --git a/docs/assets/stylesheets/extra.css b/docs/assets/stylesheets/extra.css index 98190f9..8fb90b2 100644 --- a/docs/assets/stylesheets/extra.css +++ b/docs/assets/stylesheets/extra.css @@ -1,4 +1,4 @@ -/* App colors: dark tile of the logo, cyan to indigo tint (frontend/public/favicon.svg) */ +/* App colors: dark tile of the logo, cyan to indigo tint (apps/web/public/favicon.svg) */ :root { --md-primary-fg-color: #101828; --md-primary-fg-color--light: #1e293b; diff --git a/docs/configuration.md b/docs/configuration.md index 45e30ac..9fd2606 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -35,7 +35,7 @@ Inside the container the UI listens on port 80. Change the port on the host side ## Data directory -All state lives in one directory, `/app/snapraid` in the container. Mount it as a volume so it survives updates. The [installer](installation.md#without-docker) uses `/var/lib/snapraid-ui`. When you run the backend from the repository without `SNAPRAID_BASE_PATH`, it is `../snapraid` relative to the backend's working directory, which is `snapraid/` in the repository root; the standalone binary uses `snapraid/` in its working directory. +All state lives in one directory, `/app/snapraid` in the container. Mount it as a volume so it survives updates. The [installer](installation.md#without-docker) uses `/var/lib/snapraid-ui`. When you run the backend from the repository without `SNAPRAID_BASE_PATH`, it is `../../snapraid` relative to the backend's working directory (`apps/server/`), which is `snapraid/` in the repository root; the standalone binary uses `snapraid/` in its working directory. | Path | Contents | |---|---| diff --git a/docs/development.md b/docs/development.md index caf903b..63da7ee 100644 --- a/docs/development.md +++ b/docs/development.md @@ -6,9 +6,9 @@ This page gets you from a fresh clone to a running development setup, and covers | Tool | Version | Needed for | |---|---|---| -| [Bun](https://bun.com/) | the version in `packageManager` of `backend/package.json` and `frontend/package.json` (1.4.4) | Everything JavaScript: runs the backend and its tests, installs the packages, runs Vite and Vitest and Playwright's test runner (`bun --bun playwright test`), compiles the standalone binary (CI and the image use the same version) | -| [fish](https://fishshell.com/) | | `start.sh` is a fish script | -| `curl`, `tar`, `gcc`, `make` | | Building SnapRAID for the demo sandbox (`dev/setup.sh`) | +| [Bun](https://bun.com/) | the version in `packageManager` of the root `package.json` (1.4.4) | Everything JavaScript: runs the backend and its tests, installs the packages, runs Vite and Vitest and Playwright's test runner (`bun --bun playwright test`), compiles the standalone binary (CI and the image use the same version) | +| [fish](https://fishshell.com/) | | `scripts/start.sh` is a fish script | +| `curl`, `tar`, `gcc`, `make` | | Building SnapRAID for the demo sandbox (`scripts/dev/setup.sh`) | | SnapRAID | 14.0 or newer | Only for running against a real array without `--demo`; it has to be on your `PATH` or set via `SNAPRAID_BIN` | | Docker or Podman | | Only for building the image | @@ -17,25 +17,25 @@ This page gets you from a fresh clone to a running development setup, and covers ```bash git clone https://github.com/firsttris/snapraid-ui cd snapraid-ui -./install.sh # bun install in frontend/ and backend/ -./start.sh --demo # sandbox with fake disks, no real array needed +bun install # all packages of the workspace, from the one bun.lock +bun run demo # scripts/start.sh --demo: sandbox with fake disks, no real array needed ``` Open **http://localhost:3000**. The frontend dev server runs on port 3000, the backend API and WebSocket on port 8080. `Ctrl+C` stops both. -`./start.sh` has two modes: +`scripts/start.sh` (`bun run dev`, `bun run demo`) has two modes: | Command | Data directory | SnapRAID binary | Extra | |---|---|---|---| -| `./start.sh` | `./snapraid/` (created on first start, ignored by git) | `snapraid` from your `PATH` | Works against your real configs | -| `./start.sh --demo` | `dev/sandbox/` | `dev/bin/snapraid` | Sets `SNAPRAID_EXTRA_ARGS=--test-skip-device` and `SNAPRAID_DEMO=1` | +| `bun run dev` (`scripts/start.sh`) | `./snapraid/` (created on first start, ignored by git) | `snapraid` from your `PATH` | Works against your real configs | +| `bun run demo` (`scripts/start.sh --demo`) | `scripts/dev/sandbox/` | `scripts/dev/bin/snapraid` | Sets `SNAPRAID_EXTRA_ARGS=--test-skip-device` and `SNAPRAID_DEMO=1` | -Under the hood it starts `bun run dev` in `backend/` (`bun --watch`, so the backend restarts on changes) and `bun run dev` in `frontend/` (Vite with hot reload). You can run these two commands yourself in separate terminals; set `SNAPRAID_BASE_PATH` and friends first (see [Configuration](configuration.md)), otherwise the backend uses `../snapraid` relative to `backend/`. +Under the hood it starts `bun run dev` in `apps/server/` (`bun --watch`, so the backend restarts on changes) and `bun run dev` in `apps/web/` (Vite with hot reload). You can run these two commands yourself in separate terminals; set `SNAPRAID_BASE_PATH` and friends first (see [Configuration](configuration.md)), otherwise the backend uses `snapraid/` in the repository's root (`../../snapraid` relative to `apps/server/`). > [!NOTE] -> In development the frontend calls the backend directly at `http://localhost:8080` (`frontend/src/lib/api/constants.ts`), and the backend only accepts credentials from the same hostname. Open the dev frontend as `http://localhost:3000`, not via a LAN IP. +> In development the frontend calls the backend directly at `http://localhost:8080` (`apps/web/src/lib/api/constants.ts`), and the backend only accepts credentials from the same hostname. Open the dev frontend as `http://localhost:3000`, not via a LAN IP. -To run the production build as the image does, build the frontend (`bun run build` in `frontend/`) and start the backend with `bun run start` in `backend/`: it finds `frontend/.output/` and serves the UI, the API and the WebSocket together on port 8080. Without a build it serves only the API. +To run the production build as the image does, build the frontend (`bun run build` in `apps/web/`) and start the backend with `bun run start` in `apps/server/`: it finds `apps/web/.output/` and serves the UI, the API and the WebSocket together on port 8080. Without a build it serves only the API.
Stopping leftover processes @@ -50,16 +50,16 @@ pkill -f "vite dev" ## Demo sandbox -`dev/setup.sh` (run automatically by `./start.sh --demo`, a no-op once everything exists) prepares a self-contained SnapRAID environment: +`scripts/dev/setup.sh` (run automatically by `bun run demo`, a no-op once everything exists) prepares a self-contained SnapRAID environment: -- builds SnapRAID (`SNAPRAID_VERSION`, default 15.1) from the official release tarball into `dev/bin/snapraid`, cached in `dev/.cache/`; -- installs the backend's packages (`bun install --frozen-lockfile` in `backend/`) if `backend/node_modules/` is missing; -- creates `dev/sandbox/` with the array "Media Array": four data "disks" (`disks/disk1`–`disk4`) and a parity disk (`disks/parity1`) filled with random files, a `snapraid.conf` and `config.json`; +- builds SnapRAID (`SNAPRAID_VERSION`, default 15.1) from the official release tarball into `scripts/dev/bin/snapraid`, cached in `scripts/dev/.cache/`; +- installs the packages (`bun install --frozen-lockfile` in the root) if `apps/server/node_modules/` is missing; +- creates `scripts/dev/sandbox/` with the array "Media Array": four data "disks" (`disks/disk1`–`disk4`) and a parity disk (`disks/parity1`) filled with random files, a `snapraid.conf` and `config.json`; - puts copies of a few files on other disks, for the duplicates page; - runs sync, scrub, touch and sync again with their logs, then adds, changes, moves and deletes files and runs `diff` and `status`, so that every page has something to show; -- gives the sandbox a past with `backend/scripts/seed-demo.ts` (run with Bun in `backend/`, whose packages it uses): the job logs move back in time (last sync 10 hours ago, scrub yesterday), `usage-history.json` gets two months of disk usage with one disk filling up, `schedules.json` a nightly sync with touch and scrub, a weekly scrub and a SMART check, and `maintenance.json` Docker pause and spindown. +- gives the sandbox a past with `apps/server/scripts/seed-demo.ts` (run with Bun in `apps/server/`, whose packages it uses): the job logs move back in time (last sync 10 hours ago, scrub yesterday), `usage-history.json` gets two months of disk usage with one disk filling up, `schedules.json` a nightly sync with touch and scrub, a weekly scrub and a SMART check, and `maintenance.json` Docker pause and spindown. -All sandbox disks are directories on one filesystem, which SnapRAID only accepts with `--test-skip-device`. They hold a few KiB and have no SMART data, power state or Docker next to them, so `SNAPRAID_DEMO=1` makes the backend answer like a real home server (`backend/src/demo.ts`): +All sandbox disks are directories on one filesystem, which SnapRAID only accepts with `--test-skip-device`. They hold a few KiB and have no SMART data, power state or Docker next to them, so `SNAPRAID_DEMO=1` makes the backend answer like a real home server (`apps/server/src/demo.ts`): - `smart` and `probe`: generated results in the real structured log format: a healthy HDD, an SSD, an HDD with growing reallocated sectors, a sleeping 16 TB disk and an 18 TB parity disk; - `status`: the real log of the sandbox, with the sizes, file counts and fragmentation of those disks and a scrub that keeps up (64 % verified, the oldest block 17 days old) instead of the sandbox's figures; @@ -67,20 +67,24 @@ All sandbox disks are directories on one filesystem, which SnapRAID only accepts - Docker: three containers (immich, jellyfin, nextcloud) that can be paused and resumed. ```bash -dev/setup.sh --reset # recreate the sandbox from scratch -DISKS_DIR=/mnt dev/setup.sh --reset # the disks at /mnt/disk1 … /mnt/parity1, as the screenshots show them -SNAPRAID_VERSION=15.1 dev/setup.sh --reset # build another SnapRAID release +scripts/dev/setup.sh --reset # recreate the sandbox from scratch +DISKS_DIR=/mnt scripts/dev/setup.sh --reset # the disks at /mnt/disk1 … /mnt/parity1, as the screenshots show them +SNAPRAID_VERSION=15.1 scripts/dev/setup.sh --reset # build another SnapRAID release ``` -`dev/bin/`, `dev/.cache/` and `dev/sandbox/` are ignored by git. +`scripts/dev/bin/`, `scripts/dev/.cache/` and `scripts/dev/sandbox/` are ignored by git. ## Project structure ```text -backend/ - package.json scripts and packages (Hono, Croner, Nodemailer, …), bun.lock next to it +package.json the Bun workspace (apps/*, packages/*, e2e) and packageManager; dev, demo +bun.lock the one lockfile of all packages +bunfig.toml linker = "isolated": each package sees only its own dependencies +tsconfig.base.json compiler options every package shares +apps/server/ the backend + package.json scripts and packages (Hono, Croner, Nodemailer, …) tsconfig.json compiler options, @shared/* paths (Bun resolves them at runtime too) - scripts/ seed-demo.ts, the demo sandbox's history (dev/setup.sh) + scripts/ seed-demo.ts, the demo sandbox's history (scripts/dev/setup.sh) src/ main.ts app setup, middleware, startup bin.ts entry point of the standalone binary; bin-cli.ts: --version, --help @@ -92,7 +96,7 @@ backend/ parsers/ structured log parsers, tests and real SnapRAID fixtures __tests__/ backend unit tests *.ts auth, scheduler, notifications, log manager, histories, wizards, backup, demo -frontend/ +apps/web/ the frontend messages/ UI texts: en.json, de.json, it.json project.inlang/ Paraglide/inlang project settings scripts/ add-messages.py @@ -103,20 +107,21 @@ frontend/ hooks/ TanStack Query hooks, running job state, selected config lib/ API client, WebSocket, log parsing, theme, i18n, commands lib/__tests__/ frontend unit tests -shared/ types and logic used by backend and frontend +packages/shared/ @snapraid-ui/shared: types and logic used by backend and frontend (src/) docker/ Dockerfile, Compose file, Quadlet units scripts/build-binary.sh the standalone binary (bun build --compile) scripts/install.sh installer of the binary with a systemd service, attached to each release -dev/setup.sh demo sandbox +scripts/start.sh local start (bun run dev, bun run demo) +scripts/dev/setup.sh demo sandbox +scripts/social-preview/ the social preview image e2e/ end-to-end tests (Playwright) against real SnapRAID and snapraid-daemon -install.sh, start.sh local setup and start ``` -Both sides import shared code as `@shared/…` through the `paths` in their `tsconfig.json` (`backend/tsconfig.json`, `frontend/tsconfig.json`). Keep code in `shared/` free of Bun-, Node- or browser-specific APIs. +Both apps depend on the workspace package `@snapraid-ui/shared` and import it as `@shared/…`: the `paths` in their `tsconfig.json` (`apps/server/tsconfig.json`, `apps/web/tsconfig.json`) map `@shared/*` to `node_modules/@snapraid-ui/shared/src/*`, the package `bun install` links there. Keep code in `packages/shared/` free of Bun-, Node- or browser-specific APIs. ## Scripts -Frontend (`frontend/package.json`, run with `bun run