diff --git a/.github/renovate.json b/.github/renovate.json5
similarity index 75%
rename from .github/renovate.json
rename to .github/renovate.json5
index 69fb4cdad..8978459cf 100644
--- a/.github/renovate.json
+++ b/.github/renovate.json5
@@ -6,7 +6,8 @@
"packageRules": [
{
"matchPackagePatterns": [
- "^@homarr/"
+ "^@homarr/",
+ "tsx" // Disabled for now as version 0.14.4 did not work with the current version of homarr. It resulted in a ERR_MODULE_NOT_FOUND error
],
"enabled": false
},
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
deleted file mode 100644
index b8fbfaa9d..000000000
--- a/.github/workflows/build.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-name: Build apps and migration script
-
-on:
- pull_request:
- branches: ["*"]
- push:
- branches: ["main"]
- merge_group:
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
-
-env:
- FORCE_COLOR: 3
-
-jobs:
- build:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
-
- - name: Setup
- uses: ./tooling/github/setup
-
- - name: Copy env
- shell: bash
- run: cp .env.example .env
-
- - name: Build
- run: pnpm build
diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml
index 228dd9989..a75c144b7 100644
--- a/.github/workflows/code-quality.yml
+++ b/.github/workflows/code-quality.yml
@@ -1,4 +1,4 @@
-name: Code quality analysis
+name: "[Quality] Code Analysis"
on:
pull_request:
@@ -8,7 +8,7 @@ on:
merge_group:
concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
+ group: "${{ github.workflow }}-${{ github.ref }}"
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
# You can leverage Vercel Remote Caching with Turbo to speed up your builds
@@ -72,3 +72,15 @@ jobs:
# Only works if you set `reportOnFailure: true` in your vite config as specified above
if: always()
uses: davelosert/vitest-coverage-report-action@v2
+
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Setup
+ uses: ./tooling/github/setup
+ - name: Copy env
+ shell: bash
+ run: cp .env.example .env
+ - name: Build
+ run: pnpm build
diff --git a/.github/workflows/conventional-commits.yml b/.github/workflows/conventions-semantic-commits.yml
similarity index 77%
rename from .github/workflows/conventional-commits.yml
rename to .github/workflows/conventions-semantic-commits.yml
index 8f2216bd7..d26307ba0 100644
--- a/.github/workflows/conventional-commits.yml
+++ b/.github/workflows/conventions-semantic-commits.yml
@@ -1,7 +1,5 @@
-
# https://github.com/webiny/action-conventional-commits?tab=readme-ov-file
-
-name: Conventional Commits
+name: "[Conventions] Semantic Commits"
on:
pull_request:
@@ -12,5 +10,5 @@ jobs:
name: Conventional Commits
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: webiny/action-conventional-commits@v1.3.0
\ No newline at end of file
diff --git a/.github/workflows/pr-conventional-commits.yml b/.github/workflows/conventions-semantic-pull-requests.yml
similarity index 81%
rename from .github/workflows/pr-conventional-commits.yml
rename to .github/workflows/conventions-semantic-pull-requests.yml
index e3dfb587b..cec459930 100644
--- a/.github/workflows/pr-conventional-commits.yml
+++ b/.github/workflows/conventions-semantic-pull-requests.yml
@@ -1,4 +1,4 @@
-name: "Lint PR"
+name: "[Conventions] Semantic PRs"
on:
pull_request_target:
@@ -11,8 +11,7 @@ permissions:
pull-requests: read
jobs:
- main:
- name: Validate PR title
+ validate-pull-request-title:
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v5
diff --git a/.github/workflows/docker-image.yml b/.github/workflows/deployment-docker-image.yml
similarity index 95%
rename from .github/workflows/docker-image.yml
rename to .github/workflows/deployment-docker-image.yml
index 046523358..40279c171 100644
--- a/.github/workflows/docker-image.yml
+++ b/.github/workflows/deployment-docker-image.yml
@@ -1,4 +1,4 @@
-name: Docker image
+name: "[Deployment] Release"
on:
pull_request:
@@ -70,13 +70,13 @@ jobs:
id: meta
uses: docker/metadata-action@v5
with:
- images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
+ images: "${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}"
tags: |
type=raw,value=latest
type=raw,value=${{ steps.semver.outputs.next }}
- name: Build and push
id: buildPushAction
- uses: docker/build-push-action@v5
+ uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64,linux/riscv64,linux/arm/v7,linux/arm/v6
context: .
diff --git a/.github/workflows/deployment-weekly-release.yml b/.github/workflows/deployment-weekly-release.yml
new file mode 100644
index 000000000..3ffacb556
--- /dev/null
+++ b/.github/workflows/deployment-weekly-release.yml
@@ -0,0 +1,82 @@
+name: "[Deployment] Automatic Weekly Release"
+
+on:
+ schedule:
+ - cron: "0 19 * * 5" # https://crontab.guru/#0_19_*_*_5
+ workflow_dispatch:
+
+permissions:
+ contents: write
+ pull-requests: write
+
+jobs:
+ create-and-merge-pr:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Discord notification
+ env:
+ DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
+ uses: Ilshidur/action-discord@master
+ with:
+ args: "Automatic release has been triggered: [run ${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: Get Next Version
+ id: semver
+ uses: ietf-tools/semver-action@v1
+ with:
+ token: ${{ github.token }}
+ branch: dev
+ - name: Create pull request
+ uses: devops-infra/action-pull-request@v0.5.5
+ id: create-pull-request
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ title: "(chore): automatic release ${{ steps.semver.outputs.next }}"
+ new_string: "**This is an automatic release**.
Manual action may be required for major bumps.
Detected change to be ``${{ steps.semver.outputs.bump }}``
Bump version from ``${{ steps.semver.outputs.current }}`` to ``${{ steps.semver.outputs.next }}``"
+ source_branch: dev
+ target_branch: main
+ label: automerge
+ - name: Discord notification
+ env:
+ DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
+ uses: Ilshidur/action-discord@master
+ with:
+ args: "Created a release PR ${{ steps.create-pull-request.outputs.url }} for version ${{ steps.semver.outputs.next }} (new behaviour: ${{ steps.semver.outputs.bump }})"
+ - id: automerge
+ if: ${{ steps.semver.outputs.bump != 'major' }}
+ name: automerge
+ uses: "pascalgn/automerge-action@v0.16.3"
+ env:
+ GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
+ MERGE_METHOD: merge # we prefer merge commits for merging to master
+ MERGE_COMMIT_MESSAGE: "(chore): automatic release ${{ steps.semver.outputs.next }}"
+ MERGE_DELETE_BRANCH: false # never set to true!
+ PULL_REQUEST: "${{ steps.create-pull-request.outputs.pr_number }}"
+ MERGE_RETRIES: 20 # 20 retries * MERGE_RETRY_SLEEP until step fails
+ MERGE_RETRY_SLEEP: 10000 # 10 seconds * MERGE_RETRIES until step fails
+ MERGE_REQUIRED_APPROVALS: 0 # do not require approvals
+
+ - name: Merged Discord notification
+ if: ${{ steps.automerge.outputs.mergeResult == 'merged' }}
+ env:
+ DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
+ uses: Ilshidur/action-discord@master
+ with:
+ args: "Merged PR ${{ steps.create-pull-request.outputs.url }} for release ${{ steps.semver.outputs.next }}"
+ - name: Major Bump Discord notification
+ if: ${{ steps.semver.outputs.bump == 'major' }}
+ env:
+ DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
+ uses: Ilshidur/action-discord@master
+ with:
+ args: "The release PR must be manually merged because the next version is a major version: ${{ steps.create-pull-request.outputs.url }} for release ${{ steps.semver.outputs.next }}"
+ - name: Discord Fail Notification
+ if: failure()
+ env:
+ DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
+ uses: Ilshidur/action-discord@master
+ with:
+ args: "The automatic release workflow [run ${{ github.run_number }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) has failed"
diff --git a/.github/workflows/renovate-automatic-approval b/.github/workflows/renovate-automatic-approval.yml
similarity index 56%
rename from .github/workflows/renovate-automatic-approval
rename to .github/workflows/renovate-automatic-approval.yml
index b4701ed96..1185fd800 100644
--- a/.github/workflows/renovate-automatic-approval
+++ b/.github/workflows/renovate-automatic-approval.yml
@@ -1,4 +1,4 @@
-name: Approve Renovate PRs
+name: "[Dependency Updates] Auto Approve"
on:
pull_request:
types: [opened, synchronize]
@@ -6,17 +6,20 @@ on:
jobs:
approve-renovate-prs:
runs-on: ubuntu-latest
-
steps:
- name: Checkout code
- uses: actions/checkout@v2
-
+ uses: actions/checkout@v4
+ - name: Obtain token
+ id: obtainToken
+ uses: tibdex/github-app-token@v2
+ with:
+ private_key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
+ app_id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
- name: Install GitHub CLI
run: sudo apt-get install -y gh
-
- name: Approve Renovate PRs
env:
- GITHUB_TOKEN: ${{ secrets.RENOVATE_APPROVE_TOKEN }}
+ GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
run: |
for pr in $(gh pr list --author homarr-renovate[bot] --json number --jq .[].number); do
gh pr review $pr --approve --body "Automatically approved by GitHub Action"
diff --git a/.nvmrc b/.nvmrc
index 48b14e6b2..907565957 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-20.14.0
+20.15.0
diff --git a/.run/db_migration_mysql_generate.run.xml b/.run/db_migration_mysql_generate.run.xml
new file mode 100644
index 000000000..1eaa49077
--- /dev/null
+++ b/.run/db_migration_mysql_generate.run.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/db_migration_sqlite_generate.run.xml b/.run/db_migration_sqlite_generate.run.xml
new file mode 100644
index 000000000..ea63d6b81
--- /dev/null
+++ b/.run/db_migration_sqlite_generate.run.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/db_push.run.xml b/.run/db_push.run.xml
new file mode 100644
index 000000000..e5d56c827
--- /dev/null
+++ b/.run/db_push.run.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/db_studio.run.xml b/.run/db_studio.run.xml
new file mode 100644
index 000000000..df61c66f7
--- /dev/null
+++ b/.run/db_studio.run.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/dev.run.xml b/.run/dev.run.xml
new file mode 100644
index 000000000..15ec68c7f
--- /dev/null
+++ b/.run/dev.run.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/docker_dev.run.xml b/.run/docker_dev.run.xml
new file mode 100644
index 000000000..0aa18c69a
--- /dev/null
+++ b/.run/docker_dev.run.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/format.run.xml b/.run/format.run.xml
new file mode 100644
index 000000000..801ecad89
--- /dev/null
+++ b/.run/format.run.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/format_fix.run.xml b/.run/format_fix.run.xml
new file mode 100644
index 000000000..391e76671
--- /dev/null
+++ b/.run/format_fix.run.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/test.run.xml b/.run/test.run.xml
new file mode 100644
index 000000000..d96c3ec1b
--- /dev/null
+++ b/.run/test.run.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/test_ui.run.xml b/.run/test_ui.run.xml
new file mode 100644
index 000000000..2f18e7aa9
--- /dev/null
+++ b/.run/test_ui.run.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index b9a963aad..94ed7a3fc 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -4,13 +4,15 @@
"mode": "auto"
}
],
+ "eslint.experimental.useFlatConfig": true,
"typescript.tsdk": "node_modules\\typescript\\lib",
"js/ts.implicitProjectConfig.experimentalDecorators": true,
"prettier.configPath": "./tooling/prettier/index.mjs",
- "cSpell.words": [
- "superjson",
- "homarr",
- "trpc",
- "Umami"
- ]
-}
\ No newline at end of file
+ "cSpell.words": ["cqmin", "homarr", "superjson", "trpc", "Umami"],
+ "i18n-ally.dirStructure": "auto",
+ "i18n-ally.enabledFrameworks": ["next-international"],
+ "i18n-ally.localesPaths": ["./packages/translation/src/lang/"],
+ "i18n-ally.enabledParsers": ["ts"],
+ "i18n-ally.extract.keyMaxLength": 0,
+ "i18n-ally.keystyle": "flat"
+}
diff --git a/Dockerfile b/Dockerfile
index b9471f77e..095f5d8ac 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM node:20.13.1-alpine AS base
+FROM node:20.15.0-alpine AS base
FROM base AS builder
RUN apk add --no-cache libc6-compat
diff --git a/apps/nextjs/eslint.config.js b/apps/nextjs/eslint.config.js
new file mode 100644
index 000000000..c131bab90
--- /dev/null
+++ b/apps/nextjs/eslint.config.js
@@ -0,0 +1,13 @@
+import baseConfig from "@homarr/eslint-config/base";
+import nextjsConfig from "@homarr/eslint-config/nextjs";
+import reactConfig from "@homarr/eslint-config/react";
+
+/** @type {import('typescript-eslint').Config} */
+export default [
+ {
+ ignores: [".next/**"],
+ },
+ ...baseConfig,
+ ...reactConfig,
+ ...nextjsConfig,
+];
diff --git a/apps/nextjs/next.config.mjs b/apps/nextjs/next.config.mjs
index fcf1321a4..0cc05f786 100644
--- a/apps/nextjs/next.config.mjs
+++ b/apps/nextjs/next.config.mjs
@@ -1,6 +1,6 @@
// Importing env files here to validate on build
-import "./src/env.mjs";
import "@homarr/auth/env.mjs";
+import "./src/env.mjs";
/** @type {import("next").NextConfig} */
const config = {
@@ -9,6 +9,13 @@ const config = {
/** We already do linting and typechecking as separate tasks in CI */
eslint: { ignoreDuringBuilds: true },
typescript: { ignoreBuildErrors: true },
+ webpack: (config) => {
+ config.module.rules.push({
+ test: /\.node$/,
+ loader: "node-loader",
+ });
+ return config;
+ },
experimental: {
optimizePackageImports: ["@mantine/core", "@mantine/hooks", "@tabler/icons-react"],
},
diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json
index d82c1decd..b7b0d4ad7 100644
--- a/apps/nextjs/package.json
+++ b/apps/nextjs/package.json
@@ -7,7 +7,7 @@
"build": "pnpm with-env next build",
"clean": "git clean -xdf .next .turbo node_modules",
"dev": "pnpm with-env next dev",
- "lint": "dotenv -v SKIP_ENV_VALIDATION=1 next lint",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"start": "pnpm with-env next start",
"typecheck": "tsc --noEmit",
@@ -31,16 +31,18 @@
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@homarr/widgets": "workspace:^0.1.0",
- "@mantine/colors-generator": "^7.10.0",
- "@mantine/hooks": "^7.10.0",
- "@mantine/modals": "^7.10.0",
- "@mantine/tiptap": "^7.10.0",
+ "@mantine/colors-generator": "^7.11.0",
+ "@mantine/core": "^7.11.0",
+ "@mantine/hooks": "^7.11.0",
+ "@mantine/modals": "^7.11.0",
+ "@mantine/tiptap": "^7.11.0",
"@homarr/server-settings": "workspace:^0.1.0",
"@t3-oss/env-nextjs": "^0.10.1",
- "@tanstack/react-query": "^5.40.0",
- "@tanstack/react-query-devtools": "^5.40.0",
- "@tanstack/react-query-next-experimental": "5.40.0",
- "@trpc/client": "11.0.0-rc.377",
+ "@tanstack/react-query": "^5.48.0",
+ "@tanstack/react-query-devtools": "^5.48.0",
+ "@tanstack/react-query-next-experimental": "5.48.0",
+ "@tabler/icons-react": "^3.7.0",
+ "@trpc/client": "next",
"@trpc/next": "next",
"@trpc/react-query": "next",
"@trpc/server": "next",
@@ -48,17 +50,21 @@
"@xterm/addon-fit": "0.10.0",
"@xterm/xterm": "^5.5.0",
"chroma-js": "^2.4.2",
+ "clsx": "^2.1.1",
"dayjs": "^1.11.11",
"dotenv": "^16.4.5",
- "flag-icons": "^7.2.2",
- "glob": "^10.4.1",
- "jotai": "^2.8.2",
- "next": "^14.2.3",
+ "flag-icons": "^7.2.3",
+ "glob": "^10.4.2",
+ "jotai": "^2.8.4",
+ "mantine-react-table": "2.0.0-beta.5",
+ "next": "^14.2.4",
"postcss-preset-mantine": "^1.15.0",
- "react": "18.3.1",
- "react-dom": "18.3.1",
+ "prismjs": "^1.29.0",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1",
"react-error-boundary": "^4.0.13",
- "sass": "^1.77.2",
+ "react-simple-code-editor": "^0.13.1",
+ "sass": "^1.77.6",
"superjson": "2.2.1",
"use-deep-compare-effect": "^1.8.1"
},
@@ -67,22 +73,15 @@
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/chroma-js": "2.4.4",
- "@types/node": "^20.12.12",
+ "@types/node": "^20.14.9",
+ "@types/prismjs": "^1.26.4",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"concurrently": "^8.2.2",
- "eslint": "^8.57.0",
- "prettier": "^3.2.5",
- "tsx": "4.11.0",
- "typescript": "^5.4.5"
- },
- "eslintConfig": {
- "root": true,
- "extends": [
- "@homarr/eslint-config/base",
- "@homarr/eslint-config/nextjs",
- "@homarr/eslint-config/react"
- ]
+ "eslint": "^9.6.0",
+ "node-loader": "^2.0.0",
+ "prettier": "^3.3.2",
+ "typescript": "^5.5.2"
},
"prettier": "@homarr/prettier-config"
}
diff --git a/apps/nextjs/src/app/[locale]/auth/login/_login-form.tsx b/apps/nextjs/src/app/[locale]/auth/login/_login-form.tsx
index d0345ec72..555862a51 100644
--- a/apps/nextjs/src/app/[locale]/auth/login/_login-form.tsx
+++ b/apps/nextjs/src/app/[locale]/auth/login/_login-form.tsx
@@ -12,6 +12,8 @@ import { useScopedI18n } from "@homarr/translation/client";
import type { z } from "@homarr/validation";
import { validation } from "@homarr/validation";
+import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
+
export const LoginForm = () => {
const t = useScopedI18n("user");
const router = useRouter();
@@ -32,7 +34,7 @@ export const LoginForm = () => {
redirect: false,
callbackUrl: "/",
})
- .then((response) => {
+ .then(async (response) => {
if (!response?.ok || response.error) {
throw response?.error;
}
@@ -41,6 +43,7 @@ export const LoginForm = () => {
title: t("action.login.notification.success.title"),
message: t("action.login.notification.success.message"),
});
+ await revalidatePathActionAsync("/");
router.push("/");
})
.catch((error: Error | string) => {
diff --git a/apps/nextjs/src/app/[locale]/boards/(content)/_context.tsx b/apps/nextjs/src/app/[locale]/boards/(content)/_context.tsx
index 808759af1..346baff75 100644
--- a/apps/nextjs/src/app/[locale]/boards/(content)/_context.tsx
+++ b/apps/nextjs/src/app/[locale]/boards/(content)/_context.tsx
@@ -49,7 +49,6 @@ export const BoardProvider = ({
useEffect(() => {
setReadySections((previous) => previous.filter((id) => data.sections.some((section) => section.id === id)));
- // eslint-disable-next-line react-hooks/exhaustive-deps
}, [data.sections.length, setReadySections]);
const markAsReady = useCallback((id: string) => {
diff --git a/apps/nextjs/src/app/[locale]/boards/(content)/_theme.tsx b/apps/nextjs/src/app/[locale]/boards/(content)/_theme.tsx
index bf68bb902..e3c87aa05 100644
--- a/apps/nextjs/src/app/[locale]/boards/(content)/_theme.tsx
+++ b/apps/nextjs/src/app/[locale]/boards/(content)/_theme.tsx
@@ -33,6 +33,7 @@ export const generateColors = (hex: string) => {
return rgbaColors.map((color) => {
return (
"#" +
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
color
.split("(")[1]!
.replaceAll(" ", "")
diff --git a/apps/nextjs/src/app/[locale]/boards/[name]/settings/_access/group-access.tsx b/apps/nextjs/src/app/[locale]/boards/[name]/settings/_access/group-access.tsx
index f465b6c08..7a6c4dce3 100644
--- a/apps/nextjs/src/app/[locale]/boards/[name]/settings/_access/group-access.tsx
+++ b/apps/nextjs/src/app/[locale]/boards/[name]/settings/_access/group-access.tsx
@@ -81,6 +81,7 @@ export const GroupsForm = ({ board, initialPermissions, onCountChange }: FormPro
{form.values.items.map((row, index) => (
}
permission={row.permission}
index={index}
diff --git a/apps/nextjs/src/app/[locale]/boards/[name]/settings/_access/user-access.tsx b/apps/nextjs/src/app/[locale]/boards/[name]/settings/_access/user-access.tsx
index 8ed7e98ee..775925252 100644
--- a/apps/nextjs/src/app/[locale]/boards/[name]/settings/_access/user-access.tsx
+++ b/apps/nextjs/src/app/[locale]/boards/[name]/settings/_access/user-access.tsx
@@ -93,6 +93,7 @@ export const UsersForm = ({ board, initialPermissions, onCountChange }: FormProp
{form.values.items.map((row, index) => (
}
permission={row.permission}
index={index}
diff --git a/apps/nextjs/src/app/[locale]/compose.tsx b/apps/nextjs/src/app/[locale]/compose.tsx
index 6997ca986..e7899c938 100644
--- a/apps/nextjs/src/app/[locale]/compose.tsx
+++ b/apps/nextjs/src/app/[locale]/compose.tsx
@@ -6,7 +6,6 @@ export const composeWrappers = (
wrappers: React.FunctionComponent[],
): React.FunctionComponent => {
return wrappers.reverse().reduce((Acc, Current): React.FunctionComponent => {
- // eslint-disable-next-line react/display-name
return (props) => (
diff --git a/apps/nextjs/src/app/[locale]/manage/_components/hero-banner.module.css b/apps/nextjs/src/app/[locale]/manage/_components/hero-banner.module.css
index cfadc375d..ae85132dc 100644
--- a/apps/nextjs/src/app/[locale]/manage/_components/hero-banner.module.css
+++ b/apps/nextjs/src/app/[locale]/manage/_components/hero-banner.module.css
@@ -1,5 +1,4 @@
.bannerContainer {
- padding: 3rem;
border-radius: 8px;
overflow: hidden;
background: linear-gradient(
diff --git a/apps/nextjs/src/app/[locale]/manage/_components/hero-banner.tsx b/apps/nextjs/src/app/[locale]/manage/_components/hero-banner.tsx
index 35943bcf9..cb1a3094a 100644
--- a/apps/nextjs/src/app/[locale]/manage/_components/hero-banner.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/_components/hero-banner.tsx
@@ -38,17 +38,17 @@ export const HeroBanner = () => {
const gridSpan = 12 / countIconGroups;
return (
-
+
-
+
Welcome back to your
-
-
- Homarr Dashboard
+
+
+ Homarr Board
-
+
{Array(countIconGroups)
.fill(0)
diff --git a/apps/nextjs/src/app/[locale]/manage/about/page.tsx b/apps/nextjs/src/app/[locale]/manage/about/page.tsx
index a1dadf037..a29e44e0f 100644
--- a/apps/nextjs/src/app/[locale]/manage/about/page.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/about/page.tsx
@@ -21,11 +21,11 @@ import { setStaticParamsLocale } from "next-international/server";
import { getScopedI18n, getStaticParams } from "@homarr/translation/server";
+import { homarrLogoPath } from "~/components/layout/logo/homarr-logo";
import { createMetaTitle } from "~/metadata";
import { getPackageAttributesAsync } from "~/versions/package-reader";
import contributorsData from "../../../../../../../static-data/contributors.json";
import translatorsData from "../../../../../../../static-data/translators.json";
-import logo from "../../../../../public/logo/logo.png";
import classes from "./about.module.css";
export async function generateMetadata() {
@@ -50,7 +50,7 @@ export default async function AboutPage({ params: { locale } }: PageProps) {
-
+
Homarr
diff --git a/apps/nextjs/src/app/[locale]/manage/apps/_app-delete-button.tsx b/apps/nextjs/src/app/[locale]/manage/apps/_app-delete-button.tsx
index 0f81cde48..dd20241d3 100644
--- a/apps/nextjs/src/app/[locale]/manage/apps/_app-delete-button.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/apps/_app-delete-button.tsx
@@ -49,7 +49,7 @@ export const AppDeleteButton = ({ app }: AppDeleteButtonProps) => {
}, [app, mutate, t, openConfirmModal]);
return (
-
+
);
diff --git a/apps/nextjs/src/app/[locale]/manage/apps/_form.tsx b/apps/nextjs/src/app/[locale]/manage/apps/_form.tsx
index 2cb6b6a47..2cf9bf9b3 100644
--- a/apps/nextjs/src/app/[locale]/manage/apps/_form.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/apps/_form.tsx
@@ -36,10 +36,10 @@ export const AppForm = (props: AppFormProps) => {
return (
diff --git a/apps/nextjs/src/app/[locale]/manage/boards/_components/create-board-button.tsx b/apps/nextjs/src/app/[locale]/manage/boards/_components/create-board-button.tsx
index 4bd24a346..1c59db412 100644
--- a/apps/nextjs/src/app/[locale]/manage/boards/_components/create-board-button.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/boards/_components/create-board-button.tsx
@@ -1,7 +1,6 @@
"use client";
import { useCallback } from "react";
-import { Button } from "@mantine/core";
import { IconCategoryPlus } from "@tabler/icons-react";
import { clientApi } from "@homarr/api/client";
@@ -10,6 +9,7 @@ import { useI18n } from "@homarr/translation/client";
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
import { AddBoardModal } from "~/components/manage/boards/add-board-modal";
+import { MobileAffixButton } from "~/components/manage/mobile-affix-button";
interface CreateBoardButtonProps {
boardNames: string[];
@@ -37,8 +37,8 @@ export const CreateBoardButton = ({ boardNames }: CreateBoardButtonProps) => {
}, [mutateAsync, boardNames, openModal]);
return (
- } onClick={onClick} loading={isPending}>
+ } onClick={onClick} loading={isPending}>
{t("management.page.board.action.new.label")}
-
+
);
};
diff --git a/apps/nextjs/src/app/[locale]/manage/boards/page.tsx b/apps/nextjs/src/app/[locale]/manage/boards/page.tsx
index 5dc4c79d7..b117a6d82 100644
--- a/apps/nextjs/src/app/[locale]/manage/boards/page.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/boards/page.tsx
@@ -10,6 +10,7 @@ import {
Group,
Menu,
MenuTarget,
+ Stack,
Text,
Title,
Tooltip,
@@ -22,6 +23,7 @@ import { getScopedI18n } from "@homarr/translation/server";
import { UserAvatar } from "@homarr/ui";
import { getBoardPermissionsAsync } from "~/components/board/permissions/server";
+import { ManageContainer } from "~/components/manage/manage-container";
import { BoardCardMenuDropdown } from "./_components/board-card-menu-dropdown";
import { CreateBoardButton } from "./_components/create-board-button";
@@ -31,20 +33,22 @@ export default async function ManageBoardsPage() {
const boards = await api.board.getAllBoards();
return (
- <>
-
- {t("title")}
- board.name)} />
-
+
+
+
+ {t("title")}
+ board.name)} />
+
-
- {boards.map((board) => (
-
-
-
- ))}
-
- >
+
+ {boards.map((board) => (
+
+
+
+ ))}
+
+
+
);
}
@@ -83,7 +87,7 @@ const BoardCard = async ({ board }: BoardCardProps) => {
{board.creator && (
- {board.creator?.name}
+ {board.creator.name}
)}
diff --git a/apps/nextjs/src/app/[locale]/manage/integrations/_integration-secret-card.tsx b/apps/nextjs/src/app/[locale]/manage/integrations/_components/secrets/integration-secret-card.tsx
similarity index 97%
rename from apps/nextjs/src/app/[locale]/manage/integrations/_integration-secret-card.tsx
rename to apps/nextjs/src/app/[locale]/manage/integrations/_components/secrets/integration-secret-card.tsx
index b23fd148a..f90b1d40f 100644
--- a/apps/nextjs/src/app/[locale]/manage/integrations/_integration-secret-card.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/integrations/_components/secrets/integration-secret-card.tsx
@@ -12,7 +12,7 @@ import type { RouterOutputs } from "@homarr/api";
import { integrationSecretKindObject } from "@homarr/definitions";
import { useI18n } from "@homarr/translation/client";
-import { integrationSecretIcons } from "./_integration-secret-icons";
+import { integrationSecretIcons } from "./integration-secret-icons";
dayjs.extend(relativeTime);
diff --git a/apps/nextjs/src/app/[locale]/manage/integrations/_integration-secret-icons.ts b/apps/nextjs/src/app/[locale]/manage/integrations/_components/secrets/integration-secret-icons.ts
similarity index 100%
rename from apps/nextjs/src/app/[locale]/manage/integrations/_integration-secret-icons.ts
rename to apps/nextjs/src/app/[locale]/manage/integrations/_components/secrets/integration-secret-icons.ts
diff --git a/apps/nextjs/src/app/[locale]/manage/integrations/_integration-secret-inputs.tsx b/apps/nextjs/src/app/[locale]/manage/integrations/_components/secrets/integration-secret-inputs.tsx
similarity index 92%
rename from apps/nextjs/src/app/[locale]/manage/integrations/_integration-secret-inputs.tsx
rename to apps/nextjs/src/app/[locale]/manage/integrations/_components/secrets/integration-secret-inputs.tsx
index f73e8516b..45f5b3156 100644
--- a/apps/nextjs/src/app/[locale]/manage/integrations/_integration-secret-inputs.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/integrations/_components/secrets/integration-secret-inputs.tsx
@@ -7,7 +7,7 @@ import { integrationSecretKindObject } from "@homarr/definitions";
import type { IntegrationSecretKind } from "@homarr/definitions";
import { useI18n } from "@homarr/translation/client";
-import { integrationSecretIcons } from "./_integration-secret-icons";
+import { integrationSecretIcons } from "./integration-secret-icons";
interface IntegrationSecretInputProps {
withAsterisk?: boolean;
@@ -50,7 +50,7 @@ const PrivateSecretInput = ({ kind, ...props }: IntegrationSecretInputProps) =>
}
/>
diff --git a/apps/nextjs/src/app/[locale]/manage/integrations/_integration-buttons.tsx b/apps/nextjs/src/app/[locale]/manage/integrations/_integration-buttons.tsx
index 2f31cb169..3640dc790 100644
--- a/apps/nextjs/src/app/[locale]/manage/integrations/_integration-buttons.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/integrations/_integration-buttons.tsx
@@ -56,7 +56,7 @@ export const DeleteIntegrationActionButton = ({ count, integration }: DeleteInte
},
});
}}
- aria-label="Delete integration"
+ aria-label={t("title")}
>
diff --git a/apps/nextjs/src/app/[locale]/manage/integrations/_integration-test-connection.tsx b/apps/nextjs/src/app/[locale]/manage/integrations/_integration-test-connection.tsx
deleted file mode 100644
index 2ae512edd..000000000
--- a/apps/nextjs/src/app/[locale]/manage/integrations/_integration-test-connection.tsx
+++ /dev/null
@@ -1,129 +0,0 @@
-"use client";
-
-import { useRef, useState } from "react";
-import { Alert, Anchor, Group, Loader } from "@mantine/core";
-import { IconCheck, IconInfoCircle, IconX } from "@tabler/icons-react";
-
-import type { RouterInputs } from "@homarr/api";
-import { clientApi } from "@homarr/api/client";
-import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
-import { useI18n, useScopedI18n } from "@homarr/translation/client";
-
-interface UseTestConnectionDirtyProps {
- defaultDirty: boolean;
- initialFormValue: {
- url: string;
- secrets: { kind: string; value: string | null }[];
- };
-}
-
-export const useTestConnectionDirty = ({ defaultDirty, initialFormValue }: UseTestConnectionDirtyProps) => {
- const [isDirty, setIsDirty] = useState(defaultDirty);
- const prevFormValueRef = useRef(initialFormValue);
-
- return {
- onValuesChange: (values: typeof initialFormValue) => {
- if (isDirty) return;
-
- // If relevant values changed, set dirty
- if (
- prevFormValueRef.current.url !== values.url ||
- !prevFormValueRef.current.secrets
- .map((secret) => secret.value)
- .every((secretValue, index) => values.secrets[index]?.value === secretValue)
- ) {
- setIsDirty(true);
- return;
- }
-
- // If relevant values changed back to last tested, set not dirty
- setIsDirty(false);
- },
- isDirty,
- removeDirty: () => {
- prevFormValueRef.current = initialFormValue;
- setIsDirty(false);
- },
- };
-};
-
-interface TestConnectionProps {
- isDirty: boolean;
- removeDirty: () => void;
- integration: RouterInputs["integration"]["testConnection"] & { name: string };
-}
-
-export const TestConnection = ({ integration, removeDirty, isDirty }: TestConnectionProps) => {
- const t = useScopedI18n("integration.testConnection");
- const { mutateAsync, ...mutation } = clientApi.integration.testConnection.useMutation();
-
- return (
-
- {
- await mutateAsync(integration, {
- onSuccess: () => {
- removeDirty();
- showSuccessNotification({
- title: t("notification.success.title"),
- message: t("notification.success.message"),
- });
- },
- onError: (error) => {
- if (error.data?.zodError?.fieldErrors.url) {
- showErrorNotification({
- title: t("notification.invalidUrl.title"),
- message: t("notification.invalidUrl.message"),
- });
- return;
- }
-
- if (error.message === "SECRETS_NOT_DEFINED") {
- showErrorNotification({
- title: t("notification.notAllSecretsProvided.title"),
- message: t("notification.notAllSecretsProvided.message"),
- });
- return;
- }
-
- showErrorNotification({
- title: t("notification.commonError.title"),
- message: t("notification.commonError.message"),
- });
- },
- });
- }}
- >
- {t("action")}
-
-
-
- );
-};
-
-interface TestConnectionIconProps {
- isDirty: boolean;
- isPending: boolean;
- isSuccess: boolean;
- isError: boolean;
- size: number;
-}
-
-const TestConnectionIcon = ({ isDirty, isPending, isSuccess, isError, size }: TestConnectionIconProps) => {
- if (isPending) return ;
- if (isDirty) return null;
- if (isSuccess) return ;
- if (isError) return ;
- return null;
-};
-
-export const TestConnectionNoticeAlert = () => {
- const t = useI18n();
- return (
- }>
- {t("integration.testConnection.alertNotice")}
-
- );
-};
diff --git a/apps/nextjs/src/app/[locale]/manage/integrations/edit/[id]/_integration-edit-form.tsx b/apps/nextjs/src/app/[locale]/manage/integrations/edit/[id]/_integration-edit-form.tsx
index e1521abd2..847479ea3 100644
--- a/apps/nextjs/src/app/[locale]/manage/integrations/edit/[id]/_integration-edit-form.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/integrations/edit/[id]/_integration-edit-form.tsx
@@ -8,6 +8,7 @@ import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { getAllSecretKindOptions, getDefaultSecretKinds } from "@homarr/definitions";
import { useZodForm } from "@homarr/form";
+import { convertIntegrationTestConnectionError } from "@homarr/integrations/client";
import { useConfirmModal } from "@homarr/modals";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n } from "@homarr/translation/client";
@@ -15,9 +16,8 @@ import type { z } from "@homarr/validation";
import { validation } from "@homarr/validation";
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
-import { SecretCard } from "../../_integration-secret-card";
-import { IntegrationSecretInput } from "../../_integration-secret-inputs";
-import { TestConnection, TestConnectionNoticeAlert, useTestConnectionDirty } from "../../_integration-test-connection";
+import { SecretCard } from "../../_components/secrets/integration-secret-card";
+import { IntegrationSecretInput } from "../../_components/secrets/integration-secret-inputs";
interface EditIntegrationForm {
integration: RouterOutputs["integration"]["byId"];
@@ -30,30 +30,23 @@ export const EditIntegrationForm = ({ integration }: EditIntegrationForm) => {
getAllSecretKindOptions(integration.kind).find((secretKinds) =>
integration.secrets.every((secret) => secretKinds.includes(secret.kind)),
) ?? getDefaultSecretKinds(integration.kind);
- const initialFormValues = {
- name: integration.name,
- url: integration.url,
- secrets: secretsKinds.map((kind) => ({
- kind,
- value: integration.secrets.find((secret) => secret.kind === kind)?.value ?? "",
- })),
- };
- const { isDirty, onValuesChange, removeDirty } = useTestConnectionDirty({
- defaultDirty: true,
- initialFormValue: initialFormValues,
- });
const router = useRouter();
const form = useZodForm(validation.integration.update.omit({ id: true }), {
- initialValues: initialFormValues,
- onValuesChange,
+ initialValues: {
+ name: integration.name,
+ url: integration.url,
+ secrets: secretsKinds.map((kind) => ({
+ kind,
+ value: integration.secrets.find((secret) => secret.kind === kind)?.value ?? "",
+ })),
+ },
});
const { mutateAsync, isPending } = clientApi.integration.update.useMutation();
const secretsMap = new Map(integration.secrets.map((secret) => [secret.kind, secret]));
const handleSubmitAsync = async (values: FormType) => {
- if (isDirty) return;
await mutateAsync(
{
id: integration.id,
@@ -71,7 +64,19 @@ export const EditIntegrationForm = ({ integration }: EditIntegrationForm) => {
});
void revalidatePathActionAsync("/manage/integrations").then(() => router.push("/manage/integrations"));
},
- onError: () => {
+ onError: (error) => {
+ const testConnectionError = convertIntegrationTestConnectionError(error.data?.error);
+
+ if (testConnectionError) {
+ showErrorNotification({
+ title: t(`integration.testConnection.notification.${testConnectionError.key}.title`),
+ message: testConnectionError.message
+ ? testConnectionError.message
+ : t(`integration.testConnection.notification.${testConnectionError.key}.message`),
+ });
+ return;
+ }
+
showErrorNotification({
title: t("integration.page.edit.notification.error.title"),
message: t("integration.page.edit.notification.error.message"),
@@ -84,8 +89,6 @@ export const EditIntegrationForm = ({ integration }: EditIntegrationForm) => {
return (
diff --git a/apps/nextjs/src/app/[locale]/manage/integrations/new/_integration-new-dropdown.tsx b/apps/nextjs/src/app/[locale]/manage/integrations/new/_integration-new-dropdown.tsx
index 8f03a39ff..b2700ff70 100644
--- a/apps/nextjs/src/app/[locale]/manage/integrations/new/_integration-new-dropdown.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/integrations/new/_integration-new-dropdown.tsx
@@ -3,7 +3,7 @@
import type { ChangeEvent } from "react";
import React, { useMemo, useState } from "react";
import Link from "next/link";
-import { Group, Menu, ScrollArea, Stack, Text, TextInput } from "@mantine/core";
+import { Flex, Group, Menu, ScrollArea, Text, TextInput } from "@mantine/core";
import { IconSearch } from "@tabler/icons-react";
import { getIntegrationName, integrationKinds } from "@homarr/definitions";
@@ -25,7 +25,7 @@ export const IntegrationCreateDropdownContent = () => {
);
return (
-
+
}
placeholder={t("integration.page.list.search")}
@@ -47,6 +47,6 @@ export const IntegrationCreateDropdownContent = () => {
) : (
{t("common.noResults")}
)}
-
+
);
};
diff --git a/apps/nextjs/src/app/[locale]/manage/integrations/new/_integration-new-form.tsx b/apps/nextjs/src/app/[locale]/manage/integrations/new/_integration-new-form.tsx
index 524b2efb9..d7ab653e1 100644
--- a/apps/nextjs/src/app/[locale]/manage/integrations/new/_integration-new-form.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/integrations/new/_integration-new-form.tsx
@@ -10,13 +10,13 @@ import type { IntegrationKind, IntegrationSecretKind } from "@homarr/definitions
import { getAllSecretKindOptions } from "@homarr/definitions";
import type { UseFormReturnType } from "@homarr/form";
import { useZodForm } from "@homarr/form";
+import { convertIntegrationTestConnectionError } from "@homarr/integrations/client";
import { showErrorNotification, showSuccessNotification } from "@homarr/notifications";
import { useI18n, useScopedI18n } from "@homarr/translation/client";
import type { z } from "@homarr/validation";
import { validation } from "@homarr/validation";
-import { IntegrationSecretInput } from "../_integration-secret-inputs";
-import { TestConnection, TestConnectionNoticeAlert, useTestConnectionDirty } from "../_integration-test-connection";
+import { IntegrationSecretInput } from "../_components/secrets/integration-secret-inputs";
import { revalidatePathActionAsync } from "../../../../revalidatePathAction";
interface NewIntegrationFormProps {
@@ -28,27 +28,20 @@ interface NewIntegrationFormProps {
export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) => {
const t = useI18n();
const secretKinds = getAllSecretKindOptions(searchParams.kind);
- const initialFormValues = {
- name: searchParams.name ?? "",
- url: searchParams.url ?? "",
- secrets: secretKinds[0].map((kind) => ({
- kind,
- value: "",
- })),
- };
- const { isDirty, onValuesChange, removeDirty } = useTestConnectionDirty({
- defaultDirty: true,
- initialFormValue: initialFormValues,
- });
const router = useRouter();
const form = useZodForm(validation.integration.create.omit({ kind: true }), {
- initialValues: initialFormValues,
- onValuesChange,
+ initialValues: {
+ name: searchParams.name ?? "",
+ url: searchParams.url ?? "",
+ secrets: secretKinds[0].map((kind) => ({
+ kind,
+ value: "",
+ })),
+ },
});
const { mutateAsync, isPending } = clientApi.integration.create.useMutation();
const handleSubmitAsync = async (values: FormType) => {
- if (isDirty) return;
await mutateAsync(
{
kind: searchParams.kind,
@@ -62,7 +55,19 @@ export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) =>
});
void revalidatePathActionAsync("/manage/integrations").then(() => router.push("/manage/integrations"));
},
- onError: () => {
+ onError: (error) => {
+ const testConnectionError = convertIntegrationTestConnectionError(error.data?.error);
+
+ if (testConnectionError) {
+ showErrorNotification({
+ title: t(`integration.testConnection.notification.${testConnectionError.key}.title`),
+ message: testConnectionError.message
+ ? testConnectionError.message
+ : t(`integration.testConnection.notification.${testConnectionError.key}.message`),
+ });
+ return;
+ }
+
showErrorNotification({
title: t("integration.page.create.notification.error.title"),
message: t("integration.page.create.notification.error.message"),
@@ -75,8 +80,6 @@ export const NewIntegrationForm = ({ searchParams }: NewIntegrationFormProps) =>
return (
diff --git a/apps/nextjs/src/app/[locale]/manage/integrations/new/page.tsx b/apps/nextjs/src/app/[locale]/manage/integrations/new/page.tsx
index 23eb474f5..0db877a11 100644
--- a/apps/nextjs/src/app/[locale]/manage/integrations/new/page.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/integrations/new/page.tsx
@@ -17,6 +17,7 @@ interface NewIntegrationPageProps {
}
export default async function IntegrationsNewPage({ searchParams }: NewIntegrationPageProps) {
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const result = z.enum([integrationKinds[0]!, ...integrationKinds.slice(1)]).safeParse(searchParams.kind);
if (!result.success) {
notFound();
diff --git a/apps/nextjs/src/app/[locale]/manage/integrations/page.tsx b/apps/nextjs/src/app/[locale]/manage/integrations/page.tsx
index 07b4c894d..8bdcd0329 100644
--- a/apps/nextjs/src/app/[locale]/manage/integrations/page.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/integrations/page.tsx
@@ -1,3 +1,5 @@
+import { Fragment } from "react";
+import type { PropsWithChildren } from "react";
import Link from "next/link";
import {
AccordionControl,
@@ -5,9 +7,11 @@ import {
AccordionPanel,
ActionIcon,
ActionIconGroup,
+ Affix,
Anchor,
+ Box,
Button,
- Container,
+ Divider,
Group,
Menu,
MenuDropdown,
@@ -22,7 +26,7 @@ import {
Text,
Title,
} from "@mantine/core";
-import { IconChevronDown, IconPencil } from "@tabler/icons-react";
+import { IconChevronDown, IconChevronUp, IconPencil } from "@tabler/icons-react";
import type { RouterOutputs } from "@homarr/api";
import { api } from "@homarr/api/server";
@@ -32,6 +36,7 @@ import { getIntegrationName } from "@homarr/definitions";
import { getScopedI18n } from "@homarr/translation/server";
import { CountBadge } from "@homarr/ui";
+import { ManageContainer } from "~/components/manage/manage-container";
import { ActiveTabAccordion } from "../../../../components/active-tab-accordion";
import { IntegrationAvatar } from "./_integration-avatar";
import { DeleteIntegrationActionButton } from "./_integration-buttons";
@@ -48,26 +53,47 @@ export default async function IntegrationsPage({ searchParams }: IntegrationsPag
const t = await getScopedI18n("integration");
return (
-
+
{t("page.list.title")}
-
+
+
+
+
+
+ }>{t("action.create")}
+
+
+
+
+
+
+
+
+ }>{t("action.create")}
+
+
+
-
+
);
}
+const IntegrationSelectMenu = ({ children }: PropsWithChildren) => {
+ return (
+
+ );
+};
+
interface IntegrationListProps {
integrations: RouterOutputs["integration"]["all"];
activeTab?: IntegrationKind;
@@ -82,6 +108,7 @@ const IntegrationList = async ({ integrations, activeTab }: IntegrationListProps
const grouppedIntegrations = integrations.reduce(
(acc, integration) => {
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!acc[integration.kind]) {
acc[integration.kind] = [];
}
@@ -104,7 +131,7 @@ const IntegrationList = async ({ integrations, activeTab }: IntegrationListProps
-
+
{t("field.name.label")}
@@ -129,7 +156,7 @@ const IntegrationList = async ({ integrations, activeTab }: IntegrationListProps
href={`/manage/integrations/edit/${integration.id}`}
variant="subtle"
color="gray"
- aria-label="Edit integration"
+ aria-label={t("page.edit.title", { name: getIntegrationName(integration.kind) })}
>
@@ -141,6 +168,34 @@ const IntegrationList = async ({ integrations, activeTab }: IntegrationListProps
))}
+
+
+ {integrations.map((integration, index) => (
+
+ {index !== 0 && }
+
+
+ {integration.name}
+
+
+
+
+
+
+
+
+ {integration.url}
+
+
+
+ ))}
+
))}
diff --git a/apps/nextjs/src/app/[locale]/manage/page.tsx b/apps/nextjs/src/app/[locale]/manage/page.tsx
index 711a2e3a3..3e45269e9 100644
--- a/apps/nextjs/src/app/[locale]/manage/page.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/page.tsx
@@ -72,8 +72,8 @@ export default async function ManagementPage() {
{links.map((link, index) => (
-
-
+
+
{link.count}
diff --git a/apps/nextjs/src/app/[locale]/manage/settings/_components/analytics.settings.tsx b/apps/nextjs/src/app/[locale]/manage/settings/_components/analytics.settings.tsx
index 7ac93b917..fe10331c9 100644
--- a/apps/nextjs/src/app/[locale]/manage/settings/_components/analytics.settings.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/settings/_components/analytics.settings.tsx
@@ -109,7 +109,9 @@ const SwitchSetting = ({
{title}
- {text}
+
+ {text}
+
diff --git a/apps/nextjs/src/app/[locale]/manage/tools/docker/DockerTable.tsx b/apps/nextjs/src/app/[locale]/manage/tools/docker/DockerTable.tsx
new file mode 100644
index 000000000..fe56590c7
--- /dev/null
+++ b/apps/nextjs/src/app/[locale]/manage/tools/docker/DockerTable.tsx
@@ -0,0 +1,154 @@
+"use client";
+
+import type { ButtonProps, MantineColor } from "@mantine/core";
+import { Avatar, Badge, Box, Button, Group, Text } from "@mantine/core";
+import { IconPlayerPlay, IconPlayerStop, IconRotateClockwise, IconTrash } from "@tabler/icons-react";
+import type { MRT_ColumnDef } from "mantine-react-table";
+import { MantineReactTable, useMantineReactTable } from "mantine-react-table";
+
+import type { RouterOutputs } from "@homarr/api";
+import { useTimeAgo } from "@homarr/common";
+import type { DockerContainerState } from "@homarr/definitions";
+import type { TranslationFunction } from "@homarr/translation";
+import { useI18n, useScopedI18n } from "@homarr/translation/client";
+import { OverflowBadge } from "@homarr/ui";
+
+const createColumns = (
+ t: TranslationFunction,
+): MRT_ColumnDef[] => [
+ {
+ accessorKey: "name",
+ header: t("docker.field.name.label"),
+ Cell({ renderedCellValue, row }) {
+ return (
+
+
+ {row.original.name.at(0)?.toUpperCase()}
+
+ {renderedCellValue}
+
+ );
+ },
+ },
+ {
+ accessorKey: "state",
+ header: t("docker.field.state.label"),
+ size: 120,
+ Cell({ cell }) {
+ return ;
+ },
+ },
+ {
+ accessorKey: "image",
+ header: t("docker.field.containerImage.label"),
+ maxSize: 200,
+ Cell({ renderedCellValue }) {
+ return (
+
+ {renderedCellValue}
+
+ );
+ },
+ },
+ {
+ accessorKey: "ports",
+ header: t("docker.field.ports.label"),
+ Cell({ cell }) {
+ return (
+ port.PrivatePort.toString())} />
+ );
+ },
+ },
+];
+
+export function DockerTable({ containers, timestamp }: RouterOutputs["docker"]["getContainers"]) {
+ const t = useI18n();
+ const tDocker = useScopedI18n("docker");
+ const relativeTime = useTimeAgo(timestamp);
+ const table = useMantineReactTable({
+ data: containers,
+ enableDensityToggle: false,
+ enableColumnActions: false,
+ enableColumnFilters: false,
+ enablePagination: false,
+ enableRowSelection: true,
+ positionToolbarAlertBanner: "top",
+ enableTableFooter: false,
+ enableBottomToolbar: false,
+ positionGlobalFilter: "right",
+ mantineSearchTextInputProps: {
+ placeholder: tDocker("table.search", { count: containers.length }),
+ style: { minWidth: 300 },
+ autoFocus: true,
+ },
+
+ initialState: { density: "xs", showGlobalFilter: true },
+ renderToolbarAlertBannerContent: ({ groupedAlert, table }) => {
+ return (
+
+ {groupedAlert}
+
+ {tDocker("table.selected", {
+ selectCount: table.getSelectedRowModel().rows.length,
+ totalCount: table.getRowCount(),
+ })}
+
+
+
+ );
+ },
+
+ columns: createColumns(t),
+ });
+ return (
+ <>
+ {tDocker("table.updated", { when: relativeTime })}
+
+ >
+ );
+}
+
+const ContainerActionBar = () => {
+ const t = useScopedI18n("docker.action");
+ const sharedButtonProps = {
+ variant: "light",
+ radius: "md",
+ } satisfies Partial;
+
+ return (
+
+ } color="green" {...sharedButtonProps}>
+ {t("start")}
+
+ } color="red" {...sharedButtonProps}>
+ {t("stop")}
+
+ } color="orange" {...sharedButtonProps}>
+ {t("restart")}
+
+ } color="red" {...sharedButtonProps}>
+ {t("remove")}
+
+
+ );
+};
+
+const containerStates = {
+ created: "cyan",
+ running: "green",
+ paused: "yellow",
+ restarting: "orange",
+ exited: "red",
+ removing: "pink",
+ dead: "dark",
+} satisfies Record;
+
+const ContainerStateBadge = ({ state }: { state: DockerContainerState }) => {
+ const t = useScopedI18n("docker.field.state.option");
+
+ return (
+
+ {t(state)}
+
+ );
+};
diff --git a/apps/nextjs/src/app/[locale]/manage/tools/docker/page.tsx b/apps/nextjs/src/app/[locale]/manage/tools/docker/page.tsx
new file mode 100644
index 000000000..9a18f5ba5
--- /dev/null
+++ b/apps/nextjs/src/app/[locale]/manage/tools/docker/page.tsx
@@ -0,0 +1,18 @@
+import { Stack, Title } from "@mantine/core";
+
+import { api } from "@homarr/api/server";
+import { getScopedI18n } from "@homarr/translation/server";
+
+import { DockerTable } from "./DockerTable";
+
+export default async function DockerPage() {
+ const { containers, timestamp } = await api.docker.getContainers();
+ const tDocker = await getScopedI18n("docker");
+
+ return (
+
+ {tDocker("title")}
+
+
+ );
+}
diff --git a/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_profile-avatar-form.tsx b/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_profile-avatar-form.tsx
index 0e819d163..4ad991bd6 100644
--- a/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_profile-avatar-form.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/_components/_profile-avatar-form.tsx
@@ -136,6 +136,6 @@ const fileToBase64Async = async (file: File): Promise =>
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
- reader.onload = () => resolve(reader.result?.toString() || "");
+ reader.onload = () => resolve(reader.result?.toString() ?? "");
reader.onerror = reject;
});
diff --git a/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/page.tsx b/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/page.tsx
index 82d3a6f89..3dfcfca59 100644
--- a/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/page.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/users/[userId]/general/page.tsx
@@ -35,7 +35,7 @@ export async function generateMetadata({ params }: Props) {
const t = await getScopedI18n("management.page.user.edit");
return {
- title: createMetaTitle(t("metaTitle", { username: user?.name })),
+ title: createMetaTitle(t("metaTitle", { username: user.name })),
};
}
diff --git a/apps/nextjs/src/app/[locale]/manage/users/[userId]/layout.tsx b/apps/nextjs/src/app/[locale]/manage/users/[userId]/layout.tsx
index be6c0f71a..b74e6d780 100644
--- a/apps/nextjs/src/app/[locale]/manage/users/[userId]/layout.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/users/[userId]/layout.tsx
@@ -1,7 +1,7 @@
import type { PropsWithChildren } from "react";
import Link from "next/link";
import { notFound } from "next/navigation";
-import { Button, Container, Grid, GridCol, Group, Stack, Text, Title } from "@mantine/core";
+import { Button, Grid, GridCol, Group, Stack, Text, Title } from "@mantine/core";
import { IconSettings, IconShieldLock } from "@tabler/icons-react";
import { api } from "@homarr/api/server";
@@ -9,6 +9,7 @@ import { auth } from "@homarr/auth/next";
import { getI18n, getScopedI18n } from "@homarr/translation/server";
import { UserAvatar } from "@homarr/ui";
+import { ManageContainer } from "~/components/manage/manage-container";
import { catchTrpcNotFound } from "~/errors/trpc-not-found";
import { NavigationLink } from "../groups/[id]/_navigation";
import { canAccessUserEditPage } from "./access";
@@ -28,7 +29,7 @@ export default async function Layout({ children, params }: PropsWithChildren
+
@@ -64,6 +65,6 @@ export default async function Layout({ children, params }: PropsWithChildren
{children}
-
+
);
}
diff --git a/apps/nextjs/src/app/[locale]/manage/users/create/_components/create-user-stepper.tsx b/apps/nextjs/src/app/[locale]/manage/users/create/_components/create-user-stepper.tsx
index eadf51ddd..7526e821a 100644
--- a/apps/nextjs/src/app/[locale]/manage/users/create/_components/create-user-stepper.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/users/create/_components/create-user-stepper.tsx
@@ -71,7 +71,8 @@ export const UserCreateStepperComponent = () => {
const allForms = useMemo(() => [generalForm, securityForm], [generalForm, securityForm]);
- const isCurrentFormValid = allForms[active] ? (allForms[active]!.isValid satisfies () => boolean) : () => true;
+ const activeForm = allForms[active];
+ const isCurrentFormValid = activeForm ? activeForm.isValid : () => true;
const canNavigateToNextStep = isCurrentFormValid();
const controlledGoToNextStep = useCallback(async () => {
diff --git a/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/layout.tsx b/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/layout.tsx
index e512c13b5..42d9acbc6 100644
--- a/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/layout.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/layout.tsx
@@ -1,11 +1,12 @@
import type { PropsWithChildren } from "react";
import Link from "next/link";
-import { Button, Container, Grid, GridCol, Group, Stack, Text, Title } from "@mantine/core";
+import { Button, Grid, GridCol, Group, Stack, Text, Title } from "@mantine/core";
import { IconLock, IconSettings, IconUsersGroup } from "@tabler/icons-react";
import { api } from "@homarr/api/server";
import { getI18n, getScopedI18n } from "@homarr/translation/server";
+import { ManageContainer } from "~/components/manage/manage-container";
import { NavigationLink } from "./_navigation";
interface LayoutProps {
@@ -18,7 +19,7 @@ export default async function Layout({ children, params }: PropsWithChildren
+
@@ -54,6 +55,6 @@ export default async function Layout({ children, params }: PropsWithChildren
{children}
-
+
);
}
diff --git a/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/_add-group-member.tsx b/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/_add-group-member.tsx
index 2d7d2941d..5bac56612 100644
--- a/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/_add-group-member.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/_add-group-member.tsx
@@ -1,7 +1,6 @@
"use client";
import { useCallback } from "react";
-import { Button } from "@mantine/core";
import { clientApi } from "@homarr/api/client";
import { useModalAction } from "@homarr/modals";
@@ -9,6 +8,7 @@ import { useScopedI18n } from "@homarr/translation/client";
import { UserSelectModal } from "~/app/[locale]/boards/[name]/settings/_access/user-select-modal";
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
+import { MobileAffixButton } from "~/components/manage/mobile-affix-button";
interface AddGroupMemberProps {
groupId: string;
@@ -40,8 +40,8 @@ export const AddGroupMember = ({ groupId, presentUserIds }: AddGroupMemberProps)
}, [openModal, presentUserIds, groupId, mutateAsync, tMembersAdd]);
return (
-
+
);
};
diff --git a/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/page.tsx b/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/page.tsx
index 7f694ba6b..e5f0fc604 100644
--- a/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/page.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/members/page.tsx
@@ -24,7 +24,8 @@ export default async function GroupsDetailPage({ params, searchParams }: GroupsD
const group = await api.group.getById({ id: params.id });
const filteredMembers = searchParams.search
- ? group.members.filter((member) => member.name?.toLowerCase().includes(searchParams.search!.trim().toLowerCase()))
+ ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ group.members.filter((member) => member.name?.toLowerCase().includes(searchParams.search!.trim().toLowerCase()))
: group.members;
return (
diff --git a/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/permissions/page.tsx b/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/permissions/page.tsx
index 21f89c5e9..303d7b22d 100644
--- a/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/permissions/page.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/users/groups/[id]/permissions/page.tsx
@@ -101,7 +101,7 @@ interface PermissionRowProps {
const PermissionRow = ({ name, label, description }: PermissionRowProps) => {
return (
-
+
{label}
{description}
diff --git a/apps/nextjs/src/app/[locale]/manage/users/groups/_add-group.tsx b/apps/nextjs/src/app/[locale]/manage/users/groups/_add-group.tsx
index b28ddccd0..ed6e6dd3d 100644
--- a/apps/nextjs/src/app/[locale]/manage/users/groups/_add-group.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/users/groups/_add-group.tsx
@@ -11,6 +11,7 @@ import { useI18n } from "@homarr/translation/client";
import { validation } from "@homarr/validation";
import { revalidatePathActionAsync } from "~/app/revalidatePathAction";
+import { MobileAffixButton } from "~/components/manage/mobile-affix-button";
export const AddGroup = () => {
const t = useI18n();
@@ -21,9 +22,9 @@ export const AddGroup = () => {
}, [openModal]);
return (
-
+
);
};
diff --git a/apps/nextjs/src/app/[locale]/manage/users/groups/page.tsx b/apps/nextjs/src/app/[locale]/manage/users/groups/page.tsx
index 410fe57ed..ec46596bc 100644
--- a/apps/nextjs/src/app/[locale]/manage/users/groups/page.tsx
+++ b/apps/nextjs/src/app/[locale]/manage/users/groups/page.tsx
@@ -1,17 +1,5 @@
import Link from "next/link";
-import {
- Anchor,
- Container,
- Group,
- Stack,
- Table,
- TableTbody,
- TableTd,
- TableTh,
- TableThead,
- TableTr,
- Title,
-} from "@mantine/core";
+import { Anchor, Group, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Title } from "@mantine/core";
import type { RouterOutputs } from "@homarr/api";
import { api } from "@homarr/api/server";
@@ -19,6 +7,7 @@ import { getI18n } from "@homarr/translation/server";
import { SearchInput, TablePagination, UserAvatarGroup } from "@homarr/ui";
import { z } from "@homarr/validation";
+import { ManageContainer } from "~/components/manage/manage-container";
import { AddGroup } from "./_add-group";
const searchParamsSchema = z.object({
@@ -41,7 +30,7 @@ export default async function GroupsListPage(props: GroupsListPageProps) {
const { items: groups, totalCount } = await api.group.getPaginated(searchParams);
return (
-
+
{t("group.title")}
@@ -72,7 +61,7 @@ export default async function GroupsListPage(props: GroupsListPageProps) {
-
+
);
}
diff --git a/apps/nextjs/src/components/board/items/item-actions.tsx b/apps/nextjs/src/components/board/items/item-actions.tsx
index 2246853ea..5fd6c0841 100644
--- a/apps/nextjs/src/components/board/items/item-actions.tsx
+++ b/apps/nextjs/src/components/board/items/item-actions.tsx
@@ -90,7 +90,6 @@ export const useItemActions = () => {
const updateItemOptions = useCallback(
({ itemId, newOptions }: UpdateItemOptions) => {
updateBoard((previous) => {
- if (!previous) return previous;
return {
...previous,
sections: previous.sections.map((section) => {
@@ -117,7 +116,6 @@ export const useItemActions = () => {
const updateItemAdvancedOptions = useCallback(
({ itemId, newAdvancedOptions }: UpdateItemAdvancedOptions) => {
updateBoard((previous) => {
- if (!previous) return previous;
return {
...previous,
sections: previous.sections.map((section) => {
@@ -144,7 +142,6 @@ export const useItemActions = () => {
const updateItemIntegrations = useCallback(
({ itemId, newIntegrations }: UpdateItemIntegrations) => {
updateBoard((previous) => {
- if (!previous) return previous;
return {
...previous,
sections: previous.sections.map((section) => {
diff --git a/apps/nextjs/src/components/board/items/item-select-modal.tsx b/apps/nextjs/src/components/board/items/item-select-modal.tsx
index 64d7c4a5f..3789ebca3 100644
--- a/apps/nextjs/src/components/board/items/item-select-modal.tsx
+++ b/apps/nextjs/src/components/board/items/item-select-modal.tsx
@@ -1,12 +1,12 @@
import { Button, Card, Center, Grid, Stack, Text } from "@mantine/core";
+import { objectEntries } from "@homarr/common";
import type { WidgetKind } from "@homarr/definitions";
import { createModal } from "@homarr/modals";
import { useI18n } from "@homarr/translation/client";
+import { widgetImports } from "@homarr/widgets";
+import type { WidgetDefinition } from "@homarr/widgets";
-import { objectEntries } from "../../../../../../packages/common/src";
-import { widgetImports } from "../../../../../../packages/widgets/src";
-import type { WidgetDefinition } from "../../../../../../packages/widgets/src/definition";
import { useItemActions } from "./item-actions";
export const ItemSelectModal = createModal(({ actions }) => {
diff --git a/apps/nextjs/src/components/board/sections/category/category-actions.ts b/apps/nextjs/src/components/board/sections/category/category-actions.ts
index f78144536..c29751f38 100644
--- a/apps/nextjs/src/components/board/sections/category/category-actions.ts
+++ b/apps/nextjs/src/components/board/sections/category/category-actions.ts
@@ -37,6 +37,7 @@ export const useCategoryActions = () => {
sections: [
// Place sections before the new category
...previous.sections.filter(
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
(section) => (section.kind === "category" || section.kind === "empty") && section.position < position,
),
{
@@ -56,6 +57,7 @@ export const useCategoryActions = () => {
...previous.sections
.filter(
(section): section is CategorySection | EmptySection =>
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
(section.kind === "category" || section.kind === "empty") && section.position >= position,
)
.map((section) => ({
@@ -74,6 +76,7 @@ export const useCategoryActions = () => {
const lastSection = previous.sections
.filter(
(section): section is CategorySection | EmptySection =>
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
section.kind === "empty" || section.kind === "category",
)
.sort((sectionA, sectionB) => sectionB.position - sectionA.position)
@@ -130,12 +133,13 @@ export const useCategoryActions = () => {
(section): section is CategorySection => section.kind === "category" && section.id === id,
);
if (!currentCategory) return previous;
- if (currentCategory?.position === 1 && direction === "up") return previous;
- if (currentCategory?.position === previous.sections.length - 2 && direction === "down") return previous;
+ if (currentCategory.position === 1 && direction === "up") return previous;
+ if (currentCategory.position === previous.sections.length - 2 && direction === "down") return previous;
return {
...previous,
sections: previous.sections.map((section) => {
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (section.kind !== "category" && section.kind !== "empty") return section;
const offset = direction === "up" ? -2 : 2;
// Move category and empty section
diff --git a/apps/nextjs/src/components/board/sections/content.tsx b/apps/nextjs/src/components/board/sections/content.tsx
index 487207eb4..96ceaed99 100644
--- a/apps/nextjs/src/components/board/sections/content.tsx
+++ b/apps/nextjs/src/components/board/sections/content.tsx
@@ -1,6 +1,3 @@
-/* eslint-disable react/no-unknown-property */
-// Ignored because of gridstack attributes
-
import type { RefObject } from "react";
import { useEffect, useMemo, useRef } from "react";
import { ActionIcon, Card, Menu } from "@mantine/core";
@@ -65,14 +62,13 @@ const BoardItem = ({ refs, item, opacity }: ItemProps) => {
gs-h={item.height}
gs-min-w={1}
gs-min-h={1}
- gs-max-w={4}
- gs-max-h={4}
ref={refs.items.current[item.id] as RefObject}
>
{
styles={{
root: {
"--opacity": opacity / 100,
+ containerType: "size",
},
}}
p={0}
@@ -122,6 +119,7 @@ const BoardItemContent = ({ item, ...dimensions }: ItemContentProps) => {
combobox.openDropdown()}
onFocus={(event) => {
@@ -81,7 +83,7 @@ export const IconPicker = ({ initialValue, onChange, error, onFocus, onBlur }: I
rightSectionPointerEvents="none"
withAsterisk
error={error}
- label="Icon URL"
+ label={t("iconPicker.label")}
/>
diff --git a/apps/nextjs/src/components/layout/background.tsx b/apps/nextjs/src/components/layout/background.tsx
index 1c5b7c8b4..5c991e1a6 100644
--- a/apps/nextjs/src/components/layout/background.tsx
+++ b/apps/nextjs/src/components/layout/background.tsx
@@ -20,11 +20,11 @@ export const useOptionalBackgroundProps = (): Partial => {
}
return {
- bg: `url(${board?.backgroundImageUrl})`,
+ bg: `url(${board.backgroundImageUrl})`,
bgp: "center center",
- bgsz: board?.backgroundImageSize ?? "cover",
- bgr: board?.backgroundImageRepeat ?? "no-repeat",
- bga: board?.backgroundImageAttachment ?? "fixed",
+ bgsz: board.backgroundImageSize,
+ bgr: board.backgroundImageRepeat,
+ bga: board.backgroundImageAttachment,
};
};
@@ -49,7 +49,7 @@ export const BoardBackgroundVideo = () => {
height: "100vh",
top: 0,
left: 0,
- objectFit: board.backgroundImageSize ?? "cover",
+ objectFit: board.backgroundImageSize,
}}
>
diff --git a/apps/nextjs/src/components/layout/header/button.tsx b/apps/nextjs/src/components/layout/header/button.tsx
index 0604c137b..98bdbe0b7 100644
--- a/apps/nextjs/src/components/layout/header/button.tsx
+++ b/apps/nextjs/src/components/layout/header/button.tsx
@@ -22,7 +22,6 @@ const headerButtonActionIconProps: ActionIconProps = {
size: "lg",
};
-// eslint-disable-next-line react/display-name
export const HeaderButton = forwardRef((props, ref) => {
if ("href" in props) {
return (
diff --git a/apps/nextjs/src/components/layout/navigation-link.tsx b/apps/nextjs/src/components/layout/navigation-link.tsx
index c65f9381c..6aec0a9f1 100644
--- a/apps/nextjs/src/components/layout/navigation-link.tsx
+++ b/apps/nextjs/src/components/layout/navigation-link.tsx
@@ -35,19 +35,24 @@ const NavLinkHref = (props: NavigationLinkHref) => {
);
};
-const NavLinkWithItems = (props: NavigationLinkWithItems) => (
-
- {props.items.map((item) => (
-
- ))}
-
-);
+const NavLinkWithItems = (props: NavigationLinkWithItems) => {
+ const pathname = usePathname();
+ const isActive = props.items.some((item) => item.href === pathname);
+ return (
+
+ {props.items.map((item) => (
+
+ ))}
+
+ );
+};
interface CommonNavigationLinkProps {
label: string;
diff --git a/apps/nextjs/src/components/manage/boards/add-board-modal.tsx b/apps/nextjs/src/components/manage/boards/add-board-modal.tsx
index bdc225a1f..1e1723edd 100644
--- a/apps/nextjs/src/components/manage/boards/add-board-modal.tsx
+++ b/apps/nextjs/src/components/manage/boards/add-board-modal.tsx
@@ -1,10 +1,9 @@
import { Button, Group, Stack, TextInput } from "@mantine/core";
-import { boardSchemas } from "node_modules/@homarr/validation/src/board";
import { useZodForm } from "@homarr/form";
import { createModal } from "@homarr/modals";
import { useI18n } from "@homarr/translation/client";
-import { z } from "@homarr/validation";
+import { validation, z } from "@homarr/validation";
import { createCustomErrorParams } from "@homarr/validation/form";
interface InnerProps {
@@ -16,7 +15,7 @@ export const AddBoardModal = createModal(({ actions, innerProps }) =
const t = useI18n();
const form = useZodForm(
z.object({
- name: boardSchemas.byName.shape.name.refine((value) => !innerProps.boardNames.includes(value), {
+ name: validation.board.byName.shape.name.refine((value) => !innerProps.boardNames.includes(value), {
params: createCustomErrorParams("boardAlreadyExists"),
}),
}),
diff --git a/apps/nextjs/src/components/manage/manage-container.tsx b/apps/nextjs/src/components/manage/manage-container.tsx
new file mode 100644
index 000000000..3d31b3e2f
--- /dev/null
+++ b/apps/nextjs/src/components/manage/manage-container.tsx
@@ -0,0 +1,11 @@
+import type { PropsWithChildren } from "react";
+import type { MantineSize } from "@mantine/core";
+import { Container } from "@mantine/core";
+
+export const ManageContainer = ({ children, size }: PropsWithChildren<{ size?: MantineSize }>) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/apps/nextjs/src/components/manage/mobile-affix-button.tsx b/apps/nextjs/src/components/manage/mobile-affix-button.tsx
new file mode 100644
index 000000000..abe4f017c
--- /dev/null
+++ b/apps/nextjs/src/components/manage/mobile-affix-button.tsx
@@ -0,0 +1,16 @@
+import { forwardRef } from "react";
+import type { ButtonProps } from "@mantine/core";
+import { Affix, Button, createPolymorphicComponent } from "@mantine/core";
+
+type MobileAffixButtonProps = Omit;
+
+export const MobileAffixButton = createPolymorphicComponent<"button", MobileAffixButtonProps>(
+ forwardRef((props, ref) => (
+ <>
+
+
+
+
+ >
+ )),
+);
diff --git a/apps/nextjs/src/components/user-avatar-menu.tsx b/apps/nextjs/src/components/user-avatar-menu.tsx
index 0cd5e0726..f8be743e6 100644
--- a/apps/nextjs/src/components/user-avatar-menu.tsx
+++ b/apps/nextjs/src/components/user-avatar-menu.tsx
@@ -72,7 +72,7 @@ export const UserAvatarMenu = ({ children }: UserAvatarMenuProps) => {
<>
}
>
{t("preferences")}
diff --git a/apps/nextjs/src/components/user-avatar.tsx b/apps/nextjs/src/components/user-avatar.tsx
index d75606caf..9ddfba554 100644
--- a/apps/nextjs/src/components/user-avatar.tsx
+++ b/apps/nextjs/src/components/user-avatar.tsx
@@ -17,7 +17,8 @@ export const UserAvatar = async ({ size }: UserAvatarProps) => {
if (!currentSession?.user) return ;
if (currentSession.user.image)
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return ;
- return {currentSession.user.name!.substring(0, 2).toUpperCase()};
+ return {currentSession.user.name?.substring(0, 2).toUpperCase()};
};
diff --git a/apps/nextjs/src/env.mjs b/apps/nextjs/src/env.mjs
index 2e4452340..d5b00da89 100644
--- a/apps/nextjs/src/env.mjs
+++ b/apps/nextjs/src/env.mjs
@@ -27,6 +27,9 @@ export const env = createEnv({
DB_USER: isUsingDbCredentials ? z.string() : z.string().optional(),
DB_PASSWORD: isUsingDbCredentials ? z.string() : z.string().optional(),
DB_NAME: isUsingDbUrl ? z.string().optional() : z.string(),
+ // Comma separated list of docker hostnames that can be used to connect to query the docker endpoints (localhost:2375,host.docker.internal:2375, ...)
+ DOCKER_HOSTNAMES: z.string().optional(),
+ DOCKER_PORTS: z.number().optional(),
},
/**
* Specify your client-side environment variables schema here.
@@ -49,6 +52,8 @@ export const env = createEnv({
DB_PORT: process.env.DB_PORT,
DB_DRIVER: process.env.DB_DRIVER,
NODE_ENV: process.env.NODE_ENV,
+ DOCKER_HOSTNAMES: process.env.DOCKER_HOSTNAMES,
+ DOCKER_PORTS: process.env.DOCKER_PORTS,
// NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR,
},
skipValidation:
diff --git a/apps/tasks/eslint.config.js b/apps/tasks/eslint.config.js
new file mode 100644
index 000000000..f44e21cd5
--- /dev/null
+++ b/apps/tasks/eslint.config.js
@@ -0,0 +1,9 @@
+import baseConfig from "@homarr/eslint-config/base";
+
+/** @type {import('typescript-eslint').Config} */
+export default [
+ {
+ ignores: ["tasks.cjs"],
+ },
+ ...baseConfig,
+];
diff --git a/apps/tasks/package.json b/apps/tasks/package.json
index da7d67760..44c6b2bd9 100644
--- a/apps/tasks/package.json
+++ b/apps/tasks/package.json
@@ -13,7 +13,7 @@
"dev": "pnpm with-env tsx ./src/main.ts",
"build": "esbuild src/main.ts --bundle --platform=node --outfile=tasks.cjs",
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit",
"with-env": "dotenv -e ../../.env --"
@@ -24,31 +24,28 @@
"@homarr/definitions": "workspace:^0.1.0",
"@homarr/icons": "workspace:^0.1.0",
"@homarr/log": "workspace:^",
+ "@homarr/ping": "workspace:^0.1.0",
"@homarr/redis": "workspace:^0.1.0",
"@homarr/server-settings": "workspace:^0.1.0",
+ "@homarr/integrations": "workspace:^0.1.0",
+ "@homarr/widgets": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@homarr/analytics": "workspace:^0.1.0",
+ "@homarr/cron-jobs-core": "workspace:^0.1.0",
"dotenv": "^16.4.5",
- "node-cron": "^3.0.3",
- "superjson": "2.2.1"
+ "superjson": "2.2.1",
+ "undici": "6.19.2"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
- "@types/node-cron": "^3.0.11",
- "@types/node": "^20.12.12",
+ "@types/node": "^20.14.9",
"dotenv-cli": "^7.4.2",
- "eslint": "^8.57.0",
- "prettier": "^3.2.5",
- "tsx": "4.11.0",
- "typescript": "^5.4.5"
- },
- "eslintConfig": {
- "root": true,
- "extends": [
- "@homarr/eslint-config/base"
- ]
+ "eslint": "^9.6.0",
+ "prettier": "^3.3.2",
+ "tsx": "4.13.3",
+ "typescript": "^5.5.2"
},
"prettier": "@homarr/prettier-config"
}
diff --git a/apps/tasks/src/jobs.ts b/apps/tasks/src/jobs.ts
index 181ce4590..88cbb3ac3 100644
--- a/apps/tasks/src/jobs.ts
+++ b/apps/tasks/src/jobs.ts
@@ -1,13 +1,17 @@
import { iconsUpdaterJob } from "~/jobs/icons-updater";
+import { smartHomeEntityStateJob } from "~/jobs/integrations/home-assistant";
import { analyticsJob } from "./jobs/analytics";
+import { pingJob } from "./jobs/ping";
import { queuesJob } from "./jobs/queue";
-import { createJobGroup } from "./lib/cron-job/group";
+import { createCronJobGroup } from "./lib/jobs";
-export const jobs = createJobGroup({
+export const jobs = createCronJobGroup({
// Add your jobs here:
+ analytics: analyticsJob,
+ iconsUpdater: iconsUpdaterJob,
+ ping: pingJob,
+ smartHomeEntityState: smartHomeEntityStateJob,
// This job is used to process queues.
queues: queuesJob,
- iconsUpdater: iconsUpdaterJob,
- analytics: analyticsJob,
});
diff --git a/apps/tasks/src/jobs/analytics.ts b/apps/tasks/src/jobs/analytics.ts
index bbdd89927..b20cb8a52 100644
--- a/apps/tasks/src/jobs/analytics.ts
+++ b/apps/tasks/src/jobs/analytics.ts
@@ -1,14 +1,14 @@
import SuperJSON from "superjson";
import { sendServerAnalyticsAsync } from "@homarr/analytics";
+import { EVERY_WEEK } from "@homarr/cron-jobs-core/expressions";
import { db, eq } from "@homarr/db";
import { serverSettings } from "@homarr/db/schema/sqlite";
-import { EVERY_WEEK } from "~/lib/cron-job/constants";
-import { createCronJob } from "~/lib/cron-job/creator";
+import { createCronJob } from "~/lib/jobs";
import type { defaultServerSettings } from "../../../../packages/server-settings";
-export const analyticsJob = createCronJob(EVERY_WEEK, {
+export const analyticsJob = createCronJob("analytics", EVERY_WEEK, {
runOnStart: true,
}).withCallback(async () => {
const analyticSetting = await db.query.serverSettings.findFirst({
diff --git a/apps/tasks/src/jobs/icons-updater.ts b/apps/tasks/src/jobs/icons-updater.ts
index 7fd274dd0..1bc109098 100644
--- a/apps/tasks/src/jobs/icons-updater.ts
+++ b/apps/tasks/src/jobs/icons-updater.ts
@@ -1,4 +1,5 @@
import { Stopwatch } from "@homarr/common";
+import { EVERY_WEEK } from "@homarr/cron-jobs-core/expressions";
import type { InferInsertModel } from "@homarr/db";
import { db, inArray } from "@homarr/db";
import { createId } from "@homarr/db/client";
@@ -6,10 +7,9 @@ import { iconRepositories, icons } from "@homarr/db/schema/sqlite";
import { fetchIconsAsync } from "@homarr/icons";
import { logger } from "@homarr/log";
-import { EVERY_WEEK } from "~/lib/cron-job/constants";
-import { createCronJob } from "~/lib/cron-job/creator";
+import { createCronJob } from "~/lib/jobs";
-export const iconsUpdaterJob = createCronJob(EVERY_WEEK, {
+export const iconsUpdaterJob = createCronJob("iconsUpdater", EVERY_WEEK, {
runOnStart: true,
}).withCallback(async () => {
logger.info("Updating icon repository cache...");
diff --git a/apps/tasks/src/jobs/integrations/home-assistant.ts b/apps/tasks/src/jobs/integrations/home-assistant.ts
new file mode 100644
index 000000000..d3ac3e040
--- /dev/null
+++ b/apps/tasks/src/jobs/integrations/home-assistant.ts
@@ -0,0 +1,64 @@
+import SuperJSON from "superjson";
+
+import { decryptSecret } from "@homarr/common";
+import { EVERY_MINUTE } from "@homarr/cron-jobs-core/expressions";
+import { db, eq } from "@homarr/db";
+import { items } from "@homarr/db/schema/sqlite";
+import { HomeAssistantIntegration } from "@homarr/integrations";
+import { logger } from "@homarr/log";
+import { homeAssistantEntityState } from "@homarr/redis";
+import type { WidgetComponentProps } from "@homarr/widgets";
+
+import { createCronJob } from "~/lib/jobs";
+
+export const smartHomeEntityStateJob = createCronJob("smartHomeEntityState", EVERY_MINUTE).withCallback(async () => {
+ const itemsForIntegration = await db.query.items.findMany({
+ where: eq(items.kind, "smartHome-entityState"),
+ with: {
+ integrations: {
+ with: {
+ integration: {
+ with: {
+ secrets: {
+ columns: {
+ kind: true,
+ value: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ });
+
+ for (const itemForIntegration of itemsForIntegration) {
+ const integration = itemForIntegration.integrations[0]?.integration;
+ if (!integration) {
+ continue;
+ }
+
+ const options = SuperJSON.parse["options"]>(
+ itemForIntegration.options,
+ );
+
+ const homeAssistant = new HomeAssistantIntegration({
+ ...integration,
+ decryptedSecrets: integration.secrets.map((secret) => ({
+ ...secret,
+ value: decryptSecret(secret.value),
+ })),
+ });
+ const state = await homeAssistant.getEntityStateAsync(options.entityId);
+
+ if (!state.success) {
+ logger.error("Unable to fetch data from Home Assistant");
+ continue;
+ }
+
+ await homeAssistantEntityState.publishAsync({
+ entityId: options.entityId,
+ state: state.data.state,
+ });
+ }
+});
diff --git a/apps/tasks/src/jobs/ping.ts b/apps/tasks/src/jobs/ping.ts
new file mode 100644
index 000000000..90b7b96c0
--- /dev/null
+++ b/apps/tasks/src/jobs/ping.ts
@@ -0,0 +1,25 @@
+import { EVERY_MINUTE } from "@homarr/cron-jobs-core/expressions";
+import { logger } from "@homarr/log";
+import { sendPingRequestAsync } from "@homarr/ping";
+import { pingChannel, pingUrlChannel } from "@homarr/redis";
+
+import { createCronJob } from "~/lib/jobs";
+
+export const pingJob = createCronJob("ping", EVERY_MINUTE).withCallback(async () => {
+ const urls = await pingUrlChannel.getAllAsync();
+
+ for (const url of new Set(urls)) {
+ const pingResult = await sendPingRequestAsync(url);
+
+ if ("statusCode" in pingResult) {
+ logger.debug(`executed ping for url ${url} with status code ${pingResult.statusCode}`);
+ } else {
+ logger.error(`Executing ping for url ${url} failed with error: ${pingResult.error}`);
+ }
+
+ await pingChannel.publishAsync({
+ url,
+ ...pingResult,
+ });
+ }
+});
diff --git a/apps/tasks/src/jobs/queue.ts b/apps/tasks/src/jobs/queue.ts
index 8eeb6f4e5..a0fc50bb0 100644
--- a/apps/tasks/src/jobs/queue.ts
+++ b/apps/tasks/src/jobs/queue.ts
@@ -1,8 +1,9 @@
-import { EVERY_MINUTE } from "../lib/cron-job/constants";
-import { createCronJob } from "../lib/cron-job/creator";
+import { EVERY_MINUTE } from "@homarr/cron-jobs-core/expressions";
+
+import { createCronJob } from "~/lib/jobs";
import { queueWorkerAsync } from "../lib/queue/worker";
// This job processes queues, it runs every minute.
-export const queuesJob = createCronJob(EVERY_MINUTE).withCallback(async () => {
+export const queuesJob = createCronJob("queues", EVERY_MINUTE).withCallback(async () => {
await queueWorkerAsync();
});
diff --git a/apps/tasks/src/lib/cron-job/constants.ts b/apps/tasks/src/lib/cron-job/constants.ts
deleted file mode 100644
index 6f40f29cc..000000000
--- a/apps/tasks/src/lib/cron-job/constants.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export const EVERY_5_SECONDS = "*/5 * * * * *";
-export const EVERY_MINUTE = "* * * * *";
-export const EVERY_5_MINUTES = "*/5 * * * *";
-export const EVERY_10_MINUTES = "*/10 * * * *";
-export const EVERY_HOUR = "0 * * * *";
-export const EVERY_DAY = "0 0 * * */1";
-export const EVERY_WEEK = "0 0 * * 1";
diff --git a/apps/tasks/src/lib/cron-job/creator.ts b/apps/tasks/src/lib/cron-job/creator.ts
deleted file mode 100644
index 1dcb59fcc..000000000
--- a/apps/tasks/src/lib/cron-job/creator.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import cron from "node-cron";
-
-import type { MaybePromise } from "@homarr/common/types";
-
-interface CreateCronJobOptions {
- runOnStart?: boolean;
-}
-
-export const createCronJob = (cronExpression: string, options: CreateCronJobOptions = { runOnStart: false }) => {
- return {
- withCallback: (callback: () => MaybePromise) => {
- if (options.runOnStart) {
- void callback();
- }
-
- const task = cron.schedule(cronExpression, () => void callback(), {
- scheduled: false,
- });
- return {
- _expression: cronExpression,
- _task: task,
- };
- },
- };
-};
diff --git a/apps/tasks/src/lib/cron-job/group.ts b/apps/tasks/src/lib/cron-job/group.ts
deleted file mode 100644
index 0854259df..000000000
--- a/apps/tasks/src/lib/cron-job/group.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { objectEntries } from "@homarr/common";
-
-import type { createCronJob } from "./creator";
-import { jobRegistry } from "./registry";
-
-type Jobs = Record["withCallback"]>>;
-
-export const createJobGroup = (jobs: TJobs) => {
- for (const [name, job] of objectEntries(jobs)) {
- if (typeof name !== "string") continue;
- jobRegistry.set(name, {
- name,
- expression: job._expression,
- active: false,
- task: job._task,
- });
- }
-
- return {
- start: (name: keyof TJobs) => {
- const job = jobRegistry.get(name as string);
- if (!job) return;
- job.active = true;
- job.task.start();
- },
- startAll: () => {
- for (const job of jobRegistry.values()) {
- job.active = true;
- job.task.start();
- }
- },
- stop: (name: keyof TJobs) => {
- const job = jobRegistry.get(name as string);
- if (!job) return;
- job.active = false;
- job.task.stop();
- },
- stopAll: () => {
- for (const job of jobRegistry.values()) {
- job.active = false;
- job.task.stop();
- }
- },
- };
-};
diff --git a/apps/tasks/src/lib/cron-job/registry.ts b/apps/tasks/src/lib/cron-job/registry.ts
deleted file mode 100644
index d78b4bc5a..000000000
--- a/apps/tasks/src/lib/cron-job/registry.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import type cron from "node-cron";
-
-interface Job {
- name: string;
- expression: string;
- active: boolean;
- task: cron.ScheduledTask;
-}
-export const jobRegistry = new Map();
diff --git a/apps/tasks/src/lib/jobs.ts b/apps/tasks/src/lib/jobs.ts
new file mode 100644
index 000000000..7cebfc85e
--- /dev/null
+++ b/apps/tasks/src/lib/jobs.ts
@@ -0,0 +1,21 @@
+import { createCronJobFunctions } from "@homarr/cron-jobs-core";
+import type { Logger } from "@homarr/cron-jobs-core/logger";
+import { logger } from "@homarr/log";
+
+class WinstonCronJobLogger implements Logger {
+ logDebug(message: string) {
+ logger.debug(message);
+ }
+
+ logInfo(message: string) {
+ logger.info(message);
+ }
+
+ logError(error: unknown) {
+ logger.error(error);
+ }
+}
+
+export const { createCronJob, createCronJobGroup } = createCronJobFunctions({
+ logger: new WinstonCronJobLogger(),
+});
diff --git a/apps/tasks/src/lib/queue/client.ts b/apps/tasks/src/lib/queue/client.ts
index b67cb968d..379fcdbcf 100644
--- a/apps/tasks/src/lib/queue/client.ts
+++ b/apps/tasks/src/lib/queue/client.ts
@@ -41,15 +41,14 @@ export const createQueueClient = (queues: TQueues) => {
};
return acc;
},
- {} as Record<
- keyof TQueues,
- (
- data: z.infer,
+ {} as {
+ [key in keyof TQueues]: (
+ data: z.infer,
props: {
executionDate?: Date;
} | void,
- ) => Promise
- >,
+ ) => Promise;
+ },
),
};
};
diff --git a/apps/tasks/src/lib/queue/creator.ts b/apps/tasks/src/lib/queue/creator.ts
index 47dd05226..438276f63 100644
--- a/apps/tasks/src/lib/queue/creator.ts
+++ b/apps/tasks/src/lib/queue/creator.ts
@@ -1,6 +1,5 @@
-import type { z } from "zod";
-
import type { MaybePromise } from "@homarr/common/types";
+import type { z } from "@homarr/validation";
export const createQueue = (input: TInput) => {
return {
diff --git a/apps/tasks/src/lib/queue/worker.ts b/apps/tasks/src/lib/queue/worker.ts
index 40d9dde9b..0e7577794 100644
--- a/apps/tasks/src/lib/queue/worker.ts
+++ b/apps/tasks/src/lib/queue/worker.ts
@@ -1,3 +1,4 @@
+import { logger } from "@homarr/log";
import { queueChannel } from "@homarr/redis";
import { queueRegistry } from "~/queues";
@@ -14,7 +15,18 @@ export const queueWorkerAsync = async () => {
for (const execution of executions) {
const queue = queueRegistry.get(execution.name);
if (!queue) continue;
- await queue.callback(execution.data);
+
+ try {
+ await queue.callback(execution.data);
+ } catch (err) {
+ logger.error(
+ `apps/tasks/src/lib/queue/worker.ts: Error occured when executing queue ${execution.name} with data`,
+ execution.data,
+ "and error:",
+ err,
+ );
+ }
+
await queueChannel.markAsDoneAsync(execution._id);
}
};
diff --git a/apps/tasks/src/main.ts b/apps/tasks/src/main.ts
index 16327cd16..c876da2da 100644
--- a/apps/tasks/src/main.ts
+++ b/apps/tasks/src/main.ts
@@ -1,6 +1,10 @@
+// This import has to be the first import in the file so that the agent is overridden before any other modules are imported.
+import "./undici-log-agent-override";
+
import { jobs } from "./jobs";
import { seedServerSettingsAsync } from "./seed-server-settings";
-jobs.startAll();
-
-void seedServerSettingsAsync();
+void (async () => {
+ await jobs.startAllAsync();
+ await seedServerSettingsAsync();
+})();
diff --git a/apps/tasks/src/undici-log-agent-override.ts b/apps/tasks/src/undici-log-agent-override.ts
new file mode 100644
index 000000000..8dfe9e8a3
--- /dev/null
+++ b/apps/tasks/src/undici-log-agent-override.ts
@@ -0,0 +1,20 @@
+import type { Dispatcher } from "undici";
+import { Agent, setGlobalDispatcher } from "undici";
+
+import { logger } from "@homarr/log";
+
+class LoggingAgent extends Agent {
+ constructor(...props: ConstructorParameters) {
+ super(...props);
+ }
+
+ dispatch(options: Dispatcher.DispatchOptions, handler: Dispatcher.DispatchHandlers): boolean {
+ logger.info(
+ `Dispatching request ${options.method} ${options.origin as string}${options.path} (${Object.keys(options.headers as object).length} headers)`,
+ );
+ return super.dispatch(options, handler);
+ }
+}
+
+const agent = new LoggingAgent();
+setGlobalDispatcher(agent);
diff --git a/apps/websocket/eslint.config.js b/apps/websocket/eslint.config.js
new file mode 100644
index 000000000..984f78162
--- /dev/null
+++ b/apps/websocket/eslint.config.js
@@ -0,0 +1,9 @@
+import baseConfig from "@homarr/eslint-config/base";
+
+/** @type {import('typescript-eslint').Config} */
+export default [
+ {
+ ignores: ["wssServer.cjs"],
+ },
+ ...baseConfig,
+];
diff --git a/apps/websocket/package.json b/apps/websocket/package.json
index e9d856950..4c7b66b15 100644
--- a/apps/websocket/package.json
+++ b/apps/websocket/package.json
@@ -8,9 +8,9 @@
"type": "module",
"scripts": {
"dev": "pnpm with-env tsx ./src/main.ts",
- "build": "esbuild src/main.ts --bundle --platform=node --outfile=wssServer.cjs --external:bcrypt --loader:.html=text",
+ "build": "esbuild src/main.ts --bundle --platform=node --outfile=wssServer.cjs --external:bcrypt --loader:.html=text --loader:.node=text",
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit",
"with-env": "dotenv -e ../../.env --"
@@ -24,7 +24,8 @@
"@homarr/log": "workspace:^",
"@homarr/redis": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
- "ws": "^8.17.0",
+ "tsx": "4.13.3",
+ "ws": "^8.17.1",
"dotenv": "^16.4.5"
},
"devDependencies": {
@@ -32,15 +33,9 @@
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/ws": "^8.5.10",
- "eslint": "^8.57.0",
- "prettier": "^3.2.5",
- "typescript": "^5.4.5"
- },
- "eslintConfig": {
- "root": true,
- "extends": [
- "@homarr/eslint-config/base"
- ]
+ "eslint": "^9.6.0",
+ "prettier": "^3.3.2",
+ "typescript": "^5.5.2"
},
"prettier": "@homarr/prettier-config"
}
diff --git a/package.json b/package.json
index 5ffe3cff8..924f04eb3 100644
--- a/package.json
+++ b/package.json
@@ -2,9 +2,9 @@
"name": "homarr",
"private": true,
"engines": {
- "node": ">=20.14.0"
+ "node": ">=20.15.0"
},
- "packageManager": "pnpm@9.1.3",
+ "packageManager": "pnpm@9.4.0",
"scripts": {
"build": "turbo build",
"clean": "git clean -xdf node_modules",
@@ -28,23 +28,18 @@
},
"devDependencies": {
"@homarr/prettier-config": "workspace:^0.1.0",
- "@turbo/gen": "^1.13.3",
- "@vitejs/plugin-react": "^4.3.0",
+ "@turbo/gen": "^2.0.6",
+ "@vitejs/plugin-react": "^4.3.1",
"@vitest/coverage-v8": "^1.6.0",
"@vitest/ui": "^1.6.0",
"cross-env": "^7.0.3",
"jsdom": "^24.1.0",
- "prettier": "^3.2.5",
- "turbo": "^1.13.3",
- "typescript": "^5.4.5",
+ "prettier": "^3.3.2",
+ "testcontainers": "^10.10.0",
+ "turbo": "^2.0.6",
+ "typescript": "^5.5.2",
"vite-tsconfig-paths": "^4.3.2",
"vitest": "^1.6.0"
},
- "dependencies": {
- "@mantine/core": "^7.10.0",
- "@mantine/dates": "^7.10.0",
- "@tabler/icons-react": "^3.5.0",
- "mantine-react-table": "2.0.0-beta.3"
- },
"prettier": "@homarr/prettier-config"
}
\ No newline at end of file
diff --git a/packages/analytics/eslint.config.js b/packages/analytics/eslint.config.js
new file mode 100644
index 000000000..f7a5a7d36
--- /dev/null
+++ b/packages/analytics/eslint.config.js
@@ -0,0 +1,4 @@
+import baseConfig from "@homarr/eslint-config/base";
+
+/** @type {import('typescript-eslint').Config} */
+export default [...baseConfig];
diff --git a/packages/analytics/package.json b/packages/analytics/package.json
index 7d2c49d17..9cd3dd781 100644
--- a/packages/analytics/package.json
+++ b/packages/analytics/package.json
@@ -16,28 +16,23 @@
"license": "MIT",
"scripts": {
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit"
},
- "devDependencies": {
+ "dependencies": {
+ "@umami/node": "^0.3.0",
+ "superjson": "2.2.1",
"@homarr/db": "workspace:^0.1.0",
- "@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/log": "workspace:^0.1.0",
+ "@homarr/server-settings": "workspace:^0.1.0"
+ },
+ "devDependencies": {
+ "@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
- "@homarr/server-settings": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
- "eslint": "^8.57.0",
- "typescript": "^5.4.5"
- },
- "eslintConfig": {
- "extends": [
- "@homarr/eslint-config/base"
- ]
+ "eslint": "^9.6.0",
+ "typescript": "^5.5.2"
},
- "prettier": "@homarr/prettier-config",
- "dependencies": {
- "@umami/node": "^0.3.0",
- "superjson": "2.2.1"
- }
+ "prettier": "@homarr/prettier-config"
}
diff --git a/packages/api/eslint.config.js b/packages/api/eslint.config.js
new file mode 100644
index 000000000..f7a5a7d36
--- /dev/null
+++ b/packages/api/eslint.config.js
@@ -0,0 +1,4 @@
+import baseConfig from "@homarr/eslint-config/base";
+
+/** @type {import('typescript-eslint').Config} */
+export default [...baseConfig];
diff --git a/packages/api/package.json b/packages/api/package.json
index 3fad704f3..3f4aadf2f 100644
--- a/packages/api/package.json
+++ b/packages/api/package.json
@@ -14,7 +14,7 @@
"type": "module",
"scripts": {
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit"
},
@@ -25,27 +25,26 @@
"@homarr/definitions": "workspace:^0.1.0",
"@homarr/integrations": "workspace:^0.1.0",
"@homarr/log": "workspace:^",
+ "@homarr/ping": "workspace:^0.1.0",
"@homarr/redis": "workspace:^0.1.0",
- "@homarr/tasks": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
"@homarr/server-settings": "workspace:^0.1.0",
+ "@trpc/react-query": "next",
"@trpc/client": "next",
"@trpc/server": "next",
- "superjson": "2.2.1"
+ "dockerode": "^4.0.2",
+ "superjson": "2.2.1",
+ "next": "^14.2.4",
+ "react": "^18.3.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
- "eslint": "^8.57.0",
- "prettier": "^3.2.5",
- "typescript": "^5.4.5"
- },
- "eslintConfig": {
- "root": true,
- "extends": [
- "@homarr/eslint-config/base"
- ]
+ "@types/dockerode": "^3.3.29",
+ "eslint": "^9.6.0",
+ "prettier": "^3.3.2",
+ "typescript": "^5.5.2"
},
"prettier": "@homarr/prettier-config"
}
diff --git a/packages/api/src/middlewares/integration.ts b/packages/api/src/middlewares/integration.ts
index 8f23903fc..9895f50ad 100644
--- a/packages/api/src/middlewares/integration.ts
+++ b/packages/api/src/middlewares/integration.ts
@@ -1,11 +1,11 @@
import { TRPCError } from "@trpc/server";
+import { decryptSecret } from "@homarr/common";
import { and, eq, inArray } from "@homarr/db";
import { integrations } from "@homarr/db/schema/sqlite";
import type { IntegrationKind } from "@homarr/definitions";
import { z } from "@homarr/validation";
-import { decryptSecret } from "../router/integration";
import { publicProcedure } from "../trpc";
export const createOneIntegrationMiddleware = (...kinds: TKind[]) => {
diff --git a/packages/api/src/root.ts b/packages/api/src/root.ts
index 1395b871e..ce71e53b8 100644
--- a/packages/api/src/root.ts
+++ b/packages/api/src/root.ts
@@ -1,9 +1,10 @@
import { appRouter as innerAppRouter } from "./router/app";
import { boardRouter } from "./router/board";
+import { dockerRouter } from "./router/docker/docker-router";
import { groupRouter } from "./router/group";
import { homeRouter } from "./router/home";
import { iconsRouter } from "./router/icons";
-import { integrationRouter } from "./router/integration";
+import { integrationRouter } from "./router/integration/integration-router";
import { inviteRouter } from "./router/invite";
import { locationRouter } from "./router/location";
import { logRouter } from "./router/log";
@@ -24,6 +25,7 @@ export const appRouter = createTRPCRouter({
log: logRouter,
icon: iconsRouter,
home: homeRouter,
+ docker: dockerRouter,
serverSettings: serverSettingsRouter,
});
diff --git a/packages/api/src/router/docker/docker-router.ts b/packages/api/src/router/docker/docker-router.ts
new file mode 100644
index 000000000..fd19f14ec
--- /dev/null
+++ b/packages/api/src/router/docker/docker-router.ts
@@ -0,0 +1,84 @@
+import type Docker from "dockerode";
+
+import { db, like, or } from "@homarr/db";
+import { icons } from "@homarr/db/schema/sqlite";
+import type { DockerContainerState } from "@homarr/definitions";
+import { createCacheChannel } from "@homarr/redis";
+
+import { createTRPCRouter, publicProcedure } from "../../trpc";
+import { DockerSingleton } from "./docker-singleton";
+
+const dockerCache = createCacheChannel<{
+ containers: (Docker.ContainerInfo & { instance: string; iconUrl: string | null })[];
+}>("docker-containers", 5 * 60 * 1000);
+
+export const dockerRouter = createTRPCRouter({
+ getContainers: publicProcedure.query(async () => {
+ const { timestamp, data } = await dockerCache.consumeAsync(async () => {
+ const dockerInstances = DockerSingleton.getInstance();
+ const containers = await Promise.all(
+ // Return all the containers of all the instances into only one item
+ dockerInstances.map(({ instance, host: key }) =>
+ instance.listContainers({ all: true }).then((containers) =>
+ containers.map((container) => ({
+ ...container,
+ instance: key,
+ })),
+ ),
+ ),
+ ).then((containers) => containers.flat());
+
+ const extractImage = (container: Docker.ContainerInfo) =>
+ container.Image.split("/").at(-1)?.split(":").at(0) ?? "";
+ const likeQueries = containers.map((container) => like(icons.name, `%${extractImage(container)}%`));
+ const dbIcons =
+ likeQueries.length >= 1
+ ? await db.query.icons.findMany({
+ where: or(...likeQueries),
+ })
+ : [];
+
+ return {
+ containers: containers.map((container) => ({
+ ...container,
+ iconUrl:
+ dbIcons.find((icon) => {
+ const extractedImage = extractImage(container);
+ if (!extractedImage) return false;
+ return icon.name.toLowerCase().includes(extractedImage.toLowerCase());
+ })?.url ?? null,
+ })),
+ };
+ });
+
+ return {
+ containers: sanitizeContainers(data.containers),
+ timestamp,
+ };
+ }),
+});
+
+interface DockerContainer {
+ name: string;
+ id: string;
+ state: DockerContainerState;
+ image: string;
+ ports: Docker.Port[];
+ iconUrl: string | null;
+}
+
+function sanitizeContainers(
+ containers: (Docker.ContainerInfo & { instance: string; iconUrl: string | null })[],
+): DockerContainer[] {
+ return containers.map((container) => {
+ return {
+ name: container.Names[0]?.split("/")[1] ?? "Unknown",
+ id: container.Id,
+ instance: container.instance,
+ state: container.State as DockerContainerState,
+ image: container.Image,
+ ports: container.Ports,
+ iconUrl: container.iconUrl,
+ };
+ });
+}
diff --git a/packages/api/src/router/docker/docker-singleton.ts b/packages/api/src/router/docker/docker-singleton.ts
new file mode 100644
index 000000000..f95a58418
--- /dev/null
+++ b/packages/api/src/router/docker/docker-singleton.ts
@@ -0,0 +1,51 @@
+import Docker from "dockerode";
+
+interface DockerInstance {
+ host: string;
+ instance: Docker;
+}
+
+export class DockerSingleton {
+ private static instances: DockerInstance[];
+
+ private createInstances() {
+ const instances: DockerInstance[] = [];
+ const hostVariable = process.env.DOCKER_HOST;
+ const portVariable = process.env.DOCKER_PORT;
+ if (hostVariable === undefined || portVariable === undefined) {
+ instances.push({ host: "socket", instance: new Docker() });
+ return instances;
+ }
+ const hosts = hostVariable.split(",");
+ const ports = portVariable.split(",");
+
+ if (hosts.length !== ports.length) {
+ throw new Error("The number of hosts and ports must match");
+ }
+
+ hosts.forEach((host, i) => {
+ instances.push({
+ host: `${host}:${ports[i]}`,
+ instance: new Docker({
+ host,
+ port: parseInt(ports[i] ?? "", 10),
+ }),
+ });
+ return instances;
+ });
+ return instances;
+ }
+
+ public static findInstance(key: string): DockerInstance | undefined {
+ return this.instances.find((instance) => instance.host === key);
+ }
+
+ public static getInstance(): DockerInstance[] {
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+ if (!DockerSingleton.instances) {
+ DockerSingleton.instances = new DockerSingleton().createInstances();
+ }
+
+ return this.instances;
+ }
+}
diff --git a/packages/api/src/router/group.ts b/packages/api/src/router/group.ts
index 17a7749d1..3d0cfe168 100644
--- a/packages/api/src/router/group.ts
+++ b/packages/api/src/router/group.ts
@@ -42,7 +42,7 @@ export const groupRouter = createTRPCRouter({
...group,
members: group.members.map((member) => member.user),
})),
- totalCount: groupCount[0]!.count,
+ totalCount: groupCount[0]?.count ?? 0,
};
}),
getById: protectedProcedure.input(validation.group.byId).query(async ({ input, ctx }) => {
diff --git a/packages/api/src/router/icons.ts b/packages/api/src/router/icons.ts
index e0c3c9a7c..e99918da3 100644
--- a/packages/api/src/router/icons.ts
+++ b/packages/api/src/router/icons.ts
@@ -15,7 +15,7 @@ export const iconsRouter = createTRPCRouter({
name: true,
url: true,
},
- where: input.searchText?.length ?? 0 > 0 ? like(icons.name, `%${input.searchText}%`) : undefined,
+ where: (input.searchText?.length ?? 0) > 0 ? like(icons.name, `%${input.searchText}%`) : undefined,
limit: 5,
},
},
diff --git a/packages/api/src/router/integration.ts b/packages/api/src/router/integration/integration-router.ts
similarity index 52%
rename from packages/api/src/router/integration.ts
rename to packages/api/src/router/integration/integration-router.ts
index 92fb2fcb9..611c288d1 100644
--- a/packages/api/src/router/integration.ts
+++ b/packages/api/src/router/integration/integration-router.ts
@@ -1,14 +1,15 @@
-import crypto from "crypto";
import { TRPCError } from "@trpc/server";
+import { decryptSecret, encryptSecret } from "@homarr/common";
import type { Database } from "@homarr/db";
import { and, createId, eq } from "@homarr/db";
import { integrations, integrationSecrets } from "@homarr/db/schema/sqlite";
import type { IntegrationSecretKind } from "@homarr/definitions";
-import { getAllSecretKindOptions, integrationKinds, integrationSecretKindObject } from "@homarr/definitions";
+import { integrationKinds, integrationSecretKindObject } from "@homarr/definitions";
import { validation } from "@homarr/validation";
-import { createTRPCRouter, publicProcedure } from "../trpc";
+import { createTRPCRouter, publicProcedure } from "../../trpc";
+import { testConnectionAsync } from "./integration-test-connection";
export const integrationRouter = createTRPCRouter({
all: publicProcedure.query(async ({ ctx }) => {
@@ -60,6 +61,14 @@ export const integrationRouter = createTRPCRouter({
};
}),
create: publicProcedure.input(validation.integration.create).mutation(async ({ ctx, input }) => {
+ await testConnectionAsync({
+ id: "new",
+ name: input.name,
+ url: input.url,
+ kind: input.kind,
+ secrets: input.secrets,
+ });
+
const integrationId = createId();
await ctx.db.insert(integrations).values({
id: integrationId,
@@ -68,13 +77,14 @@ export const integrationRouter = createTRPCRouter({
kind: input.kind,
});
- for (const secret of input.secrets) {
- await ctx.db.insert(integrationSecrets).values({
- kind: secret.kind,
- value: encryptSecret(secret.value),
- updatedAt: new Date(),
- integrationId,
- });
+ if (input.secrets.length >= 1) {
+ await ctx.db.insert(integrationSecrets).values(
+ input.secrets.map((secret) => ({
+ kind: secret.kind,
+ value: encryptSecret(secret.value),
+ integrationId,
+ })),
+ );
}
}),
update: publicProcedure.input(validation.integration.update).mutation(async ({ ctx, input }) => {
@@ -92,6 +102,17 @@ export const integrationRouter = createTRPCRouter({
});
}
+ await testConnectionAsync(
+ {
+ id: input.id,
+ name: input.name,
+ url: input.url,
+ kind: integration.kind,
+ secrets: input.secrets,
+ },
+ integration.secrets,
+ );
+
await ctx.db
.update(integrations)
.set({
@@ -100,15 +121,14 @@ export const integrationRouter = createTRPCRouter({
})
.where(eq(integrations.id, input.id));
- const decryptedSecrets = integration.secrets.map((secret) => ({
- ...secret,
- value: decryptSecret(secret.value),
- }));
-
const changedSecrets = input.secrets.filter(
(secret): secret is { kind: IntegrationSecretKind; value: string } =>
secret.value !== null && // only update secrets that have a value
- !decryptedSecrets.find((dSecret) => dSecret.kind === secret.kind && dSecret.value === secret.value),
+ !integration.secrets.find(
+ // Checked above
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ (dbSecret) => dbSecret.kind === secret.kind && dbSecret.value === encryptSecret(secret.value!),
+ ),
);
if (changedSecrets.length > 0) {
@@ -118,7 +138,7 @@ export const integrationRouter = createTRPCRouter({
value: changedSecret.value,
kind: changedSecret.kind,
};
- if (!decryptedSecrets.some((secret) => secret.kind === changedSecret.kind)) {
+ if (!integration.secrets.some((secret) => secret.kind === changedSecret.kind)) {
await addSecretAsync(ctx.db, secretInput);
} else {
await updateSecretAsync(ctx.db, secretInput);
@@ -140,94 +160,8 @@ export const integrationRouter = createTRPCRouter({
await ctx.db.delete(integrations).where(eq(integrations.id, input.id));
}),
- testConnection: publicProcedure.input(validation.integration.testConnection).mutation(async ({ ctx, input }) => {
- const secrets = input.secrets.filter((secret): secret is { kind: IntegrationSecretKind; value: string } =>
- Boolean(secret.value),
- );
-
- // Find any matching secret kinds
- let secretKinds = getAllSecretKindOptions(input.kind).find((secretKinds) =>
- secretKinds.every((secretKind) => secrets.some((secret) => secret.kind === secretKind)),
- );
-
- if (!secretKinds && input.id === null) {
- throw new TRPCError({
- code: "BAD_REQUEST",
- message: "SECRETS_NOT_DEFINED",
- });
- }
-
- if (!secretKinds && input.id !== null) {
- const integration = await ctx.db.query.integrations.findFirst({
- where: eq(integrations.id, input.id),
- with: {
- secrets: true,
- },
- });
- if (!integration) {
- throw new TRPCError({
- code: "BAD_REQUEST",
- message: "SECRETS_NOT_DEFINED",
- });
- }
- const decryptedSecrets = integration.secrets.map((secret) => ({
- ...secret,
- value: decryptSecret(secret.value),
- }));
-
- // Add secrets that are not defined in the input from the database
- for (const dbSecret of decryptedSecrets) {
- if (!secrets.find((secret) => secret.kind === dbSecret.kind)) {
- secrets.push({
- kind: dbSecret.kind,
- value: dbSecret.value,
- });
- }
- }
-
- secretKinds = getAllSecretKindOptions(input.kind).find((secretKinds) =>
- secretKinds.every((secretKind) => secrets.some((secret) => secret.kind === secretKind)),
- );
-
- if (!secretKinds) {
- throw new TRPCError({
- code: "BAD_REQUEST",
- message: "SECRETS_NOT_DEFINED",
- });
- }
- }
-
- // TODO: actually test the connection
- // Probably by calling a function on the integration class
- // getIntegration(input.kind).testConnection(secrets)
- // getIntegration(kind: IntegrationKind): Integration
- // interface Integration {
- // testConnection(): Promise;
- // }
- }),
});
-const algorithm = "aes-256-cbc"; //Using AES encryption
-const key = Buffer.from("1d71cceced68159ba59a277d056a66173613052cbeeccbfbd15ab1c909455a4d", "hex"); // TODO: generate with const data = crypto.randomBytes(32).toString('hex')
-
-export function encryptSecret(text: string): `${string}.${string}` {
- const initializationVector = crypto.randomBytes(16);
- const cipher = crypto.createCipheriv(algorithm, Buffer.from(key), initializationVector);
- let encrypted = cipher.update(text);
- encrypted = Buffer.concat([encrypted, cipher.final()]);
- return `${encrypted.toString("hex")}.${initializationVector.toString("hex")}`;
-}
-
-export function decryptSecret(value: `${string}.${string}`) {
- const [data, dataIv] = value.split(".") as [string, string];
- const initializationVector = Buffer.from(dataIv, "hex");
- const encryptedText = Buffer.from(data, "hex");
- const decipher = crypto.createDecipheriv(algorithm, Buffer.from(key), initializationVector);
- let decrypted = decipher.update(encryptedText);
- decrypted = Buffer.concat([decrypted, decipher.final()]);
- return decrypted.toString();
-}
-
interface UpdateSecretInput {
integrationId: string;
value: string;
@@ -238,7 +172,6 @@ const updateSecretAsync = async (db: Database, input: UpdateSecretInput) => {
.update(integrationSecrets)
.set({
value: encryptSecret(input.value),
- updatedAt: new Date(),
})
.where(and(eq(integrationSecrets.integrationId, input.integrationId), eq(integrationSecrets.kind, input.kind)));
};
@@ -252,7 +185,6 @@ const addSecretAsync = async (db: Database, input: AddSecretInput) => {
await db.insert(integrationSecrets).values({
kind: input.kind,
value: encryptSecret(input.value),
- updatedAt: new Date(),
integrationId: input.integrationId,
});
};
diff --git a/packages/api/src/router/integration/integration-test-connection.ts b/packages/api/src/router/integration/integration-test-connection.ts
new file mode 100644
index 000000000..ba8ffa2dd
--- /dev/null
+++ b/packages/api/src/router/integration/integration-test-connection.ts
@@ -0,0 +1,95 @@
+import { decryptSecret } from "@homarr/common";
+import type { Integration } from "@homarr/db/schema/sqlite";
+import type { IntegrationKind, IntegrationSecretKind } from "@homarr/definitions";
+import { getAllSecretKindOptions } from "@homarr/definitions";
+import { integrationCreatorByKind, IntegrationTestConnectionError } from "@homarr/integrations";
+
+type FormIntegration = Integration & {
+ secrets: {
+ kind: IntegrationSecretKind;
+ value: string | null;
+ }[];
+};
+
+export const testConnectionAsync = async (
+ integration: FormIntegration,
+ dbSecrets: {
+ kind: IntegrationSecretKind;
+ value: `${string}.${string}`;
+ }[] = [],
+) => {
+ const formSecrets = integration.secrets
+ .filter((secret) => secret.value !== null)
+ .map((secret) => ({
+ ...secret,
+ // We ensured above that the value is not null
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ value: secret.value!,
+ source: "form" as const,
+ }));
+
+ const decryptedDbSecrets = dbSecrets.map((secret) => ({
+ ...secret,
+ value: decryptSecret(secret.value),
+ source: "db" as const,
+ }));
+
+ const sourcedSecrets = [...formSecrets, ...decryptedDbSecrets];
+ const secretKinds = getSecretKindOption(integration.kind, sourcedSecrets);
+
+ const filteredSecrets = secretKinds.map((kind) => {
+ const secrets = sourcedSecrets.filter((secret) => secret.kind === kind);
+ // Will never be undefined because of the check before
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ if (secrets.length === 1) return secrets[0]!;
+
+ // There will always be a matching secret because of the getSecretKindOption function
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ return secrets.find((secret) => secret.source === "form") ?? secrets[0]!;
+ });
+
+ const integrationInstance = integrationCreatorByKind(integration.kind, {
+ id: integration.id,
+ name: integration.name,
+ url: integration.url,
+ decryptedSecrets: filteredSecrets,
+ });
+
+ await integrationInstance.testConnectionAsync();
+};
+
+interface SourcedIntegrationSecret {
+ kind: IntegrationSecretKind;
+ value: string;
+ source: "db" | "form";
+}
+
+const getSecretKindOption = (kind: IntegrationKind, sourcedSecrets: SourcedIntegrationSecret[]) => {
+ const matchingSecretKindOptions = getAllSecretKindOptions(kind).filter((secretKinds) =>
+ secretKinds.every((kind) => sourcedSecrets.some((secret) => secret.kind === kind)),
+ );
+
+ if (matchingSecretKindOptions.length === 0) {
+ throw new IntegrationTestConnectionError("secretNotDefined");
+ }
+
+ if (matchingSecretKindOptions.length === 1) {
+ // Will never be undefined because of the check above
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ return matchingSecretKindOptions[0]!;
+ }
+
+ const onlyFormSecretsKindOptions = matchingSecretKindOptions.filter((secretKinds) =>
+ sourcedSecrets.filter((secret) => secretKinds.includes(secret.kind)).every((secret) => secret.source === "form"),
+ );
+
+ if (onlyFormSecretsKindOptions.length >= 1) {
+ // Will never be undefined because of the check above
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ return onlyFormSecretsKindOptions[0]!;
+ }
+
+ // Will never be undefined because of the check above
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ return matchingSecretKindOptions[0]!;
+};
diff --git a/packages/api/src/router/test/app.spec.ts b/packages/api/src/router/test/app.spec.ts
index db9550d0c..1ab6ee559 100644
--- a/packages/api/src/router/test/app.spec.ts
+++ b/packages/api/src/router/test/app.spec.ts
@@ -1,3 +1,4 @@
+/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { describe, expect, test, vi } from "vitest";
import type { Session } from "@homarr/auth";
diff --git a/packages/api/src/router/test/group.spec.ts b/packages/api/src/router/test/group.spec.ts
index 2901ec32b..c91483e72 100644
--- a/packages/api/src/router/test/group.spec.ts
+++ b/packages/api/src/router/test/group.spec.ts
@@ -169,7 +169,7 @@ describe("byId should return group by id including members and permissions", ()
expect(result.id).toBe(groupId);
expect(result.members.length).toBe(1);
- const userKeys = Object.keys(result?.members[0] ?? {});
+ const userKeys = Object.keys(result.members[0] ?? {});
expect(userKeys.length).toBe(4);
expect(["id", "name", "email", "image"].some((key) => userKeys.includes(key)));
expect(result.permissions.length).toBe(1);
diff --git a/packages/api/src/router/test/integration.spec.ts b/packages/api/src/router/test/integration/integration-router.spec.ts
similarity index 60%
rename from packages/api/src/router/test/integration.spec.ts
rename to packages/api/src/router/test/integration/integration-router.spec.ts
index 2a68a8c39..711cfcb20 100644
--- a/packages/api/src/router/test/integration.spec.ts
+++ b/packages/api/src/router/test/integration/integration-router.spec.ts
@@ -1,16 +1,20 @@
+/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { describe, expect, it, vi } from "vitest";
import type { Session } from "@homarr/auth";
+import { encryptSecret } from "@homarr/common";
import { createId } from "@homarr/db";
import { integrations, integrationSecrets } from "@homarr/db/schema/sqlite";
import { createDb } from "@homarr/db/test";
-import type { RouterInputs } from "../..";
-import { encryptSecret, integrationRouter } from "../integration";
-import { expectToBeDefined } from "./helper";
+import { integrationRouter } from "../../integration/integration-router";
+import { expectToBeDefined } from "../helper";
// Mock the auth module to return an empty session
vi.mock("@homarr/auth", () => ({ auth: () => ({}) as Session }));
+vi.mock("../../integration/integration-test-connection", () => ({
+ testConnectionAsync: async () => await Promise.resolve(undefined),
+}));
describe("all should return all integrations", () => {
it("should return all integrations", async () => {
@@ -288,199 +292,3 @@ describe("delete should delete an integration", () => {
expect(dbSecrets.length).toBe(0);
});
});
-
-describe("testConnection should test the connection to an integration", () => {
- it.each([
- [
- "nzbGet" as const,
- [
- { kind: "username" as const, value: null },
- { kind: "password" as const, value: "Password123!" },
- ],
- ],
- [
- "nzbGet" as const,
- [
- { kind: "username" as const, value: "exampleUser" },
- { kind: "password" as const, value: null },
- ],
- ],
- ["sabNzbd" as const, [{ kind: "apiKey" as const, value: null }]],
- [
- "sabNzbd" as const,
- [
- { kind: "username" as const, value: "exampleUser" },
- { kind: "password" as const, value: "Password123!" },
- ],
- ],
- ])("should fail when a required secret is missing when creating %s integration", async (kind, secrets) => {
- const db = createDb();
- const caller = integrationRouter.createCaller({
- db,
- session: null,
- });
-
- const input: RouterInputs["integration"]["testConnection"] = {
- id: null,
- kind,
- url: `http://${kind}.local`,
- secrets,
- };
-
- const actAsync = async () => await caller.testConnection(input);
- await expect(actAsync()).rejects.toThrow("SECRETS_NOT_DEFINED");
- });
-
- it.each([
- [
- "nzbGet" as const,
- [
- { kind: "username" as const, value: "exampleUser" },
- { kind: "password" as const, value: "Password123!" },
- ],
- ],
- ["sabNzbd" as const, [{ kind: "apiKey" as const, value: "1234567890" }]],
- ])(
- "should be successful when all required secrets are defined for creation of %s integration",
- async (kind, secrets) => {
- const db = createDb();
- const caller = integrationRouter.createCaller({
- db,
- session: null,
- });
-
- const input: RouterInputs["integration"]["testConnection"] = {
- id: null,
- kind,
- url: `http://${kind}.local`,
- secrets,
- };
-
- const actAsync = async () => await caller.testConnection(input);
- await expect(actAsync()).resolves.toBeUndefined();
- },
- );
-
- it("should be successful when all required secrets are defined for updating an nzbGet integration", async () => {
- const db = createDb();
- const caller = integrationRouter.createCaller({
- db,
- session: null,
- });
-
- const input: RouterInputs["integration"]["testConnection"] = {
- id: createId(),
- kind: "nzbGet",
- url: "http://nzbGet.local",
- secrets: [
- { kind: "username", value: "exampleUser" },
- { kind: "password", value: "Password123!" },
- ],
- };
-
- const actAsync = async () => await caller.testConnection(input);
- await expect(actAsync()).resolves.toBeUndefined();
- });
-
- it("should be successful when overriding one of the secrets for an existing nzbGet integration", async () => {
- const db = createDb();
- const caller = integrationRouter.createCaller({
- db,
- session: null,
- });
-
- const integrationId = createId();
- await db.insert(integrations).values({
- id: integrationId,
- name: "NZBGet",
- kind: "nzbGet",
- url: "http://nzbGet.local",
- });
-
- await db.insert(integrationSecrets).values([
- {
- kind: "username",
- value: encryptSecret("exampleUser"),
- integrationId,
- updatedAt: new Date(),
- },
- {
- kind: "password",
- value: encryptSecret("Password123!"),
- integrationId,
- updatedAt: new Date(),
- },
- ]);
-
- const input: RouterInputs["integration"]["testConnection"] = {
- id: integrationId,
- kind: "nzbGet",
- url: "http://nzbGet.local",
- secrets: [
- { kind: "username", value: "newUser" },
- { kind: "password", value: null },
- ],
- };
-
- const actAsync = async () => await caller.testConnection(input);
- await expect(actAsync()).resolves.toBeUndefined();
- });
-
- it("should fail when a required secret is missing for an existing nzbGet integration", async () => {
- const db = createDb();
- const caller = integrationRouter.createCaller({
- db,
- session: null,
- });
-
- const integrationId = createId();
- await db.insert(integrations).values({
- id: integrationId,
- name: "NZBGet",
- kind: "nzbGet",
- url: "http://nzbGet.local",
- });
-
- await db.insert(integrationSecrets).values([
- {
- kind: "username",
- value: encryptSecret("exampleUser"),
- integrationId,
- updatedAt: new Date(),
- },
- ]);
-
- const input: RouterInputs["integration"]["testConnection"] = {
- id: integrationId,
- kind: "nzbGet",
- url: "http://nzbGet.local",
- secrets: [
- { kind: "username", value: "newUser" },
- { kind: "apiKey", value: "1234567890" },
- ],
- };
-
- const actAsync = async () => await caller.testConnection(input);
- await expect(actAsync()).rejects.toThrow("SECRETS_NOT_DEFINED");
- });
-
- it("should fail when the updating integration does not exist", async () => {
- const db = createDb();
- const caller = integrationRouter.createCaller({
- db,
- session: null,
- });
-
- const actAsync = async () =>
- await caller.testConnection({
- id: createId(),
- kind: "nzbGet",
- url: "http://nzbGet.local",
- secrets: [
- { kind: "username", value: null },
- { kind: "password", value: "Password123!" },
- ],
- });
- await expect(actAsync()).rejects.toThrow("SECRETS_NOT_DEFINED");
- });
-});
diff --git a/packages/api/src/router/test/integration/integration-test-connection.spec.ts b/packages/api/src/router/test/integration/integration-test-connection.spec.ts
new file mode 100644
index 000000000..5118f526c
--- /dev/null
+++ b/packages/api/src/router/test/integration/integration-test-connection.spec.ts
@@ -0,0 +1,253 @@
+import { describe, expect, test, vi } from "vitest";
+
+import * as homarrDefinitions from "@homarr/definitions";
+import * as homarrIntegrations from "@homarr/integrations";
+
+import { testConnectionAsync } from "../../integration/integration-test-connection";
+
+vi.mock("@homarr/common", async (importActual) => {
+ // eslint-disable-next-line @typescript-eslint/consistent-type-imports
+ const actual = await importActual();
+
+ return {
+ ...actual,
+ decryptSecret: (value: string) => value.split(".")[0],
+ };
+});
+
+describe("testConnectionAsync should run test connection of integration", () => {
+ test("with input of only form secrets matching api key kind it should use form apiKey", async () => {
+ // Arrange
+ const factorySpy = vi.spyOn(homarrIntegrations, "integrationCreatorByKind");
+ const optionsSpy = vi.spyOn(homarrDefinitions, "getAllSecretKindOptions");
+ factorySpy.mockReturnValue({
+ testConnectionAsync: async () => await Promise.resolve(),
+ } as homarrIntegrations.PiHoleIntegration);
+ optionsSpy.mockReturnValue([["apiKey"]]);
+
+ const integration = {
+ id: "new",
+ name: "Pi Hole",
+ url: "http://pi.hole",
+ kind: "piHole" as const,
+ secrets: [
+ {
+ kind: "apiKey" as const,
+ value: "secret",
+ },
+ ],
+ };
+
+ // Act
+ await testConnectionAsync(integration);
+
+ // Assert
+ expect(factorySpy).toHaveBeenCalledWith("piHole", {
+ id: "new",
+ name: "Pi Hole",
+ url: "http://pi.hole",
+ decryptedSecrets: [
+ expect.objectContaining({
+ kind: "apiKey",
+ value: "secret",
+ }),
+ ],
+ });
+ });
+
+ test("with input of only null form secrets and the required db secrets matching api key kind it should use db apiKey", async () => {
+ // Arrange
+ const factorySpy = vi.spyOn(homarrIntegrations, "integrationCreatorByKind");
+ const optionsSpy = vi.spyOn(homarrDefinitions, "getAllSecretKindOptions");
+ factorySpy.mockReturnValue({
+ testConnectionAsync: async () => await Promise.resolve(),
+ } as homarrIntegrations.PiHoleIntegration);
+ optionsSpy.mockReturnValue([["apiKey"]]);
+
+ const integration = {
+ id: "new",
+ name: "Pi Hole",
+ url: "http://pi.hole",
+ kind: "piHole" as const,
+ secrets: [
+ {
+ kind: "apiKey" as const,
+ value: null,
+ },
+ ],
+ };
+
+ const dbSecrets = [
+ {
+ kind: "apiKey" as const,
+ value: "dbSecret.encrypted" as const,
+ },
+ ];
+
+ // Act
+ await testConnectionAsync(integration, dbSecrets);
+
+ // Assert
+ expect(factorySpy).toHaveBeenCalledWith("piHole", {
+ id: "new",
+ name: "Pi Hole",
+ url: "http://pi.hole",
+ decryptedSecrets: [
+ expect.objectContaining({
+ kind: "apiKey",
+ value: "dbSecret",
+ }),
+ ],
+ });
+ });
+
+ test("with input of form and db secrets matching api key kind it should use form apiKey", async () => {
+ // Arrange
+ const factorySpy = vi.spyOn(homarrIntegrations, "integrationCreatorByKind");
+ const optionsSpy = vi.spyOn(homarrDefinitions, "getAllSecretKindOptions");
+ factorySpy.mockReturnValue({
+ testConnectionAsync: async () => await Promise.resolve(),
+ } as homarrIntegrations.PiHoleIntegration);
+ optionsSpy.mockReturnValue([["apiKey"]]);
+
+ const integration = {
+ id: "new",
+ name: "Pi Hole",
+ url: "http://pi.hole",
+ kind: "piHole" as const,
+ secrets: [
+ {
+ kind: "apiKey" as const,
+ value: "secret",
+ },
+ ],
+ };
+
+ const dbSecrets = [
+ {
+ kind: "apiKey" as const,
+ value: "dbSecret.encrypted" as const,
+ },
+ ];
+
+ // Act
+ await testConnectionAsync(integration, dbSecrets);
+
+ // Assert
+ expect(factorySpy).toHaveBeenCalledWith("piHole", {
+ id: "new",
+ name: "Pi Hole",
+ url: "http://pi.hole",
+ decryptedSecrets: [
+ expect.objectContaining({
+ kind: "apiKey",
+ value: "secret",
+ }),
+ ],
+ });
+ });
+
+ test("with input of form apiKey and db secrets for username and password it should use form apiKey when both is allowed", async () => {
+ // Arrange
+ const factorySpy = vi.spyOn(homarrIntegrations, "integrationCreatorByKind");
+ const optionsSpy = vi.spyOn(homarrDefinitions, "getAllSecretKindOptions");
+ factorySpy.mockReturnValue({
+ testConnectionAsync: async () => await Promise.resolve(),
+ } as homarrIntegrations.PiHoleIntegration);
+ optionsSpy.mockReturnValue([["username", "password"], ["apiKey"]]);
+
+ const integration = {
+ id: "new",
+ name: "Pi Hole",
+ url: "http://pi.hole",
+ kind: "piHole" as const,
+ secrets: [
+ {
+ kind: "apiKey" as const,
+ value: "secret",
+ },
+ ],
+ };
+
+ const dbSecrets = [
+ {
+ kind: "username" as const,
+ value: "dbUsername.encrypted" as const,
+ },
+ {
+ kind: "password" as const,
+ value: "dbPassword.encrypted" as const,
+ },
+ ];
+
+ // Act
+ await testConnectionAsync(integration, dbSecrets);
+
+ // Assert
+ expect(factorySpy).toHaveBeenCalledWith("piHole", {
+ id: "new",
+ name: "Pi Hole",
+ url: "http://pi.hole",
+ decryptedSecrets: [
+ expect.objectContaining({
+ kind: "apiKey",
+ value: "secret",
+ }),
+ ],
+ });
+ });
+
+ test("with input of null form apiKey and db secrets for username and password it should use db username and password when both is allowed", async () => {
+ // Arrange
+ const factorySpy = vi.spyOn(homarrIntegrations, "integrationCreatorByKind");
+ const optionsSpy = vi.spyOn(homarrDefinitions, "getAllSecretKindOptions");
+ factorySpy.mockReturnValue({
+ testConnectionAsync: async () => await Promise.resolve(),
+ } as homarrIntegrations.PiHoleIntegration);
+ optionsSpy.mockReturnValue([["username", "password"], ["apiKey"]]);
+
+ const integration = {
+ id: "new",
+ name: "Pi Hole",
+ url: "http://pi.hole",
+ kind: "piHole" as const,
+ secrets: [
+ {
+ kind: "apiKey" as const,
+ value: null,
+ },
+ ],
+ };
+
+ const dbSecrets = [
+ {
+ kind: "username" as const,
+ value: "dbUsername.encrypted" as const,
+ },
+ {
+ kind: "password" as const,
+ value: "dbPassword.encrypted" as const,
+ },
+ ];
+
+ // Act
+ await testConnectionAsync(integration, dbSecrets);
+
+ // Assert
+ expect(factorySpy).toHaveBeenCalledWith("piHole", {
+ id: "new",
+ name: "Pi Hole",
+ url: "http://pi.hole",
+ decryptedSecrets: [
+ expect.objectContaining({
+ kind: "username",
+ value: "dbUsername",
+ }),
+ expect.objectContaining({
+ kind: "password",
+ value: "dbPassword",
+ }),
+ ],
+ });
+ });
+});
diff --git a/packages/api/src/router/test/invite.spec.ts b/packages/api/src/router/test/invite.spec.ts
index 59f2fad68..bcd8c3675 100644
--- a/packages/api/src/router/test/invite.spec.ts
+++ b/packages/api/src/router/test/invite.spec.ts
@@ -1,3 +1,4 @@
+/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { describe, expect, test, vi } from "vitest";
import type { Session } from "@homarr/auth";
diff --git a/packages/api/src/router/test/widgets/app.spec.ts b/packages/api/src/router/test/widgets/app.spec.ts
new file mode 100644
index 000000000..a728f9a94
--- /dev/null
+++ b/packages/api/src/router/test/widgets/app.spec.ts
@@ -0,0 +1,51 @@
+import { describe, expect, test, vi } from "vitest";
+
+import type { Session } from "@homarr/auth";
+import { createDb } from "@homarr/db/test";
+import * as ping from "@homarr/ping";
+
+import { appRouter } from "../../widgets/app";
+
+// Mock the auth module to return an empty session
+vi.mock("@homarr/auth", () => ({ auth: () => ({}) as Session }));
+vi.mock("@homarr/ping", () => ({ sendPingRequestAsync: async () => await Promise.resolve(null) }));
+
+describe("ping should call sendPingRequestAsync with url and return result", () => {
+ test("ping with error response should return error and url", async () => {
+ // Arrange
+ const spy = vi.spyOn(ping, "sendPingRequestAsync");
+ const url = "http://localhost";
+ const db = createDb();
+ const caller = appRouter.createCaller({
+ db,
+ session: null,
+ });
+ spy.mockImplementation(() => Promise.resolve({ error: "error" }));
+
+ // Act
+ const result = await caller.ping({ url });
+
+ // Assert
+ expect(result.url).toBe(url);
+ expect("error" in result).toBe(true);
+ });
+
+ test("ping with success response should return statusCode and url", async () => {
+ // Arrange
+ const spy = vi.spyOn(ping, "sendPingRequestAsync");
+ const url = "http://localhost";
+ const db = createDb();
+ const caller = appRouter.createCaller({
+ db,
+ session: null,
+ });
+ spy.mockImplementation(() => Promise.resolve({ statusCode: 200 }));
+
+ // Act
+ const result = await caller.ping({ url });
+
+ // Assert
+ expect(result.url).toBe(url);
+ expect("statusCode" in result).toBe(true);
+ });
+});
diff --git a/packages/api/src/router/user.ts b/packages/api/src/router/user.ts
index 7d3fbd100..d73afa8c8 100644
--- a/packages/api/src/router/user.ts
+++ b/packages/api/src/router/user.ts
@@ -4,7 +4,7 @@ import { observable } from "@trpc/server/observable";
import { createSaltAsync, hashPasswordAsync } from "@homarr/auth";
import type { Database } from "@homarr/db";
import { and, createId, eq, schema } from "@homarr/db";
-import { invites, users } from "@homarr/db/schema/sqlite";
+import { groupMembers, groupPermissions, groups, invites, users } from "@homarr/db/schema/sqlite";
import { exampleChannel } from "@homarr/redis";
import { validation, z } from "@homarr/validation";
@@ -25,7 +25,21 @@ export const userRouter = createTRPCRouter({
});
}
- await createUserAsync(ctx.db, input);
+ const userId = await createUserAsync(ctx.db, input);
+ const groupId = createId();
+ await ctx.db.insert(groups).values({
+ id: groupId,
+ name: "admin",
+ ownerId: userId,
+ });
+ await ctx.db.insert(groupPermissions).values({
+ groupId,
+ permission: "admin",
+ });
+ await ctx.db.insert(groupMembers).values({
+ groupId,
+ userId,
+ });
}),
register: publicProcedure.input(validation.user.registrationApi).mutation(async ({ ctx, input }) => {
const inviteWhere = and(eq(invites.id, input.inviteId), eq(invites.token, input.token));
@@ -44,13 +58,6 @@ export const userRouter = createTRPCRouter({
});
}
- if (!dbInvite || dbInvite.expirationDate < new Date()) {
- throw new TRPCError({
- code: "FORBIDDEN",
- message: "Invalid invite",
- });
- }
-
await checkUsernameAlreadyTakenAndThrowAsync(ctx.db, input.username);
await createUserAsync(ctx.db, input);
@@ -251,6 +258,7 @@ const createUserAsync = async (db: Database, input: z.infer {
diff --git a/packages/api/src/router/widgets/app.ts b/packages/api/src/router/widgets/app.ts
new file mode 100644
index 000000000..8bfc51868
--- /dev/null
+++ b/packages/api/src/router/widgets/app.ts
@@ -0,0 +1,42 @@
+import { observable } from "@trpc/server/observable";
+
+import { sendPingRequestAsync } from "@homarr/ping";
+import { pingChannel, pingUrlChannel } from "@homarr/redis";
+import { z } from "@homarr/validation";
+
+import { createTRPCRouter, publicProcedure } from "../../trpc";
+
+export const appRouter = createTRPCRouter({
+ ping: publicProcedure.input(z.object({ url: z.string() })).query(async ({ input }) => {
+ const pingResult = await sendPingRequestAsync(input.url);
+
+ return {
+ url: input.url,
+ ...pingResult,
+ };
+ }),
+ updatedPing: publicProcedure
+ .input(
+ z.object({
+ url: z.string(),
+ }),
+ )
+ .subscription(async ({ input }) => {
+ await pingUrlChannel.addAsync(input.url);
+
+ const pingResult = await sendPingRequestAsync(input.url);
+
+ return observable<{ url: string; statusCode: number } | { url: string; error: string }>((emit) => {
+ emit.next({ url: input.url, ...pingResult });
+ pingChannel.subscribe((message) => {
+ // Only emit if same url
+ if (message.url !== input.url) return;
+ emit.next(message);
+ });
+
+ return () => {
+ void pingUrlChannel.removeAsync(input.url);
+ };
+ });
+ }),
+});
diff --git a/packages/api/src/router/widgets/index.ts b/packages/api/src/router/widgets/index.ts
index f2bd45078..7ece7bd62 100644
--- a/packages/api/src/router/widgets/index.ts
+++ b/packages/api/src/router/widgets/index.ts
@@ -1,10 +1,14 @@
import { createTRPCRouter } from "../../trpc";
+import { appRouter } from "./app";
import { dnsHoleRouter } from "./dns-hole";
import { notebookRouter } from "./notebook";
+import { smartHomeRouter } from "./smart-home";
import { weatherRouter } from "./weather";
export const widgetRouter = createTRPCRouter({
notebook: notebookRouter,
weather: weatherRouter,
+ app: appRouter,
dnsHole: dnsHoleRouter,
+ smartHome: smartHomeRouter,
});
diff --git a/packages/api/src/router/widgets/smart-home.ts b/packages/api/src/router/widgets/smart-home.ts
new file mode 100644
index 000000000..fa3ffb415
--- /dev/null
+++ b/packages/api/src/router/widgets/smart-home.ts
@@ -0,0 +1,38 @@
+import { observable } from "@trpc/server/observable";
+
+import { HomeAssistantIntegration } from "@homarr/integrations";
+import { homeAssistantEntityState } from "@homarr/redis";
+import { z } from "@homarr/validation";
+
+import { createOneIntegrationMiddleware } from "../../middlewares/integration";
+import { createTRPCRouter, publicProcedure } from "../../trpc";
+
+export const smartHomeRouter = createTRPCRouter({
+ subscribeEntityState: publicProcedure.input(z.object({ entityId: z.string() })).subscription(({ input }) => {
+ return observable<{
+ entityId: string;
+ state: string;
+ }>((emit) => {
+ homeAssistantEntityState.subscribe((message) => {
+ if (message.entityId !== input.entityId) {
+ return;
+ }
+ emit.next(message);
+ });
+ });
+ }),
+ switchEntity: publicProcedure
+ .unstable_concat(createOneIntegrationMiddleware("homeAssistant"))
+ .input(z.object({ entityId: z.string() }))
+ .mutation(async ({ ctx, input }) => {
+ const client = new HomeAssistantIntegration(ctx.integration);
+ return await client.triggerToggleAsync(input.entityId);
+ }),
+ executeAutomation: publicProcedure
+ .unstable_concat(createOneIntegrationMiddleware("homeAssistant"))
+ .input(z.object({ automationId: z.string() }))
+ .mutation(async ({ input, ctx }) => {
+ const client = new HomeAssistantIntegration(ctx.integration);
+ await client.triggerAutomationAsync(input.automationId);
+ }),
+});
diff --git a/packages/api/src/router/widgets/weather.ts b/packages/api/src/router/widgets/weather.ts
index a5440fd73..665fdedd4 100644
--- a/packages/api/src/router/widgets/weather.ts
+++ b/packages/api/src/router/widgets/weather.ts
@@ -3,13 +3,22 @@ import { validation } from "@homarr/validation";
import { createTRPCRouter, publicProcedure } from "../../trpc";
export const weatherRouter = createTRPCRouter({
- atLocation: publicProcedure
- .input(validation.widget.weather.atLocationInput)
- .output(validation.widget.weather.atLocationOutput)
- .query(async ({ input }) => {
- const res = await fetch(
- `https://api.open-meteo.com/v1/forecast?latitude=${input.latitude}&longitude=${input.longitude}&daily=weathercode,temperature_2m_max,temperature_2m_min¤t_weather=true&timezone=auto`,
- );
- return res.json();
- }),
+ atLocation: publicProcedure.input(validation.widget.weather.atLocationInput).query(async ({ input }) => {
+ const res = await fetch(
+ `https://api.open-meteo.com/v1/forecast?latitude=${input.latitude}&longitude=${input.longitude}&daily=weathercode,temperature_2m_max,temperature_2m_min¤t_weather=true&timezone=auto`,
+ );
+ const json: unknown = await res.json();
+ const weather = await validation.widget.weather.atLocationOutput.parseAsync(json);
+ return {
+ current: weather.current_weather,
+ daily: weather.daily.time.map((value, index) => {
+ return {
+ time: value,
+ weatherCode: weather.daily.weathercode[index] ?? 404,
+ maxTemp: weather.daily.temperature_2m_max[index],
+ minTemp: weather.daily.temperature_2m_min[index],
+ };
+ }),
+ };
+ }),
});
diff --git a/packages/api/src/trpc.ts b/packages/api/src/trpc.ts
index 5d231aae4..3969b204a 100644
--- a/packages/api/src/trpc.ts
+++ b/packages/api/src/trpc.ts
@@ -10,6 +10,7 @@ import { initTRPC, TRPCError } from "@trpc/server";
import superjson from "superjson";
import type { Session } from "@homarr/auth";
+import { FlattenError } from "@homarr/common";
import { db } from "@homarr/db";
import type { GroupPermissionKey } from "@homarr/definitions";
import { logger } from "@homarr/log";
@@ -52,6 +53,7 @@ const t = initTRPC.context().create({
data: {
...shape.data,
zodError: error.cause instanceof ZodError ? error.cause.flatten() : null,
+ error: error.cause instanceof FlattenError ? error.cause.flatten() : null,
},
}),
});
@@ -122,7 +124,7 @@ export const protectedProcedure = t.procedure.use(enforceUserIsAuthed);
export const permissionRequiredProcedure = {
requiresPermission: (permission: GroupPermissionKey) => {
return protectedProcedure.use(({ ctx, input, next }) => {
- if (!ctx.session?.user.permissions.includes(permission)) {
+ if (!ctx.session.user.permissions.includes(permission)) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Permission denied",
diff --git a/packages/auth/callbacks.ts b/packages/auth/callbacks.ts
index 3fdeb0c54..be34dcba7 100644
--- a/packages/auth/callbacks.ts
+++ b/packages/auth/callbacks.ts
@@ -44,10 +44,8 @@ export const createSignInCallback =
async ({ user }) => {
if (!isCredentialsRequest) return true;
- if (!user) return true;
-
// https://github.com/nextauthjs/next-auth/issues/6106
- if (!adapter?.createSession) {
+ if (!adapter.createSession) {
return false;
}
@@ -56,6 +54,7 @@ export const createSignInCallback =
await adapter.createSession({
sessionToken,
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
userId: user.id!,
expires: sessionExpiry,
});
diff --git a/packages/auth/eslint.config.js b/packages/auth/eslint.config.js
new file mode 100644
index 000000000..f7a5a7d36
--- /dev/null
+++ b/packages/auth/eslint.config.js
@@ -0,0 +1,4 @@
+import baseConfig from "@homarr/eslint-config/base";
+
+/** @type {import('typescript-eslint').Config} */
+export default [...baseConfig];
diff --git a/packages/auth/index.ts b/packages/auth/index.ts
index eed6a80b9..b8cbc55a1 100644
--- a/packages/auth/index.ts
+++ b/packages/auth/index.ts
@@ -1,12 +1,12 @@
-import type { DefaultSession } from "@auth/core/types";
+import type { DefaultSession } from "next-auth";
import type { GroupPermissionKey } from "@homarr/definitions";
import { createConfiguration } from "./configuration";
-export type { Session } from "@auth/core/types";
+export type { Session } from "next-auth";
-declare module "@auth/core/types" {
+declare module "next-auth" {
interface Session {
user: {
id: string;
diff --git a/packages/auth/package.json b/packages/auth/package.json
index 9f5732ff7..cb7ca23a6 100644
--- a/packages/auth/package.json
+++ b/packages/auth/package.json
@@ -16,21 +16,21 @@
"license": "MIT",
"scripts": {
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@homarr/db": "workspace:^0.1.0",
- "@auth/core": "^0.31.0",
- "@auth/drizzle-adapter": "^1.1.0",
+ "@auth/core": "^0.34.1",
+ "@auth/drizzle-adapter": "^1.4.1",
"@t3-oss/env-nextjs": "^0.10.1",
"bcrypt": "^5.1.1",
"cookies": "^0.9.1",
- "next": "^14.2.3",
- "next-auth": "5.0.0-beta.18",
- "react": "18.3.1",
- "react-dom": "18.3.1"
+ "next": "^14.2.4",
+ "next-auth": "5.0.0-beta.19",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
@@ -40,15 +40,9 @@
"@homarr/definitions": "workspace:^0.1.0",
"@types/bcrypt": "5.0.2",
"@types/cookies": "0.9.0",
- "eslint": "^8.57.0",
- "prettier": "^3.2.5",
- "typescript": "^5.4.5"
- },
- "eslintConfig": {
- "root": true,
- "extends": [
- "@homarr/eslint-config/base"
- ]
+ "eslint": "^9.6.0",
+ "prettier": "^3.3.2",
+ "typescript": "^5.5.2"
},
"prettier": "@homarr/prettier-config"
}
diff --git a/packages/auth/permissions/board-permissions.ts b/packages/auth/permissions/board-permissions.ts
index a3adbe8f6..af3b57816 100644
--- a/packages/auth/permissions/board-permissions.ts
+++ b/packages/auth/permissions/board-permissions.ts
@@ -1,4 +1,4 @@
-import type { Session } from "@auth/core/types";
+import type { Session } from "next-auth";
export type BoardPermissionsProps = (
| {
@@ -23,14 +23,14 @@ export const constructBoardPermissions = (board: BoardPermissionsProps, session:
const creatorId = "creator" in board ? board.creator?.id : board.creatorId;
return {
- hasFullAccess: session?.user?.id === creatorId || session?.user.permissions.includes("board-full-access"),
+ hasFullAccess: session?.user.id === creatorId || session?.user.permissions.includes("board-full-access"),
hasChangeAccess:
- session?.user?.id === creatorId ||
+ session?.user.id === creatorId ||
board.userPermissions.some(({ permission }) => permission === "board-change") ||
board.groupPermissions.some(({ permission }) => permission === "board-change") ||
session?.user.permissions.includes("board-modify-all"),
hasViewAccess:
- session?.user?.id === creatorId ||
+ session?.user.id === creatorId ||
board.userPermissions.length >= 1 ||
board.groupPermissions.length >= 1 ||
board.isPublic ||
diff --git a/packages/auth/permissions/test/board-permissions.spec.ts b/packages/auth/permissions/test/board-permissions.spec.ts
index 24b180836..05b98a039 100644
--- a/packages/auth/permissions/test/board-permissions.spec.ts
+++ b/packages/auth/permissions/test/board-permissions.spec.ts
@@ -1,4 +1,4 @@
-import type { Session } from "@auth/core/types";
+import type { Session } from "next-auth";
import { describe, expect, test } from "vitest";
import { getPermissionsWithChildren } from "@homarr/definitions";
diff --git a/packages/auth/session.ts b/packages/auth/session.ts
index 3e893db04..02ef573df 100644
--- a/packages/auth/session.ts
+++ b/packages/auth/session.ts
@@ -1,5 +1,5 @@
import { randomUUID } from "crypto";
-import type { Session } from "@auth/core/types";
+import type { Session } from "next-auth";
import type { Database } from "@homarr/db";
diff --git a/packages/auth/test/callbacks.spec.ts b/packages/auth/test/callbacks.spec.ts
index 1dced2115..ed66f9af4 100644
--- a/packages/auth/test/callbacks.spec.ts
+++ b/packages/auth/test/callbacks.spec.ts
@@ -1,8 +1,9 @@
+/* eslint-disable @typescript-eslint/no-non-null-assertion */
import type { ResponseCookie } from "next/dist/compiled/@edge-runtime/cookies";
import type { ReadonlyRequestCookies } from "next/dist/server/web/spec-extension/adapters/request-cookies";
import { cookies } from "next/headers";
import type { Adapter, AdapterUser } from "@auth/core/adapters";
-import type { Account, User } from "next-auth";
+import type { Account } from "next-auth";
import type { JWT } from "next-auth/jwt";
import { describe, expect, it, test, vi } from "vitest";
@@ -141,21 +142,11 @@ describe("createSignInCallback", () => {
expect(result).toBe(true);
});
- it("should return true if no user", async () => {
- const isCredentialsRequest = true;
- const signInCallback = createSignInCallback(createAdapter(), isCredentialsRequest);
- const result = await signInCallback({
- user: undefined as unknown as User,
- account: {} as Account,
- });
- expect(result).toBe(true);
- });
-
it("should return false if no adapter.createSession", async () => {
const isCredentialsRequest = true;
const signInCallback = createSignInCallback(
// https://github.com/nextauthjs/next-auth/issues/6106
- undefined as unknown as Adapter,
+ { createSession: undefined } as unknown as Adapter,
isCredentialsRequest,
);
const result = await signInCallback({
diff --git a/packages/common/eslint.config.js b/packages/common/eslint.config.js
new file mode 100644
index 000000000..f7a5a7d36
--- /dev/null
+++ b/packages/common/eslint.config.js
@@ -0,0 +1,4 @@
+import baseConfig from "@homarr/eslint-config/base";
+
+/** @type {import('typescript-eslint').Config} */
+export default [...baseConfig];
diff --git a/packages/common/package.json b/packages/common/package.json
index 31cd7eabb..11d8a80a3 100644
--- a/packages/common/package.json
+++ b/packages/common/package.json
@@ -17,21 +17,21 @@
"license": "MIT",
"scripts": {
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit"
},
+ "dependencies": {
+ "dayjs": "^1.11.11",
+ "react": "^18.3.1",
+ "next": "^14.2.4"
+ },
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
- "eslint": "^8.57.0",
- "typescript": "^5.4.5"
- },
- "eslintConfig": {
- "extends": [
- "@homarr/eslint-config/base"
- ]
+ "eslint": "^9.6.0",
+ "typescript": "^5.5.2"
},
"prettier": "@homarr/prettier-config"
}
diff --git a/packages/common/src/encryption.ts b/packages/common/src/encryption.ts
new file mode 100644
index 000000000..70dee629a
--- /dev/null
+++ b/packages/common/src/encryption.ts
@@ -0,0 +1,22 @@
+import crypto from "crypto";
+
+const algorithm = "aes-256-cbc"; //Using AES encryption
+const key = Buffer.from("1d71cceced68159ba59a277d056a66173613052cbeeccbfbd15ab1c909455a4d", "hex"); // TODO: generate with const data = crypto.randomBytes(32).toString('hex')
+
+export function encryptSecret(text: string): `${string}.${string}` {
+ const initializationVector = crypto.randomBytes(16);
+ const cipher = crypto.createCipheriv(algorithm, Buffer.from(key), initializationVector);
+ let encrypted = cipher.update(text);
+ encrypted = Buffer.concat([encrypted, cipher.final()]);
+ return `${encrypted.toString("hex")}.${initializationVector.toString("hex")}`;
+}
+
+export function decryptSecret(value: `${string}.${string}`) {
+ const [data, dataIv] = value.split(".") as [string, string];
+ const initializationVector = Buffer.from(dataIv, "hex");
+ const encryptedText = Buffer.from(data, "hex");
+ const decipher = crypto.createDecipheriv(algorithm, Buffer.from(key), initializationVector);
+ let decrypted = decipher.update(encryptedText);
+ decrypted = Buffer.concat([decrypted, decipher.final()]);
+ return decrypted.toString();
+}
diff --git a/packages/common/src/error.ts b/packages/common/src/error.ts
new file mode 100644
index 000000000..96adb35e0
--- /dev/null
+++ b/packages/common/src/error.ts
@@ -0,0 +1,24 @@
+export const extractErrorMessage = (error: unknown) => {
+ if (error instanceof Error) {
+ return error.message;
+ }
+
+ if (typeof error === "string") {
+ return error;
+ }
+
+ return "Unknown error";
+};
+
+export abstract class FlattenError extends Error {
+ constructor(
+ message: string,
+ private flattenResult: Record,
+ ) {
+ super(message);
+ }
+
+ public flatten(): Record {
+ return this.flattenResult;
+ }
+}
diff --git a/packages/common/src/hooks.ts b/packages/common/src/hooks.ts
new file mode 100644
index 000000000..5464d67d9
--- /dev/null
+++ b/packages/common/src/hooks.ts
@@ -0,0 +1,25 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import { useParams } from "next/navigation";
+import dayjs from "dayjs";
+import relativeTime from "dayjs/plugin/relativeTime";
+
+dayjs.extend(relativeTime);
+
+const calculateTimeAgo = (timestamp: Date, locale: string) => {
+ return dayjs().locale(locale).to(timestamp);
+};
+
+export const useTimeAgo = (timestamp: Date) => {
+ const { locale } = useParams<{ locale: string }>();
+ const [timeAgo, setTimeAgo] = useState(calculateTimeAgo(timestamp, locale));
+
+ useEffect(() => {
+ const intervalId = setInterval(() => setTimeAgo(calculateTimeAgo(timestamp, locale)), 1000); // update every second
+
+ return () => clearInterval(intervalId); // clear interval on hook unmount
+ }, [timestamp, locale]);
+
+ return timeAgo;
+};
diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts
index 5ee205da0..0b6d4cb65 100644
--- a/packages/common/src/index.ts
+++ b/packages/common/src/index.ts
@@ -3,4 +3,8 @@ export * from "./string";
export * from "./cookie";
export * from "./array";
export * from "./stopwatch";
+export * from "./hooks";
+export * from "./url";
export * from "./number";
+export * from "./error";
+export * from "./encryption";
diff --git a/packages/common/src/number.ts b/packages/common/src/number.ts
index 5341e5004..24738850d 100644
--- a/packages/common/src/number.ts
+++ b/packages/common/src/number.ts
@@ -15,3 +15,7 @@ export const formatNumber = (value: number, decimalPlaces: number) => {
}
return value.toFixed(decimalPlaces);
};
+
+export const randomInt = (min: number, max: number) => {
+ return Math.floor(Math.random() * (max - min + 1) + min);
+};
diff --git a/packages/common/src/test/error.spec.ts b/packages/common/src/test/error.spec.ts
new file mode 100644
index 000000000..c4c68d7f7
--- /dev/null
+++ b/packages/common/src/test/error.spec.ts
@@ -0,0 +1,41 @@
+import { describe, expect, test } from "vitest";
+
+import { extractErrorMessage } from "../error";
+
+describe("error to resolve to correct message", () => {
+ test("error class to resolve to error message", () => {
+ // Arrange
+ const error = new Error("Message");
+
+ // Act
+ const message = extractErrorMessage(error);
+
+ // Assert
+ expect(typeof message).toBe("string");
+ expect(message).toBe("Message");
+ });
+
+ test("error string to resolve to error message", () => {
+ // Arrange
+ const error = "Message";
+
+ // Act
+ const message = extractErrorMessage(error);
+
+ // Assert
+ expect(typeof message).toBe("string");
+ expect(message).toBe("Message");
+ });
+
+ test("error whatever to resolve to unknown error message", () => {
+ // Arrange
+ const error = 5;
+
+ // Act
+ const message = extractErrorMessage(error);
+
+ // Assert
+ expect(typeof message).toBe("string");
+ expect(message).toBe("Unknown error");
+ });
+});
diff --git a/packages/common/src/url.ts b/packages/common/src/url.ts
new file mode 100644
index 000000000..5c46343ff
--- /dev/null
+++ b/packages/common/src/url.ts
@@ -0,0 +1,9 @@
+export const appendPath = (url: URL | string, path: string) => {
+ const newUrl = new URL(url);
+ newUrl.pathname = removeTrailingSlash(newUrl.pathname) + path;
+ return newUrl;
+};
+
+const removeTrailingSlash = (path: string) => {
+ return path.at(-1) === "/" ? path.substring(0, path.length - 1) : path;
+};
diff --git a/packages/cron-jobs-core/eslint.config.js b/packages/cron-jobs-core/eslint.config.js
new file mode 100644
index 000000000..5b19b6f8a
--- /dev/null
+++ b/packages/cron-jobs-core/eslint.config.js
@@ -0,0 +1,9 @@
+import baseConfig from "@homarr/eslint-config/base";
+
+/** @type {import('typescript-eslint').Config} */
+export default [
+ {
+ ignores: [],
+ },
+ ...baseConfig,
+];
diff --git a/packages/cron-jobs-core/index.ts b/packages/cron-jobs-core/index.ts
new file mode 100644
index 000000000..3bd16e178
--- /dev/null
+++ b/packages/cron-jobs-core/index.ts
@@ -0,0 +1 @@
+export * from "./src";
diff --git a/packages/cron-jobs-core/package.json b/packages/cron-jobs-core/package.json
new file mode 100644
index 000000000..07b88c92f
--- /dev/null
+++ b/packages/cron-jobs-core/package.json
@@ -0,0 +1,38 @@
+{
+ "name": "@homarr/cron-jobs-core",
+ "private": true,
+ "version": "0.1.0",
+ "type": "module",
+ "exports": {
+ ".": "./index.ts",
+ "./expressions": "./src/expressions.ts",
+ "./logger": "./src/logger.ts"
+ },
+ "typesVersions": {
+ "*": {
+ "*": [
+ "src/*"
+ ]
+ }
+ },
+ "license": "MIT",
+ "scripts": {
+ "clean": "rm -rf .turbo node_modules",
+ "lint": "eslint",
+ "format": "prettier --check . --ignore-path ../../.gitignore",
+ "typecheck": "tsc --noEmit"
+ },
+ "dependencies": {
+ "node-cron": "^3.0.3",
+ "@homarr/common": "workspace:^0.1.0"
+ },
+ "devDependencies": {
+ "@homarr/eslint-config": "workspace:^0.2.0",
+ "@homarr/prettier-config": "workspace:^0.1.0",
+ "@homarr/tsconfig": "workspace:^0.1.0",
+ "@types/node-cron": "^3.0.11",
+ "eslint": "^9.6.0",
+ "typescript": "^5.5.2"
+ },
+ "prettier": "@homarr/prettier-config"
+}
diff --git a/packages/cron-jobs-core/src/creator.ts b/packages/cron-jobs-core/src/creator.ts
new file mode 100644
index 000000000..882a2be68
--- /dev/null
+++ b/packages/cron-jobs-core/src/creator.ts
@@ -0,0 +1,94 @@
+import cron from "node-cron";
+
+import type { MaybePromise } from "@homarr/common/types";
+
+import type { Logger } from "./logger";
+import type { ValidateCron } from "./validation";
+
+export interface CreateCronJobCreatorOptions {
+ beforeCallback?: (name: TAllowedNames) => MaybePromise;
+ onCallbackSuccess?: (name: TAllowedNames) => MaybePromise;
+ onCallbackError?: (name: TAllowedNames, error: unknown) => MaybePromise;
+ timezone?: string;
+ logger: Logger;
+}
+
+interface CreateCronJobOptions {
+ runOnStart?: boolean;
+}
+
+const createCallback = (
+ name: TName,
+ cronExpression: string,
+ options: CreateCronJobOptions,
+ creatorOptions: CreateCronJobCreatorOptions,
+) => {
+ return (callback: () => MaybePromise) => {
+ const catchingCallbackAsync = async () => {
+ try {
+ creatorOptions.logger.logDebug(`The callback of '${name}' cron job started`);
+ await creatorOptions.beforeCallback?.(name);
+ await callback();
+ creatorOptions.logger.logInfo(`The callback of '${name}' cron job succeeded`);
+ await creatorOptions.onCallbackSuccess?.(name);
+ } catch (error) {
+ creatorOptions.logger.logError(error);
+ await creatorOptions.onCallbackError?.(name, error);
+ }
+ };
+
+ /**
+ * We are not using the runOnInit method as we want to run the job only once we start the cron job schedule manually.
+ * This allows us to always run it once we start it. Additionally it will not run the callback if only the cron job file is imported.
+ */
+ const scheduledTask = cron.schedule(cronExpression, () => void catchingCallbackAsync(), {
+ scheduled: false,
+ name,
+ timezone: creatorOptions.timezone,
+ });
+ creatorOptions.logger.logDebug(
+ `The cron job '${name}' was created with expression ${cronExpression} in timezone ${creatorOptions.timezone} and runOnStart ${options.runOnStart}`,
+ );
+
+ return {
+ name,
+ cronExpression,
+ scheduledTask,
+ async onStartAsync() {
+ if (!options.runOnStart) return;
+
+ creatorOptions.logger.logDebug(`The cron job '${name}' is running because runOnStart is set to true`);
+ await catchingCallbackAsync();
+ },
+ };
+ };
+};
+
+export type JobCallback = ReturnType<
+ typeof createCallback
+>;
+
+export const createCronJobCreator = (
+ creatorOptions: CreateCronJobCreatorOptions,
+) => {
+ return (
+ name: TName,
+ cronExpression: TExpression,
+ options: CreateCronJobOptions = { runOnStart: false },
+ ) => {
+ creatorOptions.logger.logDebug(`Validating cron expression '${cronExpression}' for job: ${name}`);
+ if (!cron.validate(cronExpression)) {
+ throw new Error(`Invalid cron expression '${cronExpression}' for job '${name}'`);
+ }
+ creatorOptions.logger.logDebug(`Cron job expression '${cronExpression}' for job ${name} is valid`);
+
+ const returnValue = {
+ withCallback: createCallback(name, cronExpression, options, creatorOptions),
+ };
+
+ // This is a type guard to check if the cron expression is valid and give the user a type hint
+ return returnValue as unknown as ValidateCron extends true
+ ? typeof returnValue
+ : "Invalid cron expression";
+ };
+};
diff --git a/packages/cron-jobs-core/src/expressions.ts b/packages/cron-jobs-core/src/expressions.ts
new file mode 100644
index 000000000..d3b187190
--- /dev/null
+++ b/packages/cron-jobs-core/src/expressions.ts
@@ -0,0 +1,9 @@
+import { checkCron } from "./validation";
+
+export const EVERY_5_SECONDS = checkCron("*/5 * * * * *") satisfies string;
+export const EVERY_MINUTE = checkCron("* * * * *") satisfies string;
+export const EVERY_5_MINUTES = checkCron("*/5 * * * *") satisfies string;
+export const EVERY_10_MINUTES = checkCron("*/10 * * * *") satisfies string;
+export const EVERY_HOUR = checkCron("0 * * * *") satisfies string;
+export const EVERY_DAY = checkCron("0 0 * * */1") satisfies string;
+export const EVERY_WEEK = checkCron("0 0 * * 1") satisfies string;
diff --git a/packages/cron-jobs-core/src/group.ts b/packages/cron-jobs-core/src/group.ts
new file mode 100644
index 000000000..772c4cce8
--- /dev/null
+++ b/packages/cron-jobs-core/src/group.ts
@@ -0,0 +1,64 @@
+import { objectEntries } from "@homarr/common";
+
+import type { JobCallback } from "./creator";
+import type { Logger } from "./logger";
+import { jobRegistry } from "./registry";
+
+type Jobs = {
+ [name in TAllowedNames]: ReturnType>;
+};
+
+export interface CreateCronJobGroupCreatorOptions {
+ logger: Logger;
+}
+
+export const createJobGroupCreator = (
+ options: CreateCronJobGroupCreatorOptions,
+) => {
+ return >(jobs: TJobs) => {
+ options.logger.logDebug(`Creating job group with ${Object.keys(jobs).length} jobs.`);
+ for (const [key, job] of objectEntries(jobs)) {
+ if (typeof key !== "string" || typeof job.name !== "string") continue;
+
+ options.logger.logDebug(`Added job ${job.name} to the job registry.`);
+ jobRegistry.set(key, {
+ ...job,
+ name: job.name,
+ });
+ }
+
+ return {
+ startAsync: async (name: keyof TJobs) => {
+ const job = jobRegistry.get(name as string);
+ if (!job) return;
+
+ options.logger.logInfo(`Starting schedule cron job ${job.name}.`);
+ await job.onStartAsync();
+ job.scheduledTask.start();
+ },
+ startAllAsync: async () => {
+ for (const job of jobRegistry.values()) {
+ options.logger.logInfo(`Starting schedule of cron job ${job.name}.`);
+ await job.onStartAsync();
+ job.scheduledTask.start();
+ }
+ },
+ stop: (name: keyof TJobs) => {
+ const job = jobRegistry.get(name as string);
+ if (!job) return;
+
+ options.logger.logInfo(`Stopping schedule cron job ${job.name}.`);
+ job.scheduledTask.stop();
+ },
+ stopAll: () => {
+ for (const job of jobRegistry.values()) {
+ options.logger.logInfo(`Stopping schedule cron job ${job.name}.`);
+ job.scheduledTask.stop();
+ }
+ },
+ getJobRegistry() {
+ return jobRegistry as Map>>;
+ },
+ };
+ };
+};
diff --git a/packages/cron-jobs-core/src/index.ts b/packages/cron-jobs-core/src/index.ts
new file mode 100644
index 000000000..08ed3e75e
--- /dev/null
+++ b/packages/cron-jobs-core/src/index.ts
@@ -0,0 +1,15 @@
+import type { CreateCronJobCreatorOptions } from "./creator";
+import { createCronJobCreator } from "./creator";
+import { createJobGroupCreator } from "./group";
+import { ConsoleLogger } from "./logger";
+
+export const createCronJobFunctions = (
+ options: CreateCronJobCreatorOptions = { logger: new ConsoleLogger() },
+) => {
+ return {
+ createCronJob: createCronJobCreator(options),
+ createCronJobGroup: createJobGroupCreator({
+ logger: options.logger,
+ }),
+ };
+};
diff --git a/packages/cron-jobs-core/src/logger.ts b/packages/cron-jobs-core/src/logger.ts
new file mode 100644
index 000000000..2ff1a8030
--- /dev/null
+++ b/packages/cron-jobs-core/src/logger.ts
@@ -0,0 +1,19 @@
+export interface Logger {
+ logDebug(message: string): void;
+ logInfo(message: string): void;
+ logError(error: unknown): void;
+}
+
+export class ConsoleLogger implements Logger {
+ public logDebug(message: string) {
+ console.log(message);
+ }
+
+ public logInfo(message: string) {
+ console.log(message);
+ }
+
+ public logError(error: unknown) {
+ console.error(error);
+ }
+}
diff --git a/packages/cron-jobs-core/src/registry.ts b/packages/cron-jobs-core/src/registry.ts
new file mode 100644
index 000000000..ab8b30a72
--- /dev/null
+++ b/packages/cron-jobs-core/src/registry.ts
@@ -0,0 +1,3 @@
+import type { JobCallback } from "./creator";
+
+export const jobRegistry = new Map>>();
diff --git a/packages/cron-jobs-core/src/validation.ts b/packages/cron-jobs-core/src/validation.ts
new file mode 100644
index 000000000..7c17ea5c3
--- /dev/null
+++ b/packages/cron-jobs-core/src/validation.ts
@@ -0,0 +1,60 @@
+// The below two types are used for a number with arbitrary length. By default the type `${number}` allows spaces which is not allowed in cron expressions.
+type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
+type NumberWithoutSpaces = `${Digit}${number | ""}` & `${number | ""}${Digit}`;
+
+// The below type is used to constrain the cron expression to only allow valid characters. This will find any invalid characters in the cron expression.
+type CronChars = `${"*" | "/" | "-" | "," | NumberWithoutSpaces}`;
+
+/**
+ * Will return false if the TMaybeCron string contains any invalid characters.
+ * Otherwise it will return true.
+ */
+type ConstrainedCronString = TMaybeCron extends ""
+ ? true
+ : TMaybeCron extends `${CronChars}${infer Rest}`
+ ? ConstrainedCronString
+ : false;
+
+/**
+ * Will return true if the TMaybeCron string is a valid cron expression.
+ * Otherwise it will return false.
+ *
+ * It allows cron expressions with 5 or 6 parts. (Seconds are optional)
+ * See https://nodecron.com/docs/
+ */
+export type ValidateCron =
+ TMaybeCron extends `${infer inferedSecond} ${infer inferedMinute} ${infer inferedHour} ${infer inferedMonthDay} ${infer inferedMonth} ${infer inferedWeekDay}`
+ ? ConstrainedCronString extends true
+ ? ConstrainedCronString extends true
+ ? ConstrainedCronString extends true
+ ? ConstrainedCronString extends true
+ ? ConstrainedCronString extends true
+ ? ConstrainedCronString extends true
+ ? true
+ : false
+ : false
+ : false
+ : false
+ : false
+ : false
+ : TMaybeCron extends `${infer inferedMinute} ${infer inferedHour} ${infer inferedMonthDay} ${infer inferedMonth} ${infer inferedWeekDay}`
+ ? ConstrainedCronString extends true
+ ? ConstrainedCronString extends true
+ ? ConstrainedCronString extends true
+ ? ConstrainedCronString extends true
+ ? ConstrainedCronString extends true
+ ? true
+ : false
+ : false
+ : false
+ : false
+ : false
+ : false;
+
+/**
+ * Will return the cron expression if it is valid.
+ * Otherwise it will return void (as type).
+ */
+export const checkCron = (cron: T) => {
+ return cron as ValidateCron extends true ? T : void;
+};
diff --git a/packages/cron-jobs-core/tsconfig.json b/packages/cron-jobs-core/tsconfig.json
new file mode 100644
index 000000000..cbe8483d9
--- /dev/null
+++ b/packages/cron-jobs-core/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "@homarr/tsconfig/base.json",
+ "compilerOptions": {
+ "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
+ },
+ "include": ["*.ts", "src"],
+ "exclude": ["node_modules"]
+}
diff --git a/packages/db/configs/mysql.config.ts b/packages/db/configs/mysql.config.ts
index b3669df13..407fae864 100644
--- a/packages/db/configs/mysql.config.ts
+++ b/packages/db/configs/mysql.config.ts
@@ -1,3 +1,4 @@
+/* eslint-disable @typescript-eslint/no-non-null-assertion */
import * as dotenv from "dotenv";
import type { Config } from "drizzle-kit";
diff --git a/packages/db/configs/sqlite.config.ts b/packages/db/configs/sqlite.config.ts
index 919cbfc0b..aba34fe26 100644
--- a/packages/db/configs/sqlite.config.ts
+++ b/packages/db/configs/sqlite.config.ts
@@ -6,6 +6,7 @@ dotenv.config({ path: "../../.env" });
export default {
dialect: "sqlite",
schema: "./schema",
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
dbCredentials: { url: process.env.DB_URL! },
out: "./migrations/sqlite",
} satisfies Config;
diff --git a/packages/db/driver.ts b/packages/db/driver.ts
index 2cea933ea..36a86ece5 100644
--- a/packages/db/driver.ts
+++ b/packages/db/driver.ts
@@ -13,6 +13,7 @@ import * as sqliteSchema from "./schema/sqlite";
type HomarrDatabase = BetterSQLite3Database;
const init = () => {
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!connection) {
switch (process.env.DB_DRIVER) {
case "mysql2":
@@ -48,6 +49,7 @@ const initMySQL2 = () => {
} else {
connection = mysql.createConnection({
host: process.env.DB_HOST,
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
database: process.env.DB_NAME!,
port: Number(process.env.DB_PORT),
user: process.env.DB_USER,
diff --git a/packages/db/eslint.config.js b/packages/db/eslint.config.js
new file mode 100644
index 000000000..f7a5a7d36
--- /dev/null
+++ b/packages/db/eslint.config.js
@@ -0,0 +1,4 @@
+import baseConfig from "@homarr/eslint-config/base";
+
+/** @type {import('typescript-eslint').Config} */
+export default [...baseConfig];
diff --git a/packages/db/migrations/mysql/migrate.ts b/packages/db/migrations/mysql/migrate.ts
index fd67b7f70..50d495d00 100644
--- a/packages/db/migrations/mysql/migrate.ts
+++ b/packages/db/migrations/mysql/migrate.ts
@@ -1,3 +1,4 @@
+/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { drizzle } from "drizzle-orm/mysql2";
import { migrate } from "drizzle-orm/mysql2/migrator";
import mysql from "mysql2";
diff --git a/packages/db/package.json b/packages/db/package.json
index bd5000793..8253d0a0a 100644
--- a/packages/db/package.json
+++ b/packages/db/package.json
@@ -17,7 +17,7 @@
"build:sqlite": "esbuild migrations/sqlite/migrate.ts --bundle --platform=node --outfile=migrations/sqlite/migrate.cjs",
"build:mysql": "esbuild migrations/mysql/migrate.ts --bundle --platform=node --outfile=migrations/mysql/migrate.cjs",
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"migration:sqlite:generate": "drizzle-kit generate --config ./configs/sqlite.config.ts",
"migration:sqlite:run": "drizzle-kit migrate --config ./configs/sqlite.config.ts",
@@ -33,10 +33,12 @@
"@homarr/definitions": "workspace:^0.1.0",
"@homarr/log": "workspace:^0.1.0",
"@paralleldrive/cuid2": "^2.2.2",
- "better-sqlite3": "^10.0.0",
- "drizzle-orm": "^0.30.10",
- "mysql2": "3.9.8",
- "drizzle-kit": "^0.21.4"
+ "@auth/core": "^0.34.1",
+ "better-sqlite3": "^11.1.1",
+ "drizzle-orm": "^0.31.2",
+ "dotenv": "^16.4.5",
+ "mysql2": "3.10.1",
+ "drizzle-kit": "^0.22.7"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
@@ -44,15 +46,9 @@
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/better-sqlite3": "7.6.10",
"dotenv-cli": "^7.4.2",
- "eslint": "^8.57.0",
- "prettier": "^3.2.5",
- "typescript": "^5.4.5"
- },
- "eslintConfig": {
- "root": true,
- "extends": [
- "@homarr/eslint-config/base"
- ]
+ "eslint": "^9.6.0",
+ "prettier": "^3.3.2",
+ "typescript": "^5.5.2"
},
"prettier": "@homarr/prettier-config"
}
diff --git a/packages/db/schema/mysql.ts b/packages/db/schema/mysql.ts
index d53a9ec30..22692da00 100644
--- a/packages/db/schema/mysql.ts
+++ b/packages/db/schema/mysql.ts
@@ -141,7 +141,9 @@ export const integrationSecrets = mysqlTable(
{
kind: varchar("kind", { length: 16 }).$type().notNull(),
value: text("value").$type<`${string}.${string}`>().notNull(),
- updatedAt: timestamp("updated_at").notNull(),
+ updatedAt: timestamp("updated_at")
+ .$onUpdateFn(() => new Date())
+ .notNull(),
integrationId: varchar("integration_id", { length: 64 })
.notNull()
.references(() => integrations.id, { onDelete: "cascade" }),
diff --git a/packages/db/schema/sqlite.ts b/packages/db/schema/sqlite.ts
index f7c3c87e5..ff913f264 100644
--- a/packages/db/schema/sqlite.ts
+++ b/packages/db/schema/sqlite.ts
@@ -144,7 +144,9 @@ export const integrationSecrets = sqliteTable(
{
kind: text("kind").$type().notNull(),
value: text("value").$type<`${string}.${string}`>().notNull(),
- updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(),
+ updatedAt: integer("updated_at", { mode: "timestamp" })
+ .$onUpdateFn(() => new Date())
+ .notNull(),
integrationId: text("integration_id")
.notNull()
.references(() => integrations.id, { onDelete: "cascade" }),
diff --git a/packages/db/test/schema.spec.ts b/packages/db/test/schema.spec.ts
index 78b8363e2..1d379763a 100644
--- a/packages/db/test/schema.spec.ts
+++ b/packages/db/test/schema.spec.ts
@@ -1,3 +1,4 @@
+/* eslint-disable @typescript-eslint/no-non-null-assertion */
import type { Column, InferSelectModel } from "drizzle-orm";
import type { ForeignKey as MysqlForeignKey, MySqlTableWithColumns } from "drizzle-orm/mysql-core";
import type { ForeignKey as SqliteForeignKey, SQLiteTableWithColumns } from "drizzle-orm/sqlite-core";
diff --git a/packages/definitions/eslint.config.js b/packages/definitions/eslint.config.js
new file mode 100644
index 000000000..f7a5a7d36
--- /dev/null
+++ b/packages/definitions/eslint.config.js
@@ -0,0 +1,4 @@
+import baseConfig from "@homarr/eslint-config/base";
+
+/** @type {import('typescript-eslint').Config} */
+export default [...baseConfig];
diff --git a/packages/definitions/package.json b/packages/definitions/package.json
index c93c62866..92b5f2360 100644
--- a/packages/definitions/package.json
+++ b/packages/definitions/package.json
@@ -16,24 +16,19 @@
"license": "MIT",
"scripts": {
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit"
},
+ "dependencies": {
+ "@homarr/common": "workspace:^0.1.0"
+ },
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
- "eslint": "^8.57.0",
- "typescript": "^5.4.5"
+ "eslint": "^9.6.0",
+ "typescript": "^5.5.2"
},
- "eslintConfig": {
- "extends": [
- "@homarr/eslint-config/base"
- ]
- },
- "prettier": "@homarr/prettier-config",
- "dependencies": {
- "@homarr/common": "workspace:^0.1.0"
- }
+ "prettier": "@homarr/prettier-config"
}
diff --git a/packages/definitions/src/docker.ts b/packages/definitions/src/docker.ts
new file mode 100644
index 000000000..4a39a21ab
--- /dev/null
+++ b/packages/definitions/src/docker.ts
@@ -0,0 +1,11 @@
+export const dockerContainerStates = [
+ "created",
+ "running",
+ "paused",
+ "restarting",
+ "exited",
+ "removing",
+ "dead",
+] as const;
+
+export type DockerContainerState = (typeof dockerContainerStates)[number];
diff --git a/packages/definitions/src/index.ts b/packages/definitions/src/index.ts
index d72b9e8d1..42ce3cc29 100644
--- a/packages/definitions/src/index.ts
+++ b/packages/definitions/src/index.ts
@@ -3,3 +3,4 @@ export * from "./integration";
export * from "./section";
export * from "./widget";
export * from "./permissions";
+export * from "./docker";
diff --git a/packages/definitions/src/integration.ts b/packages/definitions/src/integration.ts
index 3f5650af4..7564a6ae8 100644
--- a/packages/definitions/src/integration.ts
+++ b/packages/definitions/src/integration.ts
@@ -115,16 +115,16 @@ export const integrationDefs = {
}
>;
-export const getIconUrl = (integration: IntegrationKind) => integrationDefs[integration]?.iconUrl ?? null;
+export const getIconUrl = (integration: IntegrationKind) => integrationDefs[integration].iconUrl;
export const getIntegrationName = (integration: IntegrationKind) => integrationDefs[integration].name;
export const getDefaultSecretKinds = (integration: IntegrationKind): IntegrationSecretKind[] =>
- integrationDefs[integration]?.secretKinds[0];
+ integrationDefs[integration].secretKinds[0];
export const getAllSecretKindOptions = (
integration: IntegrationKind,
-): [IntegrationSecretKind[], ...IntegrationSecretKind[][]] => integrationDefs[integration]?.secretKinds;
+): [IntegrationSecretKind[], ...IntegrationSecretKind[][]] => integrationDefs[integration].secretKinds;
export const integrationKinds = objectKeys(integrationDefs);
@@ -137,4 +137,5 @@ export type IntegrationCategory =
| "mediaSearch"
| "mediaRequest"
| "downloadClient"
- | "useNetClient";
+ | "useNetClient"
+ | "smartHomeServer";
diff --git a/packages/definitions/src/permissions.ts b/packages/definitions/src/permissions.ts
index 827c78df9..8c3eae83c 100644
--- a/packages/definitions/src/permissions.ts
+++ b/packages/definitions/src/permissions.ts
@@ -34,6 +34,7 @@ export const getPermissionsWithParents = (permissions: GroupPermissionKey[]): Gr
const getPermissionsInner = (permissionSet: Set, permissions: GroupPermissionKey[]) => {
permissions.forEach((permission) => {
const children = groupPermissionParents[permission as keyof typeof groupPermissionParents];
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (children) {
getPermissionsInner(permissionSet, children);
}
diff --git a/packages/definitions/src/widget.ts b/packages/definitions/src/widget.ts
index 6c50ba23b..d36b93687 100644
--- a/packages/definitions/src/widget.ts
+++ b/packages/definitions/src/widget.ts
@@ -1,2 +1,12 @@
-export const widgetKinds = ["clock", "weather", "app", "iframe", "video", "notebook", "dnsHoleSummary"] as const;
+export const widgetKinds = [
+ "clock",
+ "weather",
+ "app",
+ "iframe",
+ "video",
+ "notebook",
+ "dnsHoleSummary",
+ "smartHome-entityState",
+ "smartHome-executeAutomation",
+] as const;
export type WidgetKind = (typeof widgetKinds)[number];
diff --git a/packages/form/eslint.config.js b/packages/form/eslint.config.js
new file mode 100644
index 000000000..f7a5a7d36
--- /dev/null
+++ b/packages/form/eslint.config.js
@@ -0,0 +1,4 @@
+import baseConfig from "@homarr/eslint-config/base";
+
+/** @type {import('typescript-eslint').Config} */
+export default [...baseConfig];
diff --git a/packages/form/package.json b/packages/form/package.json
index d670b76b6..94f05fc3b 100644
--- a/packages/form/package.json
+++ b/packages/form/package.json
@@ -2,6 +2,7 @@
"name": "@homarr/form",
"private": true,
"version": "0.1.0",
+ "type": "module",
"exports": {
".": "./index.ts"
},
@@ -15,26 +16,21 @@
"license": "MIT",
"scripts": {
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit"
},
+ "dependencies": {
+ "@mantine/form": "^7.11.0",
+ "@homarr/validation": "workspace:^0.1.0",
+ "@homarr/translation": "workspace:^0.1.0"
+ },
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
- "eslint": "^8.57.0",
- "typescript": "^5.4.5"
+ "eslint": "^9.6.0",
+ "typescript": "^5.5.2"
},
- "eslintConfig": {
- "extends": [
- "@homarr/eslint-config/base"
- ]
- },
- "prettier": "@homarr/prettier-config",
- "dependencies": {
- "@mantine/form": "^7.10.0",
- "@homarr/validation": "workspace:^0.1.0",
- "@homarr/translation": "workspace:^0.1.0"
- }
+ "prettier": "@homarr/prettier-config"
}
diff --git a/packages/icons/eslint.config.js b/packages/icons/eslint.config.js
new file mode 100644
index 000000000..f7a5a7d36
--- /dev/null
+++ b/packages/icons/eslint.config.js
@@ -0,0 +1,4 @@
+import baseConfig from "@homarr/eslint-config/base";
+
+/** @type {import('typescript-eslint').Config} */
+export default [...baseConfig];
diff --git a/packages/icons/package.json b/packages/icons/package.json
index f3883cad2..31875734c 100644
--- a/packages/icons/package.json
+++ b/packages/icons/package.json
@@ -16,7 +16,7 @@
"license": "MIT",
"scripts": {
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit"
},
@@ -27,13 +27,8 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
- "eslint": "^8.57.0",
- "typescript": "^5.4.5"
- },
- "eslintConfig": {
- "extends": [
- "@homarr/eslint-config/base"
- ]
+ "eslint": "^9.6.0",
+ "typescript": "^5.5.2"
},
"prettier": "@homarr/prettier-config"
}
diff --git a/packages/icons/src/repositories/github.icon-repository.ts b/packages/icons/src/repositories/github.icon-repository.ts
index 3523bf930..780f280da 100644
--- a/packages/icons/src/repositories/github.icon-repository.ts
+++ b/packages/icons/src/repositories/github.icon-repository.ts
@@ -32,6 +32,7 @@ export class GitHubIconRepository extends IconRepository {
const fileNameWithExtension = this.getFileNameWithoutExtensionFromPath(treeItem.path);
return {
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
imageUrl: new URL(this.repositoryBlobUrlTemplate!.replace("{0}", treeItem.path)),
fileNameWithExtension: fileNameWithExtension,
local: false,
diff --git a/packages/integrations/eslint.config.js b/packages/integrations/eslint.config.js
new file mode 100644
index 000000000..f7a5a7d36
--- /dev/null
+++ b/packages/integrations/eslint.config.js
@@ -0,0 +1,4 @@
+import baseConfig from "@homarr/eslint-config/base";
+
+/** @type {import('typescript-eslint').Config} */
+export default [...baseConfig];
diff --git a/packages/integrations/package.json b/packages/integrations/package.json
index 5df87673e..ec31ba112 100644
--- a/packages/integrations/package.json
+++ b/packages/integrations/package.json
@@ -4,6 +4,7 @@
"version": "0.1.0",
"exports": {
".": "./index.ts",
+ "./client": "./src/client.ts",
"./types": "./src/types.ts"
},
"typesVersions": {
@@ -17,25 +18,23 @@
"type": "module",
"scripts": {
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit"
},
+ "dependencies": {
+ "@homarr/definitions": "workspace:^0.1.0",
+ "@homarr/log": "workspace:^0.1.0",
+ "@homarr/validation": "workspace:^0.1.0",
+ "@homarr/common": "workspace:^0.1.0",
+ "@homarr/translation": "workspace:^0.1.0"
+ },
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
- "eslint": "^8.57.0",
- "typescript": "^5.4.5"
- },
- "dependencies": {
- "@homarr/definitions": "workspace:^0.1.0",
- "@homarr/validation": "workspace:^0.1.0"
- },
- "eslintConfig": {
- "extends": [
- "@homarr/eslint-config/base"
- ]
+ "eslint": "^9.6.0",
+ "typescript": "^5.5.2"
},
"prettier": "@homarr/prettier-config"
}
diff --git a/packages/integrations/src/base/creator.ts b/packages/integrations/src/base/creator.ts
new file mode 100644
index 000000000..50aee2be8
--- /dev/null
+++ b/packages/integrations/src/base/creator.ts
@@ -0,0 +1,16 @@
+import type { IntegrationKind } from "@homarr/definitions";
+
+import { HomeAssistantIntegration } from "../homeassistant/homeassistant-integration";
+import { PiHoleIntegration } from "../pi-hole/pi-hole-integration";
+import type { IntegrationInput } from "./integration";
+
+export const integrationCreatorByKind = (kind: IntegrationKind, integration: IntegrationInput) => {
+ switch (kind) {
+ case "piHole":
+ return new PiHoleIntegration(integration);
+ case "homeAssistant":
+ return new HomeAssistantIntegration(integration);
+ default:
+ throw new Error(`Unknown integration kind ${kind}`);
+ }
+};
diff --git a/packages/integrations/src/base/integration.ts b/packages/integrations/src/base/integration.ts
index e8612736a..69b346e23 100644
--- a/packages/integrations/src/base/integration.ts
+++ b/packages/integrations/src/base/integration.ts
@@ -1,16 +1,25 @@
+import { extractErrorMessage } from "@homarr/common";
import type { IntegrationSecretKind } from "@homarr/definitions";
+import { logger } from "@homarr/log";
+import type { TranslationObject } from "@homarr/translation";
+import { z } from "@homarr/validation";
+import { IntegrationTestConnectionError } from "./test-connection-error";
import type { IntegrationSecret } from "./types";
+const causeSchema = z.object({
+ code: z.string(),
+});
+
+export interface IntegrationInput {
+ id: string;
+ name: string;
+ url: string;
+ decryptedSecrets: IntegrationSecret[];
+}
+
export abstract class Integration {
- constructor(
- protected integration: {
- id: string;
- name: string;
- url: string;
- decryptedSecrets: IntegrationSecret[];
- },
- ) {}
+ constructor(protected integration: IntegrationInput) {}
protected getSecretValue(kind: IntegrationSecretKind) {
const secret = this.integration.decryptedSecrets.find((secret) => secret.kind === kind);
@@ -19,4 +28,87 @@ export abstract class Integration {
}
return secret.value;
}
+
+ /**
+ * Test the connection to the integration
+ * @throws {IntegrationTestConnectionError} if the connection fails
+ */
+ public abstract testConnectionAsync(): Promise;
+
+ protected async handleTestConnectionResponseAsync({
+ queryFunctionAsync,
+ handleResponseAsync,
+ }: {
+ queryFunctionAsync: () => Promise;
+ handleResponseAsync?: (response: Response) => Promise;
+ }): Promise {
+ const response = await queryFunctionAsync().catch((error) => {
+ if (error instanceof Error) {
+ const cause = causeSchema.safeParse(error.cause);
+ if (!cause.success) {
+ logger.error("Failed to test connection", error);
+ throw new IntegrationTestConnectionError("commonError", extractErrorMessage(error));
+ }
+
+ if (cause.data.code === "ENOTFOUND") {
+ logger.error("Failed to test connection: Domain not found");
+ throw new IntegrationTestConnectionError("domainNotFound");
+ }
+
+ if (cause.data.code === "ECONNREFUSED") {
+ logger.error("Failed to test connection: Connection refused");
+ throw new IntegrationTestConnectionError("connectionRefused");
+ }
+
+ if (cause.data.code === "ECONNABORTED") {
+ logger.error("Failed to test connection: Connection aborted");
+ throw new IntegrationTestConnectionError("connectionAborted");
+ }
+ }
+
+ logger.error("Failed to test connection", error);
+
+ throw new IntegrationTestConnectionError("commonError", extractErrorMessage(error));
+ });
+
+ if (response.status >= 400) {
+ logger.error(`Failed to test connection with status code ${response.status}`);
+
+ throwErrorByStatusCode(response.status);
+ }
+
+ await handleResponseAsync?.(response);
+ }
}
+
+export interface TestConnectionError {
+ key: Exclude;
+ message?: string;
+}
+export type TestConnectionResult =
+ | {
+ success: false;
+ error: TestConnectionError;
+ }
+ | {
+ success: true;
+ };
+
+const throwErrorByStatusCode = (statusCode: number) => {
+ switch (statusCode) {
+ case 400:
+ throw new IntegrationTestConnectionError("badRequest");
+ case 401:
+ throw new IntegrationTestConnectionError("unauthorized");
+ case 403:
+ throw new IntegrationTestConnectionError("forbidden");
+ case 404:
+ throw new IntegrationTestConnectionError("notFound");
+ case 500:
+ throw new IntegrationTestConnectionError("internalServerError");
+ case 503:
+ throw new IntegrationTestConnectionError("serviceUnavailable");
+ default:
+ throw new IntegrationTestConnectionError("commonError");
+ }
+};
diff --git a/packages/integrations/src/base/test-connection-error.ts b/packages/integrations/src/base/test-connection-error.ts
new file mode 100644
index 000000000..8a8067f0e
--- /dev/null
+++ b/packages/integrations/src/base/test-connection-error.ts
@@ -0,0 +1,26 @@
+import { FlattenError } from "@homarr/common";
+import { z } from "@homarr/validation";
+
+import type { TestConnectionError } from "./integration";
+
+export class IntegrationTestConnectionError extends FlattenError {
+ constructor(
+ public key: TestConnectionError["key"],
+ public detailMessage?: string,
+ ) {
+ super("Checking integration connection failed", { key, message: detailMessage });
+ }
+}
+
+const schema = z.object({
+ key: z.custom((value) => z.string().parse(value)),
+ message: z.string().optional(),
+});
+export const convertIntegrationTestConnectionError = (error: unknown) => {
+ const result = schema.safeParse(error);
+ if (!result.success) {
+ return;
+ }
+
+ return result.data;
+};
diff --git a/packages/integrations/src/client.ts b/packages/integrations/src/client.ts
new file mode 100644
index 000000000..d62df98bb
--- /dev/null
+++ b/packages/integrations/src/client.ts
@@ -0,0 +1 @@
+export { convertIntegrationTestConnectionError } from "./base/test-connection-error";
diff --git a/packages/integrations/src/homeassistant/homeassistant-integration.ts b/packages/integrations/src/homeassistant/homeassistant-integration.ts
new file mode 100644
index 000000000..47f10ce3e
--- /dev/null
+++ b/packages/integrations/src/homeassistant/homeassistant-integration.ts
@@ -0,0 +1,104 @@
+import { appendPath } from "@homarr/common";
+import { logger } from "@homarr/log";
+
+import { Integration } from "../base/integration";
+import { entityStateSchema } from "./homeassistant-types";
+
+export class HomeAssistantIntegration extends Integration {
+ public async getEntityStateAsync(entityId: string) {
+ try {
+ const response = await this.getAsync(`/api/states/${entityId}`);
+ const body = (await response.json()) as unknown;
+ if (!response.ok) {
+ logger.warn(`Response did not indicate success`);
+ return {
+ error: "Response did not indicate success",
+ };
+ }
+ return entityStateSchema.safeParseAsync(body);
+ } catch (err) {
+ logger.error(`Failed to fetch from ${this.integration.url}: ${err as string}`);
+ return {
+ success: false as const,
+ error: err,
+ };
+ }
+ }
+
+ public async triggerAutomationAsync(entityId: string) {
+ try {
+ const response = await this.postAsync("/api/services/automation/trigger", {
+ entity_id: entityId,
+ });
+
+ return response.ok;
+ } catch (err) {
+ logger.error(`Failed to fetch from '${this.integration.url}': ${err as string}`);
+ return false;
+ }
+ }
+
+ /**
+ * Triggers a toggle action for a specific entity.
+ *
+ * @param entityId - The ID of the entity to toggle.
+ * @returns A boolean indicating whether the toggle action was successful.
+ */
+ public async triggerToggleAsync(entityId: string) {
+ try {
+ const response = await this.postAsync("/api/services/homeassistant/toggle", {
+ entity_id: entityId,
+ });
+
+ return response.ok;
+ } catch (err) {
+ logger.error(`Failed to fetch from '${this.integration.url}': ${err as string}`);
+ return false;
+ }
+ }
+
+ public async testConnectionAsync(): Promise {
+ await super.handleTestConnectionResponseAsync({
+ queryFunctionAsync: async () => {
+ return await this.getAsync("/api/config");
+ },
+ });
+ }
+
+ /**
+ * Makes a GET request to the Home Assistant API.
+ * It includes the authorization header with the API key.
+ * @param path full path to the API endpoint
+ * @returns the response from the API
+ */
+ private async getAsync(path: `/api/${string}`) {
+ return await fetch(appendPath(this.integration.url, path), {
+ headers: this.getAuthHeaders(),
+ });
+ }
+
+ /**
+ * Makes a POST request to the Home Assistant API.
+ * It includes the authorization header with the API key.
+ * @param path full path to the API endpoint
+ * @param body the body of the request
+ * @returns the response from the API
+ */
+ private async postAsync(path: `/api/${string}`, body: Record) {
+ return await fetch(appendPath(this.integration.url, path), {
+ headers: this.getAuthHeaders(),
+ body: JSON.stringify(body),
+ method: "POST",
+ });
+ }
+
+ /**
+ * Returns the headers required for authorization.
+ * @returns the authorization headers
+ */
+ private getAuthHeaders() {
+ return {
+ Authorization: `Bearer ${this.getSecretValue("apiKey")}`,
+ };
+ }
+}
diff --git a/packages/integrations/src/homeassistant/homeassistant-types.ts b/packages/integrations/src/homeassistant/homeassistant-types.ts
new file mode 100644
index 000000000..7015b5e23
--- /dev/null
+++ b/packages/integrations/src/homeassistant/homeassistant-types.ts
@@ -0,0 +1,13 @@
+import { z } from "@homarr/validation";
+
+export const entityStateSchema = z.object({
+ attributes: z.record(
+ z.union([z.string(), z.number(), z.boolean(), z.null(), z.array(z.union([z.string(), z.number()]))]),
+ ),
+ entity_id: z.string(),
+ last_changed: z.string().pipe(z.coerce.date()),
+ last_updated: z.string().pipe(z.coerce.date()),
+ state: z.string(),
+});
+
+export type EntityState = z.infer;
diff --git a/packages/integrations/src/index.ts b/packages/integrations/src/index.ts
index 4740f40bd..fd241bae3 100644
--- a/packages/integrations/src/index.ts
+++ b/packages/integrations/src/index.ts
@@ -1 +1,7 @@
+// General integrations
export { PiHoleIntegration } from "./pi-hole/pi-hole-integration";
+export { HomeAssistantIntegration } from "./homeassistant/homeassistant-integration";
+
+// Helpers
+export { IntegrationTestConnectionError } from "./base/test-connection-error";
+export { integrationCreatorByKind } from "./base/creator";
diff --git a/packages/integrations/src/pi-hole/pi-hole-integration.ts b/packages/integrations/src/pi-hole/pi-hole-integration.ts
index 4b309c07b..c0c61a26c 100644
--- a/packages/integrations/src/pi-hole/pi-hole-integration.ts
+++ b/packages/integrations/src/pi-hole/pi-hole-integration.ts
@@ -1,10 +1,11 @@
import { Integration } from "../base/integration";
+import { IntegrationTestConnectionError } from "../base/test-connection-error";
import type { DnsHoleSummaryIntegration } from "../interfaces/dns-hole-summary/dns-hole-summary-integration";
import type { DnsHoleSummary } from "../interfaces/dns-hole-summary/dns-hole-summary-types";
import { summaryResponseSchema } from "./pi-hole-types";
export class PiHoleIntegration extends Integration implements DnsHoleSummaryIntegration {
- async getSummaryAsync(): Promise {
+ public async getSummaryAsync(): Promise {
const apiKey = super.getSecretValue("apiKey");
const response = await fetch(`${this.integration.url}/admin/api.php?summaryRaw&auth=${apiKey}`);
if (!response.ok) {
@@ -28,4 +29,24 @@ export class PiHoleIntegration extends Integration implements DnsHoleSummaryInte
dnsQueriesToday: result.data.dns_queries_today,
};
}
+
+ public async testConnectionAsync(): Promise {
+ const apiKey = super.getSecretValue("apiKey");
+
+ await super.handleTestConnectionResponseAsync({
+ queryFunctionAsync: async () => {
+ return await fetch(`${this.integration.url}/admin/api.php?status&auth=${apiKey}`);
+ },
+ handleResponseAsync: async (response) => {
+ try {
+ const result = (await response.json()) as unknown;
+ if (typeof result === "object" && result !== null && "status" in result) return;
+ } catch (error) {
+ throw new IntegrationTestConnectionError("invalidJson");
+ }
+
+ throw new IntegrationTestConnectionError("invalidCredentials");
+ },
+ });
+ }
}
diff --git a/packages/integrations/test/base.spec.ts b/packages/integrations/test/base.spec.ts
new file mode 100644
index 000000000..9ef1f3861
--- /dev/null
+++ b/packages/integrations/test/base.spec.ts
@@ -0,0 +1,249 @@
+import { describe, expect, test } from "vitest";
+
+import { IntegrationTestConnectionError } from "../src";
+import { Integration } from "../src/base/integration";
+
+type HandleResponseProps = Parameters[0];
+
+class BaseIntegrationMock extends Integration {
+ public async fakeTestConnectionAsync(props: HandleResponseProps): Promise {
+ await super.handleTestConnectionResponseAsync(props);
+ }
+
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
+ public async testConnectionAsync(): Promise {}
+}
+
+describe("Base integration", () => {
+ describe("handleTestConnectionResponseAsync", () => {
+ test("With no cause error should throw IntegrationTestConnectionError with key commonError", async () => {
+ // Arrange
+ const integration = new BaseIntegrationMock({ id: "id", name: "name", url: "url", decryptedSecrets: [] });
+
+ const errorMessage = "The error message";
+ const props: HandleResponseProps = {
+ async queryFunctionAsync() {
+ return await Promise.reject(new Error(errorMessage));
+ },
+ };
+
+ // Act
+ const actPromise = integration.fakeTestConnectionAsync(props);
+
+ // Assert
+ await expect(actPromise).rejects.toHaveProperty("key", "commonError");
+ await expect(actPromise).rejects.toHaveProperty("detailMessage", errorMessage);
+ });
+
+ test("With cause ENOTFOUND should throw IntegrationTestConnectionError with key domainNotFound", async () => {
+ // Arrange
+ const integration = new BaseIntegrationMock({ id: "id", name: "name", url: "url", decryptedSecrets: [] });
+
+ const props: HandleResponseProps = {
+ async queryFunctionAsync() {
+ return await Promise.reject(new Error("Error", { cause: { code: "ENOTFOUND" } }));
+ },
+ };
+
+ // Act
+ const actAsync = async () => await integration.fakeTestConnectionAsync(props);
+
+ // Assert
+ await expect(actAsync()).rejects.toHaveProperty("key", "domainNotFound");
+ });
+
+ test("With cause ENOTFOUND should throw IntegrationTestConnectionError with key connectionRefused", async () => {
+ // Arrange
+ const integration = new BaseIntegrationMock({ id: "id", name: "name", url: "url", decryptedSecrets: [] });
+
+ const props: HandleResponseProps = {
+ async queryFunctionAsync() {
+ return await Promise.reject(new Error("Error", { cause: { code: "ECONNREFUSED" } }));
+ },
+ };
+
+ // Act
+ const actAsync = async () => await integration.fakeTestConnectionAsync(props);
+
+ // Assert
+ await expect(actAsync()).rejects.toHaveProperty("key", "connectionRefused");
+ });
+
+ test("With cause ENOTFOUND should throw IntegrationTestConnectionError with key connectionAborted", async () => {
+ // Arrange
+ const integration = new BaseIntegrationMock({ id: "id", name: "name", url: "url", decryptedSecrets: [] });
+
+ const props: HandleResponseProps = {
+ async queryFunctionAsync() {
+ return await Promise.reject(new Error("Error", { cause: { code: "ECONNABORTED" } }));
+ },
+ };
+
+ // Act
+ const actAsync = async () => await integration.fakeTestConnectionAsync(props);
+
+ // Assert
+ await expect(actAsync()).rejects.toHaveProperty("key", "connectionAborted");
+ });
+
+ test("With not handled cause error should throw IntegrationTestConnectionError with key commonError", async () => {
+ // Arrange
+ const integration = new BaseIntegrationMock({ id: "id", name: "name", url: "url", decryptedSecrets: [] });
+
+ const errorMessage = "The error message";
+ const props: HandleResponseProps = {
+ async queryFunctionAsync() {
+ return await Promise.reject(new Error(errorMessage));
+ },
+ };
+
+ // Act
+ const actPromise = integration.fakeTestConnectionAsync(props);
+
+ // Assert
+ await expect(actPromise).rejects.toHaveProperty("key", "commonError");
+ await expect(actPromise).rejects.toHaveProperty("detailMessage", errorMessage);
+ });
+
+ test("With response status code 400 should throw IntegrationTestConnectionError with key badRequest", async () => {
+ // Arrange
+ const integration = new BaseIntegrationMock({ id: "id", name: "name", url: "url", decryptedSecrets: [] });
+
+ const props: HandleResponseProps = {
+ async queryFunctionAsync() {
+ return await Promise.resolve(new Response(null, { status: 400 }));
+ },
+ };
+
+ // Act
+ const actAsync = async () => await integration.fakeTestConnectionAsync(props);
+
+ // Assert
+ await expect(actAsync()).rejects.toHaveProperty("key", "badRequest");
+ });
+
+ test("With response status code 401 should throw IntegrationTestConnectionError with key unauthorized", async () => {
+ // Arrange
+ const integration = new BaseIntegrationMock({ id: "id", name: "name", url: "url", decryptedSecrets: [] });
+
+ const props: HandleResponseProps = {
+ async queryFunctionAsync() {
+ return await Promise.resolve(new Response(null, { status: 401 }));
+ },
+ };
+
+ // Act
+ const actAsync = async () => await integration.fakeTestConnectionAsync(props);
+
+ // Assert
+ await expect(actAsync()).rejects.toHaveProperty("key", "unauthorized");
+ });
+
+ test("With response status code 403 should throw IntegrationTestConnectionError with key forbidden", async () => {
+ // Arrange
+ const integration = new BaseIntegrationMock({ id: "id", name: "name", url: "url", decryptedSecrets: [] });
+
+ const props: HandleResponseProps = {
+ async queryFunctionAsync() {
+ return await Promise.resolve(new Response(null, { status: 403 }));
+ },
+ };
+
+ // Act
+ const actAsync = async () => await integration.fakeTestConnectionAsync(props);
+
+ // Assert
+ await expect(actAsync()).rejects.toHaveProperty("key", "forbidden");
+ });
+
+ test("With response status code 404 should throw IntegrationTestConnectionError with key notFound", async () => {
+ // Arrange
+ const integration = new BaseIntegrationMock({ id: "id", name: "name", url: "url", decryptedSecrets: [] });
+
+ const props: HandleResponseProps = {
+ async queryFunctionAsync() {
+ return await Promise.resolve(new Response(null, { status: 404 }));
+ },
+ };
+
+ // Act
+ const actAsync = async () => await integration.fakeTestConnectionAsync(props);
+
+ // Assert
+ await expect(actAsync()).rejects.toHaveProperty("key", "notFound");
+ });
+
+ test("With response status code 500 should throw IntegrationTestConnectionError with key internalServerError", async () => {
+ // Arrange
+ const integration = new BaseIntegrationMock({ id: "id", name: "name", url: "url", decryptedSecrets: [] });
+
+ const props: HandleResponseProps = {
+ async queryFunctionAsync() {
+ return await Promise.resolve(new Response(null, { status: 500 }));
+ },
+ };
+
+ // Act
+ const actAsync = async () => await integration.fakeTestConnectionAsync(props);
+
+ // Assert
+ await expect(actAsync()).rejects.toHaveProperty("key", "internalServerError");
+ });
+
+ test("With response status code 503 should throw IntegrationTestConnectionError with key serviceUnavailable", async () => {
+ // Arrange
+ const integration = new BaseIntegrationMock({ id: "id", name: "name", url: "url", decryptedSecrets: [] });
+
+ const props: HandleResponseProps = {
+ async queryFunctionAsync() {
+ return await Promise.resolve(new Response(null, { status: 503 }));
+ },
+ };
+
+ // Act
+ const actAsync = async () => await integration.fakeTestConnectionAsync(props);
+
+ // Assert
+ await expect(actAsync()).rejects.toHaveProperty("key", "serviceUnavailable");
+ });
+
+ test("With response status code 418 (or any other unhandled code) should throw IntegrationTestConnectionError with key commonError", async () => {
+ // Arrange
+ const integration = new BaseIntegrationMock({ id: "id", name: "name", url: "url", decryptedSecrets: [] });
+
+ const props: HandleResponseProps = {
+ async queryFunctionAsync() {
+ return await Promise.resolve(new Response(null, { status: 418 }));
+ },
+ };
+
+ // Act
+ const actAsync = async () => await integration.fakeTestConnectionAsync(props);
+
+ // Assert
+ await expect(actAsync()).rejects.toHaveProperty("key", "commonError");
+ });
+
+ test("Errors from handleResponseAsync should be thrown", async () => {
+ // Arrange
+ const integration = new BaseIntegrationMock({ id: "id", name: "name", url: "url", decryptedSecrets: [] });
+
+ const errorMessage = "The error message";
+ const props: HandleResponseProps = {
+ async queryFunctionAsync() {
+ return await Promise.resolve(new Response(null, { status: 200 }));
+ },
+ async handleResponseAsync() {
+ return await Promise.reject(new IntegrationTestConnectionError("commonError", errorMessage));
+ },
+ };
+
+ // Act
+ const actPromise = integration.fakeTestConnectionAsync(props);
+
+ // Assert
+ await expect(actPromise).rejects.toHaveProperty("key", "commonError");
+ await expect(actPromise).rejects.toHaveProperty("detailMessage", errorMessage);
+ });
+ });
+});
diff --git a/packages/integrations/test/home-assistant.spec.ts b/packages/integrations/test/home-assistant.spec.ts
new file mode 100644
index 000000000..76d8446e6
--- /dev/null
+++ b/packages/integrations/test/home-assistant.spec.ts
@@ -0,0 +1,81 @@
+import type { StartedTestContainer } from "testcontainers";
+import { GenericContainer, getContainerRuntimeClient, ImageName, Wait } from "testcontainers";
+import { beforeAll, describe, expect, test } from "vitest";
+
+import { HomeAssistantIntegration, IntegrationTestConnectionError } from "../src";
+
+const DEFAULT_API_KEY =
+ "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJkNjQwY2VjNDFjOGU0NGM5YmRlNWQ4ZmFjMjUzYWViZiIsImlhdCI6MTcxODQ3MTE1MSwiZXhwIjoyMDMzODMxMTUxfQ.uQCZ5FZTokipa6N27DtFhLHkwYEXU1LZr0fsVTryL2Q";
+const IMAGE_NAME = "ghcr.io/home-assistant/home-assistant:stable";
+
+describe("Home Assistant integration", () => {
+ beforeAll(async () => {
+ const containerRuntimeClient = await getContainerRuntimeClient();
+ await containerRuntimeClient.image.pull(ImageName.fromString(IMAGE_NAME));
+ }, 100_000);
+
+ test("Test connection should work", async () => {
+ // Arrange
+ const startedContainer = await prepareHomeAssistantContainerAsync();
+ const homeAssistantIntegration = createHomeAssistantIntegration(startedContainer);
+
+ // Act
+ const actAsync = async () => await homeAssistantIntegration.testConnectionAsync();
+
+ // Assert
+ await expect(actAsync()).resolves.not.toThrow();
+
+ // Cleanup
+ await startedContainer.stop();
+ }, 20_000); // Timeout of 20 seconds
+ test("Test connection should fail with wrong credentials", async () => {
+ // Arrange
+ const startedContainer = await prepareHomeAssistantContainerAsync();
+ const homeAssistantIntegration = createHomeAssistantIntegration(startedContainer, "wrong-api-key");
+
+ // Act
+ const actAsync = async () => await homeAssistantIntegration.testConnectionAsync();
+
+ // Assert
+ await expect(actAsync()).rejects.toThrow(IntegrationTestConnectionError);
+
+ // Cleanup
+ await startedContainer.stop();
+ }, 20_000); // Timeout of 20 seconds
+});
+
+const prepareHomeAssistantContainerAsync = async () => {
+ const homeAssistantContainer = createHomeAssistantContainer();
+ const startedContainer = await homeAssistantContainer.start();
+
+ await startedContainer.exec(["unzip", "-o", "/tmp/config.zip", "-d", "/config"]);
+ await startedContainer.restart();
+ return startedContainer;
+};
+
+const createHomeAssistantContainer = () => {
+ return new GenericContainer(IMAGE_NAME)
+ .withCopyFilesToContainer([
+ {
+ source: __dirname + "/volumes/home-assistant-config.zip",
+ target: "/tmp/config.zip",
+ },
+ ])
+ .withPrivilegedMode()
+ .withExposedPorts(8123)
+ .withWaitStrategy(Wait.forHttp("/", 8123));
+};
+
+const createHomeAssistantIntegration = (container: StartedTestContainer, apiKeyOverride?: string) => {
+ return new HomeAssistantIntegration({
+ id: "1",
+ decryptedSecrets: [
+ {
+ kind: "apiKey",
+ value: apiKeyOverride ?? DEFAULT_API_KEY,
+ },
+ ],
+ name: "Home assistant",
+ url: `http://${container.getHost()}:${container.getMappedPort(8123)}`,
+ });
+};
diff --git a/packages/integrations/test/pi-hole.spec.ts b/packages/integrations/test/pi-hole.spec.ts
new file mode 100644
index 000000000..23f8ef425
--- /dev/null
+++ b/packages/integrations/test/pi-hole.spec.ts
@@ -0,0 +1,81 @@
+import type { StartedTestContainer } from "testcontainers";
+import { GenericContainer, Wait } from "testcontainers";
+import { describe, expect, test } from "vitest";
+
+import { PiHoleIntegration } from "../src";
+
+const DEFAULT_PASSWORD = "12341234";
+const DEFAULT_API_KEY = "3b1434980677dcf53fa8c4a611db3b1f0f88478790097515c0abb539102778b9"; // Some hash generated from password
+
+describe("Pi-hole integration", () => {
+ test("getSummaryAsync should return summary from pi-hole", async () => {
+ // Arrange
+ const piholeContainer = await createPiHoleContainer(DEFAULT_PASSWORD).start();
+ const piHoleIntegration = createPiHoleIntegration(piholeContainer, DEFAULT_API_KEY);
+
+ // Act
+ const result = await piHoleIntegration.getSummaryAsync();
+
+ // Assert
+ expect(result.adsBlockedToday).toBe(0);
+ expect(result.adsBlockedTodayPercentage).toBe(0);
+ expect(result.dnsQueriesToday).toBe(0);
+ expect(result.domainsBeingBlocked).toBeGreaterThan(1);
+
+ // Cleanup
+ await piholeContainer.stop();
+ }, 20_000); // Timeout of 20 seconds
+
+ test("testConnectionAsync should not throw", async () => {
+ // Arrange
+ const piholeContainer = await createPiHoleContainer(DEFAULT_PASSWORD).start();
+ const piHoleIntegration = createPiHoleIntegration(piholeContainer, DEFAULT_API_KEY);
+
+ // Act
+ const actAsync = async () => await piHoleIntegration.testConnectionAsync();
+
+ // Assert
+ await expect(actAsync()).resolves.not.toThrow();
+
+ // Cleanup
+ await piholeContainer.stop();
+ }, 20_000); // Timeout of 20 seconds
+
+ test("testConnectionAsync should throw with wrong credentials", async () => {
+ // Arrange
+ const piholeContainer = await createPiHoleContainer(DEFAULT_PASSWORD).start();
+ const piHoleIntegration = createPiHoleIntegration(piholeContainer, "wrong-api-key");
+
+ // Act
+ const actAsync = async () => await piHoleIntegration.testConnectionAsync();
+
+ // Assert
+ await expect(actAsync()).rejects.toThrow();
+
+ // Cleanup
+ await piholeContainer.stop();
+ }, 20_000); // Timeout of 20 seconds
+});
+
+const createPiHoleContainer = (password: string) => {
+ return new GenericContainer("pihole/pihole:latest")
+ .withEnvironment({
+ WEBPASSWORD: password,
+ })
+ .withExposedPorts(80)
+ .withWaitStrategy(Wait.forLogMessage("Pi-hole Enabled"));
+};
+
+const createPiHoleIntegration = (container: StartedTestContainer, apiKey: string) => {
+ return new PiHoleIntegration({
+ id: "1",
+ decryptedSecrets: [
+ {
+ kind: "apiKey",
+ value: apiKey,
+ },
+ ],
+ name: "Pi hole",
+ url: `http://${container.getHost()}:${container.getMappedPort(80)}`,
+ });
+};
diff --git a/packages/integrations/test/volumes/home-assistant-config.zip b/packages/integrations/test/volumes/home-assistant-config.zip
new file mode 100644
index 000000000..e66c5c76b
Binary files /dev/null and b/packages/integrations/test/volumes/home-assistant-config.zip differ
diff --git a/packages/integrations/tsconfig.json b/packages/integrations/tsconfig.json
index cbe8483d9..d4e53f79b 100644
--- a/packages/integrations/tsconfig.json
+++ b/packages/integrations/tsconfig.json
@@ -3,6 +3,6 @@
"compilerOptions": {
"tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
},
- "include": ["*.ts", "src"],
+ "include": ["*.ts", "src", "test"],
"exclude": ["node_modules"]
}
diff --git a/packages/log/eslint.config.js b/packages/log/eslint.config.js
new file mode 100644
index 000000000..f7a5a7d36
--- /dev/null
+++ b/packages/log/eslint.config.js
@@ -0,0 +1,4 @@
+import baseConfig from "@homarr/eslint-config/base";
+
+/** @type {import('typescript-eslint').Config} */
+export default [...baseConfig];
diff --git a/packages/log/package.json b/packages/log/package.json
index 18c403c27..d2d921c90 100644
--- a/packages/log/package.json
+++ b/packages/log/package.json
@@ -2,6 +2,7 @@
"name": "@homarr/log",
"private": true,
"version": "0.1.0",
+ "type": "module",
"exports": {
".": {
"types": "./src/index.d.ts",
@@ -19,7 +20,7 @@
"license": "MIT",
"scripts": {
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit"
},
@@ -32,13 +33,8 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
- "eslint": "^8.57.0",
- "typescript": "^5.4.5"
- },
- "eslintConfig": {
- "extends": [
- "@homarr/eslint-config/base"
- ]
+ "eslint": "^9.6.0",
+ "typescript": "^5.5.2"
},
"prettier": "@homarr/prettier-config"
}
diff --git a/packages/log/src/override.cjs b/packages/log/src/override.cjs
index c8538f602..8d4c5ca94 100644
--- a/packages/log/src/override.cjs
+++ b/packages/log/src/override.cjs
@@ -1,7 +1,6 @@
void (async () => {
const { logger } = await import("./index.mjs");
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires
const nextLogger = require("next/dist/build/output/log");
const getWinstonMethodForConsole = (consoleMethod) => {
@@ -37,9 +36,7 @@ void (async () => {
}
};
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
Object.keys(nextLogger.prefixes).forEach((method) => {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
nextLogger[method] = getWinstonMethodForNext(method);
});
})();
diff --git a/packages/modals/eslint.config.js b/packages/modals/eslint.config.js
new file mode 100644
index 000000000..1df6129af
--- /dev/null
+++ b/packages/modals/eslint.config.js
@@ -0,0 +1,5 @@
+import baseConfig from "@homarr/eslint-config/base";
+import reactConfig from "@homarr/eslint-config/react";
+
+/** @type {import('typescript-eslint').Config} */
+export default [...baseConfig, ...reactConfig];
diff --git a/packages/modals/package.json b/packages/modals/package.json
index 9736781c6..f00dfd9ec 100644
--- a/packages/modals/package.json
+++ b/packages/modals/package.json
@@ -2,6 +2,7 @@
"name": "@homarr/modals",
"private": true,
"version": "0.1.0",
+ "type": "module",
"exports": {
".": "./index.ts"
},
@@ -15,25 +16,23 @@
"license": "MIT",
"scripts": {
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@homarr/ui": "workspace:^0.1.0",
- "@homarr/translation": "workspace:^0.1.0"
+ "@homarr/translation": "workspace:^0.1.0",
+ "react": "^18.3.1",
+ "@mantine/core": "^7.11.0",
+ "@mantine/hooks": "^7.11.0"
},
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
- "eslint": "^8.57.0",
- "typescript": "^5.4.5"
- },
- "eslintConfig": {
- "extends": [
- "@homarr/eslint-config/base"
- ]
+ "eslint": "^9.6.0",
+ "typescript": "^5.5.2"
},
"prettier": "@homarr/prettier-config"
}
diff --git a/packages/modals/src/confirm-modal.tsx b/packages/modals/src/confirm-modal.tsx
index d1434b874..9b26d6508 100644
--- a/packages/modals/src/confirm-modal.tsx
+++ b/packages/modals/src/confirm-modal.tsx
@@ -41,7 +41,7 @@ export const ConfirmModal = createModal>(({ act
const handleCancel = useCallback(
async (event: React.MouseEvent) => {
- typeof cancelProps?.onClick === "function" && cancelProps?.onClick(event);
+ typeof cancelProps?.onClick === "function" && cancelProps.onClick(event);
typeof onCancel === "function" && (await onCancel());
closeOnCancel && actions.closeModal();
},
@@ -51,7 +51,7 @@ export const ConfirmModal = createModal>(({ act
const handleConfirm = useCallback(
async (event: React.MouseEvent) => {
setLoading(true);
- typeof confirmProps?.onClick === "function" && confirmProps?.onClick(event);
+ typeof confirmProps?.onClick === "function" && confirmProps.onClick(event);
typeof onConfirm === "function" && (await onConfirm());
closeOnConfirm && actions.closeModal();
setLoading(false);
@@ -65,11 +65,11 @@ export const ConfirmModal = createModal>(({ act
>
diff --git a/packages/modals/src/index.tsx b/packages/modals/src/index.tsx
index 68876a6b6..f3c9e1386 100644
--- a/packages/modals/src/index.tsx
+++ b/packages/modals/src/index.tsx
@@ -138,6 +138,8 @@ export const useModalAction = (modal: TModal) =>
return {
openModal: (innerProps: inferInnerProps, options: OpenModalOptions | void) => {
+ // void actually is undefined
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
context.openModalInner({ modal, innerProps, options: options ?? {} });
},
};
diff --git a/packages/modals/src/reducer.tsx b/packages/modals/src/reducer.tsx
index ab45cb386..ba22f7297 100644
--- a/packages/modals/src/reducer.tsx
+++ b/packages/modals/src/reducer.tsx
@@ -62,7 +62,7 @@ export const modalReducer = (state: ModalsState, action: OpenAction | CloseActio
const remainingModals = state.modals.filter((modal) => modal.id !== action.modalId);
return {
- current: remainingModals[remainingModals.length - 1] || state.current,
+ current: remainingModals[remainingModals.length - 1] ?? state.current,
modals: remainingModals,
};
}
diff --git a/packages/notifications/eslint.config.js b/packages/notifications/eslint.config.js
new file mode 100644
index 000000000..f7a5a7d36
--- /dev/null
+++ b/packages/notifications/eslint.config.js
@@ -0,0 +1,4 @@
+import baseConfig from "@homarr/eslint-config/base";
+
+/** @type {import('typescript-eslint').Config} */
+export default [...baseConfig];
diff --git a/packages/notifications/package.json b/packages/notifications/package.json
index 211c3d1bc..6bddfb16e 100644
--- a/packages/notifications/package.json
+++ b/packages/notifications/package.json
@@ -2,6 +2,7 @@
"name": "@homarr/notifications",
"private": true,
"version": "0.1.0",
+ "type": "module",
"exports": {
".": "./index.ts",
"./styles.css": "./src/styles.css"
@@ -16,25 +17,21 @@
"license": "MIT",
"scripts": {
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit"
},
+ "dependencies": {
+ "@mantine/notifications": "^7.11.0",
+ "@homarr/ui": "workspace:^0.1.0",
+ "@tabler/icons-react": "^3.7.0"
+ },
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
- "eslint": "^8.57.0",
- "typescript": "^5.4.5"
- },
- "dependencies": {
- "@mantine/notifications": "^7.10.0",
- "@homarr/ui": "workspace:^0.1.0"
- },
- "eslintConfig": {
- "extends": [
- "@homarr/eslint-config/base"
- ]
+ "eslint": "^9.6.0",
+ "typescript": "^5.5.2"
},
"prettier": "@homarr/prettier-config"
}
diff --git a/packages/ping/eslint.config.js b/packages/ping/eslint.config.js
new file mode 100644
index 000000000..f7a5a7d36
--- /dev/null
+++ b/packages/ping/eslint.config.js
@@ -0,0 +1,4 @@
+import baseConfig from "@homarr/eslint-config/base";
+
+/** @type {import('typescript-eslint').Config} */
+export default [...baseConfig];
diff --git a/packages/ping/index.ts b/packages/ping/index.ts
new file mode 100644
index 000000000..3bd16e178
--- /dev/null
+++ b/packages/ping/index.ts
@@ -0,0 +1 @@
+export * from "./src";
diff --git a/packages/ping/package.json b/packages/ping/package.json
new file mode 100644
index 000000000..e9abe68cc
--- /dev/null
+++ b/packages/ping/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "@homarr/ping",
+ "private": true,
+ "version": "0.1.0",
+ "exports": {
+ ".": "./index.ts"
+ },
+ "typesVersions": {
+ "*": {
+ "*": [
+ "src/*"
+ ]
+ }
+ },
+ "license": "MIT",
+ "type": "module",
+ "scripts": {
+ "clean": "rm -rf .turbo node_modules",
+ "lint": "eslint",
+ "format": "prettier --check . --ignore-path ../../.gitignore",
+ "typecheck": "tsc --noEmit"
+ },
+ "dependencies": {
+ "@homarr/common": "workspace:^0.1.0",
+ "@homarr/log": "workspace:^0.1.0"
+ },
+ "devDependencies": {
+ "@homarr/eslint-config": "workspace:^0.2.0",
+ "@homarr/prettier-config": "workspace:^0.1.0",
+ "@homarr/tsconfig": "workspace:^0.1.0",
+ "eslint": "^9.6.0",
+ "typescript": "^5.5.2"
+ },
+ "prettier": "@homarr/prettier-config"
+}
diff --git a/packages/ping/src/index.ts b/packages/ping/src/index.ts
new file mode 100644
index 000000000..3dafac233
--- /dev/null
+++ b/packages/ping/src/index.ts
@@ -0,0 +1,13 @@
+import { extractErrorMessage } from "@homarr/common";
+import { logger } from "@homarr/log";
+
+export const sendPingRequestAsync = async (url: string) => {
+ try {
+ return await fetch(url).then((response) => ({ statusCode: response.status }));
+ } catch (error) {
+ logger.error("packages/ping/src/index.ts:", error);
+ return {
+ error: extractErrorMessage(error),
+ };
+ }
+};
diff --git a/packages/ping/tsconfig.json b/packages/ping/tsconfig.json
new file mode 100644
index 000000000..612bef8df
--- /dev/null
+++ b/packages/ping/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "@homarr/tsconfig/base.json",
+ "compilerOptions": {
+ "types": ["node"],
+ "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json"
+ },
+ "include": ["*.ts", "src"],
+ "exclude": ["node_modules"]
+}
diff --git a/packages/redis/eslint.config.js b/packages/redis/eslint.config.js
new file mode 100644
index 000000000..f7a5a7d36
--- /dev/null
+++ b/packages/redis/eslint.config.js
@@ -0,0 +1,4 @@
+import baseConfig from "@homarr/eslint-config/base";
+
+/** @type {import('typescript-eslint').Config} */
+export default [...baseConfig];
diff --git a/packages/redis/package.json b/packages/redis/package.json
index 90be8eef6..97f559ec6 100644
--- a/packages/redis/package.json
+++ b/packages/redis/package.json
@@ -16,12 +16,13 @@
"type": "module",
"scripts": {
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"ioredis": "5.4.1",
+ "superjson": "2.2.1",
"@homarr/log": "workspace:^",
"@homarr/db": "workspace:^",
"@homarr/common": "workspace:^"
@@ -30,13 +31,8 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
- "eslint": "^8.57.0",
- "typescript": "^5.4.5"
- },
- "eslintConfig": {
- "extends": [
- "@homarr/eslint-config/base"
- ]
+ "eslint": "^9.6.0",
+ "typescript": "^5.5.2"
},
"prettier": "@homarr/prettier-config"
}
diff --git a/packages/redis/src/index.ts b/packages/redis/src/index.ts
index beb5f1c70..78300c3cf 100644
--- a/packages/redis/src/index.ts
+++ b/packages/redis/src/index.ts
@@ -1,8 +1,18 @@
-import { createQueueChannel, createSubPubChannel } from "./lib/channel";
+import { createListChannel, createQueueChannel, createSubPubChannel } from "./lib/channel";
export { createCacheChannel } from "./lib/channel";
export const exampleChannel = createSubPubChannel<{ message: string }>("example");
+export const pingChannel = createSubPubChannel<{ url: string; statusCode: number } | { url: string; error: string }>(
+ "ping",
+);
+export const pingUrlChannel = createListChannel("ping-url");
+
+export const homeAssistantEntityState = createSubPubChannel<{
+ entityId: string;
+ state: string;
+}>("home-assistant/entity-state");
+
export const queueChannel = createQueueChannel<{
name: string;
executionDate: Date;
diff --git a/packages/redis/src/lib/channel.ts b/packages/redis/src/lib/channel.ts
index 270b3c6ce..7a59e6fd7 100644
--- a/packages/redis/src/lib/channel.ts
+++ b/packages/redis/src/lib/channel.ts
@@ -51,14 +51,48 @@ export const createSubPubChannel = (name: string) => {
};
};
-const cacheClient = createRedisConnection();
+const getSetClient = createRedisConnection();
+
+/**
+ * Creates a new redis channel for a list
+ * @param name name of channel
+ * @returns list channel object
+ */
+export const createListChannel = (name: string) => {
+ const listChannelName = `list:${name}`;
+ return {
+ /**
+ * Get all items in list
+ * @returns an array of all items
+ */
+ getAllAsync: async () => {
+ const items = await getSetClient.lrange(listChannelName, 0, -1);
+ return items.map((item) => superjson.parse(item));
+ },
+ /**
+ * Remove an item from the channels list by item
+ * @param item item to remove
+ */
+ removeAsync: async (item: TItem) => {
+ await getSetClient.lrem(listChannelName, 0, superjson.stringify(item));
+ },
+ /**
+ * Add an item to the channels list
+ * @param item item to add
+ */
+ addAsync: async (item: TItem) => {
+ await getSetClient.lpush(listChannelName, superjson.stringify(item));
+ },
+ };
+};
/**
* Creates a new cache channel.
* @param name name of the channel
+ * @param cacheDurationMs duration in milliseconds to cache
* @returns cache channel object
*/
-export const createCacheChannel = (name: string, cacheDurationSeconds: number = 5 * 60 * 1000) => {
+export const createCacheChannel = (name: string, cacheDurationMs: number = 5 * 60 * 1000) => {
const cacheChannelName = `cache:${name}`;
return {
@@ -67,13 +101,13 @@ export const createCacheChannel = (name: string, cacheDurationSeconds: nu
* @returns data or null if not found or expired
*/
getAsync: async () => {
- const data = await cacheClient.get(cacheChannelName);
+ const data = await getSetClient.get(cacheChannelName);
if (!data) return null;
const parsedData = superjson.parse<{ data: TData; timestamp: Date }>(data);
const now = new Date();
const diff = now.getTime() - parsedData.timestamp.getTime();
- if (diff > cacheDurationSeconds) return null;
+ if (diff > cacheDurationMs) return null;
return parsedData;
},
@@ -83,13 +117,13 @@ export const createCacheChannel = (name: string, cacheDurationSeconds: nu
* @returns data or new data if not present or expired
*/
consumeAsync: async (callback: () => Promise) => {
- const data = await cacheClient.get(cacheChannelName);
+ const data = await getSetClient.get(cacheChannelName);
const getNewDataAsync = async () => {
logger.debug(`Cache miss for channel '${cacheChannelName}'`);
const newData = await callback();
const result = { data: newData, timestamp: new Date() };
- await cacheClient.set(cacheChannelName, superjson.stringify(result));
+ await getSetClient.set(cacheChannelName, superjson.stringify(result));
logger.debug(`Cache updated for channel '${cacheChannelName}'`);
return result;
};
@@ -102,7 +136,7 @@ export const createCacheChannel = (name: string, cacheDurationSeconds: nu
const now = new Date();
const diff = now.getTime() - parsedData.timestamp.getTime();
- if (diff > cacheDurationSeconds) {
+ if (diff > cacheDurationMs) {
return await getNewDataAsync();
}
@@ -114,14 +148,14 @@ export const createCacheChannel = (name: string, cacheDurationSeconds: nu
* Invalidate the cache channels data.
*/
invalidateAsync: async () => {
- await cacheClient.del(cacheChannelName);
+ await getSetClient.del(cacheChannelName);
},
/**
* Set the data in the cache channel.
* @param data data to be stored in the cache channel
*/
setAsync: async (data: TData) => {
- await cacheClient.set(cacheChannelName, superjson.stringify({ data, timestamp: new Date() }));
+ await getSetClient.set(cacheChannelName, superjson.stringify({ data, timestamp: new Date() }));
},
};
};
diff --git a/packages/server-settings/eslint.config.js b/packages/server-settings/eslint.config.js
new file mode 100644
index 000000000..f7a5a7d36
--- /dev/null
+++ b/packages/server-settings/eslint.config.js
@@ -0,0 +1,4 @@
+import baseConfig from "@homarr/eslint-config/base";
+
+/** @type {import('typescript-eslint').Config} */
+export default [...baseConfig];
diff --git a/packages/server-settings/package.json b/packages/server-settings/package.json
index 8e0ffc6c8..a7a95bab1 100644
--- a/packages/server-settings/package.json
+++ b/packages/server-settings/package.json
@@ -16,7 +16,7 @@
"license": "MIT",
"scripts": {
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit"
},
@@ -24,13 +24,8 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
- "eslint": "^8.57.0",
- "typescript": "^5.4.5"
- },
- "eslintConfig": {
- "extends": [
- "@homarr/eslint-config/base"
- ]
+ "eslint": "^9.6.0",
+ "typescript": "^5.5.2"
},
"prettier": "@homarr/prettier-config"
}
diff --git a/packages/spotlight/eslint.config.js b/packages/spotlight/eslint.config.js
new file mode 100644
index 000000000..1df6129af
--- /dev/null
+++ b/packages/spotlight/eslint.config.js
@@ -0,0 +1,5 @@
+import baseConfig from "@homarr/eslint-config/base";
+import reactConfig from "@homarr/eslint-config/react";
+
+/** @type {import('typescript-eslint').Config} */
+export default [...baseConfig, ...reactConfig];
diff --git a/packages/spotlight/package.json b/packages/spotlight/package.json
index 286f3c1a6..4d59d6b9c 100644
--- a/packages/spotlight/package.json
+++ b/packages/spotlight/package.json
@@ -2,6 +2,7 @@
"name": "@homarr/spotlight",
"private": true,
"version": "0.1.0",
+ "type": "module",
"exports": {
".": "./index.ts",
"./styles.css": "./src/styles.css"
@@ -16,26 +17,28 @@
"license": "MIT",
"scripts": {
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit"
},
+ "dependencies": {
+ "@homarr/ui": "workspace:^0.1.0",
+ "@homarr/translation": "workspace:^0.1.0",
+ "@mantine/core": "^7.11.0",
+ "@mantine/hooks": "^7.11.0",
+ "@mantine/spotlight": "^7.11.0",
+ "@tabler/icons-react": "^3.7.0",
+ "jotai": "^2.8.4",
+ "next": "^14.2.4",
+ "react": "^18.3.1",
+ "use-deep-compare-effect": "^1.8.1"
+ },
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
- "eslint": "^8.57.0",
- "typescript": "^5.4.5"
+ "eslint": "^9.6.0",
+ "typescript": "^5.5.2"
},
- "eslintConfig": {
- "extends": [
- "@homarr/eslint-config/base"
- ]
- },
- "prettier": "@homarr/prettier-config",
- "dependencies": {
- "@mantine/spotlight": "^7.10.0",
- "@homarr/ui": "workspace:^0.1.0",
- "@homarr/translation": "workspace:^0.1.0"
- }
+ "prettier": "@homarr/prettier-config"
}
diff --git a/packages/translation/eslint.config.js b/packages/translation/eslint.config.js
new file mode 100644
index 000000000..f7a5a7d36
--- /dev/null
+++ b/packages/translation/eslint.config.js
@@ -0,0 +1,4 @@
+import baseConfig from "@homarr/eslint-config/base";
+
+/** @type {import('typescript-eslint').Config} */
+export default [...baseConfig];
diff --git a/packages/translation/package.json b/packages/translation/package.json
index 506e5e048..439cea2f8 100644
--- a/packages/translation/package.json
+++ b/packages/translation/package.json
@@ -2,6 +2,7 @@
"name": "@homarr/translation",
"private": true,
"version": "0.1.0",
+ "type": "module",
"exports": {
".": "./index.ts",
"./client": "./src/client.ts",
@@ -18,24 +19,20 @@
"license": "MIT",
"scripts": {
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit"
},
+ "dependencies": {
+ "dayjs": "^1.11.11",
+ "next-international": "^1.2.4"
+ },
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
- "eslint": "^8.57.0",
- "typescript": "^5.4.5"
+ "eslint": "^9.6.0",
+ "typescript": "^5.5.2"
},
- "eslintConfig": {
- "extends": [
- "@homarr/eslint-config/base"
- ]
- },
- "prettier": "@homarr/prettier-config",
- "dependencies": {
- "next-international": "^1.2.4"
- }
+ "prettier": "@homarr/prettier-config"
}
diff --git a/packages/translation/src/lang/en.ts b/packages/translation/src/lang/en.ts
index fa5d56756..ef44dba64 100644
--- a/packages/translation/src/lang/en.ts
+++ b/packages/translation/src/lang/en.ts
@@ -317,6 +317,17 @@ export default {
},
},
},
+ field: {
+ name: {
+ label: "Name",
+ },
+ description: {
+ label: "Description",
+ },
+ url: {
+ label: "Url",
+ },
+ },
},
integration: {
page: {
@@ -378,7 +389,10 @@ export default {
create: "New integration",
},
testConnection: {
- action: "Test connection",
+ action: {
+ create: "Test connection and create",
+ edit: "Test connection and save",
+ },
alertNotice: "The Save button is enabled once a successful connection is established",
notification: {
success: {
@@ -389,7 +403,7 @@ export default {
title: "Invalid URL",
message: "The URL is invalid",
},
- notAllSecretsProvided: {
+ secretNotDefined: {
title: "Missing credentials",
message: "Not all credentials were provided",
},
@@ -401,6 +415,50 @@ export default {
title: "Connection failed",
message: "The connection could not be established",
},
+ badRequest: {
+ title: "Bad request",
+ message: "The request was malformed",
+ },
+ unauthorized: {
+ title: "Unauthorized",
+ message: "Probably wrong credentials",
+ },
+ forbidden: {
+ title: "Forbidden",
+ message: "Probably missing permissions",
+ },
+ notFound: {
+ title: "Not found",
+ message: "Probably wrong url or path",
+ },
+ internalServerError: {
+ title: "Internal server error",
+ message: "The server encountered an error",
+ },
+ serviceUnavailable: {
+ title: "Service unavailable",
+ message: "The server is currently unavailable",
+ },
+ connectionAborted: {
+ title: "Connection aborted",
+ message: "The connection was aborted",
+ },
+ domainNotFound: {
+ title: "Domain not found",
+ message: "The domain could not be found",
+ },
+ connectionRefused: {
+ title: "Connection refused",
+ message: "The connection was refused",
+ },
+ invalidJson: {
+ title: "Invalid JSON",
+ message: "The response was not valid JSON",
+ },
+ wrongPath: {
+ title: "Wrong path",
+ message: "The path is probably not correct",
+ },
},
},
secrets: {
@@ -429,6 +487,9 @@ export default {
},
common: {
rtl: "{value}{symbol}",
+ symbols: {
+ colon: ": ",
+ },
action: {
add: "Add",
apply: "Apply",
@@ -450,8 +511,13 @@ export default {
tryAgain: "Try again",
},
iconPicker: {
+ label: "Icon URL",
header: "Type name or objects to filter for icons... Homarr will search through {countIcons} icons for you.",
},
+ information: {
+ min: "Min",
+ max: "Max",
+ },
notification: {
create: {
success: "Creation successful",
@@ -630,9 +696,15 @@ export default {
openInNewTab: {
label: "Open in new tab",
},
+ showTitle: {
+ label: "Show app name",
+ },
showDescriptionTooltip: {
label: "Show description tooltip",
},
+ pingEnabled: {
+ label: "Enable simple ping",
+ },
},
error: {
notFound: {
@@ -667,8 +739,8 @@ export default {
internalServerError: "Failed to fetch DNS Hole Summary",
},
data: {
- adsBlockedToday: "blocked today",
- adsBlockedTodayPercentage: "blocked today",
+ adsBlockedToday: "Blocked today",
+ adsBlockedTodayPercentage: "Blocked today",
dnsQueriesToday: "Queries today",
domainsBeingBlocked: "Domains on blocklist",
},
@@ -805,6 +877,36 @@ export default {
noBrowerSupport: "Your Browser does not support iframes. Please update your browser.",
},
},
+ "smartHome-entityState": {
+ name: "Entity State",
+ description: "Display the state of an entity and toggle it optionally",
+ option: {
+ entityId: {
+ label: "Entity ID",
+ },
+ displayName: {
+ label: "Display-name",
+ },
+ entityUnit: {
+ label: "Entity Unit",
+ },
+ clickable: {
+ label: "Clickable",
+ },
+ },
+ },
+ "smartHome-executeAutomation": {
+ name: "Execute Automation",
+ description: "Trigger an automation with one click",
+ option: {
+ displayName: {
+ label: "Display name",
+ },
+ automationId: {
+ label: "Automation ID",
+ },
+ },
+ },
weather: {
name: "Weather",
description: "Displays the current weather information of a set location.",
@@ -1369,4 +1471,41 @@ export default {
},
},
},
+ docker: {
+ title: "Containers",
+ table: {
+ updated: "Updated {when}",
+ search: "Seach {count} containers",
+ selected: "{selectCount} of {totalCount} containers selected",
+ },
+ field: {
+ name: {
+ label: "Name",
+ },
+ state: {
+ label: "State",
+ option: {
+ created: "Created",
+ running: "Running",
+ paused: "Paused",
+ restarting: "Restarting",
+ exited: "Exited",
+ removing: "Removing",
+ dead: "Dead",
+ },
+ },
+ containerImage: {
+ label: "Image",
+ },
+ ports: {
+ label: "Ports",
+ },
+ },
+ action: {
+ start: "Start",
+ stop: "Stop",
+ restart: "Restart",
+ remove: "Remove",
+ },
+ },
} as const;
diff --git a/packages/ui/eslint.config.js b/packages/ui/eslint.config.js
new file mode 100644
index 000000000..1df6129af
--- /dev/null
+++ b/packages/ui/eslint.config.js
@@ -0,0 +1,5 @@
+import baseConfig from "@homarr/eslint-config/base";
+import reactConfig from "@homarr/eslint-config/react";
+
+/** @type {import('typescript-eslint').Config} */
+export default [...baseConfig, ...reactConfig];
diff --git a/packages/ui/package.json b/packages/ui/package.json
index 783154b52..b0c256b87 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -2,6 +2,7 @@
"name": "@homarr/ui",
"private": true,
"version": "0.1.0",
+ "type": "module",
"exports": {
".": "./index.ts",
"./styles.css": "./src/styles.css"
@@ -16,26 +17,27 @@
"license": "MIT",
"scripts": {
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit"
},
+ "dependencies": {
+ "@homarr/log": "workspace:^0.1.0",
+ "@homarr/translation": "workspace:^0.1.0",
+ "@mantine/core": "^7.11.0",
+ "@mantine/dates": "^7.11.0",
+ "@mantine/hooks": "^7.11.0",
+ "@tabler/icons-react": "^3.7.0",
+ "next": "^14.2.4",
+ "react": "^18.3.1"
+ },
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
"@types/css-modules": "^1.0.5",
- "eslint": "^8.57.0",
- "typescript": "^5.4.5"
- },
- "dependencies": {
- "@homarr/log": "workspace:^0.1.0",
- "@homarr/translation": "workspace:^0.1.0"
- },
- "eslintConfig": {
- "extends": [
- "@homarr/eslint-config/base"
- ]
+ "eslint": "^9.6.0",
+ "typescript": "^5.5.2"
},
"prettier": "@homarr/prettier-config"
}
diff --git a/packages/ui/src/components/index.tsx b/packages/ui/src/components/index.tsx
index 425545c50..073965df3 100644
--- a/packages/ui/src/components/index.tsx
+++ b/packages/ui/src/components/index.tsx
@@ -1,8 +1,9 @@
export * from "./count-badge";
+export { OverflowBadge } from "./overflow-badge";
+export { SearchInput } from "./search-input";
export * from "./select-with-description";
export * from "./select-with-description-and-badge";
-export { UserAvatar } from "./user-avatar";
-export { UserAvatarGroup } from "./user-avatar-group";
export { TablePagination } from "./table-pagination";
-export { SearchInput } from "./search-input";
export { TextMultiSelect } from "./text-multi-select";
+export { UserAvatar } from "./user-avatar";
+export { UserAvatarGroup } from "./user-avatar-group";
diff --git a/packages/ui/src/components/overflow-badge.tsx b/packages/ui/src/components/overflow-badge.tsx
new file mode 100644
index 000000000..976f8e2fd
--- /dev/null
+++ b/packages/ui/src/components/overflow-badge.tsx
@@ -0,0 +1,55 @@
+import type { BadgeProps } from "@mantine/core";
+import { ActionIcon, Badge, Group, Popover, Stack } from "@mantine/core";
+
+export function OverflowBadge({
+ data,
+ overflowCount = 3,
+ ...props
+}: {
+ data: string[];
+ overflowCount?: number;
+} & BadgeProps) {
+ const badgeProps = {
+ variant: "default",
+ size: "lg",
+ radius: "sm",
+ ...props,
+ };
+ return (
+
+
+ {data.slice(0, overflowCount).map((item) => (
+
+ {item}
+
+ ))}
+ {data.length > overflowCount && (
+
+
+ +{data.length - overflowCount}
+
+
+ )}
+
+
+
+ {data.slice(overflowCount).map((item) => (
+
+ {item}
+
+ ))}
+
+
+
+ );
+}
diff --git a/packages/ui/src/components/text-multi-select.tsx b/packages/ui/src/components/text-multi-select.tsx
index e63d570dc..5561d7baf 100644
--- a/packages/ui/src/components/text-multi-select.tsx
+++ b/packages/ui/src/components/text-multi-select.tsx
@@ -72,6 +72,7 @@ export const TextMultiSelect = ({ label, value = [], onChange, onBlur, onFocus,
onKeyDown={(event) => {
if (event.key === "Backspace" && search.length === 0) {
event.preventDefault();
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
handleValueRemove(value.at(-1)!);
}
}}
diff --git a/packages/validation/eslint.config.js b/packages/validation/eslint.config.js
new file mode 100644
index 000000000..f7a5a7d36
--- /dev/null
+++ b/packages/validation/eslint.config.js
@@ -0,0 +1,4 @@
+import baseConfig from "@homarr/eslint-config/base";
+
+/** @type {import('typescript-eslint').Config} */
+export default [...baseConfig];
diff --git a/packages/validation/package.json b/packages/validation/package.json
index fa45fba35..9bb563bf5 100644
--- a/packages/validation/package.json
+++ b/packages/validation/package.json
@@ -17,26 +17,21 @@
"license": "MIT",
"scripts": {
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit"
},
+ "dependencies": {
+ "zod": "^3.23.8",
+ "@homarr/definitions": "workspace:^0.1.0",
+ "@homarr/translation": "workspace:^0.1.0"
+ },
"devDependencies": {
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
- "eslint": "^8.57.0",
- "typescript": "^5.4.5"
+ "eslint": "^9.6.0",
+ "typescript": "^5.5.2"
},
- "eslintConfig": {
- "extends": [
- "@homarr/eslint-config/base"
- ]
- },
- "prettier": "@homarr/prettier-config",
- "dependencies": {
- "zod": "^3.23.8",
- "@homarr/definitions": "workspace:^0.1.0",
- "@homarr/translation": "workspace:^0.1.0"
- }
+ "prettier": "@homarr/prettier-config"
}
diff --git a/packages/validation/src/enums.ts b/packages/validation/src/enums.ts
index f9e59ef89..988198adc 100644
--- a/packages/validation/src/enums.ts
+++ b/packages/validation/src/enums.ts
@@ -3,7 +3,9 @@ import { z } from "zod";
type CouldBeReadonlyArray = T[] | readonly T[];
export const zodEnumFromArray = (array: CouldBeReadonlyArray) =>
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
z.enum([array[0]!, ...array.slice(1)]);
export const zodUnionFromArray = (array: CouldBeReadonlyArray) =>
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
z.union([array[0]!, array[1]!, ...array.slice(2)]);
diff --git a/packages/validation/src/form/i18n.ts b/packages/validation/src/form/i18n.ts
index 08f97e988..5a8e4f65d 100644
--- a/packages/validation/src/form/i18n.ts
+++ b/packages/validation/src/form/i18n.ts
@@ -13,7 +13,7 @@ export const zodErrorMap = <
const error = handleZodError(issue, ctx);
if ("message" in error && error.message)
return {
- message: error.message ?? ctx.defaultError,
+ message: error.message,
};
return {
message: t(error.key ? `common.zod.${error.key}` : "common.zod.errors.default", error.params ?? {}),
diff --git a/packages/widgets/eslint.config.js b/packages/widgets/eslint.config.js
new file mode 100644
index 000000000..1df6129af
--- /dev/null
+++ b/packages/widgets/eslint.config.js
@@ -0,0 +1,5 @@
+import baseConfig from "@homarr/eslint-config/base";
+import reactConfig from "@homarr/eslint-config/react";
+
+/** @type {import('typescript-eslint').Config} */
+export default [...baseConfig, ...reactConfig];
diff --git a/packages/widgets/package.json b/packages/widgets/package.json
index 42ffa2132..0bffd4a68 100644
--- a/packages/widgets/package.json
+++ b/packages/widgets/package.json
@@ -2,6 +2,7 @@
"name": "@homarr/widgets",
"private": true,
"version": "0.1.0",
+ "type": "module",
"exports": {
".": "./index.ts",
"./errors": "./src/errors/component.tsx"
@@ -16,25 +17,10 @@
"license": "MIT",
"scripts": {
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit"
},
- "devDependencies": {
- "@homarr/eslint-config": "workspace:^0.2.0",
- "@homarr/prettier-config": "workspace:^0.1.0",
- "@homarr/tsconfig": "workspace:^0.1.0",
- "@types/prismjs": "^1.26.4",
- "@types/video.js": "^7.3.58",
- "eslint": "^8.57.0",
- "typescript": "^5.4.5"
- },
- "eslintConfig": {
- "extends": [
- "@homarr/eslint-config/base"
- ]
- },
- "prettier": "@homarr/prettier-config",
"dependencies": {
"@homarr/api": "workspace:^0.1.0",
"@homarr/common": "workspace:^0.1.0",
@@ -48,6 +34,9 @@
"@homarr/translation": "workspace:^0.1.0",
"@homarr/ui": "workspace:^0.1.0",
"@homarr/validation": "workspace:^0.1.0",
+ "@mantine/hooks": "^7.11.0",
+ "@mantine/core": "^7.11.0",
+ "@tabler/icons-react": "^3.7.0",
"@tiptap/extension-color": "2.4.0",
"@tiptap/extension-highlight": "2.4.0",
"@tiptap/extension-image": "2.4.0",
@@ -63,8 +52,19 @@
"@tiptap/extension-underline": "2.4.0",
"@tiptap/react": "^2.4.0",
"@tiptap/starter-kit": "^2.4.0",
- "prismjs": "^1.29.0",
- "react-simple-code-editor": "^0.13.1",
+ "clsx": "^2.1.1",
+ "dayjs": "^1.11.11",
+ "next": "^14.2.4",
+ "react": "^18.3.1",
"video.js": "^8.12.0"
- }
+ },
+ "devDependencies": {
+ "@homarr/eslint-config": "workspace:^0.2.0",
+ "@homarr/prettier-config": "workspace:^0.1.0",
+ "@homarr/tsconfig": "workspace:^0.1.0",
+ "@types/video.js": "^7.3.58",
+ "eslint": "^9.6.0",
+ "typescript": "^5.5.2"
+ },
+ "prettier": "@homarr/prettier-config"
}
diff --git a/packages/widgets/src/_inputs/widget-multiselect-input.tsx b/packages/widgets/src/_inputs/widget-multiselect-input.tsx
index 9f3db9e92..6793dade3 100644
--- a/packages/widgets/src/_inputs/widget-multiselect-input.tsx
+++ b/packages/widgets/src/_inputs/widget-multiselect-input.tsx
@@ -20,7 +20,7 @@ export const WidgetMultiSelectInput = ({ property, kind, options }: CommonWidget
? option
: {
value: option.value,
- label: translateIfNecessary(t, option.label)!,
+ label: translateIfNecessary(t, option.label) ?? option.value,
},
)}
description={options.withDescription ? t("description") : undefined}
diff --git a/packages/widgets/src/_inputs/widget-select-input.tsx b/packages/widgets/src/_inputs/widget-select-input.tsx
index fd1df1d8f..ec07b2932 100644
--- a/packages/widgets/src/_inputs/widget-select-input.tsx
+++ b/packages/widgets/src/_inputs/widget-select-input.tsx
@@ -36,7 +36,7 @@ export const WidgetSelectInput = ({ property, kind, options }: CommonWidgetInput
? option
: {
value: option.value,
- label: translateIfNecessary(t, option.label)!,
+ label: translateIfNecessary(t, option.label) ?? option.value,
},
)}
description={options.withDescription ? tWidget("description") : undefined}
diff --git a/packages/widgets/src/app/component.tsx b/packages/widgets/src/app/component.tsx
index 85754a0cf..bff9ad604 100644
--- a/packages/widgets/src/app/component.tsx
+++ b/packages/widgets/src/app/component.tsx
@@ -1,9 +1,12 @@
"use client";
import type { PropsWithChildren } from "react";
-import { Center, Flex, Loader, Stack, Text, Tooltip, UnstyledButton } from "@mantine/core";
+import { useState } from "react";
+import { Box, Center, Flex, Loader, Stack, Text, Tooltip, UnstyledButton } from "@mantine/core";
import { IconDeviceDesktopX } from "@tabler/icons-react";
+import combineClasses from "clsx";
+import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import { useRegisterSpotlightActions } from "@homarr/spotlight";
import { useScopedI18n } from "@homarr/translation/client";
@@ -11,7 +14,7 @@ import { useScopedI18n } from "@homarr/translation/client";
import type { WidgetComponentProps } from "../definition";
import classes from "./app.module.css";
-export default function AppWidget({ options, serverData, isEditMode, width, height }: WidgetComponentProps<"app">) {
+export default function AppWidget({ options, serverData, isEditMode, width }: WidgetComponentProps<"app">) {
const t = useScopedI18n("widget.app");
const isQueryEnabled = Boolean(options.appId);
const {
@@ -25,7 +28,7 @@ export default function AppWidget({ options, serverData, isEditMode, width, heig
{
initialData:
// We need to check if the id's match because otherwise the same initialData for a changed id will be used
- serverData?.app?.id === options.appId ? serverData?.app : undefined,
+ serverData?.app?.id === options.appId ? serverData.app : undefined,
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
@@ -33,18 +36,31 @@ export default function AppWidget({ options, serverData, isEditMode, width, heig
},
);
+ const [pingResult, setPingResult] = useState(null);
+
+ const shouldRunPing = Boolean(app?.href) && options.pingEnabled;
+ clientApi.widget.app.updatedPing.useSubscription(
+ { url: app?.href ?? "" },
+ {
+ enabled: shouldRunPing,
+ onData(data) {
+ setPingResult(data);
+ },
+ },
+ );
+
useRegisterSpotlightActions(
`app-${options.appId}`,
app?.href
? [
{
id: `app-${options.appId}`,
- title: app?.name,
- description: app?.description ?? "",
- icon: app?.iconUrl,
+ title: app.name,
+ description: app.description ?? "",
+ icon: app.iconUrl,
group: "app",
type: "link",
- href: app?.href,
+ href: app.href,
openInNewTab: options.openInNewTab,
},
]
@@ -77,33 +93,31 @@ export default function AppWidget({ options, serverData, isEditMode, width, heig
return (
-
-
+
-
- {height >= 96 && (
-
- {app?.name}
-
- )}
-
-
-
-
+ {options.showTitle && (
+
+ {app?.name}
+
+ )}
+
+
+
+ {shouldRunPing && }
);
}
@@ -122,3 +136,31 @@ const AppLink = ({ href, openInNewTab, enabled, children }: PropsWithChildren {
+ return (
+
+
+ = 500
+ ? "red"
+ : "green",
+ }}
+ w={16}
+ h={16}
+ >
+
+
+ );
+};
diff --git a/packages/widgets/src/app/index.ts b/packages/widgets/src/app/index.ts
index 4eb1d5440..cfa202e5c 100644
--- a/packages/widgets/src/app/index.ts
+++ b/packages/widgets/src/app/index.ts
@@ -8,7 +8,9 @@ export const { definition, componentLoader, serverDataLoader } = createWidgetDef
options: optionsBuilder.from((factory) => ({
appId: factory.app(),
openInNewTab: factory.switch({ defaultValue: true }),
+ showTitle: factory.switch({ defaultValue: true }),
showDescriptionTooltip: factory.switch({ defaultValue: false }),
+ pingEnabled: factory.switch({ defaultValue: false }),
})),
})
.withServerData(() => import("./serverData"))
diff --git a/packages/widgets/src/app/serverData.ts b/packages/widgets/src/app/serverData.ts
index a6df69678..d4d995f2c 100644
--- a/packages/widgets/src/app/serverData.ts
+++ b/packages/widgets/src/app/serverData.ts
@@ -1,14 +1,25 @@
"use server";
+import type { RouterOutputs } from "@homarr/api";
import { api } from "@homarr/api/server";
import type { WidgetProps } from "../definition";
export default async function getServerDataAsync({ options }: WidgetProps<"app">) {
+ if (!options.appId) {
+ return { app: null, pingResult: null };
+ }
+
try {
const app = await api.app.byId({ id: options.appId });
- return { app };
+ let pingResult: RouterOutputs["widget"]["app"]["ping"] | null = null;
+
+ if (app.href && options.pingEnabled) {
+ pingResult = await api.widget.app.ping({ url: app.href });
+ }
+
+ return { app, pingResult };
} catch (error) {
- return { app: null };
+ return { app: null, pingResult: null };
}
}
diff --git a/packages/widgets/src/app/test/serverData.spec.ts b/packages/widgets/src/app/test/serverData.spec.ts
new file mode 100644
index 000000000..2834eab58
--- /dev/null
+++ b/packages/widgets/src/app/test/serverData.spec.ts
@@ -0,0 +1,129 @@
+import { TRPCError } from "@trpc/server";
+import { describe, expect, test, vi } from "vitest";
+
+import type { RouterOutputs } from "@homarr/api";
+import { api } from "@homarr/api/server";
+import { objectKeys } from "@homarr/common";
+
+import type { WidgetProps } from "../../definition";
+import getServerDataAsync from "../serverData";
+
+const mockApp = (override: Partial) =>
+ ({
+ id: "1",
+ name: "Mock app",
+ iconUrl: "https://some.com/icon.png",
+ description: null,
+ href: "https://google.ch",
+ ...override,
+ }) satisfies RouterOutputs["app"]["byId"];
+
+vi.mock("@homarr/api/server", () => ({
+ api: {
+ app: {
+ byId: () => null,
+ },
+ widget: {
+ app: {
+ ping: () => null,
+ },
+ },
+ },
+}));
+
+describe("getServerDataAsync should load app and ping result", () => {
+ test("when appId is empty it should return null for app and pingResult", async () => {
+ // Arrange
+ const options = {
+ appId: "",
+ pingEnabled: true,
+ };
+
+ // Act
+ const result = await getServerDataAsync({ options } as unknown as WidgetProps<"app">);
+
+ // Assert
+ expect(result.app).toBeNull();
+ expect(result.pingResult).toBeNull();
+ });
+
+ test("when app exists and ping is disabled it should return existing app and pingResult null", async () => {
+ // Arrange
+ const spy = vi.spyOn(api.app, "byId");
+ const options = {
+ appId: "1",
+ pingEnabled: false,
+ };
+ const mockedApp = mockApp({});
+ spy.mockImplementation(() => Promise.resolve(mockedApp));
+
+ // Act
+ const result = await getServerDataAsync({ options } as unknown as WidgetProps<"app">);
+
+ // Assert
+ expect(result.pingResult).toBeNull();
+ objectKeys(mockedApp).forEach((key) => expect(result.app?.[key]).toBe(mockedApp[key]));
+ });
+
+ test("when app exists without href and ping enabled it should return existing app and pingResult null", async () => {
+ // Arrange
+ const spy = vi.spyOn(api.app, "byId");
+ const options = {
+ appId: "1",
+ pingEnabled: true,
+ };
+ const mockedApp = mockApp({ href: null });
+ spy.mockImplementation(() => Promise.resolve(mockedApp));
+
+ // Act
+ const result = await getServerDataAsync({ options } as unknown as WidgetProps<"app">);
+
+ // Assert
+ expect(result.pingResult).toBeNull();
+ objectKeys(mockedApp).forEach((key) => expect(result.app?.[key]).toBe(mockedApp[key]));
+ });
+
+ test("when app does not exist it should return for both null", async () => {
+ // Arrange
+ const spy = vi.spyOn(api.app, "byId");
+ const options = {
+ appId: "1",
+ pingEnabled: true,
+ };
+ spy.mockImplementation(() =>
+ Promise.reject(
+ new TRPCError({
+ code: "NOT_FOUND",
+ }),
+ ),
+ );
+
+ // Act
+ const result = await getServerDataAsync({ options } as unknown as WidgetProps<"app">);
+
+ // Assert
+ expect(result.pingResult).toBeNull();
+ expect(result.app).toBeNull();
+ });
+
+ test("when app found and ping enabled it should return existing app and pingResult", async () => {
+ // Arrange
+ const spyById = vi.spyOn(api.app, "byId");
+ const spyPing = vi.spyOn(api.widget.app, "ping");
+ const options = {
+ appId: "1",
+ pingEnabled: true,
+ };
+ const mockedApp = mockApp({});
+ const pingResult = { statusCode: 200, url: "http://localhost" };
+ spyById.mockImplementation(() => Promise.resolve(mockedApp));
+ spyPing.mockImplementation(() => Promise.resolve(pingResult));
+
+ // Act
+ const result = await getServerDataAsync({ options } as unknown as WidgetProps<"app">);
+
+ // Assert
+ expect(result.pingResult).toBe(pingResult);
+ expect(result.app).toBe(mockedApp);
+ });
+});
diff --git a/packages/widgets/src/clock/component.tsx b/packages/widgets/src/clock/component.tsx
index ce3969714..0d09fae0a 100644
--- a/packages/widgets/src/clock/component.tsx
+++ b/packages/widgets/src/clock/component.tsx
@@ -1,7 +1,7 @@
"use client";
import { useEffect, useMemo, useRef, useState } from "react";
-import { Flex, Stack, Text } from "@mantine/core";
+import { Stack, Text } from "@mantine/core";
import dayjs from "dayjs";
import advancedFormat from "dayjs/plugin/advancedFormat";
import timezones from "dayjs/plugin/timezone";
@@ -20,21 +20,21 @@ export default function ClockWidget({ options }: WidgetComponentProps<"clock">)
const timezone = options.useCustomTimezone ? options.timezone : Intl.DateTimeFormat().resolvedOptions().timeZone;
const time = useCurrentTime(options);
return (
-
-
- {options.customTitleToggle && (
- {options.customTitle}
- )}
-
- {dayjs(time).tz(timezone).format(timeFormat)}
+
+ {options.customTitleToggle && (
+
+ {options.customTitle}
- {options.showDate && (
-
- {dayjs(time).tz(timezone).format(dateFormat)}
-
- )}
-
-
+ )}
+
+ {dayjs(time).tz(timezone).format(timeFormat)}
+
+ {options.showDate && (
+
+ {dayjs(time).tz(timezone).format(dateFormat)}
+
+ )}
+
);
}
diff --git a/packages/widgets/src/dns-hole/summary/component.tsx b/packages/widgets/src/dns-hole/summary/component.tsx
index 915e6a2bd..8eb1a8a02 100644
--- a/packages/widgets/src/dns-hole/summary/component.tsx
+++ b/packages/widgets/src/dns-hole/summary/component.tsx
@@ -1,7 +1,7 @@
"use client";
import type { BoxProps } from "@mantine/core";
-import { Box, Card, Center, Flex, Text } from "@mantine/core";
+import { Box, Card, Flex, Text } from "@mantine/core";
import { useElementSize } from "@mantine/hooks";
import { IconBarrierBlock, IconPercentage, IconSearch, IconWorldWww } from "@tabler/icons-react";
@@ -93,35 +93,46 @@ const StatCard = ({ item, data, usePiHoleColors }: StatCardProps) => {
return (
-
-
-
-
-
- {item.value(data, t)}
+
+
+
+
+ {item.value(data, t)}
+
+ {item.label && (
+
+ {translateIfNecessary(t, item.label)}
- {item.label && (
-
- {translateIfNecessary(t, item.label)}
-
- )}
-
+ )}
-
+
);
};
diff --git a/packages/widgets/src/index.tsx b/packages/widgets/src/index.tsx
index 98c071668..3049fae66 100644
--- a/packages/widgets/src/index.tsx
+++ b/packages/widgets/src/index.tsx
@@ -12,6 +12,8 @@ import * as dnsHoleSummary from "./dns-hole/summary";
import * as iframe from "./iframe";
import type { WidgetImportRecord } from "./import";
import * as notebook from "./notebook";
+import * as smartHomeEntityState from "./smart-home/entity-state";
+import * as smartHomeExecuteAutomation from "./smart-home/execute-automation";
import * as video from "./video";
import * as weather from "./weather";
@@ -29,10 +31,14 @@ export const widgetImports = {
iframe,
video,
dnsHoleSummary,
+ "smartHome-entityState": smartHomeEntityState,
+ "smartHome-executeAutomation": smartHomeExecuteAutomation,
} satisfies WidgetImportRecord;
export type WidgetImports = typeof widgetImports;
export type WidgetImportKey = keyof WidgetImports;
+export type { WidgetComponentProps };
+export type { WidgetDefinition } from "./definition";
const loadedComponents = new Map>>();
diff --git a/packages/widgets/src/modals/widget-edit-modal.tsx b/packages/widgets/src/modals/widget-edit-modal.tsx
index 10dc57a02..4330e2752 100644
--- a/packages/widgets/src/modals/widget-edit-modal.tsx
+++ b/packages/widgets/src/modals/widget-edit-modal.tsx
@@ -62,6 +62,7 @@ export const WidgetEditModal = createModal>(({ actions, i
{Object.entries(definition.options).map(([key, value]: [string, OptionsBuilderResult[string]]) => {
const Input = getInputForType(value.type);
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!Input || value.shouldHide?.(form.values.options as never)) {
return null;
}
diff --git a/packages/widgets/src/notebook/notebook.tsx b/packages/widgets/src/notebook/notebook.tsx
index e7967d81f..7ca935bc6 100644
--- a/packages/widgets/src/notebook/notebook.tsx
+++ b/packages/widgets/src/notebook/notebook.tsx
@@ -303,7 +303,9 @@ export function Notebook({ options, isEditMode, boardId, itemId }: WidgetCompone
- {(editor?.isActive("taskList") || editor?.isActive("bulletList") || editor?.isActive("orderedList")) && (
+ {(Boolean(editor?.isActive("taskList")) ||
+ Boolean(editor?.isActive("bulletList")) ||
+ Boolean(editor?.isActive("orderedList"))) && (
<>
@@ -680,7 +682,7 @@ function ListIndentIncrease() {
}, [editor, itemType]);
editor?.on("selectionUpdate", ({ editor }) => {
- setItemType(editor?.isActive("taskItem") ? "taskItem" : "listItem");
+ setItemType(editor.isActive("taskItem") ? "taskItem" : "listItem");
});
return (
@@ -704,7 +706,7 @@ function ListIndentDecrease() {
}, [editor, itemType]);
editor?.on("selectionUpdate", ({ editor }) => {
- setItemType(editor?.isActive("taskItem") ? "taskItem" : "listItem");
+ setItemType(editor.isActive("taskItem") ? "taskItem" : "listItem");
});
return (
diff --git a/packages/widgets/src/smart-home/entity-state/component.tsx b/packages/widgets/src/smart-home/entity-state/component.tsx
new file mode 100644
index 000000000..c7e590b71
--- /dev/null
+++ b/packages/widgets/src/smart-home/entity-state/component.tsx
@@ -0,0 +1,76 @@
+"use client";
+
+import React, { useState } from "react";
+import { Center, Stack, Text, UnstyledButton } from "@mantine/core";
+
+import { clientApi } from "@homarr/api/client";
+
+import type { WidgetComponentProps } from "../../definition";
+
+export default function SmartHomeEntityStateWidget({
+ options,
+ integrationIds,
+ isEditMode,
+}: WidgetComponentProps<"smartHome-entityState">) {
+ const [lastState, setLastState] = useState<{
+ entityId: string;
+ state: string;
+ }>();
+
+ const utils = clientApi.useUtils();
+
+ clientApi.widget.smartHome.subscribeEntityState.useSubscription(
+ {
+ entityId: options.entityId,
+ },
+ {
+ onData(data) {
+ setLastState(data);
+ },
+ },
+ );
+
+ const { mutate } = clientApi.widget.smartHome.switchEntity.useMutation({
+ onSettled: () => {
+ void utils.widget.smartHome.invalidate();
+ },
+ });
+
+ const attribute = options.entityUnit.length > 0 ? " " + options.entityUnit : "";
+
+ const handleClick = React.useCallback(() => {
+ if (isEditMode) {
+ return;
+ }
+
+ if (!options.clickable) {
+ return;
+ }
+
+ mutate({
+ entityId: options.entityId,
+ integrationId: integrationIds[0] ?? "",
+ });
+ }, []);
+
+ return (
+
+
+
+
+ {options.displayName}
+
+
+ {lastState?.state}
+ {attribute}
+
+
+
+
+ );
+}
diff --git a/packages/widgets/src/smart-home/entity-state/index.ts b/packages/widgets/src/smart-home/entity-state/index.ts
new file mode 100644
index 000000000..300188e5f
--- /dev/null
+++ b/packages/widgets/src/smart-home/entity-state/index.ts
@@ -0,0 +1,19 @@
+import { IconBinaryTree } from "@tabler/icons-react";
+
+import { createWidgetDefinition } from "../../definition";
+import { optionsBuilder } from "../../options";
+
+export const { definition, componentLoader } = createWidgetDefinition("smartHome-entityState", {
+ icon: IconBinaryTree,
+ options: optionsBuilder.from((factory) => ({
+ entityId: factory.text({
+ defaultValue: "sun.sun",
+ }),
+ displayName: factory.text({
+ defaultValue: "Sun",
+ }),
+ entityUnit: factory.text(),
+ clickable: factory.switch(),
+ })),
+ supportedIntegrations: ["homeAssistant"],
+}).withDynamicImport(() => import("./component"));
diff --git a/packages/widgets/src/smart-home/execute-automation/component.tsx b/packages/widgets/src/smart-home/execute-automation/component.tsx
new file mode 100644
index 000000000..f05417e60
--- /dev/null
+++ b/packages/widgets/src/smart-home/execute-automation/component.tsx
@@ -0,0 +1,55 @@
+import React from "react";
+import { ActionIcon, Center, LoadingOverlay, Overlay, Stack, Text, UnstyledButton } from "@mantine/core";
+import { useDisclosure, useTimeout } from "@mantine/hooks";
+import { IconAutomation, IconCheck } from "@tabler/icons-react";
+
+import { clientApi } from "@homarr/api/client";
+
+import type { WidgetComponentProps } from "../../definition";
+
+export default function SmartHomeTriggerAutomationWidget({
+ options,
+ integrationIds,
+ isEditMode,
+}: WidgetComponentProps<"smartHome-executeAutomation">) {
+ const [isShowSuccess, { open: showSuccess, close: closeSuccess }] = useDisclosure();
+ const { start } = useTimeout(() => {
+ closeSuccess();
+ }, 1000);
+
+ const { mutateAsync, isPending } = clientApi.widget.smartHome.executeAutomation.useMutation({
+ onSuccess: () => {
+ showSuccess();
+ start();
+ },
+ });
+ const handleClick = React.useCallback(async () => {
+ if (isEditMode) {
+ return;
+ }
+ await mutateAsync({
+ automationId: options.automationId,
+ integrationId: integrationIds[0] ?? "",
+ });
+ }, [isEditMode]);
+ return (
+
+ {isShowSuccess && (
+
+
+
+
+
+
+
+ )}
+
+
+
+
+ {options.displayName}
+
+
+
+ );
+}
diff --git a/packages/widgets/src/smart-home/execute-automation/index.ts b/packages/widgets/src/smart-home/execute-automation/index.ts
new file mode 100644
index 000000000..a1996e9fe
--- /dev/null
+++ b/packages/widgets/src/smart-home/execute-automation/index.ts
@@ -0,0 +1,13 @@
+import { IconBinaryTree } from "@tabler/icons-react";
+
+import { createWidgetDefinition } from "../../definition";
+import { optionsBuilder } from "../../options";
+
+export const { definition, componentLoader } = createWidgetDefinition("smartHome-executeAutomation", {
+ icon: IconBinaryTree,
+ options: optionsBuilder.from((factory) => ({
+ displayName: factory.text(),
+ automationId: factory.text(),
+ })),
+ supportedIntegrations: ["homeAssistant"],
+}).withDynamicImport(() => import("./component"));
diff --git a/packages/widgets/src/weather/component.tsx b/packages/widgets/src/weather/component.tsx
index cbe2ad5f4..d7ad12484 100644
--- a/packages/widgets/src/weather/component.tsx
+++ b/packages/widgets/src/weather/component.tsx
@@ -1,13 +1,15 @@
-import { Card, Flex, Group, Stack, Text, Title } from "@mantine/core";
+import { Box, Group, HoverCard, Space, Stack, Text } from "@mantine/core";
import { IconArrowDownRight, IconArrowUpRight, IconMapPin } from "@tabler/icons-react";
+import combineClasses from "clsx";
+import dayjs from "dayjs";
import type { RouterOutputs } from "@homarr/api";
import { clientApi } from "@homarr/api/client";
import type { WidgetComponentProps } from "../definition";
-import { WeatherIcon } from "./icon";
+import { WeatherDescription, WeatherIcon } from "./icon";
-export default function WeatherWidget({ options, width }: WidgetComponentProps<"weather">) {
+export default function WeatherWidget({ options }: WidgetComponentProps<"weather">) {
const [weather] = clientApi.widget.weather.atLocation.useSuspenseQuery(
{
latitude: options.location.latitude,
@@ -21,113 +23,123 @@ export default function WeatherWidget({ options, width }: WidgetComponentProps<"
);
return (
-
-
-
+
+ {options.hasForecast ? (
+
+ ) : (
+
+ )}
);
}
-interface DailyWeatherProps extends Pick, "width" | "options"> {
- shouldHide: boolean;
+interface WeatherProps extends Pick, "options"> {
weather: RouterOutputs["widget"]["weather"]["atLocation"];
}
-const DailyWeather = ({ shouldHide, width, options, weather }: DailyWeatherProps) => {
- if (shouldHide) {
- return null;
- }
-
+const DailyWeather = ({ options, weather }: WeatherProps) => {
return (
<>
-
-
- {getPreferredUnit(weather.current_weather.temperature, options.isFormatFahrenheit)}
-
-
- {width > 200 && (
-
-
- {getPreferredUnit(weather.daily.temperature_2m_max[0]!, options.isFormatFahrenheit)}
-
- {getPreferredUnit(weather.daily.temperature_2m_min[0]!, options.isFormatFahrenheit)}
-
- )}
-
+
+
+
+
+
+
+
+
+
+
+
+ {getPreferredUnit(weather.current.temperature, options.isFormatFahrenheit)}
+
+
+
+
+ {getPreferredUnit(weather.daily[0]?.maxTemp, options.isFormatFahrenheit)}
+
+
+ {getPreferredUnit(weather.daily[0]?.minTemp, options.isFormatFahrenheit)}
+
{options.showCity && (
-
-
- {options.location.name}
-
+ <>
+
+
+
+
+ {options.location.name}
+
+
+ >
)}
>
);
};
-interface WeeklyForecastProps extends Pick, "width" | "options"> {
- shouldHide: boolean;
- weather: RouterOutputs["widget"]["weather"]["atLocation"];
-}
-
-const WeeklyForecast = ({ shouldHide, width, options, weather }: WeeklyForecastProps) => {
- if (shouldHide) {
- return null;
- }
-
+const WeeklyForecast = ({ options, weather }: WeatherProps) => {
return (
<>
-
+
{options.showCity && (
-
-
-
+ <>
+
+
{options.location.name}
-
+
+ >
)}
-
- 20 ? "red" : "blue"}>
- {getPreferredUnit(weather.current_weather.temperature, options.isFormatFahrenheit)}
-
-
-
+
+
+
+
+
+
+
+
+
+
+ {getPreferredUnit(weather.current.temperature, options.isFormatFahrenheit)}
+
+
+
>
);
};
-interface ForecastProps extends Pick, "options" | "width"> {
- weather: RouterOutputs["widget"]["weather"]["atLocation"];
-}
-
-function Forecast({ weather, options, width }: ForecastProps) {
+function Forecast({ weather, options }: WeatherProps) {
return (
-
- {weather.daily.time
- .slice(0, Math.min(options.forecastDayCount, width / (width < 300 ? 64 : 92)))
- .map((time, index) => (
-
-
-
- {new Date(time).getDate().toString().padStart(2, "0")}
-
-
-
- {getPreferredUnit(weather.daily.temperature_2m_max[index]!, options.isFormatFahrenheit)}
-
-
- {getPreferredUnit(weather.daily.temperature_2m_min[index]!, options.isFormatFahrenheit)}
-
-
-
- ))}
-
+
+ {weather.daily.slice(0, options.forecastDayCount).map((dayWeather, index) => (
+
+
+
+ {dayjs(dayWeather.time).format("dd")}
+
+ {getPreferredUnit(dayWeather.maxTemp, options.isFormatFahrenheit)}
+
+
+
+
+
+
+ ))}
+
);
}
-const getPreferredUnit = (value: number, isFahrenheit = false): string =>
- isFahrenheit ? `${(value * (9 / 5) + 32).toFixed(1)}°F` : `${value.toFixed(1)}°C`;
+const getPreferredUnit = (value?: number, isFahrenheit = false): string =>
+ value ? (isFahrenheit ? `${(value * (9 / 5) + 32).toFixed(1)}°F` : `${value.toFixed(1)}°C`) : "?";
diff --git a/packages/widgets/src/weather/icon.tsx b/packages/widgets/src/weather/icon.tsx
index 71d9dc87b..25a25b34d 100644
--- a/packages/widgets/src/weather/icon.tsx
+++ b/packages/widgets/src/weather/icon.tsx
@@ -1,4 +1,4 @@
-import { Box, Tooltip } from "@mantine/core";
+import { Stack, Text } from "@mantine/core";
import {
IconCloud,
IconCloudFog,
@@ -9,6 +9,7 @@ import {
IconSnowflake,
IconSun,
} from "@tabler/icons-react";
+import dayjs from "dayjs";
import type { TranslationObject } from "@homarr/translation";
import { useScopedI18n } from "@homarr/translation/client";
@@ -16,26 +17,64 @@ import type { TablerIcon } from "@homarr/ui";
interface WeatherIconProps {
code: number;
- size?: number;
+ size?: string | number;
}
/**
* Icon which should be displayed when specific code is defined
* @param code weather code from api
- * @returns weather tile component
+ * @param size size of the icon, accepts relative sizes too
+ * @returns Icon corresponding to the weather code
*/
export const WeatherIcon = ({ code, size = 50 }: WeatherIconProps) => {
+ const { icon: Icon } = weatherDefinitions.find((definition) => definition.codes.includes(code)) ?? unknownWeather;
+
+ return ;
+};
+
+interface WeatherDescriptionProps {
+ weatherOnly?: boolean;
+ time?: string;
+ weatherCode: number;
+ maxTemp?: string;
+ minTemp?: string;
+}
+
+/**
+ * Description Dropdown for a given set of parameters
+ * @param time date that can be formatted by dayjs
+ * @param weatherCode weather code from api
+ * @param maxTemp preformatted string for max temperature
+ * @param minTemp preformatted string for min temperature
+ * @returns Content for a HoverCard dropdown presenting weather information
+ */
+export const WeatherDescription = ({ weatherOnly, time, weatherCode, maxTemp, minTemp }: WeatherDescriptionProps) => {
const t = useScopedI18n("widget.weather");
+ const tCommon = useScopedI18n("common");
+
+ const { name } = weatherDefinitions.find((definition) => definition.codes.includes(weatherCode)) ?? unknownWeather;
- const { icon: Icon, name } =
- weatherDefinitions.find((definition) => definition.codes.includes(code)) ?? unknownWeather;
+ if (weatherOnly) {
+ return {t(`kind.${name}`)};
+ }
return (
-
-
-
-
-
+
+ {dayjs(time).format("dddd MMMM D YYYY")}
+ {t(`kind.${name}`)}
+
+ {tCommon("rtl", {
+ value: tCommon("information.max"),
+ symbol: tCommon("symbols.colon"),
+ }) + maxTemp}
+
+
+ {tCommon("rtl", {
+ value: tCommon("information.min"),
+ symbol: tCommon("symbols.colon"),
+ }) + minTemp}
+
+
);
};
diff --git a/packages/widgets/src/widget-integration-select.tsx b/packages/widgets/src/widget-integration-select.tsx
index 5014034e0..39bef4d2d 100644
--- a/packages/widgets/src/widget-integration-select.tsx
+++ b/packages/widgets/src/widget-integration-select.tsx
@@ -98,6 +98,7 @@ export const WidgetIntegrationSelect = ({
if (event.key !== "Backspace") return;
event.preventDefault();
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
handleValueRemove(multiSelectValues[multiSelectValues.length - 1]!);
}}
/>
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d59f7573d..b3e91ca65 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -7,32 +7,19 @@ settings:
importers:
.:
- dependencies:
- '@mantine/core':
- specifier: ^7.10.0
- version: 7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- '@mantine/dates':
- specifier: ^7.10.0
- version: 7.10.0(@mantine/core@7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.10.0(react@18.3.1))(dayjs@1.11.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- '@tabler/icons-react':
- specifier: ^3.5.0
- version: 3.5.0(react@18.3.1)
- mantine-react-table:
- specifier: 2.0.0-beta.3
- version: 2.0.0-beta.3(@mantine/core@7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/dates@7.10.0(@mantine/core@7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.10.0(react@18.3.1))(dayjs@1.11.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.10.0(react@18.3.1))(@tabler/icons-react@3.5.0(react@18.3.1))(clsx@2.1.1)(dayjs@1.11.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
devDependencies:
'@homarr/prettier-config':
specifier: workspace:^0.1.0
version: link:tooling/prettier
'@turbo/gen':
- specifier: ^1.13.3
- version: 1.13.3(@types/node@20.12.12)(typescript@5.4.5)
+ specifier: ^2.0.6
+ version: 2.0.6(@types/node@20.14.9)(typescript@5.5.2)
'@vitejs/plugin-react':
- specifier: ^4.3.0
- version: 4.3.0(vite@5.2.6(@types/node@20.12.12)(sass@1.77.2)(sugarss@4.0.1))
+ specifier: ^4.3.1
+ version: 4.3.1(vite@5.2.6(@types/node@20.14.9)(sass@1.77.6)(sugarss@4.0.1)(terser@5.31.0))
'@vitest/coverage-v8':
specifier: ^1.6.0
- version: 1.6.0(vitest@1.6.0(@types/node@20.12.12)(@vitest/ui@1.6.0)(jsdom@24.1.0)(sass@1.77.2)(sugarss@4.0.1))
+ version: 1.6.0(vitest@1.6.0(@types/node@20.14.9)(@vitest/ui@1.6.0)(jsdom@24.1.0)(sass@1.77.6)(sugarss@4.0.1)(terser@5.31.0))
'@vitest/ui':
specifier: ^1.6.0
version: 1.6.0(vitest@1.6.0)
@@ -43,20 +30,23 @@ importers:
specifier: ^24.1.0
version: 24.1.0
prettier:
- specifier: ^3.2.5
- version: 3.2.5
+ specifier: ^3.3.2
+ version: 3.3.2
+ testcontainers:
+ specifier: ^10.10.0
+ version: 10.10.0
turbo:
- specifier: ^1.13.3
- version: 1.13.3
+ specifier: ^2.0.6
+ version: 2.0.6
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
vite-tsconfig-paths:
specifier: ^4.3.2
- version: 4.3.2(typescript@5.4.5)(vite@5.2.6(@types/node@20.12.12)(sass@1.77.2)(sugarss@4.0.1))
+ version: 4.3.2(typescript@5.5.2)(vite@5.2.6(@types/node@20.14.9)(sass@1.77.6)(sugarss@4.0.1)(terser@5.31.0))
vitest:
specifier: ^1.6.0
- version: 1.6.0(@types/node@20.12.12)(@vitest/ui@1.6.0)(jsdom@24.1.0)(sass@1.77.2)(sugarss@4.0.1)
+ version: 1.6.0(@types/node@20.14.9)(@vitest/ui@1.6.0)(jsdom@24.1.0)(sass@1.77.6)(sugarss@4.0.1)(terser@5.31.0)
apps/nextjs:
dependencies:
@@ -83,7 +73,7 @@ importers:
version: link:../../packages/form
'@homarr/gridstack':
specifier: ^1.0.0
- version: 1.0.2
+ version: 1.0.0
'@homarr/integrations':
specifier: workspace:^0.1.0
version: link:../../packages/integrations
@@ -115,41 +105,47 @@ importers:
specifier: workspace:^0.1.0
version: link:../../packages/widgets
'@mantine/colors-generator':
- specifier: ^7.10.0
- version: 7.10.0(chroma-js@2.4.2)
+ specifier: ^7.11.0
+ version: 7.11.0(chroma-js@2.4.2)
+ '@mantine/core':
+ specifier: ^7.11.0
+ version: 7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@mantine/hooks':
- specifier: ^7.10.0
- version: 7.10.0(react@18.3.1)
+ specifier: ^7.11.0
+ version: 7.11.0(react@18.3.1)
'@mantine/modals':
- specifier: ^7.10.0
- version: 7.10.0(@mantine/core@7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.10.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ specifier: ^7.11.0
+ version: 7.11.0(@mantine/core@7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.11.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@mantine/tiptap':
- specifier: ^7.10.0
- version: 7.10.0(@mantine/core@7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.10.0(react@18.3.1))(@tiptap/extension-link@2.4.0(@tiptap/core@2.4.0(@tiptap/pm@2.2.4))(@tiptap/pm@2.2.4))(@tiptap/react@2.4.0(@tiptap/core@2.4.0(@tiptap/pm@2.2.4))(@tiptap/pm@2.2.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ specifier: ^7.11.0
+ version: 7.11.0(@mantine/core@7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.11.0(react@18.3.1))(@tiptap/extension-link@2.4.0(@tiptap/core@2.4.0(@tiptap/pm@2.2.4))(@tiptap/pm@2.2.4))(@tiptap/react@2.4.0(@tiptap/core@2.4.0(@tiptap/pm@2.2.4))(@tiptap/pm@2.2.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@t3-oss/env-nextjs':
specifier: ^0.10.1
- version: 0.10.1(typescript@5.4.5)(zod@3.23.8)
+ version: 0.10.1(typescript@5.5.2)(zod@3.23.8)
+ '@tabler/icons-react':
+ specifier: ^3.7.0
+ version: 3.7.0(react@18.3.1)
'@tanstack/react-query':
- specifier: ^5.40.0
- version: 5.40.0(react@18.3.1)
+ specifier: ^5.48.0
+ version: 5.48.0(react@18.3.1)
'@tanstack/react-query-devtools':
- specifier: ^5.40.0
- version: 5.40.0(@tanstack/react-query@5.40.0(react@18.3.1))(react@18.3.1)
+ specifier: ^5.48.0
+ version: 5.48.0(@tanstack/react-query@5.48.0(react@18.3.1))(react@18.3.1)
'@tanstack/react-query-next-experimental':
- specifier: 5.40.0
- version: 5.40.0(@tanstack/react-query@5.40.0(react@18.3.1))(next@14.2.3(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.2))(react@18.3.1)
+ specifier: 5.48.0
+ version: 5.48.0(@tanstack/react-query@5.48.0(react@18.3.1))(next@14.2.4(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1)
'@trpc/client':
- specifier: 11.0.0-rc.377
- version: 11.0.0-rc.377(@trpc/server@11.0.0-rc.382)
+ specifier: next
+ version: 11.0.0-rc.421(@trpc/server@11.0.0-rc.421)
'@trpc/next':
specifier: next
- version: 11.0.0-rc.382(@tanstack/react-query@5.40.0(react@18.3.1))(@trpc/client@11.0.0-rc.377(@trpc/server@11.0.0-rc.382))(@trpc/react-query@11.0.0-rc.382(@tanstack/react-query@5.40.0(react@18.3.1))(@trpc/client@11.0.0-rc.377(@trpc/server@11.0.0-rc.382))(@trpc/server@11.0.0-rc.382)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@trpc/server@11.0.0-rc.382)(next@14.2.3(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ version: 11.0.0-rc.421(@tanstack/react-query@5.48.0(react@18.3.1))(@trpc/client@11.0.0-rc.421(@trpc/server@11.0.0-rc.421))(@trpc/react-query@11.0.0-rc.421(@tanstack/react-query@5.48.0(react@18.3.1))(@trpc/client@11.0.0-rc.421(@trpc/server@11.0.0-rc.421))(@trpc/server@11.0.0-rc.421)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@trpc/server@11.0.0-rc.421)(next@14.2.4(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@trpc/react-query':
specifier: next
- version: 11.0.0-rc.382(@tanstack/react-query@5.40.0(react@18.3.1))(@trpc/client@11.0.0-rc.377(@trpc/server@11.0.0-rc.382))(@trpc/server@11.0.0-rc.382)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ version: 11.0.0-rc.421(@tanstack/react-query@5.48.0(react@18.3.1))(@trpc/client@11.0.0-rc.421(@trpc/server@11.0.0-rc.421))(@trpc/server@11.0.0-rc.421)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@trpc/server':
specifier: next
- version: 11.0.0-rc.382
+ version: 11.0.0-rc.421
'@xterm/addon-canvas':
specifier: ^0.7.0
version: 0.7.0(@xterm/xterm@5.5.0)
@@ -162,6 +158,9 @@ importers:
chroma-js:
specifier: ^2.4.2
version: 2.4.2
+ clsx:
+ specifier: ^2.1.1
+ version: 2.1.1
dayjs:
specifier: ^1.11.11
version: 1.11.11
@@ -169,32 +168,41 @@ importers:
specifier: ^16.4.5
version: 16.4.5
flag-icons:
- specifier: ^7.2.2
- version: 7.2.2
+ specifier: ^7.2.3
+ version: 7.2.3
glob:
- specifier: ^10.4.1
- version: 10.4.1
+ specifier: ^10.4.2
+ version: 10.4.2
jotai:
- specifier: ^2.8.2
- version: 2.8.2(@types/react@18.3.3)(react@18.3.1)
+ specifier: ^2.8.4
+ version: 2.8.4(@types/react@18.3.3)(react@18.3.1)
+ mantine-react-table:
+ specifier: 2.0.0-beta.5
+ version: 2.0.0-beta.5(@mantine/core@7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/dates@7.11.0(@mantine/core@7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.11.0(react@18.3.1))(dayjs@1.11.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.11.0(react@18.3.1))(@tabler/icons-react@3.7.0(react@18.3.1))(clsx@2.1.1)(dayjs@1.11.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next:
- specifier: ^14.2.3
- version: 14.2.3(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.2)
+ specifier: ^14.2.4
+ version: 14.2.4(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6)
postcss-preset-mantine:
specifier: ^1.15.0
version: 1.15.0(postcss@8.4.38)
+ prismjs:
+ specifier: ^1.29.0
+ version: 1.29.0
react:
- specifier: 18.3.1
+ specifier: ^18.3.1
version: 18.3.1
react-dom:
- specifier: 18.3.1
+ specifier: ^18.3.1
version: 18.3.1(react@18.3.1)
react-error-boundary:
specifier: ^4.0.13
version: 4.0.13(react@18.3.1)
+ react-simple-code-editor:
+ specifier: ^0.13.1
+ version: 0.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
sass:
- specifier: ^1.77.2
- version: 1.77.2
+ specifier: ^1.77.6
+ version: 1.77.6
superjson:
specifier: 2.2.1
version: 2.2.1
@@ -215,8 +223,11 @@ importers:
specifier: 2.4.4
version: 2.4.4
'@types/node':
- specifier: ^20.12.12
- version: 20.12.12
+ specifier: ^20.14.9
+ version: 20.14.9
+ '@types/prismjs':
+ specifier: ^1.26.4
+ version: 1.26.4
'@types/react':
specifier: ^18.3.3
version: 18.3.3
@@ -227,17 +238,17 @@ importers:
specifier: ^8.2.2
version: 8.2.2
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
+ node-loader:
+ specifier: ^2.0.0
+ version: 2.0.0(webpack@5.91.0)
prettier:
- specifier: ^3.2.5
- version: 3.2.5
- tsx:
- specifier: 4.11.0
- version: 4.11.0
+ specifier: ^3.3.2
+ version: 3.3.2
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
apps/tasks:
dependencies:
@@ -247,6 +258,9 @@ importers:
'@homarr/common':
specifier: workspace:^0.1.0
version: link:../../packages/common
+ '@homarr/cron-jobs-core':
+ specifier: workspace:^0.1.0
+ version: link:../../packages/cron-jobs-core
'@homarr/db':
specifier: workspace:^0.1.0
version: link:../../packages/db
@@ -256,9 +270,15 @@ importers:
'@homarr/icons':
specifier: workspace:^0.1.0
version: link:../../packages/icons
+ '@homarr/integrations':
+ specifier: workspace:^0.1.0
+ version: link:../../packages/integrations
'@homarr/log':
specifier: workspace:^
version: link:../../packages/log
+ '@homarr/ping':
+ specifier: workspace:^0.1.0
+ version: link:../../packages/ping
'@homarr/redis':
specifier: workspace:^0.1.0
version: link:../../packages/redis
@@ -268,15 +288,18 @@ importers:
'@homarr/validation':
specifier: workspace:^0.1.0
version: link:../../packages/validation
+ '@homarr/widgets':
+ specifier: workspace:^0.1.0
+ version: link:../../packages/widgets
dotenv:
specifier: ^16.4.5
version: 16.4.5
- node-cron:
- specifier: ^3.0.3
- version: 3.0.3
superjson:
specifier: 2.2.1
version: 2.2.1
+ undici:
+ specifier: 6.19.2
+ version: 6.19.2
devDependencies:
'@homarr/eslint-config':
specifier: workspace:^0.2.0
@@ -288,26 +311,23 @@ importers:
specifier: workspace:^0.1.0
version: link:../../tooling/typescript
'@types/node':
- specifier: ^20.12.12
- version: 20.12.12
- '@types/node-cron':
- specifier: ^3.0.11
- version: 3.0.11
+ specifier: ^20.14.9
+ version: 20.14.9
dotenv-cli:
specifier: ^7.4.2
version: 7.4.2
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
prettier:
- specifier: ^3.2.5
- version: 3.2.5
+ specifier: ^3.3.2
+ version: 3.3.2
tsx:
- specifier: 4.11.0
- version: 4.11.0
+ specifier: 4.13.3
+ version: 4.13.3
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
apps/websocket:
dependencies:
@@ -338,9 +358,12 @@ importers:
dotenv:
specifier: ^16.4.5
version: 16.4.5
+ tsx:
+ specifier: 4.13.3
+ version: 4.13.3
ws:
- specifier: ^8.17.0
- version: 8.17.0
+ specifier: ^8.17.1
+ version: 8.17.1
devDependencies:
'@homarr/eslint-config':
specifier: workspace:^0.2.0
@@ -355,17 +378,26 @@ importers:
specifier: ^8.5.10
version: 8.5.10
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
prettier:
- specifier: ^3.2.5
- version: 3.2.5
+ specifier: ^3.3.2
+ version: 3.3.2
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
packages/analytics:
dependencies:
+ '@homarr/db':
+ specifier: workspace:^0.1.0
+ version: link:../db
+ '@homarr/log':
+ specifier: workspace:^0.1.0
+ version: link:../log
+ '@homarr/server-settings':
+ specifier: workspace:^0.1.0
+ version: link:../server-settings
'@umami/node':
specifier: ^0.3.0
version: 0.3.0
@@ -373,30 +405,21 @@ importers:
specifier: 2.2.1
version: 2.2.1
devDependencies:
- '@homarr/db':
- specifier: workspace:^0.1.0
- version: link:../db
'@homarr/eslint-config':
specifier: workspace:^0.2.0
version: link:../../tooling/eslint
- '@homarr/log':
- specifier: workspace:^0.1.0
- version: link:../log
'@homarr/prettier-config':
specifier: workspace:^0.1.0
version: link:../../tooling/prettier
- '@homarr/server-settings':
- specifier: workspace:^0.1.0
- version: link:../server-settings
'@homarr/tsconfig':
specifier: workspace:^0.1.0
version: link:../../tooling/typescript
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
packages/api:
dependencies:
@@ -418,24 +441,36 @@ importers:
'@homarr/log':
specifier: workspace:^
version: link:../log
+ '@homarr/ping':
+ specifier: workspace:^0.1.0
+ version: link:../ping
'@homarr/redis':
specifier: workspace:^0.1.0
version: link:../redis
'@homarr/server-settings':
specifier: workspace:^0.1.0
version: link:../server-settings
- '@homarr/tasks':
- specifier: workspace:^0.1.0
- version: link:../../apps/tasks
'@homarr/validation':
specifier: workspace:^0.1.0
version: link:../validation
'@trpc/client':
specifier: next
- version: 11.0.0-rc.382(@trpc/server@11.0.0-rc.382)
+ version: 11.0.0-rc.421(@trpc/server@11.0.0-rc.421)
+ '@trpc/react-query':
+ specifier: next
+ version: 11.0.0-rc.421(@tanstack/react-query@5.48.0(react@18.3.1))(@trpc/client@11.0.0-rc.421(@trpc/server@11.0.0-rc.421))(@trpc/server@11.0.0-rc.421)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@trpc/server':
specifier: next
- version: 11.0.0-rc.382
+ version: 11.0.0-rc.421
+ dockerode:
+ specifier: ^4.0.2
+ version: 4.0.2
+ next:
+ specifier: ^14.2.4
+ version: 14.2.4(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6)
+ react:
+ specifier: ^18.3.1
+ version: 18.3.1
superjson:
specifier: 2.2.1
version: 2.2.1
@@ -449,30 +484,33 @@ importers:
'@homarr/tsconfig':
specifier: workspace:^0.1.0
version: link:../../tooling/typescript
+ '@types/dockerode':
+ specifier: ^3.3.29
+ version: 3.3.29
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
prettier:
- specifier: ^3.2.5
- version: 3.2.5
+ specifier: ^3.3.2
+ version: 3.3.2
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
packages/auth:
dependencies:
'@auth/core':
- specifier: ^0.31.0
- version: 0.31.0
+ specifier: ^0.34.1
+ version: 0.34.1
'@auth/drizzle-adapter':
- specifier: ^1.1.0
- version: 1.1.0
+ specifier: ^1.4.1
+ version: 1.4.1
'@homarr/db':
specifier: workspace:^0.1.0
version: link:../db
'@t3-oss/env-nextjs':
specifier: ^0.10.1
- version: 0.10.1(typescript@5.4.5)(zod@3.23.8)
+ version: 0.10.1(typescript@5.5.2)(zod@3.23.8)
bcrypt:
specifier: ^5.1.1
version: 5.1.1
@@ -480,16 +518,16 @@ importers:
specifier: ^0.9.1
version: 0.9.1
next:
- specifier: ^14.2.3
- version: 14.2.3(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.2)
+ specifier: ^14.2.4
+ version: 14.2.4(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6)
next-auth:
- specifier: 5.0.0-beta.18
- version: 5.0.0-beta.18(next@14.2.3(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.2))(react@18.3.1)
+ specifier: 5.0.0-beta.19
+ version: 5.0.0-beta.19(next@14.2.4(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1)
react:
- specifier: 18.3.1
+ specifier: ^18.3.1
version: 18.3.1
react-dom:
- specifier: 18.3.1
+ specifier: ^18.3.1
version: 18.3.1(react@18.3.1)
devDependencies:
'@homarr/definitions':
@@ -514,16 +552,51 @@ importers:
specifier: 0.9.0
version: 0.9.0
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
prettier:
- specifier: ^3.2.5
- version: 3.2.5
+ specifier: ^3.3.2
+ version: 3.3.2
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
packages/common:
+ dependencies:
+ dayjs:
+ specifier: ^1.11.11
+ version: 1.11.11
+ next:
+ specifier: ^14.2.4
+ version: 14.2.4(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6)
+ react:
+ specifier: ^18.3.1
+ version: 18.3.1
+ devDependencies:
+ '@homarr/eslint-config':
+ specifier: workspace:^0.2.0
+ version: link:../../tooling/eslint
+ '@homarr/prettier-config':
+ specifier: workspace:^0.1.0
+ version: link:../../tooling/prettier
+ '@homarr/tsconfig':
+ specifier: workspace:^0.1.0
+ version: link:../../tooling/typescript
+ eslint:
+ specifier: ^9.6.0
+ version: 9.6.0
+ typescript:
+ specifier: ^5.5.2
+ version: 5.5.2
+
+ packages/cron-jobs-core:
+ dependencies:
+ '@homarr/common':
+ specifier: workspace:^0.1.0
+ version: link:../common
+ node-cron:
+ specifier: ^3.0.3
+ version: 3.0.3
devDependencies:
'@homarr/eslint-config':
specifier: workspace:^0.2.0
@@ -534,15 +607,21 @@ importers:
'@homarr/tsconfig':
specifier: workspace:^0.1.0
version: link:../../tooling/typescript
+ '@types/node-cron':
+ specifier: ^3.0.11
+ version: 3.0.11
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
packages/db:
dependencies:
+ '@auth/core':
+ specifier: ^0.34.1
+ version: 0.34.1
'@homarr/common':
specifier: workspace:^0.1.0
version: link:../common
@@ -556,17 +635,20 @@ importers:
specifier: ^2.2.2
version: 2.2.2
better-sqlite3:
- specifier: ^10.0.0
- version: 10.0.0
+ specifier: ^11.1.1
+ version: 11.1.1
+ dotenv:
+ specifier: ^16.4.5
+ version: 16.4.5
drizzle-kit:
- specifier: ^0.21.4
- version: 0.21.4
+ specifier: ^0.22.7
+ version: 0.22.7
drizzle-orm:
- specifier: ^0.30.10
- version: 0.30.10(@types/better-sqlite3@7.6.10)(@types/react@18.3.3)(better-sqlite3@10.0.0)(mysql2@3.9.8)(react@18.3.1)
+ specifier: ^0.31.2
+ version: 0.31.2(@types/better-sqlite3@7.6.10)(@types/react@18.3.3)(better-sqlite3@11.1.1)(mysql2@3.10.1)(react@18.3.1)
mysql2:
- specifier: 3.9.8
- version: 3.9.8
+ specifier: 3.10.1
+ version: 3.10.1
devDependencies:
'@homarr/eslint-config':
specifier: workspace:^0.2.0
@@ -584,14 +666,14 @@ importers:
specifier: ^7.4.2
version: 7.4.2
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
prettier:
- specifier: ^3.2.5
- version: 3.2.5
+ specifier: ^3.3.2
+ version: 3.3.2
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
packages/definitions:
dependencies:
@@ -609,11 +691,11 @@ importers:
specifier: workspace:^0.1.0
version: link:../../tooling/typescript
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
packages/form:
dependencies:
@@ -624,8 +706,8 @@ importers:
specifier: workspace:^0.1.0
version: link:../validation
'@mantine/form':
- specifier: ^7.10.0
- version: 7.10.0(react@18.3.1)
+ specifier: ^7.11.0
+ version: 7.11.0(react@18.3.1)
devDependencies:
'@homarr/eslint-config':
specifier: workspace:^0.2.0
@@ -637,11 +719,11 @@ importers:
specifier: workspace:^0.1.0
version: link:../../tooling/typescript
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
packages/icons:
dependencies:
@@ -659,17 +741,26 @@ importers:
specifier: workspace:^0.1.0
version: link:../../tooling/typescript
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
packages/integrations:
dependencies:
+ '@homarr/common':
+ specifier: workspace:^0.1.0
+ version: link:../common
'@homarr/definitions':
specifier: workspace:^0.1.0
version: link:../definitions
+ '@homarr/log':
+ specifier: workspace:^0.1.0
+ version: link:../log
+ '@homarr/translation':
+ specifier: workspace:^0.1.0
+ version: link:../translation
'@homarr/validation':
specifier: workspace:^0.1.0
version: link:../validation
@@ -684,11 +775,11 @@ importers:
specifier: workspace:^0.1.0
version: link:../../tooling/typescript
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
packages/log:
dependencies:
@@ -712,11 +803,11 @@ importers:
specifier: workspace:^0.1.0
version: link:../../tooling/typescript
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
packages/modals:
dependencies:
@@ -726,6 +817,15 @@ importers:
'@homarr/ui':
specifier: workspace:^0.1.0
version: link:../ui
+ '@mantine/core':
+ specifier: ^7.11.0
+ version: 7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@mantine/hooks':
+ specifier: ^7.11.0
+ version: 7.11.0(react@18.3.1)
+ react:
+ specifier: ^18.3.1
+ version: 18.3.1
devDependencies:
'@homarr/eslint-config':
specifier: workspace:^0.2.0
@@ -737,11 +837,11 @@ importers:
specifier: workspace:^0.1.0
version: link:../../tooling/typescript
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
packages/notifications:
dependencies:
@@ -749,8 +849,36 @@ importers:
specifier: workspace:^0.1.0
version: link:../ui
'@mantine/notifications':
- specifier: ^7.10.0
- version: 7.10.0(@mantine/core@7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.10.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ specifier: ^7.11.0
+ version: 7.11.0(@mantine/core@7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.11.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@tabler/icons-react':
+ specifier: ^3.7.0
+ version: 3.7.0(react@18.3.1)
+ devDependencies:
+ '@homarr/eslint-config':
+ specifier: workspace:^0.2.0
+ version: link:../../tooling/eslint
+ '@homarr/prettier-config':
+ specifier: workspace:^0.1.0
+ version: link:../../tooling/prettier
+ '@homarr/tsconfig':
+ specifier: workspace:^0.1.0
+ version: link:../../tooling/typescript
+ eslint:
+ specifier: ^9.6.0
+ version: 9.6.0
+ typescript:
+ specifier: ^5.5.2
+ version: 5.5.2
+
+ packages/ping:
+ dependencies:
+ '@homarr/common':
+ specifier: workspace:^0.1.0
+ version: link:../common
+ '@homarr/log':
+ specifier: workspace:^0.1.0
+ version: link:../log
devDependencies:
'@homarr/eslint-config':
specifier: workspace:^0.2.0
@@ -762,11 +890,11 @@ importers:
specifier: workspace:^0.1.0
version: link:../../tooling/typescript
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
packages/redis:
dependencies:
@@ -782,6 +910,9 @@ importers:
ioredis:
specifier: 5.4.1
version: 5.4.1
+ superjson:
+ specifier: 2.2.1
+ version: 2.2.1
devDependencies:
'@homarr/eslint-config':
specifier: workspace:^0.2.0
@@ -793,11 +924,11 @@ importers:
specifier: workspace:^0.1.0
version: link:../../tooling/typescript
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
packages/server-settings:
devDependencies:
@@ -811,11 +942,11 @@ importers:
specifier: workspace:^0.1.0
version: link:../../tooling/typescript
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
packages/spotlight:
dependencies:
@@ -825,9 +956,30 @@ importers:
'@homarr/ui':
specifier: workspace:^0.1.0
version: link:../ui
+ '@mantine/core':
+ specifier: ^7.11.0
+ version: 7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@mantine/hooks':
+ specifier: ^7.11.0
+ version: 7.11.0(react@18.3.1)
'@mantine/spotlight':
- specifier: ^7.10.0
- version: 7.10.0(@mantine/core@7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.10.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ specifier: ^7.11.0
+ version: 7.11.0(@mantine/core@7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.11.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@tabler/icons-react':
+ specifier: ^3.7.0
+ version: 3.7.0(react@18.3.1)
+ jotai:
+ specifier: ^2.8.4
+ version: 2.8.4(@types/react@18.3.3)(react@18.3.1)
+ next:
+ specifier: ^14.2.4
+ version: 14.2.4(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6)
+ react:
+ specifier: ^18.3.1
+ version: 18.3.1
+ use-deep-compare-effect:
+ specifier: ^1.8.1
+ version: 1.8.1(react@18.3.1)
devDependencies:
'@homarr/eslint-config':
specifier: workspace:^0.2.0
@@ -839,14 +991,17 @@ importers:
specifier: workspace:^0.1.0
version: link:../../tooling/typescript
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
packages/translation:
dependencies:
+ dayjs:
+ specifier: ^1.11.11
+ version: 1.11.11
next-international:
specifier: ^1.2.4
version: 1.2.4
@@ -861,11 +1016,11 @@ importers:
specifier: workspace:^0.1.0
version: link:../../tooling/typescript
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
packages/ui:
dependencies:
@@ -875,6 +1030,24 @@ importers:
'@homarr/translation':
specifier: workspace:^0.1.0
version: link:../translation
+ '@mantine/core':
+ specifier: ^7.11.0
+ version: 7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@mantine/dates':
+ specifier: ^7.11.0
+ version: 7.11.0(@mantine/core@7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.11.0(react@18.3.1))(dayjs@1.11.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@mantine/hooks':
+ specifier: ^7.11.0
+ version: 7.11.0(react@18.3.1)
+ '@tabler/icons-react':
+ specifier: ^3.7.0
+ version: 3.7.0(react@18.3.1)
+ next:
+ specifier: ^14.2.4
+ version: 14.2.4(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6)
+ react:
+ specifier: ^18.3.1
+ version: 18.3.1
devDependencies:
'@homarr/eslint-config':
specifier: workspace:^0.2.0
@@ -889,11 +1062,11 @@ importers:
specifier: ^1.0.5
version: 1.0.5
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
packages/validation:
dependencies:
@@ -917,11 +1090,11 @@ importers:
specifier: workspace:^0.1.0
version: link:../../tooling/typescript
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
packages/widgets:
dependencies:
@@ -961,6 +1134,15 @@ importers:
'@homarr/validation':
specifier: workspace:^0.1.0
version: link:../validation
+ '@mantine/core':
+ specifier: ^7.11.0
+ version: 7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@mantine/hooks':
+ specifier: ^7.11.0
+ version: 7.11.0(react@18.3.1)
+ '@tabler/icons-react':
+ specifier: ^3.7.0
+ version: 3.7.0(react@18.3.1)
'@tiptap/extension-color':
specifier: 2.4.0
version: 2.4.0(@tiptap/core@2.4.0(@tiptap/pm@2.2.4))(@tiptap/extension-text-style@2.4.0(@tiptap/core@2.4.0(@tiptap/pm@2.2.4)))
@@ -1006,12 +1188,18 @@ importers:
'@tiptap/starter-kit':
specifier: ^2.4.0
version: 2.4.0(@tiptap/pm@2.2.4)
- prismjs:
- specifier: ^1.29.0
- version: 1.29.0
- react-simple-code-editor:
- specifier: ^0.13.1
- version: 0.13.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ clsx:
+ specifier: ^2.1.1
+ version: 2.1.1
+ dayjs:
+ specifier: ^1.11.11
+ version: 1.11.11
+ next:
+ specifier: ^14.2.4
+ version: 14.2.4(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6)
+ react:
+ specifier: ^18.3.1
+ version: 18.3.1
video.js:
specifier: ^8.12.0
version: 8.12.0
@@ -1025,48 +1213,42 @@ importers:
'@homarr/tsconfig':
specifier: workspace:^0.1.0
version: link:../../tooling/typescript
- '@types/prismjs':
- specifier: ^1.26.4
- version: 1.26.4
'@types/video.js':
specifier: ^7.3.58
version: 7.3.58
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
tooling/eslint:
dependencies:
'@next/eslint-plugin-next':
- specifier: ^14.2.3
- version: 14.2.3
- '@typescript-eslint/eslint-plugin':
- specifier: ^7.11.0
- version: 7.11.0(@typescript-eslint/parser@7.11.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
- '@typescript-eslint/parser':
- specifier: ^7.11.0
- version: 7.11.0(eslint@8.57.0)(typescript@5.4.5)
+ specifier: ^14.2.4
+ version: 14.2.4
eslint-config-prettier:
specifier: ^9.1.0
- version: 9.1.0(eslint@8.57.0)
+ version: 9.1.0(eslint@9.6.0)
eslint-config-turbo:
- specifier: ^1.13.3
- version: 1.13.3(eslint@8.57.0)
+ specifier: ^2.0.6
+ version: 2.0.6(eslint@9.6.0)
eslint-plugin-import:
specifier: ^2.29.1
- version: 2.29.1(@typescript-eslint/parser@7.11.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)
+ version: 2.29.1(@typescript-eslint/parser@7.14.1(eslint@9.6.0)(typescript@5.5.2))(eslint@9.6.0)
eslint-plugin-jsx-a11y:
- specifier: ^6.8.0
- version: 6.8.0(eslint@8.57.0)
+ specifier: ^6.9.0
+ version: 6.9.0(eslint@9.6.0)
eslint-plugin-react:
- specifier: ^7.34.1
- version: 7.34.1(eslint@8.57.0)
+ specifier: ^7.34.3
+ version: 7.34.3(eslint@9.6.0)
eslint-plugin-react-hooks:
specifier: ^4.6.2
- version: 4.6.2(eslint@8.57.0)
+ version: 4.6.2(eslint@9.6.0)
+ typescript-eslint:
+ specifier: ^7.14.1
+ version: 7.14.1(eslint@9.6.0)(typescript@5.5.2)
devDependencies:
'@homarr/prettier-config':
specifier: workspace:^0.1.0
@@ -1074,33 +1256,30 @@ importers:
'@homarr/tsconfig':
specifier: workspace:^0.1.0
version: link:../typescript
- '@types/eslint':
- specifier: ^8.56.10
- version: 8.56.10
eslint:
- specifier: ^8.57.0
- version: 8.57.0
+ specifier: ^9.6.0
+ version: 9.6.0
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
tooling/github: {}
tooling/prettier:
dependencies:
'@ianvs/prettier-plugin-sort-imports':
- specifier: ^4.2.1
- version: 4.2.1(prettier@3.2.5)
+ specifier: ^4.3.0
+ version: 4.3.0(prettier@3.3.2)
prettier:
- specifier: ^3.2.5
- version: 3.2.5
+ specifier: ^3.3.2
+ version: 3.3.2
devDependencies:
'@homarr/tsconfig':
specifier: workspace:^0.1.0
version: link:../typescript
typescript:
- specifier: ^5.4.5
- version: 5.4.5
+ specifier: ^5.5.2
+ version: 5.5.2
tooling/semver: {}
@@ -1116,12 +1295,8 @@ packages:
resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==}
engines: {node: '>=6.0.0'}
- '@ampproject/remapping@2.3.0':
- resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
- engines: {node: '>=6.0.0'}
-
- '@auth/core@0.31.0':
- resolution: {integrity: sha512-UKk3psvA1cRbk4/c9CkpWB8mdWrkKvzw0DmEYRsWolUQytQ2cRqx+hYuV6ZCsngw/xbj9hpmkZmAZEyq2g4fMg==}
+ '@auth/core@0.32.0':
+ resolution: {integrity: sha512-3+ssTScBd+1fd0/fscAyQN1tSygXzuhysuVVzB942ggU4mdfiTbv36P0ccVnExKWYJKvu3E2r3/zxXCCAmTOrg==}
peerDependencies:
'@simplewebauthn/browser': ^9.0.1
'@simplewebauthn/server': ^9.0.2
@@ -1134,87 +1309,59 @@ packages:
nodemailer:
optional: true
- '@auth/drizzle-adapter@1.1.0':
- resolution: {integrity: sha512-ZC83ne3Ulkxh2II2T5qazgrqc1AmWbbPzs3oR+C93DMlIV8DPIgEaw4cmbV/kesO9xO+Gt7HpkctmOa2B26A1Q==}
+ '@auth/core@0.34.1':
+ resolution: {integrity: sha512-tuYU2VIbI8rFbkSwP710LmybB2FXJsPN7j3sjRVfN9SXVQBK2ej6LdewQaofpBGp4Mk+cC2UeiGNH0or4tgaeA==}
+ peerDependencies:
+ '@simplewebauthn/browser': ^9.0.1
+ '@simplewebauthn/server': ^9.0.2
+ nodemailer: ^6.8.0
+ peerDependenciesMeta:
+ '@simplewebauthn/browser':
+ optional: true
+ '@simplewebauthn/server':
+ optional: true
+ nodemailer:
+ optional: true
- '@babel/code-frame@7.23.5':
- resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==}
- engines: {node: '>=6.9.0'}
+ '@auth/drizzle-adapter@1.4.1':
+ resolution: {integrity: sha512-pUC8D0jfANDvThH1CrcUXmjZyF98ccVMY3iEZUQzUTr0U1csuppvRoz5JccOLzjv3tu+Nb9Qd6SvrmmsnuYgSw==}
'@babel/code-frame@7.24.6':
resolution: {integrity: sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==}
engines: {node: '>=6.9.0'}
- '@babel/compat-data@7.23.5':
- resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==}
- engines: {node: '>=6.9.0'}
-
'@babel/compat-data@7.24.6':
resolution: {integrity: sha512-aC2DGhBq5eEdyXWqrDInSqQjO0k8xtPRf5YylULqx8MCd6jBtzqfta/3ETMRpuKIc5hyswfO80ObyA1MvkCcUQ==}
engines: {node: '>=6.9.0'}
- '@babel/core@7.24.0':
- resolution: {integrity: sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==}
- engines: {node: '>=6.9.0'}
-
'@babel/core@7.24.6':
resolution: {integrity: sha512-qAHSfAdVyFmIvl0VHELib8xar7ONuSHrE2hLnsaWkYNTI68dmi1x8GYDhJjMI/e7XWal9QBlZkwbOnkcw7Z8gQ==}
engines: {node: '>=6.9.0'}
- '@babel/generator@7.23.6':
- resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==}
- engines: {node: '>=6.9.0'}
-
'@babel/generator@7.24.6':
resolution: {integrity: sha512-S7m4eNa6YAPJRHmKsLHIDJhNAGNKoWNiWefz1MBbpnt8g9lvMDl1hir4P9bo/57bQEmuwEhnRU/AMWsD0G/Fbg==}
engines: {node: '>=6.9.0'}
- '@babel/helper-compilation-targets@7.23.6':
- resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==}
- engines: {node: '>=6.9.0'}
-
'@babel/helper-compilation-targets@7.24.6':
resolution: {integrity: sha512-VZQ57UsDGlX/5fFA7GkVPplZhHsVc+vuErWgdOiysI9Ksnw0Pbbd6pnPiR/mmJyKHgyIW0c7KT32gmhiF+cirg==}
engines: {node: '>=6.9.0'}
- '@babel/helper-environment-visitor@7.22.20':
- resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==}
- engines: {node: '>=6.9.0'}
-
'@babel/helper-environment-visitor@7.24.6':
resolution: {integrity: sha512-Y50Cg3k0LKLMjxdPjIl40SdJgMB85iXn27Vk/qbHZCFx/o5XO3PSnpi675h1KEmmDb6OFArfd5SCQEQ5Q4H88g==}
engines: {node: '>=6.9.0'}
- '@babel/helper-function-name@7.23.0':
- resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==}
- engines: {node: '>=6.9.0'}
-
'@babel/helper-function-name@7.24.6':
resolution: {integrity: sha512-xpeLqeeRkbxhnYimfr2PC+iA0Q7ljX/d1eZ9/inYbmfG2jpl8Lu3DyXvpOAnrS5kxkfOWJjioIMQsaMBXFI05w==}
engines: {node: '>=6.9.0'}
- '@babel/helper-hoist-variables@7.22.5':
- resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
- engines: {node: '>=6.9.0'}
-
'@babel/helper-hoist-variables@7.24.6':
resolution: {integrity: sha512-SF/EMrC3OD7dSta1bLJIlrsVxwtd0UpjRJqLno6125epQMJ/kyFmpTT4pbvPbdQHzCHg+biQ7Syo8lnDtbR+uA==}
engines: {node: '>=6.9.0'}
- '@babel/helper-module-imports@7.22.15':
- resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==}
- engines: {node: '>=6.9.0'}
-
'@babel/helper-module-imports@7.24.6':
resolution: {integrity: sha512-a26dmxFJBF62rRO9mmpgrfTLsAuyHk4e1hKTUkD/fcMfynt8gvEKwQPQDVxWhca8dHoDck+55DFt42zV0QMw5g==}
engines: {node: '>=6.9.0'}
- '@babel/helper-module-transforms@7.23.3':
- resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==}
- engines: {node: '>=6.9.0'}
- peerDependencies:
- '@babel/core': ^7.0.0
-
'@babel/helper-module-transforms@7.24.6':
resolution: {integrity: sha512-Y/YMPm83mV2HJTbX1Qh2sjgjqcacvOlhbzdCCsSlblOKjSYmQqEbO6rUniWQyRo9ncyfjT8hnUjlG06RXDEmcA==}
engines: {node: '>=6.9.0'}
@@ -1225,67 +1372,34 @@ packages:
resolution: {integrity: sha512-MZG/JcWfxybKwsA9N9PmtF2lOSFSEMVCpIRrbxccZFLJPrJciJdG/UhSh5W96GEteJI2ARqm5UAHxISwRDLSNg==}
engines: {node: '>=6.9.0'}
- '@babel/helper-simple-access@7.22.5':
- resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
- engines: {node: '>=6.9.0'}
-
'@babel/helper-simple-access@7.24.6':
resolution: {integrity: sha512-nZzcMMD4ZhmB35MOOzQuiGO5RzL6tJbsT37Zx8M5L/i9KSrukGXWTjLe1knIbb/RmxoJE9GON9soq0c0VEMM5g==}
engines: {node: '>=6.9.0'}
- '@babel/helper-split-export-declaration@7.22.6':
- resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==}
- engines: {node: '>=6.9.0'}
-
'@babel/helper-split-export-declaration@7.24.6':
resolution: {integrity: sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw==}
engines: {node: '>=6.9.0'}
- '@babel/helper-string-parser@7.23.4':
- resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==}
- engines: {node: '>=6.9.0'}
-
'@babel/helper-string-parser@7.24.6':
resolution: {integrity: sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q==}
engines: {node: '>=6.9.0'}
- '@babel/helper-validator-identifier@7.22.20':
- resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==}
- engines: {node: '>=6.9.0'}
-
'@babel/helper-validator-identifier@7.24.6':
resolution: {integrity: sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==}
engines: {node: '>=6.9.0'}
- '@babel/helper-validator-option@7.23.5':
- resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==}
- engines: {node: '>=6.9.0'}
-
'@babel/helper-validator-option@7.24.6':
resolution: {integrity: sha512-Jktc8KkF3zIkePb48QO+IapbXlSapOW9S+ogZZkcO6bABgYAxtZcjZ/O005111YLf+j4M84uEgwYoidDkXbCkQ==}
engines: {node: '>=6.9.0'}
- '@babel/helpers@7.24.0':
- resolution: {integrity: sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==}
- engines: {node: '>=6.9.0'}
-
'@babel/helpers@7.24.6':
resolution: {integrity: sha512-V2PI+NqnyFu1i0GyTd/O/cTpxzQCYioSkUIRmgo7gFEHKKCg5w46+r/A6WeUR1+P3TeQ49dspGPNd/E3n9AnnA==}
engines: {node: '>=6.9.0'}
- '@babel/highlight@7.23.4':
- resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==}
- engines: {node: '>=6.9.0'}
-
'@babel/highlight@7.24.6':
resolution: {integrity: sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==}
engines: {node: '>=6.9.0'}
- '@babel/parser@7.24.0':
- resolution: {integrity: sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==}
- engines: {node: '>=6.0.0'}
- hasBin: true
-
'@babel/parser@7.24.6':
resolution: {integrity: sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==}
engines: {node: '>=6.0.0'}
@@ -1311,38 +1425,21 @@ packages:
resolution: {integrity: sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==}
engines: {node: '>=6.9.0'}
- '@babel/template@7.23.9':
- resolution: {integrity: sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==}
- engines: {node: '>=6.9.0'}
-
- '@babel/template@7.24.0':
- resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==}
- engines: {node: '>=6.9.0'}
-
'@babel/template@7.24.6':
resolution: {integrity: sha512-3vgazJlLwNXi9jhrR1ef8qiB65L1RK90+lEQwv4OxveHnqC3BfmnHdgySwRLzf6akhlOYenT+b7AfWq+a//AHw==}
engines: {node: '>=6.9.0'}
- '@babel/traverse@7.24.0':
- resolution: {integrity: sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==}
- engines: {node: '>=6.9.0'}
-
'@babel/traverse@7.24.6':
resolution: {integrity: sha512-OsNjaJwT9Zn8ozxcfoBc+RaHdj3gFmCmYoQLUII1o6ZrUwku0BMg80FoOTPx+Gi6XhcQxAYE4xyjPTo4SxEQqw==}
engines: {node: '>=6.9.0'}
- '@babel/types@7.23.9':
- resolution: {integrity: sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==}
- engines: {node: '>=6.9.0'}
-
- '@babel/types@7.24.0':
- resolution: {integrity: sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==}
- engines: {node: '>=6.9.0'}
-
'@babel/types@7.24.6':
resolution: {integrity: sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ==}
engines: {node: '>=6.9.0'}
+ '@balena/dockerignore@1.0.2':
+ resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==}
+
'@bcoe/v8-coverage@0.2.3':
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
@@ -1781,13 +1878,21 @@ packages:
resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==}
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
- '@eslint/eslintrc@2.1.4':
- resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ '@eslint/config-array@0.17.0':
+ resolution: {integrity: sha512-A68TBu6/1mHHuc5YJL0U0VVeGNiklLAL6rRmhTCP2B5XjWLMnrX+HkO+IAXyHvks5cyyY1jjK5ITPQ1HGS2EVA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
- '@eslint/js@8.57.0':
- resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ '@eslint/eslintrc@3.1.0':
+ resolution: {integrity: sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/js@9.6.0':
+ resolution: {integrity: sha512-D9B0/3vNg44ZeWbYMpBoXqNP4j6eQD5vNwIlGAuFRRzK/WtT/jvDQW3Bi9kkf3PMDMlM7Yi+73VLUsn5bJcl8A==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/object-schema@2.1.4':
+ resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
'@floating-ui/core@1.6.0':
resolution: {integrity: sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==}
@@ -1810,22 +1915,19 @@ packages:
'@floating-ui/utils@0.2.1':
resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==}
- '@homarr/gridstack@1.0.2':
- resolution: {integrity: sha512-YL9oF9SwAhioto4QNaowHdMNEhjy2G6v22aN3BlGtumiAWPayo3Ni9P37ZQ7Zv/1s8qKmC2t3GZAp+IsCPjgUA==}
-
- '@humanwhocodes/config-array@0.11.14':
- resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
- engines: {node: '>=10.10.0'}
+ '@homarr/gridstack@1.0.0':
+ resolution: {integrity: sha512-KM9024BipLD9BmtM6jHI8OKLZ1Iy4vZdTfU53ww4qEda/330XQYhIC2SBcQgkNnDB2MTkn/laNSO5gTy+lJg9Q==}
'@humanwhocodes/module-importer@1.0.1':
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
engines: {node: '>=12.22'}
- '@humanwhocodes/object-schema@2.0.2':
- resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==}
+ '@humanwhocodes/retry@0.3.0':
+ resolution: {integrity: sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==}
+ engines: {node: '>=18.18'}
- '@ianvs/prettier-plugin-sort-imports@4.2.1':
- resolution: {integrity: sha512-NKN1LVFWUDGDGr3vt+6Ey3qPeN/163uR1pOPAlkWpgvAqgxQ6kSdUf1F0it8aHUtKRUzEGcK38Wxd07O61d7+Q==}
+ '@ianvs/prettier-plugin-sort-imports@4.3.0':
+ resolution: {integrity: sha512-OOMtUcO4J3LoL63dOKAe7bn+lSRRPeit2DqNHpx+wvBp3Grejo2PMaK4Mp1mwy8pnat64ccSgk/lBZbsAdLErw==}
peerDependencies:
'@vue/compiler-sfc': 2.7.x || 3.x
prettier: 2 || 3
@@ -1868,6 +1970,9 @@ packages:
resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
engines: {node: '>=6.0.0'}
+ '@jridgewell/source-map@0.3.6':
+ resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==}
+
'@jridgewell/sourcemap-codec@1.4.15':
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
@@ -1877,71 +1982,71 @@ packages:
'@jridgewell/trace-mapping@0.3.9':
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
- '@mantine/colors-generator@7.10.0':
- resolution: {integrity: sha512-oJB6Oj8+aMDwwjdFEhbla07XHGp/I8yd+Bo3+6u12nk7ctJ4Tte/ffm/HdWuzzjR1AJW9R8WLDH7NWh8ydFZRA==}
+ '@mantine/colors-generator@7.11.0':
+ resolution: {integrity: sha512-txSzQeAVQ4w7g/hZxZnS0TA3OMqJvKearnRagjwHEMX7tvcnPQaUWEkGu/0wNTjKRai4bWDkfyBFmoQvxrSBEQ==}
peerDependencies:
chroma-js: ^2.4.2
- '@mantine/core@7.10.0':
- resolution: {integrity: sha512-hNqhdn/+4x8+FDWzR5fu1eMgnG1Mw4fZHw4WjIYjKrSv0NeKHY263RiesZz8RwcUQ8r7LlD95/2tUOMnKVTV5Q==}
+ '@mantine/core@7.11.0':
+ resolution: {integrity: sha512-yw2Llww9mw8rDWZtucdEuvkqqjHdreUibos7JCUpejL721FW1Tn9L91nsxO/YQFSS7jn4Q0CP+1YbQ/PMULmwA==}
peerDependencies:
- '@mantine/hooks': 7.10.0
+ '@mantine/hooks': 7.11.0
react: ^18.2.0
react-dom: ^18.2.0
- '@mantine/dates@7.10.0':
- resolution: {integrity: sha512-LBBh1U/RzxFQKGA6sSYxbCwYEMoM5lNIhwofY6g8zOTAZuRQqo5FIWItmB9I9ltT+M2o75SADeP6ZBLi4ec8ZA==}
+ '@mantine/dates@7.11.0':
+ resolution: {integrity: sha512-4wKzOyOvDDh8/TV1SYGHDWydflweRiem4cJt3S49LO1Forpy2SiJ18Gn4ylGiqZdWxz1HY4Jb5kl7GAzxLD4tQ==}
peerDependencies:
- '@mantine/core': 7.10.0
- '@mantine/hooks': 7.10.0
+ '@mantine/core': 7.11.0
+ '@mantine/hooks': 7.11.0
dayjs: '>=1.0.0'
react: ^18.2.0
react-dom: ^18.2.0
- '@mantine/form@7.10.0':
- resolution: {integrity: sha512-ChAtqdQCAZrnH6iiCivumyMuMsev+tFWIgsCCgAmbP2sOyMtjbNtypKrcwBwI/PzAH9N4jSJlsmJsnRdXNeEkQ==}
+ '@mantine/form@7.11.0':
+ resolution: {integrity: sha512-BmkzRp57O1zZuxCYH76w6zeBNhczq7OeRtkG/zvMo35BJp1K5u8eetN3AC1WwkGLmrNid2BCIsvTFHDP9DYnaQ==}
peerDependencies:
react: ^18.2.0
- '@mantine/hooks@7.10.0':
- resolution: {integrity: sha512-fnalwYS2WQEFS4wmhmAetDZ/VdJPLNeUXPX9t+S21o3p/dRTX1xhU2mS7yWaQUKM0hPD1TcujqXGlP2M2g/A9A==}
+ '@mantine/hooks@7.11.0':
+ resolution: {integrity: sha512-T3472GhUXFhuhXUHlxjHv0wfb73lFyNuaw631c7Ddtgvewq0WKtNqYd7j/Zz/k02DuS3r0QXA7e12/XgqHBZjg==}
peerDependencies:
react: ^18.2.0
- '@mantine/modals@7.10.0':
- resolution: {integrity: sha512-UVtmRpTBWDqcJjdv97IUYLduYcZBrqteyDwnspHT453iFZlvCglHUXYR+LvN5ExE+kxUe2IUXL/pEaIRTjwtKQ==}
+ '@mantine/modals@7.11.0':
+ resolution: {integrity: sha512-I4bxdXirLNvVbmVcS9lhU9z1bknE8XlteGeSxAZ00SLUk9EowG+AX/9nK0TrSG2GBNDX82fFxp2z98/o7bTw5w==}
peerDependencies:
- '@mantine/core': 7.10.0
- '@mantine/hooks': 7.10.0
+ '@mantine/core': 7.11.0
+ '@mantine/hooks': 7.11.0
react: ^18.2.0
react-dom: ^18.2.0
- '@mantine/notifications@7.10.0':
- resolution: {integrity: sha512-3a0mmM9Kr3nPP+8VHsIuly507nda6ciu2aB/xSxb7gFIKHw3GqSu77pxXa+5l4Y6AQKKvP9360K4KjH6+rOBWw==}
+ '@mantine/notifications@7.11.0':
+ resolution: {integrity: sha512-UtAHJoSi4s+lfVZrkUDWMlg6j0w1LZaiMEOBMG9p5MV5dP38W75LeCy2cio2Znji2S5YzXaZolOkHBT5ZONKAw==}
peerDependencies:
- '@mantine/core': 7.10.0
- '@mantine/hooks': 7.10.0
+ '@mantine/core': 7.11.0
+ '@mantine/hooks': 7.11.0
react: ^18.2.0
react-dom: ^18.2.0
- '@mantine/spotlight@7.10.0':
- resolution: {integrity: sha512-gkoBdd+GbAZ3heKxBm3MRdVwEYEgAxc3F5va3i/4X+AbyJ9ZeutCcyA2SL6++ZDNrb3bmWJG4zfLwdKI799dXg==}
+ '@mantine/spotlight@7.11.0':
+ resolution: {integrity: sha512-MKlm/JApbs08MIcaGHjGCKWygNLLY8Gz35rfjps4PP2fFENpqwMi5MDONU6B7ZpLzX0GRo1wV15HWbuk8n9BEA==}
peerDependencies:
- '@mantine/core': 7.10.0
- '@mantine/hooks': 7.10.0
+ '@mantine/core': 7.11.0
+ '@mantine/hooks': 7.11.0
react: ^18.2.0
react-dom: ^18.2.0
- '@mantine/store@7.10.0':
- resolution: {integrity: sha512-B6AyUX0cA97/hI9v0att7eJJnQTcUG7zBlTdWhOsptBV5UoDNrzdv3DDWIFxrA8h+nhNKGBh6Dif5HWh1+QLeA==}
+ '@mantine/store@7.11.0':
+ resolution: {integrity: sha512-zPmOpdFgvkUqYKSK7NNKbhgXsh2QPw51m3iypTaj0mw+rZbk3WSH9vZvaEx59X0QG+ahwUg2/HezbjfXFUbvrA==}
peerDependencies:
react: ^18.2.0
- '@mantine/tiptap@7.10.0':
- resolution: {integrity: sha512-C8wURpoh1dduWPgGgyknVc+E9/gDZVOMIyPxZXNx/r74/OoaE8tu8tgF/T21t8DtCQ4jter0PEGZFDB9hIXuag==}
+ '@mantine/tiptap@7.11.0':
+ resolution: {integrity: sha512-6AXSteD22B4gqscEKLejJ8v1QQsl7Q/2XLYSHwrT8T4l9JVeL881SG5wl23TOKp2K23BqULZbDmm4hhEKmnCZA==}
peerDependencies:
- '@mantine/core': 7.10.0
- '@mantine/hooks': 7.10.0
+ '@mantine/core': 7.11.0
+ '@mantine/hooks': 7.11.0
'@tiptap/extension-link': '>=2.1.12'
'@tiptap/react': '>=2.1.12'
react: ^18.2.0
@@ -1951,62 +2056,62 @@ packages:
resolution: {integrity: sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==}
hasBin: true
- '@next/env@14.2.3':
- resolution: {integrity: sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==}
+ '@next/env@14.2.4':
+ resolution: {integrity: sha512-3EtkY5VDkuV2+lNmKlbkibIJxcO4oIHEhBWne6PaAp+76J9KoSsGvNikp6ivzAT8dhhBMYrm6op2pS1ApG0Hzg==}
- '@next/eslint-plugin-next@14.2.3':
- resolution: {integrity: sha512-L3oDricIIjgj1AVnRdRor21gI7mShlSwU/1ZGHmqM3LzHhXXhdkrfeNY5zif25Bi5Dd7fiJHsbhoZCHfXYvlAw==}
+ '@next/eslint-plugin-next@14.2.4':
+ resolution: {integrity: sha512-svSFxW9f3xDaZA3idQmlFw7SusOuWTpDTAeBlO3AEPDltrraV+lqs7mAc6A27YdnpQVVIA3sODqUAAHdWhVWsA==}
- '@next/swc-darwin-arm64@14.2.3':
- resolution: {integrity: sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==}
+ '@next/swc-darwin-arm64@14.2.4':
+ resolution: {integrity: sha512-AH3mO4JlFUqsYcwFUHb1wAKlebHU/Hv2u2kb1pAuRanDZ7pD/A/KPD98RHZmwsJpdHQwfEc/06mgpSzwrJYnNg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
- '@next/swc-darwin-x64@14.2.3':
- resolution: {integrity: sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==}
+ '@next/swc-darwin-x64@14.2.4':
+ resolution: {integrity: sha512-QVadW73sWIO6E2VroyUjuAxhWLZWEpiFqHdZdoQ/AMpN9YWGuHV8t2rChr0ahy+irKX5mlDU7OY68k3n4tAZTg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
- '@next/swc-linux-arm64-gnu@14.2.3':
- resolution: {integrity: sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==}
+ '@next/swc-linux-arm64-gnu@14.2.4':
+ resolution: {integrity: sha512-KT6GUrb3oyCfcfJ+WliXuJnD6pCpZiosx2X3k66HLR+DMoilRb76LpWPGb4tZprawTtcnyrv75ElD6VncVamUQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- '@next/swc-linux-arm64-musl@14.2.3':
- resolution: {integrity: sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==}
+ '@next/swc-linux-arm64-musl@14.2.4':
+ resolution: {integrity: sha512-Alv8/XGSs/ytwQcbCHwze1HmiIkIVhDHYLjczSVrf0Wi2MvKn/blt7+S6FJitj3yTlMwMxII1gIJ9WepI4aZ/A==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
- '@next/swc-linux-x64-gnu@14.2.3':
- resolution: {integrity: sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==}
+ '@next/swc-linux-x64-gnu@14.2.4':
+ resolution: {integrity: sha512-ze0ShQDBPCqxLImzw4sCdfnB3lRmN3qGMB2GWDRlq5Wqy4G36pxtNOo2usu/Nm9+V2Rh/QQnrRc2l94kYFXO6Q==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- '@next/swc-linux-x64-musl@14.2.3':
- resolution: {integrity: sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==}
+ '@next/swc-linux-x64-musl@14.2.4':
+ resolution: {integrity: sha512-8dwC0UJoc6fC7PX70csdaznVMNr16hQrTDAMPvLPloazlcaWfdPogq+UpZX6Drqb1OBlwowz8iG7WR0Tzk/diQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
- '@next/swc-win32-arm64-msvc@14.2.3':
- resolution: {integrity: sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==}
+ '@next/swc-win32-arm64-msvc@14.2.4':
+ resolution: {integrity: sha512-jxyg67NbEWkDyvM+O8UDbPAyYRZqGLQDTPwvrBBeOSyVWW/jFQkQKQ70JDqDSYg1ZDdl+E3nkbFbq8xM8E9x8A==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
- '@next/swc-win32-ia32-msvc@14.2.3':
- resolution: {integrity: sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==}
+ '@next/swc-win32-ia32-msvc@14.2.4':
+ resolution: {integrity: sha512-twrmN753hjXRdcrZmZttb/m5xaCBFa48Dt3FbeEItpJArxriYDunWxJn+QFXdJ3hPkm4u7CKxncVvnmgQMY1ag==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
- '@next/swc-win32-x64-msvc@14.2.3':
- resolution: {integrity: sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==}
+ '@next/swc-win32-x64-msvc@14.2.4':
+ resolution: {integrity: sha512-tkLrjBzqFTP8DVrAAQmZelEahfR9OxWpFR++vAI9FBhCiIxtwHwBHC23SBHCTURBtwB4kc/x44imVOnkKGNVGg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
@@ -2144,61 +2249,61 @@ packages:
typescript:
optional: true
- '@tabler/icons-react@3.5.0':
- resolution: {integrity: sha512-bn05XKZV3ZfOv5Jr1FCTmVPOQGBVJoA4NefrnR919rqg6WGXAa08NovONHJGSuMxXUMV3b9Cni85diIW/E9yuw==}
+ '@tabler/icons-react@3.7.0':
+ resolution: {integrity: sha512-8AU4oO9pgLhHLOSGAJIsRhsTy7w8D7uKBlR0DIFxaILFA1fe2rC4wrNGYUtIFei82y41OuXz9vMk0Oe+IWT3SA==}
peerDependencies:
react: '>= 16'
- '@tabler/icons@3.5.0':
- resolution: {integrity: sha512-I53dC3ZSHQ2MZFGvDYJelfXm91L2bTTixS4w5jTAulLhHbCZso5Bih4Rk/NYZxlngLQMKHvEYwZQ+6w/WluKiA==}
+ '@tabler/icons@3.7.0':
+ resolution: {integrity: sha512-lJGIZLSWrPO6VygRUbaVvQjWgL2EaiBMD8e6leCYUQ8ZPO4LIzKMq358C8KlhXJcyNiRz1Io3YWoc/DNTcWqSg==}
'@tanstack/match-sorter-utils@8.15.1':
resolution: {integrity: sha512-PnVV3d2poenUM31ZbZi/yXkBu3J7kd5k2u51CGwwNojag451AjTH9N6n41yjXz2fpLeewleyLBmNS6+HcGDlXw==}
engines: {node: '>=12'}
- '@tanstack/query-core@5.40.0':
- resolution: {integrity: sha512-eD8K8jsOIq0Z5u/QbvOmfvKKE/XC39jA7yv4hgpl/1SRiU+J8QCIwgM/mEHuunQsL87dcvnHqSVLmf9pD4CiaA==}
+ '@tanstack/query-core@5.48.0':
+ resolution: {integrity: sha512-lZAfPPeVIqXCswE9SSbG33B6/91XOWt/Iq41bFeWb/mnHwQSIfFRbkS4bfs+WhIk9abRArF9Id2fp0Mgo+hq6Q==}
- '@tanstack/query-devtools@5.37.1':
- resolution: {integrity: sha512-XcG4IIHIv0YQKrexTqo2zogQWR1Sz672tX2KsfE9kzB+9zhx44vRKH5si4WDILE1PIWQpStFs/NnrDQrBAUQpg==}
+ '@tanstack/query-devtools@5.47.0':
+ resolution: {integrity: sha512-oo10s7Nqaf/Q3QF4vuSwS0xw7zuYr5nXef4RHQIXVJKvRQeo9WowPLAnB0SF/voB0c4GTX6Vq8wfQ1G72ezEdw==}
- '@tanstack/react-query-devtools@5.40.0':
- resolution: {integrity: sha512-JoQOQj/LKaHoHVMAh73R0pc4lIAHiZMV8L4DGHsTSvHcKi22LZmSC9aYBY9oMkqGpFtKmbspwrUIn55+czNSbA==}
+ '@tanstack/react-query-devtools@5.48.0':
+ resolution: {integrity: sha512-0xvk8KDvEfQuLz3dy9jqtP0f6iR724d57Br7KtrtClbyqB53cy3Iy6BiTsiHaSsYnex/+cxWJZIX1UJWeV8Amw==}
peerDependencies:
- '@tanstack/react-query': ^5.40.0
+ '@tanstack/react-query': ^5.48.0
react: ^18 || ^19
- '@tanstack/react-query-next-experimental@5.40.0':
- resolution: {integrity: sha512-JOEKhzjF4juzzB7N6T+dc8BX5a6UxR7IUJBbmfRYa+Ju8Ibamw4W6xVWzZmH1mVd0G3XGuhcIKhba80n+iK+EQ==}
+ '@tanstack/react-query-next-experimental@5.48.0':
+ resolution: {integrity: sha512-+t41XBiPMtwa+xEfsNuVCxPCQQh4vnZ4Xzd6NtDaqWMsWSeR6hN6mTtMvJb2KFzBs8gZghB8yebLRE4msYnF1A==}
peerDependencies:
- '@tanstack/react-query': ^5.40.0
+ '@tanstack/react-query': ^5.48.0
next: ^13 || ^14 || ^15
react: ^18 || ^19
- '@tanstack/react-query@5.40.0':
- resolution: {integrity: sha512-iv/W0Axc4aXhFzkrByToE1JQqayxTPNotCoSCnarR/A1vDIHaoKpg7FTIfP3Ev2mbKn1yrxq0ZKYUdLEJxs6Tg==}
+ '@tanstack/react-query@5.48.0':
+ resolution: {integrity: sha512-GDExbjYWzvDokyRqMSWXdrPiYpp95Aig0oeMIrxTaruOJJgWiWfUP//OAaowm2RrRkGVsavSZdko/XmIrrV2Nw==}
peerDependencies:
react: ^18.0.0
- '@tanstack/react-table@8.16.0':
- resolution: {integrity: sha512-rKRjnt8ostqN2fercRVOIH/dq7MAmOENCMvVlKx6P9Iokhh6woBGnIZEkqsY/vEJf1jN3TqLOb34xQGLVRuhAg==}
+ '@tanstack/react-table@8.17.3':
+ resolution: {integrity: sha512-5gwg5SvPD3lNAXPuJJz1fOCEZYk9/GeBFH3w/hCgnfyszOIzwkwgp5I7Q4MJtn0WECp84b5STQUDdmvGi8m3nA==}
engines: {node: '>=12'}
peerDependencies:
react: '>=16.8'
react-dom: '>=16.8'
- '@tanstack/react-virtual@3.5.0':
- resolution: {integrity: sha512-rtvo7KwuIvqK9zb0VZ5IL7fiJAEnG+0EiFZz8FUOs+2mhGqdGmjKIaT1XU7Zq0eFqL0jonLlhbayJI/J2SA/Bw==}
+ '@tanstack/react-virtual@3.5.1':
+ resolution: {integrity: sha512-jIsuhfgy8GqA67PdWqg73ZB2LFE+HD9hjWL1L6ifEIZVyZVAKpYmgUG4WsKQ005aEyImJmbuimPiEvc57IY0Aw==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
- '@tanstack/table-core@8.16.0':
- resolution: {integrity: sha512-dCG8vQGk4js5v88/k83tTedWOwjGnIyONrKpHpfmSJB8jwFHl8GSu1sBBxbtACVAPtAQgwNxl0rw1d3RqRM1Tg==}
+ '@tanstack/table-core@8.17.3':
+ resolution: {integrity: sha512-mPBodDGVL+fl6d90wUREepHa/7lhsghg2A3vFpakEhrhtbIlgNAZiMr7ccTgak5qbHqF14Fwy+W1yFWQt+WmYQ==}
engines: {node: '>=12'}
- '@tanstack/virtual-core@3.5.0':
- resolution: {integrity: sha512-KnPRCkQTyqhanNC0K63GBG3wA8I+D1fQuVnAvcBF8f13akOKeQp1gSbu6f77zCxhEk727iV5oQnbHLYzHrECLg==}
+ '@tanstack/virtual-core@3.5.1':
+ resolution: {integrity: sha512-046+AUSiDru/V9pajE1du8WayvBKeCvJ2NmKPy/mR8/SbKKrqmSbj7LJBfXE+nSq4f5TBXvnCzu0kcYebI9WdQ==}
'@tiptap/core@2.4.0':
resolution: {integrity: sha512-YJSahk8pkxpCs8SflCZfTnJpE7IPyUWIylfgXM2DefjRQa5DZ+c6sNY0s/zbxKYFQ6AuHVX40r9pCfcqHChGxQ==}
@@ -2398,23 +2503,18 @@ packages:
'@tootallnate/quickjs-emscripten@0.23.0':
resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==}
- '@trpc/client@11.0.0-rc.377':
- resolution: {integrity: sha512-76l9naqQXtVDnkyDs12DrXhzD3fw1SMe7Fzcnc9mxYQlJpbQebGdXnN0sLn7IFodXp2hhfsTj5Azk6tIKBrqOw==}
+ '@trpc/client@11.0.0-rc.421':
+ resolution: {integrity: sha512-Ek9g3EEDtNeU35qgys1M7slyQPjwnAX2hHs8JN2AIXjDyfoT8n+ZdLJlJ8n9aoyGyO2LCFfbfyGhi3MeTMNHtg==}
peerDependencies:
- '@trpc/server': 11.0.0-rc.377+4a672ddd6
+ '@trpc/server': 11.0.0-rc.421+bce4f97e9
- '@trpc/client@11.0.0-rc.382':
- resolution: {integrity: sha512-O+MSRed5r8AJJ+j3peZkd/b7WINEkhKaFilRuPH8VQsrlMZToxUzQl9aShPIgnOhhQGxvWqdh2lXwUvrFCea/A==}
+ '@trpc/next@11.0.0-rc.421':
+ resolution: {integrity: sha512-ieKgfcKGPh9mngCVZDTOxVyZlWjrxgMKwgrQeVtmJcxxTA4H286wKX1wPSE0ryGe72Fx6RhOK+7YyRPEbbbAIw==}
peerDependencies:
- '@trpc/server': 11.0.0-rc.382+94f198fec
-
- '@trpc/next@11.0.0-rc.382':
- resolution: {integrity: sha512-0gpoxaIQ2WPVAK9iYrcd8NzaL0BhPgLJG/Gv5PlrFSb4qdKM91/+U2ze5i189pV66azhDs+qe2+QeiLO4pOMGQ==}
- peerDependencies:
- '@tanstack/react-query': ^5.25.0
- '@trpc/client': 11.0.0-rc.382+94f198fec
- '@trpc/react-query': 11.0.0-rc.382+94f198fec
- '@trpc/server': 11.0.0-rc.382+94f198fec
+ '@tanstack/react-query': ^5.40.0
+ '@trpc/client': 11.0.0-rc.421+bce4f97e9
+ '@trpc/react-query': 11.0.0-rc.421+bce4f97e9
+ '@trpc/server': 11.0.0-rc.421+bce4f97e9
next: '*'
react: '>=16.8.0'
react-dom: '>=16.8.0'
@@ -2424,17 +2524,17 @@ packages:
'@trpc/react-query':
optional: true
- '@trpc/react-query@11.0.0-rc.382':
- resolution: {integrity: sha512-VlYe+ufYy/hWQFpBEG2MHKsGrGmt2oqqPBO4lHWBpuCcQnzttvsW+LkBkMoy6qZqFcrGyQJC8KQXHO6njmfmZQ==}
+ '@trpc/react-query@11.0.0-rc.421':
+ resolution: {integrity: sha512-xoArO8tqzDDch4e2aH48bZ0SVHMpstsuhk/S0qTB1WybMV1pjZDnT/9qDQY8WwEkaqdVk/7BXXf94Kg5REAIpA==}
peerDependencies:
- '@tanstack/react-query': ^5.25.0
- '@trpc/client': 11.0.0-rc.382+94f198fec
- '@trpc/server': 11.0.0-rc.382+94f198fec
+ '@tanstack/react-query': ^5.40.0
+ '@trpc/client': 11.0.0-rc.421+bce4f97e9
+ '@trpc/server': 11.0.0-rc.421+bce4f97e9
react: '>=18.2.0'
react-dom: '>=18.2.0'
- '@trpc/server@11.0.0-rc.382':
- resolution: {integrity: sha512-a3A0osTgFhbClRjE+fZ3nA3TEjupnWM23L4hidjecg3xN+QghwYTjwXJcYXPsdKyT6PhFtzWBKJxShmsZRElZw==}
+ '@trpc/server@11.0.0-rc.421':
+ resolution: {integrity: sha512-4epaOaNiqvIDuyA7FfjthKcUvFMfFoKwBTUzhu/xLGeSClgLZ0v+1HWRPPWGFLMu1ZKLHMeeySXhnY4yLFlYjw==}
'@tsconfig/node10@1.0.9':
resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==}
@@ -2448,12 +2548,12 @@ packages:
'@tsconfig/node16@1.0.4':
resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==}
- '@turbo/gen@1.13.3':
- resolution: {integrity: sha512-l+EM1gGzckFMaaVQyj3BVRa0QJ+tpp8HfiHOhGpBWW3Vc0Hfj92AY87Di/7HGABa+HVY7ueatMi7DJG+zkJBYg==}
+ '@turbo/gen@2.0.6':
+ resolution: {integrity: sha512-Epv3tt40mrNCzL6XKELdHvM1KSYvY7bg4uRZAjS0SuYYY5E0gGjpeZPqqUNZl7fwmTHZLEZH5qqoKxbGWZsKXQ==}
hasBin: true
- '@turbo/workspaces@1.13.3':
- resolution: {integrity: sha512-QYZ8g3IVQebqNM8IsBlWYOWmOKjBZY55e6lx4EDOLuch1iWmyk+U8CLAI9UomMrSaKTs1Sx+PDkt63EgakvhUw==}
+ '@turbo/workspaces@2.0.6':
+ resolution: {integrity: sha512-WMX8OZLgUAZZMzyVfEk7s3/cs0uoOWpJ9y8sGSLlbDdc0Wdhoa88B2967xiMI8dPtWHg4mpEUduyYy2Lzmaofg==}
hasBin: true
'@types/babel__core@7.20.5':
@@ -2465,8 +2565,8 @@ packages:
'@types/babel__template@7.4.4':
resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
- '@types/babel__traverse@7.20.6':
- resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==}
+ '@types/babel__traverse@7.20.5':
+ resolution: {integrity: sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==}
'@types/bcrypt@5.0.2':
resolution: {integrity: sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==}
@@ -2492,6 +2592,15 @@ packages:
'@types/css-modules@1.0.5':
resolution: {integrity: sha512-oeKafs/df9lwOvtfiXVliZsocFVOexK9PZtLQWuPeuVCFR7jwiqlg60lu80JTe5NFNtH3tnV6Fs/ySR8BUPHAw==}
+ '@types/docker-modem@3.0.6':
+ resolution: {integrity: sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==}
+
+ '@types/dockerode@3.3.29':
+ resolution: {integrity: sha512-5PRRq/yt5OT/Jf77ltIdz4EiR9+VLnPF+HpU4xGFwUqmV24Co2HKBNW3w+slqZ1CYchbcDeqJASHDYWzZCcMiQ==}
+
+ '@types/eslint-scope@3.7.7':
+ resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==}
+
'@types/eslint@8.56.10':
resolution: {integrity: sha512-Shavhk87gCtY2fhXDctcfS3e6FdxWkCx1iUZ9eEUbh7rTqlZT0/IzOkCOVt0fCjcFuZ9FPYfuezTBImfHCDBGQ==}
@@ -2534,8 +2643,11 @@ packages:
'@types/node-cron@3.0.11':
resolution: {integrity: sha512-0ikrnug3/IyneSHqCBeslAhlK2aBfYek1fGo4bP4QnZPmiqSGRK+Oy7ZMisLWkesffJvQ1cqAcBnJC+8+nxIAg==}
- '@types/node@20.12.12':
- resolution: {integrity: sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==}
+ '@types/node@18.19.33':
+ resolution: {integrity: sha512-NR9+KrpSajr2qBVp/Yt5TU/rp+b5Mayi3+OlMlcg2cVCfRmcG5PWZ7S4+MG9PZ5gWBoc9Pd0BKSRViuBCRPu0A==}
+
+ '@types/node@20.14.9':
+ resolution: {integrity: sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg==}
'@types/object.omit@3.0.3':
resolution: {integrity: sha512-xrq4bQTBGYY2cw+gV4PzoG2Lv3L0pjZ1uXStRRDQoATOYW1lCsFQHhQ+OkPhIcQoqLjAq7gYif7D14Qaa6Zbew==}
@@ -2567,6 +2679,15 @@ packages:
'@types/serve-static@1.15.5':
resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==}
+ '@types/ssh2-streams@0.1.12':
+ resolution: {integrity: sha512-Sy8tpEmCce4Tq0oSOYdfqaBpA3hDM8SoxoFh5vzFsu2oL+znzGz8oVWW7xb4K920yYMUY+PIG31qZnFMfPWNCg==}
+
+ '@types/ssh2@0.5.52':
+ resolution: {integrity: sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==}
+
+ '@types/ssh2@1.15.0':
+ resolution: {integrity: sha512-YcT8jP5F8NzWeevWvcyrrLB3zcneVjzYY9ZDSMAMboI+2zR1qYWFhwsyOFVzT7Jorn67vqxC0FRiw8YyG9P1ww==}
+
'@types/throttle-debounce@2.1.0':
resolution: {integrity: sha512-5eQEtSCoESnh2FsiLTxE121IiE60hnMqcb435fShf4bpLRjEu1Eoekht23y6zXS9Ts3l+Szu3TARnTsA0GkOkQ==}
@@ -2585,8 +2706,8 @@ packages:
'@types/ws@8.5.10':
resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==}
- '@typescript-eslint/eslint-plugin@7.11.0':
- resolution: {integrity: sha512-P+qEahbgeHW4JQ/87FuItjBj8O3MYv5gELDzr8QaQ7fsll1gSMTYb6j87MYyxwf3DtD7uGFB9ShwgmCJB5KmaQ==}
+ '@typescript-eslint/eslint-plugin@7.14.1':
+ resolution: {integrity: sha512-aAJd6bIf2vvQRjUG3ZkNXkmBpN+J7Wd0mfQiiVCJMu9Z5GcZZdcc0j8XwN/BM97Fl7e3SkTXODSk4VehUv7CGw==}
engines: {node: ^18.18.0 || >=20.0.0}
peerDependencies:
'@typescript-eslint/parser': ^7.0.0
@@ -2596,8 +2717,8 @@ packages:
typescript:
optional: true
- '@typescript-eslint/parser@7.11.0':
- resolution: {integrity: sha512-yimw99teuaXVWsBcPO1Ais02kwJ1jmNA1KxE7ng0aT7ndr1pT1wqj0OJnsYVGKKlc4QJai86l/025L6z8CljOg==}
+ '@typescript-eslint/parser@7.14.1':
+ resolution: {integrity: sha512-8lKUOebNLcR0D7RvlcloOacTOWzOqemWEWkKSVpMZVF/XVcwjPR+3MD08QzbW9TCGJ+DwIc6zUSGZ9vd8cO1IA==}
engines: {node: ^18.18.0 || >=20.0.0}
peerDependencies:
eslint: ^8.56.0
@@ -2606,12 +2727,12 @@ packages:
typescript:
optional: true
- '@typescript-eslint/scope-manager@7.11.0':
- resolution: {integrity: sha512-27tGdVEiutD4POirLZX4YzT180vevUURJl4wJGmm6TrQoiYwuxTIY98PBp6L2oN+JQxzE0URvYlzJaBHIekXAw==}
+ '@typescript-eslint/scope-manager@7.14.1':
+ resolution: {integrity: sha512-gPrFSsoYcsffYXTOZ+hT7fyJr95rdVe4kGVX1ps/dJ+DfmlnjFN/GcMxXcVkeHDKqsq6uAcVaQaIi3cFffmAbA==}
engines: {node: ^18.18.0 || >=20.0.0}
- '@typescript-eslint/type-utils@7.11.0':
- resolution: {integrity: sha512-WmppUEgYy+y1NTseNMJ6mCFxt03/7jTOy08bcg7bxJJdsM4nuhnchyBbE8vryveaJUf62noH7LodPSo5Z0WUCg==}
+ '@typescript-eslint/type-utils@7.14.1':
+ resolution: {integrity: sha512-/MzmgNd3nnbDbOi3LfasXWWe292+iuo+umJ0bCCMCPc1jLO/z2BQmWUUUXvXLbrQey/JgzdF/OV+I5bzEGwJkQ==}
engines: {node: ^18.18.0 || >=20.0.0}
peerDependencies:
eslint: ^8.56.0
@@ -2620,12 +2741,12 @@ packages:
typescript:
optional: true
- '@typescript-eslint/types@7.11.0':
- resolution: {integrity: sha512-MPEsDRZTyCiXkD4vd3zywDCifi7tatc4K37KqTprCvaXptP7Xlpdw0NR2hRJTetG5TxbWDB79Ys4kLmHliEo/w==}
+ '@typescript-eslint/types@7.14.1':
+ resolution: {integrity: sha512-mL7zNEOQybo5R3AavY+Am7KLv8BorIv7HCYS5rKoNZKQD9tsfGUpO4KdAn3sSUvTiS4PQkr2+K0KJbxj8H9NDg==}
engines: {node: ^18.18.0 || >=20.0.0}
- '@typescript-eslint/typescript-estree@7.11.0':
- resolution: {integrity: sha512-cxkhZ2C/iyi3/6U9EPc5y+a6csqHItndvN/CzbNXTNrsC3/ASoYQZEt9uMaEp+xFNjasqQyszp5TumAVKKvJeQ==}
+ '@typescript-eslint/typescript-estree@7.14.1':
+ resolution: {integrity: sha512-k5d0VuxViE2ulIO6FbxxSZaxqDVUyMbXcidC8rHvii0I56XZPv8cq+EhMns+d/EVIL41sMXqRbK3D10Oza1bbA==}
engines: {node: ^18.18.0 || >=20.0.0}
peerDependencies:
typescript: '*'
@@ -2633,22 +2754,19 @@ packages:
typescript:
optional: true
- '@typescript-eslint/utils@7.11.0':
- resolution: {integrity: sha512-xlAWwPleNRHwF37AhrZurOxA1wyXowW4PqVXZVUNCLjB48CqdPJoJWkrpH2nij9Q3Lb7rtWindtoXwxjxlKKCA==}
+ '@typescript-eslint/utils@7.14.1':
+ resolution: {integrity: sha512-CMmVVELns3nak3cpJhZosDkm63n+DwBlDX8g0k4QUa9BMnF+lH2lr3d130M1Zt1xxmB3LLk3NV7KQCq86ZBBhQ==}
engines: {node: ^18.18.0 || >=20.0.0}
peerDependencies:
eslint: ^8.56.0
- '@typescript-eslint/visitor-keys@7.11.0':
- resolution: {integrity: sha512-7syYk4MzjxTEk0g/w3iqtgxnFQspDJfn6QKD36xMuuhTzjcxY7F8EmBLnALjVyaOF1/bVocu3bS/2/F7rXrveQ==}
+ '@typescript-eslint/visitor-keys@7.14.1':
+ resolution: {integrity: sha512-Crb+F75U1JAEtBeQGxSKwI60hZmmzaqA3z9sYsVm8X7W5cwLEm5bRe0/uXS6+MR/y8CVpKSR/ontIAIEPFcEkA==}
engines: {node: ^18.18.0 || >=20.0.0}
'@umami/node@0.3.0':
resolution: {integrity: sha512-+1cZ7o7jVN8oXDYZRqigfLHrWbEv5vtGWjB7blfVH1QUa+DRmWB6GfhMZtE2aSW+P9ACal8ZW7xD2PCAejlNCQ==}
- '@ungap/structured-clone@1.2.0':
- resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
-
'@videojs/http-streaming@3.12.1':
resolution: {integrity: sha512-rpB5AMt0QZ9bMXzwiWhynF2NLNnm5g2DZjPOFX6OoFqqXhbe2ngY2nqm9lLRhRVe22YeysQCmAlvBNwGuWFI8Q==}
engines: {node: '>=8', npm: '>=5'}
@@ -2666,8 +2784,8 @@ packages:
'@videojs/xhr@2.6.0':
resolution: {integrity: sha512-7J361GiN1tXpm+gd0xz2QWr3xNWBE+rytvo8J3KuggFaLg+U37gZQ2BuPLcnkfGffy2e+ozY70RHC8jt7zjA6Q==}
- '@vitejs/plugin-react@4.3.0':
- resolution: {integrity: sha512-KcEbMsn4Dpk+LIbHMj7gDPRKaTMStxxWRkRmxsg/jVdFdJCZWt1SchZcf0M4t8lIKdwwMsEyzhrcOXRrDPtOBw==}
+ '@vitejs/plugin-react@4.3.1':
+ resolution: {integrity: sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
vite: ^4.2.0 || ^5.0.0
@@ -2697,6 +2815,51 @@ packages:
'@vitest/utils@1.6.0':
resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==}
+ '@webassemblyjs/ast@1.12.1':
+ resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==}
+
+ '@webassemblyjs/floating-point-hex-parser@1.11.6':
+ resolution: {integrity: sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==}
+
+ '@webassemblyjs/helper-api-error@1.11.6':
+ resolution: {integrity: sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==}
+
+ '@webassemblyjs/helper-buffer@1.12.1':
+ resolution: {integrity: sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==}
+
+ '@webassemblyjs/helper-numbers@1.11.6':
+ resolution: {integrity: sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==}
+
+ '@webassemblyjs/helper-wasm-bytecode@1.11.6':
+ resolution: {integrity: sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==}
+
+ '@webassemblyjs/helper-wasm-section@1.12.1':
+ resolution: {integrity: sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==}
+
+ '@webassemblyjs/ieee754@1.11.6':
+ resolution: {integrity: sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==}
+
+ '@webassemblyjs/leb128@1.11.6':
+ resolution: {integrity: sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==}
+
+ '@webassemblyjs/utf8@1.11.6':
+ resolution: {integrity: sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==}
+
+ '@webassemblyjs/wasm-edit@1.12.1':
+ resolution: {integrity: sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==}
+
+ '@webassemblyjs/wasm-gen@1.12.1':
+ resolution: {integrity: sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==}
+
+ '@webassemblyjs/wasm-opt@1.12.1':
+ resolution: {integrity: sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==}
+
+ '@webassemblyjs/wasm-parser@1.12.1':
+ resolution: {integrity: sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==}
+
+ '@webassemblyjs/wast-printer@1.12.1':
+ resolution: {integrity: sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==}
+
'@xmldom/xmldom@0.8.10':
resolution: {integrity: sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==}
engines: {node: '>=10.0.0'}
@@ -2714,9 +2877,20 @@ packages:
'@xterm/xterm@5.5.0':
resolution: {integrity: sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==}
+ '@xtuc/ieee754@1.2.0':
+ resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==}
+
+ '@xtuc/long@4.2.2':
+ resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==}
+
abbrev@1.1.1:
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
+ acorn-import-assertions@1.9.0:
+ resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==}
+ peerDependencies:
+ acorn: ^8
+
acorn-jsx@5.3.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
@@ -2726,8 +2900,8 @@ packages:
resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==}
engines: {node: '>=0.4.0'}
- acorn@8.11.3:
- resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==}
+ acorn@8.12.0:
+ resolution: {integrity: sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==}
engines: {node: '>=0.4.0'}
hasBin: true
@@ -2746,6 +2920,11 @@ packages:
resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
engines: {node: '>=8'}
+ ajv-keywords@3.5.2:
+ resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==}
+ peerDependencies:
+ ajv: ^6.9.1
+
ajv@6.12.6:
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
@@ -2784,6 +2963,18 @@ packages:
aproba@2.0.0:
resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
+ archiver-utils@2.1.0:
+ resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==}
+ engines: {node: '>= 6'}
+
+ archiver-utils@3.0.4:
+ resolution: {integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==}
+ engines: {node: '>= 10'}
+
+ archiver@5.3.2:
+ resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==}
+ engines: {node: '>= 10'}
+
are-we-there-yet@2.0.0:
resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==}
engines: {node: '>=10'}
@@ -2795,8 +2986,8 @@ packages:
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
- aria-query@5.3.0:
- resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
+ aria-query@5.1.3:
+ resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==}
array-buffer-byte-length@1.0.1:
resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==}
@@ -2806,6 +2997,10 @@ packages:
resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==}
engines: {node: '>= 0.4'}
+ array-includes@3.1.8:
+ resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==}
+ engines: {node: '>= 0.4'}
+
array-union@2.1.0:
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
engines: {node: '>=8'}
@@ -2814,8 +3009,8 @@ packages:
resolution: {integrity: sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==}
engines: {node: '>= 0.4'}
- array.prototype.findlast@1.2.4:
- resolution: {integrity: sha512-BMtLxpV+8BD+6ZPFIWmnUBpQoy+A+ujcg4rhp2iwCRJYA7PEh2MS4NL3lz8EiDlLrJPp2hg9qWihr5pd//jcGw==}
+ array.prototype.findlast@1.2.5:
+ resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==}
engines: {node: '>= 0.4'}
array.prototype.findlastindex@1.2.4:
@@ -2833,13 +3028,17 @@ packages:
array.prototype.toreversed@1.1.2:
resolution: {integrity: sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==}
- array.prototype.tosorted@1.1.3:
- resolution: {integrity: sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==}
+ array.prototype.tosorted@1.1.4:
+ resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==}
+ engines: {node: '>= 0.4'}
arraybuffer.prototype.slice@1.0.3:
resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==}
engines: {node: '>= 0.4'}
+ asn1@0.2.6:
+ resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==}
+
assertion-error@1.1.0:
resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}
@@ -2850,12 +3049,12 @@ packages:
resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==}
engines: {node: '>=4'}
+ async-lock@1.4.1:
+ resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==}
+
async@3.2.5:
resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==}
- asynciterator.prototype@1.0.0:
- resolution: {integrity: sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==}
-
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
@@ -2863,16 +3062,38 @@ packages:
resolution: {integrity: sha512-j1QzY8iPNPG4o4xmO3ptzpRxTciqD3MgEHtifP/YnJpIo58Xu+ne4BejlbkuaLfXn/nz6HFiw29bLpj2PNMdGg==}
engines: {node: '>= 0.4'}
- axe-core@4.7.0:
- resolution: {integrity: sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==}
+ available-typed-arrays@1.0.7:
+ resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
+ engines: {node: '>= 0.4'}
+
+ axe-core@4.9.1:
+ resolution: {integrity: sha512-QbUdXJVTpvUTHU7871ppZkdOLBeGUKBQWHkHrvN2V9IQWGMt61zf3B45BtzjxEJzYuj0JBjBZP/hmYS/R9pmAw==}
engines: {node: '>=4'}
- axobject-query@3.2.1:
- resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==}
+ axobject-query@3.1.1:
+ resolution: {integrity: sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==}
+
+ b4a@1.6.6:
+ resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==}
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+ bare-events@2.4.2:
+ resolution: {integrity: sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==}
+
+ bare-fs@2.3.1:
+ resolution: {integrity: sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==}
+
+ bare-os@2.4.0:
+ resolution: {integrity: sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==}
+
+ bare-path@2.1.3:
+ resolution: {integrity: sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==}
+
+ bare-stream@2.1.3:
+ resolution: {integrity: sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==}
+
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
@@ -2880,12 +3101,18 @@ packages:
resolution: {integrity: sha512-8PzkB0arJFV4jJWSGOYR+OEic6aeKMu/osRhBULN6RY0ykby6LKhbmuQ5ublvaas5BOwboah5D87nrHyuh8PPA==}
engines: {node: '>=10.0.0'}
+ bcrypt-pbkdf@1.0.2:
+ resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==}
+
bcrypt@5.1.1:
resolution: {integrity: sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==}
engines: {node: '>= 10.0.0'}
- better-sqlite3@10.0.0:
- resolution: {integrity: sha512-rOz0JY8bt9oMgrFssP7GnvA5R3yln73y/NizzWqy3WlFth8Ux8+g4r/N9fjX97nn4X1YX6MTER2doNpTu5pqiA==}
+ better-sqlite3@11.1.1:
+ resolution: {integrity: sha512-bAlQQb7gwCgxNpDYafK0O4AaIOiTwA7srfqRtBbw0Nsiq6P+qxEYGl3hLw+9C5jX2FVjKW7oxkSouxlJ+3VX8A==}
+
+ big.js@5.2.2:
+ resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==}
binary-extensions@2.2.0:
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
@@ -2912,12 +3139,19 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
+ buffer-crc32@0.2.13:
+ resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
+
buffer-from@1.1.2:
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
buffer@5.7.1:
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
+ buildcheck@0.0.6:
+ resolution: {integrity: sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==}
+ engines: {node: '>=10.0.0'}
+
builtins@5.0.1:
resolution: {integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==}
@@ -2925,6 +3159,10 @@ packages:
resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
engines: {node: '>=10.16.0'}
+ byline@5.0.0:
+ resolution: {integrity: sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==}
+ engines: {node: '>=0.10.0'}
+
cac@6.7.14:
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
engines: {node: '>=8'}
@@ -2990,14 +3228,14 @@ packages:
chroma-js@2.4.2:
resolution: {integrity: sha512-U9eDw6+wt7V8z5NncY2jJfZa+hUH8XEj8FQHgFJTrUFnJfXYf4Ml4adI2vXZOjqRDpFWtYVWypDfZwnJ+HIR4A==}
+ chrome-trace-event@1.0.3:
+ resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==}
+ engines: {node: '>=6.0'}
+
clean-stack@2.2.0:
resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
engines: {node: '>=6'}
- cli-color@2.0.3:
- resolution: {integrity: sha512-OkoZnxyC4ERN3zLzZaY9Emb7f/MhBOIpePv0Ycok0fJYT+Ouo00UBEIwsVsr0yoow++n5YWlSUgST9GKhNHiRQ==}
- engines: {node: '>=0.10'}
-
cli-cursor@3.1.0:
resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
engines: {node: '>=8'}
@@ -3063,9 +3301,12 @@ packages:
resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
engines: {node: '>=14'}
- commander@9.5.0:
- resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
- engines: {node: ^12.20.0 || >=14}
+ commander@2.20.3:
+ resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
+
+ compress-commons@4.1.2:
+ resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==}
+ engines: {node: '>= 10'}
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
@@ -3099,6 +3340,22 @@ packages:
core-js-pure@3.36.0:
resolution: {integrity: sha512-cN28qmhRNgbMZZMc/RFu5w8pK9VJzpb2rJVR/lHuZJKwmXnoWOpXmMkxqBB514igkp1Hu8WGROsiOAzUcKdHOQ==}
+ core-util-is@1.0.3:
+ resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
+
+ cpu-features@0.0.10:
+ resolution: {integrity: sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==}
+ engines: {node: '>=10.0.0'}
+
+ crc-32@1.2.2:
+ resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
+ engines: {node: '>=0.8'}
+ hasBin: true
+
+ crc32-stream@4.0.3:
+ resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==}
+ engines: {node: '>= 10'}
+
create-require@1.1.1:
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
@@ -3126,9 +3383,6 @@ packages:
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
- d@1.0.1:
- resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==}
-
damerau-levenshtein@1.0.8:
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
@@ -3143,6 +3397,18 @@ packages:
resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
engines: {node: '>=18'}
+ data-view-buffer@1.0.1:
+ resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==}
+ engines: {node: '>= 0.4'}
+
+ data-view-byte-length@1.0.1:
+ resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==}
+ engines: {node: '>= 0.4'}
+
+ data-view-byte-offset@1.0.0:
+ resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==}
+ engines: {node: '>= 0.4'}
+
date-fns@2.30.0:
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
engines: {node: '>=0.11'}
@@ -3167,6 +3433,15 @@ packages:
supports-color:
optional: true
+ debug@4.3.5:
+ resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
decimal.js@10.4.3:
resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
@@ -3178,6 +3453,10 @@ packages:
resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==}
engines: {node: '>=6'}
+ deep-equal@2.2.3:
+ resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==}
+ engines: {node: '>= 0.4'}
+
deep-extend@0.6.0:
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
engines: {node: '>=4.0.0'}
@@ -3242,21 +3521,34 @@ packages:
resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
engines: {node: '>=0.3.1'}
- difflib@0.2.4:
- resolution: {integrity: sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w==}
-
dir-glob@3.0.1:
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
engines: {node: '>=8'}
+ docker-compose@0.24.8:
+ resolution: {integrity: sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw==}
+ engines: {node: '>= 6.0.0'}
+
+ docker-modem@3.0.8:
+ resolution: {integrity: sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ==}
+ engines: {node: '>= 8.0'}
+
+ docker-modem@5.0.3:
+ resolution: {integrity: sha512-89zhop5YVhcPEt5FpUFGr3cDyceGhq/F9J+ZndQ4KfqNvfbJpPMfgeixFgUj5OjCYAboElqODxY5Z1EBsSa6sg==}
+ engines: {node: '>= 8.0'}
+
+ dockerode@3.3.5:
+ resolution: {integrity: sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA==}
+ engines: {node: '>= 8.0'}
+
+ dockerode@4.0.2:
+ resolution: {integrity: sha512-9wM1BVpVMFr2Pw3eJNXrYYt6DT9k0xMcsSCjtPvyQ+xa1iPg/Mo3T/gUcwI0B2cczqCeCYRPF8yFYDwtFXT0+w==}
+ engines: {node: '>= 8.0'}
+
doctrine@2.1.0:
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
engines: {node: '>=0.10.0'}
- doctrine@3.0.0:
- resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
- engines: {node: '>=6.0.0'}
-
dom-helpers@5.2.1:
resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
@@ -3282,16 +3574,12 @@ packages:
resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
engines: {node: '>=12'}
- dreamopt@0.8.0:
- resolution: {integrity: sha512-vyJTp8+mC+G+5dfgsY+r3ckxlz+QMX40VjPQsZc5gxVAxLmi64TBoVkP54A/pRAXMXsbu2GMMBrZPxNv23waMg==}
- engines: {node: '>=0.4.0'}
-
- drizzle-kit@0.21.4:
- resolution: {integrity: sha512-Nxcc1ONJLRgbhmR+azxjNF9Ly9privNLEIgW53c92whb4xp8jZLH1kMCh/54ci1mTMuYxPdOukqLwJ8wRudNwA==}
+ drizzle-kit@0.22.7:
+ resolution: {integrity: sha512-9THPCb2l1GPt7wxhws9LvTR0YG565ZlVgTuqGMwjs590Kch1pXu4GyjEArVijSF5m0OBj3qgdeKmuJXhKXgWFw==}
hasBin: true
- drizzle-orm@0.30.10:
- resolution: {integrity: sha512-IRy/QmMWw9lAQHpwbUh1b8fcn27S/a9zMIzqea1WNOxK9/4EB8gIo+FZWLiPXzl2n9ixGSv8BhsLZiOppWEwBw==}
+ drizzle-orm@0.31.2:
+ resolution: {integrity: sha512-QnenevbnnAzmbNzQwbhklvIYrDE8YER8K7kSrAWQSV1YvFCdSQPzj+jzqRdTSsV2cDqSpQ0NXGyL1G9I43LDLg==}
peerDependencies:
'@aws-sdk/client-rds-data': '>=3'
'@cloudflare/workers-types': '>=3'
@@ -3301,6 +3589,7 @@ packages:
'@op-engineering/op-sqlite': '>=2'
'@opentelemetry/api': ^1.4.1
'@planetscale/database': '>=1'
+ '@tidbcloud/serverless': '*'
'@types/better-sqlite3': '*'
'@types/pg': '*'
'@types/react': '>=18'
@@ -3335,6 +3624,8 @@ packages:
optional: true
'@planetscale/database':
optional: true
+ '@tidbcloud/serverless':
+ optional: true
'@types/better-sqlite3':
optional: true
'@types/pg':
@@ -3382,24 +3673,32 @@ packages:
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+ emojis-list@3.0.0:
+ resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==}
+ engines: {node: '>= 4'}
+
enabled@2.0.0:
resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==}
end-of-stream@1.4.4:
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
+ enhanced-resolve@5.16.1:
+ resolution: {integrity: sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==}
+ engines: {node: '>=10.13.0'}
+
entities@4.5.0:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
- env-paths@3.0.0:
- resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==}
- engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-
es-abstract@1.22.4:
resolution: {integrity: sha512-vZYJlk2u6qHYxBOTjAeg7qUxHdNfih64Uu2J8QqWgXZ2cri0ZpJAkzDUK/q593+mvKwlxyaxr6F1Q+3LKoQRgg==}
engines: {node: '>= 0.4'}
+ es-abstract@1.23.3:
+ resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==}
+ engines: {node: '>= 0.4'}
+
es-array-method-boxes-properly@1.0.0:
resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==}
@@ -3411,14 +3710,28 @@ packages:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
- es-iterator-helpers@1.0.17:
- resolution: {integrity: sha512-lh7BsUqelv4KUbR5a/ZTaGGIMLCjPGPqJ6q+Oq24YP0RdyptX1uzm4vvaqzk7Zx3bpl/76YLTTDj9L7uYQ92oQ==}
+ es-get-iterator@1.1.3:
+ resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==}
+
+ es-iterator-helpers@1.0.19:
+ resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==}
+ engines: {node: '>= 0.4'}
+
+ es-module-lexer@1.5.3:
+ resolution: {integrity: sha512-i1gCgmR9dCl6Vil6UKPI/trA69s08g/syhiDK9TG0Nf1RJjjFI+AzoWW7sPufzkgYAn861skuCwJa0pIIHYxvg==}
+
+ es-object-atoms@1.0.0:
+ resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==}
engines: {node: '>= 0.4'}
es-set-tostringtag@2.0.2:
resolution: {integrity: sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==}
engines: {node: '>= 0.4'}
+ es-set-tostringtag@2.0.3:
+ resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==}
+ engines: {node: '>= 0.4'}
+
es-shim-unscopables@1.0.2:
resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==}
@@ -3426,19 +3739,6 @@ packages:
resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
engines: {node: '>= 0.4'}
- es5-ext@0.10.62:
- resolution: {integrity: sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==}
- engines: {node: '>=0.10'}
-
- es6-iterator@2.0.3:
- resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==}
-
- es6-symbol@3.1.3:
- resolution: {integrity: sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==}
-
- es6-weak-map@2.0.3:
- resolution: {integrity: sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==}
-
esbuild-register@3.5.0:
resolution: {integrity: sha512-+4G/XmakeBAsvJuDugJvtyF1x+XJT4FMocynNpxrvEBViirpfUn2PgNpCHedfWhF4WokNsO/OvMKrmJOIJsI5A==}
peerDependencies:
@@ -3482,8 +3782,8 @@ packages:
peerDependencies:
eslint: '>=7.0.0'
- eslint-config-turbo@1.13.3:
- resolution: {integrity: sha512-if/QtwEiWZ5b7Bg8yZBPSvS0TeCG2Zvfa/+XBYANS7uSYucjmW+BBC8enJB0PqpB/YLGGOumeo3x7h1Nuba9iw==}
+ eslint-config-turbo@2.0.6:
+ resolution: {integrity: sha512-PkRjFnZUZWPcrYT4Xoi5OWOUtnn6xVGh88I6TsayiH4AQZuLs/MDmzfJRK+PiWIrI7Q7sbsVEQP+nUyyRE3uAw==}
peerDependencies:
eslint: '>6.6.0'
@@ -3521,8 +3821,8 @@ packages:
'@typescript-eslint/parser':
optional: true
- eslint-plugin-jsx-a11y@6.8.0:
- resolution: {integrity: sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==}
+ eslint-plugin-jsx-a11y@6.9.0:
+ resolution: {integrity: sha512-nOFOCaJG2pYqORjK19lqPqxMO/JpvdCZdPtNdxY3kvom3jTvkAbOvQvD8wuD0G8BYR0IGAGYDlzqWJOh/ybn2g==}
engines: {node: '>=4.0'}
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
@@ -3533,33 +3833,41 @@ packages:
peerDependencies:
eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0
- eslint-plugin-react@7.34.1:
- resolution: {integrity: sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==}
+ eslint-plugin-react@7.34.3:
+ resolution: {integrity: sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA==}
engines: {node: '>=4'}
peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
- eslint-plugin-turbo@1.13.3:
- resolution: {integrity: sha512-RjmlnqYsEqnJ+U3M3IS5jLJDjWv5NsvReCpsC61n5pJ4JMHTZ/lU0EIoL1ccuL1L5wP0APzdXdByBxERcPQ+Nw==}
+ eslint-plugin-turbo@2.0.6:
+ resolution: {integrity: sha512-yGnpMvyBxI09ZrF5bGpaniBz57MiExTCsRnNxP+JnbMFD+xU3jG3ukRzehVol8LYNdC/G7E4HoH+x7OEpoSGAQ==}
peerDependencies:
eslint: '>6.6.0'
- eslint-scope@7.2.2:
- resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ eslint-scope@5.1.1:
+ resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
+ engines: {node: '>=8.0.0'}
+
+ eslint-scope@8.0.1:
+ resolution: {integrity: sha512-pL8XjgP4ZOmmwfFE8mEhSxA7ZY4C+LWyqjQ3o4yWkkmD0qcMT9kkW3zWHOczhWcjTSgqycYAgwSlXvZltv65og==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
eslint-visitor-keys@3.4.3:
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- eslint@8.57.0:
- resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ eslint-visitor-keys@4.0.0:
+ resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint@9.6.0:
+ resolution: {integrity: sha512-ElQkdLMEEqQNM9Njff+2Y4q2afHk7JpkPvrd7Xh7xefwgQynqPxwf55J7di9+MEibWUGdNjFF9ITG9Pck5M84w==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
hasBin: true
- espree@9.6.1:
- resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ espree@10.1.0:
+ resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
esprima@4.0.1:
resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
@@ -3574,6 +3882,10 @@ packages:
resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
engines: {node: '>=4.0'}
+ estraverse@4.3.0:
+ resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==}
+ engines: {node: '>=4.0'}
+
estraverse@5.3.0:
resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
engines: {node: '>=4.0'}
@@ -3585,8 +3897,9 @@ packages:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'}
- event-emitter@0.3.5:
- resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==}
+ events@3.3.0:
+ resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
+ engines: {node: '>=0.8.x'}
execa@5.1.1:
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
@@ -3600,9 +3913,6 @@ packages:
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
engines: {node: '>=6'}
- ext@1.7.0:
- resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==}
-
external-editor@3.1.0:
resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
engines: {node: '>=4'}
@@ -3610,6 +3920,9 @@ packages:
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+ fast-fifo@1.3.2:
+ resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==}
+
fast-glob@3.3.2:
resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
engines: {node: '>=8.6.0'}
@@ -3633,9 +3946,9 @@ packages:
resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
engines: {node: '>=8'}
- file-entry-cache@6.0.1:
- resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
- engines: {node: ^10.12.0 || >=12.0.0}
+ file-entry-cache@8.0.0:
+ resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
+ engines: {node: '>=16.0.0'}
file-uri-to-path@1.0.0:
resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
@@ -3648,12 +3961,12 @@ packages:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
engines: {node: '>=10'}
- flag-icons@7.2.2:
- resolution: {integrity: sha512-9cC2y77npwpNJ1Whl3iACVFd/vR7ZQe9AwN8BRDJU2sZROMpEzIC3tOT1qbRRm/QS9b9UTXBHEKAO4I6d7L0xg==}
+ flag-icons@7.2.3:
+ resolution: {integrity: sha512-X2gUdteNuqdNqob2KKTJTS+ZCvyWeLCtDz9Ty8uJP17Y4o82Y+U/Vd4JNrdwTAjagYsRznOn9DZ+E/Q52qbmqg==}
- flat-cache@3.2.0:
- resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
- engines: {node: ^10.12.0 || >=12.0.0}
+ flat-cache@4.0.1:
+ resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
+ engines: {node: '>=16'}
flatted@3.2.9:
resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==}
@@ -3732,6 +4045,10 @@ packages:
resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
engines: {node: '>=6'}
+ get-port@5.1.1:
+ resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==}
+ engines: {node: '>=8'}
+
get-stream@6.0.1:
resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
engines: {node: '>=10'}
@@ -3762,13 +4079,16 @@ packages:
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
engines: {node: '>=10.13.0'}
+ glob-to-regexp@0.4.1:
+ resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==}
+
glob@10.3.10:
resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==}
engines: {node: '>=16 || 14 >=14.17'}
hasBin: true
- glob@10.4.1:
- resolution: {integrity: sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==}
+ glob@10.4.2:
+ resolution: {integrity: sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==}
engines: {node: '>=16 || 14 >=14.18'}
hasBin: true
@@ -3776,11 +4096,6 @@ packages:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
deprecated: Glob versions prior to v9 are no longer supported
- glob@8.1.0:
- resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
- engines: {node: '>=12'}
- deprecated: Glob versions prior to v9 are no longer supported
-
global@4.4.0:
resolution: {integrity: sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==}
@@ -3788,9 +4103,9 @@ packages:
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
engines: {node: '>=4'}
- globals@13.24.0:
- resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
- engines: {node: '>=8'}
+ globals@14.0.0:
+ resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
+ engines: {node: '>=18'}
globalthis@1.0.3:
resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==}
@@ -3825,9 +4140,6 @@ packages:
engines: {node: '>=0.4.7'}
hasBin: true
- hanji@0.0.5:
- resolution: {integrity: sha512-Abxw1Lq+TnYiL4BueXqMau222fPSPMFtya8HdpWsz/xVAhifXou71mPh/kY2+08RgFcVccjG3uZHs6K5HAe3zw==}
-
has-bigints@1.0.2:
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
@@ -3846,6 +4158,10 @@ packages:
resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==}
engines: {node: '>= 0.4'}
+ has-proto@1.0.3:
+ resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==}
+ engines: {node: '>= 0.4'}
+
has-symbols@1.0.3:
resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
engines: {node: '>= 0.4'}
@@ -3861,12 +4177,13 @@ packages:
resolution: {integrity: sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==}
engines: {node: '>= 0.4'}
+ hasown@2.0.2:
+ resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+ engines: {node: '>= 0.4'}
+
header-case@1.0.1:
resolution: {integrity: sha512-i0q9mkOeSuhXw6bGgiQCCBgY/jlZuV/7dZXyZ9c6LcBrqwvT8eT719E9uxE5LiZftdl+z81Ugbg/VvXV4OJOeQ==}
- heap@0.2.7:
- resolution: {integrity: sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==}
-
html-encoding-sniffer@4.0.0:
resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
engines: {node: '>=18'}
@@ -3963,6 +4280,10 @@ packages:
resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==}
engines: {node: '>= 12'}
+ is-arguments@1.1.1:
+ resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==}
+ engines: {node: '>= 0.4'}
+
is-array-buffer@3.0.4:
resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==}
engines: {node: '>= 0.4'}
@@ -3992,6 +4313,10 @@ packages:
is-core-module@2.13.1:
resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
+ is-data-view@1.0.1:
+ resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==}
+ engines: {node: '>= 0.4'}
+
is-date-object@1.0.5:
resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
engines: {node: '>= 0.4'}
@@ -4036,6 +4361,10 @@ packages:
resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==}
engines: {node: '>= 0.4'}
+ is-negative-zero@2.0.3:
+ resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
+ engines: {node: '>= 0.4'}
+
is-number-object@1.0.7:
resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==}
engines: {node: '>= 0.4'}
@@ -4059,9 +4388,6 @@ packages:
is-potential-custom-element-name@1.0.1:
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
- is-promise@2.2.2:
- resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==}
-
is-property@1.0.2:
resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==}
@@ -4075,6 +4401,10 @@ packages:
is-shared-array-buffer@1.0.2:
resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==}
+ is-shared-array-buffer@1.0.3:
+ resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==}
+ engines: {node: '>= 0.4'}
+
is-stream@2.0.1:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
engines: {node: '>=8'}
@@ -4115,6 +4445,9 @@ packages:
resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==}
engines: {node: '>=12.13'}
+ isarray@1.0.0:
+ resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
+
isarray@2.0.5:
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
@@ -4156,11 +4489,15 @@ packages:
resolution: {integrity: sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==}
engines: {node: '>=14'}
+ jest-worker@27.5.1:
+ resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
+ engines: {node: '>= 10.13.0'}
+
jose@5.2.2:
resolution: {integrity: sha512-/WByRr4jDcsKlvMd1dRJnPfS1GVO3WuKyaurJ/vvXcOaUQO8rnNObCQMlv/5uCceVQIq5Q4WLF44ohsdiTohdg==}
- jotai@2.8.2:
- resolution: {integrity: sha512-AU+EU82YqP94izfbGYQQL3oa/06gmn+Ijf/CKx0QybAURtbqh2e4N6zA2fxeIh0JEUgASF6z5IhagJ8NicR95A==}
+ jotai@2.8.4:
+ resolution: {integrity: sha512-f6jwjhBJcDtpeauT2xH01gnqadKEySwwt1qNBLvAXcnojkmb76EdqRt05Ym8IamfHGAQz2qMKAwftnyjeSoHAA==}
engines: {node: '>=12.20.0'}
peerDependencies:
'@types/react': '>=17.0.0'
@@ -4201,9 +4538,8 @@ packages:
json-buffer@3.0.1:
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
- json-diff@0.9.0:
- resolution: {integrity: sha512-cVnggDrVkAAA3OvFfHpFEhOnmcsUpleEKq4d4O8sQWWSH40MBrWstKigVB1kGrgLWzuom+7rRdaCsnBD6VyObQ==}
- hasBin: true
+ json-parse-even-better-errors@2.3.1:
+ resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
json-schema-traverse@0.4.1:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
@@ -4254,6 +4590,10 @@ packages:
resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==}
engines: {node: '>=0.10'}
+ lazystream@1.0.1:
+ resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==}
+ engines: {node: '>= 0.6.3'}
+
levn@0.4.1:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'}
@@ -4264,6 +4604,14 @@ packages:
linkifyjs@4.1.3:
resolution: {integrity: sha512-auMesunaJ8yfkHvK4gfg1K0SaKX/6Wn9g2Aac/NwX+l5VdmFZzo/hdPGxEOETj+ryRa4/fiOPjeeKURSAJx1sg==}
+ loader-runner@4.3.0:
+ resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==}
+ engines: {node: '>=6.11.5'}
+
+ loader-utils@2.0.4:
+ resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==}
+ engines: {node: '>=8.9.0'}
+
local-pkg@0.5.0:
resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==}
engines: {node: '>=14'}
@@ -4275,17 +4623,26 @@ packages:
lodash.defaults@4.2.0:
resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==}
+ lodash.difference@4.5.0:
+ resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==}
+
+ lodash.flatten@4.4.0:
+ resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==}
+
lodash.get@4.4.2:
resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
lodash.isarguments@3.1.0:
resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==}
+ lodash.isplainobject@4.0.6:
+ resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
+
lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
- lodash.throttle@4.1.1:
- resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==}
+ lodash.union@4.6.0:
+ resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==}
lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
@@ -4337,9 +4694,6 @@ packages:
resolution: {integrity: sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==}
engines: {node: '>=16.14'}
- lru-queue@0.1.0:
- resolution: {integrity: sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==}
-
m3u8-parser@7.1.0:
resolution: {integrity: sha512-7N+pk79EH4oLKPEYdgRXgAsKDyA/VCo0qCHlUwacttQA0WqsjZQYmNfywMvjlY9MpEBVZEt0jKFd73Kv15EBYQ==}
@@ -4361,8 +4715,8 @@ packages:
make-error@1.3.6:
resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
- mantine-react-table@2.0.0-beta.3:
- resolution: {integrity: sha512-mA4KYss+E2hPIT8oU4j7uYhLGCNM6SPd0yzbrYRbtPujRxpe7fl0sCxv+1ovyZstu24Aaqvb8HwXGe23Te5RLQ==}
+ mantine-react-table@2.0.0-beta.5:
+ resolution: {integrity: sha512-jd5gEI2zCU+5tkGvf1puq7XPCY0/WJOG+1Zd1mJkyocRvtT/8gV6HMJ0ARR0bYLZF0NWz0TMQyFCl9dtI/mVEg==}
engines: {node: '>=16'}
peerDependencies:
'@mantine/core': ^7.9
@@ -4381,9 +4735,6 @@ packages:
mdurl@2.0.0:
resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
- memoizee@0.4.15:
- resolution: {integrity: sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==}
-
merge-stream@2.0.0:
resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
@@ -4440,10 +4791,6 @@ packages:
resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
engines: {node: '>=8'}
- minipass@7.0.4:
- resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==}
- engines: {node: '>=16 || 14 >=14.17'}
-
minipass@7.1.2:
resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -4489,14 +4836,17 @@ packages:
engines: {node: '>=8', npm: '>=5'}
hasBin: true
- mysql2@3.9.8:
- resolution: {integrity: sha512-+5JKNjPuks1FNMoy9TYpl77f+5frbTklz7eb3XDwbpsERRLEeXiW2PDEkakYF50UuKU2qwfGnyXpKYvukv8mGA==}
+ mysql2@3.10.1:
+ resolution: {integrity: sha512-6zo1T3GILsXMCex3YEu7hCz2OXLUarxFsxvFcUHWMpkPtmZLeTTWgRdc1gWyNJiYt6AxITmIf9bZDRy/jAfWew==}
engines: {node: '>= 8.0'}
named-placeholders@1.1.3:
resolution: {integrity: sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==}
engines: {node: '>=12.0.0'}
+ nan@2.19.0:
+ resolution: {integrity: sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==}
+
nanoid@3.3.7:
resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -4515,14 +4865,14 @@ packages:
resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==}
engines: {node: '>= 0.4.0'}
- next-auth@5.0.0-beta.18:
- resolution: {integrity: sha512-x55L8wZb8PcPGCYA3e/l9tdpd7YL3FDuhas4W8pxq3PjrWJ9OoDxNN0otK9axJamJBbBgjfzTJjVQB6hXoe0ZQ==}
+ next-auth@5.0.0-beta.19:
+ resolution: {integrity: sha512-YHu1igcAxZPh8ZB7GIM93dqgY6gcAzq66FOhQFheAdOx1raxNcApt05nNyNCSB6NegSiyJ4XOPsaNow4pfDmsg==}
peerDependencies:
'@simplewebauthn/browser': ^9.0.1
'@simplewebauthn/server': ^9.0.2
- next: ^14
+ next: ^14 || ^15.0.0-0
nodemailer: ^6.6.5
- react: ^18.2.0
+ react: ^18.2.0 || ^19.0.0-0
peerDependenciesMeta:
'@simplewebauthn/browser':
optional: true
@@ -4534,11 +4884,8 @@ packages:
next-international@1.2.4:
resolution: {integrity: sha512-JQvp+h2iSgA/t8hu5S/Lwow1ZErJutQRdpnplxjv4VTlCiND8T95fYih8BjkHcVhQbtM+Wu9Mb1CM32wD9hlWQ==}
- next-tick@1.1.0:
- resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
-
- next@14.2.3:
- resolution: {integrity: sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==}
+ next@14.2.4:
+ resolution: {integrity: sha512-R8/V7vugY+822rsQGQCjoLhMuC9oFj9SOi4Cl4b2wjDrseD0LRZ10W7R6Czo4w9ZznVSshKjuIomsRjvm9EKJQ==}
engines: {node: '>=18.17.0'}
hasBin: true
peerDependencies:
@@ -4578,6 +4925,12 @@ packages:
encoding:
optional: true
+ node-loader@2.0.0:
+ resolution: {integrity: sha512-I5VN34NO4/5UYJaUBtkrODPWxbobrE4hgDqPrjB25yPkonFhCmZ146vTH+Zg417E9Iwoh1l/MbRs1apc5J295Q==}
+ engines: {node: '>= 10.13.0'}
+ peerDependencies:
+ webpack: ^5.0.0
+
node-plop@0.26.3:
resolution: {integrity: sha512-Cov028YhBZ5aB7MdMWJEmwyBig43aGL5WT4vdoB28Oitau1zZAcHUn8Sgfk9HM33TqhtLJ9PlM/O0Mv+QpV/4Q==}
engines: {node: '>=8.9.4'}
@@ -4612,6 +4965,9 @@ packages:
oauth4webapi@2.10.3:
resolution: {integrity: sha512-9FkXEXfzVKzH63GUOZz1zMr3wBaICSzk6DLXx+CGdrQ10ItNk2ePWzYYc1fdmKq1ayGFb2aX97sRCoZ2s0mkDw==}
+ oauth4webapi@2.11.0:
+ resolution: {integrity: sha512-UchKtYyLuWwrZLB7g9Px+0JScIAK/bD1awLK5gdOCIs0cjxc7ol+ESL+Rr4nOmEJSvib6kEgvdUHvXm8DrZQ+w==}
+
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
@@ -4619,6 +4975,10 @@ packages:
object-inspect@1.13.1:
resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
+ object-is@1.1.6:
+ resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==}
+ engines: {node: '>= 0.4'}
+
object-keys@1.1.1:
resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
engines: {node: '>= 0.4'}
@@ -4627,19 +4987,24 @@ packages:
resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==}
engines: {node: '>= 0.4'}
- object.entries@1.1.7:
- resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==}
+ object.entries@1.1.8:
+ resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==}
engines: {node: '>= 0.4'}
object.fromentries@2.0.7:
resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==}
engines: {node: '>= 0.4'}
+ object.fromentries@2.0.8:
+ resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==}
+ engines: {node: '>= 0.4'}
+
object.groupby@1.0.2:
resolution: {integrity: sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==}
- object.hasown@1.1.3:
- resolution: {integrity: sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==}
+ object.hasown@1.1.4:
+ resolution: {integrity: sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==}
+ engines: {node: '>= 0.4'}
object.omit@3.0.0:
resolution: {integrity: sha512-EO+BCv6LJfu+gBIF3ggLicFebFLN5zqzz/WWJlMFfkMyGth+oBkhxzDl0wx2W4GkLzuQs/FsSkXZb2IMWQqmBQ==}
@@ -4653,6 +5018,10 @@ packages:
resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==}
engines: {node: '>= 0.4'}
+ object.values@1.2.0:
+ resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==}
+ engines: {node: '>= 0.4'}
+
once@1.4.0:
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
@@ -4710,6 +5079,9 @@ packages:
resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==}
engines: {node: '>= 14'}
+ package-json-from-dist@1.0.0:
+ resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==}
+
param-case@2.1.1:
resolution: {integrity: sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==}
@@ -4745,10 +5117,6 @@ packages:
path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
- path-scurry@1.11.0:
- resolution: {integrity: sha512-LNHTaVkzaYaLGlO+0u3rQTz7QrHTFOuKyba9JMTQutkmtNew8dw8wOD7mTU/5fCPZzCWpfW0XnQKzY61P0aTaw==}
- engines: {node: '>=16 || 14 >=14.17'}
-
path-scurry@1.11.1:
resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
engines: {node: '>=16 || 14 >=14.18'}
@@ -4766,9 +5134,6 @@ packages:
picocolors@1.0.0:
resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
- picocolors@1.0.1:
- resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
-
picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
@@ -4780,6 +5145,10 @@ packages:
pkg-types@1.0.3:
resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==}
+ possible-typed-array-names@1.0.0:
+ resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==}
+ engines: {node: '>= 0.4'}
+
postcss-js@4.0.1:
resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
engines: {node: ^12 || ^14 || >= 16}
@@ -4838,8 +5207,8 @@ packages:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
- prettier@3.2.5:
- resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==}
+ prettier@3.3.2:
+ resolution: {integrity: sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==}
engines: {node: '>=14'}
hasBin: true
@@ -4854,6 +5223,9 @@ packages:
resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==}
engines: {node: '>=6'}
+ process-nextick-args@2.0.1:
+ resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
+
process@0.11.10:
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
engines: {node: '>= 0.6.0'}
@@ -4861,6 +5233,13 @@ packages:
prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
+ proper-lockfile@4.1.2:
+ resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==}
+
+ properties-reader@2.3.0:
+ resolution: {integrity: sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==}
+ engines: {node: '>=14'}
+
prosemirror-changeset@2.2.1:
resolution: {integrity: sha512-J7msc6wbxB4ekDFj+n9gTW/jav/p53kdlivvuppHsrZXCaQdVgRghoZbSS3kwrRyAstRVQ4/+u5k7YfLgkkQvQ==}
@@ -4946,6 +5325,12 @@ packages:
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+ queue-tick@1.0.1:
+ resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==}
+
+ randombytes@2.1.0:
+ resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
+
rc@1.2.8:
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
hasBin: true
@@ -5028,10 +5413,16 @@ packages:
resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
engines: {node: '>=0.10.0'}
+ readable-stream@2.3.8:
+ resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==}
+
readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'}
+ readdir-glob@1.1.3:
+ resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==}
+
readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
@@ -5091,6 +5482,10 @@ packages:
resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
engines: {node: '>=8'}
+ retry@0.12.0:
+ resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==}
+ engines: {node: '>= 4'}
+
reusify@1.0.4:
resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
@@ -5135,6 +5530,13 @@ packages:
resolution: {integrity: sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==}
engines: {node: '>=0.4'}
+ safe-array-concat@1.1.2:
+ resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==}
+ engines: {node: '>=0.4'}
+
+ safe-buffer@5.1.2:
+ resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
+
safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
@@ -5152,8 +5554,8 @@ packages:
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
- sass@1.77.2:
- resolution: {integrity: sha512-eb4GZt1C3avsX3heBNlrc7I09nyT00IUuo4eFhAbeXWU2fvA7oXI53SxODVAA+zgZCk9aunAZgO+losjR3fAwA==}
+ sass@1.77.6:
+ resolution: {integrity: sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==}
engines: {node: '>=14.0.0'}
hasBin: true
@@ -5164,6 +5566,10 @@ packages:
scheduler@0.23.2:
resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
+ schema-utils@3.3.0:
+ resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==}
+ engines: {node: '>= 10.13.0'}
+
semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
@@ -5173,17 +5579,15 @@ packages:
engines: {node: '>=10'}
hasBin: true
- semver@7.6.2:
- resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==}
- engines: {node: '>=10'}
- hasBin: true
-
sentence-case@2.1.1:
resolution: {integrity: sha512-ENl7cYHaK/Ktwk5OTD+aDbQ3uC8IByu/6Bkg+HDv8Mm+XnBnppVNalcfJTNsp1ibstKh030/JKQQWglDvtKwEQ==}
seq-queue@0.0.5:
resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==}
+ serialize-javascript@6.0.2:
+ resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
+
server-only@0.0.1:
resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==}
@@ -5194,8 +5598,8 @@ packages:
resolution: {integrity: sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==}
engines: {node: '>= 0.4'}
- set-function-name@2.0.1:
- resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==}
+ set-function-name@2.0.2:
+ resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
engines: {node: '>= 0.4'}
shebang-command@2.0.0:
@@ -5213,6 +5617,10 @@ packages:
resolution: {integrity: sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==}
engines: {node: '>= 0.4'}
+ side-channel@1.0.6:
+ resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==}
+ engines: {node: '>= 0.4'}
+
siginfo@2.0.0:
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
@@ -5236,9 +5644,6 @@ packages:
resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==}
engines: {node: '>= 10'}
- sisteransi@1.0.5:
- resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
-
slash@3.0.0:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'}
@@ -5273,6 +5678,9 @@ packages:
spawn-command@0.0.2:
resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==}
+ split-ca@1.0.1:
+ resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==}
+
sprintf-js@1.1.3:
resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==}
@@ -5280,6 +5688,13 @@ packages:
resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==}
engines: {node: '>= 0.6'}
+ ssh-remote-port-forward@1.0.4:
+ resolution: {integrity: sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==}
+
+ ssh2@1.15.0:
+ resolution: {integrity: sha512-C0PHgX4h6lBxYx7hcXwu3QWdh4tg6tZZsTfXcdvc5caW/EMxaB4H9dWsl7qk+F7LAW762hp8VbXOX7x4xUYvEw==}
+ engines: {node: '>=10.16.0'}
+
stack-trace@0.0.10:
resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==}
@@ -5292,10 +5707,17 @@ packages:
std-env@3.7.0:
resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==}
+ stop-iteration-iterator@1.0.0:
+ resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==}
+ engines: {node: '>= 0.4'}
+
streamsearch@1.1.0:
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
engines: {node: '>=10.0.0'}
+ streamx@2.18.0:
+ resolution: {integrity: sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==}
+
string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
@@ -5304,19 +5726,37 @@ packages:
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
engines: {node: '>=12'}
- string.prototype.matchall@4.0.10:
- resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==}
+ string.prototype.includes@2.0.0:
+ resolution: {integrity: sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==}
+
+ string.prototype.matchall@4.0.11:
+ resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==}
+ engines: {node: '>= 0.4'}
string.prototype.trim@1.2.8:
resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==}
engines: {node: '>= 0.4'}
+ string.prototype.trim@1.2.9:
+ resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==}
+ engines: {node: '>= 0.4'}
+
string.prototype.trimend@1.0.7:
resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==}
+ string.prototype.trimend@1.0.8:
+ resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==}
+
string.prototype.trimstart@1.0.7:
resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==}
+ string.prototype.trimstart@1.0.8:
+ resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
+ engines: {node: '>= 0.4'}
+
+ string_decoder@1.1.1:
+ resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
+
string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
@@ -5399,21 +5839,61 @@ packages:
tabbable@6.2.0:
resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
+ tapable@2.2.1:
+ resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
+ engines: {node: '>=6'}
+
+ tar-fs@2.0.1:
+ resolution: {integrity: sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==}
+
tar-fs@2.1.1:
resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==}
+ tar-fs@3.0.6:
+ resolution: {integrity: sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==}
+
tar-stream@2.2.0:
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
engines: {node: '>=6'}
+ tar-stream@3.1.7:
+ resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==}
+
tar@6.2.0:
resolution: {integrity: sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==}
engines: {node: '>=10'}
+ terser-webpack-plugin@5.3.10:
+ resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==}
+ engines: {node: '>= 10.13.0'}
+ peerDependencies:
+ '@swc/core': '*'
+ esbuild: '*'
+ uglify-js: '*'
+ webpack: ^5.1.0
+ peerDependenciesMeta:
+ '@swc/core':
+ optional: true
+ esbuild:
+ optional: true
+ uglify-js:
+ optional: true
+
+ terser@5.31.0:
+ resolution: {integrity: sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==}
+ engines: {node: '>=10'}
+ hasBin: true
+
test-exclude@6.0.0:
resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
engines: {node: '>=8'}
+ testcontainers@10.10.0:
+ resolution: {integrity: sha512-XlAdr6XzxM9ywTc5D6xA97Ug8dCotDnrOgOqmy3vYnAwUYrzhzAwrp791g97QAMvDOYp2pxkJl/P4WxuUbGShw==}
+
+ text-decoder@1.1.0:
+ resolution: {integrity: sha512-TmLJNj6UgX8xcUZo4UDStGQtDiTzF7BzWlzn9g7UWrjkpHr5uJTK1ld16wZ3LXb2vb6jH8qU89dW5whuMdXYdw==}
+
text-hex@1.0.0:
resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==}
@@ -5427,9 +5907,6 @@ packages:
through@2.3.8:
resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
- timers-ext@0.1.7:
- resolution: {integrity: sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==}
-
tinybench@2.6.0:
resolution: {integrity: sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==}
@@ -5457,6 +5934,10 @@ packages:
resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
engines: {node: '>=0.6.0'}
+ tmp@0.2.3:
+ resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==}
+ engines: {node: '>=14.14'}
+
to-fast-properties@2.0.0:
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
engines: {node: '>=4'}
@@ -5531,48 +6012,51 @@ packages:
resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==}
engines: {node: '>=0.6.x'}
- tsx@4.11.0:
- resolution: {integrity: sha512-vzGGELOgAupsNVssAmZjbUDfdm/pWP4R+Kg8TVdsonxbXk0bEpE1qh0yV6/QxUVXaVlNemgcPajGdJJ82n3stg==}
+ tsx@4.13.3:
+ resolution: {integrity: sha512-FTAJJLQCMiIbt78kD5qhLjHIR5NOQDKC63wcdelWRDBE+d1xSrXYhXq4DzejnC2tGhFZHpDy2Ika0Ugf7sK8gA==}
engines: {node: '>=18.0.0'}
hasBin: true
tunnel-agent@0.6.0:
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
- turbo-darwin-64@1.13.3:
- resolution: {integrity: sha512-glup8Qx1qEFB5jerAnXbS8WrL92OKyMmg5Hnd4PleLljAeYmx+cmmnsmLT7tpaVZIN58EAAwu8wHC6kIIqhbWA==}
+ turbo-darwin-64@2.0.6:
+ resolution: {integrity: sha512-XpgBwWj3Ggmz/gQVqXdMKXHC1iFPMDiuwugLwSzE7Ih0O13JuNtYZKhQnopvbDQnFQCeRq2Vsm5OTWabg/oB/g==}
cpu: [x64]
os: [darwin]
- turbo-darwin-arm64@1.13.3:
- resolution: {integrity: sha512-/np2xD+f/+9qY8BVtuOQXRq5f9LehCFxamiQnwdqWm5iZmdjygC5T3uVSYuagVFsZKMvX3ycySwh8dylGTl6lg==}
+ turbo-darwin-arm64@2.0.6:
+ resolution: {integrity: sha512-RfeZYXIAkiA21E8lsvfptGTqz/256YD+eI1x37fedfvnHFWuIMFZGAOwJxtZc6QasQunDZ9TRRREbJNI68tkIw==}
cpu: [arm64]
os: [darwin]
- turbo-linux-64@1.13.3:
- resolution: {integrity: sha512-G+HGrau54iAnbXLfl+N/PynqpDwi/uDzb6iM9hXEDG+yJnSJxaHMShhOkXYJPk9offm9prH33Khx2scXrYVW1g==}
+ turbo-linux-64@2.0.6:
+ resolution: {integrity: sha512-92UDa0xNQQbx0HdSp9ag3YSS3xPdavhc7q9q9mxIAcqyjjD6VElA4Y85m4F/DDGE5SolCrvBz2sQhVmkOd6Caw==}
cpu: [x64]
os: [linux]
- turbo-linux-arm64@1.13.3:
- resolution: {integrity: sha512-qWwEl5VR02NqRyl68/3pwp3c/olZuSp+vwlwrunuoNTm6JXGLG5pTeme4zoHNnk0qn4cCX7DFrOboArlYxv0wQ==}
+ turbo-linux-arm64@2.0.6:
+ resolution: {integrity: sha512-eQKu6utCVUkIH2kqOzD8OS6E0ba6COjWm6PRDTNCHQRljZW503ycaTUIdMOiJrVg1MkEjDyOReUg8s8D18aJ4Q==}
cpu: [arm64]
os: [linux]
- turbo-windows-64@1.13.3:
- resolution: {integrity: sha512-Nudr4bRChfJzBPzEmpVV85VwUYRCGKecwkBFpbp2a4NtrJ3+UP1VZES653ckqCu2FRyRuS0n03v9euMbAvzH+Q==}
+ turbo-windows-64@2.0.6:
+ resolution: {integrity: sha512-+9u4EPrpoeHYCQ46dRcou9kbkSoelhOelHNcbs2d86D6ruYD/oIAHK9qgYK8LeARRz0jxhZIA/dWYdYsxJJWkw==}
cpu: [x64]
os: [win32]
- turbo-windows-arm64@1.13.3:
- resolution: {integrity: sha512-ouJCgsVLd3icjRLmRvHQDDZnmGzT64GBupM1Y+TjtYn2LVaEBoV6hicFy8x5DUpnqdLy+YpCzRMkWlwhmkX7sQ==}
+ turbo-windows-arm64@2.0.6:
+ resolution: {integrity: sha512-rdrKL+p+EjtdDVg0wQ/7yTbzkIYrnb0Pw4IKcjsy3M0RqUM9UcEi67b94XOAyTa5a0GqJL1+tUj2ebsFGPgZbg==}
cpu: [arm64]
os: [win32]
- turbo@1.13.3:
- resolution: {integrity: sha512-n17HJv4F4CpsYTvKzUJhLbyewbXjq1oLCi90i5tW1TiWDz16ML1eDG7wi5dHaKxzh5efIM56SITnuVbMq5dk4g==}
+ turbo@2.0.6:
+ resolution: {integrity: sha512-/Ftmxd5Mq//a9yMonvmwENNUN65jOVTwhhBPQjEtNZutYT9YKyzydFGLyVM1nzhpLWahQSMamRc/RDBv5EapzA==}
hasBin: true
+ tweetnacl@0.14.5:
+ resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==}
+
type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'}
@@ -5581,10 +6065,6 @@ packages:
resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
engines: {node: '>=4'}
- type-fest@0.20.2:
- resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
- engines: {node: '>=10'}
-
type-fest@0.21.3:
resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
engines: {node: '>=10'}
@@ -5597,29 +6077,49 @@ packages:
resolution: {integrity: sha512-5Y2/pp2wtJk8o08G0CMkuFPCO354FGwk/vbidxrdhRGZfd0tFnb4Qb8anp9XxXriwBgVPjdWbKpGl4J9lJY2jQ==}
engines: {node: '>=16'}
- type@1.2.0:
- resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==}
-
- type@2.7.2:
- resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==}
-
typed-array-buffer@1.0.1:
resolution: {integrity: sha512-RSqu1UEuSlrBhHTWC8O9FnPjOduNs4M7rJ4pRKoEjtx1zUNOPN2sSXHLDX+Y2WPbHIxbvg4JFo2DNAEfPIKWoQ==}
engines: {node: '>= 0.4'}
+ typed-array-buffer@1.0.2:
+ resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==}
+ engines: {node: '>= 0.4'}
+
typed-array-byte-length@1.0.0:
resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==}
engines: {node: '>= 0.4'}
+ typed-array-byte-length@1.0.1:
+ resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==}
+ engines: {node: '>= 0.4'}
+
typed-array-byte-offset@1.0.0:
resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==}
engines: {node: '>= 0.4'}
+ typed-array-byte-offset@1.0.2:
+ resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==}
+ engines: {node: '>= 0.4'}
+
typed-array-length@1.0.4:
resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==}
- typescript@5.4.5:
- resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==}
+ typed-array-length@1.0.6:
+ resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==}
+ engines: {node: '>= 0.4'}
+
+ typescript-eslint@7.14.1:
+ resolution: {integrity: sha512-Eo1X+Y0JgGPspcANKjeR6nIqXl4VL5ldXLc15k4m9upq+eY5fhU2IueiEZL6jmHrKH8aCfbIvM/v3IrX5Hg99w==}
+ engines: {node: ^18.18.0 || >=20.0.0}
+ peerDependencies:
+ eslint: ^8.56.0
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ typescript@5.5.2:
+ resolution: {integrity: sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==}
engines: {node: '>=14.17'}
hasBin: true
@@ -5640,6 +6140,10 @@ packages:
undici-types@5.26.5:
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
+ undici@6.19.2:
+ resolution: {integrity: sha512-JfjKqIauur3Q6biAtHJ564e3bWa8VvT+7cSiOJHFbX4Erv6CLGDpg8z+Fmg/1OI/47RA+GI2QZaF48SSaLvyBA==}
+ engines: {node: '>=18.17'}
+
universalify@0.2.0:
resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
engines: {node: '>= 4.0.0'}
@@ -5823,6 +6327,10 @@ packages:
resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
engines: {node: '>=18'}
+ watchpack@2.4.1:
+ resolution: {integrity: sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==}
+ engines: {node: '>=10.13.0'}
+
wcwidth@1.0.1:
resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
@@ -5833,6 +6341,20 @@ packages:
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
engines: {node: '>=12'}
+ webpack-sources@3.2.3:
+ resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==}
+ engines: {node: '>=10.13.0'}
+
+ webpack@5.91.0:
+ resolution: {integrity: sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==}
+ engines: {node: '>=10.13.0'}
+ hasBin: true
+ peerDependencies:
+ webpack-cli: '*'
+ peerDependenciesMeta:
+ webpack-cli:
+ optional: true
+
whatwg-encoding@3.1.1:
resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
engines: {node: '>=18'}
@@ -5862,6 +6384,10 @@ packages:
resolution: {integrity: sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==}
engines: {node: '>= 0.4'}
+ which-typed-array@1.1.15:
+ resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==}
+ engines: {node: '>= 0.4'}
+
which@2.0.2:
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
engines: {node: '>= 8'}
@@ -5901,8 +6427,8 @@ packages:
wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
- ws@8.17.0:
- resolution: {integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==}
+ ws@8.17.1:
+ resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
@@ -5930,6 +6456,11 @@ packages:
yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+ yaml@2.4.5:
+ resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==}
+ engines: {node: '>= 14'}
+ hasBin: true
+
yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
@@ -5950,6 +6481,10 @@ packages:
resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
engines: {node: '>=12.20'}
+ zip-stream@4.1.1:
+ resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==}
+ engines: {node: '>= 10'}
+
zod@3.23.8:
resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==}
@@ -5962,66 +6497,44 @@ snapshots:
'@jridgewell/gen-mapping': 0.3.3
'@jridgewell/trace-mapping': 0.3.25
- '@ampproject/remapping@2.3.0':
+ '@auth/core@0.32.0':
dependencies:
- '@jridgewell/gen-mapping': 0.3.5
- '@jridgewell/trace-mapping': 0.3.25
+ '@panva/hkdf': 1.1.1
+ '@types/cookie': 0.6.0
+ cookie: 0.6.0
+ jose: 5.2.2
+ oauth4webapi: 2.10.3
+ preact: 10.11.3
+ preact-render-to-string: 5.2.3(preact@10.11.3)
- '@auth/core@0.31.0':
+ '@auth/core@0.34.1':
dependencies:
'@panva/hkdf': 1.1.1
'@types/cookie': 0.6.0
cookie: 0.6.0
jose: 5.2.2
- oauth4webapi: 2.10.3
+ oauth4webapi: 2.11.0
preact: 10.11.3
preact-render-to-string: 5.2.3(preact@10.11.3)
- '@auth/drizzle-adapter@1.1.0':
+ '@auth/drizzle-adapter@1.4.1':
dependencies:
- '@auth/core': 0.31.0
+ '@auth/core': 0.34.1
transitivePeerDependencies:
- '@simplewebauthn/browser'
- '@simplewebauthn/server'
- nodemailer
- '@babel/code-frame@7.23.5':
- dependencies:
- '@babel/highlight': 7.23.4
- chalk: 2.4.2
-
'@babel/code-frame@7.24.6':
dependencies:
'@babel/highlight': 7.24.6
- picocolors: 1.0.1
-
- '@babel/compat-data@7.23.5': {}
+ picocolors: 1.0.0
'@babel/compat-data@7.24.6': {}
- '@babel/core@7.24.0':
- dependencies:
- '@ampproject/remapping': 2.2.1
- '@babel/code-frame': 7.23.5
- '@babel/generator': 7.23.6
- '@babel/helper-compilation-targets': 7.23.6
- '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0)
- '@babel/helpers': 7.24.0
- '@babel/parser': 7.24.0
- '@babel/template': 7.24.0
- '@babel/traverse': 7.24.0
- '@babel/types': 7.24.0
- convert-source-map: 2.0.0
- debug: 4.3.4
- gensync: 1.0.0-beta.2
- json5: 2.2.3
- semver: 6.3.1
- transitivePeerDependencies:
- - supports-color
-
'@babel/core@7.24.6':
dependencies:
- '@ampproject/remapping': 2.3.0
+ '@ampproject/remapping': 2.2.1
'@babel/code-frame': 7.24.6
'@babel/generator': 7.24.6
'@babel/helper-compilation-targets': 7.24.6
@@ -6032,20 +6545,13 @@ snapshots:
'@babel/traverse': 7.24.6
'@babel/types': 7.24.6
convert-source-map: 2.0.0
- debug: 4.3.4
+ debug: 4.3.5
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
transitivePeerDependencies:
- supports-color
- '@babel/generator@7.23.6':
- dependencies:
- '@babel/types': 7.23.9
- '@jridgewell/gen-mapping': 0.3.3
- '@jridgewell/trace-mapping': 0.3.25
- jsesc: 2.5.2
-
'@babel/generator@7.24.6':
dependencies:
'@babel/types': 7.24.6
@@ -6053,14 +6559,6 @@ snapshots:
'@jridgewell/trace-mapping': 0.3.25
jsesc: 2.5.2
- '@babel/helper-compilation-targets@7.23.6':
- dependencies:
- '@babel/compat-data': 7.23.5
- '@babel/helper-validator-option': 7.23.5
- browserslist: 4.23.0
- lru-cache: 5.1.1
- semver: 6.3.1
-
'@babel/helper-compilation-targets@7.24.6':
dependencies:
'@babel/compat-data': 7.24.6
@@ -6069,45 +6567,21 @@ snapshots:
lru-cache: 5.1.1
semver: 6.3.1
- '@babel/helper-environment-visitor@7.22.20': {}
-
'@babel/helper-environment-visitor@7.24.6': {}
- '@babel/helper-function-name@7.23.0':
- dependencies:
- '@babel/template': 7.23.9
- '@babel/types': 7.24.0
-
'@babel/helper-function-name@7.24.6':
dependencies:
'@babel/template': 7.24.6
'@babel/types': 7.24.6
- '@babel/helper-hoist-variables@7.22.5':
- dependencies:
- '@babel/types': 7.23.9
-
'@babel/helper-hoist-variables@7.24.6':
dependencies:
'@babel/types': 7.24.6
- '@babel/helper-module-imports@7.22.15':
- dependencies:
- '@babel/types': 7.24.6
-
'@babel/helper-module-imports@7.24.6':
dependencies:
'@babel/types': 7.24.6
- '@babel/helper-module-transforms@7.23.3(@babel/core@7.24.0)':
- dependencies:
- '@babel/core': 7.24.0
- '@babel/helper-environment-visitor': 7.22.20
- '@babel/helper-module-imports': 7.22.15
- '@babel/helper-simple-access': 7.22.5
- '@babel/helper-split-export-declaration': 7.22.6
- '@babel/helper-validator-identifier': 7.22.20
-
'@babel/helper-module-transforms@7.24.6(@babel/core@7.24.6)':
dependencies:
'@babel/core': 7.24.6
@@ -6119,63 +6593,31 @@ snapshots:
'@babel/helper-plugin-utils@7.24.6': {}
- '@babel/helper-simple-access@7.22.5':
- dependencies:
- '@babel/types': 7.24.6
-
'@babel/helper-simple-access@7.24.6':
dependencies:
'@babel/types': 7.24.6
- '@babel/helper-split-export-declaration@7.22.6':
- dependencies:
- '@babel/types': 7.23.9
-
'@babel/helper-split-export-declaration@7.24.6':
dependencies:
'@babel/types': 7.24.6
- '@babel/helper-string-parser@7.23.4': {}
-
'@babel/helper-string-parser@7.24.6': {}
- '@babel/helper-validator-identifier@7.22.20': {}
-
'@babel/helper-validator-identifier@7.24.6': {}
- '@babel/helper-validator-option@7.23.5': {}
-
'@babel/helper-validator-option@7.24.6': {}
- '@babel/helpers@7.24.0':
- dependencies:
- '@babel/template': 7.24.0
- '@babel/traverse': 7.24.0
- '@babel/types': 7.24.0
- transitivePeerDependencies:
- - supports-color
-
'@babel/helpers@7.24.6':
dependencies:
'@babel/template': 7.24.6
'@babel/types': 7.24.6
- '@babel/highlight@7.23.4':
- dependencies:
- '@babel/helper-validator-identifier': 7.22.20
- chalk: 2.4.2
- js-tokens: 4.0.0
-
'@babel/highlight@7.24.6':
dependencies:
'@babel/helper-validator-identifier': 7.24.6
chalk: 2.4.2
js-tokens: 4.0.0
- picocolors: 1.0.1
-
- '@babel/parser@7.24.0':
- dependencies:
- '@babel/types': 7.24.0
+ picocolors: 1.0.0
'@babel/parser@7.24.6':
dependencies:
@@ -6200,39 +6642,12 @@ snapshots:
dependencies:
regenerator-runtime: 0.14.1
- '@babel/template@7.23.9':
- dependencies:
- '@babel/code-frame': 7.24.6
- '@babel/parser': 7.24.6
- '@babel/types': 7.24.6
-
- '@babel/template@7.24.0':
- dependencies:
- '@babel/code-frame': 7.23.5
- '@babel/parser': 7.24.0
- '@babel/types': 7.24.0
-
'@babel/template@7.24.6':
dependencies:
'@babel/code-frame': 7.24.6
'@babel/parser': 7.24.6
'@babel/types': 7.24.6
- '@babel/traverse@7.24.0':
- dependencies:
- '@babel/code-frame': 7.23.5
- '@babel/generator': 7.23.6
- '@babel/helper-environment-visitor': 7.22.20
- '@babel/helper-function-name': 7.23.0
- '@babel/helper-hoist-variables': 7.22.5
- '@babel/helper-split-export-declaration': 7.22.6
- '@babel/parser': 7.24.0
- '@babel/types': 7.24.0
- debug: 4.3.4
- globals: 11.12.0
- transitivePeerDependencies:
- - supports-color
-
'@babel/traverse@7.24.6':
dependencies:
'@babel/code-frame': 7.24.6
@@ -6243,29 +6658,19 @@ snapshots:
'@babel/helper-split-export-declaration': 7.24.6
'@babel/parser': 7.24.6
'@babel/types': 7.24.6
- debug: 4.3.4
+ debug: 4.3.5
globals: 11.12.0
transitivePeerDependencies:
- supports-color
- '@babel/types@7.23.9':
- dependencies:
- '@babel/helper-string-parser': 7.23.4
- '@babel/helper-validator-identifier': 7.22.20
- to-fast-properties: 2.0.0
-
- '@babel/types@7.24.0':
- dependencies:
- '@babel/helper-string-parser': 7.23.4
- '@babel/helper-validator-identifier': 7.22.20
- to-fast-properties: 2.0.0
-
'@babel/types@7.24.6':
dependencies:
'@babel/helper-string-parser': 7.24.6
'@babel/helper-validator-identifier': 7.24.6
to-fast-properties: 2.0.0
+ '@balena/dockerignore@1.0.2': {}
+
'@bcoe/v8-coverage@0.2.3': {}
'@colors/colors@1.6.0': {}
@@ -6494,19 +6899,27 @@ snapshots:
'@esbuild/win32-x64@0.20.2':
optional: true
- '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)':
+ '@eslint-community/eslint-utils@4.4.0(eslint@9.6.0)':
dependencies:
- eslint: 8.57.0
+ eslint: 9.6.0
eslint-visitor-keys: 3.4.3
'@eslint-community/regexpp@4.10.0': {}
- '@eslint/eslintrc@2.1.4':
+ '@eslint/config-array@0.17.0':
+ dependencies:
+ '@eslint/object-schema': 2.1.4
+ debug: 4.3.5
+ minimatch: 3.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/eslintrc@3.1.0':
dependencies:
ajv: 6.12.6
- debug: 4.3.4
- espree: 9.6.1
- globals: 13.24.0
+ debug: 4.3.5
+ espree: 10.1.0
+ globals: 14.0.0
ignore: 5.3.1
import-fresh: 3.3.0
js-yaml: 4.1.0
@@ -6515,7 +6928,9 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@eslint/js@8.57.0': {}
+ '@eslint/js@9.6.0': {}
+
+ '@eslint/object-schema@2.1.4': {}
'@floating-ui/core@1.6.0':
dependencies:
@@ -6542,28 +6957,20 @@ snapshots:
'@floating-ui/utils@0.2.1': {}
- '@homarr/gridstack@1.0.2': {}
-
- '@humanwhocodes/config-array@0.11.14':
- dependencies:
- '@humanwhocodes/object-schema': 2.0.2
- debug: 4.3.4
- minimatch: 3.1.2
- transitivePeerDependencies:
- - supports-color
+ '@homarr/gridstack@1.0.0': {}
'@humanwhocodes/module-importer@1.0.1': {}
- '@humanwhocodes/object-schema@2.0.2': {}
+ '@humanwhocodes/retry@0.3.0': {}
- '@ianvs/prettier-plugin-sort-imports@4.2.1(prettier@3.2.5)':
+ '@ianvs/prettier-plugin-sort-imports@4.3.0(prettier@3.3.2)':
dependencies:
- '@babel/core': 7.24.0
- '@babel/generator': 7.23.6
- '@babel/parser': 7.24.0
- '@babel/traverse': 7.24.0
- '@babel/types': 7.24.0
- prettier: 3.2.5
+ '@babel/core': 7.24.6
+ '@babel/generator': 7.24.6
+ '@babel/parser': 7.24.6
+ '@babel/traverse': 7.24.6
+ '@babel/types': 7.24.6
+ prettier: 3.3.2
semver: 7.6.0
transitivePeerDependencies:
- supports-color
@@ -6603,6 +7010,11 @@ snapshots:
'@jridgewell/set-array@1.2.1': {}
+ '@jridgewell/source-map@0.3.6':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.5
+ '@jridgewell/trace-mapping': 0.3.25
+
'@jridgewell/sourcemap-codec@1.4.15': {}
'@jridgewell/trace-mapping@0.3.25':
@@ -6615,14 +7027,14 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.4.15
- '@mantine/colors-generator@7.10.0(chroma-js@2.4.2)':
+ '@mantine/colors-generator@7.11.0(chroma-js@2.4.2)':
dependencies:
chroma-js: 2.4.2
- '@mantine/core@7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ '@mantine/core@7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@floating-ui/react': 0.26.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- '@mantine/hooks': 7.10.0(react@18.3.1)
+ '@mantine/hooks': 7.11.0(react@18.3.1)
clsx: 2.1.1
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
@@ -6633,57 +7045,57 @@ snapshots:
transitivePeerDependencies:
- '@types/react'
- '@mantine/dates@7.10.0(@mantine/core@7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.10.0(react@18.3.1))(dayjs@1.11.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ '@mantine/dates@7.11.0(@mantine/core@7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.11.0(react@18.3.1))(dayjs@1.11.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
- '@mantine/core': 7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- '@mantine/hooks': 7.10.0(react@18.3.1)
+ '@mantine/core': 7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@mantine/hooks': 7.11.0(react@18.3.1)
clsx: 2.1.1
dayjs: 1.11.11
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
- '@mantine/form@7.10.0(react@18.3.1)':
+ '@mantine/form@7.11.0(react@18.3.1)':
dependencies:
fast-deep-equal: 3.1.3
klona: 2.0.6
react: 18.3.1
- '@mantine/hooks@7.10.0(react@18.3.1)':
+ '@mantine/hooks@7.11.0(react@18.3.1)':
dependencies:
react: 18.3.1
- '@mantine/modals@7.10.0(@mantine/core@7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.10.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ '@mantine/modals@7.11.0(@mantine/core@7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.11.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
- '@mantine/core': 7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- '@mantine/hooks': 7.10.0(react@18.3.1)
+ '@mantine/core': 7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@mantine/hooks': 7.11.0(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
- '@mantine/notifications@7.10.0(@mantine/core@7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.10.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ '@mantine/notifications@7.11.0(@mantine/core@7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.11.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
- '@mantine/core': 7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- '@mantine/hooks': 7.10.0(react@18.3.1)
- '@mantine/store': 7.10.0(react@18.3.1)
+ '@mantine/core': 7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@mantine/hooks': 7.11.0(react@18.3.1)
+ '@mantine/store': 7.11.0(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- '@mantine/spotlight@7.10.0(@mantine/core@7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.10.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ '@mantine/spotlight@7.11.0(@mantine/core@7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.11.0(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
- '@mantine/core': 7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- '@mantine/hooks': 7.10.0(react@18.3.1)
- '@mantine/store': 7.10.0(react@18.3.1)
+ '@mantine/core': 7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@mantine/hooks': 7.11.0(react@18.3.1)
+ '@mantine/store': 7.11.0(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
- '@mantine/store@7.10.0(react@18.3.1)':
+ '@mantine/store@7.11.0(react@18.3.1)':
dependencies:
react: 18.3.1
- '@mantine/tiptap@7.10.0(@mantine/core@7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.10.0(react@18.3.1))(@tiptap/extension-link@2.4.0(@tiptap/core@2.4.0(@tiptap/pm@2.2.4))(@tiptap/pm@2.2.4))(@tiptap/react@2.4.0(@tiptap/core@2.4.0(@tiptap/pm@2.2.4))(@tiptap/pm@2.2.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ '@mantine/tiptap@7.11.0(@mantine/core@7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.11.0(react@18.3.1))(@tiptap/extension-link@2.4.0(@tiptap/core@2.4.0(@tiptap/pm@2.2.4))(@tiptap/pm@2.2.4))(@tiptap/react@2.4.0(@tiptap/core@2.4.0(@tiptap/pm@2.2.4))(@tiptap/pm@2.2.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
- '@mantine/core': 7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- '@mantine/hooks': 7.10.0(react@18.3.1)
+ '@mantine/core': 7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@mantine/hooks': 7.11.0(react@18.3.1)
'@tiptap/extension-link': 2.4.0(@tiptap/core@2.4.0(@tiptap/pm@2.2.4))(@tiptap/pm@2.2.4)
'@tiptap/react': 2.4.0(@tiptap/core@2.4.0(@tiptap/pm@2.2.4))(@tiptap/pm@2.2.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
@@ -6704,37 +7116,37 @@ snapshots:
- encoding
- supports-color
- '@next/env@14.2.3': {}
+ '@next/env@14.2.4': {}
- '@next/eslint-plugin-next@14.2.3':
+ '@next/eslint-plugin-next@14.2.4':
dependencies:
glob: 10.3.10
- '@next/swc-darwin-arm64@14.2.3':
+ '@next/swc-darwin-arm64@14.2.4':
optional: true
- '@next/swc-darwin-x64@14.2.3':
+ '@next/swc-darwin-x64@14.2.4':
optional: true
- '@next/swc-linux-arm64-gnu@14.2.3':
+ '@next/swc-linux-arm64-gnu@14.2.4':
optional: true
- '@next/swc-linux-arm64-musl@14.2.3':
+ '@next/swc-linux-arm64-musl@14.2.4':
optional: true
- '@next/swc-linux-x64-gnu@14.2.3':
+ '@next/swc-linux-x64-gnu@14.2.4':
optional: true
- '@next/swc-linux-x64-musl@14.2.3':
+ '@next/swc-linux-x64-musl@14.2.4':
optional: true
- '@next/swc-win32-arm64-msvc@14.2.3':
+ '@next/swc-win32-arm64-msvc@14.2.4':
optional: true
- '@next/swc-win32-ia32-msvc@14.2.3':
+ '@next/swc-win32-ia32-msvc@14.2.4':
optional: true
- '@next/swc-win32-x64-msvc@14.2.3':
+ '@next/swc-win32-x64-msvc@14.2.4':
optional: true
'@noble/hashes@1.3.3': {}
@@ -6834,66 +7246,66 @@ snapshots:
'@swc/counter': 0.1.3
tslib: 2.6.2
- '@t3-oss/env-core@0.10.1(typescript@5.4.5)(zod@3.23.8)':
+ '@t3-oss/env-core@0.10.1(typescript@5.5.2)(zod@3.23.8)':
dependencies:
zod: 3.23.8
optionalDependencies:
- typescript: 5.4.5
+ typescript: 5.5.2
- '@t3-oss/env-nextjs@0.10.1(typescript@5.4.5)(zod@3.23.8)':
+ '@t3-oss/env-nextjs@0.10.1(typescript@5.5.2)(zod@3.23.8)':
dependencies:
- '@t3-oss/env-core': 0.10.1(typescript@5.4.5)(zod@3.23.8)
+ '@t3-oss/env-core': 0.10.1(typescript@5.5.2)(zod@3.23.8)
zod: 3.23.8
optionalDependencies:
- typescript: 5.4.5
+ typescript: 5.5.2
- '@tabler/icons-react@3.5.0(react@18.3.1)':
+ '@tabler/icons-react@3.7.0(react@18.3.1)':
dependencies:
- '@tabler/icons': 3.5.0
+ '@tabler/icons': 3.7.0
react: 18.3.1
- '@tabler/icons@3.5.0': {}
+ '@tabler/icons@3.7.0': {}
'@tanstack/match-sorter-utils@8.15.1':
dependencies:
remove-accents: 0.5.0
- '@tanstack/query-core@5.40.0': {}
+ '@tanstack/query-core@5.48.0': {}
- '@tanstack/query-devtools@5.37.1': {}
+ '@tanstack/query-devtools@5.47.0': {}
- '@tanstack/react-query-devtools@5.40.0(@tanstack/react-query@5.40.0(react@18.3.1))(react@18.3.1)':
+ '@tanstack/react-query-devtools@5.48.0(@tanstack/react-query@5.48.0(react@18.3.1))(react@18.3.1)':
dependencies:
- '@tanstack/query-devtools': 5.37.1
- '@tanstack/react-query': 5.40.0(react@18.3.1)
+ '@tanstack/query-devtools': 5.47.0
+ '@tanstack/react-query': 5.48.0(react@18.3.1)
react: 18.3.1
- '@tanstack/react-query-next-experimental@5.40.0(@tanstack/react-query@5.40.0(react@18.3.1))(next@14.2.3(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.2))(react@18.3.1)':
+ '@tanstack/react-query-next-experimental@5.48.0(@tanstack/react-query@5.48.0(react@18.3.1))(next@14.2.4(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1)':
dependencies:
- '@tanstack/react-query': 5.40.0(react@18.3.1)
- next: 14.2.3(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.2)
+ '@tanstack/react-query': 5.48.0(react@18.3.1)
+ next: 14.2.4(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6)
react: 18.3.1
- '@tanstack/react-query@5.40.0(react@18.3.1)':
+ '@tanstack/react-query@5.48.0(react@18.3.1)':
dependencies:
- '@tanstack/query-core': 5.40.0
+ '@tanstack/query-core': 5.48.0
react: 18.3.1
- '@tanstack/react-table@8.16.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ '@tanstack/react-table@8.17.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
- '@tanstack/table-core': 8.16.0
+ '@tanstack/table-core': 8.17.3
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
- '@tanstack/react-virtual@3.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ '@tanstack/react-virtual@3.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
- '@tanstack/virtual-core': 3.5.0
+ '@tanstack/virtual-core': 3.5.1
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
- '@tanstack/table-core@8.16.0': {}
+ '@tanstack/table-core@8.17.3': {}
- '@tanstack/virtual-core@3.5.0': {}
+ '@tanstack/virtual-core@3.5.1': {}
'@tiptap/core@2.4.0(@tiptap/pm@2.2.4)':
dependencies:
@@ -7101,34 +7513,30 @@ snapshots:
'@tootallnate/quickjs-emscripten@0.23.0': {}
- '@trpc/client@11.0.0-rc.377(@trpc/server@11.0.0-rc.382)':
- dependencies:
- '@trpc/server': 11.0.0-rc.382
-
- '@trpc/client@11.0.0-rc.382(@trpc/server@11.0.0-rc.382)':
+ '@trpc/client@11.0.0-rc.421(@trpc/server@11.0.0-rc.421)':
dependencies:
- '@trpc/server': 11.0.0-rc.382
+ '@trpc/server': 11.0.0-rc.421
- '@trpc/next@11.0.0-rc.382(@tanstack/react-query@5.40.0(react@18.3.1))(@trpc/client@11.0.0-rc.377(@trpc/server@11.0.0-rc.382))(@trpc/react-query@11.0.0-rc.382(@tanstack/react-query@5.40.0(react@18.3.1))(@trpc/client@11.0.0-rc.377(@trpc/server@11.0.0-rc.382))(@trpc/server@11.0.0-rc.382)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@trpc/server@11.0.0-rc.382)(next@14.2.3(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ '@trpc/next@11.0.0-rc.421(@tanstack/react-query@5.48.0(react@18.3.1))(@trpc/client@11.0.0-rc.421(@trpc/server@11.0.0-rc.421))(@trpc/react-query@11.0.0-rc.421(@tanstack/react-query@5.48.0(react@18.3.1))(@trpc/client@11.0.0-rc.421(@trpc/server@11.0.0-rc.421))(@trpc/server@11.0.0-rc.421)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@trpc/server@11.0.0-rc.421)(next@14.2.4(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
- '@trpc/client': 11.0.0-rc.377(@trpc/server@11.0.0-rc.382)
- '@trpc/server': 11.0.0-rc.382
- next: 14.2.3(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.2)
+ '@trpc/client': 11.0.0-rc.421(@trpc/server@11.0.0-rc.421)
+ '@trpc/server': 11.0.0-rc.421
+ next: 14.2.4(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
- '@tanstack/react-query': 5.40.0(react@18.3.1)
- '@trpc/react-query': 11.0.0-rc.382(@tanstack/react-query@5.40.0(react@18.3.1))(@trpc/client@11.0.0-rc.377(@trpc/server@11.0.0-rc.382))(@trpc/server@11.0.0-rc.382)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@tanstack/react-query': 5.48.0(react@18.3.1)
+ '@trpc/react-query': 11.0.0-rc.421(@tanstack/react-query@5.48.0(react@18.3.1))(@trpc/client@11.0.0-rc.421(@trpc/server@11.0.0-rc.421))(@trpc/server@11.0.0-rc.421)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- '@trpc/react-query@11.0.0-rc.382(@tanstack/react-query@5.40.0(react@18.3.1))(@trpc/client@11.0.0-rc.377(@trpc/server@11.0.0-rc.382))(@trpc/server@11.0.0-rc.382)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ '@trpc/react-query@11.0.0-rc.421(@tanstack/react-query@5.48.0(react@18.3.1))(@trpc/client@11.0.0-rc.421(@trpc/server@11.0.0-rc.421))(@trpc/server@11.0.0-rc.421)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
- '@tanstack/react-query': 5.40.0(react@18.3.1)
- '@trpc/client': 11.0.0-rc.377(@trpc/server@11.0.0-rc.382)
- '@trpc/server': 11.0.0-rc.382
+ '@tanstack/react-query': 5.48.0(react@18.3.1)
+ '@trpc/client': 11.0.0-rc.421(@trpc/server@11.0.0-rc.421)
+ '@trpc/server': 11.0.0-rc.421
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
- '@trpc/server@11.0.0-rc.382': {}
+ '@trpc/server@11.0.0-rc.421': {}
'@tsconfig/node10@1.0.9': {}
@@ -7138,9 +7546,9 @@ snapshots:
'@tsconfig/node16@1.0.4': {}
- '@turbo/gen@1.13.3(@types/node@20.12.12)(typescript@5.4.5)':
+ '@turbo/gen@2.0.6(@types/node@20.14.9)(typescript@5.5.2)':
dependencies:
- '@turbo/workspaces': 1.13.3
+ '@turbo/workspaces': 2.0.6
chalk: 2.4.2
commander: 10.0.1
fs-extra: 10.1.0
@@ -7148,7 +7556,7 @@ snapshots:
minimatch: 9.0.4
node-plop: 0.26.3
proxy-agent: 6.4.0
- ts-node: 10.9.2(@types/node@20.12.12)(typescript@5.4.5)
+ ts-node: 10.9.2(@types/node@20.14.9)(typescript@5.5.2)
update-check: 1.5.4
validate-npm-package-name: 5.0.0
transitivePeerDependencies:
@@ -7158,7 +7566,7 @@ snapshots:
- supports-color
- typescript
- '@turbo/workspaces@1.13.3':
+ '@turbo/workspaces@2.0.6':
dependencies:
chalk: 2.4.2
commander: 10.0.1
@@ -7179,7 +7587,7 @@ snapshots:
'@babel/types': 7.24.6
'@types/babel__generator': 7.6.8
'@types/babel__template': 7.4.4
- '@types/babel__traverse': 7.20.6
+ '@types/babel__traverse': 7.20.5
'@types/babel__generator@7.6.8':
dependencies:
@@ -7190,28 +7598,28 @@ snapshots:
'@babel/parser': 7.24.6
'@babel/types': 7.24.6
- '@types/babel__traverse@7.20.6':
+ '@types/babel__traverse@7.20.5':
dependencies:
'@babel/types': 7.24.6
'@types/bcrypt@5.0.2':
dependencies:
- '@types/node': 20.12.12
+ '@types/node': 20.14.9
'@types/better-sqlite3@7.6.10':
dependencies:
- '@types/node': 20.12.12
+ '@types/node': 20.14.9
'@types/body-parser@1.19.5':
dependencies:
'@types/connect': 3.4.38
- '@types/node': 20.12.12
+ '@types/node': 20.14.9
'@types/chroma-js@2.4.4': {}
'@types/connect@3.4.38':
dependencies:
- '@types/node': 20.12.12
+ '@types/node': 20.14.9
'@types/cookie@0.6.0': {}
@@ -7220,10 +7628,26 @@ snapshots:
'@types/connect': 3.4.38
'@types/express': 4.17.21
'@types/keygrip': 1.0.6
- '@types/node': 20.12.12
+ '@types/node': 20.14.9
'@types/css-modules@1.0.5': {}
+ '@types/docker-modem@3.0.6':
+ dependencies:
+ '@types/node': 20.14.9
+ '@types/ssh2': 1.15.0
+
+ '@types/dockerode@3.3.29':
+ dependencies:
+ '@types/docker-modem': 3.0.6
+ '@types/node': 20.14.9
+ '@types/ssh2': 1.15.0
+
+ '@types/eslint-scope@3.7.7':
+ dependencies:
+ '@types/eslint': 8.56.10
+ '@types/estree': 1.0.5
+
'@types/eslint@8.56.10':
dependencies:
'@types/estree': 1.0.5
@@ -7233,7 +7657,7 @@ snapshots:
'@types/express-serve-static-core@4.17.43':
dependencies:
- '@types/node': 20.12.12
+ '@types/node': 20.14.9
'@types/qs': 6.9.11
'@types/range-parser': 1.2.7
'@types/send': 0.17.4
@@ -7248,7 +7672,7 @@ snapshots:
'@types/glob@7.2.0':
dependencies:
'@types/minimatch': 5.1.2
- '@types/node': 20.12.12
+ '@types/node': 20.14.9
'@types/http-errors@2.0.4': {}
@@ -7271,7 +7695,11 @@ snapshots:
'@types/node-cron@3.0.11': {}
- '@types/node@20.12.12':
+ '@types/node@18.19.33':
+ dependencies:
+ undici-types: 5.26.5
+
+ '@types/node@20.14.9':
dependencies:
undici-types: 5.26.5
@@ -7299,19 +7727,32 @@ snapshots:
'@types/send@0.17.4':
dependencies:
'@types/mime': 1.3.5
- '@types/node': 20.12.12
+ '@types/node': 20.14.9
'@types/serve-static@1.15.5':
dependencies:
'@types/http-errors': 2.0.4
'@types/mime': 3.0.4
- '@types/node': 20.12.12
+ '@types/node': 20.14.9
+
+ '@types/ssh2-streams@0.1.12':
+ dependencies:
+ '@types/node': 20.14.9
+
+ '@types/ssh2@0.5.52':
+ dependencies:
+ '@types/node': 20.14.9
+ '@types/ssh2-streams': 0.1.12
+
+ '@types/ssh2@1.15.0':
+ dependencies:
+ '@types/node': 18.19.33
'@types/throttle-debounce@2.1.0': {}
'@types/through@0.0.33':
dependencies:
- '@types/node': 20.12.12
+ '@types/node': 20.14.9
'@types/tinycolor2@1.4.6': {}
@@ -7321,93 +7762,91 @@ snapshots:
'@types/ws@8.5.10':
dependencies:
- '@types/node': 20.12.12
+ '@types/node': 20.14.9
- '@typescript-eslint/eslint-plugin@7.11.0(@typescript-eslint/parser@7.11.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)':
+ '@typescript-eslint/eslint-plugin@7.14.1(@typescript-eslint/parser@7.14.1(eslint@9.6.0)(typescript@5.5.2))(eslint@9.6.0)(typescript@5.5.2)':
dependencies:
'@eslint-community/regexpp': 4.10.0
- '@typescript-eslint/parser': 7.11.0(eslint@8.57.0)(typescript@5.4.5)
- '@typescript-eslint/scope-manager': 7.11.0
- '@typescript-eslint/type-utils': 7.11.0(eslint@8.57.0)(typescript@5.4.5)
- '@typescript-eslint/utils': 7.11.0(eslint@8.57.0)(typescript@5.4.5)
- '@typescript-eslint/visitor-keys': 7.11.0
- eslint: 8.57.0
+ '@typescript-eslint/parser': 7.14.1(eslint@9.6.0)(typescript@5.5.2)
+ '@typescript-eslint/scope-manager': 7.14.1
+ '@typescript-eslint/type-utils': 7.14.1(eslint@9.6.0)(typescript@5.5.2)
+ '@typescript-eslint/utils': 7.14.1(eslint@9.6.0)(typescript@5.5.2)
+ '@typescript-eslint/visitor-keys': 7.14.1
+ eslint: 9.6.0
graphemer: 1.4.0
ignore: 5.3.1
natural-compare: 1.4.0
- ts-api-utils: 1.3.0(typescript@5.4.5)
+ ts-api-utils: 1.3.0(typescript@5.5.2)
optionalDependencies:
- typescript: 5.4.5
+ typescript: 5.5.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/parser@7.11.0(eslint@8.57.0)(typescript@5.4.5)':
+ '@typescript-eslint/parser@7.14.1(eslint@9.6.0)(typescript@5.5.2)':
dependencies:
- '@typescript-eslint/scope-manager': 7.11.0
- '@typescript-eslint/types': 7.11.0
- '@typescript-eslint/typescript-estree': 7.11.0(typescript@5.4.5)
- '@typescript-eslint/visitor-keys': 7.11.0
- debug: 4.3.4
- eslint: 8.57.0
+ '@typescript-eslint/scope-manager': 7.14.1
+ '@typescript-eslint/types': 7.14.1
+ '@typescript-eslint/typescript-estree': 7.14.1(typescript@5.5.2)
+ '@typescript-eslint/visitor-keys': 7.14.1
+ debug: 4.3.5
+ eslint: 9.6.0
optionalDependencies:
- typescript: 5.4.5
+ typescript: 5.5.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/scope-manager@7.11.0':
+ '@typescript-eslint/scope-manager@7.14.1':
dependencies:
- '@typescript-eslint/types': 7.11.0
- '@typescript-eslint/visitor-keys': 7.11.0
+ '@typescript-eslint/types': 7.14.1
+ '@typescript-eslint/visitor-keys': 7.14.1
- '@typescript-eslint/type-utils@7.11.0(eslint@8.57.0)(typescript@5.4.5)':
+ '@typescript-eslint/type-utils@7.14.1(eslint@9.6.0)(typescript@5.5.2)':
dependencies:
- '@typescript-eslint/typescript-estree': 7.11.0(typescript@5.4.5)
- '@typescript-eslint/utils': 7.11.0(eslint@8.57.0)(typescript@5.4.5)
- debug: 4.3.4
- eslint: 8.57.0
- ts-api-utils: 1.3.0(typescript@5.4.5)
+ '@typescript-eslint/typescript-estree': 7.14.1(typescript@5.5.2)
+ '@typescript-eslint/utils': 7.14.1(eslint@9.6.0)(typescript@5.5.2)
+ debug: 4.3.5
+ eslint: 9.6.0
+ ts-api-utils: 1.3.0(typescript@5.5.2)
optionalDependencies:
- typescript: 5.4.5
+ typescript: 5.5.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/types@7.11.0': {}
+ '@typescript-eslint/types@7.14.1': {}
- '@typescript-eslint/typescript-estree@7.11.0(typescript@5.4.5)':
+ '@typescript-eslint/typescript-estree@7.14.1(typescript@5.5.2)':
dependencies:
- '@typescript-eslint/types': 7.11.0
- '@typescript-eslint/visitor-keys': 7.11.0
- debug: 4.3.4
+ '@typescript-eslint/types': 7.14.1
+ '@typescript-eslint/visitor-keys': 7.14.1
+ debug: 4.3.5
globby: 11.1.0
is-glob: 4.0.3
minimatch: 9.0.4
- semver: 7.6.2
- ts-api-utils: 1.3.0(typescript@5.4.5)
+ semver: 7.6.0
+ ts-api-utils: 1.3.0(typescript@5.5.2)
optionalDependencies:
- typescript: 5.4.5
+ typescript: 5.5.2
transitivePeerDependencies:
- supports-color
- '@typescript-eslint/utils@7.11.0(eslint@8.57.0)(typescript@5.4.5)':
+ '@typescript-eslint/utils@7.14.1(eslint@9.6.0)(typescript@5.5.2)':
dependencies:
- '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
- '@typescript-eslint/scope-manager': 7.11.0
- '@typescript-eslint/types': 7.11.0
- '@typescript-eslint/typescript-estree': 7.11.0(typescript@5.4.5)
- eslint: 8.57.0
+ '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0)
+ '@typescript-eslint/scope-manager': 7.14.1
+ '@typescript-eslint/types': 7.14.1
+ '@typescript-eslint/typescript-estree': 7.14.1(typescript@5.5.2)
+ eslint: 9.6.0
transitivePeerDependencies:
- supports-color
- typescript
- '@typescript-eslint/visitor-keys@7.11.0':
+ '@typescript-eslint/visitor-keys@7.14.1':
dependencies:
- '@typescript-eslint/types': 7.11.0
+ '@typescript-eslint/types': 7.14.1
eslint-visitor-keys: 3.4.3
'@umami/node@0.3.0': {}
- '@ungap/structured-clone@1.2.0': {}
-
'@videojs/http-streaming@3.12.1(video.js@8.12.0)':
dependencies:
'@babel/runtime': 7.23.9
@@ -7437,18 +7876,18 @@ snapshots:
global: 4.4.0
is-function: 1.0.2
- '@vitejs/plugin-react@4.3.0(vite@5.2.6(@types/node@20.12.12)(sass@1.77.2)(sugarss@4.0.1))':
+ '@vitejs/plugin-react@4.3.1(vite@5.2.6(@types/node@20.14.9)(sass@1.77.6)(sugarss@4.0.1)(terser@5.31.0))':
dependencies:
'@babel/core': 7.24.6
'@babel/plugin-transform-react-jsx-self': 7.24.6(@babel/core@7.24.6)
'@babel/plugin-transform-react-jsx-source': 7.24.6(@babel/core@7.24.6)
'@types/babel__core': 7.20.5
react-refresh: 0.14.2
- vite: 5.2.6(@types/node@20.12.12)(sass@1.77.2)(sugarss@4.0.1)
+ vite: 5.2.6(@types/node@20.14.9)(sass@1.77.6)(sugarss@4.0.1)(terser@5.31.0)
transitivePeerDependencies:
- supports-color
- '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@20.12.12)(@vitest/ui@1.6.0)(jsdom@24.1.0)(sass@1.77.2)(sugarss@4.0.1))':
+ '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@20.14.9)(@vitest/ui@1.6.0)(jsdom@24.1.0)(sass@1.77.6)(sugarss@4.0.1)(terser@5.31.0))':
dependencies:
'@ampproject/remapping': 2.2.1
'@bcoe/v8-coverage': 0.2.3
@@ -7463,7 +7902,7 @@ snapshots:
std-env: 3.7.0
strip-literal: 2.0.0
test-exclude: 6.0.0
- vitest: 1.6.0(@types/node@20.12.12)(@vitest/ui@1.6.0)(jsdom@24.1.0)(sass@1.77.2)(sugarss@4.0.1)
+ vitest: 1.6.0(@types/node@20.14.9)(@vitest/ui@1.6.0)(jsdom@24.1.0)(sass@1.77.6)(sugarss@4.0.1)(terser@5.31.0)
transitivePeerDependencies:
- supports-color
@@ -7498,7 +7937,7 @@ snapshots:
pathe: 1.1.2
picocolors: 1.0.0
sirv: 2.0.4
- vitest: 1.6.0(@types/node@20.12.12)(@vitest/ui@1.6.0)(jsdom@24.1.0)(sass@1.77.2)(sugarss@4.0.1)
+ vitest: 1.6.0(@types/node@20.14.9)(@vitest/ui@1.6.0)(jsdom@24.1.0)(sass@1.77.6)(sugarss@4.0.1)(terser@5.31.0)
'@vitest/utils@1.6.0':
dependencies:
@@ -7507,6 +7946,82 @@ snapshots:
loupe: 2.3.7
pretty-format: 29.7.0
+ '@webassemblyjs/ast@1.12.1':
+ dependencies:
+ '@webassemblyjs/helper-numbers': 1.11.6
+ '@webassemblyjs/helper-wasm-bytecode': 1.11.6
+
+ '@webassemblyjs/floating-point-hex-parser@1.11.6': {}
+
+ '@webassemblyjs/helper-api-error@1.11.6': {}
+
+ '@webassemblyjs/helper-buffer@1.12.1': {}
+
+ '@webassemblyjs/helper-numbers@1.11.6':
+ dependencies:
+ '@webassemblyjs/floating-point-hex-parser': 1.11.6
+ '@webassemblyjs/helper-api-error': 1.11.6
+ '@xtuc/long': 4.2.2
+
+ '@webassemblyjs/helper-wasm-bytecode@1.11.6': {}
+
+ '@webassemblyjs/helper-wasm-section@1.12.1':
+ dependencies:
+ '@webassemblyjs/ast': 1.12.1
+ '@webassemblyjs/helper-buffer': 1.12.1
+ '@webassemblyjs/helper-wasm-bytecode': 1.11.6
+ '@webassemblyjs/wasm-gen': 1.12.1
+
+ '@webassemblyjs/ieee754@1.11.6':
+ dependencies:
+ '@xtuc/ieee754': 1.2.0
+
+ '@webassemblyjs/leb128@1.11.6':
+ dependencies:
+ '@xtuc/long': 4.2.2
+
+ '@webassemblyjs/utf8@1.11.6': {}
+
+ '@webassemblyjs/wasm-edit@1.12.1':
+ dependencies:
+ '@webassemblyjs/ast': 1.12.1
+ '@webassemblyjs/helper-buffer': 1.12.1
+ '@webassemblyjs/helper-wasm-bytecode': 1.11.6
+ '@webassemblyjs/helper-wasm-section': 1.12.1
+ '@webassemblyjs/wasm-gen': 1.12.1
+ '@webassemblyjs/wasm-opt': 1.12.1
+ '@webassemblyjs/wasm-parser': 1.12.1
+ '@webassemblyjs/wast-printer': 1.12.1
+
+ '@webassemblyjs/wasm-gen@1.12.1':
+ dependencies:
+ '@webassemblyjs/ast': 1.12.1
+ '@webassemblyjs/helper-wasm-bytecode': 1.11.6
+ '@webassemblyjs/ieee754': 1.11.6
+ '@webassemblyjs/leb128': 1.11.6
+ '@webassemblyjs/utf8': 1.11.6
+
+ '@webassemblyjs/wasm-opt@1.12.1':
+ dependencies:
+ '@webassemblyjs/ast': 1.12.1
+ '@webassemblyjs/helper-buffer': 1.12.1
+ '@webassemblyjs/wasm-gen': 1.12.1
+ '@webassemblyjs/wasm-parser': 1.12.1
+
+ '@webassemblyjs/wasm-parser@1.12.1':
+ dependencies:
+ '@webassemblyjs/ast': 1.12.1
+ '@webassemblyjs/helper-api-error': 1.11.6
+ '@webassemblyjs/helper-wasm-bytecode': 1.11.6
+ '@webassemblyjs/ieee754': 1.11.6
+ '@webassemblyjs/leb128': 1.11.6
+ '@webassemblyjs/utf8': 1.11.6
+
+ '@webassemblyjs/wast-printer@1.12.1':
+ dependencies:
+ '@webassemblyjs/ast': 1.12.1
+ '@xtuc/long': 4.2.2
+
'@xmldom/xmldom@0.8.10': {}
'@xterm/addon-canvas@0.7.0(@xterm/xterm@5.5.0)':
@@ -7519,15 +8034,23 @@ snapshots:
'@xterm/xterm@5.5.0': {}
+ '@xtuc/ieee754@1.2.0': {}
+
+ '@xtuc/long@4.2.2': {}
+
abbrev@1.1.1: {}
- acorn-jsx@5.3.2(acorn@8.11.3):
+ acorn-import-assertions@1.9.0(acorn@8.12.0):
+ dependencies:
+ acorn: 8.12.0
+
+ acorn-jsx@5.3.2(acorn@8.12.0):
dependencies:
- acorn: 8.11.3
+ acorn: 8.12.0
acorn-walk@8.3.2: {}
- acorn@8.11.3: {}
+ acorn@8.12.0: {}
aes-decrypter@4.0.1:
dependencies:
@@ -7538,13 +8061,13 @@ snapshots:
agent-base@6.0.2:
dependencies:
- debug: 4.3.4
+ debug: 4.3.5
transitivePeerDependencies:
- supports-color
agent-base@7.1.0:
dependencies:
- debug: 4.3.4
+ debug: 4.3.5
transitivePeerDependencies:
- supports-color
@@ -7553,6 +8076,10 @@ snapshots:
clean-stack: 2.2.0
indent-string: 4.0.0
+ ajv-keywords@3.5.2(ajv@6.12.6):
+ dependencies:
+ ajv: 6.12.6
+
ajv@6.12.6:
dependencies:
fast-deep-equal: 3.1.3
@@ -7587,6 +8114,42 @@ snapshots:
aproba@2.0.0: {}
+ archiver-utils@2.1.0:
+ dependencies:
+ glob: 7.2.3
+ graceful-fs: 4.2.11
+ lazystream: 1.0.1
+ lodash.defaults: 4.2.0
+ lodash.difference: 4.5.0
+ lodash.flatten: 4.4.0
+ lodash.isplainobject: 4.0.6
+ lodash.union: 4.6.0
+ normalize-path: 3.0.0
+ readable-stream: 2.3.8
+
+ archiver-utils@3.0.4:
+ dependencies:
+ glob: 7.2.3
+ graceful-fs: 4.2.11
+ lazystream: 1.0.1
+ lodash.defaults: 4.2.0
+ lodash.difference: 4.5.0
+ lodash.flatten: 4.4.0
+ lodash.isplainobject: 4.0.6
+ lodash.union: 4.6.0
+ normalize-path: 3.0.0
+ readable-stream: 3.6.2
+
+ archiver@5.3.2:
+ dependencies:
+ archiver-utils: 2.1.0
+ async: 3.2.5
+ buffer-crc32: 0.2.13
+ readable-stream: 3.6.2
+ readdir-glob: 1.1.3
+ tar-stream: 2.2.0
+ zip-stream: 4.1.1
+
are-we-there-yet@2.0.0:
dependencies:
delegates: 1.0.0
@@ -7596,9 +8159,9 @@ snapshots:
argparse@2.0.1: {}
- aria-query@5.3.0:
+ aria-query@5.1.3:
dependencies:
- dequal: 2.0.3
+ deep-equal: 2.2.3
array-buffer-byte-length@1.0.1:
dependencies:
@@ -7613,6 +8176,15 @@ snapshots:
get-intrinsic: 1.2.4
is-string: 1.0.7
+ array-includes@3.1.8:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-abstract: 1.23.3
+ es-object-atoms: 1.0.0
+ get-intrinsic: 1.2.4
+ is-string: 1.0.7
+
array-union@2.1.0: {}
array.prototype.filter@1.0.3:
@@ -7623,12 +8195,13 @@ snapshots:
es-array-method-boxes-properly: 1.0.0
is-string: 1.0.7
- array.prototype.findlast@1.2.4:
+ array.prototype.findlast@1.2.5:
dependencies:
call-bind: 1.0.7
define-properties: 1.2.1
- es-abstract: 1.22.4
+ es-abstract: 1.23.3
es-errors: 1.3.0
+ es-object-atoms: 1.0.0
es-shim-unscopables: 1.0.2
array.prototype.findlastindex@1.2.4:
@@ -7657,14 +8230,14 @@ snapshots:
dependencies:
call-bind: 1.0.7
define-properties: 1.2.1
- es-abstract: 1.22.4
+ es-abstract: 1.23.3
es-shim-unscopables: 1.0.2
- array.prototype.tosorted@1.1.3:
+ array.prototype.tosorted@1.1.4:
dependencies:
call-bind: 1.0.7
define-properties: 1.2.1
- es-abstract: 1.22.4
+ es-abstract: 1.23.3
es-errors: 1.3.0
es-shim-unscopables: 1.0.2
@@ -7673,12 +8246,16 @@ snapshots:
array-buffer-byte-length: 1.0.1
call-bind: 1.0.7
define-properties: 1.2.1
- es-abstract: 1.22.4
+ es-abstract: 1.23.3
es-errors: 1.3.0
get-intrinsic: 1.2.4
is-array-buffer: 3.0.4
is-shared-array-buffer: 1.0.2
+ asn1@0.2.6:
+ dependencies:
+ safer-buffer: 2.1.2
+
assertion-error@1.1.0: {}
ast-types-flow@0.0.8: {}
@@ -7687,28 +8264,59 @@ snapshots:
dependencies:
tslib: 2.6.2
- async@3.2.5: {}
+ async-lock@1.4.1: {}
- asynciterator.prototype@1.0.0:
- dependencies:
- has-symbols: 1.0.3
+ async@3.2.5: {}
asynckit@0.4.0: {}
available-typed-arrays@1.0.6: {}
- axe-core@4.7.0: {}
+ available-typed-arrays@1.0.7:
+ dependencies:
+ possible-typed-array-names: 1.0.0
+
+ axe-core@4.9.1: {}
- axobject-query@3.2.1:
+ axobject-query@3.1.1:
dependencies:
- dequal: 2.0.3
+ deep-equal: 2.2.3
+
+ b4a@1.6.6: {}
balanced-match@1.0.2: {}
+ bare-events@2.4.2:
+ optional: true
+
+ bare-fs@2.3.1:
+ dependencies:
+ bare-events: 2.4.2
+ bare-path: 2.1.3
+ bare-stream: 2.1.3
+ optional: true
+
+ bare-os@2.4.0:
+ optional: true
+
+ bare-path@2.1.3:
+ dependencies:
+ bare-os: 2.4.0
+ optional: true
+
+ bare-stream@2.1.3:
+ dependencies:
+ streamx: 2.18.0
+ optional: true
+
base64-js@1.5.1: {}
basic-ftp@5.0.4: {}
+ bcrypt-pbkdf@1.0.2:
+ dependencies:
+ tweetnacl: 0.14.5
+
bcrypt@5.1.1:
dependencies:
'@mapbox/node-pre-gyp': 1.0.11
@@ -7717,11 +8325,13 @@ snapshots:
- encoding
- supports-color
- better-sqlite3@10.0.0:
+ better-sqlite3@11.1.1:
dependencies:
bindings: 1.5.0
prebuild-install: 7.1.1
+ big.js@5.2.2: {}
+
binary-extensions@2.2.0: {}
bindings@1.5.0:
@@ -7754,6 +8364,8 @@ snapshots:
node-releases: 2.0.14
update-browserslist-db: 1.0.13(browserslist@4.23.0)
+ buffer-crc32@0.2.13: {}
+
buffer-from@1.1.2: {}
buffer@5.7.1:
@@ -7761,6 +8373,9 @@ snapshots:
base64-js: 1.5.1
ieee754: 1.2.1
+ buildcheck@0.0.6:
+ optional: true
+
builtins@5.0.1:
dependencies:
semver: 7.6.0
@@ -7769,6 +8384,8 @@ snapshots:
dependencies:
streamsearch: 1.1.0
+ byline@5.0.0: {}
+
cac@6.7.14: {}
call-bind@1.0.7:
@@ -7863,15 +8480,9 @@ snapshots:
chroma-js@2.4.2: {}
- clean-stack@2.2.0: {}
+ chrome-trace-event@1.0.3: {}
- cli-color@2.0.3:
- dependencies:
- d: 1.0.1
- es5-ext: 0.10.62
- es6-iterator: 2.0.3
- memoizee: 0.4.15
- timers-ext: 0.1.7
+ clean-stack@2.2.0: {}
cli-cursor@3.1.0:
dependencies:
@@ -7930,7 +8541,14 @@ snapshots:
commander@10.0.1: {}
- commander@9.5.0: {}
+ commander@2.20.3: {}
+
+ compress-commons@4.1.2:
+ dependencies:
+ buffer-crc32: 0.2.13
+ crc32-stream: 4.0.3
+ normalize-path: 3.0.0
+ readable-stream: 3.6.2
concat-map@0.0.1: {}
@@ -7968,6 +8586,21 @@ snapshots:
core-js-pure@3.36.0: {}
+ core-util-is@1.0.3: {}
+
+ cpu-features@0.0.10:
+ dependencies:
+ buildcheck: 0.0.6
+ nan: 2.19.0
+ optional: true
+
+ crc-32@1.2.2: {}
+
+ crc32-stream@4.0.3:
+ dependencies:
+ crc-32: 1.2.2
+ readable-stream: 3.6.2
+
create-require@1.1.1: {}
crelt@1.0.6: {}
@@ -7990,11 +8623,6 @@ snapshots:
csstype@3.1.3: {}
- d@1.0.1:
- dependencies:
- es5-ext: 0.10.62
- type: 1.2.0
-
damerau-levenshtein@1.0.8: {}
dash-get@1.0.2: {}
@@ -8006,6 +8634,24 @@ snapshots:
whatwg-mimetype: 4.0.0
whatwg-url: 14.0.0
+ data-view-buffer@1.0.1:
+ dependencies:
+ call-bind: 1.0.7
+ es-errors: 1.3.0
+ is-data-view: 1.0.1
+
+ data-view-byte-length@1.0.1:
+ dependencies:
+ call-bind: 1.0.7
+ es-errors: 1.3.0
+ is-data-view: 1.0.1
+
+ data-view-byte-offset@1.0.0:
+ dependencies:
+ call-bind: 1.0.7
+ es-errors: 1.3.0
+ is-data-view: 1.0.1
+
date-fns@2.30.0:
dependencies:
'@babel/runtime': 7.23.9
@@ -8020,6 +8666,10 @@ snapshots:
dependencies:
ms: 2.1.2
+ debug@4.3.5:
+ dependencies:
+ ms: 2.1.2
+
decimal.js@10.4.3: {}
decompress-response@6.0.0:
@@ -8030,6 +8680,27 @@ snapshots:
dependencies:
type-detect: 4.0.8
+ deep-equal@2.2.3:
+ dependencies:
+ array-buffer-byte-length: 1.0.1
+ call-bind: 1.0.7
+ es-get-iterator: 1.1.3
+ get-intrinsic: 1.2.4
+ is-arguments: 1.1.1
+ is-array-buffer: 3.0.4
+ is-date-object: 1.0.5
+ is-regex: 1.1.4
+ is-shared-array-buffer: 1.0.3
+ isarray: 2.0.5
+ object-is: 1.1.6
+ object-keys: 1.1.1
+ object.assign: 4.1.5
+ regexp.prototype.flags: 1.5.2
+ side-channel: 1.0.6
+ which-boxed-primitive: 1.0.2
+ which-collection: 1.0.1
+ which-typed-array: 1.1.15
+
deep-extend@0.6.0: {}
deep-is@0.1.4: {}
@@ -8087,19 +8758,49 @@ snapshots:
diff@4.0.2: {}
- difflib@0.2.4:
- dependencies:
- heap: 0.2.7
-
dir-glob@3.0.1:
dependencies:
path-type: 4.0.0
- doctrine@2.1.0:
+ docker-compose@0.24.8:
dependencies:
- esutils: 2.0.3
+ yaml: 2.4.5
+
+ docker-modem@3.0.8:
+ dependencies:
+ debug: 4.3.5
+ readable-stream: 3.6.2
+ split-ca: 1.0.1
+ ssh2: 1.15.0
+ transitivePeerDependencies:
+ - supports-color
- doctrine@3.0.0:
+ docker-modem@5.0.3:
+ dependencies:
+ debug: 4.3.5
+ readable-stream: 3.6.2
+ split-ca: 1.0.1
+ ssh2: 1.15.0
+ transitivePeerDependencies:
+ - supports-color
+
+ dockerode@3.3.5:
+ dependencies:
+ '@balena/dockerignore': 1.0.2
+ docker-modem: 3.0.8
+ tar-fs: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ dockerode@4.0.2:
+ dependencies:
+ '@balena/dockerignore': 1.0.2
+ docker-modem: 5.0.3
+ tar-fs: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ doctrine@2.1.0:
dependencies:
esutils: 2.0.3
@@ -8127,30 +8828,20 @@ snapshots:
dotenv@16.4.5: {}
- dreamopt@0.8.0:
- dependencies:
- wordwrap: 1.0.0
-
- drizzle-kit@0.21.4:
+ drizzle-kit@0.22.7:
dependencies:
'@esbuild-kit/esm-loader': 2.6.5
- commander: 9.5.0
- env-paths: 3.0.0
esbuild: 0.19.12
esbuild-register: 3.5.0(esbuild@0.19.12)
- glob: 8.1.0
- hanji: 0.0.5
- json-diff: 0.9.0
- zod: 3.23.8
transitivePeerDependencies:
- supports-color
- drizzle-orm@0.30.10(@types/better-sqlite3@7.6.10)(@types/react@18.3.3)(better-sqlite3@10.0.0)(mysql2@3.9.8)(react@18.3.1):
+ drizzle-orm@0.31.2(@types/better-sqlite3@7.6.10)(@types/react@18.3.3)(better-sqlite3@11.1.1)(mysql2@3.10.1)(react@18.3.1):
optionalDependencies:
'@types/better-sqlite3': 7.6.10
'@types/react': 18.3.3
- better-sqlite3: 10.0.0
- mysql2: 3.9.8
+ better-sqlite3: 11.1.1
+ mysql2: 3.10.1
react: 18.3.1
eastasianwidth@0.2.0: {}
@@ -8161,15 +8852,20 @@ snapshots:
emoji-regex@9.2.2: {}
+ emojis-list@3.0.0: {}
+
enabled@2.0.0: {}
end-of-stream@1.4.4:
dependencies:
once: 1.4.0
- entities@4.5.0: {}
+ enhanced-resolve@5.16.1:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.2.1
- env-paths@3.0.0: {}
+ entities@4.5.0: {}
es-abstract@1.22.4:
dependencies:
@@ -8189,7 +8885,7 @@ snapshots:
has-property-descriptors: 1.0.2
has-proto: 1.0.1
has-symbols: 1.0.3
- hasown: 2.0.1
+ hasown: 2.0.2
internal-slot: 1.0.7
is-array-buffer: 3.0.4
is-callable: 1.2.7
@@ -8215,6 +8911,55 @@ snapshots:
unbox-primitive: 1.0.2
which-typed-array: 1.1.14
+ es-abstract@1.23.3:
+ dependencies:
+ array-buffer-byte-length: 1.0.1
+ arraybuffer.prototype.slice: 1.0.3
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.7
+ data-view-buffer: 1.0.1
+ data-view-byte-length: 1.0.1
+ data-view-byte-offset: 1.0.0
+ es-define-property: 1.0.0
+ es-errors: 1.3.0
+ es-object-atoms: 1.0.0
+ es-set-tostringtag: 2.0.3
+ es-to-primitive: 1.2.1
+ function.prototype.name: 1.1.6
+ get-intrinsic: 1.2.4
+ get-symbol-description: 1.0.2
+ globalthis: 1.0.3
+ gopd: 1.0.1
+ has-property-descriptors: 1.0.2
+ has-proto: 1.0.3
+ has-symbols: 1.0.3
+ hasown: 2.0.2
+ internal-slot: 1.0.7
+ is-array-buffer: 3.0.4
+ is-callable: 1.2.7
+ is-data-view: 1.0.1
+ is-negative-zero: 2.0.3
+ is-regex: 1.1.4
+ is-shared-array-buffer: 1.0.3
+ is-string: 1.0.7
+ is-typed-array: 1.1.13
+ is-weakref: 1.0.2
+ object-inspect: 1.13.1
+ object-keys: 1.1.1
+ object.assign: 4.1.5
+ regexp.prototype.flags: 1.5.2
+ safe-array-concat: 1.1.2
+ safe-regex-test: 1.0.3
+ string.prototype.trim: 1.2.9
+ string.prototype.trimend: 1.0.8
+ string.prototype.trimstart: 1.0.8
+ typed-array-buffer: 1.0.2
+ typed-array-byte-length: 1.0.1
+ typed-array-byte-offset: 1.0.2
+ typed-array-length: 1.0.6
+ unbox-primitive: 1.0.2
+ which-typed-array: 1.1.15
+
es-array-method-boxes-properly@1.0.0: {}
es-define-property@1.0.0:
@@ -8223,33 +8968,56 @@ snapshots:
es-errors@1.3.0: {}
- es-iterator-helpers@1.0.17:
+ es-get-iterator@1.1.3:
+ dependencies:
+ call-bind: 1.0.7
+ get-intrinsic: 1.2.4
+ has-symbols: 1.0.3
+ is-arguments: 1.1.1
+ is-map: 2.0.2
+ is-set: 2.0.2
+ is-string: 1.0.7
+ isarray: 2.0.5
+ stop-iteration-iterator: 1.0.0
+
+ es-iterator-helpers@1.0.19:
dependencies:
- asynciterator.prototype: 1.0.0
call-bind: 1.0.7
define-properties: 1.2.1
- es-abstract: 1.22.4
+ es-abstract: 1.23.3
es-errors: 1.3.0
- es-set-tostringtag: 2.0.2
+ es-set-tostringtag: 2.0.3
function-bind: 1.1.2
get-intrinsic: 1.2.4
globalthis: 1.0.3
has-property-descriptors: 1.0.2
- has-proto: 1.0.1
+ has-proto: 1.0.3
has-symbols: 1.0.3
internal-slot: 1.0.7
iterator.prototype: 1.1.2
- safe-array-concat: 1.1.0
+ safe-array-concat: 1.1.2
+
+ es-module-lexer@1.5.3: {}
+
+ es-object-atoms@1.0.0:
+ dependencies:
+ es-errors: 1.3.0
es-set-tostringtag@2.0.2:
dependencies:
get-intrinsic: 1.2.4
has-tostringtag: 1.0.2
- hasown: 2.0.1
+ hasown: 2.0.2
+
+ es-set-tostringtag@2.0.3:
+ dependencies:
+ get-intrinsic: 1.2.4
+ has-tostringtag: 1.0.2
+ hasown: 2.0.2
es-shim-unscopables@1.0.2:
dependencies:
- hasown: 2.0.1
+ hasown: 2.0.2
es-to-primitive@1.2.1:
dependencies:
@@ -8257,33 +9025,9 @@ snapshots:
is-date-object: 1.0.5
is-symbol: 1.0.4
- es5-ext@0.10.62:
- dependencies:
- es6-iterator: 2.0.3
- es6-symbol: 3.1.3
- next-tick: 1.1.0
-
- es6-iterator@2.0.3:
- dependencies:
- d: 1.0.1
- es5-ext: 0.10.62
- es6-symbol: 3.1.3
-
- es6-symbol@3.1.3:
- dependencies:
- d: 1.0.1
- ext: 1.7.0
-
- es6-weak-map@2.0.3:
- dependencies:
- d: 1.0.1
- es5-ext: 0.10.62
- es6-iterator: 2.0.3
- es6-symbol: 3.1.3
-
esbuild-register@3.5.0(esbuild@0.19.12):
dependencies:
- debug: 4.3.4
+ debug: 4.3.5
esbuild: 0.19.12
transitivePeerDependencies:
- supports-color
@@ -8379,14 +9123,14 @@ snapshots:
optionalDependencies:
source-map: 0.6.1
- eslint-config-prettier@9.1.0(eslint@8.57.0):
+ eslint-config-prettier@9.1.0(eslint@9.6.0):
dependencies:
- eslint: 8.57.0
+ eslint: 9.6.0
- eslint-config-turbo@1.13.3(eslint@8.57.0):
+ eslint-config-turbo@2.0.6(eslint@9.6.0):
dependencies:
- eslint: 8.57.0
- eslint-plugin-turbo: 1.13.3(eslint@8.57.0)
+ eslint: 9.6.0
+ eslint-plugin-turbo: 2.0.6(eslint@9.6.0)
eslint-import-resolver-node@0.3.9:
dependencies:
@@ -8396,17 +9140,17 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-module-utils@2.8.0(@typescript-eslint/parser@7.11.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0):
+ eslint-module-utils@2.8.0(@typescript-eslint/parser@7.14.1(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint@9.6.0):
dependencies:
debug: 3.2.7
optionalDependencies:
- '@typescript-eslint/parser': 7.11.0(eslint@8.57.0)(typescript@5.4.5)
- eslint: 8.57.0
+ '@typescript-eslint/parser': 7.14.1(eslint@9.6.0)(typescript@5.5.2)
+ eslint: 9.6.0
eslint-import-resolver-node: 0.3.9
transitivePeerDependencies:
- supports-color
- eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.11.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0):
+ eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.14.1(eslint@9.6.0)(typescript@5.5.2))(eslint@9.6.0):
dependencies:
array-includes: 3.1.7
array.prototype.findlastindex: 1.2.4
@@ -8414,9 +9158,9 @@ snapshots:
array.prototype.flatmap: 1.3.2
debug: 3.2.7
doctrine: 2.1.0
- eslint: 8.57.0
+ eslint: 9.6.0
eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.11.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0)
+ eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.14.1(eslint@9.6.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint@9.6.0)
hasown: 2.0.1
is-core-module: 2.13.1
is-glob: 4.0.3
@@ -8427,102 +9171,105 @@ snapshots:
semver: 6.3.1
tsconfig-paths: 3.15.0
optionalDependencies:
- '@typescript-eslint/parser': 7.11.0(eslint@8.57.0)(typescript@5.4.5)
+ '@typescript-eslint/parser': 7.14.1(eslint@9.6.0)(typescript@5.5.2)
transitivePeerDependencies:
- eslint-import-resolver-typescript
- eslint-import-resolver-webpack
- supports-color
- eslint-plugin-jsx-a11y@6.8.0(eslint@8.57.0):
+ eslint-plugin-jsx-a11y@6.9.0(eslint@9.6.0):
dependencies:
- '@babel/runtime': 7.23.9
- aria-query: 5.3.0
- array-includes: 3.1.7
+ aria-query: 5.1.3
+ array-includes: 3.1.8
array.prototype.flatmap: 1.3.2
ast-types-flow: 0.0.8
- axe-core: 4.7.0
- axobject-query: 3.2.1
+ axe-core: 4.9.1
+ axobject-query: 3.1.1
damerau-levenshtein: 1.0.8
emoji-regex: 9.2.2
- es-iterator-helpers: 1.0.17
- eslint: 8.57.0
- hasown: 2.0.1
+ es-iterator-helpers: 1.0.19
+ eslint: 9.6.0
+ hasown: 2.0.2
jsx-ast-utils: 3.3.5
language-tags: 1.0.9
minimatch: 3.1.2
- object.entries: 1.1.7
- object.fromentries: 2.0.7
+ object.fromentries: 2.0.8
+ safe-regex-test: 1.0.3
+ string.prototype.includes: 2.0.0
- eslint-plugin-react-hooks@4.6.2(eslint@8.57.0):
+ eslint-plugin-react-hooks@4.6.2(eslint@9.6.0):
dependencies:
- eslint: 8.57.0
+ eslint: 9.6.0
- eslint-plugin-react@7.34.1(eslint@8.57.0):
+ eslint-plugin-react@7.34.3(eslint@9.6.0):
dependencies:
- array-includes: 3.1.7
- array.prototype.findlast: 1.2.4
+ array-includes: 3.1.8
+ array.prototype.findlast: 1.2.5
array.prototype.flatmap: 1.3.2
array.prototype.toreversed: 1.1.2
- array.prototype.tosorted: 1.1.3
+ array.prototype.tosorted: 1.1.4
doctrine: 2.1.0
- es-iterator-helpers: 1.0.17
- eslint: 8.57.0
+ es-iterator-helpers: 1.0.19
+ eslint: 9.6.0
estraverse: 5.3.0
jsx-ast-utils: 3.3.5
minimatch: 3.1.2
- object.entries: 1.1.7
- object.fromentries: 2.0.7
- object.hasown: 1.1.3
- object.values: 1.1.7
+ object.entries: 1.1.8
+ object.fromentries: 2.0.8
+ object.hasown: 1.1.4
+ object.values: 1.2.0
prop-types: 15.8.1
resolve: 2.0.0-next.5
semver: 6.3.1
- string.prototype.matchall: 4.0.10
+ string.prototype.matchall: 4.0.11
- eslint-plugin-turbo@1.13.3(eslint@8.57.0):
+ eslint-plugin-turbo@2.0.6(eslint@9.6.0):
dependencies:
dotenv: 16.0.3
- eslint: 8.57.0
+ eslint: 9.6.0
- eslint-scope@7.2.2:
+ eslint-scope@5.1.1:
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 4.3.0
+
+ eslint-scope@8.0.1:
dependencies:
esrecurse: 4.3.0
estraverse: 5.3.0
eslint-visitor-keys@3.4.3: {}
- eslint@8.57.0:
+ eslint-visitor-keys@4.0.0: {}
+
+ eslint@9.6.0:
dependencies:
- '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
+ '@eslint-community/eslint-utils': 4.4.0(eslint@9.6.0)
'@eslint-community/regexpp': 4.10.0
- '@eslint/eslintrc': 2.1.4
- '@eslint/js': 8.57.0
- '@humanwhocodes/config-array': 0.11.14
+ '@eslint/config-array': 0.17.0
+ '@eslint/eslintrc': 3.1.0
+ '@eslint/js': 9.6.0
'@humanwhocodes/module-importer': 1.0.1
+ '@humanwhocodes/retry': 0.3.0
'@nodelib/fs.walk': 1.2.8
- '@ungap/structured-clone': 1.2.0
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.3
- debug: 4.3.4
- doctrine: 3.0.0
+ debug: 4.3.5
escape-string-regexp: 4.0.0
- eslint-scope: 7.2.2
- eslint-visitor-keys: 3.4.3
- espree: 9.6.1
+ eslint-scope: 8.0.1
+ eslint-visitor-keys: 4.0.0
+ espree: 10.1.0
esquery: 1.5.0
esutils: 2.0.3
fast-deep-equal: 3.1.3
- file-entry-cache: 6.0.1
+ file-entry-cache: 8.0.0
find-up: 5.0.0
glob-parent: 6.0.2
- globals: 13.24.0
- graphemer: 1.4.0
ignore: 5.3.1
imurmurhash: 0.1.4
is-glob: 4.0.3
is-path-inside: 3.0.3
- js-yaml: 4.1.0
json-stable-stringify-without-jsonify: 1.0.1
levn: 0.4.1
lodash.merge: 4.6.2
@@ -8534,11 +9281,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
- espree@9.6.1:
+ espree@10.1.0:
dependencies:
- acorn: 8.11.3
- acorn-jsx: 5.3.2(acorn@8.11.3)
- eslint-visitor-keys: 3.4.3
+ acorn: 8.12.0
+ acorn-jsx: 5.3.2(acorn@8.12.0)
+ eslint-visitor-keys: 4.0.0
esprima@4.0.1: {}
@@ -8550,6 +9297,8 @@ snapshots:
dependencies:
estraverse: 5.3.0
+ estraverse@4.3.0: {}
+
estraverse@5.3.0: {}
estree-walker@3.0.3:
@@ -8558,10 +9307,7 @@ snapshots:
esutils@2.0.3: {}
- event-emitter@0.3.5:
- dependencies:
- d: 1.0.1
- es5-ext: 0.10.62
+ events@3.3.0: {}
execa@5.1.1:
dependencies:
@@ -8589,10 +9335,6 @@ snapshots:
expand-template@2.0.3: {}
- ext@1.7.0:
- dependencies:
- type: 2.7.2
-
external-editor@3.1.0:
dependencies:
chardet: 0.7.0
@@ -8601,6 +9343,8 @@ snapshots:
fast-deep-equal@3.1.3: {}
+ fast-fifo@1.3.2: {}
+
fast-glob@3.3.2:
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -8625,9 +9369,9 @@ snapshots:
dependencies:
escape-string-regexp: 1.0.5
- file-entry-cache@6.0.1:
+ file-entry-cache@8.0.0:
dependencies:
- flat-cache: 3.2.0
+ flat-cache: 4.0.1
file-uri-to-path@1.0.0: {}
@@ -8640,13 +9384,12 @@ snapshots:
locate-path: 6.0.0
path-exists: 4.0.0
- flag-icons@7.2.2: {}
+ flag-icons@7.2.3: {}
- flat-cache@3.2.0:
+ flat-cache@4.0.1:
dependencies:
flatted: 3.2.9
keyv: 4.5.4
- rimraf: 3.0.2
flatted@3.2.9: {}
@@ -8696,7 +9439,7 @@ snapshots:
dependencies:
call-bind: 1.0.7
define-properties: 1.2.1
- es-abstract: 1.22.4
+ es-abstract: 1.23.3
functions-have-names: 1.2.3
functions-have-names@1.2.3: {}
@@ -8729,10 +9472,12 @@ snapshots:
function-bind: 1.1.2
has-proto: 1.0.1
has-symbols: 1.0.3
- hasown: 2.0.1
+ hasown: 2.0.2
get-nonce@1.0.1: {}
+ get-port@5.1.1: {}
+
get-stream@6.0.1: {}
get-stream@8.0.1: {}
@@ -8751,7 +9496,7 @@ snapshots:
dependencies:
basic-ftp: 5.0.4
data-uri-to-buffer: 6.0.2
- debug: 4.3.4
+ debug: 4.3.5
fs-extra: 11.2.0
transitivePeerDependencies:
- supports-color
@@ -8766,20 +9511,23 @@ snapshots:
dependencies:
is-glob: 4.0.3
+ glob-to-regexp@0.4.1: {}
+
glob@10.3.10:
dependencies:
foreground-child: 3.1.1
jackspeak: 2.3.6
minimatch: 9.0.4
- minipass: 7.0.4
- path-scurry: 1.11.0
+ minipass: 7.1.2
+ path-scurry: 1.11.1
- glob@10.4.1:
+ glob@10.4.2:
dependencies:
foreground-child: 3.1.1
jackspeak: 3.1.2
minimatch: 9.0.4
minipass: 7.1.2
+ package-json-from-dist: 1.0.0
path-scurry: 1.11.1
glob@7.2.3:
@@ -8791,14 +9539,6 @@ snapshots:
once: 1.4.0
path-is-absolute: 1.0.1
- glob@8.1.0:
- dependencies:
- fs.realpath: 1.0.0
- inflight: 1.0.6
- inherits: 2.0.4
- minimatch: 5.1.6
- once: 1.4.0
-
global@4.4.0:
dependencies:
min-document: 2.19.0
@@ -8806,9 +9546,7 @@ snapshots:
globals@11.12.0: {}
- globals@13.24.0:
- dependencies:
- type-fest: 0.20.2
+ globals@14.0.0: {}
globalthis@1.0.3:
dependencies:
@@ -8858,11 +9596,6 @@ snapshots:
optionalDependencies:
uglify-js: 3.17.4
- hanji@0.0.5:
- dependencies:
- lodash.throttle: 4.1.1
- sisteransi: 1.0.5
-
has-bigints@1.0.2: {}
has-flag@3.0.0: {}
@@ -8875,6 +9608,8 @@ snapshots:
has-proto@1.0.1: {}
+ has-proto@1.0.3: {}
+
has-symbols@1.0.3: {}
has-tostringtag@1.0.2:
@@ -8887,13 +9622,15 @@ snapshots:
dependencies:
function-bind: 1.1.2
+ hasown@2.0.2:
+ dependencies:
+ function-bind: 1.1.2
+
header-case@1.0.1:
dependencies:
no-case: 2.3.2
upper-case: 1.1.3
- heap@0.2.7: {}
-
html-encoding-sniffer@4.0.0:
dependencies:
whatwg-encoding: 3.1.1
@@ -8903,21 +9640,21 @@ snapshots:
http-proxy-agent@7.0.2:
dependencies:
agent-base: 7.1.0
- debug: 4.3.4
+ debug: 4.3.5
transitivePeerDependencies:
- supports-color
https-proxy-agent@5.0.1:
dependencies:
agent-base: 6.0.2
- debug: 4.3.4
+ debug: 4.3.5
transitivePeerDependencies:
- supports-color
https-proxy-agent@7.0.4:
dependencies:
agent-base: 7.1.0
- debug: 4.3.4
+ debug: 4.3.5
transitivePeerDependencies:
- supports-color
@@ -8996,7 +9733,7 @@ snapshots:
internal-slot@1.0.7:
dependencies:
es-errors: 1.3.0
- hasown: 2.0.1
+ hasown: 2.0.2
side-channel: 1.0.5
international-types@0.8.1: {}
@@ -9024,6 +9761,11 @@ snapshots:
jsbn: 1.1.0
sprintf-js: 1.1.3
+ is-arguments@1.1.1:
+ dependencies:
+ call-bind: 1.0.7
+ has-tostringtag: 1.0.2
+
is-array-buffer@3.0.4:
dependencies:
call-bind: 1.0.7
@@ -9054,6 +9796,10 @@ snapshots:
dependencies:
hasown: 2.0.1
+ is-data-view@1.0.1:
+ dependencies:
+ is-typed-array: 1.1.13
+
is-date-object@1.0.5:
dependencies:
has-tostringtag: 1.0.2
@@ -9090,6 +9836,8 @@ snapshots:
is-negative-zero@2.0.2: {}
+ is-negative-zero@2.0.3: {}
+
is-number-object@1.0.7:
dependencies:
has-tostringtag: 1.0.2
@@ -9106,8 +9854,6 @@ snapshots:
is-potential-custom-element-name@1.0.1: {}
- is-promise@2.2.2: {}
-
is-property@1.0.2: {}
is-regex@1.1.4:
@@ -9121,6 +9867,10 @@ snapshots:
dependencies:
call-bind: 1.0.7
+ is-shared-array-buffer@1.0.3:
+ dependencies:
+ call-bind: 1.0.7
+
is-stream@2.0.1: {}
is-stream@3.0.0: {}
@@ -9156,6 +9906,8 @@ snapshots:
is-what@4.1.16: {}
+ isarray@1.0.0: {}
+
isarray@2.0.5: {}
isbinaryfile@4.0.10: {}
@@ -9175,7 +9927,7 @@ snapshots:
istanbul-lib-source-maps@5.0.4:
dependencies:
'@jridgewell/trace-mapping': 0.3.25
- debug: 4.3.4
+ debug: 4.3.5
istanbul-lib-coverage: 3.2.2
transitivePeerDependencies:
- supports-color
@@ -9191,7 +9943,7 @@ snapshots:
get-intrinsic: 1.2.4
has-symbols: 1.0.3
reflect.getprototypeof: 1.0.5
- set-function-name: 2.0.1
+ set-function-name: 2.0.2
jackspeak@2.3.6:
dependencies:
@@ -9205,9 +9957,15 @@ snapshots:
optionalDependencies:
'@pkgjs/parseargs': 0.11.0
+ jest-worker@27.5.1:
+ dependencies:
+ '@types/node': 20.14.9
+ merge-stream: 2.0.0
+ supports-color: 8.1.1
+
jose@5.2.2: {}
- jotai@2.8.2(@types/react@18.3.3)(react@18.3.1):
+ jotai@2.8.4(@types/react@18.3.3)(react@18.3.1):
optionalDependencies:
'@types/react': 18.3.3
react: 18.3.1
@@ -9243,7 +10001,7 @@ snapshots:
whatwg-encoding: 3.1.1
whatwg-mimetype: 4.0.0
whatwg-url: 14.0.0
- ws: 8.17.0
+ ws: 8.17.1
xml-name-validator: 5.0.0
transitivePeerDependencies:
- bufferutil
@@ -9254,11 +10012,7 @@ snapshots:
json-buffer@3.0.1: {}
- json-diff@0.9.0:
- dependencies:
- cli-color: 2.0.3
- difflib: 0.2.4
- dreamopt: 0.8.0
+ json-parse-even-better-errors@2.3.1: {}
json-schema-traverse@0.4.1: {}
@@ -9280,10 +10034,10 @@ snapshots:
jsx-ast-utils@3.3.5:
dependencies:
- array-includes: 3.1.7
+ array-includes: 3.1.8
array.prototype.flat: 1.3.2
object.assign: 4.1.5
- object.values: 1.1.7
+ object.values: 1.2.0
keycode@2.2.0: {}
@@ -9305,6 +10059,10 @@ snapshots:
dependencies:
language-subtag-registry: 0.3.22
+ lazystream@1.0.1:
+ dependencies:
+ readable-stream: 2.3.8
+
levn@0.4.1:
dependencies:
prelude-ls: 1.2.1
@@ -9316,6 +10074,14 @@ snapshots:
linkifyjs@4.1.3: {}
+ loader-runner@4.3.0: {}
+
+ loader-utils@2.0.4:
+ dependencies:
+ big.js: 5.2.2
+ emojis-list: 3.0.0
+ json5: 2.2.3
+
local-pkg@0.5.0:
dependencies:
mlly: 1.5.0
@@ -9327,13 +10093,19 @@ snapshots:
lodash.defaults@4.2.0: {}
+ lodash.difference@4.5.0: {}
+
+ lodash.flatten@4.4.0: {}
+
lodash.get@4.4.2: {}
lodash.isarguments@3.1.0: {}
+ lodash.isplainobject@4.0.6: {}
+
lodash.merge@4.6.2: {}
- lodash.throttle@4.1.1: {}
+ lodash.union@4.6.0: {}
lodash@4.17.21: {}
@@ -9385,10 +10157,6 @@ snapshots:
lru-cache@8.0.5: {}
- lru-queue@0.1.0:
- dependencies:
- es5-ext: 0.10.62
-
m3u8-parser@7.1.0:
dependencies:
'@babel/runtime': 7.23.9
@@ -9401,8 +10169,8 @@ snapshots:
magicast@0.3.3:
dependencies:
- '@babel/parser': 7.24.0
- '@babel/types': 7.24.0
+ '@babel/parser': 7.24.6
+ '@babel/types': 7.24.6
source-map-js: 1.2.0
make-dir@3.1.0:
@@ -9415,15 +10183,15 @@ snapshots:
make-error@1.3.6: {}
- mantine-react-table@2.0.0-beta.3(@mantine/core@7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/dates@7.10.0(@mantine/core@7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.10.0(react@18.3.1))(dayjs@1.11.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.10.0(react@18.3.1))(@tabler/icons-react@3.5.0(react@18.3.1))(clsx@2.1.1)(dayjs@1.11.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ mantine-react-table@2.0.0-beta.5(@mantine/core@7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/dates@7.11.0(@mantine/core@7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.11.0(react@18.3.1))(dayjs@1.11.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.11.0(react@18.3.1))(@tabler/icons-react@3.7.0(react@18.3.1))(clsx@2.1.1)(dayjs@1.11.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
- '@mantine/core': 7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- '@mantine/dates': 7.10.0(@mantine/core@7.10.0(@mantine/hooks@7.10.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.10.0(react@18.3.1))(dayjs@1.11.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- '@mantine/hooks': 7.10.0(react@18.3.1)
- '@tabler/icons-react': 3.5.0(react@18.3.1)
+ '@mantine/core': 7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@mantine/dates': 7.11.0(@mantine/core@7.11.0(@mantine/hooks@7.11.0(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.11.0(react@18.3.1))(dayjs@1.11.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@mantine/hooks': 7.11.0(react@18.3.1)
+ '@tabler/icons-react': 3.7.0(react@18.3.1)
'@tanstack/match-sorter-utils': 8.15.1
- '@tanstack/react-table': 8.16.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
- '@tanstack/react-virtual': 3.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@tanstack/react-table': 8.17.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ '@tanstack/react-virtual': 3.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
clsx: 2.1.1
dayjs: 1.11.11
react: 18.3.1
@@ -9440,17 +10208,6 @@ snapshots:
mdurl@2.0.0: {}
- memoizee@0.4.15:
- dependencies:
- d: 1.0.1
- es5-ext: 0.10.62
- es6-weak-map: 2.0.3
- event-emitter: 0.3.5
- is-promise: 2.2.2
- lru-queue: 0.1.0
- next-tick: 1.1.0
- timers-ext: 0.1.7
-
merge-stream@2.0.0: {}
merge2@1.4.1: {}
@@ -9496,8 +10253,6 @@ snapshots:
minipass@5.0.0: {}
- minipass@7.0.4: {}
-
minipass@7.1.2: {}
minizlib@2.1.2:
@@ -9515,7 +10270,7 @@ snapshots:
mlly@1.5.0:
dependencies:
- acorn: 8.11.3
+ acorn: 8.12.0
pathe: 1.1.2
pkg-types: 1.0.3
ufo: 1.4.0
@@ -9540,7 +10295,7 @@ snapshots:
'@babel/runtime': 7.23.9
global: 4.4.0
- mysql2@3.9.8:
+ mysql2@3.10.1:
dependencies:
denque: 2.1.0
generate-function: 2.3.1
@@ -9555,6 +10310,9 @@ snapshots:
dependencies:
lru-cache: 7.18.3
+ nan@2.19.0:
+ optional: true
+
nanoid@3.3.7: {}
napi-build-utils@1.0.2: {}
@@ -9565,10 +10323,10 @@ snapshots:
netmask@2.0.2: {}
- next-auth@5.0.0-beta.18(next@14.2.3(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.2))(react@18.3.1):
+ next-auth@5.0.0-beta.19(next@14.2.4(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6))(react@18.3.1):
dependencies:
- '@auth/core': 0.31.0
- next: 14.2.3(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.2)
+ '@auth/core': 0.32.0
+ next: 14.2.4(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6)
react: 18.3.1
next-international@1.2.4:
@@ -9577,11 +10335,9 @@ snapshots:
international-types: 0.8.1
server-only: 0.0.1
- next-tick@1.1.0: {}
-
- next@14.2.3(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.2):
+ next@14.2.4(@babel/core@7.24.6)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.6):
dependencies:
- '@next/env': 14.2.3
+ '@next/env': 14.2.4
'@swc/helpers': 0.5.5
busboy: 1.6.0
caniuse-lite: 1.0.30001587
@@ -9591,16 +10347,16 @@ snapshots:
react-dom: 18.3.1(react@18.3.1)
styled-jsx: 5.1.1(@babel/core@7.24.6)(react@18.3.1)
optionalDependencies:
- '@next/swc-darwin-arm64': 14.2.3
- '@next/swc-darwin-x64': 14.2.3
- '@next/swc-linux-arm64-gnu': 14.2.3
- '@next/swc-linux-arm64-musl': 14.2.3
- '@next/swc-linux-x64-gnu': 14.2.3
- '@next/swc-linux-x64-musl': 14.2.3
- '@next/swc-win32-arm64-msvc': 14.2.3
- '@next/swc-win32-ia32-msvc': 14.2.3
- '@next/swc-win32-x64-msvc': 14.2.3
- sass: 1.77.2
+ '@next/swc-darwin-arm64': 14.2.4
+ '@next/swc-darwin-x64': 14.2.4
+ '@next/swc-linux-arm64-gnu': 14.2.4
+ '@next/swc-linux-arm64-musl': 14.2.4
+ '@next/swc-linux-x64-gnu': 14.2.4
+ '@next/swc-linux-x64-musl': 14.2.4
+ '@next/swc-win32-arm64-msvc': 14.2.4
+ '@next/swc-win32-ia32-msvc': 14.2.4
+ '@next/swc-win32-x64-msvc': 14.2.4
+ sass: 1.77.6
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
@@ -9623,6 +10379,11 @@ snapshots:
dependencies:
whatwg-url: 5.0.0
+ node-loader@2.0.0(webpack@5.91.0):
+ dependencies:
+ loader-utils: 2.0.4
+ webpack: 5.91.0
+
node-plop@0.26.3:
dependencies:
'@babel/runtime-corejs3': 7.23.9
@@ -9664,10 +10425,17 @@ snapshots:
oauth4webapi@2.10.3: {}
+ oauth4webapi@2.11.0: {}
+
object-assign@4.1.1: {}
object-inspect@1.13.1: {}
+ object-is@1.1.6:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+
object-keys@1.1.1: {}
object.assign@4.1.5:
@@ -9677,11 +10445,11 @@ snapshots:
has-symbols: 1.0.3
object-keys: 1.1.1
- object.entries@1.1.7:
+ object.entries@1.1.8:
dependencies:
call-bind: 1.0.7
define-properties: 1.2.1
- es-abstract: 1.22.4
+ es-object-atoms: 1.0.0
object.fromentries@2.0.7:
dependencies:
@@ -9689,6 +10457,13 @@ snapshots:
define-properties: 1.2.1
es-abstract: 1.22.4
+ object.fromentries@2.0.8:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-abstract: 1.23.3
+ es-object-atoms: 1.0.0
+
object.groupby@1.0.2:
dependencies:
array.prototype.filter: 1.0.3
@@ -9697,10 +10472,11 @@ snapshots:
es-abstract: 1.22.4
es-errors: 1.3.0
- object.hasown@1.1.3:
+ object.hasown@1.1.4:
dependencies:
define-properties: 1.2.1
- es-abstract: 1.22.4
+ es-abstract: 1.23.3
+ es-object-atoms: 1.0.0
object.omit@3.0.0:
dependencies:
@@ -9716,6 +10492,12 @@ snapshots:
define-properties: 1.2.1
es-abstract: 1.22.4
+ object.values@1.2.0:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-object-atoms: 1.0.0
+
once@1.4.0:
dependencies:
wrappy: 1.0.2
@@ -9788,7 +10570,7 @@ snapshots:
dependencies:
'@tootallnate/quickjs-emscripten': 0.23.0
agent-base: 7.1.0
- debug: 4.3.4
+ debug: 4.3.5
get-uri: 6.0.3
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.4
@@ -9802,6 +10584,8 @@ snapshots:
degenerator: 5.0.1
netmask: 2.0.2
+ package-json-from-dist@1.0.0: {}
+
param-case@2.1.1:
dependencies:
no-case: 2.3.2
@@ -9833,11 +10617,6 @@ snapshots:
path-parse@1.0.7: {}
- path-scurry@1.11.0:
- dependencies:
- lru-cache: 10.2.0
- minipass: 7.0.4
-
path-scurry@1.11.1:
dependencies:
lru-cache: 10.2.0
@@ -9851,8 +10630,6 @@ snapshots:
picocolors@1.0.0: {}
- picocolors@1.0.1: {}
-
picomatch@2.3.1: {}
pkcs7@1.0.4:
@@ -9865,6 +10642,8 @@ snapshots:
mlly: 1.5.0
pathe: 1.1.2
+ possible-typed-array-names@1.0.0: {}
+
postcss-js@4.0.1(postcss@8.4.38):
dependencies:
camelcase-css: 2.0.1
@@ -9934,7 +10713,7 @@ snapshots:
prelude-ls@1.2.1: {}
- prettier@3.2.5: {}
+ prettier@3.3.2: {}
pretty-format@29.7.0:
dependencies:
@@ -9946,6 +10725,8 @@ snapshots:
prismjs@1.29.0: {}
+ process-nextick-args@2.0.1: {}
+
process@0.11.10: {}
prop-types@15.8.1:
@@ -9954,6 +10735,16 @@ snapshots:
object-assign: 4.1.1
react-is: 16.13.1
+ proper-lockfile@4.1.2:
+ dependencies:
+ graceful-fs: 4.2.11
+ retry: 0.12.0
+ signal-exit: 3.0.7
+
+ properties-reader@2.3.0:
+ dependencies:
+ mkdirp: 1.0.4
+
prosemirror-changeset@2.2.1:
dependencies:
prosemirror-transform: 1.8.0
@@ -10060,7 +10851,7 @@ snapshots:
proxy-agent@6.4.0:
dependencies:
agent-base: 7.1.0
- debug: 4.3.4
+ debug: 4.3.5
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.4
lru-cache: 7.18.3
@@ -10087,6 +10878,12 @@ snapshots:
queue-microtask@1.2.3: {}
+ queue-tick@1.0.1: {}
+
+ randombytes@2.1.0:
+ dependencies:
+ safe-buffer: 5.2.1
+
rc@1.2.8:
dependencies:
deep-extend: 0.6.0
@@ -10172,12 +10969,26 @@ snapshots:
dependencies:
loose-envify: 1.4.0
+ readable-stream@2.3.8:
+ dependencies:
+ core-util-is: 1.0.3
+ inherits: 2.0.4
+ isarray: 1.0.0
+ process-nextick-args: 2.0.1
+ safe-buffer: 5.1.2
+ string_decoder: 1.1.1
+ util-deprecate: 1.0.2
+
readable-stream@3.6.2:
dependencies:
inherits: 2.0.4
string_decoder: 1.3.0
util-deprecate: 1.0.2
+ readdir-glob@1.1.3:
+ dependencies:
+ minimatch: 5.1.6
+
readdirp@3.6.0:
dependencies:
picomatch: 2.3.1
@@ -10192,7 +11003,7 @@ snapshots:
dependencies:
call-bind: 1.0.7
define-properties: 1.2.1
- es-abstract: 1.22.4
+ es-abstract: 1.23.3
es-errors: 1.3.0
get-intrinsic: 1.2.4
globalthis: 1.0.3
@@ -10205,7 +11016,7 @@ snapshots:
call-bind: 1.0.7
define-properties: 1.2.1
es-errors: 1.3.0
- set-function-name: 2.0.1
+ set-function-name: 2.0.2
registry-auth-token@3.3.2:
dependencies:
@@ -10243,6 +11054,8 @@ snapshots:
onetime: 5.1.2
signal-exit: 3.0.7
+ retry@0.12.0: {}
+
reusify@1.0.4: {}
rimraf@3.0.2:
@@ -10299,6 +11112,15 @@ snapshots:
has-symbols: 1.0.3
isarray: 2.0.5
+ safe-array-concat@1.1.2:
+ dependencies:
+ call-bind: 1.0.7
+ get-intrinsic: 1.2.4
+ has-symbols: 1.0.3
+ isarray: 2.0.5
+
+ safe-buffer@5.1.2: {}
+
safe-buffer@5.2.1: {}
safe-json-parse@4.0.0:
@@ -10315,7 +11137,7 @@ snapshots:
safer-buffer@2.1.2: {}
- sass@1.77.2:
+ sass@1.77.6:
dependencies:
chokidar: 3.6.0
immutable: 4.3.5
@@ -10329,14 +11151,18 @@ snapshots:
dependencies:
loose-envify: 1.4.0
+ schema-utils@3.3.0:
+ dependencies:
+ '@types/json-schema': 7.0.15
+ ajv: 6.12.6
+ ajv-keywords: 3.5.2(ajv@6.12.6)
+
semver@6.3.1: {}
semver@7.6.0:
dependencies:
lru-cache: 6.0.0
- semver@7.6.2: {}
-
sentence-case@2.1.1:
dependencies:
no-case: 2.3.2
@@ -10344,6 +11170,10 @@ snapshots:
seq-queue@0.0.5: {}
+ serialize-javascript@6.0.2:
+ dependencies:
+ randombytes: 2.1.0
+
server-only@0.0.1: {}
set-blocking@2.0.0: {}
@@ -10357,9 +11187,10 @@ snapshots:
gopd: 1.0.1
has-property-descriptors: 1.0.2
- set-function-name@2.0.1:
+ 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
@@ -10378,6 +11209,13 @@ snapshots:
get-intrinsic: 1.2.4
object-inspect: 1.13.1
+ side-channel@1.0.6:
+ dependencies:
+ call-bind: 1.0.7
+ es-errors: 1.3.0
+ get-intrinsic: 1.2.4
+ object-inspect: 1.13.1
+
siginfo@2.0.0: {}
signal-exit@3.0.7: {}
@@ -10402,8 +11240,6 @@ snapshots:
mrmime: 2.0.0
totalist: 3.0.1
- sisteransi@1.0.5: {}
-
slash@3.0.0: {}
smart-buffer@4.2.0: {}
@@ -10415,7 +11251,7 @@ snapshots:
socks-proxy-agent@8.0.2:
dependencies:
agent-base: 7.1.0
- debug: 4.3.4
+ debug: 4.3.5
socks: 2.7.3
transitivePeerDependencies:
- supports-color
@@ -10436,10 +11272,25 @@ snapshots:
spawn-command@0.0.2: {}
+ split-ca@1.0.1: {}
+
sprintf-js@1.1.3: {}
sqlstring@2.3.3: {}
+ ssh-remote-port-forward@1.0.4:
+ dependencies:
+ '@types/ssh2': 0.5.52
+ ssh2: 1.15.0
+
+ ssh2@1.15.0:
+ dependencies:
+ asn1: 0.2.6
+ bcrypt-pbkdf: 1.0.2
+ optionalDependencies:
+ cpu-features: 0.0.10
+ nan: 2.19.0
+
stack-trace@0.0.10: {}
stackback@0.0.2: {}
@@ -10448,8 +11299,20 @@ snapshots:
std-env@3.7.0: {}
+ stop-iteration-iterator@1.0.0:
+ dependencies:
+ internal-slot: 1.0.7
+
streamsearch@1.1.0: {}
+ streamx@2.18.0:
+ dependencies:
+ fast-fifo: 1.3.2
+ queue-tick: 1.0.1
+ text-decoder: 1.1.0
+ optionalDependencies:
+ bare-events: 2.4.2
+
string-width@4.2.3:
dependencies:
emoji-regex: 8.0.0
@@ -10462,35 +11325,66 @@ snapshots:
emoji-regex: 9.2.2
strip-ansi: 7.1.0
- string.prototype.matchall@4.0.10:
+ string.prototype.includes@2.0.0:
+ dependencies:
+ define-properties: 1.2.1
+ es-abstract: 1.23.3
+
+ string.prototype.matchall@4.0.11:
dependencies:
call-bind: 1.0.7
define-properties: 1.2.1
- es-abstract: 1.22.4
+ es-abstract: 1.23.3
+ es-errors: 1.3.0
+ es-object-atoms: 1.0.0
get-intrinsic: 1.2.4
+ gopd: 1.0.1
has-symbols: 1.0.3
internal-slot: 1.0.7
regexp.prototype.flags: 1.5.2
- set-function-name: 2.0.1
- side-channel: 1.0.5
+ set-function-name: 2.0.2
+ side-channel: 1.0.6
string.prototype.trim@1.2.8:
dependencies:
call-bind: 1.0.7
define-properties: 1.2.1
- es-abstract: 1.22.4
+ es-abstract: 1.23.3
+
+ string.prototype.trim@1.2.9:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-abstract: 1.23.3
+ es-object-atoms: 1.0.0
string.prototype.trimend@1.0.7:
dependencies:
call-bind: 1.0.7
define-properties: 1.2.1
- es-abstract: 1.22.4
+ es-abstract: 1.23.3
+
+ string.prototype.trimend@1.0.8:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-object-atoms: 1.0.0
string.prototype.trimstart@1.0.7:
dependencies:
call-bind: 1.0.7
define-properties: 1.2.1
- es-abstract: 1.22.4
+ es-abstract: 1.23.3
+
+ string.prototype.trimstart@1.0.8:
+ dependencies:
+ call-bind: 1.0.7
+ define-properties: 1.2.1
+ es-object-atoms: 1.0.0
+
+ string_decoder@1.1.1:
+ dependencies:
+ safe-buffer: 5.1.2
string_decoder@1.3.0:
dependencies:
@@ -10556,6 +11450,15 @@ snapshots:
tabbable@6.2.0: {}
+ tapable@2.2.1: {}
+
+ tar-fs@2.0.1:
+ dependencies:
+ chownr: 1.1.4
+ mkdirp-classic: 0.5.3
+ pump: 3.0.0
+ tar-stream: 2.2.0
+
tar-fs@2.1.1:
dependencies:
chownr: 1.1.4
@@ -10563,6 +11466,14 @@ snapshots:
pump: 3.0.0
tar-stream: 2.2.0
+ tar-fs@3.0.6:
+ dependencies:
+ pump: 3.0.0
+ tar-stream: 3.1.7
+ optionalDependencies:
+ bare-fs: 2.3.1
+ bare-path: 2.1.3
+
tar-stream@2.2.0:
dependencies:
bl: 4.1.0
@@ -10571,6 +11482,12 @@ snapshots:
inherits: 2.0.4
readable-stream: 3.6.2
+ tar-stream@3.1.7:
+ dependencies:
+ b4a: 1.6.6
+ fast-fifo: 1.3.2
+ streamx: 2.18.0
+
tar@6.2.0:
dependencies:
chownr: 2.0.0
@@ -10580,12 +11497,53 @@ snapshots:
mkdirp: 1.0.4
yallist: 4.0.0
+ terser-webpack-plugin@5.3.10(webpack@5.91.0):
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.25
+ jest-worker: 27.5.1
+ schema-utils: 3.3.0
+ serialize-javascript: 6.0.2
+ terser: 5.31.0
+ webpack: 5.91.0
+
+ terser@5.31.0:
+ dependencies:
+ '@jridgewell/source-map': 0.3.6
+ acorn: 8.12.0
+ commander: 2.20.3
+ source-map-support: 0.5.21
+
test-exclude@6.0.0:
dependencies:
'@istanbuljs/schema': 0.1.3
glob: 7.2.3
minimatch: 3.1.2
+ testcontainers@10.10.0:
+ dependencies:
+ '@balena/dockerignore': 1.0.2
+ '@types/dockerode': 3.3.29
+ archiver: 5.3.2
+ async-lock: 1.4.1
+ byline: 5.0.0
+ debug: 4.3.5
+ docker-compose: 0.24.8
+ dockerode: 3.3.5
+ get-port: 5.1.1
+ node-fetch: 2.7.0
+ proper-lockfile: 4.1.2
+ properties-reader: 2.3.0
+ ssh-remote-port-forward: 1.0.4
+ tar-fs: 3.0.6
+ tmp: 0.2.3
+ transitivePeerDependencies:
+ - encoding
+ - supports-color
+
+ text-decoder@1.1.0:
+ dependencies:
+ b4a: 1.6.6
+
text-hex@1.0.0: {}
text-table@0.2.0: {}
@@ -10594,11 +11552,6 @@ snapshots:
through@2.3.8: {}
- timers-ext@0.1.7:
- dependencies:
- es5-ext: 0.10.62
- next-tick: 1.1.0
-
tinybench@2.6.0: {}
tinycolor2@1.6.0: {}
@@ -10625,6 +11578,8 @@ snapshots:
dependencies:
os-tmpdir: 1.0.2
+ tmp@0.2.3: {}
+
to-fast-properties@2.0.0: {}
to-regex-range@5.0.1:
@@ -10650,31 +11605,31 @@ snapshots:
triple-beam@1.4.1: {}
- ts-api-utils@1.3.0(typescript@5.4.5):
+ ts-api-utils@1.3.0(typescript@5.5.2):
dependencies:
- typescript: 5.4.5
+ typescript: 5.5.2
- ts-node@10.9.2(@types/node@20.12.12)(typescript@5.4.5):
+ ts-node@10.9.2(@types/node@20.14.9)(typescript@5.5.2):
dependencies:
'@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.9
'@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.4
- '@types/node': 20.12.12
- acorn: 8.11.3
+ '@types/node': 20.14.9
+ acorn: 8.12.0
acorn-walk: 8.3.2
arg: 4.1.3
create-require: 1.1.1
diff: 4.0.2
make-error: 1.3.6
- typescript: 5.4.5
+ typescript: 5.5.2
v8-compile-cache-lib: 3.0.1
yn: 3.1.1
- tsconfck@3.0.3(typescript@5.4.5):
+ tsconfck@3.0.3(typescript@5.5.2):
optionalDependencies:
- typescript: 5.4.5
+ typescript: 5.5.2
tsconfig-paths@3.15.0:
dependencies:
@@ -10689,7 +11644,7 @@ snapshots:
tsscmp@1.0.6: {}
- tsx@4.11.0:
+ tsx@4.13.3:
dependencies:
esbuild: 0.20.2
get-tsconfig: 4.7.5
@@ -10700,32 +11655,34 @@ snapshots:
dependencies:
safe-buffer: 5.2.1
- turbo-darwin-64@1.13.3:
+ turbo-darwin-64@2.0.6:
optional: true
- turbo-darwin-arm64@1.13.3:
+ turbo-darwin-arm64@2.0.6:
optional: true
- turbo-linux-64@1.13.3:
+ turbo-linux-64@2.0.6:
optional: true
- turbo-linux-arm64@1.13.3:
+ turbo-linux-arm64@2.0.6:
optional: true
- turbo-windows-64@1.13.3:
+ turbo-windows-64@2.0.6:
optional: true
- turbo-windows-arm64@1.13.3:
+ turbo-windows-arm64@2.0.6:
optional: true
- turbo@1.13.3:
+ turbo@2.0.6:
optionalDependencies:
- turbo-darwin-64: 1.13.3
- turbo-darwin-arm64: 1.13.3
- turbo-linux-64: 1.13.3
- turbo-linux-arm64: 1.13.3
- turbo-windows-64: 1.13.3
- turbo-windows-arm64: 1.13.3
+ turbo-darwin-64: 2.0.6
+ turbo-darwin-arm64: 2.0.6
+ turbo-linux-64: 2.0.6
+ turbo-linux-arm64: 2.0.6
+ turbo-windows-64: 2.0.6
+ turbo-windows-arm64: 2.0.6
+
+ tweetnacl@0.14.5: {}
type-check@0.4.0:
dependencies:
@@ -10733,29 +11690,37 @@ snapshots:
type-detect@4.0.8: {}
- type-fest@0.20.2: {}
-
type-fest@0.21.3: {}
type-fest@2.19.0: {}
type-fest@4.12.0: {}
- type@1.2.0: {}
-
- type@2.7.2: {}
-
typed-array-buffer@1.0.1:
dependencies:
call-bind: 1.0.7
es-errors: 1.3.0
is-typed-array: 1.1.13
+ typed-array-buffer@1.0.2:
+ dependencies:
+ call-bind: 1.0.7
+ es-errors: 1.3.0
+ is-typed-array: 1.1.13
+
typed-array-byte-length@1.0.0:
dependencies:
call-bind: 1.0.7
for-each: 0.3.3
- has-proto: 1.0.1
+ has-proto: 1.0.3
+ is-typed-array: 1.1.13
+
+ typed-array-byte-length@1.0.1:
+ dependencies:
+ call-bind: 1.0.7
+ for-each: 0.3.3
+ gopd: 1.0.1
+ has-proto: 1.0.3
is-typed-array: 1.1.13
typed-array-byte-offset@1.0.0:
@@ -10763,7 +11728,16 @@ snapshots:
available-typed-arrays: 1.0.6
call-bind: 1.0.7
for-each: 0.3.3
- has-proto: 1.0.1
+ has-proto: 1.0.3
+ is-typed-array: 1.1.13
+
+ typed-array-byte-offset@1.0.2:
+ dependencies:
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.7
+ for-each: 0.3.3
+ gopd: 1.0.1
+ has-proto: 1.0.3
is-typed-array: 1.1.13
typed-array-length@1.0.4:
@@ -10772,7 +11746,27 @@ snapshots:
for-each: 0.3.3
is-typed-array: 1.1.13
- typescript@5.4.5: {}
+ typed-array-length@1.0.6:
+ dependencies:
+ call-bind: 1.0.7
+ for-each: 0.3.3
+ gopd: 1.0.1
+ has-proto: 1.0.3
+ is-typed-array: 1.1.13
+ possible-typed-array-names: 1.0.0
+
+ typescript-eslint@7.14.1(eslint@9.6.0)(typescript@5.5.2):
+ dependencies:
+ '@typescript-eslint/eslint-plugin': 7.14.1(@typescript-eslint/parser@7.14.1(eslint@9.6.0)(typescript@5.5.2))(eslint@9.6.0)(typescript@5.5.2)
+ '@typescript-eslint/parser': 7.14.1(eslint@9.6.0)(typescript@5.5.2)
+ '@typescript-eslint/utils': 7.14.1(eslint@9.6.0)(typescript@5.5.2)
+ eslint: 9.6.0
+ optionalDependencies:
+ typescript: 5.5.2
+ transitivePeerDependencies:
+ - supports-color
+
+ typescript@5.5.2: {}
uc.micro@2.1.0: {}
@@ -10790,6 +11784,8 @@ snapshots:
undici-types@5.26.5: {}
+ undici@6.19.2: {}
+
universalify@0.2.0: {}
universalify@2.0.1: {}
@@ -10798,7 +11794,7 @@ snapshots:
dependencies:
browserslist: 4.23.0
escalade: 3.1.2
- picocolors: 1.0.1
+ picocolors: 1.0.0
update-check@1.5.4:
dependencies:
@@ -10898,13 +11894,13 @@ snapshots:
dependencies:
global: 4.4.0
- vite-node@1.6.0(@types/node@20.12.12)(sass@1.77.2)(sugarss@4.0.1):
+ vite-node@1.6.0(@types/node@20.14.9)(sass@1.77.6)(sugarss@4.0.1)(terser@5.31.0):
dependencies:
cac: 6.7.14
- debug: 4.3.4
+ debug: 4.3.5
pathe: 1.1.2
picocolors: 1.0.0
- vite: 5.2.6(@types/node@20.12.12)(sass@1.77.2)(sugarss@4.0.1)
+ vite: 5.2.6(@types/node@20.14.9)(sass@1.77.6)(sugarss@4.0.1)(terser@5.31.0)
transitivePeerDependencies:
- '@types/node'
- less
@@ -10915,29 +11911,30 @@ snapshots:
- supports-color
- terser
- vite-tsconfig-paths@4.3.2(typescript@5.4.5)(vite@5.2.6(@types/node@20.12.12)(sass@1.77.2)(sugarss@4.0.1)):
+ vite-tsconfig-paths@4.3.2(typescript@5.5.2)(vite@5.2.6(@types/node@20.14.9)(sass@1.77.6)(sugarss@4.0.1)(terser@5.31.0)):
dependencies:
debug: 4.3.4
globrex: 0.1.2
- tsconfck: 3.0.3(typescript@5.4.5)
+ tsconfck: 3.0.3(typescript@5.5.2)
optionalDependencies:
- vite: 5.2.6(@types/node@20.12.12)(sass@1.77.2)(sugarss@4.0.1)
+ vite: 5.2.6(@types/node@20.14.9)(sass@1.77.6)(sugarss@4.0.1)(terser@5.31.0)
transitivePeerDependencies:
- supports-color
- typescript
- vite@5.2.6(@types/node@20.12.12)(sass@1.77.2)(sugarss@4.0.1):
+ vite@5.2.6(@types/node@20.14.9)(sass@1.77.6)(sugarss@4.0.1)(terser@5.31.0):
dependencies:
esbuild: 0.20.2
postcss: 8.4.38
rollup: 4.13.0
optionalDependencies:
- '@types/node': 20.12.12
+ '@types/node': 20.14.9
fsevents: 2.3.3
- sass: 1.77.2
+ sass: 1.77.6
sugarss: 4.0.1(postcss@8.4.38)
+ terser: 5.31.0
- vitest@1.6.0(@types/node@20.12.12)(@vitest/ui@1.6.0)(jsdom@24.1.0)(sass@1.77.2)(sugarss@4.0.1):
+ vitest@1.6.0(@types/node@20.14.9)(@vitest/ui@1.6.0)(jsdom@24.1.0)(sass@1.77.6)(sugarss@4.0.1)(terser@5.31.0):
dependencies:
'@vitest/expect': 1.6.0
'@vitest/runner': 1.6.0
@@ -10956,11 +11953,11 @@ snapshots:
strip-literal: 2.0.0
tinybench: 2.6.0
tinypool: 0.8.3
- vite: 5.2.6(@types/node@20.12.12)(sass@1.77.2)(sugarss@4.0.1)
- vite-node: 1.6.0(@types/node@20.12.12)(sass@1.77.2)(sugarss@4.0.1)
+ vite: 5.2.6(@types/node@20.14.9)(sass@1.77.6)(sugarss@4.0.1)(terser@5.31.0)
+ vite-node: 1.6.0(@types/node@20.14.9)(sass@1.77.6)(sugarss@4.0.1)(terser@5.31.0)
why-is-node-running: 2.2.2
optionalDependencies:
- '@types/node': 20.12.12
+ '@types/node': 20.14.9
'@vitest/ui': 1.6.0(vitest@1.6.0)
jsdom: 24.1.0
transitivePeerDependencies:
@@ -10978,6 +11975,11 @@ snapshots:
dependencies:
xml-name-validator: 5.0.0
+ watchpack@2.4.1:
+ dependencies:
+ glob-to-regexp: 0.4.1
+ graceful-fs: 4.2.11
+
wcwidth@1.0.1:
dependencies:
defaults: 1.0.4
@@ -10986,6 +11988,39 @@ snapshots:
webidl-conversions@7.0.0: {}
+ webpack-sources@3.2.3: {}
+
+ webpack@5.91.0:
+ dependencies:
+ '@types/eslint-scope': 3.7.7
+ '@types/estree': 1.0.5
+ '@webassemblyjs/ast': 1.12.1
+ '@webassemblyjs/wasm-edit': 1.12.1
+ '@webassemblyjs/wasm-parser': 1.12.1
+ acorn: 8.12.0
+ acorn-import-assertions: 1.9.0(acorn@8.12.0)
+ browserslist: 4.23.0
+ chrome-trace-event: 1.0.3
+ enhanced-resolve: 5.16.1
+ es-module-lexer: 1.5.3
+ eslint-scope: 5.1.1
+ events: 3.3.0
+ glob-to-regexp: 0.4.1
+ graceful-fs: 4.2.11
+ json-parse-even-better-errors: 2.3.1
+ loader-runner: 4.3.0
+ mime-types: 2.1.35
+ neo-async: 2.6.2
+ schema-utils: 3.3.0
+ tapable: 2.2.1
+ terser-webpack-plugin: 5.3.10(webpack@5.91.0)
+ watchpack: 2.4.1
+ webpack-sources: 3.2.3
+ transitivePeerDependencies:
+ - '@swc/core'
+ - esbuild
+ - uglify-js
+
whatwg-encoding@3.1.1:
dependencies:
iconv-lite: 0.6.3
@@ -11023,7 +12058,7 @@ snapshots:
isarray: 2.0.5
which-boxed-primitive: 1.0.2
which-collection: 1.0.1
- which-typed-array: 1.1.14
+ which-typed-array: 1.1.15
which-collection@1.0.1:
dependencies:
@@ -11040,6 +12075,14 @@ snapshots:
gopd: 1.0.1
has-tostringtag: 1.0.2
+ which-typed-array@1.1.15:
+ dependencies:
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.7
+ for-each: 0.3.3
+ gopd: 1.0.1
+ has-tostringtag: 1.0.2
+
which@2.0.2:
dependencies:
isexe: 2.0.0
@@ -11095,7 +12138,7 @@ snapshots:
wrappy@1.0.2: {}
- ws@8.17.0: {}
+ ws@8.17.1: {}
xml-name-validator@5.0.0: {}
@@ -11107,6 +12150,8 @@ snapshots:
yallist@4.0.0: {}
+ yaml@2.4.5: {}
+
yargs-parser@21.1.1: {}
yargs@17.7.2:
@@ -11125,4 +12170,10 @@ snapshots:
yocto-queue@1.0.0: {}
+ zip-stream@4.1.1:
+ dependencies:
+ archiver-utils: 3.0.4
+ compress-commons: 4.1.2
+ readable-stream: 3.6.2
+
zod@3.23.8: {}
diff --git a/tooling/eslint/base.js b/tooling/eslint/base.js
index bb154044f..118762731 100644
--- a/tooling/eslint/base.js
+++ b/tooling/eslint/base.js
@@ -1,80 +1,90 @@
-/** @type {import("eslint").Linter.Config} */
-const config = {
- extends: [
- "turbo",
- "eslint:recommended",
- "plugin:@typescript-eslint/recommended-type-checked",
- "plugin:@typescript-eslint/stylistic-type-checked",
- "prettier",
- ],
- env: {
- es2022: true,
- node: true,
- },
- parser: "@typescript-eslint/parser",
- parserOptions: {
- project: true,
+///
+
+import eslint from "@eslint/js";
+import importPlugin from "eslint-plugin-import";
+import tseslint from "typescript-eslint";
+
+export default tseslint.config(
+ {
+ // Globally ignored files
+ ignores: ["**/*.config.js"],
},
- plugins: ["@typescript-eslint", "import"],
- rules: {
- "id-length": [
- "warn",
- {
- min: 3,
- exceptions: ["_", "i", "z", "t", "id", "db"], // _ for unused variables, i for index, z for zod, t for translation
- properties: "never", // This allows for example the use of as sm and md would be too short
- },
- ],
- "@typescript-eslint/prefer-nullish-coalescing": "off",
- "turbo/no-undeclared-env-vars": "off",
- "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }],
- "@typescript-eslint/consistent-type-imports": [
- "warn",
- { prefer: "type-imports", fixStyle: "separate-type-imports" },
- ],
- "@typescript-eslint/no-misused-promises": [2, { checksVoidReturn: { attributes: false } }],
- "import/consistent-type-specifier-style": ["error", "prefer-top-level"],
- "no-restricted-syntax": [
- "error",
- {
- selector: "FunctionDeclaration[async=false][id.name=/Async$/]",
- message: "Function ending in 'Async' must be declared async",
- },
- {
- selector:
- "FunctionDeclaration[async=true][id.name=/^[a-z].*$/][id.name=/ ^(?!generateMetadata$)[a-z].*$/][id.name!=/Async$/]",
- message: "Async function name must end in 'Async' (function declaration)",
- },
- {
- selector: "MethodDefinition[value.async=false][key.name=/Async$/]",
- message: "Method ending in 'Async' must be declared async",
- },
- {
- selector: "MethodDefinition[value.async=true][key.name!=/Async$/]",
- message: "Async method name must end in 'Async'",
- },
- {
- selector: "Property[value.type=/FunctionExpression$/][value.async=false][key.name=/Async$/]",
- message: "Function ending in 'Async' must be declared async",
- },
- {
- selector:
- "Property[value.type=/FunctionExpression$/][value.async=true][key.name!=/^on(Success|Settled)$/][key.name!=/Async$/]",
- message: "Async function name must end in 'Async' (property)",
- },
- {
- selector: "VariableDeclarator[init.type=/FunctionExpression$/][init.async=false][id.name=/Async$/]",
- message: "Function ending in 'Async' must be declared async",
- },
- {
- selector:
- "VariableDeclarator[init.type=/FunctionExpression$/][init.async=true][id.name=/^[a-z].*$/][id.name!=/Async$/]",
- message: "Async function name must end in 'Async' (variable declarator)",
- },
+ {
+ files: ["**/*.js", "**/*.ts", "**/*.tsx"],
+ plugins: {
+ import: importPlugin,
+ },
+ extends: [
+ eslint.configs.recommended,
+ ...tseslint.configs.recommended,
+ ...tseslint.configs.recommendedTypeChecked,
+ ...tseslint.configs.stylisticTypeChecked,
],
+ rules: {
+ "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }],
+ "@typescript-eslint/consistent-type-imports": [
+ "warn",
+ { prefer: "type-imports", fixStyle: "separate-type-imports" },
+ ],
+ "@typescript-eslint/no-misused-promises": [2, { checksVoidReturn: { attributes: false } }],
+ "@typescript-eslint/no-unnecessary-condition": [
+ "error",
+ {
+ allowConstantLoopConditions: true,
+ },
+ ],
+ "@typescript-eslint/no-non-null-assertion": "error",
+ "import/consistent-type-specifier-style": ["error", "prefer-top-level"],
+ "id-length": [
+ "warn",
+ {
+ min: 3,
+ exceptions: ["_", "i", "z", "t", "id", "db"], // _ for unused variables, i for index, z for zod, t for translation
+ properties: "never", // This allows for example the use of as sm and md would be too short
+ },
+ ],
+ "no-restricted-syntax": [
+ "error",
+ {
+ selector: "FunctionDeclaration[async=false][id.name=/Async$/]",
+ message: "Function ending in 'Async' must be declared async",
+ },
+ {
+ selector:
+ "FunctionDeclaration[async=true][id.name=/^[a-z].*$/][id.name=/ ^(?!generateMetadata$)[a-z].*$/][id.name!=/Async$/]",
+ message: "Async function name must end in 'Async' (function declaration)",
+ },
+ {
+ selector: "MethodDefinition[value.async=false][key.name=/Async$/]",
+ message: "Method ending in 'Async' must be declared async",
+ },
+ {
+ selector: "MethodDefinition[value.async=true][key.name!=/Async$/]",
+ message: "Async method name must end in 'Async'",
+ },
+ {
+ selector: "Property[value.type=/FunctionExpression$/][value.async=false][key.name=/Async$/]",
+ message: "Function ending in 'Async' must be declared async",
+ },
+ {
+ selector:
+ "Property[value.type=/FunctionExpression$/][value.async=true][key.name!=/^on(Success|Settled)$/][key.name!=/Async$/]",
+ message: "Async function name must end in 'Async' (property)",
+ },
+ {
+ selector: "VariableDeclarator[init.type=/FunctionExpression$/][init.async=false][id.name=/Async$/]",
+ message: "Function ending in 'Async' must be declared async",
+ },
+ {
+ selector:
+ "VariableDeclarator[init.type=/FunctionExpression$/][init.async=true][id.name=/^[a-z].*$/][id.name!=/Async$/]",
+ message: "Async function name must end in 'Async' (variable declarator)",
+ },
+ ],
+ },
},
- ignorePatterns: ["**/.eslintrc.cjs", "**/*.config.js", "**/*.config.cjs", ".next", "dist", "pnpm-lock.yaml"],
- reportUnusedDisableDirectives: true,
-};
-
-module.exports = config;
+ {
+ linterOptions: { reportUnusedDisableDirectives: true },
+ languageOptions: { parserOptions: { project: true } },
+ },
+);
diff --git a/tooling/eslint/eslint.config.js b/tooling/eslint/eslint.config.js
new file mode 100644
index 000000000..f7a5a7d36
--- /dev/null
+++ b/tooling/eslint/eslint.config.js
@@ -0,0 +1,4 @@
+import baseConfig from "@homarr/eslint-config/base";
+
+/** @type {import('typescript-eslint').Config} */
+export default [...baseConfig];
diff --git a/tooling/eslint/nextjs.js b/tooling/eslint/nextjs.js
index 278d86600..b876fc84e 100644
--- a/tooling/eslint/nextjs.js
+++ b/tooling/eslint/nextjs.js
@@ -1,9 +1,17 @@
-/** @type {import('eslint').Linter.Config} */
-const config = {
- extends: ["plugin:@next/next/recommended"],
- rules: {
- "@next/next/no-html-link-for-pages": "off",
- },
-};
+import nextPlugin from "@next/eslint-plugin-next";
-module.exports = config;
+/** @type {Awaited} */
+export default [
+ {
+ files: ["**/*.ts", "**/*.tsx"],
+ plugins: {
+ "@next/next": nextPlugin,
+ },
+ rules: {
+ ...nextPlugin.configs.recommended.rules,
+ ...nextPlugin.configs["core-web-vitals"].rules,
+ // TypeError: context.getAncestors is not a function
+ "@next/next/no-duplicate-head": "off",
+ },
+ },
+];
diff --git a/tooling/eslint/package.json b/tooling/eslint/package.json
index 9dcfcd032..b4a1e698a 100644
--- a/tooling/eslint/package.json
+++ b/tooling/eslint/package.json
@@ -3,40 +3,33 @@
"version": "0.2.0",
"private": true,
"license": "MIT",
- "files": [
- "./base.js",
- "./nextjs.js",
- "./react.js"
- ],
+ "type": "module",
+ "exports": {
+ "./base": "./base.js",
+ "./nextjs": "./nextjs.js",
+ "./react": "./react.js"
+ },
"scripts": {
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit"
},
"dependencies": {
- "@next/eslint-plugin-next": "^14.2.3",
- "@typescript-eslint/eslint-plugin": "^7.11.0",
- "@typescript-eslint/parser": "^7.11.0",
+ "@next/eslint-plugin-next": "^14.2.4",
"eslint-config-prettier": "^9.1.0",
- "eslint-config-turbo": "^1.13.3",
+ "eslint-config-turbo": "^2.0.6",
"eslint-plugin-import": "^2.29.1",
- "eslint-plugin-jsx-a11y": "^6.8.0",
- "eslint-plugin-react": "^7.34.1",
- "eslint-plugin-react-hooks": "^4.6.2"
+ "eslint-plugin-jsx-a11y": "^6.9.0",
+ "eslint-plugin-react": "^7.34.3",
+ "eslint-plugin-react-hooks": "^4.6.2",
+ "typescript-eslint": "^7.14.1"
},
"devDependencies": {
- "@types/eslint": "^8.56.10",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
- "eslint": "^8.57.0",
- "typescript": "^5.4.5"
- },
- "eslintConfig": {
- "root": true,
- "extends": [
- "./base.js"
- ]
+ "eslint": "^9.6.0",
+ "typescript": "^5.5.2"
},
"prettier": "@homarr/prettier-config"
}
diff --git a/tooling/eslint/react.js b/tooling/eslint/react.js
index ee21a219f..a87b5c165 100644
--- a/tooling/eslint/react.js
+++ b/tooling/eslint/react.js
@@ -1,20 +1,24 @@
-/** @type {import('eslint').Linter.Config} */
-const config = {
- extends: ["plugin:react/recommended", "plugin:react-hooks/recommended", "plugin:jsx-a11y/recommended"],
- rules: {
- "react/prop-types": "off",
- },
- globals: {
- React: "writable",
- },
- settings: {
- react: {
- version: "detect",
+import reactPlugin from "eslint-plugin-react";
+import hooksPlugin from "eslint-plugin-react-hooks";
+
+/** @type {Awaited} */
+export default [
+ {
+ files: ["**/*.ts", "**/*.tsx"],
+ plugins: {
+ react: reactPlugin,
+ "react-hooks": hooksPlugin,
+ },
+ rules: {
+ ...reactPlugin.configs["jsx-runtime"].rules,
+ ...hooksPlugin.configs.recommended.rules,
+ // context.getSource is not a function
+ "react-hooks/exhaustive-deps": "off",
+ },
+ languageOptions: {
+ globals: {
+ React: "writable",
+ },
},
},
- env: {
- browser: true,
- },
-};
-
-module.exports = config;
+];
diff --git a/tooling/eslint/types.d.ts b/tooling/eslint/types.d.ts
new file mode 100644
index 000000000..37cfded93
--- /dev/null
+++ b/tooling/eslint/types.d.ts
@@ -0,0 +1,58 @@
+/**
+ * Since the ecosystem hasn't fully migrated to ESLint's new FlatConfig system yet,
+ * we "need" to type some of the plugins manually :(
+ */
+
+declare module "@eslint/js" {
+ // Why the hell doesn't eslint themselves export their types?
+ import type { Linter } from "eslint";
+
+ export const configs: {
+ readonly recommended: { readonly rules: Readonly };
+ readonly all: { readonly rules: Readonly };
+ };
+}
+
+declare module "eslint-plugin-import" {
+ import type { Linter, Rule } from "eslint";
+
+ export const configs: {
+ recommended: { rules: Linter.RulesRecord };
+ };
+ export const rules: Record;
+}
+
+declare module "eslint-plugin-react" {
+ import type { Linter, Rule } from "eslint";
+
+ export const configs: {
+ recommended: { rules: Linter.RulesRecord };
+ all: { rules: Linter.RulesRecord };
+ "jsx-runtime": { rules: Linter.RulesRecord };
+ };
+ export const rules: Record;
+}
+
+declare module "eslint-plugin-react-hooks" {
+ import type { Linter, Rule } from "eslint";
+
+ export const configs: {
+ recommended: {
+ rules: {
+ "rules-of-hooks": Linter.RuleEntry;
+ "exhaustive-deps": Linter.RuleEntry;
+ };
+ };
+ };
+ export const rules: Record;
+}
+
+declare module "@next/eslint-plugin-next" {
+ import type { Linter, Rule } from "eslint";
+
+ export const configs: {
+ recommended: { rules: Linter.RulesRecord };
+ "core-web-vitals": { rules: Linter.RulesRecord };
+ };
+ export const rules: Record;
+}
diff --git a/tooling/prettier/package.json b/tooling/prettier/package.json
index f02433e4b..17bd43d4d 100644
--- a/tooling/prettier/package.json
+++ b/tooling/prettier/package.json
@@ -9,12 +9,12 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
- "@ianvs/prettier-plugin-sort-imports": "^4.2.1",
- "prettier": "^3.2.5"
+ "@ianvs/prettier-plugin-sort-imports": "^4.3.0",
+ "prettier": "^3.3.2"
},
"devDependencies": {
"@homarr/tsconfig": "workspace:^0.1.0",
- "typescript": "^5.4.5"
+ "typescript": "^5.5.2"
},
"prettier": "@homarr/prettier-config"
}
diff --git a/tooling/typescript/base.json b/tooling/typescript/base.json
index 944bb0a3c..37f6290fb 100644
--- a/tooling/typescript/base.json
+++ b/tooling/typescript/base.json
@@ -2,7 +2,11 @@
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"target": "ES2022",
- "lib": ["dom", "dom.iterable", "ES2022"],
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "ES2022"
+ ],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@@ -16,6 +20,7 @@
"jsx": "preserve",
"incremental": true,
"noUncheckedIndexedAccess": true,
+ "strictNullChecks": true,
"baseUrl": ".",
"paths": {
"*": [
@@ -23,5 +28,10 @@
]
}
},
- "exclude": ["node_modules", "build", "dist", ".next"]
-}
+ "exclude": [
+ "node_modules",
+ "build",
+ "dist",
+ ".next"
+ ]
+}
\ No newline at end of file
diff --git a/turbo.json b/turbo.json
index aac9d5904..2ef5dfb2d 100644
--- a/turbo.json
+++ b/turbo.json
@@ -3,7 +3,16 @@
"globalDependencies": [
"**/.env"
],
- "pipeline": {
+ "globalEnv": [
+ "DATABASE_URL",
+ "AUTH_DISCORD_ID",
+ "AUTH_DISCORD_SECRET",
+ "AUTH_REDIRECT_PROXY_URL",
+ "AUTH_SECRET",
+ "AUTH_URL"
+ ],
+ "ui": "stream",
+ "tasks": {
"topo": {
"dependsOn": [
"^topo"
@@ -29,7 +38,7 @@
"outputs": [
"node_modules/.cache/.prettiercache"
],
- "outputMode": "new-only"
+ "outputLogs": "new-only"
},
"lint": {
"dependsOn": [
@@ -53,13 +62,5 @@
"//#clean": {
"cache": false
}
- },
- "globalEnv": [
- "DATABASE_URL",
- "AUTH_DISCORD_ID",
- "AUTH_DISCORD_SECRET",
- "AUTH_REDIRECT_PROXY_URL",
- "AUTH_SECRET",
- "AUTH_URL"
- ]
+ }
}
\ No newline at end of file
diff --git a/turbo/generators/config.ts b/turbo/generators/config.ts
index 1bf6d4896..4ee93a8f8 100644
--- a/turbo/generators/config.ts
+++ b/turbo/generators/config.ts
@@ -15,14 +15,12 @@ export default function generator(plop: PlopTypes.NodePlopAPI): void {
{
type: "input",
name: "name",
- message:
- "What is the name of the package? (You can skip the `@homarr/` prefix)",
+ message: "What is the name of the package? (You can skip the `@homarr/` prefix)",
},
{
type: "input",
name: "deps",
- message:
- "Enter a space separated list of dependencies you would like to install",
+ message: "Enter a space separated list of dependencies you would like to install",
},
],
actions: [
@@ -34,6 +32,11 @@ export default function generator(plop: PlopTypes.NodePlopAPI): void {
}
return "Config sanitized";
},
+ {
+ type: "add",
+ path: "packages/{{ name }}/eslint.config.js",
+ templateFile: "templates/eslint.config.js.hbs",
+ },
{
type: "add",
path: "packages/{{ name }}/package.json",
@@ -61,9 +64,7 @@ export default function generator(plop: PlopTypes.NodePlopAPI): void {
if ("deps" in answers && typeof answers.deps === "string") {
const pkg = JSON.parse(content) as PackageJson;
for (const dep of answers.deps.split(" ").filter(Boolean)) {
- const version = await fetch(
- `https://registry.npmjs.org/-/package/${dep}/dist-tags`,
- )
+ const version = await fetch(`https://registry.npmjs.org/-/package/${dep}/dist-tags`)
.then((res) => res.json())
.then((json) => json.latest);
if (!pkg.dependencies) pkg.dependencies = {};
@@ -83,9 +84,7 @@ export default function generator(plop: PlopTypes.NodePlopAPI): void {
// stdio: "inherit",
// });
execSync("pnpm i", { stdio: "inherit" });
- execSync(
- `pnpm prettier --write packages/${answers.name}/** --list-different`,
- );
+ execSync(`pnpm prettier --write packages/${answers.name}/** --list-different`);
return "Package scaffolded";
}
return "Package not scaffolded";
diff --git a/turbo/generators/templates/eslint.config.js.hbs b/turbo/generators/templates/eslint.config.js.hbs
new file mode 100644
index 000000000..26c6c4ba1
--- /dev/null
+++ b/turbo/generators/templates/eslint.config.js.hbs
@@ -0,0 +1,9 @@
+import baseConfig from "@homarr/eslint-config/base";
+
+/** @type {import('typescript-eslint').Config} */
+export default [
+ {
+ ignores: [],
+ },
+ ...baseConfig,
+];
\ No newline at end of file
diff --git a/turbo/generators/templates/package.json.hbs b/turbo/generators/templates/package.json.hbs
index fd1754a76..0b52369f4 100644
--- a/turbo/generators/templates/package.json.hbs
+++ b/turbo/generators/templates/package.json.hbs
@@ -2,6 +2,7 @@
"name": "@homarr/{{name}}",
"private": true,
"version": "0.1.0",
+ "type": "module",
"exports": {
".": "./index.ts"
},
@@ -15,7 +16,7 @@
"license": "MIT",
"scripts": {
"clean": "rm -rf .turbo node_modules",
- "lint": "eslint .",
+ "lint": "eslint",
"format": "prettier --check . --ignore-path ../../.gitignore",
"typecheck": "tsc --noEmit"
},
@@ -23,13 +24,8 @@
"@homarr/eslint-config": "workspace:^0.2.0",
"@homarr/prettier-config": "workspace:^0.1.0",
"@homarr/tsconfig": "workspace:^0.1.0",
- "eslint": "^8.57.0",
- "typescript": "^5.4.5"
- },
- "eslintConfig": {
- "extends": [
- "@homarr/eslint-config/base"
- ]
+ "eslint": "^9.5.0",
+ "typescript": "^5.5.2"
},
"prettier": "@homarr/prettier-config"
}
\ No newline at end of file