Browse Source

[Playwright] Improvements around node (#6321)

* Playwright node improvements

* Upgrade Keycloak compose to trixie
Timshel 4 days ago
parent
commit
7c597e88f9

+ 1 - 1
playwright/README.md

@@ -15,7 +15,7 @@ It is possible to run `Playwright` outside of the container, this removes the ne
 You will additionally need `nodejs` then run:
 
 ```bash
-npm install
+npm ci --ignore-scripts
 npx playwright install-deps
 npx playwright install firefox
 ```

+ 7 - 28
playwright/compose/keycloak/Dockerfile

@@ -1,40 +1,19 @@
-FROM docker.io/library/debian:bookworm-slim as build
+FROM docker.io/library/debian:trixie-slim
 
-ENV DEBIAN_FRONTEND=noninteractive
 ARG KEYCLOAK_VERSION
 
-SHELL ["/bin/bash", "-o", "pipefail", "-c"]
-
-RUN apt-get update \
-    && apt-get install -y ca-certificates curl wget \
-    && rm -rf /var/lib/apt/lists/*
-
-WORKDIR /
-
-RUN wget -c https://github.com/keycloak/keycloak/releases/download/${KEYCLOAK_VERSION}/keycloak-${KEYCLOAK_VERSION}.tar.gz -O - | tar -xz
-
-FROM docker.io/library/debian:bookworm-slim
-
 ENV DEBIAN_FRONTEND=noninteractive
-ARG KEYCLOAK_VERSION
-
 SHELL ["/bin/bash", "-o", "pipefail", "-c"]
 
-RUN apt-get update \
-    && apt-get install -y ca-certificates curl wget \
-    && rm -rf /var/lib/apt/lists/*
-
-ARG JAVA_URL
-ARG JAVA_VERSION
-
-ENV JAVA_VERSION=${JAVA_VERSION}
-
-RUN mkdir -p /opt/openjdk && cd /opt/openjdk \
-    && wget -c "${JAVA_URL}"  -O - | tar -xz
+RUN apt-get update && apt-get install -y ca-certificates curl jq openjdk-21-jdk-headless wget
 
 WORKDIR /
 
+RUN wget -c https://github.com/keycloak/keycloak/releases/download/${KEYCLOAK_VERSION}/keycloak-${KEYCLOAK_VERSION}.tar.gz -O - | tar -xz \
+    && mkdir -p /opt/keycloak \
+    && mv /keycloak-${KEYCLOAK_VERSION}/bin /opt/keycloak/bin \
+    && rm -rf /keycloak-${KEYCLOAK_VERSION}
+
 COPY setup.sh /setup.sh
-COPY --from=build /keycloak-${KEYCLOAK_VERSION}/bin /opt/keycloak/bin
 
 CMD "/setup.sh"

+ 10 - 2
playwright/compose/keycloak/setup.sh

@@ -1,7 +1,6 @@
 #!/bin/bash
 
-export PATH=/opt/keycloak/bin:/opt/openjdk/jdk-${JAVA_VERSION}/bin:$PATH
-export JAVA_HOME=/opt/openjdk/jdk-${JAVA_VERSION}
+export PATH=/opt/keycloak/bin:$PATH
 
 STATUS_CODE=0
 while [[ "$STATUS_CODE" != "404" ]] ; do
@@ -34,3 +33,12 @@ kcadm.sh update users/$TEST_USER3_ID/reset-password -r "$TEST_REALM" -s type=pas
 
 # Dummy realm to mark end of setup
 kcadm.sh create realms -s realm="$DUMMY_REALM" -s enabled=true -s "accessTokenLifespan=600"
+
+# TO DEBUG uncomment the following line to keep the setup container running
+# sleep 3600
+# THEN in another terminal:
+# docker exec -it keycloakSetup-dev /bin/bash
+# export PATH=$PATH:/opt/keycloak/bin
+# kcadm.sh config credentials --server "http://${KC_HTTP_HOST}:${KC_HTTP_PORT}" --realm master --user "$KEYCLOAK_ADMIN" --password "$KEYCLOAK_ADMIN_PASSWORD" --client admin-cli
+# ENJOY
+# Doc: https://wjw465150.gitbooks.io/keycloak-documentation/content/server_admin/topics/admin-cli.html

+ 4 - 4
playwright/compose/playwright/Dockerfile

@@ -1,4 +1,4 @@
-FROM docker.io/library/debian:bookworm-slim
+FROM docker.io/library/debian:trixie-slim
 
 SHELL ["/bin/bash", "-o", "pipefail", "-c"]
 
@@ -8,7 +8,7 @@ RUN apt-get update \
     && apt-get install -y ca-certificates curl \
     && curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc \
     && chmod a+r /etc/apt/keyrings/docker.asc \
-    && echo "deb [signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian bookworm stable" | tee /etc/apt/sources.list.d/docker.list \
+    && echo "deb [signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian trixie stable" | tee /etc/apt/sources.list.d/docker.list \
     && apt-get update \
     && apt-get install -y --no-install-recommends \
         containerd.io \
@@ -27,8 +27,8 @@ RUN apt-get update \
 RUN mkdir /playwright
 WORKDIR /playwright
 
-COPY package.json .
-RUN npm install && npx playwright install-deps && npx playwright install firefox
+COPY package.json package-lock.json .
+RUN npm ci --ignore-scripts && npx playwright install-deps && npx playwright install firefox
 
 COPY docker-compose.yml test.env ./
 COPY compose ./compose

+ 4 - 5
playwright/docker-compose.yml

@@ -30,9 +30,10 @@ services:
       - SMTP_FROM
       - SMTP_DEBUG
       - SSO_DEBUG_TOKENS
-      - SSO_FRONTEND
       - SSO_ENABLED
+      - SSO_FRONTEND
       - SSO_ONLY
+      - SSO_SCOPES
     restart: "no"
     depends_on:
       - VaultwardenPrebuild
@@ -100,7 +101,7 @@ services:
   Keycloak:
     profiles: ["keycloak", "vaultwarden"]
     container_name: keycloak-${ENV:-dev}
-    image: quay.io/keycloak/keycloak:25.0.4
+    image: quay.io/keycloak/keycloak:26.3.4
     network_mode: "host"
     command:
       - start-dev
@@ -114,9 +115,7 @@ services:
       context: compose/keycloak
       dockerfile: Dockerfile
       args:
-        KEYCLOAK_VERSION: 25.0.4
-        JAVA_URL: https://download.java.net/java/GA/jdk21.0.2/f2283984656d49d69e91c558476027ac/13/GPL/openjdk-21.0.2_linux-x64_bin.tar.gz
-        JAVA_VERSION: 21.0.2
+        KEYCLOAK_VERSION: 26.3.4
     network_mode: "host"
     depends_on:
       - Keycloak

+ 1 - 1
playwright/global-utils.ts

@@ -10,7 +10,7 @@ const fs = require("fs");
 const { spawn } = require('node:child_process');
 
 export function loadEnv(){
-    var myEnv = dotenv.config({ path: 'test.env' });
+    var myEnv = dotenv.config({ path: 'test.env', quiet: true });
     dotenvExpand.expand(myEnv);
 
     return {

+ 366 - 178
playwright/package-lock.json

@@ -9,34 +9,53 @@
             "version": "1.0.0",
             "license": "ISC",
             "dependencies": {
-                "mysql2": "^3.14.3",
-                "otpauth": "^9.4.0",
-                "pg": "^8.16.3"
+                "mysql2": "3.15.0",
+                "otpauth": "9.4.1",
+                "pg": "8.16.3"
             },
             "devDependencies": {
-                "@playwright/test": "^1.54.2",
-                "dotenv": "^16.6.1",
-                "dotenv-expand": "^12.0.2",
-                "maildev": "npm:@timshel_npm/maildev@^3.2.1"
+                "@playwright/test": "1.55.1",
+                "dotenv": "17.2.2",
+                "dotenv-expand": "12.0.3",
+                "maildev": "npm:@timshel_npm/maildev@^3.2.3"
             }
         },
         "node_modules/@asamuzakjp/css-color": {
-            "version": "3.2.0",
-            "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz",
-            "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==",
+            "version": "4.0.5",
+            "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.0.5.tgz",
+            "integrity": "sha512-lMrXidNhPGsDjytDy11Vwlb6OIGrT3CmLg3VWNFyWkLWtijKl7xjvForlh8vuj0SHGjgl4qZEQzUmYTeQA2JFQ==",
             "dev": true,
             "dependencies": {
-                "@csstools/css-calc": "^2.1.3",
-                "@csstools/css-color-parser": "^3.0.9",
-                "@csstools/css-parser-algorithms": "^3.0.4",
-                "@csstools/css-tokenizer": "^3.0.3",
-                "lru-cache": "^10.4.3"
+                "@csstools/css-calc": "^2.1.4",
+                "@csstools/css-color-parser": "^3.1.0",
+                "@csstools/css-parser-algorithms": "^3.0.5",
+                "@csstools/css-tokenizer": "^3.0.4",
+                "lru-cache": "^11.2.1"
+            }
+        },
+        "node_modules/@asamuzakjp/dom-selector": {
+            "version": "6.5.6",
+            "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.5.6.tgz",
+            "integrity": "sha512-Mj3Hu9ymlsERd7WOsUKNUZnJYL4IZ/I9wVVYgtvOsWYiEFbkQ4G7VRIh2USxTVW4BBDIsLG+gBUgqOqf2Kvqow==",
+            "dev": true,
+            "dependencies": {
+                "@asamuzakjp/nwsapi": "^2.3.9",
+                "bidi-js": "^1.0.3",
+                "css-tree": "^3.1.0",
+                "is-potential-custom-element-name": "^1.0.1",
+                "lru-cache": "^11.2.1"
             }
         },
+        "node_modules/@asamuzakjp/nwsapi": {
+            "version": "2.3.9",
+            "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz",
+            "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==",
+            "dev": true
+        },
         "node_modules/@csstools/color-helpers": {
-            "version": "5.0.2",
-            "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz",
-            "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==",
+            "version": "5.1.0",
+            "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz",
+            "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==",
             "dev": true,
             "funding": [
                 {
@@ -76,9 +95,9 @@
             }
         },
         "node_modules/@csstools/css-color-parser": {
-            "version": "3.0.10",
-            "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz",
-            "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==",
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz",
+            "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==",
             "dev": true,
             "funding": [
                 {
@@ -91,7 +110,7 @@
                 }
             ],
             "dependencies": {
-                "@csstools/color-helpers": "^5.0.2",
+                "@csstools/color-helpers": "^5.1.0",
                 "@csstools/css-calc": "^2.1.4"
             },
             "engines": {
@@ -124,6 +143,28 @@
                 "@csstools/css-tokenizer": "^3.0.4"
             }
         },
+        "node_modules/@csstools/css-syntax-patches-for-csstree": {
+            "version": "1.0.14",
+            "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.14.tgz",
+            "integrity": "sha512-zSlIxa20WvMojjpCSy8WrNpcZ61RqfTfX3XTaOeVlGJrt/8HF3YbzgFZa01yTbT4GWQLwfTcC3EB8i3XnB647Q==",
+            "dev": true,
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/csstools"
+                },
+                {
+                    "type": "opencollective",
+                    "url": "https://opencollective.com/csstools"
+                }
+            ],
+            "engines": {
+                "node": ">=18"
+            },
+            "peerDependencies": {
+                "postcss": "^8.4"
+            }
+        },
         "node_modules/@csstools/css-tokenizer": {
             "version": "3.0.4",
             "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz",
@@ -144,9 +185,9 @@
             }
         },
         "node_modules/@noble/hashes": {
-            "version": "1.7.1",
-            "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz",
-            "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==",
+            "version": "1.8.0",
+            "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
+            "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
             "engines": {
                 "node": "^14.21.3 || >=16"
             },
@@ -155,12 +196,12 @@
             }
         },
         "node_modules/@playwright/test": {
-            "version": "1.54.2",
-            "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.54.2.tgz",
-            "integrity": "sha512-A+znathYxPf+72riFd1r1ovOLqsIIB0jKIoPjyK2kqEIe30/6jF6BC7QNluHuwUmsD2tv1XZVugN8GqfTMOxsA==",
+            "version": "1.55.1",
+            "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.1.tgz",
+            "integrity": "sha512-IVAh/nOJaw6W9g+RJVlIQJ6gSiER+ae6mKQ5CX1bERzQgbC1VSeBlwdvczT7pxb0GWiyrxH4TGKbMfDb4Sq/ig==",
             "dev": true,
             "dependencies": {
-                "playwright": "1.54.2"
+                "playwright": "1.55.1"
             },
             "bin": {
                 "playwright": "cli.js"
@@ -208,12 +249,12 @@
             }
         },
         "node_modules/@types/node": {
-            "version": "24.2.1",
-            "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz",
-            "integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==",
+            "version": "24.5.2",
+            "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz",
+            "integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==",
             "dev": true,
             "dependencies": {
-                "undici-types": "~7.10.0"
+                "undici-types": "~7.12.0"
             }
         },
         "node_modules/@types/trusted-types": {
@@ -292,6 +333,15 @@
                 "node": "^4.5.0 || >= 5.9"
             }
         },
+        "node_modules/bidi-js": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
+            "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
+            "dev": true,
+            "dependencies": {
+                "require-from-string": "^2.0.2"
+            }
+        },
         "node_modules/body-parser": {
             "version": "2.2.0",
             "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
@@ -313,9 +363,9 @@
             }
         },
         "node_modules/body-parser/node_modules/debug": {
-            "version": "4.4.1",
-            "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
-            "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+            "version": "4.4.3",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+            "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
             "dev": true,
             "dependencies": {
                 "ms": "^2.1.3"
@@ -374,12 +424,12 @@
             }
         },
         "node_modules/commander": {
-            "version": "13.1.0",
-            "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz",
-            "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==",
+            "version": "14.0.1",
+            "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.1.tgz",
+            "integrity": "sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==",
             "dev": true,
             "engines": {
-                "node": ">=18"
+                "node": ">=20"
             }
         },
         "node_modules/compressible": {
@@ -464,30 +514,44 @@
                 "node": ">= 0.10"
             }
         },
+        "node_modules/css-tree": {
+            "version": "3.1.0",
+            "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz",
+            "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==",
+            "dev": true,
+            "dependencies": {
+                "mdn-data": "2.12.2",
+                "source-map-js": "^1.0.1"
+            },
+            "engines": {
+                "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+            }
+        },
         "node_modules/cssstyle": {
-            "version": "4.6.0",
-            "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz",
-            "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==",
+            "version": "5.3.1",
+            "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.1.tgz",
+            "integrity": "sha512-g5PC9Aiph9eiczFpcgUhd9S4UUO3F+LHGRIi5NUMZ+4xtoIYbHNZwZnWA2JsFGe8OU8nl4WyaEFiZuGuxlutJQ==",
             "dev": true,
             "dependencies": {
-                "@asamuzakjp/css-color": "^3.2.0",
-                "rrweb-cssom": "^0.8.0"
+                "@asamuzakjp/css-color": "^4.0.3",
+                "@csstools/css-syntax-patches-for-csstree": "^1.0.14",
+                "css-tree": "^3.1.0"
             },
             "engines": {
-                "node": ">=18"
+                "node": ">=20"
             }
         },
         "node_modules/data-urls": {
-            "version": "5.0.0",
-            "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
-            "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-6.0.0.tgz",
+            "integrity": "sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==",
             "dev": true,
             "dependencies": {
                 "whatwg-mimetype": "^4.0.0",
-                "whatwg-url": "^14.0.0"
+                "whatwg-url": "^15.0.0"
             },
             "engines": {
-                "node": ">=18"
+                "node": ">=20"
             }
         },
         "node_modules/debug": {
@@ -573,9 +637,9 @@
             }
         },
         "node_modules/dompurify": {
-            "version": "3.2.6",
-            "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz",
-            "integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==",
+            "version": "3.2.7",
+            "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz",
+            "integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==",
             "dev": true,
             "optionalDependencies": {
                 "@types/trusted-types": "^2.0.7"
@@ -596,9 +660,9 @@
             }
         },
         "node_modules/dotenv": {
-            "version": "16.6.1",
-            "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
-            "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
+            "version": "17.2.2",
+            "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.2.tgz",
+            "integrity": "sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==",
             "dev": true,
             "engines": {
                 "node": ">=12"
@@ -608,9 +672,9 @@
             }
         },
         "node_modules/dotenv-expand": {
-            "version": "12.0.2",
-            "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.2.tgz",
-            "integrity": "sha512-lXpXz2ZE1cea1gL4sz2Ipj8y4PiVjytYr3Ij0SWoms1PGxIv7m2CRKuRuCRtHdVuvM/hNJPMxt5PbhboNC4dPQ==",
+            "version": "12.0.3",
+            "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.3.tgz",
+            "integrity": "sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==",
             "dev": true,
             "dependencies": {
                 "dotenv": "^16.4.5"
@@ -622,6 +686,18 @@
                 "url": "https://dotenvx.com"
             }
         },
+        "node_modules/dotenv-expand/node_modules/dotenv": {
+            "version": "16.6.1",
+            "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
+            "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
+            "dev": true,
+            "engines": {
+                "node": ">=12"
+            },
+            "funding": {
+                "url": "https://dotenvx.com"
+            }
+        },
         "node_modules/dunder-proto": {
             "version": "1.0.1",
             "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -876,9 +952,9 @@
             }
         },
         "node_modules/express/node_modules/debug": {
-            "version": "4.4.1",
-            "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
-            "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+            "version": "4.4.3",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+            "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
             "dev": true,
             "dependencies": {
                 "ms": "^2.1.3"
@@ -916,9 +992,9 @@
             }
         },
         "node_modules/finalhandler/node_modules/debug": {
-            "version": "4.4.1",
-            "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
-            "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+            "version": "4.4.3",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+            "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
             "dev": true,
             "dependencies": {
                 "ms": "^2.1.3"
@@ -1155,9 +1231,9 @@
             }
         },
         "node_modules/http-proxy-agent/node_modules/debug": {
-            "version": "4.4.1",
-            "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
-            "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+            "version": "4.4.3",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+            "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
             "dev": true,
             "dependencies": {
                 "ms": "^2.1.3"
@@ -1191,9 +1267,9 @@
             }
         },
         "node_modules/https-proxy-agent/node_modules/debug": {
-            "version": "4.4.1",
-            "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
-            "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+            "version": "4.4.3",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+            "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
             "dev": true,
             "dependencies": {
                 "ms": "^2.1.3"
@@ -1217,6 +1293,7 @@
             "version": "0.6.3",
             "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
             "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+            "dev": true,
             "dependencies": {
                 "safer-buffer": ">= 2.1.2 < 3.0.0"
             },
@@ -1263,34 +1340,34 @@
             "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="
         },
         "node_modules/jsdom": {
-            "version": "26.1.0",
-            "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz",
-            "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==",
+            "version": "27.0.0",
+            "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.0.0.tgz",
+            "integrity": "sha512-lIHeR1qlIRrIN5VMccd8tI2Sgw6ieYXSVktcSHaNe3Z5nE/tcPQYQWOq00wxMvYOsz+73eAkNenVvmPC6bba9A==",
             "dev": true,
             "dependencies": {
-                "cssstyle": "^4.2.1",
-                "data-urls": "^5.0.0",
+                "@asamuzakjp/dom-selector": "^6.5.4",
+                "cssstyle": "^5.3.0",
+                "data-urls": "^6.0.0",
                 "decimal.js": "^10.5.0",
                 "html-encoding-sniffer": "^4.0.0",
                 "http-proxy-agent": "^7.0.2",
                 "https-proxy-agent": "^7.0.6",
                 "is-potential-custom-element-name": "^1.0.1",
-                "nwsapi": "^2.2.16",
-                "parse5": "^7.2.1",
+                "parse5": "^7.3.0",
                 "rrweb-cssom": "^0.8.0",
                 "saxes": "^6.0.0",
                 "symbol-tree": "^3.2.4",
-                "tough-cookie": "^5.1.1",
+                "tough-cookie": "^6.0.0",
                 "w3c-xmlserializer": "^5.0.0",
-                "webidl-conversions": "^7.0.0",
+                "webidl-conversions": "^8.0.0",
                 "whatwg-encoding": "^3.1.1",
                 "whatwg-mimetype": "^4.0.0",
-                "whatwg-url": "^14.1.1",
-                "ws": "^8.18.0",
+                "whatwg-url": "^15.0.0",
+                "ws": "^8.18.2",
                 "xml-name-validator": "^5.0.0"
             },
             "engines": {
-                "node": ">=18"
+                "node": ">=20"
             },
             "peerDependencies": {
                 "canvas": "^3.0.0"
@@ -1349,10 +1426,13 @@
             "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="
         },
         "node_modules/lru-cache": {
-            "version": "10.4.3",
-            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
-            "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
-            "dev": true
+            "version": "11.2.1",
+            "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.1.tgz",
+            "integrity": "sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==",
+            "dev": true,
+            "engines": {
+                "node": "20 || >=22"
+            }
         },
         "node_modules/lru.min": {
             "version": "1.1.2",
@@ -1370,32 +1450,32 @@
         },
         "node_modules/maildev": {
             "name": "@timshel_npm/maildev",
-            "version": "3.2.1",
-            "resolved": "https://registry.npmjs.org/@timshel_npm/maildev/-/maildev-3.2.1.tgz",
-            "integrity": "sha512-CE9ccmo8JgCgZGR5FfU+eTCks13Ux4ogrG7G3TNq1W/5NXKSPLT/MZpvC5kDfoeQwplDxWe5a/nld67D0DcauA==",
+            "version": "3.2.3",
+            "resolved": "https://registry.npmjs.org/@timshel_npm/maildev/-/maildev-3.2.3.tgz",
+            "integrity": "sha512-CNxMz4Obw7nL8MZbx4y1YUFeqqAQk+Qwm51tcBV5lRBotMlXKeYhuEcayb1v66nUwq832bUfKF4hyQpJixFZrw==",
             "dev": true,
             "dependencies": {
-                "@types/mailparser": "^3.4.6",
+                "@types/mailparser": "3.4.6",
                 "addressparser": "1.0.1",
-                "async": "^3.2.6",
-                "commander": "^13.1.0",
-                "compression": "^1.8.1",
-                "cors": "^2.8.5",
-                "dompurify": "^3.2.6",
-                "express": "^5.1.0",
-                "jsdom": "^26.1.0",
-                "mailparser": "^3.7.4",
-                "mime": "^3.0.0",
-                "nodemailer": "^7.0.5",
-                "smtp-server": "^3.14.0",
-                "socket.io": "^4.8.1",
+                "async": "3.2.6",
+                "commander": "14.0.1",
+                "compression": "1.8.1",
+                "cors": "2.8.5",
+                "dompurify": "3.2.7",
+                "express": "5.1.0",
+                "jsdom": "27.0.0",
+                "mailparser": "3.7.4",
+                "mime": "4.1.0",
+                "nodemailer": "7.0.6",
+                "smtp-server": "3.14.0",
+                "socket.io": "4.8.1",
                 "wildstring": "1.0.9"
             },
             "bin": {
                 "maildev": "bin/maildev"
             },
             "engines": {
-                "node": ">=18.0.0"
+                "node": ">=20.0.0"
             }
         },
         "node_modules/mailparser": {
@@ -1445,6 +1525,12 @@
                 "node": ">= 0.4"
             }
         },
+        "node_modules/mdn-data": {
+            "version": "2.12.2",
+            "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz",
+            "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==",
+            "dev": true
+        },
         "node_modules/media-typer": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
@@ -1467,15 +1553,18 @@
             }
         },
         "node_modules/mime": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
-            "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
+            "version": "4.1.0",
+            "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz",
+            "integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==",
             "dev": true,
+            "funding": [
+                "https://github.com/sponsors/broofa"
+            ],
             "bin": {
-                "mime": "cli.js"
+                "mime": "bin/cli.js"
             },
             "engines": {
-                "node": ">=10.0.0"
+                "node": ">=16"
             }
         },
         "node_modules/mime-db": {
@@ -1506,14 +1595,14 @@
             "dev": true
         },
         "node_modules/mysql2": {
-            "version": "3.14.3",
-            "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.14.3.tgz",
-            "integrity": "sha512-fD6MLV8XJ1KiNFIF0bS7Msl8eZyhlTDCDl75ajU5SJtpdx9ZPEACulJcqJWr1Y8OYyxsFc4j3+nflpmhxCU5aQ==",
+            "version": "3.15.0",
+            "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.15.0.tgz",
+            "integrity": "sha512-tT6pomf5Z/I7Jzxu8sScgrYBMK9bUFWd7Kbo6Fs1L0M13OOIJ/ZobGKS3Z7tQ8Re4lj+LnLXIQVZZxa3fhYKzA==",
             "dependencies": {
                 "aws-ssl-profiles": "^1.1.1",
                 "denque": "^2.1.0",
                 "generate-function": "^2.3.1",
-                "iconv-lite": "^0.6.3",
+                "iconv-lite": "^0.7.0",
                 "long": "^5.2.1",
                 "lru.min": "^1.0.0",
                 "named-placeholders": "^1.1.3",
@@ -1524,6 +1613,21 @@
                 "node": ">= 8.0"
             }
         },
+        "node_modules/mysql2/node_modules/iconv-lite": {
+            "version": "0.7.0",
+            "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz",
+            "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
+            "dependencies": {
+                "safer-buffer": ">= 2.1.2 < 3.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/express"
+            }
+        },
         "node_modules/named-placeholders": {
             "version": "1.1.3",
             "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
@@ -1543,6 +1647,25 @@
                 "node": ">=12"
             }
         },
+        "node_modules/nanoid": {
+            "version": "3.3.11",
+            "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+            "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+            "dev": true,
+            "funding": [
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/ai"
+                }
+            ],
+            "peer": true,
+            "bin": {
+                "nanoid": "bin/nanoid.cjs"
+            },
+            "engines": {
+                "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+            }
+        },
         "node_modules/negotiator": {
             "version": "0.6.4",
             "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz",
@@ -1553,20 +1676,14 @@
             }
         },
         "node_modules/nodemailer": {
-            "version": "7.0.5",
-            "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.5.tgz",
-            "integrity": "sha512-nsrh2lO3j4GkLLXoeEksAMgAOqxOv6QumNRVQTJwKH4nuiww6iC2y7GyANs9kRAxCexg3+lTWM3PZ91iLlVjfg==",
+            "version": "7.0.6",
+            "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.6.tgz",
+            "integrity": "sha512-F44uVzgwo49xboqbFgBGkRaiMgtoBrBEWCVincJPK9+S9Adkzt/wXCLKbf7dxucmxfTI5gHGB+bEmdyzN6QKjw==",
             "dev": true,
             "engines": {
                 "node": ">=6.0.0"
             }
         },
-        "node_modules/nwsapi": {
-            "version": "2.2.21",
-            "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.21.tgz",
-            "integrity": "sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==",
-            "dev": true
-        },
         "node_modules/object-assign": {
             "version": "4.1.1",
             "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -1619,11 +1736,11 @@
             }
         },
         "node_modules/otpauth": {
-            "version": "9.4.0",
-            "resolved": "https://registry.npmjs.org/otpauth/-/otpauth-9.4.0.tgz",
-            "integrity": "sha512-fHIfzIG5RqCkK9cmV8WU+dPQr9/ebR5QOwGZn2JAr1RQF+lmAuLL2YdtdqvmBjNmgJlYk3KZ4a0XokaEhg1Jsw==",
+            "version": "9.4.1",
+            "resolved": "https://registry.npmjs.org/otpauth/-/otpauth-9.4.1.tgz",
+            "integrity": "sha512-+iVvys36CFsyXEqfNftQm1II7SW23W1wx9RwNk0Cd97lbvorqAhBDksb/0bYry087QMxjiuBS0wokdoZ0iUeAw==",
             "dependencies": {
-                "@noble/hashes": "1.7.1"
+                "@noble/hashes": "1.8.0"
             },
             "funding": {
                 "url": "https://github.com/hectorm/otpauth?sponsor=1"
@@ -1676,12 +1793,13 @@
             }
         },
         "node_modules/path-to-regexp": {
-            "version": "8.2.0",
-            "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
-            "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
+            "version": "8.3.0",
+            "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
+            "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
             "dev": true,
-            "engines": {
-                "node": ">=16"
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/express"
             }
         },
         "node_modules/peberminta": {
@@ -1774,13 +1892,20 @@
                 "split2": "^4.1.0"
             }
         },
+        "node_modules/picocolors": {
+            "version": "1.1.1",
+            "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+            "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+            "dev": true,
+            "peer": true
+        },
         "node_modules/playwright": {
-            "version": "1.54.2",
-            "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.2.tgz",
-            "integrity": "sha512-Hu/BMoA1NAdRUuulyvQC0pEqZ4vQbGfn8f7wPXcnqQmM+zct9UliKxsIkLNmz/ku7LElUNqmaiv1TG/aL5ACsw==",
+            "version": "1.55.1",
+            "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.1.tgz",
+            "integrity": "sha512-cJW4Xd/G3v5ovXtJJ52MAOclqeac9S/aGGgRzLabuF8TnIb6xHvMzKIa6JmrRzUkeXJgfL1MhukP0NK6l39h3A==",
             "dev": true,
             "dependencies": {
-                "playwright-core": "1.54.2"
+                "playwright-core": "1.55.1"
             },
             "bin": {
                 "playwright": "cli.js"
@@ -1793,9 +1918,9 @@
             }
         },
         "node_modules/playwright-core": {
-            "version": "1.54.2",
-            "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.2.tgz",
-            "integrity": "sha512-n5r4HFbMmWsB4twG7tJLDN9gmBUeSPcsBZiWSE4DnYz9mJMAFqr2ID7+eGC9kpEnxExJ1epttwR59LEWCk8mtA==",
+            "version": "1.55.1",
+            "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.1.tgz",
+            "integrity": "sha512-Z6Mh9mkwX+zxSlHqdr5AOcJnfp+xUWLCt9uKV18fhzA8eyxUd8NUWzAjxUh55RZKSYwDGX0cfaySdhZJGMoJ+w==",
             "dev": true,
             "bin": {
                 "playwright-core": "cli.js"
@@ -1804,6 +1929,35 @@
                 "node": ">=18"
             }
         },
+        "node_modules/postcss": {
+            "version": "8.5.6",
+            "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+            "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+            "dev": true,
+            "funding": [
+                {
+                    "type": "opencollective",
+                    "url": "https://opencollective.com/postcss/"
+                },
+                {
+                    "type": "tidelift",
+                    "url": "https://tidelift.com/funding/github/npm/postcss"
+                },
+                {
+                    "type": "github",
+                    "url": "https://github.com/sponsors/ai"
+                }
+            ],
+            "peer": true,
+            "dependencies": {
+                "nanoid": "^3.3.11",
+                "picocolors": "^1.1.1",
+                "source-map-js": "^1.2.1"
+            },
+            "engines": {
+                "node": "^10 || ^12 || >=14"
+            }
+        },
         "node_modules/postgres-array": {
             "version": "2.0.0",
             "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
@@ -1895,18 +2049,43 @@
             }
         },
         "node_modules/raw-body": {
-            "version": "3.0.0",
-            "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
-            "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
+            "version": "3.0.1",
+            "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz",
+            "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==",
             "dev": true,
             "dependencies": {
                 "bytes": "3.1.2",
                 "http-errors": "2.0.0",
-                "iconv-lite": "0.6.3",
+                "iconv-lite": "0.7.0",
                 "unpipe": "1.0.0"
             },
             "engines": {
-                "node": ">= 0.8"
+                "node": ">= 0.10"
+            }
+        },
+        "node_modules/raw-body/node_modules/iconv-lite": {
+            "version": "0.7.0",
+            "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz",
+            "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
+            "dev": true,
+            "dependencies": {
+                "safer-buffer": ">= 2.1.2 < 3.0.0"
+            },
+            "engines": {
+                "node": ">=0.10.0"
+            },
+            "funding": {
+                "type": "opencollective",
+                "url": "https://opencollective.com/express"
+            }
+        },
+        "node_modules/require-from-string": {
+            "version": "2.0.2",
+            "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+            "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+            "dev": true,
+            "engines": {
+                "node": ">=0.10.0"
             }
         },
         "node_modules/router": {
@@ -1926,9 +2105,9 @@
             }
         },
         "node_modules/router/node_modules/debug": {
-            "version": "4.4.1",
-            "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
-            "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+            "version": "4.4.3",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+            "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
             "dev": true,
             "dependencies": {
                 "ms": "^2.1.3"
@@ -2026,9 +2205,9 @@
             }
         },
         "node_modules/send/node_modules/debug": {
-            "version": "4.4.1",
-            "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
-            "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+            "version": "4.4.3",
+            "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+            "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
             "dev": true,
             "dependencies": {
                 "ms": "^2.1.3"
@@ -2344,6 +2523,15 @@
                 "node": ">= 0.6"
             }
         },
+        "node_modules/source-map-js": {
+            "version": "1.2.1",
+            "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+            "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+            "dev": true,
+            "engines": {
+                "node": ">=0.10.0"
+            }
+        },
         "node_modules/split2": {
             "version": "4.2.0",
             "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
@@ -2385,21 +2573,21 @@
             }
         },
         "node_modules/tldts": {
-            "version": "6.1.86",
-            "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz",
-            "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==",
+            "version": "7.0.16",
+            "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.16.tgz",
+            "integrity": "sha512-5bdPHSwbKTeHmXrgecID4Ljff8rQjv7g8zKQPkCozRo2HWWni+p310FSn5ImI+9kWw9kK4lzOB5q/a6iv0IJsw==",
             "dev": true,
             "dependencies": {
-                "tldts-core": "^6.1.86"
+                "tldts-core": "^7.0.16"
             },
             "bin": {
                 "tldts": "bin/cli.js"
             }
         },
         "node_modules/tldts-core": {
-            "version": "6.1.86",
-            "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz",
-            "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==",
+            "version": "7.0.16",
+            "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.16.tgz",
+            "integrity": "sha512-XHhPmHxphLi+LGbH0G/O7dmUH9V65OY20R7vH8gETHsp5AZCjBk9l8sqmRKLaGOxnETU7XNSDUPtewAy/K6jbA==",
             "dev": true
         },
         "node_modules/toidentifier": {
@@ -2412,27 +2600,27 @@
             }
         },
         "node_modules/tough-cookie": {
-            "version": "5.1.2",
-            "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz",
-            "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==",
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz",
+            "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==",
             "dev": true,
             "dependencies": {
-                "tldts": "^6.1.32"
+                "tldts": "^7.0.5"
             },
             "engines": {
                 "node": ">=16"
             }
         },
         "node_modules/tr46": {
-            "version": "5.1.1",
-            "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
-            "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==",
+            "version": "6.0.0",
+            "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz",
+            "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==",
             "dev": true,
             "dependencies": {
                 "punycode": "^2.3.1"
             },
             "engines": {
-                "node": ">=18"
+                "node": ">=20"
             }
         },
         "node_modules/type-is": {
@@ -2456,9 +2644,9 @@
             "dev": true
         },
         "node_modules/undici-types": {
-            "version": "7.10.0",
-            "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
-            "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
+            "version": "7.12.0",
+            "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz",
+            "integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==",
             "dev": true
         },
         "node_modules/unpipe": {
@@ -2492,12 +2680,12 @@
             }
         },
         "node_modules/webidl-conversions": {
-            "version": "7.0.0",
-            "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
-            "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+            "version": "8.0.0",
+            "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.0.tgz",
+            "integrity": "sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==",
             "dev": true,
             "engines": {
-                "node": ">=12"
+                "node": ">=20"
             }
         },
         "node_modules/whatwg-encoding": {
@@ -2522,16 +2710,16 @@
             }
         },
         "node_modules/whatwg-url": {
-            "version": "14.2.0",
-            "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz",
-            "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==",
+            "version": "15.1.0",
+            "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz",
+            "integrity": "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==",
             "dev": true,
             "dependencies": {
-                "tr46": "^5.1.0",
-                "webidl-conversions": "^7.0.0"
+                "tr46": "^6.0.0",
+                "webidl-conversions": "^8.0.0"
             },
             "engines": {
-                "node": ">=18"
+                "node": ">=20"
             }
         },
         "node_modules/wildstring": {

+ 7 - 7
playwright/package.json

@@ -8,14 +8,14 @@
     "author": "",
     "license": "ISC",
     "devDependencies": {
-        "@playwright/test": "^1.54.2",
-        "dotenv": "^16.6.1",
-        "dotenv-expand": "^12.0.2",
-        "maildev": "npm:@timshel_npm/maildev@^3.2.1"
+        "@playwright/test": "1.55.1",
+        "dotenv": "17.2.2",
+        "dotenv-expand": "12.0.3",
+        "maildev": "npm:@timshel_npm/maildev@^3.2.3"
     },
     "dependencies": {
-        "mysql2": "^3.14.3",
-        "otpauth": "^9.4.0",
-        "pg": "^8.16.3"
+        "mysql2": "3.15.0",
+        "otpauth": "9.4.1",
+        "pg": "8.16.3"
     }
 }