chore(deps): batch dependency updates, pnpm v11 migration, and fix integration cache (#5939)

This commit is contained in:
Thomas authored and GitHub committed 2026-06-12 13:52:43 +02:00
1 parent 905f795b56
commit d16571d4c4
35 files changed
+2104 -2052

No files matched your search

+1 -1
View File
@@ -22,7 +22,7 @@
automerge: true, automerge: true,
}, },
{ {
// disallow automerge for onlyBuiltDependencies // disallow automerge for allowBuilds dependencies (native builds need manual verification)
matchPackageNames: [ matchPackageNames: [
"@tree-sitter-grammars/tree-sitter-yaml", "@tree-sitter-grammars/tree-sitter-yaml",
"bcrypt", "bcrypt",
+1 -1
View File
@@ -52,7 +52,7 @@ jobs:
xargs -r -a /tmp/changed-files pnpm exec oxfmt --write xargs -r -a /tmp/changed-files pnpm exec oxfmt --write
- name: Commit fixes - name: Commit fixes
uses: stefanzweifel/git-auto-commit-action@v5 uses: stefanzweifel/git-auto-commit-action@v7
with: with:
commit_message: "style: auto-fix lint and format" commit_message: "style: auto-fix lint and format"
+3 -3
View File
@@ -38,9 +38,9 @@ jobs:
env: env:
CI: "true" CI: "true"
- name: Setup Pages - name: Setup Pages
uses: actions/configure-pages@v5 uses: actions/configure-pages@v6
- name: Upload pages artifact - name: Upload pages artifact
uses: actions/upload-pages-artifact@v4 uses: actions/upload-pages-artifact@v5
with: with:
path: "apps/docs/build" path: "apps/docs/build"
@@ -53,4 +53,4 @@ jobs:
steps: steps:
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
id: deployment id: deployment
uses: actions/deploy-pages@v4 uses: actions/deploy-pages@v5
@@ -31,7 +31,7 @@ jobs:
run: pnpm api-dump-contributors run: pnpm api-dump-contributors
- name: Commit updated contributors - name: Commit updated contributors
uses: EndBug/add-and-commit@v9 uses: EndBug/add-and-commit@v10
with: with:
add: "apps/docs/static" add: "apps/docs/static"
default_author: github_actor default_author: github_actor
+1 -1
View File
@@ -58,7 +58,7 @@ jobs:
echo "OpenAPI spec updated ($(jq '.paths | length' /tmp/openapi.json) paths)" echo "OpenAPI spec updated ($(jq '.paths | length' /tmp/openapi.json) paths)"
- name: Commit updated spec - name: Commit updated spec
uses: EndBug/add-and-commit@v9 uses: EndBug/add-and-commit@v10
with: with:
add: "apps/docs/static/api/" add: "apps/docs/static/api/"
default_author: github_actor default_author: github_actor
-1
View File
@@ -1,3 +1,2 @@
[tools] [tools]
node = "24.16.0" node = "24.16.0"
pnpm = "10.34.1"
+2 -6
View File
@@ -1,6 +1,2 @@
# Expo doesn't play nice with pnpm by default. # pnpm v11: non-auth settings moved to pnpm-workspace.yaml
# The symbolic links of pnpm break the rules of Expo monorepos. # Keep this file for any future registry/auth configuration
# @link https://docs.expo.dev/guides/monorepos/#common-issues
node-linker=hoisted
strict-peer-dependencies=false
engine-strict=true
+2 -4
View File
@@ -289,13 +289,11 @@ const config: Config = {
}; };
}, },
"@signalwire/docusaurus-plugin-llms-txt", "@signalwire/docusaurus-plugin-llms-txt",
async function tailwindCssPlugin(context, options) { async function tailwindCssPlugin() {
return { return {
name: "docusaurus-tailwindcss", name: "docusaurus-tailwindcss",
configurePostCss(postcssOptions) { configurePostCss(postcssOptions) {
// Appends TailwindCSS and AutoPrefixer. postcssOptions.plugins.push(require("@tailwindcss/postcss"));
postcssOptions.plugins.push(require("tailwindcss"));
postcssOptions.plugins.push(require("autoprefixer"));
return postcssOptions; return postcssOptions;
}, },
}; };
+4 -4
View File
@@ -32,25 +32,25 @@
"@scalar/api-reference-react": "catalog:", "@scalar/api-reference-react": "catalog:",
"@signalwire/docusaurus-plugin-llms-txt": "^1.2.2", "@signalwire/docusaurus-plugin-llms-txt": "^1.2.2",
"@tabler/icons-react": "catalog:", "@tabler/icons-react": "catalog:",
"autoprefixer": "^10.5.0", "@tailwindcss/postcss": "^4.3.0",
"clsx": "catalog:", "clsx": "catalog:",
"dayjs": "catalog:", "dayjs": "catalog:",
"docusaurus-plugin-image-zoom": "^3.0.1", "docusaurus-plugin-image-zoom": "^3.0.1",
"leader-line-new": "^1.1.9", "leader-line-new": "^1.1.9",
"postcss": "^8.4.32", "postcss": "^8.5.10",
"posthog-docusaurus": "^2.0.5", "posthog-docusaurus": "^2.0.5",
"prism-react-renderer": "^2.4.1", "prism-react-renderer": "^2.4.1",
"random-word-slugs": "^0.1.7", "random-word-slugs": "^0.1.7",
"react": "catalog:", "react": "catalog:",
"react-dom": "catalog:", "react-dom": "catalog:",
"swiper": "^12.2.0", "swiper": "^12.2.0",
"tailwindcss": "^3.4.19" "tailwindcss": "^4.3.0"
}, },
"devDependencies": { "devDependencies": {
"@docusaurus/module-type-aliases": "^3.10.1", "@docusaurus/module-type-aliases": "^3.10.1",
"@docusaurus/tsconfig": "^3.10.1", "@docusaurus/tsconfig": "^3.10.1",
"@docusaurus/types": "^3.10.1", "@docusaurus/types": "^3.10.1",
"@fec/remark-a11y-emoji": "^3.1.0", "@fec/remark-a11y-emoji": "^4.0.2",
"@tsconfig/docusaurus": "^2.0.9", "@tsconfig/docusaurus": "^2.0.9",
"@types/react": "catalog:", "@types/react": "catalog:",
"@types/react-dom": "catalog:", "@types/react-dom": "catalog:",
+10 -4
View File
@@ -1,9 +1,15 @@
@tailwind base; @import "tailwindcss/theme" layer(theme);
@tailwind components; @import "tailwindcss/utilities" layer(utilities);
@tailwind utilities;
@import url(https://api.fontshare.com/v2/css?f[]=work-sans@2,1&display=swap); @import url(https://api.fontshare.com/v2/css?f[]=work-sans@2,1&display=swap);
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
@theme {
--spacing-128: 32rem;
--breakpoint-3xl: 1697px;
}
:root { :root {
--ifm-color-primary: #fa5252; --ifm-color-primary: #fa5252;
--ifm-color-primary-dark: #f93232; --ifm-color-primary-dark: #f93232;
@@ -87,7 +93,7 @@
} }
.menu__list { .menu__list {
transition: all 0.s ease-in-out !important; transition: all 0.5s ease-in-out !important;
} }
.announcementBar_node_modules-\@docusaurus-theme-classic-lib-theme-AnnouncementBar-styles-module { .announcementBar_node_modules-\@docusaurus-theme-classic-lib-theme-AnnouncementBar-styles-module {
-19
View File
@@ -1,19 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./src/**/*.{js,jsx,ts,tsx}"],
theme: {
extend: {
spacing: {
128: "32rem",
},
screens: {
"3xl": "1697px",
},
},
},
darkMode: ["class", '[data-theme="dark"]'],
plugins: [],
corePlugins: {
preflight: false,
},
};
+1
View File
@@ -1,6 +1,7 @@
{ {
"extends": "@docusaurus/tsconfig", "extends": "@docusaurus/tsconfig",
"compilerOptions": { "compilerOptions": {
"ignoreDeprecations": "6.0",
"baseUrl": ".", "baseUrl": ".",
"target": "es5", "target": "es5",
"lib": [ "lib": [
@@ -19,6 +19,7 @@ export const DeleteIntegrationActionButton = ({ count, integration }: DeleteInte
const t = useScopedI18n("integration.page.delete"); const t = useScopedI18n("integration.page.delete");
const router = useRouter(); const router = useRouter();
const { openConfirmModal } = useConfirmModal(); const { openConfirmModal } = useConfirmModal();
const utils = clientApi.useUtils();
const { mutateAsync, isPending } = clientApi.integration.delete.useMutation(); const { mutateAsync, isPending } = clientApi.integration.delete.useMutation();
return ( return (
@@ -43,6 +44,7 @@ export const DeleteIntegrationActionButton = ({ count, integration }: DeleteInte
router.replace("/manage/integrations"); router.replace("/manage/integrations");
} }
void revalidatePathActionAsync("/manage/integrations"); void revalidatePathActionAsync("/manage/integrations");
void utils.integration.invalidate();
}, },
onError: () => { onError: () => {
showErrorNotification({ showErrorNotification({
@@ -54,6 +54,7 @@ export const EditIntegrationForm = ({ integration }: EditIntegrationForm) => {
const hasUrlSecret = initialSecretsKinds.includes("url"); const hasUrlSecret = initialSecretsKinds.includes("url");
const utils = clientApi.useUtils();
const router = useRouter(); const router = useRouter();
const form = useZodForm(formSchema, { const form = useZodForm(formSchema, {
initialValues: { initialValues: {
@@ -102,6 +103,7 @@ export const EditIntegrationForm = ({ integration }: EditIntegrationForm) => {
title: t("integration.page.edit.notification.success.title"), title: t("integration.page.edit.notification.success.title"),
message: t("integration.page.edit.notification.success.message"), message: t("integration.page.edit.notification.success.message"),
}); });
void utils.integration.invalidate();
void revalidatePathActionAsync("/manage/integrations").then(() => router.push("/manage/integrations")); void revalidatePathActionAsync("/manage/integrations").then(() => router.push("/manage/integrations"));
}, },
onError: () => { onError: () => {
@@ -97,13 +97,19 @@ export const NewIntegrationForm = ({
}, },
}); });
const utils = clientApi.useUtils();
const { mutateAsync: createIntegrationAsync, isPending: isCreatePending } = clientApi.integration.create.useMutation({ const { mutateAsync: createIntegrationAsync, isPending: isCreatePending } = clientApi.integration.create.useMutation({
async onSuccess() { async onSuccess() {
await revalidatePathActionAsync("/manage/integrations"); await revalidatePathActionAsync("/manage/integrations");
await utils.integration.invalidate();
}, },
}); });
const { mutateAsync: createOnboardingIntegrationAsync, isPending: isOnboardingCreatePending } = const { mutateAsync: createOnboardingIntegrationAsync, isPending: isOnboardingCreatePending } =
clientApi.onboard.createIntegration.useMutation(); clientApi.onboard.createIntegration.useMutation({
async onSuccess() {
await utils.integration.invalidate();
},
});
const isPending = isCreatePending || isOnboardingCreatePending; const isPending = isCreatePending || isOnboardingCreatePending;
const [error, setError] = useState<null | AnyMappedTestConnectionError>(null); const [error, setError] = useState<null | AnyMappedTestConnectionError>(null);
@@ -2,7 +2,7 @@
import type { SegmentedControlItem } from "@mantine/core"; import type { SegmentedControlItem } from "@mantine/core";
import { Button, Fieldset, Grid, Group, SegmentedControl, Stack, Textarea, TextInput } from "@mantine/core"; import { Button, Fieldset, Grid, Group, SegmentedControl, Stack, Textarea, TextInput } from "@mantine/core";
import { WidgetIntegrationSelect } from "node_modules/@homarr/widgets/src/widget-integration-select"; import { WidgetIntegrationSelect } from "@homarr/widgets/widget-integration-select";
import type { z } from "zod/v4"; import type { z } from "zod/v4";
import { clientApi } from "@homarr/api/client"; import { clientApi } from "@homarr/api/client";
@@ -2,7 +2,7 @@ import { Badge, Card } from "@mantine/core";
import { useElementSize } from "@mantine/hooks"; import { useElementSize } from "@mantine/hooks";
import { QueryErrorResetBoundary } from "@tanstack/react-query"; import { QueryErrorResetBoundary } from "@tanstack/react-query";
import combineClasses from "clsx"; import combineClasses from "clsx";
import { NoIntegrationSelectedError } from "node_modules/@homarr/widgets/src/errors"; import { NoIntegrationSelectedError } from "@homarr/widgets/errors/classes";
import { ErrorBoundary } from "react-error-boundary"; import { ErrorBoundary } from "react-error-boundary";
import { useRequiredBoard } from "@homarr/boards/context"; import { useRequiredBoard } from "@homarr/boards/context";
-1
View File
@@ -1,7 +1,6 @@
{ {
"extends": "@homarr/tsconfig/base.json", "extends": "@homarr/tsconfig/base.json",
"compilerOptions": { "compilerOptions": {
"baseUrl": ".",
"paths": { "paths": {
"~/*": ["./src/*"], "~/*": ["./src/*"],
"@homarr/node-unifi": ["../../node_modules/@types/node-unifi"] "@homarr/node-unifi": ["../../node_modules/@types/node-unifi"]
+1 -2
View File
@@ -1,9 +1,8 @@
{ {
"extends": "@homarr/tsconfig/base.json", "extends": "@homarr/tsconfig/base.json",
"compilerOptions": { "compilerOptions": {
"baseUrl": ".",
"paths": { "paths": {
"~/*": ["src/*"], "~/*": ["./src/*"],
"@homarr/node-unifi": ["../../node_modules/@types/node-unifi"] "@homarr/node-unifi": ["../../node_modules/@types/node-unifi"]
}, },
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
+1 -2
View File
@@ -1,9 +1,8 @@
{ {
"extends": "@homarr/tsconfig/base.json", "extends": "@homarr/tsconfig/base.json",
"compilerOptions": { "compilerOptions": {
"baseUrl": ".",
"paths": { "paths": {
"~/*": ["src/*"], "~/*": ["./src/*"],
"@homarr/node-unifi": ["../../node_modules/@types/node-unifi"] "@homarr/node-unifi": ["../../node_modules/@types/node-unifi"]
}, },
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
+1
View File
@@ -2,5 +2,6 @@ FROM node:24.16.0-trixie AS base
WORKDIR /app WORKDIR /app
COPY package.json . COPY package.json .
COPY pnpm-lock.yaml . COPY pnpm-lock.yaml .
COPY pnpm-workspace.yaml .
RUN corepack enable pnpm && pnpm install --frozen-lockfile RUN corepack enable pnpm && pnpm install --frozen-lockfile
CMD ["sleep", "60s"] CMD ["sleep", "60s"]
+2 -7
View File
@@ -6,12 +6,7 @@
}, },
"engines": { "engines": {
"node": ">=24.16.0", "node": ">=24.16.0",
"pnpm": ">=10.34.1" "pnpm": ">=11.0.0"
}, },
"packageManager": "pnpm@10.34.1", "packageManager": "pnpm@11.5.3"
"pnpm": {
"onlyBuiltDependencies": [
"better-sqlite3"
]
}
} }
@@ -0,0 +1,2 @@
allowBuilds:
better-sqlite3: true
+3 -47
View File
@@ -56,59 +56,15 @@
"testcontainers": "catalog:", "testcontainers": "catalog:",
"turbo": "catalog:", "turbo": "catalog:",
"typescript": "catalog:", "typescript": "catalog:",
"vite": "catalog:",
"vite-tsconfig-paths": "catalog:", "vite-tsconfig-paths": "catalog:",
"vitest": "catalog:" "vitest": "catalog:"
}, },
"engines": { "engines": {
"node": ">=24.16.0", "node": ">=24.16.0",
"pnpm": ">=10.34.1" "pnpm": ">=11.0.0"
},
"packageManager": "pnpm@10.34.1",
"pnpm": {
"onlyBuiltDependencies": [
"@tree-sitter-grammars/tree-sitter-yaml",
"bcrypt",
"better-sqlite3",
"cpu-features",
"esbuild",
"sharp",
"ssh2",
"tree-sitter",
"tree-sitter-json"
],
"overrides": {
"@babel/helpers@<7.26.10": ">=7.29.7",
"@babel/runtime@<7.26.10": ">=7.29.7",
"axios@>=1.0.0 <1.8.2": ">=1.17.0",
"brace-expansion@>=2.0.0 <=2.0.1": ">=5.0.6",
"brace-expansion@>=1.0.0 <=1.1.11": ">=5.0.6",
"esbuild@<=0.24.2": ">=0.28.0",
"form-data@>=4.0.0 <4.0.4": ">=4.0.5",
"hono@<4.6.5": ">=4.12.23",
"linkifyjs@<4.3.2": ">=4.3.3",
"nanoid@>=4.0.0 <5.0.9": ">=5.1.11",
"prismjs@<1.30.0": ">=1.30.0",
"proxmox-api>undici": "7.27.1",
"react-is": "^19.2.7",
"rollup@>=4.0.0 <4.22.4": ">=4.61.1",
"sha.js@<=2.4.11": ">=2.4.12",
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.2",
"tar-fs@>=2.0.0 <2.1.3": ">=3.1.2",
"tmp@<=0.2.3": ">=0.2.7",
"vite@>=5.0.0 <=5.4.18": ">=8.0.16"
},
"patchedDependencies": {
"@types/node-unifi": "patches/@types__node-unifi.patch",
"trpc-to-openapi": "patches/trpc-to-openapi.patch"
},
"allowUnusedPatches": true,
"ignoredBuiltDependencies": [
"@scarf/scarf",
"@swc/core",
"core-js-pure",
"protobufjs"
]
}, },
"packageManager": "pnpm@11.5.3",
"dependencies": { "dependencies": {
"jsonpath-plus": "catalog:" "jsonpath-plus": "catalog:"
} }
@@ -91,8 +91,8 @@ export type HomarrDocumentationPath =
| "/docs/tags/redis" | "/docs/tags/redis"
| "/docs/tags/responsive" | "/docs/tags/responsive"
| "/docs/tags/roles" | "/docs/tags/roles"
| "/docs/tags/search"
| "/docs/tags/search-engines" | "/docs/tags/search-engines"
| "/docs/tags/search"
| "/docs/tags/secrets" | "/docs/tags/secrets"
| "/docs/tags/security" | "/docs/tags/security"
| "/docs/tags/self-signed" | "/docs/tags/self-signed"
@@ -126,8 +126,8 @@ export type HomarrDocumentationPath =
| "/docs/category/community" | "/docs/category/community"
| "/docs/category/developer-guides" | "/docs/category/developer-guides"
| "/docs/category/getting-started" | "/docs/category/getting-started"
| "/docs/category/installation"
| "/docs/category/installation-1" | "/docs/category/installation-1"
| "/docs/category/installation"
| "/docs/category/integrations" | "/docs/category/integrations"
| "/docs/category/management" | "/docs/category/management"
| "/docs/category/widgets" | "/docs/category/widgets"
@@ -156,6 +156,7 @@ export type HomarrDocumentationPath =
| "/docs/integrations/adguard-home" | "/docs/integrations/adguard-home"
| "/docs/integrations/anchor" | "/docs/integrations/anchor"
| "/docs/integrations/aria2" | "/docs/integrations/aria2"
| "/docs/integrations/audiobookshelf"
| "/docs/integrations/codeberg" | "/docs/integrations/codeberg"
| "/docs/integrations/coolify" | "/docs/integrations/coolify"
| "/docs/integrations/dash-dot" | "/docs/integrations/dash-dot"
@@ -175,6 +176,7 @@ export type HomarrDocumentationPath =
| "/docs/integrations/kubernetes" | "/docs/integrations/kubernetes"
| "/docs/integrations/lidarr" | "/docs/integrations/lidarr"
| "/docs/integrations/linux-server-io" | "/docs/integrations/linux-server-io"
| "/docs/integrations/navidrome"
| "/docs/integrations/nextcloud" | "/docs/integrations/nextcloud"
| "/docs/integrations/npm" | "/docs/integrations/npm"
| "/docs/integrations/ntfy" | "/docs/integrations/ntfy"
@@ -182,6 +184,7 @@ export type HomarrDocumentationPath =
| "/docs/integrations/open-media-vault" | "/docs/integrations/open-media-vault"
| "/docs/integrations/opnsense" | "/docs/integrations/opnsense"
| "/docs/integrations/overseerr" | "/docs/integrations/overseerr"
| "/docs/integrations/paperless-ngx"
| "/docs/integrations/peanut" | "/docs/integrations/peanut"
| "/docs/integrations/pi-hole" | "/docs/integrations/pi-hole"
| "/docs/integrations/plex" | "/docs/integrations/plex"
@@ -198,12 +201,14 @@ export type HomarrDocumentationPath =
| "/docs/integrations/sonarr" | "/docs/integrations/sonarr"
| "/docs/integrations/speedtest-tracker" | "/docs/integrations/speedtest-tracker"
| "/docs/integrations/tdarr" | "/docs/integrations/tdarr"
| "/docs/integrations/technitium-dns"
| "/docs/integrations/tracearr" | "/docs/integrations/tracearr"
| "/docs/integrations/transmission" | "/docs/integrations/transmission"
| "/docs/integrations/truenas" | "/docs/integrations/truenas"
| "/docs/integrations/umami" | "/docs/integrations/umami"
| "/docs/integrations/unifi-controller" | "/docs/integrations/unifi-controller"
| "/docs/integrations/unraid" | "/docs/integrations/unraid"
| "/docs/integrations/uptime-kuma"
| "/docs/management/api" | "/docs/management/api"
| "/docs/management/apps" | "/docs/management/apps"
| "/docs/management/boards" | "/docs/management/boards"
@@ -216,6 +221,7 @@ export type HomarrDocumentationPath =
| "/docs/management/users" | "/docs/management/users"
| "/docs/widgets/anchor-note" | "/docs/widgets/anchor-note"
| "/docs/widgets/app" | "/docs/widgets/app"
| "/docs/widgets/audio-stats"
| "/docs/widgets/bookmarks" | "/docs/widgets/bookmarks"
| "/docs/widgets/calendar" | "/docs/widgets/calendar"
| "/docs/widgets/clock" | "/docs/widgets/clock"
@@ -240,6 +246,7 @@ export type HomarrDocumentationPath =
| "/docs/widgets/network-controller-summary" | "/docs/widgets/network-controller-summary"
| "/docs/widgets/notebook" | "/docs/widgets/notebook"
| "/docs/widgets/notifications" | "/docs/widgets/notifications"
| "/docs/widgets/paperless-ngx"
| "/docs/widgets/releases" | "/docs/widgets/releases"
| "/docs/widgets/rss-feed" | "/docs/widgets/rss-feed"
| "/docs/widgets/smart-home-entity-state" | "/docs/widgets/smart-home-entity-state"
@@ -251,6 +258,7 @@ export type HomarrDocumentationPath =
| "/docs/widgets/tracearr" | "/docs/widgets/tracearr"
| "/docs/widgets/umami" | "/docs/widgets/umami"
| "/docs/widgets/ups" | "/docs/widgets/ups"
| "/docs/widgets/uptime-kuma"
| "/docs/widgets/video" | "/docs/widgets/video"
| "/docs/widgets/weather" | "/docs/widgets/weather"
| "" | ""
@@ -55,7 +55,8 @@ export class ProxmoxIntegration extends Integration implements IClusterHealthMon
host: this.url("/").host, host: this.url("/").host,
tokenID: `${this.getSecretValue("username")}@${this.getSecretValue("realm")}!${this.getSecretValue("tokenId")}`, tokenID: `${this.getSecretValue("username")}@${this.getSecretValue("realm")}!${this.getSecretValue("tokenId")}`,
tokenSecret: this.getSecretValue("apiKey"), tokenSecret: this.getSecretValue("apiKey"),
fetch: fetchAsync, // proxmox-api bundles its own undici types which are incompatible with the project's undici version
fetch: fetchAsync as any,
}); });
} }
} }
@@ -1,5 +1,4 @@
import proxmoxApi from "proxmox-api"; import proxmoxApi from "proxmox-api";
import type { fetch as undiciFetch } from "undici";
import { Response } from "undici"; import { Response } from "undici";
import { describe, expect, test } from "vitest"; import { describe, expect, test } from "vitest";
@@ -9,48 +8,32 @@ import { ProxmoxApiErrorHandler } from "../proxmox-error-handler";
describe("ProxmoxApiErrorHandler handleError should handle the provided error accordingly", () => { describe("ProxmoxApiErrorHandler handleError should handle the provided error accordingly", () => {
test.each([400, 401, 500])("should handle %s error", async (statusCode) => { test.each([400, 401, 500])("should handle %s error", async (statusCode) => {
// Arrange const mockedFetch = async () => Promise.resolve(createFakeResponse(statusCode));
// eslint-disable-next-line no-restricted-syntax
const mockedFetch: typeof undiciFetch = async () => {
return Promise.resolve(createFakeResponse(statusCode));
};
// Act
const result = await runWithAsync(mockedFetch); const result = await runWithAsync(mockedFetch);
// Assert
expect(result).toBeInstanceOf(IntegrationResponseError); expect(result).toBeInstanceOf(IntegrationResponseError);
const error = result as unknown as IntegrationResponseError; const error = result as unknown as IntegrationResponseError;
expect(error.cause.statusCode).toBe(statusCode); expect(error.cause.statusCode).toBe(statusCode);
}); });
test("should handle other non successful status codes", async () => { test("should handle other non successful status codes", async () => {
// Arrange const mockedFetch = async () => Promise.resolve(createFakeResponse(404));
// eslint-disable-next-line no-restricted-syntax
const mockedFetch: typeof undiciFetch = async () => {
return Promise.resolve(createFakeResponse(404));
};
// Act
const result = await runWithAsync(mockedFetch); const result = await runWithAsync(mockedFetch);
// Assert
expect(result).toBeInstanceOf(IntegrationResponseError); expect(result).toBeInstanceOf(IntegrationResponseError);
const error = result as unknown as IntegrationResponseError; const error = result as unknown as IntegrationResponseError;
expect(error.cause.statusCode).toBe(404); expect(error.cause.statusCode).toBe(404);
}); });
test("should handle request error", async () => { test("should handle request error", async () => {
// Arrange const mockedFetch = () => {
const mockedFetch: typeof undiciFetch = () => {
const errorWithCode = new Error("Inner error") as Error & { code: string }; const errorWithCode = new Error("Inner error") as Error & { code: string };
errorWithCode.code = "ECONNREFUSED"; errorWithCode.code = "ECONNREFUSED";
throw new TypeError("Outer error", { cause: errorWithCode }); throw new TypeError("Outer error", { cause: errorWithCode });
}; };
// Act
const result = await runWithAsync(mockedFetch); const result = await runWithAsync(mockedFetch);
// Assert
// In the end in should have the structure IntegrationRequestError -> RequestError -> TypeError -> Error (with code)
expect(result).toBeInstanceOf(IntegrationRequestError); expect(result).toBeInstanceOf(IntegrationRequestError);
const error = result as unknown as IntegrationRequestError; const error = result as unknown as IntegrationRequestError;
expect(error.cause.cause).toBeInstanceOf(TypeError); expect(error.cause.cause).toBeInstanceOf(TypeError);
@@ -65,13 +48,11 @@ describe("ProxmoxApiErrorHandler handleError should handle the provided error ac
const createFakeResponse = (statusCode: number) => { const createFakeResponse = (statusCode: number) => {
return new Response(JSON.stringify({ data: {} }), { return new Response(JSON.stringify({ data: {} }), {
status: statusCode, status: statusCode,
// It expects a content-type and valid json response
// https://github.com/UrielCh/proxmox-api/blob/master/api/src/ProxmoxEngine.ts#L258
headers: { "content-type": "application/json;charset=UTF-8" }, headers: { "content-type": "application/json;charset=UTF-8" },
}); });
}; };
const runWithAsync = async (mockedFetch: typeof undiciFetch) => { const runWithAsync = async (mockedFetch: (...args: any[]) => any) => {
const integration = { id: "test", name: "test", url: "http://proxmox.example.com" }; const integration = { id: "test", name: "test", url: "http://proxmox.example.com" };
const client = createProxmoxClient(mockedFetch); const client = createProxmoxClient(mockedFetch);
const handler = new ProxmoxApiErrorHandler(); const handler = new ProxmoxApiErrorHandler();
@@ -79,11 +60,11 @@ const runWithAsync = async (mockedFetch: typeof undiciFetch) => {
return await client.nodes.$get().catch((error) => handler.handleError(error, integration)); return await client.nodes.$get().catch((error) => handler.handleError(error, integration));
}; };
const createProxmoxClient = (fetch: typeof undiciFetch) => { const createProxmoxClient = (fetch: (...args: any[]) => any) => {
return proxmoxApi({ return proxmoxApi({
host: "proxmox.example.com", host: "proxmox.example.com",
tokenID: "username@realm!tokenId", tokenID: "username@realm!tokenId",
tokenSecret: crypto.randomUUID(), tokenSecret: crypto.randomUUID(),
fetch, fetch: fetch as any,
}); });
}; };
@@ -0,0 +1,2 @@
declare const classes: Record<string, string>;
export default classes;
@@ -0,0 +1,2 @@
declare const classes: Record<string, string>;
export default classes;
+2
View File
@@ -17,6 +17,8 @@
"./custom-api/component": "./src/custom-api/component.tsx", "./custom-api/component": "./src/custom-api/component.tsx",
"./custom-api/extract-display-data": "./src/custom-api/extract-display-data.ts", "./custom-api/extract-display-data": "./src/custom-api/extract-display-data.ts",
"./errors": "./src/errors/component.tsx", "./errors": "./src/errors/component.tsx",
"./errors/classes": "./src/errors/index.ts",
"./widget-integration-select": "./src/widget-integration-select.tsx",
"./modals": "./src/modals/index.ts", "./modals": "./src/modals/index.ts",
"./prefetch": "./src/prefetch.ts" "./prefetch": "./src/prefetch.ts"
}, },
+1944 -1876
View File
File diff suppressed because it is too large. Load diff
+74 -28
View File
@@ -21,7 +21,7 @@ catalog:
"@immich/sdk": ^2.7.5 "@immich/sdk": ^2.7.5
"@jellyfin/sdk": ^0.13.0 "@jellyfin/sdk": ^0.13.0
"@kubernetes/client-node": ^1.4.0 "@kubernetes/client-node": ^1.4.0
"@modelcontextprotocol/sdk": 1.26.0 "@modelcontextprotocol/sdk": 1.29.0
"@mantine/charts": ^9.3.0 "@mantine/charts": ^9.3.0
"@mantine/colors-generator": ^9.3.0 "@mantine/colors-generator": ^9.3.0
"@mantine/core": ^9.3.0 "@mantine/core": ^9.3.0
@@ -52,22 +52,22 @@ catalog:
"@testcontainers/mysql": ^12.0.1 "@testcontainers/mysql": ^12.0.1
"@testcontainers/postgresql": ^12.0.1 "@testcontainers/postgresql": ^12.0.1
"@testcontainers/redis": ^12.0.1 "@testcontainers/redis": ^12.0.1
"@tiptap/extension-color": 3.25.0 "@tiptap/extension-color": 3.26.0
"@tiptap/extension-highlight": 3.25.0 "@tiptap/extension-highlight": 3.26.0
"@tiptap/extension-image": 3.25.0 "@tiptap/extension-image": 3.26.0
"@tiptap/extension-link": ^3.25.0 "@tiptap/extension-link": ^3.26.0
"@tiptap/extension-placeholder": ^3.25.0 "@tiptap/extension-placeholder": ^3.26.0
"@tiptap/extension-table": 3.25.0 "@tiptap/extension-table": 3.26.0
"@tiptap/extension-table-cell": 3.25.0 "@tiptap/extension-table-cell": 3.26.0
"@tiptap/extension-table-header": 3.25.0 "@tiptap/extension-table-header": 3.26.0
"@tiptap/extension-table-row": 3.25.0 "@tiptap/extension-table-row": 3.26.0
"@tiptap/extension-task-item": 3.25.0 "@tiptap/extension-task-item": 3.26.0
"@tiptap/extension-task-list": 3.25.0 "@tiptap/extension-task-list": 3.26.0
"@tiptap/extension-text-align": 3.25.0 "@tiptap/extension-text-align": 3.26.0
"@tiptap/extension-text-style": 3.25.0 "@tiptap/extension-text-style": 3.26.0
"@tiptap/extension-underline": 3.25.0 "@tiptap/extension-underline": 3.26.0
"@tiptap/react": ^3.25.0 "@tiptap/react": ^3.26.0
"@tiptap/starter-kit": ^3.25.0 "@tiptap/starter-kit": ^3.26.0
"@trpc/client": ^11.17.0 "@trpc/client": ^11.17.0
"@trpc/next": ^11.17.0 "@trpc/next": ^11.17.0
"@trpc/react-query": ^11.17.0 "@trpc/react-query": ^11.17.0
@@ -86,14 +86,14 @@ catalog:
"@types/node-unifi": ^2.5.1 "@types/node-unifi": ^2.5.1
"@types/pg": ^8.20.0 "@types/pg": ^8.20.0
"@types/prismjs": ^1.26.6 "@types/prismjs": ^1.26.6
"@types/react": 19.2.16 "@types/react": 19.2.17
"@types/react-dom": 19.2.3 "@types/react-dom": 19.2.3
"@scalar/api-reference-react": ^0.9.42 "@scalar/api-reference-react": ^0.9.42
"@types/video.js": ^7.3.58 "@types/video.js": ^7.3.58
"@types/ws": ^8.18.1 "@types/ws": ^8.18.1
"@types/xml2js": ^0.4.14 "@types/xml2js": ^0.4.14
"@umami/node": ^0.4.0 "@umami/node": ^0.4.0
"@vitejs/plugin-react": ^5.2.0 "@vitejs/plugin-react": ^6.0.2
"@vitest/coverage-v8": ^4.1.8 "@vitest/coverage-v8": ^4.1.8
"@vitest/ui": ^4.1.8 "@vitest/ui": ^4.1.8
"@xterm/addon-canvas": ^0.7.0 "@xterm/addon-canvas": ^0.7.0
@@ -101,13 +101,13 @@ catalog:
"@xterm/xterm": ^6.0.0 "@xterm/xterm": ^6.0.0
adm-zip: 0.5.17 adm-zip: 0.5.17
jszip: ^3.10.1 jszip: ^3.10.1
sql.js: ^1.12.0 sql.js: ^1.14.1
babel-plugin-react-compiler: ^1.0.0 babel-plugin-react-compiler: ^1.0.0
bcrypt: ^6.0.0 bcrypt: ^6.0.0
better-sqlite3: ^12.10.0 better-sqlite3: ^12.10.0
chroma-js: ^3.2.0 chroma-js: ^3.2.0
clsx: ^2.1.1 clsx: ^2.1.1
concurrently: ^9.2.1 concurrently: ^10.0.3
conventional-changelog-conventionalcommits: ^9.3.1 conventional-changelog-conventionalcommits: ^9.3.1
cookie: 1.1.1 cookie: 1.1.1
cookies: ^0.9.1 cookies: ^0.9.1
@@ -129,7 +129,7 @@ catalog:
flag-icons: ^7.5.0 flag-icons: ^7.5.0
glob: ^13.0.6 glob: ^13.0.6
ical.js: ^2.2.1 ical.js: ^2.2.1
ioredis: 5.11.0 ioredis: 5.11.1
isomorphic-dompurify: ^3.16.0 isomorphic-dompurify: ^3.16.0
jotai: ^2.20.0 jotai: ^2.20.0
jsdom: ^29.1.1 jsdom: ^29.1.1
@@ -138,19 +138,19 @@ catalog:
ldapts: 8.1.8 ldapts: 8.1.8
lodash.clonedeep: ^4.5.0 lodash.clonedeep: ^4.5.0
lucide-react: ^1.17.0 lucide-react: ^1.17.0
"mantine-contextmenu": ^7.17.1 "mantine-contextmenu": ^9.2.1
mantine-datatable: ^9.2.2 mantine-datatable: ^9.2.2
mantine-react-table: npm:mantine-react-table-open@9.0.3 mantine-react-table: npm:mantine-react-table-open@9.0.3
mcp-handler: ^1.1.0 mcp-handler: ^1.1.0
maria2: ^0.4.1 maria2: ^0.4.1
mysql2: 3.22.4 mysql2: 3.22.5
next: 16.2.7 next: 16.2.7
next-auth: 5.0.0-beta.31 next-auth: 5.0.0-beta.31
next-intl: 4.13.0 next-intl: 4.13.0
node-loader: ^2.1.0 node-loader: ^2.1.0
octokit: ^5.0.5 octokit: ^5.0.5
oxfmt: ^0.53.0 oxfmt: ^0.54.0
oxlint: ^1.67.0 oxlint: ^1.68.0
pg: ^8.21.0 pg: ^8.21.0
postcss-preset-mantine: ^1.18.0 postcss-preset-mantine: ^1.18.0
prismjs: ^1.30.0 prismjs: ^1.30.0
@@ -175,8 +175,9 @@ catalog:
tsdav: ^2.2.2 tsdav: ^2.2.2
tsx: 4.20.4 tsx: 4.20.4
turbo: ^2.9.16 turbo: ^2.9.16
typescript: ^5.9.3 typescript: ^6.0.3
undici: 8.4.0 vite: ^8.0.16
undici: 7.27.1
use-deep-compare-effect: ^1.8.1 use-deep-compare-effect: ^1.8.1
video.js: ^8.23.7 video.js: ^8.23.7
vite-tsconfig-paths: ^6.1.1 vite-tsconfig-paths: ^6.1.1
@@ -189,9 +190,54 @@ catalog:
zod-form-data: ^3.0.2 zod-form-data: ^3.0.2
zod-validation-error: ^5.0.0 zod-validation-error: ^5.0.0
catalogMode: strict catalogMode: strict
nodeLinker: hoisted
strictPeerDependencies: false
engineStrict: true
verifyDepsBeforeRun: warn
minimumReleaseAge: 4320 minimumReleaseAge: 4320
minimumReleaseAgeExclude: minimumReleaseAgeExclude:
- next - next
- "@next/*" - "@next/*"
# Renovate security update: turbo@2.9.14 # Renovate security update: turbo@2.9.14
- turbo@2.9.14 - turbo@2.9.14
overrides:
"@babel/helpers@<7.26.10": ">=7.29.7"
"@babel/runtime@<7.26.10": ">=7.29.7"
"axios@>=1.0.0 <1.8.2": ">=1.17.0"
"brace-expansion@>=2.0.0 <=2.0.1": ">=5.0.6"
"brace-expansion@>=1.0.0 <=1.1.11": ">=5.0.6"
"esbuild@<=0.24.2": ">=0.28.0"
"form-data@>=4.0.0 <4.0.4": ">=4.0.5"
"hono@<4.6.5": ">=4.12.23"
"linkifyjs@<4.3.2": ">=4.3.3"
"nanoid@>=4.0.0 <5.0.9": ">=5.1.11"
"prismjs@<1.30.0": ">=1.30.0"
"proxmox-api>undici": "7.27.1"
"react-is": "^19.2.7"
"rollup@>=4.0.0 <4.22.4": ">=4.61.1"
"sha.js@<=2.4.11": ">=2.4.12"
"tar-fs@>=3.0.0 <3.0.9": ">=3.1.2"
"tar-fs@>=2.0.0 <2.1.3": ">=3.1.2"
"tmp@<=0.2.3": ">=0.2.7"
"vite@>=5.0.0 <=5.4.18": ">=8.0.16"
patchedDependencies:
"@types/node-unifi": "patches/@types__node-unifi.patch"
"trpc-to-openapi": "patches/trpc-to-openapi.patch"
allowUnusedPatches: true
allowBuilds:
"@tree-sitter-grammars/tree-sitter-yaml": true
bcrypt: true
better-sqlite3: true
cpu-features: true
esbuild: true
sharp: true
ssh2: true
tree-sitter: true
tree-sitter-json: true
"@scarf/scarf": false
"@swc/core": false
core-js: false
core-js-pure: false
msw: false
protobufjs: false
vue-demi: false
+11 -5
View File
@@ -1,22 +1,28 @@
import fs from "fs/promises"; import fs from "fs/promises";
import { join } from "path"; import { join } from "path";
import json5 from "json5"; import json5 from "json5";
import { parse } from "yaml";
import { describe, test } from "vitest"; import { describe, test } from "vitest";
describe("Renovate configuration tests", () => { describe("Renovate configuration tests", () => {
test("automerge should be disabled for onlyBuiltDependencies", async () => { test("automerge should be disabled for allowBuilds dependencies", async () => {
const packageJson = await import("../package.json"); const workspaceYaml = await fs.readFile(join(__dirname, "../pnpm-workspace.yaml"), "utf-8").then(parse);
const renovateConfig = await fs.readFile(join(__dirname, "../.github/renovate.json5"), "utf-8").then(json5.parse); const renovateConfig = await fs.readFile(join(__dirname, "../.github/renovate.json5"), "utf-8").then(json5.parse);
const onlyBuiltDependencies = packageJson.pnpm.onlyBuiltDependencies; if (!workspaceYaml.allowBuilds || typeof workspaceYaml.allowBuilds !== "object") {
throw new Error("pnpm-workspace.yaml must define allowBuilds as an object");
}
const allowedBuilds = Object.entries(workspaceYaml.allowBuilds)
.filter(([, allowed]) => allowed === true)
.map(([name]) => name);
const automergeDisabledDeps = renovateConfig.packageRules const automergeDisabledDeps = renovateConfig.packageRules
.filter((rule: any) => rule.automerge === false) .filter((rule: any) => rule.automerge === false)
.flatMap((rule: any) => rule.matchPackageNames || []); .flatMap((rule: any) => rule.matchPackageNames || []);
const missingDeps = onlyBuiltDependencies.filter((dep: string) => !automergeDisabledDeps.includes(dep)); const missingDeps = allowedBuilds.filter((dep: string) => !automergeDisabledDeps.includes(dep));
if (missingDeps.length > 0) { if (missingDeps.length > 0) {
throw new Error( throw new Error(
`The following onlyBuiltDependencies are missing automerge disable rules in renovate.json5: ${missingDeps.join(", ")}`, `The following allowBuilds dependencies are missing automerge disable rules in renovate.json5: ${missingDeps.join(", ")}`,
); );
} }
}); });
+2 -3
View File
@@ -19,10 +19,9 @@
"noUncheckedIndexedAccess": true, "noUncheckedIndexedAccess": true,
"strictNullChecks": true, "strictNullChecks": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"baseUrl": ".", "types": ["node"],
"paths": { "paths": {
"@homarr/node-unifi": ["${configDir}/../../node_modules/@types/node-unifi"], "@homarr/node-unifi": ["${configDir}/../../node_modules/@types/node-unifi"]
"*": ["node_modules/*"]
} }
}, },
"exclude": ["node_modules", "build", "dist", ".next"] "exclude": ["node_modules", "build", "dist", ".next"]
-5
View File
@@ -9,11 +9,6 @@ export default defineConfig({
environment: "jsdom", environment: "jsdom",
include: ["**/*.spec.ts"], include: ["**/*.spec.ts"],
clearMocks: true, clearMocks: true,
poolOptions: {
threads: {
singleThread: false,
},
},
coverage: { coverage: {
provider: "v8", provider: "v8",
reporter: ["html", "json-summary", "json"], reporter: ["html", "json-summary", "json"],